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
5
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6
7
#include "IDBObjectStore.h"
8
9
#include "FileInfo.h"
10
#include "IDBCursor.h"
11
#include "IDBDatabase.h"
12
#include "IDBEvents.h"
13
#include "IDBFactory.h"
14
#include "IDBIndex.h"
15
#include "IDBKeyRange.h"
16
#include "IDBMutableFile.h"
17
#include "IDBRequest.h"
18
#include "IDBTransaction.h"
19
#include "IndexedDatabase.h"
20
#include "IndexedDatabaseInlines.h"
21
#include "IndexedDatabaseManager.h"
22
#include "js/Array.h" // JS::GetArrayLength, JS::IsArrayObject
23
#include "js/Class.h"
24
#include "js/Date.h"
25
#include "js/StructuredClone.h"
26
#include "KeyPath.h"
27
#include "mozilla/ClearOnShutdown.h"
28
#include "mozilla/EndianUtils.h"
29
#include "mozilla/ErrorResult.h"
30
#include "mozilla/JSObjectHolder.h"
31
#include "mozilla/Move.h"
32
#include "mozilla/NullPrincipal.h"
33
#include "mozilla/dom/BindingUtils.h"
34
#include "mozilla/dom/File.h"
35
#include "mozilla/dom/FileBlobImpl.h"
36
#include "mozilla/dom/IDBMutableFileBinding.h"
37
#include "mozilla/dom/BlobBinding.h"
38
#include "mozilla/dom/IDBObjectStoreBinding.h"
39
#include "mozilla/dom/MemoryBlobImpl.h"
40
#include "mozilla/dom/StreamBlobImpl.h"
41
#include "mozilla/dom/StructuredCloneHolder.h"
42
#include "mozilla/dom/StructuredCloneTags.h"
43
#include "mozilla/dom/indexedDB/PBackgroundIDBSharedTypes.h"
44
#include "mozilla/dom/WorkerPrivate.h"
45
#include "mozilla/dom/WorkerScope.h"
46
#include "mozilla/ipc/BackgroundChild.h"
47
#include "mozilla/ipc/PBackgroundSharedTypes.h"
48
#include "mozilla/SystemGroup.h"
49
#include "nsCOMPtr.h"
50
#include "nsIXPConnect.h"
51
#include "nsQueryObject.h"
52
#include "nsStreamUtils.h"
53
#include "nsStringStream.h"
54
#include "ProfilerHelpers.h"
55
#include "ReportInternalError.h"
56
57
#include <numeric>
58
59
// Include this last to avoid path problems on Windows.
60
#include "ActorsChild.h"
61
62
namespace mozilla {
63
namespace dom {
64
65
using namespace mozilla::dom::indexedDB;
66
using namespace mozilla::dom::quota;
67
using namespace mozilla::ipc;
68
69
namespace {
70
71
IndexUpdateInfo MakeIndexUpdateInfo(const int64_t aIndexID, const Key& aKey,
72
const nsCString& aLocale,
73
ErrorResult* const aRv) {
74
IndexUpdateInfo indexUpdateInfo;
75
indexUpdateInfo.indexId() = aIndexID;
76
indexUpdateInfo.value() = aKey;
77
if (!aLocale.IsEmpty()) {
78
const auto result =
79
aKey.ToLocaleAwareKey(indexUpdateInfo.localizedValue(), aLocale, *aRv);
80
if (result.Is(Invalid, *aRv)) {
81
aRv->Throw(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
82
}
83
}
84
return indexUpdateInfo;
85
}
86
87
} // namespace
88
89
struct IDBObjectStore::StructuredCloneWriteInfo {
90
JSAutoStructuredCloneBuffer mCloneBuffer;
91
nsTArray<StructuredCloneFile> mFiles;
92
IDBDatabase* mDatabase;
93
uint64_t mOffsetToKeyProp;
94
95
explicit StructuredCloneWriteInfo(IDBDatabase* aDatabase)
96
: mCloneBuffer(JS::StructuredCloneScope::DifferentProcessForIndexedDB,
97
nullptr, nullptr),
98
mDatabase(aDatabase),
99
mOffsetToKeyProp(0) {
100
MOZ_ASSERT(aDatabase);
101
102
MOZ_COUNT_CTOR(StructuredCloneWriteInfo);
103
}
104
105
StructuredCloneWriteInfo(StructuredCloneWriteInfo&& aCloneWriteInfo) noexcept
106
: mCloneBuffer(std::move(aCloneWriteInfo.mCloneBuffer)),
107
mDatabase(aCloneWriteInfo.mDatabase),
108
mOffsetToKeyProp(aCloneWriteInfo.mOffsetToKeyProp) {
109
MOZ_ASSERT(mDatabase);
110
111
MOZ_COUNT_CTOR(StructuredCloneWriteInfo);
112
113
mFiles.SwapElements(aCloneWriteInfo.mFiles);
114
aCloneWriteInfo.mOffsetToKeyProp = 0;
115
}
116
117
~StructuredCloneWriteInfo() { MOZ_COUNT_DTOR(StructuredCloneWriteInfo); }
118
};
119
120
// Used by ValueWrapper::Clone to hold strong references to any blob-like
121
// objects through the clone process. This is necessary because:
122
// - The structured clone process may trigger content code via getters/other
123
// which can potentially cause existing strong references to be dropped,
124
// necessitating the clone to hold its own strong references.
125
// - The structured clone can abort partway through, so it's necessary to track
126
// what strong references have been acquired so that they can be freed even
127
// if a de-serialization does not occur.
128
struct IDBObjectStore::StructuredCloneInfo {
129
nsTArray<StructuredCloneFile> mFiles;
130
};
131
132
namespace {
133
134
struct MOZ_STACK_CLASS MutableFileData final {
135
nsString type;
136
nsString name;
137
138
MutableFileData() { MOZ_COUNT_CTOR(MutableFileData); }
139
140
~MutableFileData() { MOZ_COUNT_DTOR(MutableFileData); }
141
};
142
143
struct MOZ_STACK_CLASS BlobOrFileData final {
144
uint32_t tag;
145
uint64_t size;
146
nsString type;
147
nsString name;
148
int64_t lastModifiedDate;
149
150
BlobOrFileData() : tag(0), size(0), lastModifiedDate(INT64_MAX) {
151
MOZ_COUNT_CTOR(BlobOrFileData);
152
}
153
154
~BlobOrFileData() { MOZ_COUNT_DTOR(BlobOrFileData); }
155
};
156
157
struct MOZ_STACK_CLASS WasmModuleData final {
158
uint32_t bytecodeIndex;
159
uint32_t compiledIndex;
160
uint32_t flags;
161
162
explicit WasmModuleData(uint32_t aFlags)
163
: bytecodeIndex(0), compiledIndex(0), flags(aFlags) {
164
MOZ_COUNT_CTOR(WasmModuleData);
165
}
166
167
~WasmModuleData() { MOZ_COUNT_DTOR(WasmModuleData); }
168
};
169
170
struct MOZ_STACK_CLASS GetAddInfoClosure final {
171
IDBObjectStore::StructuredCloneWriteInfo& mCloneWriteInfo;
172
JS::Handle<JS::Value> mValue;
173
174
GetAddInfoClosure(IDBObjectStore::StructuredCloneWriteInfo& aCloneWriteInfo,
175
JS::Handle<JS::Value> aValue)
176
: mCloneWriteInfo(aCloneWriteInfo), mValue(aValue) {
177
MOZ_COUNT_CTOR(GetAddInfoClosure);
178
}
179
180
~GetAddInfoClosure() { MOZ_COUNT_DTOR(GetAddInfoClosure); }
181
};
182
183
already_AddRefed<IDBRequest> GenerateRequest(JSContext* aCx,
184
IDBObjectStore* aObjectStore) {
185
MOZ_ASSERT(aObjectStore);
186
aObjectStore->AssertIsOnOwningThread();
187
188
IDBTransaction* const transaction = aObjectStore->Transaction();
189
190
RefPtr<IDBRequest> request = IDBRequest::Create(
191
aCx, aObjectStore, transaction->Database(), transaction);
192
MOZ_ASSERT(request);
193
194
return request.forget();
195
}
196
197
bool StructuredCloneWriteCallback(JSContext* aCx,
198
JSStructuredCloneWriter* aWriter,
199
JS::Handle<JSObject*> aObj, void* aClosure) {
200
MOZ_ASSERT(aCx);
201
MOZ_ASSERT(aWriter);
202
MOZ_ASSERT(aClosure);
203
204
auto* const cloneWriteInfo =
205
static_cast<IDBObjectStore::StructuredCloneWriteInfo*>(aClosure);
206
207
if (JS_GetClass(aObj) == IDBObjectStore::DummyPropClass()) {
208
MOZ_ASSERT(!cloneWriteInfo->mOffsetToKeyProp);
209
cloneWriteInfo->mOffsetToKeyProp = js::GetSCOffset(aWriter);
210
211
uint64_t value = 0;
212
// Omit endian swap
213
return JS_WriteBytes(aWriter, &value, sizeof(value));
214
}
215
216
// UNWRAP_OBJECT calls might mutate this.
217
JS::Rooted<JSObject*> obj(aCx, aObj);
218
219
IDBMutableFile* mutableFile;
220
if (NS_SUCCEEDED(UNWRAP_OBJECT(IDBMutableFile, &obj, mutableFile))) {
221
if (cloneWriteInfo->mDatabase->IsFileHandleDisabled()) {
222
return false;
223
}
224
225
IDBDatabase* const database = mutableFile->Database();
226
MOZ_ASSERT(database);
227
228
// Throw when trying to store IDBMutableFile objects that live in a
229
// different database.
230
if (database != cloneWriteInfo->mDatabase) {
231
MOZ_ASSERT(!SameCOMIdentity(database, cloneWriteInfo->mDatabase));
232
233
if (database->Name() != cloneWriteInfo->mDatabase->Name()) {
234
return false;
235
}
236
237
nsCString fileOrigin, databaseOrigin;
238
PersistenceType filePersistenceType, databasePersistenceType;
239
240
if (NS_WARN_IF(NS_FAILED(
241
database->GetQuotaInfo(fileOrigin, &filePersistenceType)))) {
242
return false;
243
}
244
245
if (NS_WARN_IF(NS_FAILED(cloneWriteInfo->mDatabase->GetQuotaInfo(
246
databaseOrigin, &databasePersistenceType)))) {
247
return false;
248
}
249
250
if (filePersistenceType != databasePersistenceType ||
251
fileOrigin != databaseOrigin) {
252
return false;
253
}
254
}
255
256
if (cloneWriteInfo->mFiles.Length() > size_t(UINT32_MAX)) {
257
MOZ_ASSERT(false, "Fix the structured clone data to use a bigger type!");
258
return false;
259
}
260
261
const uint32_t index = cloneWriteInfo->mFiles.Length();
262
263
const NS_ConvertUTF16toUTF8 convType(mutableFile->Type());
264
const uint32_t convTypeLength =
265
NativeEndian::swapToLittleEndian(convType.Length());
266
267
const NS_ConvertUTF16toUTF8 convName(mutableFile->Name());
268
const uint32_t convNameLength =
269
NativeEndian::swapToLittleEndian(convName.Length());
270
271
if (!JS_WriteUint32Pair(aWriter, SCTAG_DOM_MUTABLEFILE, uint32_t(index)) ||
272
!JS_WriteBytes(aWriter, &convTypeLength, sizeof(uint32_t)) ||
273
!JS_WriteBytes(aWriter, convType.get(), convType.Length()) ||
274
!JS_WriteBytes(aWriter, &convNameLength, sizeof(uint32_t)) ||
275
!JS_WriteBytes(aWriter, convName.get(), convName.Length())) {
276
return false;
277
}
278
279
const DebugOnly<StructuredCloneFile*> newFile =
280
cloneWriteInfo->mFiles.EmplaceBack(mutableFile);
281
MOZ_ASSERT(newFile);
282
283
return true;
284
}
285
286
{
287
Blob* blob = nullptr;
288
if (NS_SUCCEEDED(UNWRAP_OBJECT(Blob, &obj, blob))) {
289
ErrorResult rv;
290
const uint64_t nativeEndianSize = blob->GetSize(rv);
291
MOZ_ASSERT(!rv.Failed());
292
293
const uint64_t size = NativeEndian::swapToLittleEndian(nativeEndianSize);
294
295
nsString type;
296
blob->GetType(type);
297
298
const NS_ConvertUTF16toUTF8 convType(type);
299
const uint32_t convTypeLength =
300
NativeEndian::swapToLittleEndian(convType.Length());
301
302
if (cloneWriteInfo->mFiles.Length() > size_t(UINT32_MAX)) {
303
MOZ_ASSERT(false,
304
"Fix the structured clone data to use a bigger type!");
305
return false;
306
}
307
308
const uint32_t index = cloneWriteInfo->mFiles.Length();
309
310
if (!JS_WriteUint32Pair(aWriter,
311
blob->IsFile() ? SCTAG_DOM_FILE : SCTAG_DOM_BLOB,
312
index) ||
313
!JS_WriteBytes(aWriter, &size, sizeof(size)) ||
314
!JS_WriteBytes(aWriter, &convTypeLength, sizeof(convTypeLength)) ||
315
!JS_WriteBytes(aWriter, convType.get(), convType.Length())) {
316
return false;
317
}
318
319
const RefPtr<File> file = blob->ToFile();
320
if (file) {
321
ErrorResult rv;
322
const int64_t nativeEndianLastModifiedDate = file->GetLastModified(rv);
323
MOZ_ALWAYS_TRUE(!rv.Failed());
324
325
const int64_t lastModifiedDate =
326
NativeEndian::swapToLittleEndian(nativeEndianLastModifiedDate);
327
328
nsString name;
329
file->GetName(name);
330
331
const NS_ConvertUTF16toUTF8 convName(name);
332
const uint32_t convNameLength =
333
NativeEndian::swapToLittleEndian(convName.Length());
334
335
if (!JS_WriteBytes(aWriter, &lastModifiedDate,
336
sizeof(lastModifiedDate)) ||
337
!JS_WriteBytes(aWriter, &convNameLength, sizeof(convNameLength)) ||
338
!JS_WriteBytes(aWriter, convName.get(), convName.Length())) {
339
return false;
340
}
341
}
342
343
const DebugOnly<StructuredCloneFile*> newFile =
344
cloneWriteInfo->mFiles.EmplaceBack(StructuredCloneFile::eBlob, blob);
345
MOZ_ASSERT(newFile);
346
347
return true;
348
}
349
}
350
351
return StructuredCloneHolder::WriteFullySerializableObjects(aCx, aWriter,
352
aObj);
353
}
354
355
bool CopyingStructuredCloneWriteCallback(JSContext* aCx,
356
JSStructuredCloneWriter* aWriter,
357
JS::Handle<JSObject*> aObj,
358
void* aClosure) {
359
MOZ_ASSERT(aCx);
360
MOZ_ASSERT(aWriter);
361
MOZ_ASSERT(aClosure);
362
363
auto* const cloneInfo =
364
static_cast<IDBObjectStore::StructuredCloneInfo*>(aClosure);
365
366
// UNWRAP_OBJECT calls might mutate this.
367
JS::Rooted<JSObject*> obj(aCx, aObj);
368
369
{
370
Blob* blob = nullptr;
371
if (NS_SUCCEEDED(UNWRAP_OBJECT(Blob, &obj, blob))) {
372
if (cloneInfo->mFiles.Length() > size_t(UINT32_MAX)) {
373
MOZ_ASSERT(false,
374
"Fix the structured clone data to use a bigger type!");
375
return false;
376
}
377
378
const uint32_t index = cloneInfo->mFiles.Length();
379
380
if (!JS_WriteUint32Pair(aWriter,
381
blob->IsFile() ? SCTAG_DOM_FILE : SCTAG_DOM_BLOB,
382
index)) {
383
return false;
384
}
385
386
const DebugOnly<StructuredCloneFile*> newFile =
387
cloneInfo->mFiles.EmplaceBack(StructuredCloneFile::eBlob, blob);
388
MOZ_ASSERT(newFile);
389
390
return true;
391
}
392
}
393
394
{
395
IDBMutableFile* mutableFile;
396
if (NS_SUCCEEDED(UNWRAP_OBJECT(IDBMutableFile, &obj, mutableFile))) {
397
if (cloneInfo->mFiles.Length() > size_t(UINT32_MAX)) {
398
MOZ_ASSERT(false,
399
"Fix the structured clone data to use a bigger type!");
400
return false;
401
}
402
403
const uint32_t index = cloneInfo->mFiles.Length();
404
405
if (!JS_WriteUint32Pair(aWriter, SCTAG_DOM_MUTABLEFILE, index)) {
406
return false;
407
}
408
409
const DebugOnly<StructuredCloneFile*> newFile =
410
cloneInfo->mFiles.EmplaceBack(mutableFile);
411
MOZ_ASSERT(newFile);
412
413
return true;
414
}
415
}
416
417
return StructuredCloneHolder::WriteFullySerializableObjects(aCx, aWriter,
418
aObj);
419
}
420
421
nsresult GetAddInfoCallback(JSContext* aCx, void* aClosure) {
422
static const JSStructuredCloneCallbacks kStructuredCloneCallbacks = {
423
nullptr /* read */,
424
StructuredCloneWriteCallback /* write */,
425
nullptr /* reportError */,
426
nullptr /* readTransfer */,
427
nullptr /* writeTransfer */,
428
nullptr /* freeTransfer */,
429
nullptr /* canTransfer */
430
};
431
432
MOZ_ASSERT(aCx);
433
434
auto* const data = static_cast<GetAddInfoClosure*>(aClosure);
435
MOZ_ASSERT(data);
436
437
data->mCloneWriteInfo.mOffsetToKeyProp = 0;
438
439
if (!data->mCloneWriteInfo.mCloneBuffer.write(aCx, data->mValue,
440
&kStructuredCloneCallbacks,
441
&data->mCloneWriteInfo)) {
442
return NS_ERROR_DOM_DATA_CLONE_ERR;
443
}
444
445
return NS_OK;
446
}
447
448
bool ResolveMysteryMutableFile(IDBMutableFile* aMutableFile,
449
const nsString& aName, const nsString& aType) {
450
MOZ_ASSERT(aMutableFile);
451
aMutableFile->SetLazyData(aName, aType);
452
return true;
453
}
454
455
bool StructuredCloneReadString(JSStructuredCloneReader* aReader,
456
nsCString& aString) {
457
uint32_t length;
458
if (!JS_ReadBytes(aReader, &length, sizeof(uint32_t))) {
459
NS_WARNING("Failed to read length!");
460
return false;
461
}
462
length = NativeEndian::swapFromLittleEndian(length);
463
464
if (!aString.SetLength(length, fallible)) {
465
NS_WARNING("Out of memory?");
466
return false;
467
}
468
char* const buffer = aString.BeginWriting();
469
470
if (!JS_ReadBytes(aReader, buffer, length)) {
471
NS_WARNING("Failed to read type!");
472
return false;
473
}
474
475
return true;
476
}
477
478
bool ReadFileHandle(JSStructuredCloneReader* aReader,
479
MutableFileData* aRetval) {
480
static_assert(SCTAG_DOM_MUTABLEFILE == 0xFFFF8004, "Update me!");
481
MOZ_ASSERT(aReader && aRetval);
482
483
nsCString type;
484
if (!StructuredCloneReadString(aReader, type)) {
485
return false;
486
}
487
CopyUTF8toUTF16(type, aRetval->type);
488
489
nsCString name;
490
if (!StructuredCloneReadString(aReader, name)) {
491
return false;
492
}
493
CopyUTF8toUTF16(name, aRetval->name);
494
495
return true;
496
}
497
498
bool ReadBlobOrFile(JSStructuredCloneReader* aReader, uint32_t aTag,
499
BlobOrFileData* aRetval) {
500
static_assert(SCTAG_DOM_BLOB == 0xffff8001 &&
501
SCTAG_DOM_FILE_WITHOUT_LASTMODIFIEDDATE == 0xffff8002 &&
502
SCTAG_DOM_FILE == 0xffff8005,
503
"Update me!");
504
505
MOZ_ASSERT(aReader);
506
MOZ_ASSERT(aTag == SCTAG_DOM_FILE ||
507
aTag == SCTAG_DOM_FILE_WITHOUT_LASTMODIFIEDDATE ||
508
aTag == SCTAG_DOM_BLOB);
509
MOZ_ASSERT(aRetval);
510
511
aRetval->tag = aTag;
512
513
uint64_t size;
514
if (NS_WARN_IF(!JS_ReadBytes(aReader, &size, sizeof(uint64_t)))) {
515
return false;
516
}
517
518
aRetval->size = NativeEndian::swapFromLittleEndian(size);
519
520
nsCString type;
521
if (NS_WARN_IF(!StructuredCloneReadString(aReader, type))) {
522
return false;
523
}
524
525
CopyUTF8toUTF16(type, aRetval->type);
526
527
// Blobs are done.
528
if (aTag == SCTAG_DOM_BLOB) {
529
return true;
530
}
531
532
MOZ_ASSERT(aTag == SCTAG_DOM_FILE ||
533
aTag == SCTAG_DOM_FILE_WITHOUT_LASTMODIFIEDDATE);
534
535
int64_t lastModifiedDate;
536
if (aTag == SCTAG_DOM_FILE_WITHOUT_LASTMODIFIEDDATE) {
537
lastModifiedDate = INT64_MAX;
538
} else {
539
if (NS_WARN_IF(!JS_ReadBytes(aReader, &lastModifiedDate,
540
sizeof(lastModifiedDate)))) {
541
return false;
542
}
543
lastModifiedDate = NativeEndian::swapFromLittleEndian(lastModifiedDate);
544
}
545
546
aRetval->lastModifiedDate = lastModifiedDate;
547
548
nsCString name;
549
if (NS_WARN_IF(!StructuredCloneReadString(aReader, name))) {
550
return false;
551
}
552
553
CopyUTF8toUTF16(name, aRetval->name);
554
555
return true;
556
}
557
558
bool ReadWasmModule(JSStructuredCloneReader* aReader, WasmModuleData* aRetval) {
559
static_assert(SCTAG_DOM_WASM == 0xFFFF8006, "Update me!");
560
MOZ_ASSERT(aReader && aRetval);
561
562
uint32_t bytecodeIndex;
563
uint32_t compiledIndex;
564
if (NS_WARN_IF(!JS_ReadUint32Pair(aReader, &bytecodeIndex, &compiledIndex))) {
565
return false;
566
}
567
568
aRetval->bytecodeIndex = bytecodeIndex;
569
aRetval->compiledIndex = compiledIndex;
570
571
return true;
572
}
573
574
class ValueDeserializationHelper {
575
public:
576
static bool CreateAndWrapMutableFile(JSContext* aCx,
577
StructuredCloneFile& aFile,
578
const MutableFileData& aData,
579
JS::MutableHandle<JSObject*> aResult) {
580
MOZ_ASSERT(aCx);
581
582
// If we have eBlob, we are in an IDB SQLite schema upgrade where we don't
583
// care about a real 'MutableFile', but we just care of having a propert
584
// |mType| flag.
585
if (aFile.mType == StructuredCloneFile::eBlob) {
586
aFile.mType = StructuredCloneFile::eMutableFile;
587
588
// Just make a dummy object.
589
JS::Rooted<JSObject*> obj(aCx, JS_NewPlainObject(aCx));
590
591
if (NS_WARN_IF(!obj)) {
592
return false;
593
}
594
595
aResult.set(obj);
596
return true;
597
}
598
599
MOZ_ASSERT(aFile.mType == StructuredCloneFile::eMutableFile);
600
601
if (!aFile.mMutableFile || !NS_IsMainThread()) {
602
return false;
603
}
604
605
if (NS_WARN_IF(!ResolveMysteryMutableFile(aFile.mMutableFile, aData.name,
606
aData.type))) {
607
return false;
608
}
609
610
JS::Rooted<JS::Value> wrappedMutableFile(aCx);
611
if (!ToJSValue(aCx, aFile.mMutableFile, &wrappedMutableFile)) {
612
return false;
613
}
614
615
aResult.set(&wrappedMutableFile.toObject());
616
return true;
617
}
618
619
static bool CreateAndWrapBlobOrFile(JSContext* aCx, IDBDatabase* aDatabase,
620
StructuredCloneFile& aFile,
621
const BlobOrFileData& aData,
622
JS::MutableHandle<JSObject*> aResult) {
623
MOZ_ASSERT(aCx);
624
MOZ_ASSERT(aData.tag == SCTAG_DOM_FILE ||
625
aData.tag == SCTAG_DOM_FILE_WITHOUT_LASTMODIFIEDDATE ||
626
aData.tag == SCTAG_DOM_BLOB);
627
MOZ_ASSERT(aFile.mType == StructuredCloneFile::eBlob);
628
629
RefPtr<Blob> blob = aFile.mBlob;
630
631
// It can happen that this IDB is chrome code, so there is no parent, but
632
// still we want to set a correct parent for the new File object.
633
nsCOMPtr<nsIGlobalObject> global;
634
if (NS_IsMainThread()) {
635
if (aDatabase && aDatabase->GetParentObject()) {
636
global = aDatabase->GetParentObject();
637
} else {
638
global = xpc::CurrentNativeGlobal(aCx);
639
}
640
} else {
641
WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate();
642
MOZ_ASSERT(workerPrivate);
643
644
WorkerGlobalScope* globalScope = workerPrivate->GlobalScope();
645
MOZ_ASSERT(globalScope);
646
647
global = do_QueryObject(globalScope);
648
}
649
650
MOZ_ASSERT(global);
651
652
/* If we are creating an index, we do not have an mBlob but do have an
653
* mInfo. Unlike other index or upgrade cases, we do need a real-looking
654
* Blob/File instance because the index's key path can reference their
655
* properties. Rather than create a fake-looking object, create a real
656
* Blob. */
657
if (!blob) {
658
MOZ_ASSERT(aFile.mFileInfo);
659
const nsCOMPtr<nsIFile> file =
660
FileInfo::GetFileForFileInfo(aFile.mFileInfo);
661
if (!file) {
662
return false;
663
}
664
665
const RefPtr<FileBlobImpl> impl = new FileBlobImpl(file);
666
impl->SetFileId(aFile.mFileInfo->Id());
667
blob = File::Create(global, impl);
668
if (NS_WARN_IF(!blob)) {
669
return false;
670
}
671
}
672
673
if (aData.tag == SCTAG_DOM_BLOB) {
674
blob->Impl()->SetLazyData(VoidString(), aData.type, aData.size,
675
INT64_MAX);
676
MOZ_ASSERT(!blob->IsFile());
677
678
// ActorsParent sends here a kind of half blob and half file wrapped into
679
// a DOM File object. DOM File and DOM Blob are a WebIDL wrapper around a
680
// BlobImpl object. SetLazyData() has just changed the BlobImpl to be a
681
// Blob (see the previous assert), but 'blob' still has the WebIDL DOM
682
// File wrapping.
683
// Before exposing it to content, we must recreate a DOM Blob object.
684
685
const RefPtr<Blob> exposedBlob =
686
Blob::Create(blob->GetParentObject(), blob->Impl());
687
if (NS_WARN_IF(!exposedBlob)) {
688
return false;
689
}
690
691
MOZ_ASSERT(exposedBlob);
692
JS::Rooted<JS::Value> wrappedBlob(aCx);
693
if (!ToJSValue(aCx, exposedBlob, &wrappedBlob)) {
694
return false;
695
}
696
697
aResult.set(&wrappedBlob.toObject());
698
return true;
699
}
700
701
blob->Impl()->SetLazyData(aData.name, aData.type, aData.size,
702
aData.lastModifiedDate * PR_USEC_PER_MSEC);
703
704
MOZ_ASSERT(blob->IsFile());
705
const RefPtr<File> file = blob->ToFile();
706
MOZ_ASSERT(file);
707
708
JS::Rooted<JS::Value> wrappedFile(aCx);
709
if (!ToJSValue(aCx, file, &wrappedFile)) {
710
return false;
711
}
712
713
aResult.set(&wrappedFile.toObject());
714
return true;
715
}
716
717
static bool CreateAndWrapWasmModule(JSContext* aCx,
718
StructuredCloneFile& aFile,
719
const WasmModuleData& aData,
720
JS::MutableHandle<JSObject*> aResult) {
721
MOZ_ASSERT(aCx);
722
MOZ_ASSERT(aFile.mType == StructuredCloneFile::eWasmBytecode);
723
MOZ_ASSERT(!aFile.mBlob);
724
725
// Just create a plain object here, support for de-serialization of
726
// WebAssembly.Modules has been removed in bug 1561876. Full removal is
727
// tracked in bug 1487479.
728
729
JS::Rooted<JSObject*> obj(aCx, JS_NewPlainObject(aCx));
730
if (NS_WARN_IF(!obj)) {
731
return false;
732
}
733
734
aResult.set(obj);
735
return true;
736
}
737
};
738
739
JSObject* CommonStructuredCloneReadCallback(JSContext* aCx,
740
JSStructuredCloneReader* aReader,
741
uint32_t aTag, uint32_t aData,
742
void* aClosure) {
743
// We need to statically assert that our tag values are what we expect
744
// so that if people accidentally change them they notice.
745
static_assert(SCTAG_DOM_BLOB == 0xffff8001 &&
746
SCTAG_DOM_FILE_WITHOUT_LASTMODIFIEDDATE == 0xffff8002 &&
747
SCTAG_DOM_MUTABLEFILE == 0xffff8004 &&
748
SCTAG_DOM_FILE == 0xffff8005 &&
749
SCTAG_DOM_WASM == 0xffff8006,
750
"You changed our structured clone tag values and just ate "
751
"everyone's IndexedDB data. I hope you are happy.");
752
753
if (aTag == SCTAG_DOM_FILE_WITHOUT_LASTMODIFIEDDATE ||
754
aTag == SCTAG_DOM_BLOB || aTag == SCTAG_DOM_FILE ||
755
aTag == SCTAG_DOM_MUTABLEFILE || aTag == SCTAG_DOM_WASM) {
756
auto* const cloneReadInfo = static_cast<StructuredCloneReadInfo*>(aClosure);
757
758
JS::Rooted<JSObject*> result(aCx);
759
760
if (aTag == SCTAG_DOM_WASM) {
761
WasmModuleData data(aData);
762
if (NS_WARN_IF(!ReadWasmModule(aReader, &data))) {
763
return nullptr;
764
}
765
766
MOZ_ASSERT(data.compiledIndex == data.bytecodeIndex + 1);
767
MOZ_ASSERT(!data.flags);
768
769
if (data.bytecodeIndex >= cloneReadInfo->mFiles.Length() ||
770
data.compiledIndex >= cloneReadInfo->mFiles.Length()) {
771
MOZ_ASSERT(false, "Bad index value!");
772
return nullptr;
773
}
774
775
StructuredCloneFile& file = cloneReadInfo->mFiles[data.bytecodeIndex];
776
777
if (NS_WARN_IF(!ValueDeserializationHelper::CreateAndWrapWasmModule(
778
aCx, file, data, &result))) {
779
return nullptr;
780
}
781
782
return result;
783
}
784
785
if (aData >= cloneReadInfo->mFiles.Length()) {
786
MOZ_ASSERT(false, "Bad index value!");
787
return nullptr;
788
}
789
790
StructuredCloneFile& file = cloneReadInfo->mFiles[aData];
791
792
if (aTag == SCTAG_DOM_MUTABLEFILE) {
793
MutableFileData data;
794
if (NS_WARN_IF(!ReadFileHandle(aReader, &data))) {
795
return nullptr;
796
}
797
798
if (NS_WARN_IF(!ValueDeserializationHelper::CreateAndWrapMutableFile(
799
aCx, file, data, &result))) {
800
return nullptr;
801
}
802
803
return result;
804
}
805
806
BlobOrFileData data;
807
if (NS_WARN_IF(!ReadBlobOrFile(aReader, aTag, &data))) {
808
return nullptr;
809
}
810
811
if (NS_WARN_IF(!ValueDeserializationHelper::CreateAndWrapBlobOrFile(
812
aCx, cloneReadInfo->mDatabase, file, data, &result))) {
813
return nullptr;
814
}
815
816
return result;
817
}
818
819
return StructuredCloneHolder::ReadFullySerializableObjects(aCx, aReader,
820
aTag);
821
}
822
823
JSObject* CopyingStructuredCloneReadCallback(JSContext* aCx,
824
JSStructuredCloneReader* aReader,
825
uint32_t aTag, uint32_t aData,
826
void* aClosure) {
827
MOZ_ASSERT(aTag != SCTAG_DOM_FILE_WITHOUT_LASTMODIFIEDDATE);
828
829
if (aTag == SCTAG_DOM_BLOB || aTag == SCTAG_DOM_FILE ||
830
aTag == SCTAG_DOM_MUTABLEFILE) {
831
auto* const cloneInfo =
832
static_cast<IDBObjectStore::StructuredCloneInfo*>(aClosure);
833
834
JS::Rooted<JSObject*> result(aCx);
835
836
if (aData >= cloneInfo->mFiles.Length()) {
837
MOZ_ASSERT(false, "Bad index value!");
838
return nullptr;
839
}
840
841
StructuredCloneFile& file = cloneInfo->mFiles[aData];
842
843
if (aTag == SCTAG_DOM_BLOB) {
844
MOZ_ASSERT(file.mType == StructuredCloneFile::eBlob);
845
MOZ_ASSERT(!file.mBlob->IsFile());
846
847
JS::Rooted<JS::Value> wrappedBlob(aCx);
848
if (NS_WARN_IF(!ToJSValue(aCx, file.mBlob, &wrappedBlob))) {
849
return nullptr;
850
}
851
852
result.set(&wrappedBlob.toObject());
853
854
return result;
855
}
856
857
if (aTag == SCTAG_DOM_FILE) {
858
MOZ_ASSERT(file.mType == StructuredCloneFile::eBlob);
859
860
{
861
// Create a scope so ~RefPtr fires before returning an unwrapped
862
// JS::Value.
863
const RefPtr<Blob> blob = file.mBlob;
864
MOZ_ASSERT(blob->IsFile());
865
866
const RefPtr<File> file = blob->ToFile();
867
MOZ_ASSERT(file);
868
869
JS::Rooted<JS::Value> wrappedFile(aCx);
870
if (NS_WARN_IF(!ToJSValue(aCx, file, &wrappedFile))) {
871
return nullptr;
872
}
873
874
result.set(&wrappedFile.toObject());
875
}
876
877
return result;
878
}
879
880
MOZ_ASSERT(file.mType == StructuredCloneFile::eMutableFile);
881
882
JS::Rooted<JS::Value> wrappedMutableFile(aCx);
883
if (NS_WARN_IF(!ToJSValue(aCx, file.mMutableFile, &wrappedMutableFile))) {
884
return nullptr;
885
}
886
887
result.set(&wrappedMutableFile.toObject());
888
889
return result;
890
}
891
892
return StructuredCloneHolder::ReadFullySerializableObjects(aCx, aReader,
893
aTag);
894
}
895
896
} // namespace
897
898
const JSClass IDBObjectStore::sDummyPropJSClass = {
899
"IDBObjectStore Dummy", 0 /* flags */
900
};
901
902
IDBObjectStore::IDBObjectStore(IDBTransaction* aTransaction,
903
const ObjectStoreSpec* aSpec)
904
: mTransaction(aTransaction),
905
mCachedKeyPath(JS::UndefinedValue()),
906
mSpec(aSpec),
907
mId(aSpec->metadata().id()),
908
mRooted(false) {
909
MOZ_ASSERT(aTransaction);
910
aTransaction->AssertIsOnOwningThread();
911
MOZ_ASSERT(aSpec);
912
}
913
914
IDBObjectStore::~IDBObjectStore() {
915
AssertIsOnOwningThread();
916
917
if (mRooted) {
918
mCachedKeyPath.setUndefined();
919
mozilla::DropJSObjects(this);
920
}
921
}
922
923
// static
924
already_AddRefed<IDBObjectStore> IDBObjectStore::Create(
925
IDBTransaction* aTransaction, const ObjectStoreSpec& aSpec) {
926
MOZ_ASSERT(aTransaction);
927
aTransaction->AssertIsOnOwningThread();
928
929
RefPtr<IDBObjectStore> objectStore = new IDBObjectStore(aTransaction, &aSpec);
930
931
return objectStore.forget();
932
}
933
934
// static
935
void IDBObjectStore::AppendIndexUpdateInfo(
936
const int64_t aIndexID, const KeyPath& aKeyPath, const bool aMultiEntry,
937
const nsCString& aLocale, JSContext* const aCx, JS::Handle<JS::Value> aVal,
938
nsTArray<IndexUpdateInfo>* const aUpdateInfoArray, ErrorResult* const aRv) {
939
// This precondition holds when `aVal` is the result of a structured clone.
940
js::AutoAssertNoContentJS noContentJS(aCx);
941
942
if (!aMultiEntry) {
943
Key key;
944
*aRv = aKeyPath.ExtractKey(aCx, aVal, key);
945
946
// If an index's keyPath doesn't match an object, we ignore that object.
947
if (aRv->ErrorCodeIs(NS_ERROR_DOM_INDEXEDDB_DATA_ERR) || key.IsUnset()) {
948
aRv->SuppressException();
949
return;
950
}
951
952
if (aRv->Failed()) {
953
return;
954
}
955
956
*aUpdateInfoArray->AppendElement() =
957
MakeIndexUpdateInfo(aIndexID, key, aLocale, aRv);
958
return;
959
}
960
961
JS::Rooted<JS::Value> val(aCx);
962
if (NS_FAILED(aKeyPath.ExtractKeyAsJSVal(aCx, aVal, val.address()))) {
963
return;
964
}
965
966
bool isArray;
967
if (NS_WARN_IF(!JS::IsArrayObject(aCx, val, &isArray))) {
968
IDB_REPORT_INTERNAL_ERR();
969
aRv->Throw(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
970
return;
971
}
972
if (isArray) {
973
JS::Rooted<JSObject*> array(aCx, &val.toObject());
974
uint32_t arrayLength;
975
if (NS_WARN_IF(!JS::GetArrayLength(aCx, array, &arrayLength))) {
976
IDB_REPORT_INTERNAL_ERR();
977
aRv->Throw(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
978
return;
979
}
980
981
for (uint32_t arrayIndex = 0; arrayIndex < arrayLength; arrayIndex++) {
982
JS::RootedId indexId(aCx);
983
if (NS_WARN_IF(!JS_IndexToId(aCx, arrayIndex, &indexId))) {
984
IDB_REPORT_INTERNAL_ERR();
985
aRv->Throw(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
986
return;
987
}
988
989
bool hasOwnProperty;
990
if (NS_WARN_IF(
991
!JS_HasOwnPropertyById(aCx, array, indexId, &hasOwnProperty))) {
992
IDB_REPORT_INTERNAL_ERR();
993
aRv->Throw(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
994
return;
995
}
996
997
if (!hasOwnProperty) {
998
continue;
999
}
1000
1001
JS::RootedValue arrayItem(aCx);
1002
if (NS_WARN_IF(!JS_GetPropertyById(aCx, array, indexId, &arrayItem))) {
1003
IDB_REPORT_INTERNAL_ERR();
1004
aRv->Throw(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
1005
return;
1006
}
1007
1008
Key value;
1009
const auto result = value.SetFromJSVal(aCx, arrayItem, *aRv);
1010
if (!result.Is(Ok, *aRv) || value.IsUnset()) {
1011
// Not a value we can do anything with, ignore it.
1012
aRv->SuppressException();
1013
continue;
1014
}
1015
1016
*aUpdateInfoArray->AppendElement() =
1017
MakeIndexUpdateInfo(aIndexID, value, aLocale, aRv);
1018
if (aRv->Failed()) {
1019
return;
1020
}
1021
}
1022
} else {
1023
Key value;
1024
const auto result = value.SetFromJSVal(aCx, val, *aRv);
1025
if (!result.Is(Ok, *aRv) || value.IsUnset()) {
1026
// Not a value we can do anything with, ignore it.
1027
aRv->SuppressException();
1028
return;
1029
}
1030
1031
*aUpdateInfoArray->AppendElement() =
1032
MakeIndexUpdateInfo(aIndexID, value, aLocale, aRv);
1033
}
1034
}
1035
1036
// static
1037
void IDBObjectStore::ClearCloneReadInfo(StructuredCloneReadInfo& aReadInfo) {
1038
// This is kind of tricky, we only want to release stuff on the main thread,
1039
// but we can end up being called on other threads if we have already been
1040
// cleared on the main thread.
1041
if (!aReadInfo.mFiles.Length()) {
1042
return;
1043
}
1044
1045
aReadInfo.mFiles.Clear();
1046
}
1047
1048
// static
1049
bool IDBObjectStore::DeserializeValue(JSContext* aCx,
1050
StructuredCloneReadInfo& aCloneReadInfo,
1051
JS::MutableHandle<JS::Value> aValue) {
1052
MOZ_ASSERT(aCx);
1053
1054
if (!aCloneReadInfo.mData.Size()) {
1055
aValue.setUndefined();
1056
return true;
1057
}
1058
1059
MOZ_ASSERT(!(aCloneReadInfo.mData.Size() % sizeof(uint64_t)));
1060
1061
static const JSStructuredCloneCallbacks callbacks = {
1062
CommonStructuredCloneReadCallback,
1063
nullptr,
1064
nullptr,
1065
nullptr,
1066
nullptr,
1067
nullptr,
1068
nullptr};
1069
1070
// FIXME: Consider to use StructuredCloneHolder here and in other
1071
// deserializing methods.
1072
return JS_ReadStructuredClone(
1073
aCx, aCloneReadInfo.mData, JS_STRUCTURED_CLONE_VERSION,
1074
JS::StructuredCloneScope::DifferentProcessForIndexedDB, aValue,
1075
JS::CloneDataPolicy(), &callbacks, &aCloneReadInfo);
1076
}
1077
1078
namespace {
1079
1080
// This class helps to create only 1 sandbox.
1081
class SandboxHolder final {
1082
public:
1083
NS_INLINE_DECL_REFCOUNTING(SandboxHolder)
1084
1085
static JSObject* GetSandbox(JSContext* aCx) {
1086
SandboxHolder* holder = GetOrCreate();
1087
return holder->GetSandboxInternal(aCx);
1088
}
1089
1090
private:
1091
~SandboxHolder() = default;
1092
1093
static SandboxHolder* GetOrCreate() {
1094
MOZ_ASSERT(XRE_IsParentProcess());
1095
MOZ_ASSERT(NS_IsMainThread());
1096
1097
static StaticRefPtr<SandboxHolder> sHolder;
1098
if (!sHolder) {
1099
sHolder = new SandboxHolder();
1100
ClearOnShutdown(&sHolder);
1101
}
1102
return sHolder;
1103
}
1104
1105
JSObject* GetSandboxInternal(JSContext* aCx) {
1106
if (!mSandbox) {
1107
nsIXPConnect* const xpc = nsContentUtils::XPConnect();
1108
MOZ_ASSERT(xpc, "This should never be null!");
1109
1110
// Let's use a null principal.
1111
const nsCOMPtr<nsIPrincipal> principal =
1112
NullPrincipal::CreateWithoutOriginAttributes();
1113
1114
JS::Rooted<JSObject*> sandbox(aCx);
1115
nsresult rv = xpc->CreateSandbox(aCx, principal, sandbox.address());
1116
if (NS_WARN_IF(NS_FAILED(rv))) {
1117
return nullptr;
1118
}
1119
1120
mSandbox = new JSObjectHolder(aCx, sandbox);
1121
}
1122
1123
return mSandbox->GetJSObject();
1124
}
1125
1126
RefPtr<JSObjectHolder> mSandbox;
1127
};
1128
1129
class DeserializeIndexValueHelper final : public Runnable {
1130
public:
1131
DeserializeIndexValueHelper(int64_t aIndexID, const KeyPath& aKeyPath,
1132
bool aMultiEntry, const nsCString& aLocale,
1133
StructuredCloneReadInfo& aCloneReadInfo,
1134
nsTArray<IndexUpdateInfo>& aUpdateInfoArray)
1135
: Runnable("DeserializeIndexValueHelper"),
1136
mMonitor("DeserializeIndexValueHelper::mMonitor"),
1137
mIndexID(aIndexID),
1138
mKeyPath(aKeyPath),
1139
mMultiEntry(aMultiEntry),
1140
mLocale(aLocale),
1141
mCloneReadInfo(aCloneReadInfo),
1142
mUpdateInfoArray(aUpdateInfoArray),
1143
mStatus(NS_ERROR_FAILURE) {}
1144
1145
void DispatchAndWait(ErrorResult& aRv) {
1146
// We don't need to go to the main-thread and use the sandbox. Let's create
1147
// the updateInfo data here.
1148
if (!mCloneReadInfo.mData.Size()) {
1149
AutoJSAPI jsapi;
1150
jsapi.Init();
1151
1152
JS::Rooted<JS::Value> value(jsapi.cx());
1153
value.setUndefined();
1154
1155
IDBObjectStore::AppendIndexUpdateInfo(mIndexID, mKeyPath, mMultiEntry,
1156
mLocale, jsapi.cx(), value,
1157
&mUpdateInfoArray, &aRv);
1158
return;
1159
}
1160
1161
// The operation will continue on the main-thread.
1162
1163
MOZ_ASSERT(!(mCloneReadInfo.mData.Size() % sizeof(uint64_t)));
1164
1165
MonitorAutoLock lock(mMonitor);
1166
1167
RefPtr<Runnable> self = this;
1168
const nsresult rv =
1169
SystemGroup::Dispatch(TaskCategory::Other, self.forget());
1170
if (NS_WARN_IF(NS_FAILED(rv))) {
1171
aRv.Throw(rv);
1172
return;
1173
}
1174
1175
lock.Wait();
1176
aRv = mStatus;
1177
}
1178
1179
NS_IMETHOD
1180
Run() override {
1181
MOZ_ASSERT(NS_IsMainThread());
1182
1183
AutoJSAPI jsapi;
1184
jsapi.Init();
1185
JSContext* const cx = jsapi.cx();
1186
1187
JS::Rooted<JSObject*> global(cx, SandboxHolder::GetSandbox(cx));
1188
if (NS_WARN_IF(!global)) {
1189
OperationCompleted(NS_ERROR_FAILURE);
1190
return NS_OK;
1191
}
1192
1193
const JSAutoRealm ar(cx, global);
1194
1195
JS::Rooted<JS::Value> value(cx);
1196
const nsresult rv = DeserializeIndexValue(cx, &value);
1197
if (NS_WARN_IF(NS_FAILED(rv))) {
1198
OperationCompleted(rv);
1199
return NS_OK;
1200
}
1201
1202
ErrorResult errorResult;
1203
IDBObjectStore::AppendIndexUpdateInfo(mIndexID, mKeyPath, mMultiEntry,
1204
mLocale, cx, value, &mUpdateInfoArray,
1205
&errorResult);
1206
if (NS_WARN_IF(errorResult.Failed())) {
1207
OperationCompleted(errorResult.StealNSResult());
1208
return NS_OK;
1209
}
1210
1211
OperationCompleted(NS_OK);
1212
return NS_OK;
1213
}
1214
1215
private:
1216
nsresult DeserializeIndexValue(JSContext* aCx,
1217
JS::MutableHandle<JS::Value> aValue) {
1218
static const JSStructuredCloneCallbacks callbacks = {
1219
CommonStructuredCloneReadCallback,
1220
nullptr,
1221
nullptr,
1222
nullptr,
1223
nullptr,
1224
nullptr,
1225
nullptr};
1226
1227
if (!JS_ReadStructuredClone(
1228
aCx, mCloneReadInfo.mData, JS_STRUCTURED_CLONE_VERSION,
1229
JS::StructuredCloneScope::DifferentProcessForIndexedDB, aValue,
1230
JS::CloneDataPolicy(), &callbacks, &mCloneReadInfo)) {
1231
return NS_ERROR_DOM_DATA_CLONE_ERR;
1232
}
1233
1234
return NS_OK;
1235
}
1236
1237
void OperationCompleted(nsresult aStatus) {
1238
mStatus = aStatus;
1239
1240
MonitorAutoLock lock(mMonitor);
1241
lock.Notify();
1242
}
1243
1244
Monitor mMonitor;
1245
1246
const int64_t mIndexID;
1247
const KeyPath& mKeyPath;
1248
const bool mMultiEntry;
1249
const nsCString mLocale;
1250
StructuredCloneReadInfo& mCloneReadInfo;
1251
nsTArray<IndexUpdateInfo>& mUpdateInfoArray;
1252
nsresult mStatus;
1253
};
1254
1255
class DeserializeUpgradeValueHelper final : public Runnable {
1256
public:
1257
explicit DeserializeUpgradeValueHelper(
1258
StructuredCloneReadInfo& aCloneReadInfo)
1259
: Runnable("DeserializeUpgradeValueHelper"),
1260
mMonitor("DeserializeUpgradeValueHelper::mMonitor"),
1261
mCloneReadInfo(aCloneReadInfo),
1262
mStatus(NS_ERROR_FAILURE) {}
1263
1264
nsresult DispatchAndWait(nsAString& aFileIds) {
1265
// We don't need to go to the main-thread and use the sandbox.
1266
if (!mCloneReadInfo.mData.Size()) {
1267
PopulateFileIds(aFileIds);
1268
return NS_OK;
1269
}
1270
1271
// The operation will continue on the main-thread.
1272
1273
MOZ_ASSERT(!(mCloneReadInfo.mData.Size() % sizeof(uint64_t)));
1274
1275
MonitorAutoLock lock(mMonitor);
1276
1277
RefPtr<Runnable> self = this;
1278
const nsresult rv =
1279
SystemGroup::Dispatch(TaskCategory::Other, self.forget());
1280
if (NS_WARN_IF(NS_FAILED(rv))) {
1281
return rv;
1282
}
1283
1284
lock.Wait();
1285
1286
if (NS_FAILED(mStatus)) {
1287
return mStatus;
1288
}
1289
1290
PopulateFileIds(aFileIds);
1291
return NS_OK;
1292
}
1293
1294
NS_IMETHOD
1295
Run() override {
1296
MOZ_ASSERT(NS_IsMainThread());
1297
1298
AutoJSAPI jsapi;
1299
jsapi.Init();
1300
JSContext* cx = jsapi.cx();
1301
1302
JS::Rooted<JSObject*> global(cx, SandboxHolder::GetSandbox(cx));
1303
if (NS_WARN_IF(!global)) {
1304
OperationCompleted(NS_ERROR_FAILURE);
1305
return NS_OK;
1306
}
1307
1308
const JSAutoRealm ar(cx, global);
1309
1310
JS::Rooted<JS::Value> value(cx);
1311
const nsresult rv = DeserializeUpgradeValue(cx, &value);
1312
if (NS_WARN_IF(NS_FAILED(rv))) {
1313
OperationCompleted(rv);
1314
return NS_OK;
1315
}
1316
1317
OperationCompleted(NS_OK);
1318
return NS_OK;
1319
}
1320
1321
private:
1322
nsresult DeserializeUpgradeValue(JSContext* aCx,
1323
JS::MutableHandle<JS::Value> aValue) {
1324
static const JSStructuredCloneCallbacks callbacks = {
1325
CommonStructuredCloneReadCallback,
1326
nullptr,
1327
nullptr,
1328
nullptr,
1329
nullptr,
1330
nullptr,
1331
nullptr};
1332
1333
if (!JS_ReadStructuredClone(
1334
aCx, mCloneReadInfo.mData, JS_STRUCTURED_CLONE_VERSION,
1335
JS::StructuredCloneScope::DifferentProcessForIndexedDB, aValue,
1336
JS::CloneDataPolicy(), &callbacks, &mCloneReadInfo)) {
1337
return NS_ERROR_DOM_DATA_CLONE_ERR;
1338
}
1339
1340
return NS_OK;
1341
}
1342
1343
void PopulateFileIds(nsAString& aFileIds) {
1344
for (uint32_t count = mCloneReadInfo.mFiles.Length(), index = 0;
1345
index < count; index++) {
1346
const StructuredCloneFile& file = mCloneReadInfo.mFiles[index];
1347
MOZ_ASSERT(file.mFileInfo);
1348
1349
const int64_t id = file.mFileInfo->Id();
1350
1351
if (index) {
1352
aFileIds.Append(' ');
1353
}
1354
aFileIds.AppendInt(file.mType == StructuredCloneFile::eBlob ? id : -id);
1355
}
1356
}
1357
1358
void OperationCompleted(nsresult aStatus) {
1359
mStatus = aStatus;
1360
1361
MonitorAutoLock lock(mMonitor);
1362
lock.Notify();
1363
}
1364
1365
Monitor mMonitor;
1366
StructuredCloneReadInfo& mCloneReadInfo;
1367
nsresult mStatus;
1368
};
1369
1370
} // namespace
1371
1372
// static
1373
void IDBObjectStore::DeserializeIndexValueToUpdateInfos(
1374
int64_t aIndexID, const KeyPath& aKeyPath, bool aMultiEntry,
1375
const nsCString& aLocale, StructuredCloneReadInfo& aCloneReadInfo,
1376
nsTArray<IndexUpdateInfo>& aUpdateInfoArray, ErrorResult& aRv) {
1377
MOZ_ASSERT(!NS_IsMainThread());
1378
1379
const RefPtr<DeserializeIndexValueHelper> helper =
1380
new DeserializeIndexValueHelper(aIndexID, aKeyPath, aMultiEntry, aLocale,
1381
aCloneReadInfo, aUpdateInfoArray);
1382
helper->DispatchAndWait(aRv);
1383
}
1384
1385
// static
1386
nsresult IDBObjectStore::DeserializeUpgradeValueToFileIds(
1387
StructuredCloneReadInfo& aCloneReadInfo, nsAString& aFileIds) {
1388
MOZ_ASSERT(!NS_IsMainThread());
1389
1390
const RefPtr<DeserializeUpgradeValueHelper> helper =
1391
new DeserializeUpgradeValueHelper(aCloneReadInfo);
1392
return helper->DispatchAndWait(aFileIds);
1393
}
1394
1395
#ifdef DEBUG
1396
1397
void IDBObjectStore::AssertIsOnOwningThread() const {
1398
MOZ_ASSERT(mTransaction);
1399
mTransaction->AssertIsOnOwningThread();
1400
}
1401
1402
#endif // DEBUG
1403
1404
void IDBObjectStore::GetAddInfo(JSContext* aCx, ValueWrapper& aValueWrapper,
1405
JS::Handle<JS::Value> aKeyVal,
1406
StructuredCloneWriteInfo& aCloneWriteInfo,
1407
Key& aKey,
1408
nsTArray<IndexUpdateInfo>& aUpdateInfoArray,
1409
ErrorResult& aRv) {
1410
// Return DATA_ERR if a key was passed in and this objectStore uses inline
1411
// keys.
1412
if (!aKeyVal.isUndefined() && HasValidKeyPath()) {
1413
aRv.Throw(NS_ERROR_DOM_INDEXEDDB_DATA_ERR);
1414
return;
1415
}
1416
1417
const bool isAutoIncrement = AutoIncrement();
1418
1419
if (!HasValidKeyPath()) {
1420
// Out-of-line keys must be passed in.
1421
const auto result = aKey.SetFromJSVal(aCx, aKeyVal, aRv);
1422
if (!result.Is(Ok, aRv)) {
1423
if (result.Is(Invalid, aRv)) {
1424
aRv.Throw(NS_ERROR_DOM_INDEXEDDB_DATA_ERR);
1425
}
1426
return;
1427
}
1428
} else if (!isAutoIncrement) {
1429
if (!aValueWrapper.Clone(aCx)) {
1430
aRv.Throw(NS_ERROR_DOM_DATA_CLONE_ERR);
1431
return;
1432
}
1433
1434
aRv = GetKeyPath().ExtractKey(aCx, aValueWrapper.Value(), aKey);
1435
if (aRv.Failed()) {
1436
return;
1437
}
1438
}
1439
1440
// Return DATA_ERR if no key was specified this isn't an autoIncrement
1441
// objectStore.
1442
if (aKey.IsUnset() && !isAutoIncrement) {
1443
aRv.Throw(NS_ERROR_DOM_INDEXEDDB_DATA_ERR);
1444
return;
1445
}
1446
1447
// Figure out indexes and the index values to update here.
1448
1449
if (mSpec->indexes().Length() && !aValueWrapper.Clone(aCx)) {
1450
aRv.Throw(NS_ERROR_DOM_DATA_CLONE_ERR);
1451
return;
1452
}
1453
1454
{
1455
const nsTArray<IndexMetadata>& indexes = mSpec->indexes();
1456
const uint32_t idxCount = indexes.Length();
1457
1458
aUpdateInfoArray.SetCapacity(idxCount); // Pretty good estimate
1459
1460
for (uint32_t idxIndex = 0; idxIndex < idxCount; idxIndex++) {
1461
const IndexMetadata& metadata = indexes[idxIndex];
1462
1463
AppendIndexUpdateInfo(metadata.id(), metadata.keyPath(),
1464
metadata.multiEntry(), metadata.locale(), aCx,
1465
aValueWrapper.Value(), &aUpdateInfoArray, &aRv);
1466
if (NS_WARN_IF(aRv.Failed())) {
1467
return;
1468
}
1469
}
1470
}
1471
1472
if (isAutoIncrement && HasValidKeyPath()) {
1473
if (!aValueWrapper.Clone(aCx)) {
1474
aRv.Throw(NS_ERROR_DOM_DATA_CLONE_ERR);
1475
return;
1476
}
1477
1478
GetAddInfoClosure data(aCloneWriteInfo, aValueWrapper.Value());
1479
1480
MOZ_ASSERT(aKey.IsUnset());
1481
1482
aRv = GetKeyPath().ExtractOrCreateKey(aCx, aValueWrapper.Value(), aKey,
1483
&GetAddInfoCallback, &data);
1484
} else {
1485
GetAddInfoClosure data(aCloneWriteInfo, aValueWrapper.Value());
1486
1487
aRv = GetAddInfoCallback(aCx, &data);
1488
}
1489
}
1490
1491
already_AddRefed<IDBRequest> IDBObjectStore::AddOrPut(
1492
JSContext* aCx, ValueWrapper& aValueWrapper, JS::Handle<JS::Value> aKey,
1493
bool aOverwrite, bool aFromCursor, ErrorResult& aRv) {
1494
AssertIsOnOwningThread();
1495
MOZ_ASSERT(aCx);
1496
MOZ_ASSERT_IF(aFromCursor, aOverwrite);
1497
1498
if (mTransaction->GetMode() == IDBTransaction::Mode::Cleanup ||
1499
mDeletedSpec) {
1500
aRv.Throw(NS_ERROR_DOM_INDEXEDDB_NOT_ALLOWED_ERR);
1501
return nullptr;
1502
}
1503
1504
if (!mTransaction->CanAcceptRequests()) {
1505
aRv.Throw(NS_ERROR_DOM_INDEXEDDB_TRANSACTION_INACTIVE_ERR);
1506
return nullptr;
1507
}
1508
1509
if (!mTransaction->IsWriteAllowed()) {
1510
aRv.Throw(NS_ERROR_DOM_INDEXEDDB_READ_ONLY_ERR);
1511
return nullptr;
1512
}
1513
1514
Key key;
1515
StructuredCloneWriteInfo cloneWriteInfo(mTransaction->Database());
1516
nsTArray<IndexUpdateInfo> updateInfo;
1517
1518
{
1519
const auto autoStateRestore =
1520
mTransaction->TemporarilyTransitionToInactive();
1521
GetAddInfo(aCx, aValueWrapper, aKey, cloneWriteInfo, key, updateInfo, aRv);
1522
}
1523
1524
if (aRv.Failed()) {
1525
return nullptr;
1526
}
1527
1528
if (!mTransaction->CanAcceptRequests()) {
1529
aRv.Throw(NS_ERROR_DOM_INDEXEDDB_TRANSACTION_INACTIVE_ERR);
1530
return nullptr;
1531
}
1532
1533
// Check the size limit of the serialized message which mainly consists of
1534
// a StructuredCloneBuffer, an encoded object key, and the encoded index keys.
1535
// kMaxIDBMsgOverhead covers the minor stuff not included in this calculation
1536
// because the precise calculation would slow down this AddOrPut operation.
1537
static const size_t kMaxIDBMsgOverhead = 1024 * 1024; // 1MB
1538
const uint32_t maximalSizeFromPref =
1539
IndexedDatabaseManager::MaxSerializedMsgSize();
1540
MOZ_ASSERT(maximalSizeFromPref > kMaxIDBMsgOverhead);
1541
const size_t kMaxMessageSize = maximalSizeFromPref - kMaxIDBMsgOverhead;
1542
1543
const size_t indexUpdateInfoSize =
1544
std::accumulate(updateInfo.cbegin(), updateInfo.cend(), 0u,
1545
[](size_t old, const IndexUpdateInfo& updateInfo) {
1546
return old + updateInfo.value().GetBuffer().Length() +
1547
updateInfo.localizedValue().GetBuffer().Length();
1548
});
1549
1550
const size_t messageSize = cloneWriteInfo.mCloneBuffer.data().Size() +
1551
key.GetBuffer().Length() + indexUpdateInfoSize;
1552
1553
if (messageSize > kMaxMessageSize) {
1554
IDB_REPORT_INTERNAL_ERR();
1555
aRv.ThrowDOMException(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR,
1556
nsPrintfCString("The serialized value is too large"
1557
" (size=%zu bytes, max=%zu bytes).",
1558
messageSize, kMaxMessageSize));
1559
return nullptr;
1560
}
1561
1562
ObjectStoreAddPutParams commonParams;
1563
commonParams.objectStoreId() = Id();
1564
commonParams.cloneInfo().data().data =
1565
std::move(cloneWriteInfo.mCloneBuffer.data());
1566
commonParams.cloneInfo().offsetToKeyProp() = cloneWriteInfo.mOffsetToKeyProp;
1567
commonParams.key() = key;
1568
commonParams.indexUpdateInfos().SwapElements(updateInfo);
1569
1570
// Convert any blobs or mutable files into FileAddInfo.
1571
nsTArray<StructuredCloneFile>& files = cloneWriteInfo.mFiles;
1572
1573
if (!files.IsEmpty()) {
1574
const uint32_t count = files.Length();
1575
1576
FallibleTArray<FileAddInfo> fileAddInfos;
1577
if (NS_WARN_IF(!fileAddInfos.SetCapacity(count, fallible))) {
1578
aRv = NS_ERROR_OUT_OF_MEMORY;
1579
return nullptr;
1580
}
1581
1582
IDBDatabase* const database = mTransaction->Database();
1583
1584
for (uint32_t index = 0; index < count; index++) {
1585
StructuredCloneFile& file = files[index];
1586
1587
FileAddInfo* const fileAddInfo = fileAddInfos.AppendElement(fallible);
1588
MOZ_ASSERT(fileAddInfo);
1589
1590
switch (file.mType) {
1591
case StructuredCloneFile::eBlob: {
1592
MOZ_ASSERT(file.mBlob);
1593
MOZ_ASSERT(!file.mMutableFile);
1594
1595
PBackgroundIDBDatabaseFileChild* const fileActor =
1596
database->GetOrCreateFileActorForBlob(file.mBlob);
1597
if (NS_WARN_IF(!fileActor)) {
1598
IDB_REPORT_INTERNAL_ERR();
1599
aRv = NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
1600
return nullptr;
1601
}
1602
1603
fileAddInfo->file() = fileActor;
1604
fileAddInfo->type() = StructuredCloneFile::eBlob;
1605
1606
break;
1607
}
1608
1609
case StructuredCloneFile::eMutableFile: {
1610
MOZ_ASSERT(file.mMutableFile);
1611
MOZ_ASSERT(!file.mBlob);
1612
1613
PBackgroundMutableFileChild* const mutableFileActor =
1614
file.mMutableFile->GetBackgroundActor();
1615
if (NS_WARN_IF(!mutableFileActor)) {
1616
IDB_REPORT_INTERNAL_ERR();
1617
aRv = NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
1618
return nullptr;
1619
}
1620
1621
fileAddInfo->file() = mutableFileActor;
1622
fileAddInfo->type() = StructuredCloneFile::eMutableFile;
1623
1624
break;
1625
}
1626
1627
case StructuredCloneFile::eWasmBytecode:
1628
case StructuredCloneFile::eWasmCompiled: {
1629
MOZ_ASSERT(file.mBlob);
1630
MOZ_ASSERT(!file.mMutableFile);
1631
1632
PBackgroundIDBDatabaseFileChild* const fileActor =
1633
database->GetOrCreateFileActorForBlob(file.mBlob);
1634
if (NS_WARN_IF(!fileActor)) {
1635
IDB_REPORT_INTERNAL_ERR();
1636
aRv = NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
1637
return nullptr;
1638
}
1639
1640
fileAddInfo->file() = fileActor;
1641
fileAddInfo->type() = file.mType;
1642
1643
break;
1644
}
1645
1646
default:
1647
MOZ_CRASH("Should never get here!");
1648
}
1649
}
1650
1651
commonParams.fileAddInfos().SwapElements(fileAddInfos);
1652
}
1653
1654
const auto& params = aOverwrite
1655
? RequestParams{ObjectStorePutParams(commonParams)}
1656
: RequestParams{ObjectStoreAddParams(commonParams)};
1657
1658
RefPtr<IDBRequest> request = GenerateRequest(aCx, this);
1659
MOZ_ASSERT(request);
1660
1661
if (!aFromCursor) {
1662
if (aOverwrite) {
1663
IDB_LOG_MARK_CHILD_TRANSACTION_REQUEST(
1664
"database(%s).transaction(%s).objectStore(%s).put(%s)",
1665
"IDBObjectStore.put()", mTransaction->LoggingSerialNumber(),
1666
request->LoggingSerialNumber(),
1667
IDB_LOG_STRINGIFY(mTransaction->Database()),
1668
IDB_LOG_STRINGIFY(mTransaction), IDB_LOG_STRINGIFY(this),
1669
IDB_LOG_STRINGIFY(key));
1670
} else {
1671
IDB_LOG_MARK_CHILD_TRANSACTION_REQUEST(
1672
"database(%s).transaction(%s).objectStore(%s).add(%s)",
1673
"IDBObjectStore.add()", mTransaction->LoggingSerialNumber(),
1674
request->LoggingSerialNumber(),
1675
IDB_LOG_STRINGIFY(mTransaction->Database()),
1676
IDB_LOG_STRINGIFY(mTransaction), IDB_LOG_STRINGIFY(this),
1677
IDB_LOG_STRINGIFY(key));
1678
}
1679
}
1680
1681
mTransaction->StartRequest(request, params);
1682
1683
mTransaction->InvalidateCursorCaches();
1684
1685
return request.forget();
1686
}
1687
1688
already_AddRefed<IDBRequest> IDBObjectStore::GetAllInternal(
1689
bool aKeysOnly, JSContext* aCx, JS::Handle<JS::Value> aKey,
1690
const Optional<uint32_t>& aLimit, ErrorResult& aRv) {
1691
AssertIsOnOwningThread();
1692
1693
if (mDeletedSpec) {
1694
aRv.Throw(NS_ERROR_DOM_INDEXEDDB_NOT_ALLOWED_ERR);
1695
return nullptr;
1696
}
1697
1698
if (!mTransaction->CanAcceptRequests()) {
1699
aRv.Throw(NS_ERROR_DOM_INDEXEDDB_TRANSACTION_INACTIVE_ERR);
1700
return nullptr;
1701
}
1702
1703
RefPtr<IDBKeyRange> keyRange;
1704
IDBKeyRange::FromJSVal(aCx, aKey, getter_AddRefs(keyRange), aRv);
1705
if (NS_WARN_IF(aRv.Failed())) {
1706
return nullptr;
1707
}
1708
1709
const int64_t id = Id();
1710
1711
Maybe<SerializedKeyRange> optionalKeyRange;
1712
if (keyRange) {
1713
SerializedKeyRange serializedKeyRange;
1714
keyRange->ToSerialized(serializedKeyRange);
1715
optionalKeyRange.emplace(serializedKeyRange);
1716
}
1717
1718
const uint32_t limit = aLimit.WasPassed() ? aLimit.Value() : 0;
1719
1720
RequestParams params;
1721
if (aKeysOnly) {
1722
params = ObjectStoreGetAllKeysParams(id, optionalKeyRange, limit);
1723
} else {
1724
params = ObjectStoreGetAllParams(id, optionalKeyRange, limit);
1725
}
1726
1727
RefPtr<IDBRequest> request = GenerateRequest(aCx, this);
1728
MOZ_ASSERT(request);
1729
1730
if (aKeysOnly) {
1731
IDB_LOG_MARK_CHILD_TRANSACTION_REQUEST(
1732
"database(%s).transaction(%s).objectStore(%s)."
1733
"getAllKeys(%s, %s)",
1734
"IDBObjectStore.getAllKeys()", mTransaction->LoggingSerialNumber(),
1735
request->LoggingSerialNumber(),
1736
IDB_LOG_STRINGIFY(mTransaction->Database()),
1737
IDB_LOG_STRINGIFY(mTransaction), IDB_LOG_STRINGIFY(this),
1738
IDB_LOG_STRINGIFY(keyRange), IDB_LOG_STRINGIFY(aLimit));
1739
} else {
1740
IDB_LOG_MARK_CHILD_TRANSACTION_REQUEST(
1741
"database(%s).transaction(%s).objectStore(%s)."
1742
"getAll(%s, %s)",
1743
"IDBObjectStore.getAll()", mTransaction->LoggingSerialNumber(),
1744
request->LoggingSerialNumber(),
1745
IDB_LOG_STRINGIFY(mTransaction->Database()),
1746
IDB_LOG_STRINGIFY(mTransaction), IDB_LOG_STRINGIFY(this),
1747
IDB_LOG_STRINGIFY(keyRange), IDB_LOG_STRINGIFY(aLimit));
1748
}
1749
1750
// TODO: This is necessary to preserve request ordering only. Proper
1751
// sequencing of requests should be done in a more sophisticated manner that
1752
// doesn't require invalidating cursor caches (Bug 1580499).
1753
mTransaction->InvalidateCursorCaches();
1754
1755
mTransaction->StartRequest(request, params);
1756
1757
return request.forget();
1758
}
1759
1760
already_AddRefed<IDBRequest> IDBObjectStore::Clear(JSContext* aCx,
1761
ErrorResult& aRv) {
1762
AssertIsOnOwningThread();
1763
1764
if (mDeletedSpec) {
1765
aRv.Throw(NS_ERROR_DOM_INDEXEDDB_NOT_ALLOWED_ERR);
1766
return nullptr;
1767
}
1768
1769
if (!mTransaction->CanAcceptRequests()) {
1770
aRv.Throw(NS_ERROR_DOM_INDEXEDDB_TRANSACTION_INACTIVE_ERR);
1771
return nullptr;
1772
}
1773
1774
if (!mTransaction->IsWriteAllowed()) {
1775
aRv.Throw(NS_ERROR_DOM_INDEXEDDB_READ_ONLY_ERR);
1776
return nullptr;
1777
}
1778
1779
const ObjectStoreClearParams params = {Id()};
1780
1781
RefPtr<IDBRequest> request = GenerateRequest(aCx, this);
1782
MOZ_ASSERT(request);
1783
1784
IDB_LOG_MARK_CHILD_TRANSACTION_REQUEST(
1785
"database(%s).transaction(%s).objectStore(%s).clear()",
1786
"IDBObjectStore.clear()", mTransaction->LoggingSerialNumber(),
1787
request->LoggingSerialNumber(),
1788
IDB_LOG_STRINGIFY(mTransaction->Database()),
1789
IDB_LOG_STRINGIFY(mTransaction), IDB_LOG_STRINGIFY(this));
1790
1791
mTransaction->InvalidateCursorCaches();
1792
1793
mTransaction->StartRequest(request, params);
1794
1795
return request.forget();
1796
}
1797
1798
already_AddRefed<IDBIndex> IDBObjectStore::Index(const nsAString& aName,
1799
ErrorResult& aRv) {
1800
AssertIsOnOwningThread();
1801
1802
if (mTransaction->IsCommittingOrFinished() || mDeletedSpec) {
1803
aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
1804
return nullptr;
1805
}
1806
1807
const nsTArray<IndexMetadata>& indexMetadatas = mSpec->indexes();
1808
1809
const auto endIndexMetadatas = indexMetadatas.cend();
1810
const auto foundMetadata =
1811
std::find_if(indexMetadatas.cbegin(), endIndexMetadatas,
1812
[&aName](const auto& indexMetadata) {
1813
return indexMetadata.name() == aName;
1814