1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
5 * You can obtain one at http://mozilla.org/MPL/2.0/. */
6
7 #include "SDBConnection.h"
8
9 // Local includes
10 #include "ActorsChild.h"
11 #include "SDBRequest.h"
12 #include "SimpleDBCommon.h"
13
14 // Global includes
15 #include <stdint.h>
16 #include <utility>
17 #include "MainThreadUtils.h"
18 #include "js/ArrayBuffer.h"
19 #include "js/RootingAPI.h"
20 #include "js/TypeDecls.h"
21 #include "js/experimental/TypedData.h"
22 #include "mozilla/Assertions.h"
23 #include "mozilla/MacroForEach.h"
24 #include "mozilla/Maybe.h"
25 #include "mozilla/Preferences.h"
26 #include "mozilla/RefPtr.h"
27 #include "mozilla/Variant.h"
28 #include "mozilla/dom/PBackgroundSDBConnection.h"
29 #include "mozilla/dom/quota/QuotaManager.h"
30 #include "mozilla/fallible.h"
31 #include "mozilla/ipc/BackgroundChild.h"
32 #include "mozilla/ipc/BackgroundUtils.h"
33 #include "mozilla/ipc/PBackgroundChild.h"
34 #include "mozilla/ipc/PBackgroundSharedTypes.h"
35 #include "nsDebug.h"
36 #include "nsError.h"
37 #include "nsISDBCallbacks.h"
38 #include "nsISupportsUtils.h"
39 #include "nsStringFwd.h"
40 #include "nscore.h"
41
42 namespace mozilla::dom {
43
44 using namespace mozilla::ipc;
45
46 namespace {
47
GetWriteData(JSContext * aCx,JS::Handle<JS::Value> aValue,nsCString & aData)48 nsresult GetWriteData(JSContext* aCx, JS::Handle<JS::Value> aValue,
49 nsCString& aData) {
50 if (aValue.isObject()) {
51 JS::Rooted<JSObject*> obj(aCx, &aValue.toObject());
52
53 bool isView = false;
54 if (JS::IsArrayBufferObject(obj) ||
55 (isView = JS_IsArrayBufferViewObject(obj))) {
56 uint8_t* data;
57 size_t length;
58 bool unused;
59 if (isView) {
60 JS_GetObjectAsArrayBufferView(obj, &length, &unused, &data);
61 } else {
62 JS::GetObjectAsArrayBuffer(obj, &length, &data);
63 }
64
65 // Throw for large buffers to prevent truncation.
66 if (length > INT32_MAX) {
67 return NS_ERROR_ILLEGAL_VALUE;
68 }
69
70 if (NS_WARN_IF(!aData.Assign(reinterpret_cast<char*>(data), length,
71 fallible_t()))) {
72 return NS_ERROR_OUT_OF_MEMORY;
73 }
74
75 return NS_OK;
76 }
77 }
78
79 return NS_ERROR_NOT_IMPLEMENTED;
80 }
81
82 } // namespace
83
SDBConnection()84 SDBConnection::SDBConnection()
85 : mBackgroundActor(nullptr),
86 mPersistenceType(quota::PERSISTENCE_TYPE_INVALID),
87 mRunningRequest(false),
88 mOpen(false),
89 mAllowedToClose(false) {
90 AssertIsOnOwningThread();
91 }
92
~SDBConnection()93 SDBConnection::~SDBConnection() {
94 AssertIsOnOwningThread();
95
96 if (mBackgroundActor) {
97 mBackgroundActor->SendDeleteMeInternal();
98 MOZ_ASSERT(!mBackgroundActor, "SendDeleteMeInternal should have cleared!");
99 }
100 }
101
102 // static
Create(nsISupports * aOuter,REFNSIID aIID,void ** aResult)103 nsresult SDBConnection::Create(nsISupports* aOuter, REFNSIID aIID,
104 void** aResult) {
105 MOZ_ASSERT(aResult);
106
107 if (NS_WARN_IF(!Preferences::GetBool(kPrefSimpleDBEnabled, false))) {
108 return NS_ERROR_NOT_AVAILABLE;
109 }
110
111 if (aOuter) {
112 return NS_ERROR_NO_AGGREGATION;
113 }
114
115 RefPtr<SDBConnection> connection = new SDBConnection();
116
117 nsresult rv = connection->QueryInterface(aIID, aResult);
118 if (NS_WARN_IF(NS_FAILED(rv))) {
119 return rv;
120 }
121
122 return NS_OK;
123 }
124
ClearBackgroundActor()125 void SDBConnection::ClearBackgroundActor() {
126 AssertIsOnOwningThread();
127
128 mBackgroundActor = nullptr;
129 }
130
OnNewRequest()131 void SDBConnection::OnNewRequest() {
132 AssertIsOnOwningThread();
133 MOZ_ASSERT(!mRunningRequest);
134
135 mRunningRequest = true;
136 }
137
OnRequestFinished()138 void SDBConnection::OnRequestFinished() {
139 AssertIsOnOwningThread();
140 MOZ_ASSERT(mRunningRequest);
141
142 mRunningRequest = false;
143 }
144
OnOpen()145 void SDBConnection::OnOpen() {
146 AssertIsOnOwningThread();
147 MOZ_ASSERT(!mOpen);
148
149 mOpen = true;
150 }
151
OnClose(bool aAbnormal)152 void SDBConnection::OnClose(bool aAbnormal) {
153 AssertIsOnOwningThread();
154 MOZ_ASSERT(mOpen);
155
156 mOpen = false;
157
158 if (aAbnormal) {
159 MOZ_ASSERT(mAllowedToClose);
160
161 if (mCloseCallback) {
162 mCloseCallback->OnClose(this);
163 }
164 }
165 }
166
AllowToClose()167 void SDBConnection::AllowToClose() {
168 AssertIsOnOwningThread();
169
170 mAllowedToClose = true;
171 }
172
CheckState()173 nsresult SDBConnection::CheckState() {
174 AssertIsOnOwningThread();
175
176 if (mAllowedToClose) {
177 return NS_ERROR_ABORT;
178 }
179
180 if (mRunningRequest) {
181 return NS_ERROR_NOT_AVAILABLE;
182 }
183
184 return NS_OK;
185 }
186
EnsureBackgroundActor()187 nsresult SDBConnection::EnsureBackgroundActor() {
188 AssertIsOnOwningThread();
189
190 if (mBackgroundActor) {
191 return NS_OK;
192 }
193
194 PBackgroundChild* backgroundActor =
195 BackgroundChild::GetOrCreateForCurrentThread();
196 if (NS_WARN_IF(!backgroundActor)) {
197 return NS_ERROR_FAILURE;
198 }
199
200 SDBConnectionChild* actor = new SDBConnectionChild(this);
201
202 mBackgroundActor = static_cast<SDBConnectionChild*>(
203 backgroundActor->SendPBackgroundSDBConnectionConstructor(
204 actor, mPersistenceType, *mPrincipalInfo));
205 if (NS_WARN_IF(!mBackgroundActor)) {
206 return NS_ERROR_FAILURE;
207 }
208
209 return NS_OK;
210 }
211
InitiateRequest(SDBRequest * aRequest,const SDBRequestParams & aParams)212 nsresult SDBConnection::InitiateRequest(SDBRequest* aRequest,
213 const SDBRequestParams& aParams) {
214 AssertIsOnOwningThread();
215 MOZ_ASSERT(aRequest);
216 MOZ_ASSERT(mBackgroundActor);
217
218 auto actor = new SDBRequestChild(aRequest);
219
220 if (!mBackgroundActor->SendPBackgroundSDBRequestConstructor(actor, aParams)) {
221 return NS_ERROR_FAILURE;
222 }
223
224 // Balanced in SDBRequestChild::Recv__delete__().
225 OnNewRequest();
226
227 return NS_OK;
228 }
229
NS_IMPL_ISUPPORTS(SDBConnection,nsISDBConnection)230 NS_IMPL_ISUPPORTS(SDBConnection, nsISDBConnection)
231
232 NS_IMETHODIMP
233 SDBConnection::Init(nsIPrincipal* aPrincipal,
234 const nsACString& aPersistenceType) {
235 MOZ_ASSERT(NS_IsMainThread());
236 MOZ_ASSERT(aPrincipal);
237
238 UniquePtr<PrincipalInfo> principalInfo(new PrincipalInfo());
239 nsresult rv = PrincipalToPrincipalInfo(aPrincipal, principalInfo.get());
240 if (NS_WARN_IF(NS_FAILED(rv))) {
241 return rv;
242 }
243
244 if (principalInfo->type() != PrincipalInfo::TContentPrincipalInfo &&
245 principalInfo->type() != PrincipalInfo::TSystemPrincipalInfo) {
246 NS_WARNING("Simpledb not allowed for this principal!");
247 return NS_ERROR_INVALID_ARG;
248 }
249
250 if (NS_WARN_IF(!quota::QuotaManager::IsPrincipalInfoValid(*principalInfo))) {
251 return NS_ERROR_INVALID_ARG;
252 }
253
254 PersistenceType persistenceType;
255 if (aPersistenceType.IsVoid()) {
256 persistenceType = quota::PERSISTENCE_TYPE_DEFAULT;
257 } else {
258 const auto maybePersistenceType =
259 quota::PersistenceTypeFromString(aPersistenceType, fallible);
260 if (NS_WARN_IF(maybePersistenceType.isNothing())) {
261 return NS_ERROR_INVALID_ARG;
262 }
263
264 persistenceType = maybePersistenceType.value();
265 }
266
267 mPrincipalInfo = std::move(principalInfo);
268 mPersistenceType = persistenceType;
269
270 return NS_OK;
271 }
272
273 NS_IMETHODIMP
Open(const nsAString & aName,nsISDBRequest ** _retval)274 SDBConnection::Open(const nsAString& aName, nsISDBRequest** _retval) {
275 AssertIsOnOwningThread();
276
277 nsresult rv = CheckState();
278 if (NS_WARN_IF(NS_FAILED(rv))) {
279 return rv;
280 }
281
282 if (mOpen) {
283 return NS_ERROR_ALREADY_INITIALIZED;
284 }
285
286 SDBRequestOpenParams params;
287 params.name() = aName;
288
289 RefPtr<SDBRequest> request = new SDBRequest(this);
290
291 rv = EnsureBackgroundActor();
292 if (NS_WARN_IF(NS_FAILED(rv))) {
293 return rv;
294 }
295
296 rv = InitiateRequest(request, params);
297 if (NS_WARN_IF(NS_FAILED(rv))) {
298 return rv;
299 }
300
301 request.forget(_retval);
302 return NS_OK;
303 }
304
305 NS_IMETHODIMP
Seek(uint64_t aOffset,nsISDBRequest ** _retval)306 SDBConnection::Seek(uint64_t aOffset, nsISDBRequest** _retval) {
307 AssertIsOnOwningThread();
308
309 nsresult rv = CheckState();
310 if (NS_WARN_IF(NS_FAILED(rv))) {
311 return rv;
312 }
313
314 if (!mOpen) {
315 return NS_BASE_STREAM_CLOSED;
316 }
317
318 SDBRequestSeekParams params;
319 params.offset() = aOffset;
320
321 RefPtr<SDBRequest> request = new SDBRequest(this);
322
323 rv = InitiateRequest(request, params);
324 if (NS_WARN_IF(NS_FAILED(rv))) {
325 return rv;
326 }
327
328 request.forget(_retval);
329 return NS_OK;
330 }
331
332 NS_IMETHODIMP
Read(uint64_t aSize,nsISDBRequest ** _retval)333 SDBConnection::Read(uint64_t aSize, nsISDBRequest** _retval) {
334 AssertIsOnOwningThread();
335
336 nsresult rv = CheckState();
337 if (NS_WARN_IF(NS_FAILED(rv))) {
338 return rv;
339 }
340
341 if (!mOpen) {
342 return NS_BASE_STREAM_CLOSED;
343 }
344
345 SDBRequestReadParams params;
346 params.size() = aSize;
347
348 RefPtr<SDBRequest> request = new SDBRequest(this);
349
350 rv = InitiateRequest(request, params);
351 if (NS_WARN_IF(NS_FAILED(rv))) {
352 return rv;
353 }
354
355 request.forget(_retval);
356 return NS_OK;
357 }
358
359 NS_IMETHODIMP
Write(JS::HandleValue aValue,JSContext * aCx,nsISDBRequest ** _retval)360 SDBConnection::Write(JS::HandleValue aValue, JSContext* aCx,
361 nsISDBRequest** _retval) {
362 AssertIsOnOwningThread();
363
364 nsresult rv = CheckState();
365 if (NS_WARN_IF(NS_FAILED(rv))) {
366 return rv;
367 }
368
369 if (!mOpen) {
370 return NS_BASE_STREAM_CLOSED;
371 }
372
373 JS::Rooted<JS::Value> value(aCx, aValue);
374
375 nsCString data;
376 rv = GetWriteData(aCx, value, data);
377 if (NS_WARN_IF(NS_FAILED(rv))) {
378 return rv;
379 }
380
381 SDBRequestWriteParams params;
382 params.data() = data;
383
384 RefPtr<SDBRequest> request = new SDBRequest(this);
385
386 rv = InitiateRequest(request, params);
387 if (NS_WARN_IF(NS_FAILED(rv))) {
388 return rv;
389 }
390
391 request.forget(_retval);
392 return NS_OK;
393 }
394
395 NS_IMETHODIMP
Close(nsISDBRequest ** _retval)396 SDBConnection::Close(nsISDBRequest** _retval) {
397 AssertIsOnOwningThread();
398
399 nsresult rv = CheckState();
400 if (NS_WARN_IF(NS_FAILED(rv))) {
401 return rv;
402 }
403
404 if (!mOpen) {
405 return NS_BASE_STREAM_CLOSED;
406 }
407
408 SDBRequestCloseParams params;
409
410 RefPtr<SDBRequest> request = new SDBRequest(this);
411
412 rv = InitiateRequest(request, params);
413 if (NS_WARN_IF(NS_FAILED(rv))) {
414 return rv;
415 }
416
417 request.forget(_retval);
418 return NS_OK;
419 }
420
421 NS_IMETHODIMP
GetCloseCallback(nsISDBCloseCallback ** aCloseCallback)422 SDBConnection::GetCloseCallback(nsISDBCloseCallback** aCloseCallback) {
423 AssertIsOnOwningThread();
424 MOZ_ASSERT(aCloseCallback);
425
426 NS_IF_ADDREF(*aCloseCallback = mCloseCallback);
427 return NS_OK;
428 }
429
430 NS_IMETHODIMP
SetCloseCallback(nsISDBCloseCallback * aCloseCallback)431 SDBConnection::SetCloseCallback(nsISDBCloseCallback* aCloseCallback) {
432 AssertIsOnOwningThread();
433
434 mCloseCallback = aCloseCallback;
435 return NS_OK;
436 }
437
438 } // namespace mozilla::dom
439