Source code

Revision control

Other Tools

1
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2
/* vim: set ts=8 sts=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 "ActorsChild.h"
8
9
#include "BackgroundChildImpl.h"
10
#include "FileSnapshot.h"
11
#include "IDBDatabase.h"
12
#include "IDBEvents.h"
13
#include "IDBFactory.h"
14
#include "IDBFileHandle.h"
15
#include "IDBIndex.h"
16
#include "IDBMutableFile.h"
17
#include "IDBObjectStore.h"
18
#include "IDBRequest.h"
19
#include "IDBTransaction.h"
20
#include "IndexedDatabase.h"
21
#include "IndexedDatabaseInlines.h"
22
#include <mozIIPCBlobInputStream.h>
23
#include "mozilla/BasicEvents.h"
24
#include "mozilla/CycleCollectedJSRuntime.h"
25
#include "mozilla/Maybe.h"
26
#include "mozilla/SnappyUncompressInputStream.h"
27
#include "mozilla/TypeTraits.h"
28
#include "mozilla/dom/Element.h"
29
#include "mozilla/dom/Event.h"
30
#include "mozilla/dom/PermissionMessageUtils.h"
31
#include "mozilla/dom/BrowserChild.h"
32
#include "mozilla/dom/indexedDB/PBackgroundIDBDatabaseFileChild.h"
33
#include "mozilla/dom/IPCBlobUtils.h"
34
#include "mozilla/dom/PendingIPCBlobChild.h"
35
#include "mozilla/dom/WorkerPrivate.h"
36
#include "mozilla/dom/WorkerRunnable.h"
37
#include "mozilla/Encoding.h"
38
#include "mozilla/ipc/BackgroundUtils.h"
39
#include "mozilla/TaskQueue.h"
40
#include "nsCOMPtr.h"
41
#include "nsContentUtils.h"
42
#include "nsIAsyncInputStream.h"
43
#include "nsIBFCacheEntry.h"
44
#include "mozilla/dom/Document.h"
45
#include "nsIEventTarget.h"
46
#include "nsIFileStreams.h"
47
#include "nsNetCID.h"
48
#include "nsPIDOMWindow.h"
49
#include "nsThreadUtils.h"
50
#include "nsTraceRefcnt.h"
51
#include "PermissionRequestBase.h"
52
#include "ProfilerHelpers.h"
53
#include "ReportInternalError.h"
54
55
#ifdef DEBUG
56
# include "IndexedDatabaseManager.h"
57
#endif
58
59
#define GC_ON_IPC_MESSAGES 0
60
61
#if defined(DEBUG) || GC_ON_IPC_MESSAGES
62
63
# include "js/GCAPI.h"
64
# include "nsJSEnvironment.h"
65
66
# define BUILD_GC_ON_IPC_MESSAGES
67
68
#endif // DEBUG || GC_ON_IPC_MESSAGES
69
70
namespace mozilla {
71
72
using ipc::PrincipalInfo;
73
74
namespace dom {
75
76
namespace indexedDB {
77
78
namespace {
79
80
/*******************************************************************************
81
* Constants
82
******************************************************************************/
83
84
const uint32_t kFileCopyBufferSize = 32768;
85
86
} // namespace
87
88
/*******************************************************************************
89
* ThreadLocal
90
******************************************************************************/
91
92
ThreadLocal::ThreadLocal(const nsID& aBackgroundChildLoggingId)
93
: mLoggingInfo(aBackgroundChildLoggingId, 1, -1, 1),
94
mCurrentTransaction(nullptr) {
95
MOZ_COUNT_CTOR(mozilla::dom::indexedDB::ThreadLocal);
96
97
// NSID_LENGTH counts the null terminator, SetLength() does not.
98
mLoggingIdString.SetLength(NSID_LENGTH - 1);
99
100
aBackgroundChildLoggingId.ToProvidedString(
101
*reinterpret_cast<char(*)[NSID_LENGTH]>(mLoggingIdString.BeginWriting()));
102
}
103
104
ThreadLocal::~ThreadLocal() {
105
MOZ_COUNT_DTOR(mozilla::dom::indexedDB::ThreadLocal);
106
}
107
108
/*******************************************************************************
109
* Helpers
110
******************************************************************************/
111
112
namespace {
113
114
void MaybeCollectGarbageOnIPCMessage() {
115
#ifdef BUILD_GC_ON_IPC_MESSAGES
116
static const bool kCollectGarbageOnIPCMessages =
117
# if GC_ON_IPC_MESSAGES
118
true;
119
# else
120
false;
121
# endif // GC_ON_IPC_MESSAGES
122
123
if (!kCollectGarbageOnIPCMessages) {
124
return;
125
}
126
127
static bool haveWarnedAboutGC = false;
128
static bool haveWarnedAboutNonMainThread = false;
129
130
if (!haveWarnedAboutGC) {
131
haveWarnedAboutGC = true;
132
NS_WARNING("IndexedDB child actor GC debugging enabled!");
133
}
134
135
if (!NS_IsMainThread()) {
136
if (!haveWarnedAboutNonMainThread) {
137
haveWarnedAboutNonMainThread = true;
138
NS_WARNING("Don't know how to GC on a non-main thread yet.");
139
}
140
return;
141
}
142
143
nsJSContext::GarbageCollectNow(JS::GCReason::DOM_IPC);
144
nsJSContext::CycleCollectNow();
145
#endif // BUILD_GC_ON_IPC_MESSAGES
146
}
147
148
class MOZ_STACK_CLASS AutoSetCurrentTransaction final {
149
typedef mozilla::ipc::BackgroundChildImpl BackgroundChildImpl;
150
151
IDBTransaction* const mTransaction;
152
IDBTransaction* mPreviousTransaction;
153
ThreadLocal* mThreadLocal;
154
155
public:
156
AutoSetCurrentTransaction(const AutoSetCurrentTransaction&) = delete;
157
AutoSetCurrentTransaction(AutoSetCurrentTransaction&&) = delete;
158
AutoSetCurrentTransaction& operator=(const AutoSetCurrentTransaction&) =
159
delete;
160
AutoSetCurrentTransaction& operator=(AutoSetCurrentTransaction&&) = delete;
161
162
explicit AutoSetCurrentTransaction(IDBTransaction* aTransaction)
163
: mTransaction(aTransaction),
164
mPreviousTransaction(nullptr),
165
mThreadLocal(nullptr) {
166
if (aTransaction) {
167
BackgroundChildImpl::ThreadLocal* threadLocal =
168
BackgroundChildImpl::GetThreadLocalForCurrentThread();
169
MOZ_ASSERT(threadLocal);
170
171
// Hang onto this for resetting later.
172
mThreadLocal = threadLocal->mIndexedDBThreadLocal;
173
MOZ_ASSERT(mThreadLocal);
174
175
// Save the current value.
176
mPreviousTransaction = mThreadLocal->GetCurrentTransaction();
177
178
// Set the new value.
179
mThreadLocal->SetCurrentTransaction(aTransaction);
180
}
181
}
182
183
~AutoSetCurrentTransaction() {
184
MOZ_ASSERT_IF(mThreadLocal, mTransaction);
185
MOZ_ASSERT_IF(mThreadLocal,
186
mThreadLocal->GetCurrentTransaction() == mTransaction);
187
188
if (mThreadLocal) {
189
// Reset old value.
190
mThreadLocal->SetCurrentTransaction(mPreviousTransaction);
191
}
192
}
193
194
IDBTransaction* Transaction() const { return mTransaction; }
195
};
196
197
class MOZ_STACK_CLASS ResultHelper final : public IDBRequest::ResultCallback {
198
IDBRequest* const mRequest;
199
AutoSetCurrentTransaction mAutoTransaction;
200
201
union {
202
IDBDatabase* mDatabase;
203
IDBCursor* mCursor;
204
IDBMutableFile* mMutableFile;
205
StructuredCloneReadInfo* mStructuredClone;
206
const nsTArray<StructuredCloneReadInfo>* mStructuredCloneArray;
207
const Key* mKey;
208
const nsTArray<Key>* mKeyArray;
209
const JS::Value* mJSVal;
210
const JS::Handle<JS::Value>* mJSValHandle;
211
} mResult;
212
213
enum {
214
ResultTypeDatabase,
215
ResultTypeCursor,
216
ResultTypeMutableFile,
217
ResultTypeStructuredClone,
218
ResultTypeStructuredCloneArray,
219
ResultTypeKey,
220
ResultTypeKeyArray,
221
ResultTypeJSVal,
222
ResultTypeJSValHandle,
223
} mResultType;
224
225
public:
226
ResultHelper(IDBRequest* aRequest, IDBTransaction* aTransaction,
227
IDBDatabase* aResult)
228
: mRequest(aRequest),
229
mAutoTransaction(aTransaction),
230
mResultType(ResultTypeDatabase) {
231
MOZ_ASSERT(aRequest);
232
MOZ_ASSERT(aResult);
233
234
mResult.mDatabase = aResult;
235
}
236
237
ResultHelper(IDBRequest* aRequest, IDBTransaction* aTransaction,
238
IDBCursor* aResult)
239
: mRequest(aRequest),
240
mAutoTransaction(aTransaction),
241
mResultType(ResultTypeCursor) {
242
MOZ_ASSERT(aRequest);
243
244
mResult.mCursor = aResult;
245
}
246
247
ResultHelper(IDBRequest* aRequest, IDBTransaction* aTransaction,
248
IDBMutableFile* aResult)
249
: mRequest(aRequest),
250
mAutoTransaction(aTransaction),
251
mResultType(ResultTypeMutableFile) {
252
MOZ_ASSERT(aRequest);
253
254
mResult.mMutableFile = aResult;
255
}
256
257
ResultHelper(IDBRequest* aRequest, IDBTransaction* aTransaction,
258
StructuredCloneReadInfo* aResult)
259
: mRequest(aRequest),
260
mAutoTransaction(aTransaction),
261
mResultType(ResultTypeStructuredClone) {
262
MOZ_ASSERT(aRequest);
263
MOZ_ASSERT(aResult);
264
265
mResult.mStructuredClone = aResult;
266
}
267
268
ResultHelper(IDBRequest* aRequest, IDBTransaction* aTransaction,
269
const nsTArray<StructuredCloneReadInfo>* aResult)
270
: mRequest(aRequest),
271
mAutoTransaction(aTransaction),
272
mResultType(ResultTypeStructuredCloneArray) {
273
MOZ_ASSERT(aRequest);
274
MOZ_ASSERT(aResult);
275
276
mResult.mStructuredCloneArray = aResult;
277
}
278
279
ResultHelper(IDBRequest* aRequest, IDBTransaction* aTransaction,
280
const Key* aResult)
281
: mRequest(aRequest),
282
mAutoTransaction(aTransaction),
283
mResultType(ResultTypeKey) {
284
MOZ_ASSERT(aRequest);
285
MOZ_ASSERT(aResult);
286
287
mResult.mKey = aResult;
288
}
289
290
ResultHelper(IDBRequest* aRequest, IDBTransaction* aTransaction,
291
const nsTArray<Key>* aResult)
292
: mRequest(aRequest),
293
mAutoTransaction(aTransaction),
294
mResultType(ResultTypeKeyArray) {
295
MOZ_ASSERT(aRequest);
296
MOZ_ASSERT(aResult);
297
298
mResult.mKeyArray = aResult;
299
}
300
301
ResultHelper(IDBRequest* aRequest, IDBTransaction* aTransaction,
302
const JS::Value* aResult)
303
: mRequest(aRequest),
304
mAutoTransaction(aTransaction),
305
mResultType(ResultTypeJSVal) {
306
MOZ_ASSERT(aRequest);
307
MOZ_ASSERT(!aResult->isGCThing());
308
309
mResult.mJSVal = aResult;
310
}
311
312
ResultHelper(IDBRequest* aRequest, IDBTransaction* aTransaction,
313
const JS::Handle<JS::Value>* aResult)
314
: mRequest(aRequest),
315
mAutoTransaction(aTransaction),
316
mResultType(ResultTypeJSValHandle) {
317
MOZ_ASSERT(aRequest);
318
319
mResult.mJSValHandle = aResult;
320
}
321
322
IDBRequest* Request() const { return mRequest; }
323
324
IDBTransaction* Transaction() const { return mAutoTransaction.Transaction(); }
325
326
virtual nsresult GetResult(JSContext* aCx,
327
JS::MutableHandle<JS::Value> aResult) override {
328
MOZ_ASSERT(aCx);
329
MOZ_ASSERT(mRequest);
330
331
switch (mResultType) {
332
case ResultTypeDatabase:
333
return GetResult(aCx, mResult.mDatabase, aResult);
334
335
case ResultTypeCursor:
336
return GetResult(aCx, mResult.mCursor, aResult);
337
338
case ResultTypeMutableFile:
339
return GetResult(aCx, mResult.mMutableFile, aResult);
340
341
case ResultTypeStructuredClone:
342
return GetResult(aCx, mResult.mStructuredClone, aResult);
343
344
case ResultTypeStructuredCloneArray:
345
return GetResult(aCx, mResult.mStructuredCloneArray, aResult);
346
347
case ResultTypeKey:
348
return GetResult(aCx, mResult.mKey, aResult);
349
350
case ResultTypeKeyArray:
351
return GetResult(aCx, mResult.mKeyArray, aResult);
352
353
case ResultTypeJSVal:
354
aResult.set(*mResult.mJSVal);
355
return NS_OK;
356
357
case ResultTypeJSValHandle:
358
aResult.set(*mResult.mJSValHandle);
359
return NS_OK;
360
361
default:
362
MOZ_CRASH("Unknown result type!");
363
}
364
365
MOZ_CRASH("Should never get here!");
366
}
367
368
private:
369
template <class T>
370
typename EnableIf<IsSame<T, IDBDatabase>::value ||
371
IsSame<T, IDBCursor>::value ||
372
IsSame<T, IDBMutableFile>::value,
373
nsresult>::Type
374
GetResult(JSContext* aCx, T* aDOMObject,
375
JS::MutableHandle<JS::Value> aResult) {
376
if (!aDOMObject) {
377
aResult.setNull();
378
return NS_OK;
379
}
380
381
const bool ok = GetOrCreateDOMReflector(aCx, aDOMObject, aResult);
382
if (NS_WARN_IF(!ok)) {
383
IDB_REPORT_INTERNAL_ERR();
384
return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
385
}
386
387
return NS_OK;
388
}
389
390
nsresult GetResult(JSContext* aCx, StructuredCloneReadInfo* aCloneInfo,
391
JS::MutableHandle<JS::Value> aResult) {
392
const bool ok = IDBObjectStore::DeserializeValue(aCx, *aCloneInfo, aResult);
393
394
if (NS_WARN_IF(!ok)) {
395
return NS_ERROR_DOM_DATA_CLONE_ERR;
396
}
397
398
return NS_OK;
399
}
400
401
nsresult GetResult(JSContext* aCx,
402
const nsTArray<StructuredCloneReadInfo>* aCloneInfos,
403
JS::MutableHandle<JS::Value> aResult) {
404
JS::Rooted<JSObject*> array(aCx, JS_NewArrayObject(aCx, 0));
405
if (NS_WARN_IF(!array)) {
406
IDB_REPORT_INTERNAL_ERR();
407
return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
408
}
409
410
if (!aCloneInfos->IsEmpty()) {
411
const uint32_t count = aCloneInfos->Length();
412
413
if (NS_WARN_IF(!JS_SetArrayLength(aCx, array, count))) {
414
IDB_REPORT_INTERNAL_ERR();
415
return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
416
}
417
418
for (uint32_t index = 0; index < count; index++) {
419
auto& cloneInfo =
420
const_cast<StructuredCloneReadInfo&>(aCloneInfos->ElementAt(index));
421
422
JS::Rooted<JS::Value> value(aCx);
423
424
const nsresult rv = GetResult(aCx, &cloneInfo, &value);
425
if (NS_WARN_IF(NS_FAILED(rv))) {
426
return rv;
427
}
428
429
if (NS_WARN_IF(!JS_DefineElement(aCx, array, index, value,
430
JSPROP_ENUMERATE))) {
431
IDB_REPORT_INTERNAL_ERR();
432
return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
433
}
434
}
435
}
436
437
aResult.setObject(*array);
438
return NS_OK;
439
}
440
441
nsresult GetResult(JSContext* aCx, const Key* aKey,
442
JS::MutableHandle<JS::Value> aResult) {
443
const nsresult rv = aKey->ToJSVal(aCx, aResult);
444
if (NS_WARN_IF(NS_FAILED(rv))) {
445
return rv;
446
}
447
return NS_OK;
448
}
449
450
nsresult GetResult(JSContext* aCx, const nsTArray<Key>* aKeys,
451
JS::MutableHandle<JS::Value> aResult) {
452
JS::Rooted<JSObject*> array(aCx, JS_NewArrayObject(aCx, 0));
453
if (NS_WARN_IF(!array)) {
454
IDB_REPORT_INTERNAL_ERR();
455
return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
456
}
457
458
if (!aKeys->IsEmpty()) {
459
const uint32_t count = aKeys->Length();
460
461
if (NS_WARN_IF(!JS_SetArrayLength(aCx, array, count))) {
462
IDB_REPORT_INTERNAL_ERR();
463
return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
464
}
465
466
for (uint32_t index = 0; index < count; index++) {
467
const Key& key = aKeys->ElementAt(index);
468
MOZ_ASSERT(!key.IsUnset());
469
470
JS::Rooted<JS::Value> value(aCx);
471
472
const nsresult rv = GetResult(aCx, &key, &value);
473
if (NS_WARN_IF(NS_FAILED(rv))) {
474
return rv;
475
}
476
477
if (NS_WARN_IF(!JS_DefineElement(aCx, array, index, value,
478
JSPROP_ENUMERATE))) {
479
IDB_REPORT_INTERNAL_ERR();
480
return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
481
}
482
}
483
}
484
485
aResult.setObject(*array);
486
return NS_OK;
487
}
488
};
489
490
class PermissionRequestMainProcessHelper final : public PermissionRequestBase {
491
BackgroundFactoryRequestChild* mActor;
492
RefPtr<IDBFactory> mFactory;
493
494
public:
495
PermissionRequestMainProcessHelper(BackgroundFactoryRequestChild* aActor,
496
IDBFactory* aFactory,
497
Element* aOwnerElement,
498
nsIPrincipal* aPrincipal)
499
: PermissionRequestBase(aOwnerElement, aPrincipal),
500
mActor(aActor),
501
mFactory(aFactory) {
502
MOZ_ASSERT(aActor);
503
MOZ_ASSERT(aFactory);
504
aActor->AssertIsOnOwningThread();
505
}
506
507
protected:
508
~PermissionRequestMainProcessHelper() = default;
509
510
private:
511
virtual void OnPromptComplete(PermissionValue aPermissionValue) override;
512
};
513
514
auto DeserializeStructuredCloneFiles(
515
IDBDatabase* aDatabase,
516
const nsTArray<SerializedStructuredCloneFile>& aSerializedFiles,
517
bool aForPreprocess) {
518
MOZ_ASSERT_IF(aForPreprocess, aSerializedFiles.Length() == 1);
519
520
const auto count = aSerializedFiles.Length();
521
auto files = nsTArray<StructuredCloneFile>(count);
522
523
for (const auto& serializedFile : aSerializedFiles) {
524
MOZ_ASSERT_IF(aForPreprocess, serializedFile.type() ==
525
StructuredCloneFile::eStructuredClone);
526
527
const BlobOrMutableFile& blobOrMutableFile = serializedFile.file();
528
529
switch (serializedFile.type()) {
530
case StructuredCloneFile::eBlob: {
531
MOZ_ASSERT(blobOrMutableFile.type() == BlobOrMutableFile::TIPCBlob);
532
533
const IPCBlob& ipcBlob = blobOrMutableFile.get_IPCBlob();
534
535
const RefPtr<BlobImpl> blobImpl = IPCBlobUtils::Deserialize(ipcBlob);
536
MOZ_ASSERT(blobImpl);
537
538
RefPtr<Blob> blob = Blob::Create(aDatabase->GetOwnerGlobal(), blobImpl);
539
MOZ_ASSERT(blob);
540
541
files.EmplaceBack(StructuredCloneFile::eBlob, std::move(blob));
542
543
break;
544
}
545
546
case StructuredCloneFile::eMutableFile: {
547
MOZ_ASSERT(blobOrMutableFile.type() == BlobOrMutableFile::Tnull_t ||
548
blobOrMutableFile.type() ==
549
BlobOrMutableFile::TPBackgroundMutableFileChild);
550
551
switch (blobOrMutableFile.type()) {
552
case BlobOrMutableFile::Tnull_t: {
553
files.EmplaceBack(StructuredCloneFile::eMutableFile);
554
555
break;
556
}
557
558
case BlobOrMutableFile::TPBackgroundMutableFileChild: {
559
auto* const actor = static_cast<BackgroundMutableFileChild*>(
560
blobOrMutableFile.get_PBackgroundMutableFileChild());
561
MOZ_ASSERT(actor);
562
563
actor->EnsureDOMObject();
564
565
auto* const mutableFile =
566
static_cast<IDBMutableFile*>(actor->GetDOMObject());
567
MOZ_ASSERT(mutableFile);
568
569
files.EmplaceBack(mutableFile);
570
571
actor->ReleaseDOMObject();
572
573
break;
574
}
575
576
default:
577
MOZ_CRASH("Should never get here!");
578
}
579
580
break;
581
}
582
583
case StructuredCloneFile::eStructuredClone: {
584
if (aForPreprocess) {
585
MOZ_ASSERT(blobOrMutableFile.type() == BlobOrMutableFile::TIPCBlob);
586
587
const IPCBlob& ipcBlob = blobOrMutableFile.get_IPCBlob();
588
589
const RefPtr<BlobImpl> blobImpl = IPCBlobUtils::Deserialize(ipcBlob);
590
MOZ_ASSERT(blobImpl);
591
592
RefPtr<Blob> blob =
593
Blob::Create(aDatabase->GetOwnerGlobal(), blobImpl);
594
MOZ_ASSERT(blob);
595
596
files.EmplaceBack(StructuredCloneFile::eStructuredClone,
597
std::move(blob));
598
} else {
599
MOZ_ASSERT(blobOrMutableFile.type() == BlobOrMutableFile::Tnull_t);
600
601
files.EmplaceBack(StructuredCloneFile::eStructuredClone);
602
}
603
604
break;
605
}
606
607
case StructuredCloneFile::eWasmBytecode:
608
case StructuredCloneFile::eWasmCompiled: {
609
MOZ_ASSERT(blobOrMutableFile.type() == BlobOrMutableFile::Tnull_t);
610
611
files.EmplaceBack(serializedFile.type());
612
613
// Don't set mBlob, support for storing WebAssembly.Modules has been
614
// removed in bug 1469395. Support for de-serialization of
615
// WebAssembly.Modules has been removed in bug 1561876. Full removal
616
// is tracked in bug 1487479.
617
618
break;
619
}
620
621
default:
622
MOZ_CRASH("Should never get here!");
623
}
624
}
625
626
return files;
627
}
628
629
StructuredCloneReadInfo DeserializeStructuredCloneReadInfo(
630
SerializedStructuredCloneReadInfo&& aSerialized, IDBDatabase* aDatabase) {
631
return StructuredCloneReadInfo{
632
std::move(aSerialized.data().data),
633
DeserializeStructuredCloneFiles(aDatabase, aSerialized.files(),
634
/* aForPreprocess */ false),
635
aDatabase, aSerialized.hasPreprocessInfo()};
636
}
637
638
// TODO: Remove duplication between DispatchErrorEvent and DispatchSucessEvent.
639
640
void DispatchErrorEvent(IDBRequest* aRequest, nsresult aErrorCode,
641
IDBTransaction* aTransaction = nullptr,
642
Event* aEvent = nullptr) {
643
MOZ_ASSERT(aRequest);
644
aRequest->AssertIsOnOwningThread();
645
MOZ_ASSERT(NS_FAILED(aErrorCode));
646
MOZ_ASSERT(NS_ERROR_GET_MODULE(aErrorCode) == NS_ERROR_MODULE_DOM_INDEXEDDB);
647
648
AUTO_PROFILER_LABEL("IndexedDB:DispatchErrorEvent", DOM);
649
650
const RefPtr<IDBRequest> request = aRequest;
651
const RefPtr<IDBTransaction> transaction = aTransaction;
652
653
request->SetError(aErrorCode);
654
655
RefPtr<Event> errorEvent;
656
if (!aEvent) {
657
// Make an error event and fire it at the target.
658
errorEvent = CreateGenericEvent(request, nsDependentString(kErrorEventType),
659
eDoesBubble, eCancelable);
660
MOZ_ASSERT(errorEvent);
661
662
aEvent = errorEvent;
663
}
664
665
Maybe<AutoSetCurrentTransaction> asct;
666
if (aTransaction) {
667
asct.emplace(aTransaction);
668
}
669
670
if (transaction && transaction->IsInactive()) {
671
transaction->TransitionToActive();
672
}
673
674
if (transaction) {
675
IDB_LOG_MARK_CHILD_TRANSACTION_REQUEST(
676
"Firing %s event with error 0x%x", "%s (0x%x)",
677
transaction->LoggingSerialNumber(), request->LoggingSerialNumber(),
678
IDB_LOG_STRINGIFY(aEvent, kErrorEventType), aErrorCode);
679
} else {
680
IDB_LOG_MARK_CHILD_REQUEST("Firing %s event with error 0x%x", "%s (0x%x)",
681
request->LoggingSerialNumber(),
682
IDB_LOG_STRINGIFY(aEvent, kErrorEventType),
683
aErrorCode);
684
}
685
686
IgnoredErrorResult rv;
687
const bool doDefault =
688
request->DispatchEvent(*aEvent, CallerType::System, rv);
689
if (NS_WARN_IF(rv.Failed())) {
690
return;
691
}
692
693
MOZ_ASSERT(!transaction || transaction->IsActive() ||
694
transaction->IsAborted());
695
696
if (transaction && transaction->IsActive()) {
697
transaction->TransitionToInactive();
698
699
// Do not abort the transaction here if this request is failed due to the
700
// abortion of its transaction to ensure that the correct error cause of
701
// the abort event be set in IDBTransaction::FireCompleteOrAbortEvents()
702
// later.
703
if (aErrorCode != NS_ERROR_DOM_INDEXEDDB_ABORT_ERR) {
704
WidgetEvent* const internalEvent = aEvent->WidgetEventPtr();
705
MOZ_ASSERT(internalEvent);
706
707
if (internalEvent->mFlags.mExceptionWasRaised) {
708
transaction->Abort(NS_ERROR_DOM_INDEXEDDB_ABORT_ERR);
709
} else if (doDefault) {
710
transaction->Abort(request);
711
}
712
}
713
}
714
}
715
716
void DispatchSuccessEvent(ResultHelper* aResultHelper,
717
Event* aEvent = nullptr) {
718
MOZ_ASSERT(aResultHelper);
719
720
AUTO_PROFILER_LABEL("IndexedDB:DispatchSuccessEvent", DOM);
721
722
const RefPtr<IDBRequest> request = aResultHelper->Request();
723
MOZ_ASSERT(request);
724
request->AssertIsOnOwningThread();
725
726
const RefPtr<IDBTransaction> transaction = aResultHelper->Transaction();
727
728
if (transaction && transaction->IsAborted()) {
729
DispatchErrorEvent(request, transaction->AbortCode(), transaction);
730
return;
731
}
732
733
RefPtr<Event> successEvent;
734
if (!aEvent) {
735
successEvent =
736
CreateGenericEvent(request, nsDependentString(kSuccessEventType),
737
eDoesNotBubble, eNotCancelable);
738
MOZ_ASSERT(successEvent);
739
740
aEvent = successEvent;
741
}
742
743
request->SetResultCallback(aResultHelper);
744
745
MOZ_ASSERT(aEvent);
746
747
if (transaction && transaction->IsInactive()) {
748
transaction->TransitionToActive();
749
}
750
751
if (transaction) {
752
IDB_LOG_MARK_CHILD_TRANSACTION_REQUEST(
753
"Firing %s event", "%s", transaction->LoggingSerialNumber(),
754
request->LoggingSerialNumber(),
755
IDB_LOG_STRINGIFY(aEvent, kSuccessEventType));
756
} else {
757
IDB_LOG_MARK_CHILD_REQUEST("Firing %s event", "%s",
758
request->LoggingSerialNumber(),
759
IDB_LOG_STRINGIFY(aEvent, kSuccessEventType));
760
}
761
762
MOZ_ASSERT_IF(transaction,
763
transaction->IsActive() && !transaction->IsAborted());
764
765
IgnoredErrorResult rv;
766
request->DispatchEvent(*aEvent, rv);
767
if (NS_WARN_IF(rv.Failed())) {
768
return;
769
}
770
771
WidgetEvent* const internalEvent = aEvent->WidgetEventPtr();
772
MOZ_ASSERT(internalEvent);
773
774
if (transaction && transaction->IsActive()) {
775
transaction->TransitionToInactive();
776
777
if (internalEvent->mFlags.mExceptionWasRaised) {
778
transaction->Abort(NS_ERROR_DOM_INDEXEDDB_ABORT_ERR);
779
} else {
780
// To handle upgrade transaction.
781
transaction->Run();
782
}
783
}
784
}
785
786
PRFileDesc* GetFileDescriptorFromStream(nsIInputStream* aStream) {
787
MOZ_ASSERT(aStream);
788
789
const nsCOMPtr<nsIFileMetadata> fileMetadata = do_QueryInterface(aStream);
790
if (NS_WARN_IF(!fileMetadata)) {
791
return nullptr;
792
}
793
794
PRFileDesc* fileDesc;
795
const nsresult rv = fileMetadata->GetFileDescriptor(&fileDesc);
796
if (NS_WARN_IF(NS_FAILED(rv))) {
797
return nullptr;
798
}
799
800
MOZ_ASSERT(fileDesc);
801
802
return fileDesc;
803
}
804
805
class WorkerPermissionChallenge;
806
807
// This class calles WorkerPermissionChallenge::OperationCompleted() in the
808
// worker thread.
809
class WorkerPermissionOperationCompleted final : public WorkerControlRunnable {
810
RefPtr<WorkerPermissionChallenge> mChallenge;
811
812
public:
813
WorkerPermissionOperationCompleted(WorkerPrivate* aWorkerPrivate,
814
WorkerPermissionChallenge* aChallenge)
815
: WorkerControlRunnable(aWorkerPrivate, WorkerThreadUnchangedBusyCount),
816
mChallenge(aChallenge) {
817
MOZ_ASSERT(NS_IsMainThread());
818
}
819
820
virtual bool WorkerRun(JSContext* aCx,
821
WorkerPrivate* aWorkerPrivate) override;
822
};
823
824
// This class used to do prompting in the main thread and main process.
825
class WorkerPermissionRequest final : public PermissionRequestBase {
826
RefPtr<WorkerPermissionChallenge> mChallenge;
827
828
public:
829
WorkerPermissionRequest(Element* aElement, nsIPrincipal* aPrincipal,
830
WorkerPermissionChallenge* aChallenge)
831
: PermissionRequestBase(aElement, aPrincipal), mChallenge(aChallenge) {
832
MOZ_ASSERT(XRE_IsParentProcess());
833
MOZ_ASSERT(NS_IsMainThread());
834
MOZ_ASSERT(aChallenge);
835
}
836
837
private:
838
~WorkerPermissionRequest() { MOZ_ASSERT(NS_IsMainThread()); }
839
840
virtual void OnPromptComplete(PermissionValue aPermissionValue) override;
841
};
842
843
class WorkerPermissionChallenge final : public Runnable {
844
public:
845
WorkerPermissionChallenge(WorkerPrivate* aWorkerPrivate,
846
BackgroundFactoryRequestChild* aActor,
847
IDBFactory* aFactory,
848
const PrincipalInfo& aPrincipalInfo)
849
: Runnable("indexedDB::WorkerPermissionChallenge"),
850
mWorkerPrivate(aWorkerPrivate),
851
mActor(aActor),
852
mFactory(aFactory),
853
mPrincipalInfo(aPrincipalInfo) {
854
MOZ_ASSERT(mWorkerPrivate);
855
MOZ_ASSERT(aActor);
856
MOZ_ASSERT(aFactory);
857
mWorkerPrivate->AssertIsOnWorkerThread();
858
}
859
860
bool Dispatch() {
861
mWorkerPrivate->AssertIsOnWorkerThread();
862
if (NS_WARN_IF(!mWorkerPrivate->ModifyBusyCountFromWorker(true))) {
863
return false;
864
}
865
866
if (NS_WARN_IF(NS_FAILED(mWorkerPrivate->DispatchToMainThread(this)))) {
867
mWorkerPrivate->ModifyBusyCountFromWorker(false);
868
return false;
869
}
870
871
return true;
872
}
873
874
NS_IMETHOD
875
Run() override {
876
const bool completed = RunInternal();
877
if (completed) {
878
OperationCompleted();
879
}
880
881
return NS_OK;
882
}
883
884
void OperationCompleted() {
885
if (NS_IsMainThread()) {
886
const RefPtr<WorkerPermissionOperationCompleted> runnable =
887
new WorkerPermissionOperationCompleted(mWorkerPrivate, this);
888
889
MOZ_ALWAYS_TRUE(runnable->Dispatch());
890
return;
891
}
892
893
MOZ_ASSERT(mActor);
894
mActor->AssertIsOnOwningThread();
895
896
MaybeCollectGarbageOnIPCMessage();
897
898
RefPtr<IDBFactory> factory;
899
mFactory.swap(factory);
900
901
mActor->SendPermissionRetry();
902
mActor = nullptr;
903
904
mWorkerPrivate->AssertIsOnWorkerThread();
905
mWorkerPrivate->ModifyBusyCountFromWorker(false);
906
}
907
908
private:
909
bool RunInternal() {
910
MOZ_ASSERT(NS_IsMainThread());
911
912
// Walk up to our containing page
913
WorkerPrivate* wp = mWorkerPrivate;
914
while (wp->GetParent()) {
915
wp = wp->GetParent();
916
}
917
918
nsPIDOMWindowInner* const window = wp->GetWindow();
919
if (!window) {
920
return true;
921
}
922
923
nsresult rv;
924
const nsCOMPtr<nsIPrincipal> principal =
925
mozilla::ipc::PrincipalInfoToPrincipal(mPrincipalInfo, &rv);
926
if (NS_WARN_IF(NS_FAILED(rv))) {
927
return true;
928
}
929
930
if (XRE_IsParentProcess()) {
931
const nsCOMPtr<Element> ownerElement =
932
do_QueryInterface(window->GetChromeEventHandler());
933
if (NS_WARN_IF(!ownerElement)) {
934
return true;
935
}
936
937
RefPtr<WorkerPermissionRequest> helper =
938
new WorkerPermissionRequest(ownerElement, principal, this);
939
940
PermissionRequestBase::PermissionValue permission;
941
if (NS_WARN_IF(NS_FAILED(helper->PromptIfNeeded(&permission)))) {
942
return true;
943
}
944
945
MOZ_ASSERT(permission == PermissionRequestBase::kPermissionAllowed ||
946
permission == PermissionRequestBase::kPermissionDenied ||
947
permission == PermissionRequestBase::kPermissionPrompt);
948
949
return permission != PermissionRequestBase::kPermissionPrompt;
950
}
951
952
BrowserChild* browserChild = BrowserChild::GetFrom(window);
953
MOZ_ASSERT(browserChild);
954
955
IPC::Principal ipcPrincipal(principal);
956
957
RefPtr<WorkerPermissionChallenge> self(this);
958
browserChild->SendIndexedDBPermissionRequest(ipcPrincipal)
959
->Then(
960
GetCurrentThreadSerialEventTarget(), __func__,
961
[self](const uint32_t& aPermission) { self->OperationCompleted(); },
962
[](const mozilla::ipc::ResponseRejectReason) {});
963
return false;
964
}
965
966
private:
967
WorkerPrivate* const mWorkerPrivate;
968
BackgroundFactoryRequestChild* mActor;
969
RefPtr<IDBFactory> mFactory;
970
const PrincipalInfo mPrincipalInfo;
971
};
972
973
void WorkerPermissionRequest::OnPromptComplete(
974
PermissionValue aPermissionValue) {
975
MOZ_ASSERT(NS_IsMainThread());
976
mChallenge->OperationCompleted();
977
}
978
979
bool WorkerPermissionOperationCompleted::WorkerRun(
980
JSContext* aCx, WorkerPrivate* aWorkerPrivate) {
981
aWorkerPrivate->AssertIsOnWorkerThread();
982
mChallenge->OperationCompleted();
983
return true;
984
}
985
986
class MOZ_STACK_CLASS AutoSetCurrentFileHandle final {
987
typedef mozilla::ipc::BackgroundChildImpl BackgroundChildImpl;
988
989
IDBFileHandle* const mFileHandle;
990
IDBFileHandle* mPreviousFileHandle;
991
IDBFileHandle** mThreadLocalSlot;
992
993
public:
994
explicit AutoSetCurrentFileHandle(IDBFileHandle* aFileHandle)
995
: mFileHandle(aFileHandle),
996
mPreviousFileHandle(nullptr),
997
mThreadLocalSlot(nullptr) {
998
if (aFileHandle) {
999
BackgroundChildImpl::ThreadLocal* threadLocal =
1000
BackgroundChildImpl::GetThreadLocalForCurrentThread();
1001
MOZ_ASSERT(threadLocal);
1002
1003
// Hang onto this location for resetting later.
1004
mThreadLocalSlot = &threadLocal->mCurrentFileHandle;
1005
1006
// Save the current value.
1007
mPreviousFileHandle = *mThreadLocalSlot;
1008
1009
// Set the new value.
1010
*mThreadLocalSlot = aFileHandle;
1011
}
1012
}
1013
1014
~AutoSetCurrentFileHandle() {
1015
MOZ_ASSERT_IF(mThreadLocalSlot, mFileHandle);
1016
MOZ_ASSERT_IF(mThreadLocalSlot, *mThreadLocalSlot == mFileHandle);
1017
1018
if (mThreadLocalSlot) {
1019
// Reset old value.
1020
*mThreadLocalSlot = mPreviousFileHandle;
1021
}
1022
}
1023
1024
IDBFileHandle* FileHandle() const { return mFileHandle; }
1025
};
1026
1027
class MOZ_STACK_CLASS FileHandleResultHelper final
1028
: public IDBFileRequest::ResultCallback {
1029
IDBFileRequest* const mFileRequest;
1030
AutoSetCurrentFileHandle mAutoFileHandle;
1031
1032
union {
1033
File* mFile;
1034
const nsCString* mString;
1035
const FileRequestMetadata* mMetadata;
1036
const JS::Handle<JS::Value>* mJSValHandle;
1037
} mResult;
1038
1039
enum {
1040
ResultTypeFile,
1041
ResultTypeString,
1042
ResultTypeMetadata,
1043
ResultTypeJSValHandle,
1044
} mResultType;
1045
1046
public:
1047
FileHandleResultHelper(IDBFileRequest* aFileRequest,
1048
IDBFileHandle* aFileHandle, File* aResult)
1049
: mFileRequest(aFileRequest),
1050
mAutoFileHandle(aFileHandle),
1051
mResultType(ResultTypeFile) {
1052
MOZ_ASSERT(aFileRequest);
1053
MOZ_ASSERT(aFileHandle);
1054
MOZ_ASSERT(aResult);
1055
1056
mResult.mFile = aResult;
1057
}
1058
1059
FileHandleResultHelper(IDBFileRequest* aFileRequest,
1060
IDBFileHandle* aFileHandle, const nsCString* aResult)
1061
: mFileRequest(aFileRequest),
1062
mAutoFileHandle(aFileHandle),
1063
mResultType(ResultTypeString) {
1064
MOZ_ASSERT(aFileRequest);
1065
MOZ_ASSERT(aFileHandle);
1066
MOZ_ASSERT(aResult);
1067
1068
mResult.mString = aResult;
1069
}
1070
1071
FileHandleResultHelper(IDBFileRequest* aFileRequest,
1072
IDBFileHandle* aFileHandle,
1073
const FileRequestMetadata* aResult)
1074
: mFileRequest(aFileRequest),
1075
mAutoFileHandle(aFileHandle),
1076
mResultType(ResultTypeMetadata) {
1077
MOZ_ASSERT(aFileRequest);
1078
MOZ_ASSERT(aFileHandle);
1079
MOZ_ASSERT(aResult);
1080
1081
mResult.mMetadata = aResult;
1082
}
1083
1084
FileHandleResultHelper(IDBFileRequest* aFileRequest,
1085
IDBFileHandle* aFileHandle,
1086
const JS::Handle<JS::Value>* aResult)
1087
: mFileRequest(aFileRequest),
1088
mAutoFileHandle(aFileHandle),
1089
mResultType(ResultTypeJSValHandle) {
1090
MOZ_ASSERT(aFileRequest);
1091
MOZ_ASSERT(aFileHandle);
1092
MOZ_ASSERT(aResult);
1093
1094
mResult.mJSValHandle = aResult;
1095
}
1096
1097
IDBFileRequest* FileRequest() const { return mFileRequest; }
1098
1099
IDBFileHandle* FileHandle() const { return mAutoFileHandle.FileHandle(); }
1100
1101
virtual nsresult GetResult(JSContext* aCx,
1102
JS::MutableHandle<JS::Value> aResult) override {
1103
MOZ_ASSERT(aCx);
1104
MOZ_ASSERT(mFileRequest);
1105
1106
switch (mResultType) {
1107
case ResultTypeFile:
1108
return GetResult(aCx, mResult.mFile, aResult);
1109
1110
case ResultTypeString:
1111
return GetResult(aCx, mResult.mString, aResult);
1112
1113
case ResultTypeMetadata:
1114
return GetResult(aCx, mResult.mMetadata, aResult);
1115
1116
case ResultTypeJSValHandle:
1117
aResult.set(*mResult.mJSValHandle);
1118
return NS_OK;
1119
1120
default:
1121
MOZ_CRASH("Unknown result type!");
1122
}
1123
1124
MOZ_CRASH("Should never get here!");
1125
}
1126
1127
private:
1128
nsresult GetResult(JSContext* aCx, File* aFile,
1129
JS::MutableHandle<JS::Value> aResult) {
1130
const bool ok = GetOrCreateDOMReflector(aCx, aFile, aResult);
1131
if (NS_WARN_IF(!ok)) {
1132
return NS_ERROR_DOM_FILEHANDLE_UNKNOWN_ERR;
1133
}
1134
1135
return NS_OK;
1136
}
1137
1138
nsresult GetResult(JSContext* aCx, const nsCString* aString,
1139
JS::MutableHandle<JS::Value> aResult) {
1140
const nsCString& data = *aString;
1141
1142
nsresult rv;
1143
1144
if (!mFileRequest->HasEncoding()) {
1145
JS::Rooted<JSObject*> arrayBuffer(aCx);
1146
rv = nsContentUtils::CreateArrayBuffer(aCx, data, arrayBuffer.address());
1147
if (NS_WARN_IF(NS_FAILED(rv))) {
1148
return NS_ERROR_DOM_FILEHANDLE_UNKNOWN_ERR;
1149
}
1150
1151
aResult.setObject(*arrayBuffer);
1152
return NS_OK;
1153
}
1154
1155
// Try the API argument.
1156
const Encoding* encoding = Encoding::ForLabel(mFileRequest->GetEncoding());
1157
if (!encoding) {
1158
// API argument failed. Since we are dealing with a file system file,
1159
// we don't have a meaningful type attribute for the blob available,
1160
// so proceeding to the next step, which is defaulting to UTF-8.
1161
encoding = UTF_8_ENCODING;
1162
}
1163
1164
nsString tmpString;
1165
Tie(rv, encoding) = encoding->Decode(data, tmpString);
1166
if (NS_WARN_IF(NS_FAILED(rv))) {
1167
return NS_ERROR_DOM_FILEHANDLE_UNKNOWN_ERR;
1168
}
1169
1170
if (NS_WARN_IF(!xpc::StringToJsval(aCx, tmpString, aResult))) {
1171
return NS_ERROR_DOM_FILEHANDLE_UNKNOWN_ERR;
1172
}
1173
1174
return NS_OK;
1175
}
1176
1177
nsresult GetResult(JSContext* aCx, const FileRequestMetadata* aMetadata,
1178
JS::MutableHandle<JS::Value> aResult) {
1179
JS::Rooted<JSObject*> obj(aCx, JS_NewPlainObject(aCx));
1180
if (NS_WARN_IF(!obj)) {
1181
return NS_ERROR_DOM_FILEHANDLE_UNKNOWN_ERR;
1182
}
1183
1184
const Maybe<uint64_t>& size = aMetadata->size();
1185
if (size.isSome()) {
1186
JS::Rooted<JS::Value> number(aCx, JS_NumberValue(size.value()));
1187
1188
if (NS_WARN_IF(!JS_DefineProperty(aCx, obj, "size", number, 0))) {
1189
return NS_ERROR_DOM_FILEHANDLE_UNKNOWN_ERR;
1190
}
1191
}
1192
1193
const Maybe<int64_t>& lastModified = aMetadata->lastModified();
1194
if (lastModified.isSome()) {
1195
JS::Rooted<JSObject*> date(
1196
aCx, JS::NewDateObject(aCx, JS::TimeClip(lastModified.value())));
1197
if (NS_WARN_IF(!date)) {
1198
return NS_ERROR_DOM_FILEHANDLE_UNKNOWN_ERR;
1199
}
1200
1201
if (NS_WARN_IF(!JS_DefineProperty(aCx, obj, "lastModified", date, 0))) {
1202
return NS_ERROR_DOM_FILEHANDLE_UNKNOWN_ERR;
1203
}
1204
}
1205
1206
aResult.setObject(*obj);
1207
return NS_OK;
1208
}
1209
};
1210
1211
already_AddRefed<File> ConvertActorToFile(
1212
IDBFileHandle* aFileHandle, const FileRequestGetFileResponse& aResponse) {
1213
auto* const actor = static_cast<PendingIPCBlobChild*>(aResponse.fileChild());
1214
1215
IDBMutableFile* mutableFile = aFileHandle->GetMutableFile();
1216
MOZ_ASSERT(mutableFile);
1217
1218
const FileRequestMetadata& metadata = aResponse.metadata();
1219
1220
const Maybe<uint64_t>& size = metadata.size();
1221
MOZ_ASSERT(size.isSome());
1222
1223
const Maybe<int64_t>& lastModified = metadata.lastModified();
1224
MOZ_ASSERT(lastModified.isSome());
1225
1226
const RefPtr<BlobImpl> blobImpl = actor->SetPendingInfoAndDeleteActor(
1227
mutableFile->Name(), mutableFile->Type(), size.value(),
1228
lastModified.value());
1229
MOZ_ASSERT(blobImpl);
1230
1231
const RefPtr<BlobImpl> blobImplSnapshot =
1232
new BlobImplSnapshot(blobImpl, static_cast<IDBFileHandle*>(aFileHandle));
1233
1234
RefPtr<File> file =
1235
File::Create(mutableFile->GetOwnerGlobal(), blobImplSnapshot);
1236
return file.forget();
1237
}
1238
1239
void DispatchFileHandleErrorEvent(IDBFileRequest* aFileRequest,
1240
nsresult aErrorCode,
1241
IDBFileHandle* aFileHandle) {
1242
MOZ_ASSERT(aFileRequest);
1243
aFileRequest->AssertIsOnOwningThread();
1244
MOZ_ASSERT(NS_FAILED(aErrorCode));
1245
MOZ_ASSERT(NS_ERROR_GET_MODULE(aErrorCode) == NS_ERROR_MODULE_DOM_FILEHANDLE);
1246
MOZ_ASSERT(aFileHandle);
1247
1248
const RefPtr<IDBFileRequest> fileRequest = aFileRequest;
1249
const RefPtr<IDBFileHandle> fileHandle = aFileHandle;
1250
1251
AutoSetCurrentFileHandle ascfh(aFileHandle);
1252
1253
fileRequest->FireError(aErrorCode);
1254
1255
MOZ_ASSERT(fileHandle->IsOpen() || fileHandle->IsAborted());
1256
}
1257
1258
void DispatchFileHandleSuccessEvent(FileHandleResultHelper* aResultHelper) {
1259
MOZ_ASSERT(aResultHelper);
1260
1261
const RefPtr<IDBFileRequest> fileRequest = aResultHelper->FileRequest();
1262
MOZ_ASSERT(fileRequest);
1263
fileRequest->AssertIsOnOwningThread();
1264
1265
const RefPtr<IDBFileHandle> fileHandle = aResultHelper->FileHandle();
1266
MOZ_ASSERT(fileHandle);
1267
1268
if (fileHandle->IsAborted()) {
1269
fileRequest->FireError(NS_ERROR_DOM_FILEHANDLE_ABORT_ERR);
1270
return;
1271
}
1272
1273
MOZ_ASSERT(fileHandle->IsOpen());
1274
1275
fileRequest->SetResultCallback(aResultHelper);
1276
1277
MOZ_ASSERT(fileHandle->IsOpen() || fileHandle->IsAborted());
1278
}
1279
1280
auto GetKeyOperator(const IDBCursor::Direction aDirection) {
1281
switch (aDirection) {
1282
case IDBCursor::NEXT:
1283
case IDBCursor::NEXT_UNIQUE:
1284
return &Key::operator>=;
1285
case IDBCursor::PREV:
1286
case IDBCursor::PREV_UNIQUE:
1287
return &Key::operator<=;
1288
default:
1289
MOZ_CRASH("Should never get here.");
1290
}
1291
}
1292
1293
} // namespace
1294
1295
/*******************************************************************************
1296
* Actor class declarations
1297
******************************************************************************/
1298
1299
// CancelableRunnable is used to make workers happy.
1300
class BackgroundRequestChild::PreprocessHelper final
1301
: public CancelableRunnable,
1302
public nsIInputStreamCallback,
1303
public nsIFileMetadataCallback {
1304
enum class State {
1305
// Just created on the owning thread, dispatched to the thread pool. Next
1306
// step is either Finishing if stream was ready to be read or
1307
// WaitingForStreamReady if the stream is not ready.
1308
Initial,
1309
1310
// Waiting for stream to be ready on a thread pool thread. Next state is
1311
// Finishing.
1312
WaitingForStreamReady,
1313
1314
// Waiting to finish/finishing on the owning thread. Next step is Completed.
1315
Finishing,
1316
1317
// All done.
1318
Completed
1319
};
1320
1321
const nsCOMPtr<nsIEventTarget> mOwningEventTarget;
1322
RefPtr<TaskQueue> mTaskQueue;
1323
nsCOMPtr<nsIEventTarget> mTaskQueueEventTarget;
1324
nsCOMPtr<nsIInputStream> mStream;
1325
UniquePtr<JSStructuredCloneData> mCloneData;
1326
BackgroundRequestChild* mActor;
1327
const uint32_t mCloneDataIndex;
1328
nsresult mResultCode;
1329
State mState;
1330
1331
public:
1332
PreprocessHelper(uint32_t aCloneDataIndex, BackgroundRequestChild* aActor)
1333
: CancelableRunnable(
1334
"indexedDB::BackgroundRequestChild::PreprocessHelper"),
1335
mOwningEventTarget(aActor->GetActorEventTarget()),
1336
mActor(aActor),
1337
mCloneDataIndex(aCloneDataIndex),
1338
mResultCode(NS_OK),
1339
mState(State::Initial) {
1340
AssertIsOnOwningThread();
1341
MOZ_ASSERT(aActor);
1342
aActor->AssertIsOnOwningThread();
1343
}
1344
1345
bool IsOnOwningThread() const {
1346
MOZ_ASSERT(mOwningEventTarget);
1347
1348
bool current;
1349
return NS_SUCCEEDED(mOwningEventTarget->IsOnCurrentThread(&current)) &&
1350
current;
1351
}
1352
1353
void AssertIsOnOwningThread() const { MOZ_ASSERT(IsOnOwningThread()); }
1354
1355
void ClearActor() {
1356
AssertIsOnOwningThread();
1357
1358
mActor = nullptr;
1359
}
1360
1361
nsresult Init(const StructuredCloneFile& aFile);
1362
1363
nsresult Dispatch();
1364
1365
private:
1366
~PreprocessHelper() {
1367
MOZ_ASSERT(mState == State::Initial || mState == State::Completed);
1368
1369
if (mTaskQueue) {
1370
mTaskQueue->BeginShutdown();
1371
}
1372
}
1373
1374
nsresult Start();
1375
1376
nsresult ProcessStream();
1377
1378
void Finish();
1379
1380
NS_DECL_ISUPPORTS_INHERITED
1381
NS_DECL_NSIRUNNABLE
1382
NS_DECL_NSIINPUTSTREAMCALLBACK
1383
NS_DECL_NSIFILEMETADATACALLBACK
1384
};
1385
1386
/*******************************************************************************
1387
* Local class implementations
1388
******************************************************************************/
1389
1390
void PermissionRequestMainProcessHelper::OnPromptComplete(
1391
PermissionValue aPermissionValue) {
1392
MOZ_ASSERT(mActor);
1393
mActor->AssertIsOnOwningThread();
1394
1395
MaybeCollectGarbageOnIPCMessage();
1396
1397
mActor->SendPermissionRetry();
1398
1399
mActor = nullptr;
1400
mFactory = nullptr;
1401
}
1402
1403
/*******************************************************************************
1404
* BackgroundRequestChildBase
1405
******************************************************************************/
1406
1407
BackgroundRequestChildBase::BackgroundRequestChildBase(IDBRequest* aRequest)
1408
: mRequest(aRequest) {
1409
MOZ_ASSERT(aRequest);
1410
aRequest->AssertIsOnOwningThread();
1411
1412
MOZ_COUNT_CTOR(indexedDB::BackgroundRequestChildBase);
1413
}
1414
1415
BackgroundRequestChildBase::~BackgroundRequestChildBase() {
1416
AssertIsOnOwningThread();
1417
1418
MOZ_COUNT_DTOR(indexedDB::BackgroundRequestChildBase);
1419
}
1420
1421
#ifdef DEBUG
1422
1423
void BackgroundRequestChildBase::AssertIsOnOwningThread() const {
1424
MOZ_ASSERT(mRequest);
1425
mRequest->AssertIsOnOwningThread();
1426
}
1427
1428
#endif // DEBUG
1429
1430
/*******************************************************************************
1431
* BackgroundFactoryChild
1432
******************************************************************************/
1433
1434
BackgroundFactoryChild::BackgroundFactoryChild(IDBFactory* aFactory)
1435
: mFactory(aFactory) {
1436
AssertIsOnOwningThread();
1437
MOZ_ASSERT(aFactory);
1438
aFactory->AssertIsOnOwningThread();
1439
1440
MOZ_COUNT_CTOR(indexedDB::BackgroundFactoryChild);
1441
}
1442
1443
BackgroundFactoryChild::~BackgroundFactoryChild() {
1444
MOZ_COUNT_DTOR(indexedDB::BackgroundFactoryChild);
1445
}
1446
1447
void BackgroundFactoryChild::SendDeleteMeInternal() {
1448
AssertIsOnOwningThread();
1449
1450
if (mFactory) {
1451
mFactory->ClearBackgroundActor();
1452
mFactory = nullptr;
1453
1454
MOZ_ALWAYS_TRUE(PBackgroundIDBFactoryChild::SendDeleteMe());
1455
}
1456
}
1457
1458
void BackgroundFactoryChild::ActorDestroy(ActorDestroyReason aWhy) {
1459
AssertIsOnOwningThread();
1460
1461
MaybeCollectGarbageOnIPCMessage();
1462
1463
if (mFactory) {
1464
mFactory->ClearBackgroundActor();
1465
#ifdef DEBUG
1466
mFactory = nullptr;
1467
#endif
1468
}
1469
}
1470
1471
PBackgroundIDBFactoryRequestChild*
1472
BackgroundFactoryChild::AllocPBackgroundIDBFactoryRequestChild(
1473
const FactoryRequestParams& aParams) {
1474
MOZ_CRASH(
1475
"PBackgroundIDBFactoryRequestChild actors should be manually "
1476
"constructed!");
1477
}
1478
1479
bool BackgroundFactoryChild::DeallocPBackgroundIDBFactoryRequestChild(
1480
PBackgroundIDBFactoryRequestChild* aActor) {
1481
MOZ_ASSERT(aActor);
1482
1483
delete static_cast<BackgroundFactoryRequestChild*>(aActor);
1484
return true;
1485
}
1486
1487
PBackgroundIDBDatabaseChild*
1488
BackgroundFactoryChild::AllocPBackgroundIDBDatabaseChild(
1489
const DatabaseSpec& aSpec, PBackgroundIDBFactoryRequestChild* aRequest) {
1490
AssertIsOnOwningThread();
1491
1492
auto* const request = static_cast<BackgroundFactoryRequestChild*>(aRequest);
1493
MOZ_ASSERT(request);
1494
1495
return new BackgroundDatabaseChild(aSpec, request);
1496
}
1497
1498
bool BackgroundFactoryChild::DeallocPBackgroundIDBDatabaseChild(
1499
PBackgroundIDBDatabaseChild* aActor) {
1500
MOZ_ASSERT(aActor);
1501
1502
delete static_cast<BackgroundDatabaseChild*>(aActor);
1503
return true;
1504
}
1505
1506
mozilla::ipc::IPCResult
1507
BackgroundFactoryChild::RecvPBackgroundIDBDatabaseConstructor(
1508
PBackgroundIDBDatabaseChild* aActor, const DatabaseSpec& aSpec,
1509
PBackgroundIDBFactoryRequestChild* aRequest) {
1510
AssertIsOnOwningThread();
1511
MOZ_ASSERT(aActor);
1512
MOZ_ASSERT(aActor->GetActorEventTarget(),
1513
"The event target shall be inherited from its manager actor.");
1514
1515
return IPC_OK();
1516
}
1517
1518
/*******************************************************************************
1519
* BackgroundFactoryRequestChild
1520
******************************************************************************/
1521
1522
BackgroundFactoryRequestChild::BackgroundFactoryRequestChild(
1523
IDBFactory* aFactory, IDBOpenDBRequest* aOpenRequest, bool aIsDeleteOp,
1524
uint64_t aRequestedVersion)
1525
: BackgroundRequestChildBase(aOpenRequest),
1526
mFactory(aFactory),
1527
mDatabaseActor(nullptr),
1528
mRequestedVersion(aRequestedVersion),
1529
mIsDeleteOp(aIsDeleteOp) {
1530
// Can't assert owning thread here because IPDL has not yet set our manager!
1531
MOZ_ASSERT(aFactory);
1532
aFactory->AssertIsOnOwningThread();
1533
MOZ_ASSERT(aOpenRequest);
1534
1535
MOZ_COUNT_CTOR(indexedDB::BackgroundFactoryRequestChild);
1536
}
1537
1538
BackgroundFactoryRequestChild::~BackgroundFactoryRequestChild() {
1539
MOZ_COUNT_DTOR(indexedDB::BackgroundFactoryRequestChild);
1540
}
1541
1542
IDBOpenDBRequest* BackgroundFactoryRequestChild::GetOpenDBRequest() const {
1543
AssertIsOnOwningThread();
1544
1545
return static_cast<IDBOpenDBRequest*>(mRequest.get());
1546
}
1547
1548
void BackgroundFactoryRequestChild::SetDatabaseActor(
1549
BackgroundDatabaseChild* aActor) {
1550
AssertIsOnOwningThread();
1551
MOZ_ASSERT(!aActor || !mDatabaseActor);
1552
1553
mDatabaseActor = aActor;
1554
}
1555
1556
bool BackgroundFactoryRequestChild::HandleResponse(nsresult aResponse) {
1557
AssertIsOnOwningThread();
1558
MOZ_ASSERT(NS_FAILED(aResponse));
1559
MOZ_ASSERT(NS_ERROR_GET_MODULE(aResponse) == NS_ERROR_MODULE_DOM_INDEXEDDB);
1560
1561
mRequest->Reset();
1562
1563
DispatchErrorEvent(mRequest, aResponse);
1564
1565
if (mDatabaseActor) {
1566
mDatabaseActor->ReleaseDOMObject();
1567
MOZ_ASSERT(!mDatabaseActor);
1568
}
1569
1570
return true;
1571
}
1572
1573
bool BackgroundFactoryRequestChild::HandleResponse(
1574
const OpenDatabaseRequestResponse& aResponse) {
1575
AssertIsOnOwningThread();
1576
1577
mRequest->Reset();
1578
1579
auto databaseActor =
1580
static_cast<BackgroundDatabaseChild*>(aResponse.databaseChild());
1581
MOZ_ASSERT(databaseActor);
1582
1583
IDBDatabase* database = databaseActor->GetDOMObject();
1584
if (!database) {
1585
databaseActor->EnsureDOMObject();
1586
MOZ_ASSERT(mDatabaseActor);
1587
1588
database = databaseActor->GetDOMObject();
1589
MOZ_ASSERT(database);
1590
1591
MOZ_ASSERT(!database->IsClosed());
1592
}
1593
1594
MOZ_ASSERT(mDatabaseActor == databaseActor);
1595
1596
if (database->IsClosed()) {
1597
// If the database was closed already, which is only possible if we fired an
1598
// "upgradeneeded" event, then we shouldn't fire a "success" event here.
1599
// Instead we fire an error event with AbortErr.
1600
DispatchErrorEvent(mRequest, NS_ERROR_DOM_INDEXEDDB_ABORT_ERR);
1601
} else {
1602
ResultHelper helper(mRequest, nullptr, database);
1603
1604
DispatchSuccessEvent(&helper);
1605
}
1606
1607
databaseActor->ReleaseDOMObject();
1608
MOZ_ASSERT(!mDatabaseActor);
1609
1610
return true;
1611
}
1612
1613
bool BackgroundFactoryRequestChild::HandleResponse(
1614
const DeleteDatabaseRequestResponse& aResponse) {
1615
AssertIsOnOwningThread();
1616
1617
ResultHelper helper(mRequest, nullptr, &JS::UndefinedHandleValue);
1618
1619
RefPtr<Event> successEvent = IDBVersionChangeEvent::Create(
1620
mRequest, nsDependentString(kSuccessEventType),
1621
aResponse.previousVersion());
1622
MOZ_ASSERT(successEvent);
1623
1624
DispatchSuccessEvent(&helper, successEvent);
1625
1626
MOZ_ASSERT(!mDatabaseActor);
1627
1628
return true;
1629
}
1630
1631
void BackgroundFactoryRequestChild::ActorDestroy(ActorDestroyReason aWhy) {
1632
AssertIsOnOwningThread();
1633
1634
MaybeCollectGarbageOnIPCMessage();
1635
1636
if (aWhy != Deletion) {
1637
IDBOpenDBRequest* openRequest = GetOpenDBRequest();
1638
if (openRequest) {
1639
openRequest->NoteComplete();
1640
}
1641
}
1642
}
1643
1644
mozilla::ipc::IPCResult BackgroundFactoryRequestChild::Recv__delete__(
1645
const FactoryRequestResponse& aResponse) {
1646
AssertIsOnOwningThread();
1647
MOZ_ASSERT(mRequest);
1648
1649
MaybeCollectGarbageOnIPCMessage();
1650
1651
bool result;
1652
1653
switch (aResponse.type()) {
1654
case FactoryRequestResponse::Tnsresult:
1655
result = HandleResponse(aResponse.get_nsresult());
1656
break;
1657
1658
case FactoryRequestResponse::TOpenDatabaseRequestResponse:
1659
result = HandleResponse(aResponse.get_OpenDatabaseRequestResponse());
1660
break;
1661
1662
case FactoryRequestResponse::TDeleteDatabaseRequestResponse:
1663
result = HandleResponse(aResponse.get_DeleteDatabaseRequestResponse());
1664
break;
1665
1666
default:
1667
MOZ_CRASH("Unknown response type!");
1668
}
1669
1670
IDBOpenDBRequest* request = GetOpenDBRequest();
1671
MOZ_ASSERT(request);
1672
1673
request->NoteComplete();
1674
1675
if (NS_WARN_IF(!result)) {
1676
return IPC_FAIL_NO_REASON(this);
1677
}
1678
1679
return IPC_OK();
1680
}
1681
1682
mozilla::ipc::IPCResult BackgroundFactoryRequestChild::RecvPermissionChallenge(
1683
const PrincipalInfo& aPrincipalInfo) {
1684
AssertIsOnOwningThread();
1685
1686
MaybeCollectGarbageOnIPCMessage();
1687
1688
if (!NS_IsMainThread()) {
1689
WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate();
1690
MOZ_ASSERT(workerPrivate);
1691
workerPrivate->AssertIsOnWorkerThread();
1692
1693
RefPtr<WorkerPermissionChallenge> challenge = new WorkerPermissionChallenge(
1694
workerPrivate, this, mFactory, aPrincipalInfo);
1695
if (!challenge->Dispatch()) {
1696
return IPC_FAIL_NO_REASON(this);
1697
}
1698
return IPC_OK();
1699
}
1700
1701
nsresult rv;
1702
nsCOMPtr<nsIPrincipal> principal =
1703
mozilla::ipc::PrincipalInfoToPrincipal(aPrincipalInfo, &rv);
1704
if (NS_WARN_IF(NS_FAILED(rv))) {
1705
return IPC_FAIL_NO_REASON(this);
1706
}
1707
1708
if (XRE_IsParentProcess()) {
1709
nsCOMPtr<nsIGlobalObject> global = mFactory->GetParentObject();
1710
nsCOMPtr<nsPIDOMWindowInner> window = do_QueryInterface(global);
1711
MOZ_ASSERT(window);
1712
1713
nsCOMPtr<Element> ownerElement =
1714
do_QueryInterface(window->GetChromeEventHandler());
1715
if (NS_WARN_IF(!ownerElement)) {
1716
// If this fails, the page was navigated. Fail the permission check by
1717
// forcing an immediate retry.
1718
if (!SendPermissionRetry()) {
1719
return IPC_FAIL_NO_REASON(this);
1720
}
1721
return IPC_OK();
1722
}
1723
1724
RefPtr<PermissionRequestMainProcessHelper> helper =
1725
new PermissionRequestMainProcessHelper(this, mFactory, ownerElement,
1726
principal);
1727
1728
PermissionRequestBase::PermissionValue permission;
1729
if (NS_WARN_IF(NS_FAILED(helper->PromptIfNeeded(&permission)))) {
1730
return IPC_FAIL_NO_REASON(this);
1731
}
1732
1733
MOZ_ASSERT(permission == PermissionRequestBase::kPermissionAllowed ||
1734
permission == PermissionRequestBase::kPermissionDenied ||
1735
permission == PermissionRequestBase::kPermissionPrompt);
1736
1737
if (permission != PermissionRequestBase::kPermissionPrompt) {
1738
SendPermissionRetry();
1739
}
1740
return IPC_OK();
1741
}
1742
1743
RefPtr<BrowserChild> browserChild = mFactory->GetBrowserChild();
1744
MOZ_ASSERT(browserChild);
1745
1746
IPC::Principal ipcPrincipal(principal);
1747
1748
browserChild->SendIndexedDBPermissionRequest(ipcPrincipal)
1749
->Then(
1750
GetCurrentThreadSerialEventTarget(), __func__,
1751
[this](const uint32_t& aPermission) {
1752
this->AssertIsOnOwningThread();
1753
MaybeCollectGarbageOnIPCMessage();
1754
this->SendPermissionRetry();
1755
},
1756
[](const mozilla::ipc::ResponseRejectReason) {});
1757
1758
return IPC_OK();
1759
}
1760
1761
mozilla::ipc::IPCResult BackgroundFactoryRequestChild::RecvBlocked(
1762
const uint64_t& aCurrentVersion) {
1763
AssertIsOnOwningThread();
1764
MOZ_ASSERT(mRequest);
1765
1766
MaybeCollectGarbageOnIPCMessage();
1767
1768
const nsDependentString type(kBlockedEventType);
1769
1770
RefPtr<Event> blockedEvent;
1771
if (mIsDeleteOp) {
1772
blockedEvent =
1773
IDBVersionChangeEvent::Create(mRequest, type, aCurrentVersion);
1774
MOZ_ASSERT(blockedEvent);
1775
} else {
1776
blockedEvent = IDBVersionChangeEvent::Create(
1777
mRequest, type, aCurrentVersion, mRequestedVersion);
1778
MOZ_ASSERT(blockedEvent);
1779
}
1780
1781
RefPtr<IDBRequest> kungFuDeathGrip = mRequest;
1782
1783
IDB_LOG_MARK_CHILD_REQUEST("Firing \"blocked\" event", "\"blocked\"",
1784
kungFuDeathGrip->LoggingSerialNumber());
1785
1786
IgnoredErrorResult rv;
1787
kungFuDeathGrip->DispatchEvent(*blockedEvent, rv);
1788
if (rv.Failed()) {
1789
NS_WARNING("Failed to dispatch event!");
1790
}
1791
1792
return IPC_OK();
1793
}
1794
1795
/*******************************************************************************
1796
* BackgroundDatabaseChild
1797
******************************************************************************/
1798
1799
BackgroundDatabaseChild::BackgroundDatabaseChild(
1800
const DatabaseSpec& aSpec, BackgroundFactoryRequestChild* aOpenRequestActor)
1801
: mSpec(new DatabaseSpec(aSpec)),
1802
mOpenRequestActor(aOpenRequestActor),
1803
mDatabase(nullptr) {
1804
// Can't assert owning thread here because IPDL has not yet set our manager!
1805
MOZ_ASSERT(aOpenRequestActor);
1806
1807
MOZ_COUNT_CTOR(indexedDB::BackgroundDatabaseChild);
1808
}
1809
1810
BackgroundDatabaseChild::~BackgroundDatabaseChild() {
1811
MOZ_COUNT_DTOR(indexedDB::BackgroundDatabaseChild);
1812
}
1813
1814
#ifdef DEBUG
1815
1816
void BackgroundDatabaseChild::AssertIsOnOwningThread() const {
1817
static_cast<BackgroundFactoryChild*>(Manager())->AssertIsOnOwningThread();
1818
}
1819
1820
#endif // DEBUG
1821
1822
void BackgroundDatabaseChild::SendDeleteMeInternal() {
1823
AssertIsOnOwningThread();
1824
MOZ_ASSERT(!mTemporaryStrongDatabase);
1825
MOZ_ASSERT(!mOpenRequestActor);
1826
1827
if (mDatabase) {
1828
mDatabase->ClearBackgroundActor();
1829
mDatabase = nullptr;
1830
1831
MOZ_ALWAYS_TRUE(PBackgroundIDBDatabaseChild::SendDeleteMe());
1832
}
1833
}
1834
1835
void BackgroundDatabaseChild::EnsureDOMObject() {
1836
AssertIsOnOwningThread();
1837
MOZ_ASSERT(mOpenRequestActor);
1838
1839
if (mTemporaryStrongDatabase) {