Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/dom/indexedDB/IDBObjectStore.cpp
Line
Count
Source (jump to first uncovered line)
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/Class.h"
23
#include "js/Date.h"
24
#include "js/StructuredClone.h"
25
#include "KeyPath.h"
26
#include "mozilla/ClearOnShutdown.h"
27
#include "mozilla/EndianUtils.h"
28
#include "mozilla/ErrorResult.h"
29
#include "mozilla/JSObjectHolder.h"
30
#include "mozilla/Move.h"
31
#include "mozilla/NullPrincipal.h"
32
#include "mozilla/dom/BindingUtils.h"
33
#include "mozilla/dom/ContentChild.h"
34
#include "mozilla/dom/ContentParent.h"
35
#include "mozilla/dom/DOMStringList.h"
36
#include "mozilla/dom/File.h"
37
#include "mozilla/dom/FileBlobImpl.h"
38
#include "mozilla/dom/IDBMutableFileBinding.h"
39
#include "mozilla/dom/BlobBinding.h"
40
#include "mozilla/dom/IDBObjectStoreBinding.h"
41
#include "mozilla/dom/MemoryBlobImpl.h"
42
#include "mozilla/dom/StreamBlobImpl.h"
43
#include "mozilla/dom/StructuredCloneHolder.h"
44
#include "mozilla/dom/StructuredCloneTags.h"
45
#include "mozilla/dom/indexedDB/PBackgroundIDBSharedTypes.h"
46
#include "mozilla/dom/WorkerPrivate.h"
47
#include "mozilla/dom/WorkerScope.h"
48
#include "mozilla/ipc/BackgroundChild.h"
49
#include "mozilla/ipc/PBackgroundSharedTypes.h"
50
#include "mozilla/SystemGroup.h"
51
#include "nsCOMPtr.h"
52
#include "nsQueryObject.h"
53
#include "nsStreamUtils.h"
54
#include "nsStringStream.h"
55
#include "ProfilerHelpers.h"
56
#include "ReportInternalError.h"
57
58
// Include this last to avoid path problems on Windows.
59
#include "ActorsChild.h"
60
61
namespace mozilla {
62
namespace dom {
63
64
using namespace mozilla::dom::indexedDB;
65
using namespace mozilla::dom::quota;
66
using namespace mozilla::ipc;
67
68
struct IDBObjectStore::StructuredCloneWriteInfo
69
{
70
  JSAutoStructuredCloneBuffer mCloneBuffer;
71
  nsTArray<StructuredCloneFile> mFiles;
72
  IDBDatabase* mDatabase;
73
  uint64_t mOffsetToKeyProp;
74
75
  explicit StructuredCloneWriteInfo(IDBDatabase* aDatabase)
76
    : mCloneBuffer(JS::StructuredCloneScope::DifferentProcessForIndexedDB, nullptr,
77
                   nullptr)
78
    , mDatabase(aDatabase)
79
    , mOffsetToKeyProp(0)
80
0
  {
81
0
    MOZ_ASSERT(aDatabase);
82
0
83
0
    MOZ_COUNT_CTOR(StructuredCloneWriteInfo);
84
0
  }
85
86
  StructuredCloneWriteInfo(StructuredCloneWriteInfo&& aCloneWriteInfo)
87
    : mCloneBuffer(std::move(aCloneWriteInfo.mCloneBuffer))
88
    , mDatabase(aCloneWriteInfo.mDatabase)
89
    , mOffsetToKeyProp(aCloneWriteInfo.mOffsetToKeyProp)
90
0
  {
91
0
    MOZ_ASSERT(mDatabase);
92
0
93
0
    MOZ_COUNT_CTOR(StructuredCloneWriteInfo);
94
0
95
0
    mFiles.SwapElements(aCloneWriteInfo.mFiles);
96
0
    aCloneWriteInfo.mOffsetToKeyProp = 0;
97
0
  }
98
99
  ~StructuredCloneWriteInfo()
100
0
  {
101
0
    MOZ_COUNT_DTOR(StructuredCloneWriteInfo);
102
0
  }
103
};
104
105
// Used by ValueWrapper::Clone to hold strong references to any blob-like
106
// objects through the clone process.  This is necessary because:
107
// - The structured clone process may trigger content code via getters/other
108
//   which can potentially cause existing strong references to be dropped,
109
//   necessitating the clone to hold its own strong references.
110
// - The structured clone can abort partway through, so it's necessary to track
111
//   what strong references have been acquired so that they can be freed even
112
//   if a de-serialization does not occur.
113
struct IDBObjectStore::StructuredCloneInfo
114
{
115
  nsTArray<StructuredCloneFile> mFiles;
116
};
117
118
namespace {
119
120
struct MOZ_STACK_CLASS MutableFileData final
121
{
122
  nsString type;
123
  nsString name;
124
125
  MutableFileData()
126
0
  {
127
0
    MOZ_COUNT_CTOR(MutableFileData);
128
0
  }
129
130
  ~MutableFileData()
131
0
  {
132
0
    MOZ_COUNT_DTOR(MutableFileData);
133
0
  }
134
};
135
136
struct MOZ_STACK_CLASS BlobOrFileData final
137
{
138
  uint32_t tag;
139
  uint64_t size;
140
  nsString type;
141
  nsString name;
142
  int64_t lastModifiedDate;
143
144
  BlobOrFileData()
145
    : tag(0)
146
    , size(0)
147
    , lastModifiedDate(INT64_MAX)
148
0
  {
149
0
    MOZ_COUNT_CTOR(BlobOrFileData);
150
0
  }
151
152
  ~BlobOrFileData()
153
0
  {
154
0
    MOZ_COUNT_DTOR(BlobOrFileData);
155
0
  }
156
};
157
158
struct MOZ_STACK_CLASS WasmModuleData final
159
{
160
  uint32_t bytecodeIndex;
161
  uint32_t compiledIndex;
162
  uint32_t flags;
163
164
  explicit WasmModuleData(uint32_t aFlags)
165
    : bytecodeIndex(0)
166
    , compiledIndex(0)
167
    , flags(aFlags)
168
0
  {
169
0
    MOZ_COUNT_CTOR(WasmModuleData);
170
0
  }
171
172
  ~WasmModuleData()
173
0
  {
174
0
    MOZ_COUNT_DTOR(WasmModuleData);
175
0
  }
176
};
177
178
struct MOZ_STACK_CLASS GetAddInfoClosure final
179
{
180
  IDBObjectStore::StructuredCloneWriteInfo& mCloneWriteInfo;
181
  JS::Handle<JS::Value> mValue;
182
183
  GetAddInfoClosure(IDBObjectStore::StructuredCloneWriteInfo& aCloneWriteInfo,
184
                    JS::Handle<JS::Value> aValue)
185
    : mCloneWriteInfo(aCloneWriteInfo)
186
    , mValue(aValue)
187
0
  {
188
0
    MOZ_COUNT_CTOR(GetAddInfoClosure);
189
0
  }
190
191
  ~GetAddInfoClosure()
192
0
  {
193
0
    MOZ_COUNT_DTOR(GetAddInfoClosure);
194
0
  }
195
};
196
197
already_AddRefed<IDBRequest>
198
GenerateRequest(JSContext* aCx, IDBObjectStore* aObjectStore)
199
0
{
200
0
  MOZ_ASSERT(aObjectStore);
201
0
  aObjectStore->AssertIsOnOwningThread();
202
0
203
0
  IDBTransaction* transaction = aObjectStore->Transaction();
204
0
205
0
  RefPtr<IDBRequest> request =
206
0
    IDBRequest::Create(aCx, aObjectStore, transaction->Database(), transaction);
207
0
  MOZ_ASSERT(request);
208
0
209
0
  return request.forget();
210
0
}
211
212
bool
213
StructuredCloneWriteCallback(JSContext* aCx,
214
                             JSStructuredCloneWriter* aWriter,
215
                             JS::Handle<JSObject*> aObj,
216
                             void* aClosure)
217
0
{
218
0
  MOZ_ASSERT(aCx);
219
0
  MOZ_ASSERT(aWriter);
220
0
  MOZ_ASSERT(aClosure);
221
0
222
0
  auto* cloneWriteInfo =
223
0
    static_cast<IDBObjectStore::StructuredCloneWriteInfo*>(aClosure);
224
0
225
0
  if (JS_GetClass(aObj) == IDBObjectStore::DummyPropClass()) {
226
0
    MOZ_ASSERT(!cloneWriteInfo->mOffsetToKeyProp);
227
0
    cloneWriteInfo->mOffsetToKeyProp = js::GetSCOffset(aWriter);
228
0
229
0
    uint64_t value = 0;
230
0
    // Omit endian swap
231
0
    return JS_WriteBytes(aWriter, &value, sizeof(value));
232
0
  }
233
0
234
0
  // UNWRAP_OBJECT calls might mutate this.
235
0
  JS::Rooted<JSObject*> obj(aCx, aObj);
236
0
237
0
  IDBMutableFile* mutableFile;
238
0
  if (NS_SUCCEEDED(UNWRAP_OBJECT(IDBMutableFile, &obj, mutableFile))) {
239
0
    if (cloneWriteInfo->mDatabase->IsFileHandleDisabled()) {
240
0
      return false;
241
0
    }
242
0
243
0
    IDBDatabase* database = mutableFile->Database();
244
0
    MOZ_ASSERT(database);
245
0
246
0
    // Throw when trying to store IDBMutableFile objects that live in a
247
0
    // different database.
248
0
    if (database != cloneWriteInfo->mDatabase) {
249
0
      MOZ_ASSERT(!SameCOMIdentity(database, cloneWriteInfo->mDatabase));
250
0
251
0
      if (database->Name() != cloneWriteInfo->mDatabase->Name()) {
252
0
        return false;
253
0
      }
254
0
255
0
      nsCString fileOrigin, databaseOrigin;
256
0
      PersistenceType filePersistenceType, databasePersistenceType;
257
0
258
0
      if (NS_WARN_IF(NS_FAILED(database->GetQuotaInfo(fileOrigin,
259
0
                                                      &filePersistenceType)))) {
260
0
        return false;
261
0
      }
262
0
263
0
      if (NS_WARN_IF(NS_FAILED(cloneWriteInfo->mDatabase->GetQuotaInfo(
264
0
                                                  databaseOrigin,
265
0
                                                  &databasePersistenceType)))) {
266
0
        return false;
267
0
      }
268
0
269
0
      if (filePersistenceType != databasePersistenceType ||
270
0
          fileOrigin != databaseOrigin) {
271
0
        return false;
272
0
      }
273
0
    }
274
0
275
0
    if (cloneWriteInfo->mFiles.Length() > size_t(UINT32_MAX)) {
276
0
      MOZ_ASSERT(false, "Fix the structured clone data to use a bigger type!");
277
0
      return false;
278
0
    }
279
0
280
0
    const uint32_t index = cloneWriteInfo->mFiles.Length();
281
0
282
0
    NS_ConvertUTF16toUTF8 convType(mutableFile->Type());
283
0
    uint32_t convTypeLength =
284
0
      NativeEndian::swapToLittleEndian(convType.Length());
285
0
286
0
    NS_ConvertUTF16toUTF8 convName(mutableFile->Name());
287
0
    uint32_t convNameLength =
288
0
      NativeEndian::swapToLittleEndian(convName.Length());
289
0
290
0
    if (!JS_WriteUint32Pair(aWriter, SCTAG_DOM_MUTABLEFILE, uint32_t(index)) ||
291
0
        !JS_WriteBytes(aWriter, &convTypeLength, sizeof(uint32_t)) ||
292
0
        !JS_WriteBytes(aWriter, convType.get(), convType.Length()) ||
293
0
        !JS_WriteBytes(aWriter, &convNameLength, sizeof(uint32_t)) ||
294
0
        !JS_WriteBytes(aWriter, convName.get(), convName.Length())) {
295
0
      return false;
296
0
    }
297
0
298
0
    StructuredCloneFile* newFile = cloneWriteInfo->mFiles.AppendElement();
299
0
    newFile->mMutableFile = mutableFile;
300
0
    newFile->mType = StructuredCloneFile::eMutableFile;
301
0
302
0
    return true;
303
0
  }
304
0
305
0
  {
306
0
    Blob* blob = nullptr;
307
0
    if (NS_SUCCEEDED(UNWRAP_OBJECT(Blob, &obj, blob))) {
308
0
      ErrorResult rv;
309
0
      uint64_t size = blob->GetSize(rv);
310
0
      MOZ_ASSERT(!rv.Failed());
311
0
312
0
      size = NativeEndian::swapToLittleEndian(size);
313
0
314
0
      nsString type;
315
0
      blob->GetType(type);
316
0
317
0
      NS_ConvertUTF16toUTF8 convType(type);
318
0
      uint32_t convTypeLength =
319
0
        NativeEndian::swapToLittleEndian(convType.Length());
320
0
321
0
      if (cloneWriteInfo->mFiles.Length() > size_t(UINT32_MAX)) {
322
0
        MOZ_ASSERT(false,
323
0
                   "Fix the structured clone data to use a bigger type!");
324
0
        return false;
325
0
      }
326
0
327
0
      const uint32_t index = cloneWriteInfo->mFiles.Length();
328
0
329
0
      if (!JS_WriteUint32Pair(aWriter,
330
0
                              blob->IsFile() ? SCTAG_DOM_FILE : SCTAG_DOM_BLOB,
331
0
                              index) ||
332
0
          !JS_WriteBytes(aWriter, &size, sizeof(size)) ||
333
0
          !JS_WriteBytes(aWriter, &convTypeLength, sizeof(convTypeLength)) ||
334
0
          !JS_WriteBytes(aWriter, convType.get(), convType.Length())) {
335
0
        return false;
336
0
      }
337
0
338
0
      RefPtr<File> file = blob->ToFile();
339
0
      if (file) {
340
0
        ErrorResult rv;
341
0
        int64_t lastModifiedDate = file->GetLastModified(rv);
342
0
        MOZ_ALWAYS_TRUE(!rv.Failed());
343
0
344
0
        lastModifiedDate = NativeEndian::swapToLittleEndian(lastModifiedDate);
345
0
346
0
        nsString name;
347
0
        file->GetName(name);
348
0
349
0
        NS_ConvertUTF16toUTF8 convName(name);
350
0
        uint32_t convNameLength =
351
0
          NativeEndian::swapToLittleEndian(convName.Length());
352
0
353
0
        if (!JS_WriteBytes(aWriter, &lastModifiedDate, sizeof(lastModifiedDate)) ||
354
0
            !JS_WriteBytes(aWriter, &convNameLength, sizeof(convNameLength)) ||
355
0
            !JS_WriteBytes(aWriter, convName.get(), convName.Length())) {
356
0
          return false;
357
0
        }
358
0
      }
359
0
360
0
      StructuredCloneFile* newFile = cloneWriteInfo->mFiles.AppendElement();
361
0
      newFile->mBlob = blob;
362
0
      newFile->mType = StructuredCloneFile::eBlob;
363
0
364
0
      return true;
365
0
    }
366
0
  }
367
0
368
0
  return StructuredCloneHolder::WriteFullySerializableObjects(aCx, aWriter, aObj);
369
0
}
370
371
bool
372
CopyingStructuredCloneWriteCallback(JSContext* aCx,
373
                                    JSStructuredCloneWriter* aWriter,
374
                                    JS::Handle<JSObject*> aObj,
375
                                    void* aClosure)
376
0
{
377
0
  MOZ_ASSERT(aCx);
378
0
  MOZ_ASSERT(aWriter);
379
0
  MOZ_ASSERT(aClosure);
380
0
381
0
  auto* cloneInfo = static_cast<IDBObjectStore::StructuredCloneInfo*>(aClosure);
382
0
383
0
  // UNWRAP_OBJECT calls might mutate this.
384
0
  JS::Rooted<JSObject*> obj(aCx, aObj);
385
0
386
0
  {
387
0
    Blob* blob = nullptr;
388
0
    if (NS_SUCCEEDED(UNWRAP_OBJECT(Blob, &obj, blob))) {
389
0
      if (cloneInfo->mFiles.Length() > size_t(UINT32_MAX)) {
390
0
        MOZ_ASSERT(false,
391
0
                   "Fix the structured clone data to use a bigger type!");
392
0
        return false;
393
0
      }
394
0
395
0
      const uint32_t index = cloneInfo->mFiles.Length();
396
0
397
0
      if (!JS_WriteUint32Pair(aWriter,
398
0
                              blob->IsFile() ? SCTAG_DOM_FILE : SCTAG_DOM_BLOB,
399
0
                              index)) {
400
0
        return false;
401
0
      }
402
0
403
0
      StructuredCloneFile* newFile = cloneInfo->mFiles.AppendElement();
404
0
      newFile->mBlob = blob;
405
0
      newFile->mType = StructuredCloneFile::eBlob;
406
0
407
0
      return true;
408
0
    }
409
0
  }
410
0
411
0
  {
412
0
    IDBMutableFile* mutableFile;
413
0
    if (NS_SUCCEEDED(UNWRAP_OBJECT(IDBMutableFile, &obj, mutableFile))) {
414
0
      if (cloneInfo->mFiles.Length() > size_t(UINT32_MAX)) {
415
0
        MOZ_ASSERT(false,
416
0
                   "Fix the structured clone data to use a bigger type!");
417
0
        return false;
418
0
      }
419
0
420
0
      const uint32_t index = cloneInfo->mFiles.Length();
421
0
422
0
      if (!JS_WriteUint32Pair(aWriter, SCTAG_DOM_MUTABLEFILE, index)) {
423
0
        return false;
424
0
      }
425
0
426
0
      StructuredCloneFile* newFile = cloneInfo->mFiles.AppendElement();
427
0
      newFile->mMutableFile = mutableFile;
428
0
      newFile->mType = StructuredCloneFile::eMutableFile;
429
0
430
0
      return true;
431
0
    }
432
0
  }
433
0
434
0
  return StructuredCloneHolder::WriteFullySerializableObjects(aCx, aWriter, aObj);
435
0
}
436
437
nsresult
438
GetAddInfoCallback(JSContext* aCx, void* aClosure)
439
0
{
440
0
  static const JSStructuredCloneCallbacks kStructuredCloneCallbacks = {
441
0
    nullptr /* read */,
442
0
    StructuredCloneWriteCallback /* write */,
443
0
    nullptr /* reportError */,
444
0
    nullptr /* readTransfer */,
445
0
    nullptr /* writeTransfer */,
446
0
    nullptr /* freeTransfer */,
447
0
    nullptr /* canTransfer */
448
0
  };
449
0
450
0
  MOZ_ASSERT(aCx);
451
0
452
0
  auto* data = static_cast<GetAddInfoClosure*>(aClosure);
453
0
  MOZ_ASSERT(data);
454
0
455
0
  data->mCloneWriteInfo.mOffsetToKeyProp = 0;
456
0
457
0
  if (!data->mCloneWriteInfo.mCloneBuffer.write(aCx,
458
0
                                                data->mValue,
459
0
                                                &kStructuredCloneCallbacks,
460
0
                                                &data->mCloneWriteInfo)) {
461
0
    return NS_ERROR_DOM_DATA_CLONE_ERR;
462
0
  }
463
0
464
0
  return NS_OK;
465
0
}
466
467
bool
468
ResolveMysteryMutableFile(IDBMutableFile* aMutableFile,
469
                          const nsString& aName,
470
                          const nsString& aType)
471
0
{
472
0
  MOZ_ASSERT(aMutableFile);
473
0
  aMutableFile->SetLazyData(aName, aType);
474
0
  return true;
475
0
}
476
477
bool
478
StructuredCloneReadString(JSStructuredCloneReader* aReader,
479
                          nsCString& aString)
480
0
{
481
0
  uint32_t length;
482
0
  if (!JS_ReadBytes(aReader, &length, sizeof(uint32_t))) {
483
0
    NS_WARNING("Failed to read length!");
484
0
    return false;
485
0
  }
486
0
  length = NativeEndian::swapFromLittleEndian(length);
487
0
488
0
  if (!aString.SetLength(length, fallible)) {
489
0
    NS_WARNING("Out of memory?");
490
0
    return false;
491
0
  }
492
0
  char* buffer = aString.BeginWriting();
493
0
494
0
  if (!JS_ReadBytes(aReader, buffer, length)) {
495
0
    NS_WARNING("Failed to read type!");
496
0
    return false;
497
0
  }
498
0
499
0
  return true;
500
0
}
501
502
bool
503
ReadFileHandle(JSStructuredCloneReader* aReader,
504
               MutableFileData* aRetval)
505
0
{
506
0
  static_assert(SCTAG_DOM_MUTABLEFILE == 0xFFFF8004, "Update me!");
507
0
  MOZ_ASSERT(aReader && aRetval);
508
0
509
0
  nsCString type;
510
0
  if (!StructuredCloneReadString(aReader, type)) {
511
0
    return false;
512
0
  }
513
0
  CopyUTF8toUTF16(type, aRetval->type);
514
0
515
0
  nsCString name;
516
0
  if (!StructuredCloneReadString(aReader, name)) {
517
0
    return false;
518
0
  }
519
0
  CopyUTF8toUTF16(name, aRetval->name);
520
0
521
0
  return true;
522
0
}
523
524
bool
525
ReadBlobOrFile(JSStructuredCloneReader* aReader,
526
               uint32_t aTag,
527
               BlobOrFileData* aRetval)
528
0
{
529
0
  static_assert(SCTAG_DOM_BLOB == 0xffff8001 &&
530
0
                SCTAG_DOM_FILE_WITHOUT_LASTMODIFIEDDATE == 0xffff8002 &&
531
0
                SCTAG_DOM_FILE == 0xffff8005,
532
0
                "Update me!");
533
0
534
0
  MOZ_ASSERT(aReader);
535
0
  MOZ_ASSERT(aTag == SCTAG_DOM_FILE ||
536
0
             aTag == SCTAG_DOM_FILE_WITHOUT_LASTMODIFIEDDATE ||
537
0
             aTag == SCTAG_DOM_BLOB);
538
0
  MOZ_ASSERT(aRetval);
539
0
540
0
  aRetval->tag = aTag;
541
0
542
0
  uint64_t size;
543
0
  if (NS_WARN_IF(!JS_ReadBytes(aReader, &size, sizeof(uint64_t)))) {
544
0
    return false;
545
0
  }
546
0
547
0
  aRetval->size = NativeEndian::swapFromLittleEndian(size);
548
0
549
0
  nsCString type;
550
0
  if (NS_WARN_IF(!StructuredCloneReadString(aReader, type))) {
551
0
    return false;
552
0
  }
553
0
554
0
  CopyUTF8toUTF16(type, aRetval->type);
555
0
556
0
  // Blobs are done.
557
0
  if (aTag == SCTAG_DOM_BLOB) {
558
0
    return true;
559
0
  }
560
0
561
0
  MOZ_ASSERT(aTag == SCTAG_DOM_FILE ||
562
0
             aTag == SCTAG_DOM_FILE_WITHOUT_LASTMODIFIEDDATE);
563
0
564
0
  int64_t lastModifiedDate;
565
0
  if (aTag == SCTAG_DOM_FILE_WITHOUT_LASTMODIFIEDDATE) {
566
0
    lastModifiedDate = INT64_MAX;
567
0
  } else {
568
0
    if (NS_WARN_IF(!JS_ReadBytes(aReader, &lastModifiedDate,
569
0
                                sizeof(lastModifiedDate)))) {
570
0
      return false;
571
0
    }
572
0
    lastModifiedDate = NativeEndian::swapFromLittleEndian(lastModifiedDate);
573
0
  }
574
0
575
0
  aRetval->lastModifiedDate = lastModifiedDate;
576
0
577
0
  nsCString name;
578
0
  if (NS_WARN_IF(!StructuredCloneReadString(aReader, name))) {
579
0
    return false;
580
0
  }
581
0
582
0
  CopyUTF8toUTF16(name, aRetval->name);
583
0
584
0
  return true;
585
0
}
586
587
bool
588
ReadWasmModule(JSStructuredCloneReader* aReader,
589
               WasmModuleData* aRetval)
590
0
{
591
0
  static_assert(SCTAG_DOM_WASM == 0xFFFF8006, "Update me!");
592
0
  MOZ_ASSERT(aReader && aRetval);
593
0
594
0
  uint32_t bytecodeIndex;
595
0
  uint32_t compiledIndex;
596
0
  if (NS_WARN_IF(!JS_ReadUint32Pair(aReader,
597
0
                                    &bytecodeIndex,
598
0
                                    &compiledIndex))) {
599
0
    return false;
600
0
  }
601
0
602
0
  aRetval->bytecodeIndex = bytecodeIndex;
603
0
  aRetval->compiledIndex = compiledIndex;
604
0
605
0
  return true;
606
0
}
607
608
class ValueDeserializationHelper
609
{
610
public:
611
  static bool
612
  CreateAndWrapMutableFile(JSContext* aCx,
613
                           StructuredCloneFile& aFile,
614
                           const MutableFileData& aData,
615
                           JS::MutableHandle<JSObject*> aResult)
616
0
  {
617
0
    MOZ_ASSERT(aCx);
618
0
619
0
    // If we have eBlob, we are in an IDB SQLite schema upgrade where we don't
620
0
    // care about a real 'MutableFile', but we just care of having a propert
621
0
    // |mType| flag.
622
0
    if (aFile.mType == StructuredCloneFile::eBlob) {
623
0
      aFile.mType = StructuredCloneFile::eMutableFile;
624
0
625
0
      // Just make a dummy object.
626
0
      JS::Rooted<JSObject*> obj(aCx, JS_NewPlainObject(aCx));
627
0
628
0
      if (NS_WARN_IF(!obj)) {
629
0
        return false;
630
0
      }
631
0
632
0
      aResult.set(obj);
633
0
      return true;
634
0
    }
635
0
636
0
    MOZ_ASSERT(aFile.mType == StructuredCloneFile::eMutableFile);
637
0
638
0
    if (!aFile.mMutableFile || !NS_IsMainThread()) {
639
0
      return false;
640
0
    }
641
0
642
0
    if (NS_WARN_IF(!ResolveMysteryMutableFile(aFile.mMutableFile,
643
0
                                              aData.name,
644
0
                                              aData.type))) {
645
0
      return false;
646
0
    }
647
0
648
0
    JS::Rooted<JS::Value> wrappedMutableFile(aCx);
649
0
    if (!ToJSValue(aCx, aFile.mMutableFile, &wrappedMutableFile)) {
650
0
      return false;
651
0
    }
652
0
653
0
    aResult.set(&wrappedMutableFile.toObject());
654
0
    return true;
655
0
  }
656
657
  static bool
658
  CreateAndWrapBlobOrFile(JSContext* aCx,
659
                          IDBDatabase* aDatabase,
660
                          StructuredCloneFile& aFile,
661
                          const BlobOrFileData& aData,
662
                          JS::MutableHandle<JSObject*> aResult)
663
0
  {
664
0
    MOZ_ASSERT(aCx);
665
0
    MOZ_ASSERT(aData.tag == SCTAG_DOM_FILE ||
666
0
               aData.tag == SCTAG_DOM_FILE_WITHOUT_LASTMODIFIEDDATE ||
667
0
               aData.tag == SCTAG_DOM_BLOB);
668
0
    MOZ_ASSERT(aFile.mType == StructuredCloneFile::eBlob);
669
0
670
0
    RefPtr<Blob> blob = aFile.mBlob;
671
0
672
0
    /* If we are creating an index, we do not have an mBlob but do have an
673
0
     * mInfo.  Unlike other index or upgrade cases, we do need a real-looking
674
0
     * Blob/File instance because the index's key path can reference their
675
0
     * properties.  Rather than create a fake-looking object, create a real
676
0
     * Blob. */
677
0
    if (!blob) {
678
0
      MOZ_ASSERT(aFile.mFileInfo);
679
0
      nsCOMPtr<nsIFile> file = FileInfo::GetFileForFileInfo(aFile.mFileInfo);
680
0
      if (!file) {
681
0
        return false;
682
0
      }
683
0
684
0
685
0
      RefPtr<FileBlobImpl> impl = new FileBlobImpl(file);
686
0
      impl->SetFileId(aFile.mFileInfo->Id());
687
0
      blob = File::Create(nullptr, impl);
688
0
    }
689
0
690
0
    // It can happen that this IDB is chrome code, so there is no parent, but
691
0
    // still we want to set a correct parent for the new File object.
692
0
    nsCOMPtr<nsISupports> parent;
693
0
    if (NS_IsMainThread()) {
694
0
      if (aDatabase && aDatabase->GetParentObject()) {
695
0
        parent = aDatabase->GetParentObject();
696
0
      } else {
697
0
        parent = xpc::CurrentNativeGlobal(aCx);
698
0
      }
699
0
    } else {
700
0
      WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate();
701
0
      MOZ_ASSERT(workerPrivate);
702
0
703
0
      WorkerGlobalScope* globalScope = workerPrivate->GlobalScope();
704
0
      MOZ_ASSERT(globalScope);
705
0
706
0
      parent = do_QueryObject(globalScope);
707
0
    }
708
0
709
0
    MOZ_ASSERT(parent);
710
0
711
0
    if (aData.tag == SCTAG_DOM_BLOB) {
712
0
      blob->Impl()->SetLazyData(
713
0
        VoidString(), aData.type, aData.size, INT64_MAX);
714
0
      MOZ_ASSERT(!blob->IsFile());
715
0
716
0
      // ActorsParent sends here a kind of half blob and half file wrapped into
717
0
      // a DOM File object. DOM File and DOM Blob are a WebIDL wrapper around a
718
0
      // BlobImpl object. SetLazyData() has just changed the BlobImpl to be a
719
0
      // Blob (see the previous assert), but 'blob' still has the WebIDL DOM
720
0
      // File wrapping.
721
0
      // Before exposing it to content, we must recreate a DOM Blob object.
722
0
723
0
      RefPtr<Blob> exposedBlob =
724
0
        Blob::Create(blob->GetParentObject(), blob->Impl());
725
0
      MOZ_ASSERT(exposedBlob);
726
0
      JS::Rooted<JS::Value> wrappedBlob(aCx);
727
0
      if (!ToJSValue(aCx, exposedBlob, &wrappedBlob)) {
728
0
        return false;
729
0
      }
730
0
731
0
      aResult.set(&wrappedBlob.toObject());
732
0
      return true;
733
0
    }
734
0
735
0
    blob->Impl()->SetLazyData(
736
0
      aData.name, aData.type, aData.size,
737
0
      aData.lastModifiedDate * PR_USEC_PER_MSEC);
738
0
739
0
    MOZ_ASSERT(blob->IsFile());
740
0
    RefPtr<File> file = blob->ToFile();
741
0
    MOZ_ASSERT(file);
742
0
743
0
    JS::Rooted<JS::Value> wrappedFile(aCx);
744
0
    if (!ToJSValue(aCx, file, &wrappedFile)) {
745
0
      return false;
746
0
    }
747
0
748
0
    aResult.set(&wrappedFile.toObject());
749
0
    return true;
750
0
  }
751
752
  static bool
753
  CreateAndWrapWasmModule(JSContext* aCx,
754
                          StructuredCloneFile& aFile,
755
                          const WasmModuleData& aData,
756
                          JS::MutableHandle<JSObject*> aResult)
757
0
  {
758
0
    MOZ_ASSERT(aCx);
759
0
    MOZ_ASSERT(aFile.mType == StructuredCloneFile::eWasmBytecode);
760
0
    MOZ_ASSERT(!aFile.mBlob);
761
0
762
0
    // If we don't have a WasmModule, we are probably using it for an index
763
0
    // creation, but Wasm module can't be used in index creation, so just make a
764
0
    // dummy object.
765
0
    if (!aFile.mWasmModule) {
766
0
      JS::Rooted<JSObject*> obj(aCx, JS_NewPlainObject(aCx));
767
0
768
0
      if (NS_WARN_IF(!obj)) {
769
0
        return false;
770
0
      }
771
0
772
0
      aResult.set(obj);
773
0
      return true;
774
0
    }
775
0
776
0
    JS::Rooted<JSObject*> moduleObj(aCx, aFile.mWasmModule->createObject(aCx));
777
0
    if (NS_WARN_IF(!moduleObj)) {
778
0
      return false;
779
0
    }
780
0
781
0
    aResult.set(moduleObj);
782
0
    return true;
783
0
  }
784
};
785
786
JSObject*
787
CommonStructuredCloneReadCallback(JSContext* aCx,
788
                                  JSStructuredCloneReader* aReader,
789
                                  uint32_t aTag,
790
                                  uint32_t aData,
791
                                  void* aClosure)
792
0
{
793
0
  // We need to statically assert that our tag values are what we expect
794
0
  // so that if people accidentally change them they notice.
795
0
  static_assert(SCTAG_DOM_BLOB == 0xffff8001 &&
796
0
                SCTAG_DOM_FILE_WITHOUT_LASTMODIFIEDDATE == 0xffff8002 &&
797
0
                SCTAG_DOM_MUTABLEFILE == 0xffff8004 &&
798
0
                SCTAG_DOM_FILE == 0xffff8005 &&
799
0
                SCTAG_DOM_WASM == 0xffff8006,
800
0
                "You changed our structured clone tag values and just ate "
801
0
                "everyone's IndexedDB data.  I hope you are happy.");
802
0
803
0
  if (aTag == SCTAG_DOM_FILE_WITHOUT_LASTMODIFIEDDATE ||
804
0
      aTag == SCTAG_DOM_BLOB ||
805
0
      aTag == SCTAG_DOM_FILE ||
806
0
      aTag == SCTAG_DOM_MUTABLEFILE ||
807
0
      aTag == SCTAG_DOM_WASM) {
808
0
    auto* cloneReadInfo = static_cast<StructuredCloneReadInfo*>(aClosure);
809
0
810
0
    JS::Rooted<JSObject*> result(aCx);
811
0
812
0
    if (aTag == SCTAG_DOM_WASM) {
813
0
      WasmModuleData data(aData);
814
0
      if (NS_WARN_IF(!ReadWasmModule(aReader, &data))) {
815
0
        return nullptr;
816
0
      }
817
0
818
0
      MOZ_ASSERT(data.compiledIndex == data.bytecodeIndex + 1);
819
0
      MOZ_ASSERT(!data.flags);
820
0
821
0
      if (data.bytecodeIndex >= cloneReadInfo->mFiles.Length() ||
822
0
          data.compiledIndex >= cloneReadInfo->mFiles.Length()) {
823
0
        MOZ_ASSERT(false, "Bad index value!");
824
0
        return nullptr;
825
0
      }
826
0
827
0
      StructuredCloneFile& file = cloneReadInfo->mFiles[data.bytecodeIndex];
828
0
829
0
      if (NS_WARN_IF(!ValueDeserializationHelper::CreateAndWrapWasmModule(aCx,
830
0
                                                                          file,
831
0
                                                                          data,
832
0
                                                                          &result))) {
833
0
        return nullptr;
834
0
      }
835
0
836
0
      return result;
837
0
    }
838
0
839
0
    if (aData >= cloneReadInfo->mFiles.Length()) {
840
0
      MOZ_ASSERT(false, "Bad index value!");
841
0
      return nullptr;
842
0
    }
843
0
844
0
    StructuredCloneFile& file = cloneReadInfo->mFiles[aData];
845
0
846
0
    if (aTag == SCTAG_DOM_MUTABLEFILE) {
847
0
      MutableFileData data;
848
0
      if (NS_WARN_IF(!ReadFileHandle(aReader, &data))) {
849
0
        return nullptr;
850
0
      }
851
0
852
0
      if (NS_WARN_IF(!ValueDeserializationHelper::CreateAndWrapMutableFile(aCx,
853
0
                                                                           file,
854
0
                                                                           data,
855
0
                                                                           &result))) {
856
0
        return nullptr;
857
0
      }
858
0
859
0
      return result;
860
0
    }
861
0
862
0
    BlobOrFileData data;
863
0
    if (NS_WARN_IF(!ReadBlobOrFile(aReader, aTag, &data))) {
864
0
      return nullptr;
865
0
    }
866
0
867
0
    if (NS_WARN_IF(!ValueDeserializationHelper::CreateAndWrapBlobOrFile(aCx,
868
0
                                                                        cloneReadInfo->mDatabase,
869
0
                                                                        file,
870
0
                                                                        data,
871
0
                                                                        &result))) {
872
0
      return nullptr;
873
0
    }
874
0
875
0
    return result;
876
0
  }
877
0
878
0
  return StructuredCloneHolder::ReadFullySerializableObjects(aCx, aReader,
879
0
                                                             aTag);
880
0
}
881
882
JSObject*
883
CopyingStructuredCloneReadCallback(JSContext* aCx,
884
                                   JSStructuredCloneReader* aReader,
885
                                   uint32_t aTag,
886
                                   uint32_t aData,
887
                                   void* aClosure)
888
0
{
889
0
  MOZ_ASSERT(aTag != SCTAG_DOM_FILE_WITHOUT_LASTMODIFIEDDATE);
890
0
891
0
  if (aTag == SCTAG_DOM_BLOB ||
892
0
      aTag == SCTAG_DOM_FILE ||
893
0
      aTag == SCTAG_DOM_MUTABLEFILE ||
894
0
      aTag == SCTAG_DOM_WASM) {
895
0
    auto* cloneInfo =
896
0
      static_cast<IDBObjectStore::StructuredCloneInfo*>(aClosure);
897
0
898
0
    JS::Rooted<JSObject*> result(aCx);
899
0
900
0
    if (aData >= cloneInfo->mFiles.Length()) {
901
0
      MOZ_ASSERT(false, "Bad index value!");
902
0
      return nullptr;
903
0
    }
904
0
905
0
    StructuredCloneFile& file = cloneInfo->mFiles[aData];
906
0
907
0
    if (aTag == SCTAG_DOM_BLOB) {
908
0
      MOZ_ASSERT(file.mType == StructuredCloneFile::eBlob);
909
0
      MOZ_ASSERT(!file.mBlob->IsFile());
910
0
911
0
      JS::Rooted<JS::Value> wrappedBlob(aCx);
912
0
      if (NS_WARN_IF(!ToJSValue(aCx, file.mBlob, &wrappedBlob))) {
913
0
        return nullptr;
914
0
      }
915
0
916
0
      result.set(&wrappedBlob.toObject());
917
0
918
0
      return result;
919
0
    }
920
0
921
0
    if (aTag == SCTAG_DOM_FILE) {
922
0
      MOZ_ASSERT(file.mType == StructuredCloneFile::eBlob);
923
0
924
0
      {
925
0
        // Create a scope so ~RefPtr fires before returning an unwrapped
926
0
        // JS::Value.
927
0
        RefPtr<Blob> blob = file.mBlob;
928
0
        MOZ_ASSERT(blob->IsFile());
929
0
930
0
        RefPtr<File> file = blob->ToFile();
931
0
        MOZ_ASSERT(file);
932
0
933
0
        JS::Rooted<JS::Value> wrappedFile(aCx);
934
0
        if (NS_WARN_IF(!ToJSValue(aCx, file, &wrappedFile))) {
935
0
          return nullptr;
936
0
        }
937
0
938
0
        result.set(&wrappedFile.toObject());
939
0
      }
940
0
941
0
      return result;
942
0
    }
943
0
944
0
    if (aTag == SCTAG_DOM_MUTABLEFILE) {
945
0
      MOZ_ASSERT(file.mType == StructuredCloneFile::eMutableFile);
946
0
947
0
      JS::Rooted<JS::Value> wrappedMutableFile(aCx);
948
0
      if (NS_WARN_IF(!ToJSValue(aCx, file.mMutableFile, &wrappedMutableFile))) {
949
0
        return nullptr;
950
0
      }
951
0
952
0
      result.set(&wrappedMutableFile.toObject());
953
0
954
0
      return result;
955
0
    }
956
0
957
0
    MOZ_ASSERT(file.mType == StructuredCloneFile::eWasmBytecode);
958
0
959
0
    JS::Rooted<JSObject*> wrappedModule(aCx, file.mWasmModule->createObject(aCx));
960
0
    if (NS_WARN_IF(!wrappedModule)) {
961
0
      return nullptr;
962
0
    }
963
0
964
0
    result.set(wrappedModule);
965
0
966
0
    return result;
967
0
  }
968
0
969
0
  return StructuredCloneHolder::ReadFullySerializableObjects(aCx, aReader,
970
0
                                                             aTag);
971
0
}
972
973
} // namespace
974
975
const JSClass IDBObjectStore::sDummyPropJSClass = {
976
  "IDBObjectStore Dummy",
977
  0 /* flags */
978
};
979
980
IDBObjectStore::IDBObjectStore(IDBTransaction* aTransaction,
981
                               const ObjectStoreSpec* aSpec)
982
  : mTransaction(aTransaction)
983
  , mCachedKeyPath(JS::UndefinedValue())
984
  , mSpec(aSpec)
985
  , mId(aSpec->metadata().id())
986
  , mRooted(false)
987
0
{
988
0
  MOZ_ASSERT(aTransaction);
989
0
  aTransaction->AssertIsOnOwningThread();
990
0
  MOZ_ASSERT(aSpec);
991
0
}
992
993
IDBObjectStore::~IDBObjectStore()
994
0
{
995
0
  AssertIsOnOwningThread();
996
0
997
0
  if (mRooted) {
998
0
    mCachedKeyPath.setUndefined();
999
0
    mozilla::DropJSObjects(this);
1000
0
  }
1001
0
}
1002
1003
// static
1004
already_AddRefed<IDBObjectStore>
1005
IDBObjectStore::Create(IDBTransaction* aTransaction,
1006
                       const ObjectStoreSpec& aSpec)
1007
0
{
1008
0
  MOZ_ASSERT(aTransaction);
1009
0
  aTransaction->AssertIsOnOwningThread();
1010
0
1011
0
  RefPtr<IDBObjectStore> objectStore =
1012
0
    new IDBObjectStore(aTransaction, &aSpec);
1013
0
1014
0
  return objectStore.forget();
1015
0
}
1016
1017
// static
1018
nsresult
1019
IDBObjectStore::AppendIndexUpdateInfo(
1020
                                    int64_t aIndexID,
1021
                                    const KeyPath& aKeyPath,
1022
                                    bool aUnique,
1023
                                    bool aMultiEntry,
1024
                                    const nsCString& aLocale,
1025
                                    JSContext* aCx,
1026
                                    JS::Handle<JS::Value> aVal,
1027
                                    nsTArray<IndexUpdateInfo>& aUpdateInfoArray)
1028
0
{
1029
0
  nsresult rv;
1030
0
1031
0
  const bool localeAware = !aLocale.IsEmpty();
1032
0
1033
0
  if (!aMultiEntry) {
1034
0
    Key key;
1035
0
    rv = aKeyPath.ExtractKey(aCx, aVal, key);
1036
0
1037
0
    // If an index's keyPath doesn't match an object, we ignore that object.
1038
0
    if (rv == NS_ERROR_DOM_INDEXEDDB_DATA_ERR || key.IsUnset()) {
1039
0
      return NS_OK;
1040
0
    }
1041
0
1042
0
    if (NS_FAILED(rv)) {
1043
0
      return rv;
1044
0
    }
1045
0
1046
0
    IndexUpdateInfo* updateInfo = aUpdateInfoArray.AppendElement();
1047
0
    updateInfo->indexId() = aIndexID;
1048
0
    updateInfo->value() = key;
1049
0
    if (localeAware) {
1050
0
      rv = key.ToLocaleBasedKey(updateInfo->localizedValue(), aLocale);
1051
0
      if (NS_WARN_IF(NS_FAILED(rv))) {
1052
0
        return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
1053
0
      }
1054
0
    }
1055
0
1056
0
    return NS_OK;
1057
0
  }
1058
0
1059
0
  JS::Rooted<JS::Value> val(aCx);
1060
0
  if (NS_FAILED(aKeyPath.ExtractKeyAsJSVal(aCx, aVal, val.address()))) {
1061
0
    return NS_OK;
1062
0
  }
1063
0
1064
0
  bool isArray;
1065
0
  if (!JS_IsArrayObject(aCx, val, &isArray)) {
1066
0
    IDB_REPORT_INTERNAL_ERR();
1067
0
    return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
1068
0
  }
1069
0
  if (isArray) {
1070
0
    JS::Rooted<JSObject*> array(aCx, &val.toObject());
1071
0
    uint32_t arrayLength;
1072
0
    if (NS_WARN_IF(!JS_GetArrayLength(aCx, array, &arrayLength))) {
1073
0
      IDB_REPORT_INTERNAL_ERR();
1074
0
      return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
1075
0
    }
1076
0
1077
0
    for (uint32_t arrayIndex = 0; arrayIndex < arrayLength; arrayIndex++) {
1078
0
      JS::Rooted<JS::Value> arrayItem(aCx);
1079
0
      if (NS_WARN_IF(!JS_GetElement(aCx, array, arrayIndex, &arrayItem))) {
1080
0
        IDB_REPORT_INTERNAL_ERR();
1081
0
        return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
1082
0
      }
1083
0
1084
0
      Key value;
1085
0
      if (NS_FAILED(value.SetFromJSVal(aCx, arrayItem)) ||
1086
0
          value.IsUnset()) {
1087
0
        // Not a value we can do anything with, ignore it.
1088
0
        continue;
1089
0
      }
1090
0
1091
0
      IndexUpdateInfo* updateInfo = aUpdateInfoArray.AppendElement();
1092
0
      updateInfo->indexId() = aIndexID;
1093
0
      updateInfo->value() = value;
1094
0
      if (localeAware) {
1095
0
        rv = value.ToLocaleBasedKey(updateInfo->localizedValue(), aLocale);
1096
0
        if (NS_WARN_IF(NS_FAILED(rv))) {
1097
0
          return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
1098
0
        }
1099
0
      }
1100
0
    }
1101
0
  }
1102
0
  else {
1103
0
    Key value;
1104
0
    if (NS_FAILED(value.SetFromJSVal(aCx, val)) ||
1105
0
        value.IsUnset()) {
1106
0
      // Not a value we can do anything with, ignore it.
1107
0
      return NS_OK;
1108
0
    }
1109
0
1110
0
    IndexUpdateInfo* updateInfo = aUpdateInfoArray.AppendElement();
1111
0
    updateInfo->indexId() = aIndexID;
1112
0
    updateInfo->value() = value;
1113
0
    if (localeAware) {
1114
0
      rv = value.ToLocaleBasedKey(updateInfo->localizedValue(), aLocale);
1115
0
      if (NS_WARN_IF(NS_FAILED(rv))) {
1116
0
        return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
1117
0
      }
1118
0
    }
1119
0
  }
1120
0
1121
0
  return NS_OK;
1122
0
}
1123
1124
// static
1125
void
1126
IDBObjectStore::ClearCloneReadInfo(StructuredCloneReadInfo& aReadInfo)
1127
0
{
1128
0
  // This is kind of tricky, we only want to release stuff on the main thread,
1129
0
  // but we can end up being called on other threads if we have already been
1130
0
  // cleared on the main thread.
1131
0
  if (!aReadInfo.mFiles.Length()) {
1132
0
    return;
1133
0
  }
1134
0
1135
0
  aReadInfo.mFiles.Clear();
1136
0
}
1137
1138
// static
1139
bool
1140
IDBObjectStore::DeserializeValue(JSContext* aCx,
1141
                                 StructuredCloneReadInfo& aCloneReadInfo,
1142
                                 JS::MutableHandle<JS::Value> aValue)
1143
0
{
1144
0
  MOZ_ASSERT(aCx);
1145
0
1146
0
  if (!aCloneReadInfo.mData.Size()) {
1147
0
    aValue.setUndefined();
1148
0
    return true;
1149
0
  }
1150
0
1151
0
  MOZ_ASSERT(!(aCloneReadInfo.mData.Size() % sizeof(uint64_t)));
1152
0
1153
0
  static const JSStructuredCloneCallbacks callbacks = {
1154
0
    CommonStructuredCloneReadCallback,
1155
0
    nullptr,
1156
0
    nullptr,
1157
0
    nullptr,
1158
0
    nullptr,
1159
0
    nullptr,
1160
0
    nullptr
1161
0
  };
1162
0
1163
0
  // FIXME: Consider to use StructuredCloneHolder here and in other
1164
0
  //        deserializing methods.
1165
0
  if (!JS_ReadStructuredClone(aCx, aCloneReadInfo.mData, JS_STRUCTURED_CLONE_VERSION,
1166
0
                              JS::StructuredCloneScope::DifferentProcessForIndexedDB,
1167
0
                              aValue, &callbacks, &aCloneReadInfo)) {
1168
0
    return false;
1169
0
  }
1170
0
1171
0
  return true;
1172
0
}
1173
1174
namespace {
1175
1176
// This class helps to create only 1 sandbox.
1177
class SandboxHolder final
1178
{
1179
public:
1180
  NS_INLINE_DECL_REFCOUNTING(SandboxHolder)
1181
1182
  static JSObject*
1183
  GetSandbox(JSContext* aCx)
1184
0
  {
1185
0
    SandboxHolder* holder = GetOrCreate();
1186
0
    return holder->GetSandboxInternal(aCx);
1187
0
  }
1188
1189
private:
1190
0
  ~SandboxHolder() = default;
1191
1192
  static SandboxHolder*
1193
  GetOrCreate()
1194
0
  {
1195
0
    MOZ_ASSERT(XRE_IsParentProcess());
1196
0
    MOZ_ASSERT(NS_IsMainThread());
1197
0
1198
0
    static StaticRefPtr<SandboxHolder> sHolder;
1199
0
    if (!sHolder) {
1200
0
      sHolder = new SandboxHolder();
1201
0
      ClearOnShutdown(&sHolder);
1202
0
    }
1203
0
    return sHolder;
1204
0
  }
1205
1206
  JSObject*
1207
  GetSandboxInternal(JSContext* aCx)
1208
0
  {
1209
0
    if (!mSandbox) {
1210
0
      nsIXPConnect* xpc = nsContentUtils::XPConnect();
1211
0
      MOZ_ASSERT(xpc, "This should never be null!");
1212
0
1213
0
      // Let's use a null principal.
1214
0
      nsCOMPtr<nsIPrincipal> principal = NullPrincipal::CreateWithoutOriginAttributes();
1215
0
1216
0
      JS::Rooted<JSObject*> sandbox(aCx);
1217
0
      nsresult rv = xpc->CreateSandbox(aCx, principal, sandbox.address());
1218
0
      if (NS_WARN_IF(NS_FAILED(rv))) {
1219
0
        return nullptr;
1220
0
      }
1221
0
1222
0
      mSandbox = new JSObjectHolder(aCx, sandbox);
1223
0
    }
1224
0
1225
0
    return mSandbox->GetJSObject();
1226
0
  }
1227
1228
  RefPtr<JSObjectHolder> mSandbox;
1229
};
1230
1231
class DeserializeIndexValueHelper final : public Runnable
1232
{
1233
public:
1234
  DeserializeIndexValueHelper(int64_t aIndexID,
1235
                              const KeyPath& aKeyPath,
1236
                              bool aUnique,
1237
                              bool aMultiEntry,
1238
                              const nsCString& aLocale,
1239
                              StructuredCloneReadInfo& aCloneReadInfo,
1240
                              nsTArray<IndexUpdateInfo>& aUpdateInfoArray)
1241
    : Runnable("DeserializeIndexValueHelper")
1242
    , mMonitor("DeserializeIndexValueHelper::mMonitor")
1243
    , mIndexID(aIndexID)
1244
    , mKeyPath(aKeyPath)
1245
    , mUnique(aUnique)
1246
    , mMultiEntry(aMultiEntry)
1247
    , mLocale(aLocale)
1248
    , mCloneReadInfo(aCloneReadInfo)
1249
    , mUpdateInfoArray(aUpdateInfoArray)
1250
    , mStatus(NS_ERROR_FAILURE)
1251
0
  {}
1252
1253
  nsresult
1254
  DispatchAndWait()
1255
0
  {
1256
0
    // We don't need to go to the main-thread and use the sandbox. Let's create
1257
0
    // the updateInfo data here.
1258
0
    if (!mCloneReadInfo.mData.Size()) {
1259
0
      AutoJSAPI jsapi;
1260
0
      jsapi.Init();
1261
0
1262
0
      JS::Rooted<JS::Value> value(jsapi.cx());
1263
0
      value.setUndefined();
1264
0
1265
0
      return IDBObjectStore::AppendIndexUpdateInfo(mIndexID, mKeyPath, mUnique,
1266
0
                                                   mMultiEntry, mLocale,
1267
0
                                                   jsapi.cx(), value,
1268
0
                                                   mUpdateInfoArray);
1269
0
    }
1270
0
1271
0
    // The operation will continue on the main-thread.
1272
0
1273
0
    MOZ_ASSERT(!(mCloneReadInfo.mData.Size() % sizeof(uint64_t)));
1274
0
1275
0
    MonitorAutoLock lock(mMonitor);
1276
0
1277
0
    RefPtr<Runnable> self = this;
1278
0
    nsresult rv = SystemGroup::Dispatch(TaskCategory::Other, self.forget());
1279
0
    if (NS_WARN_IF(NS_FAILED(rv))) {
1280
0
      return rv;
1281
0
    }
1282
0
1283
0
    lock.Wait();
1284
0
    return mStatus;
1285
0
  }
1286
1287
  NS_IMETHOD
1288
  Run() override
1289
0
  {
1290
0
    MOZ_ASSERT(NS_IsMainThread());
1291
0
1292
0
    AutoJSAPI jsapi;
1293
0
    jsapi.Init();
1294
0
    JSContext* cx = jsapi.cx();
1295
0
1296
0
    JS::Rooted<JSObject*> global(cx, SandboxHolder::GetSandbox(cx));
1297
0
    if (NS_WARN_IF(!global)) {
1298
0
      OperationCompleted(NS_ERROR_FAILURE);
1299
0
      return NS_OK;
1300
0
    }
1301
0
1302
0
    JSAutoRealm ar(cx, global);
1303
0
1304
0
    JS::Rooted<JS::Value> value(cx);
1305
0
    nsresult rv = DeserializeIndexValue(cx, &value);
1306
0
    if (NS_WARN_IF(NS_FAILED(rv))) {
1307
0
      OperationCompleted(rv);
1308
0
      return NS_OK;
1309
0
    }
1310
0
1311
0
    rv = IDBObjectStore::AppendIndexUpdateInfo(mIndexID, mKeyPath, mUnique,
1312
0
                                               mMultiEntry, mLocale, cx,
1313
0
                                               value, mUpdateInfoArray);
1314
0
    if (NS_WARN_IF(NS_FAILED(rv))) {
1315
0
      OperationCompleted(rv);
1316
0
      return NS_OK;
1317
0
    }
1318
0
1319
0
    OperationCompleted(NS_OK);
1320
0
    return NS_OK;
1321
0
  }
1322
1323
private:
1324
  nsresult
1325
  DeserializeIndexValue(JSContext* aCx, JS::MutableHandle<JS::Value> aValue)
1326
0
  {
1327
0
    static const JSStructuredCloneCallbacks callbacks = {
1328
0
      CommonStructuredCloneReadCallback,
1329
0
      nullptr,
1330
0
      nullptr,
1331
0
      nullptr,
1332
0
      nullptr,
1333
0
      nullptr,
1334
0
      nullptr
1335
0
    };
1336
0
1337
0
    if (!JS_ReadStructuredClone(aCx, mCloneReadInfo.mData,
1338
0
                                JS_STRUCTURED_CLONE_VERSION,
1339
0
                                JS::StructuredCloneScope::DifferentProcessForIndexedDB,
1340
0
                                aValue, &callbacks, &mCloneReadInfo)) {
1341
0
      return NS_ERROR_DOM_DATA_CLONE_ERR;
1342
0
    }
1343
0
1344
0
    return NS_OK;
1345
0
  }
1346
1347
  void
1348
  OperationCompleted(nsresult aStatus)
1349
0
  {
1350
0
    mStatus = aStatus;
1351
0
1352
0
    MonitorAutoLock lock(mMonitor);
1353
0
    lock.Notify();
1354
0
  }
1355
1356
  Monitor mMonitor;
1357
1358
  int64_t mIndexID;
1359
  const KeyPath& mKeyPath;
1360
  bool mUnique;
1361
  bool mMultiEntry;
1362
  const nsCString mLocale;
1363
  StructuredCloneReadInfo& mCloneReadInfo;
1364
  nsTArray<IndexUpdateInfo>& mUpdateInfoArray;
1365
  nsresult mStatus;
1366
};
1367
1368
class DeserializeUpgradeValueHelper final : public Runnable
1369
{
1370
public:
1371
  explicit DeserializeUpgradeValueHelper(StructuredCloneReadInfo& aCloneReadInfo)
1372
    : Runnable("DeserializeUpgradeValueHelper")
1373
    , mMonitor("DeserializeUpgradeValueHelper::mMonitor")
1374
    , mCloneReadInfo(aCloneReadInfo)
1375
    , mStatus(NS_ERROR_FAILURE)
1376
0
  {}
1377
1378
  nsresult
1379
  DispatchAndWait(nsAString& aFileIds)
1380
0
  {
1381
0
    // We don't need to go to the main-thread and use the sandbox.
1382
0
    if (!mCloneReadInfo.mData.Size()) {
1383
0
      PopulateFileIds(aFileIds);
1384
0
      return NS_OK;
1385
0
    }
1386
0
1387
0
    // The operation will continue on the main-thread.
1388
0
1389
0
    MOZ_ASSERT(!(mCloneReadInfo.mData.Size() % sizeof(uint64_t)));
1390
0
1391
0
    MonitorAutoLock lock(mMonitor);
1392
0
1393
0
    RefPtr<Runnable> self = this;
1394
0
    nsresult rv = SystemGroup::Dispatch(TaskCategory::Other, self.forget());
1395
0
    if (NS_WARN_IF(NS_FAILED(rv))) {
1396
0
      return rv;
1397
0
    }
1398
0
1399
0
    lock.Wait();
1400
0
1401
0
    if (NS_FAILED(mStatus)) {
1402
0
      return mStatus;
1403
0
    }
1404
0
1405
0
    PopulateFileIds(aFileIds);
1406
0
    return NS_OK;
1407
0
  }
1408
1409
  NS_IMETHOD
1410
  Run() override
1411
0
  {
1412
0
    MOZ_ASSERT(NS_IsMainThread());
1413
0
1414
0
    AutoJSAPI jsapi;
1415
0
    jsapi.Init();
1416
0
    JSContext* cx = jsapi.cx();
1417
0
1418
0
    JS::Rooted<JSObject*> global(cx, SandboxHolder::GetSandbox(cx));
1419
0
    if (NS_WARN_IF(!global)) {
1420
0
      OperationCompleted(NS_ERROR_FAILURE);
1421
0
      return NS_OK;
1422
0
    }
1423
0
1424
0
    JSAutoRealm ar(cx, global);
1425
0
1426
0
    JS::Rooted<JS::Value> value(cx);
1427
0
    nsresult rv = DeserializeUpgradeValue(cx, &value);
1428
0
    if (NS_WARN_IF(NS_FAILED(rv))) {
1429
0
      OperationCompleted(rv);
1430
0
      return NS_OK;
1431
0
    }
1432
0
1433
0
    OperationCompleted(NS_OK);
1434
0
    return NS_OK;
1435
0
  }
1436
1437
private:
1438
  nsresult
1439
  DeserializeUpgradeValue(JSContext* aCx, JS::MutableHandle<JS::Value> aValue)
1440
0
  {
1441
0
    static const JSStructuredCloneCallbacks callbacks = {
1442
0
      CommonStructuredCloneReadCallback,
1443
0
      nullptr,
1444
0
      nullptr,
1445
0
      nullptr,
1446
0
      nullptr,
1447
0
      nullptr,
1448
0
      nullptr
1449
0
    };
1450
0
1451
0
    if (!JS_ReadStructuredClone(aCx, mCloneReadInfo.mData,
1452
0
                                JS_STRUCTURED_CLONE_VERSION,
1453
0
                                JS::StructuredCloneScope::DifferentProcessForIndexedDB,
1454
0
                                aValue, &callbacks, &mCloneReadInfo)) {
1455
0
      return NS_ERROR_DOM_DATA_CLONE_ERR;
1456
0
    }
1457
0
1458
0
    return NS_OK;
1459
0
  }
1460
1461
  void
1462
  PopulateFileIds(nsAString& aFileIds)
1463
0
  {
1464
0
    for (uint32_t count = mCloneReadInfo.mFiles.Length(), index = 0;
1465
0
         index < count;
1466
0
         index++) {
1467
0
      StructuredCloneFile& file = mCloneReadInfo.mFiles[index];
1468
0
      MOZ_ASSERT(file.mFileInfo);
1469
0
1470
0
      const int64_t id = file.mFileInfo->Id();
1471
0
1472
0
      if (index) {
1473
0
        aFileIds.Append(' ');
1474
0
      }
1475
0
      aFileIds.AppendInt(file.mType == StructuredCloneFile::eBlob ? id : -id);
1476
0
    }
1477
0
  }
1478
1479
  void
1480
  OperationCompleted(nsresult aStatus)
1481
0
  {
1482
0
    mStatus = aStatus;
1483
0
1484
0
    MonitorAutoLock lock(mMonitor);
1485
0
    lock.Notify();
1486
0
  }
1487
1488
  Monitor mMonitor;
1489
  StructuredCloneReadInfo& mCloneReadInfo;
1490
  nsresult mStatus;
1491
};
1492
1493
} // anonymous
1494
1495
// static
1496
nsresult
1497
IDBObjectStore::DeserializeIndexValueToUpdateInfos(int64_t aIndexID,
1498
                                                   const KeyPath& aKeyPath,
1499
                                                   bool aUnique,
1500
                                                   bool aMultiEntry,
1501
                                                   const nsCString& aLocale,
1502
                                                   StructuredCloneReadInfo& aCloneReadInfo,
1503
                                                   nsTArray<IndexUpdateInfo>& aUpdateInfoArray)
1504
0
{
1505
0
  MOZ_ASSERT(!NS_IsMainThread());
1506
0
1507
0
  RefPtr<DeserializeIndexValueHelper> helper =
1508
0
    new DeserializeIndexValueHelper(aIndexID, aKeyPath, aUnique, aMultiEntry,
1509
0
                                    aLocale, aCloneReadInfo, aUpdateInfoArray);
1510
0
  return helper->DispatchAndWait();
1511
0
}
1512
1513
// static
1514
nsresult
1515
IDBObjectStore::DeserializeUpgradeValueToFileIds(StructuredCloneReadInfo& aCloneReadInfo,
1516
                                                 nsAString& aFileIds)
1517
0
{
1518
0
  MOZ_ASSERT(!NS_IsMainThread());
1519
0
1520
0
  RefPtr<DeserializeUpgradeValueHelper> helper =
1521
0
    new DeserializeUpgradeValueHelper(aCloneReadInfo);
1522
0
  return helper->DispatchAndWait(aFileIds);
1523
0
}
1524
1525
#ifdef DEBUG
1526
1527
void
1528
IDBObjectStore::AssertIsOnOwningThread() const
1529
{
1530
  MOZ_ASSERT(mTransaction);
1531
  mTransaction->AssertIsOnOwningThread();
1532
}
1533
1534
#endif // DEBUG
1535
1536
nsresult
1537
IDBObjectStore::GetAddInfo(JSContext* aCx,
1538
                           ValueWrapper& aValueWrapper,
1539
                           JS::Handle<JS::Value> aKeyVal,
1540
                           StructuredCloneWriteInfo& aCloneWriteInfo,
1541
                           Key& aKey,
1542
                           nsTArray<IndexUpdateInfo>& aUpdateInfoArray)
1543
0
{
1544
0
  // Return DATA_ERR if a key was passed in and this objectStore uses inline
1545
0
  // keys.
1546
0
  if (!aKeyVal.isUndefined() && HasValidKeyPath()) {
1547
0
    return NS_ERROR_DOM_INDEXEDDB_DATA_ERR;
1548
0
  }
1549
0
1550
0
  bool isAutoIncrement = AutoIncrement();
1551
0
1552
0
  nsresult rv;
1553
0
1554
0
  if (!HasValidKeyPath()) {
1555
0
    // Out-of-line keys must be passed in.
1556
0
    rv = aKey.SetFromJSVal(aCx, aKeyVal);
1557
0
    if (NS_FAILED(rv)) {
1558
0
      return rv;
1559
0
    }
1560
0
  } else if (!isAutoIncrement) {
1561
0
    if (!aValueWrapper.Clone(aCx)) {
1562
0
      return NS_ERROR_DOM_DATA_CLONE_ERR;
1563
0
    }
1564
0
1565
0
    rv = GetKeyPath().ExtractKey(aCx, aValueWrapper.Value(), aKey);
1566
0
    if (NS_FAILED(rv)) {
1567
0
      return rv;
1568
0
    }
1569
0
  }
1570
0
1571
0
  // Return DATA_ERR if no key was specified this isn't an autoIncrement
1572
0
  // objectStore.
1573
0
  if (aKey.IsUnset() && !isAutoIncrement) {
1574
0
    return NS_ERROR_DOM_INDEXEDDB_DATA_ERR;
1575
0
  }
1576
0
1577
0
  // Figure out indexes and the index values to update here.
1578
0
  const nsTArray<IndexMetadata>& indexes = mSpec->indexes();
1579
0
1580
0
  uint32_t idxCount = indexes.Length();
1581
0
1582
0
  if (idxCount) {
1583
0
    if (!aValueWrapper.Clone(aCx)) {
1584
0
      return NS_ERROR_DOM_DATA_CLONE_ERR;
1585
0
    }
1586
0
1587
0
    // Update idxCount, the structured clone process may trigger content code
1588
0
    // via getters/other which can potentially call CreateIndex/DeleteIndex.
1589
0
    idxCount = indexes.Length();
1590
0
  }
1591
0
1592
0
  aUpdateInfoArray.SetCapacity(idxCount); // Pretty good estimate
1593
0
1594
0
  for (uint32_t idxIndex = 0; idxIndex < idxCount; idxIndex++) {
1595
0
    const IndexMetadata& metadata = indexes[idxIndex];
1596
0
1597
0
    rv = AppendIndexUpdateInfo(metadata.id(), metadata.keyPath(),
1598
0
                               metadata.unique(), metadata.multiEntry(),
1599
0
                               metadata.locale(), aCx, aValueWrapper.Value(),
1600
0
                               aUpdateInfoArray);
1601
0
    if (NS_WARN_IF(NS_FAILED(rv))) {
1602
0
      return rv;
1603
0
    }
1604
0
  }
1605
0
1606
0
1607
0
  if (isAutoIncrement && HasValidKeyPath()) {
1608
0
    if (!aValueWrapper.Clone(aCx)) {
1609
0
      return NS_ERROR_DOM_DATA_CLONE_ERR;
1610
0
    }
1611
0
1612
0
    GetAddInfoClosure data(aCloneWriteInfo, aValueWrapper.Value());
1613
0
1614
0
    MOZ_ASSERT(aKey.IsUnset());
1615
0
1616
0
    rv = GetKeyPath().ExtractOrCreateKey(aCx,
1617
0
                                         aValueWrapper.Value(),
1618
0
                                         aKey,
1619
0
                                         &GetAddInfoCallback,
1620
0
                                         &data);
1621
0
  } else {
1622
0
    GetAddInfoClosure data(aCloneWriteInfo, aValueWrapper.Value());
1623
0
1624
0
    rv = GetAddInfoCallback(aCx, &data);
1625
0
  }
1626
0
1627
0
  return rv;
1628
0
}
1629
1630
already_AddRefed<IDBRequest>
1631
IDBObjectStore::AddOrPut(JSContext* aCx,
1632
                         ValueWrapper& aValueWrapper,
1633
                         JS::Handle<JS::Value> aKey,
1634
                         bool aOverwrite,
1635
                         bool aFromCursor,
1636
                         ErrorResult& aRv)
1637
0
{
1638
0
  AssertIsOnOwningThread();
1639
0
  MOZ_ASSERT(aCx);
1640
0
  MOZ_ASSERT_IF(aFromCursor, aOverwrite);
1641
0
1642
0
  if (mTransaction->GetMode() == IDBTransaction::CLEANUP ||
1643
0
      mDeletedSpec) {
1644
0
    aRv.Throw(NS_ERROR_DOM_INDEXEDDB_NOT_ALLOWED_ERR);
1645
0
    return nullptr;
1646
0
  }
1647
0
1648
0
  if (!mTransaction->IsOpen()) {
1649
0
    aRv.Throw(NS_ERROR_DOM_INDEXEDDB_TRANSACTION_INACTIVE_ERR);
1650
0
    return nullptr;
1651
0
  }
1652
0
1653
0
  if (!mTransaction->IsWriteAllowed()) {
1654
0
    aRv.Throw(NS_ERROR_DOM_INDEXEDDB_READ_ONLY_ERR);
1655
0
    return nullptr;
1656
0
  }
1657
0
1658
0
  Key key;
1659
0
  StructuredCloneWriteInfo cloneWriteInfo(mTransaction->Database());
1660
0
  nsTArray<IndexUpdateInfo> updateInfo;
1661
0
1662
0
  aRv = GetAddInfo(aCx, aValueWrapper, aKey, cloneWriteInfo, key, updateInfo);
1663
0
  if (aRv.Failed()) {
1664
0
    return nullptr;
1665
0
  }
1666
0
1667
0
  // Check the size limit of the serialized message which mainly consists of
1668
0
  // a StructuredCloneBuffer, an encoded object key, and the encoded index keys.
1669
0
  // kMaxIDBMsgOverhead covers the minor stuff not included in this calculation
1670
0
  // because the precise calculation would slow down this AddOrPut operation.
1671
0
  static const size_t kMaxIDBMsgOverhead = 1024 * 1024; // 1MB
1672
0
  const uint32_t maximalSizeFromPref =
1673
0
    IndexedDatabaseManager::MaxSerializedMsgSize();
1674
0
  MOZ_ASSERT(maximalSizeFromPref > kMaxIDBMsgOverhead);
1675
0
  const size_t kMaxMessageSize = maximalSizeFromPref - kMaxIDBMsgOverhead;
1676
0
1677
0
  size_t indexUpdateInfoSize = 0;
1678
0
  for (size_t i = 0; i < updateInfo.Length(); i++) {
1679
0
    indexUpdateInfoSize += updateInfo[i].value().GetBuffer().Length();
1680
0
    indexUpdateInfoSize += updateInfo[i].localizedValue().GetBuffer().Length();
1681
0
  }
1682
0
1683
0
  size_t messageSize = cloneWriteInfo.mCloneBuffer.data().Size() +
1684
0
    key.GetBuffer().Length() + indexUpdateInfoSize;
1685
0
1686
0
  if (messageSize > kMaxMessageSize) {
1687
0
    IDB_REPORT_INTERNAL_ERR();
1688
0
    aRv.ThrowDOMException(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR,
1689
0
      nsPrintfCString("The serialized value is too large"
1690
0
                      " (size=%zu bytes, max=%zu bytes).",
1691
0
                      messageSize, kMaxMessageSize));
1692
0
    return nullptr;
1693
0
  }
1694
0
1695
0
  ObjectStoreAddPutParams commonParams;
1696
0
  commonParams.objectStoreId() = Id();
1697
0
  commonParams.cloneInfo().data().data = std::move(cloneWriteInfo.mCloneBuffer.data());
1698
0
  commonParams.cloneInfo().offsetToKeyProp() = cloneWriteInfo.mOffsetToKeyProp;
1699
0
  commonParams.key() = key;
1700
0
  commonParams.indexUpdateInfos().SwapElements(updateInfo);
1701
0
1702
0
  // Convert any blobs or mutable files into FileAddInfo.
1703
0
  nsTArray<StructuredCloneFile>& files = cloneWriteInfo.mFiles;
1704
0
1705
0
  if (!files.IsEmpty()) {
1706
0
    const uint32_t count = files.Length();
1707
0
1708
0
    FallibleTArray<FileAddInfo> fileAddInfos;
1709
0
    if (NS_WARN_IF(!fileAddInfos.SetCapacity(count, fallible))) {
1710
0
      aRv = NS_ERROR_OUT_OF_MEMORY;
1711
0
      return nullptr;
1712
0
    }
1713
0
1714
0
    IDBDatabase* database = mTransaction->Database();
1715
0
1716
0
    for (uint32_t index = 0; index < count; index++) {
1717
0
      StructuredCloneFile& file = files[index];
1718
0
1719
0
      FileAddInfo* fileAddInfo = fileAddInfos.AppendElement(fallible);
1720
0
      MOZ_ASSERT(fileAddInfo);
1721
0
1722
0
      switch (file.mType) {
1723
0
        case StructuredCloneFile::eBlob: {
1724
0
          MOZ_ASSERT(file.mBlob);
1725
0
          MOZ_ASSERT(!file.mMutableFile);
1726
0
1727
0
          PBackgroundIDBDatabaseFileChild* fileActor =
1728
0
            database->GetOrCreateFileActorForBlob(file.mBlob);
1729
0
          if (NS_WARN_IF(!fileActor)) {
1730
0
            IDB_REPORT_INTERNAL_ERR();
1731
0
            aRv = NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
1732
0
            return nullptr;
1733
0
          }
1734
0
1735
0
          fileAddInfo->file() = fileActor;
1736
0
          fileAddInfo->type() = StructuredCloneFile::eBlob;
1737
0
1738
0
          break;
1739
0
        }
1740
0
1741
0
        case StructuredCloneFile::eMutableFile: {
1742
0
          MOZ_ASSERT(file.mMutableFile);
1743
0
          MOZ_ASSERT(!file.mBlob);
1744
0
1745
0
          PBackgroundMutableFileChild* mutableFileActor =
1746
0
            file.mMutableFile->GetBackgroundActor();
1747
0
          if (NS_WARN_IF(!mutableFileActor)) {
1748
0
            IDB_REPORT_INTERNAL_ERR();
1749
0
            aRv = NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
1750
0
            return nullptr;
1751
0
          }
1752
0
1753
0
          fileAddInfo->file() = mutableFileActor;
1754
0
          fileAddInfo->type() = StructuredCloneFile::eMutableFile;
1755
0
1756
0
          break;
1757
0
        }
1758
0
1759
0
        case StructuredCloneFile::eWasmBytecode:
1760
0
        case StructuredCloneFile::eWasmCompiled: {
1761
0
          MOZ_ASSERT(file.mBlob);
1762
0
          MOZ_ASSERT(!file.mMutableFile);
1763
0
1764
0
          PBackgroundIDBDatabaseFileChild* fileActor =
1765
0
            database->GetOrCreateFileActorForBlob(file.mBlob);
1766
0
          if (NS_WARN_IF(!fileActor)) {
1767
0
            IDB_REPORT_INTERNAL_ERR();
1768
0
            aRv = NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
1769
0
            return nullptr;
1770
0
          }
1771
0
1772
0
          fileAddInfo->file() = fileActor;
1773
0
          fileAddInfo->type() = file.mType;
1774
0
1775
0
          break;
1776
0
        }
1777
0
1778
0
        default:
1779
0
          MOZ_CRASH("Should never get here!");
1780
0
      }
1781
0
    }
1782
0
1783
0
    commonParams.fileAddInfos().SwapElements(fileAddInfos);
1784
0
  }
1785
0
1786
0
  RequestParams params;
1787
0
  if (aOverwrite) {
1788
0
    params = ObjectStorePutParams(commonParams);
1789
0
  } else {
1790
0
    params = ObjectStoreAddParams(commonParams);
1791
0
  }
1792
0
1793
0
  RefPtr<IDBRequest> request = GenerateRequest(aCx, this);
1794
0
  MOZ_ASSERT(request);
1795
0
1796
0
  if (!aFromCursor) {
1797
0
    if (aOverwrite) {
1798
0
      IDB_LOG_MARK("IndexedDB %s: Child  Transaction[%lld] Request[%llu]: "
1799
0
                     "database(%s).transaction(%s).objectStore(%s).put(%s)",
1800
0
                   "IndexedDB %s: C T[%lld] R[%llu]: IDBObjectStore.put()",
1801
0
                   IDB_LOG_ID_STRING(),
1802
0
                   mTransaction->LoggingSerialNumber(),
1803
0
                   request->LoggingSerialNumber(),
1804
0
                   IDB_LOG_STRINGIFY(mTransaction->Database()),
1805
0
                   IDB_LOG_STRINGIFY(mTransaction),
1806
0
                   IDB_LOG_STRINGIFY(this),
1807
0
                   IDB_LOG_STRINGIFY(key));
1808
0
    } else {
1809
0
      IDB_LOG_MARK("IndexedDB %s: Child  Transaction[%lld] Request[%llu]: "
1810
0
                     "database(%s).transaction(%s).objectStore(%s).add(%s)",
1811
0
                   "IndexedDB %s: C T[%lld] R[%llu]: IDBObjectStore.add()",
1812
0
                   IDB_LOG_ID_STRING(),
1813
0
                   mTransaction->LoggingSerialNumber(),
1814
0
                   request->LoggingSerialNumber(),
1815
0
                   IDB_LOG_STRINGIFY(mTransaction->Database()),
1816
0
                   IDB_LOG_STRINGIFY(mTransaction),
1817
0
                   IDB_LOG_STRINGIFY(this),
1818
0
                   IDB_LOG_STRINGIFY(key));
1819
0
    }
1820
0
  }
1821
0
1822
0
  mTransaction->StartRequest(request, params);
1823
0
1824
0
  return request.forget();
1825
0
}
1826
1827
already_AddRefed<IDBRequest>
1828
IDBObjectStore::GetAllInternal(bool aKeysOnly,
1829
                               JSContext* aCx,
1830
                               JS::Handle<JS::Value> aKey,
1831
                               const Optional<uint32_t>& aLimit,
1832
                               ErrorResult& aRv)
1833
0
{
1834
0
  AssertIsOnOwningThread();
1835
0
1836
0
  if (mDeletedSpec) {
1837
0
    aRv.Throw(NS_ERROR_DOM_INDEXEDDB_NOT_ALLOWED_ERR);
1838
0
    return nullptr;
1839
0
  }
1840
0
1841
0
  if (!mTransaction->IsOpen()) {
1842
0
    aRv.Throw(NS_ERROR_DOM_INDEXEDDB_TRANSACTION_INACTIVE_ERR);
1843
0
    return nullptr;
1844
0
  }
1845
0
1846
0
  RefPtr<IDBKeyRange> keyRange;
1847
0
  aRv = IDBKeyRange::FromJSVal(aCx, aKey, getter_AddRefs(keyRange));
1848
0
  if (NS_WARN_IF(aRv.Failed())) {
1849
0
    return nullptr;
1850
0
  }
1851
0
1852
0
  const int64_t id = Id();
1853
0
1854
0
  OptionalKeyRange optionalKeyRange;
1855
0
  if (keyRange) {
1856
0
    SerializedKeyRange serializedKeyRange;
1857
0
    keyRange->ToSerialized(serializedKeyRange);
1858
0
    optionalKeyRange = serializedKeyRange;
1859
0
  } else {
1860
0
    optionalKeyRange = void_t();
1861
0
  }
1862
0
1863
0
  const uint32_t limit = aLimit.WasPassed() ? aLimit.Value() : 0;
1864
0
1865
0
  RequestParams params;
1866
0
  if (aKeysOnly) {
1867
0
    params = ObjectStoreGetAllKeysParams(id, optionalKeyRange, limit);
1868
0
  } else {
1869
0
    params = ObjectStoreGetAllParams(id, optionalKeyRange, limit);
1870
0
  }
1871
0
1872
0
  RefPtr<IDBRequest> request = GenerateRequest(aCx, this);
1873
0
  MOZ_ASSERT(request);
1874
0
1875
0
  if (aKeysOnly) {
1876
0
    IDB_LOG_MARK("IndexedDB %s: Child  Transaction[%lld] Request[%llu]: "
1877
0
                   "database(%s).transaction(%s).objectStore(%s)."
1878
0
                   "getAllKeys(%s, %s)",
1879
0
                 "IndexedDB %s: C T[%lld] R[%llu]: IDBObjectStore.getAllKeys()",
1880
0
                 IDB_LOG_ID_STRING(),
1881
0
                 mTransaction->LoggingSerialNumber(),
1882
0
                 request->LoggingSerialNumber(),
1883
0
                 IDB_LOG_STRINGIFY(mTransaction->Database()),
1884
0
                 IDB_LOG_STRINGIFY(mTransaction),
1885
0
                 IDB_LOG_STRINGIFY(this),
1886
0
                 IDB_LOG_STRINGIFY(keyRange),
1887
0
                 IDB_LOG_STRINGIFY(aLimit));
1888
0
  } else {
1889
0
    IDB_LOG_MARK("IndexedDB %s: Child  Transaction[%lld] Request[%llu]: "
1890
0
                   "database(%s).transaction(%s).objectStore(%s)."
1891
0
                   "getAll(%s, %s)",
1892
0
                 "IndexedDB %s: C T[%lld] R[%llu]: IDBObjectStore.getAll()",
1893
0
                 IDB_LOG_ID_STRING(),
1894
0
                 mTransaction->LoggingSerialNumber(),
1895
0
                 request->LoggingSerialNumber(),
1896
0
                 IDB_LOG_STRINGIFY(mTransaction->Database()),
1897
0
                 IDB_LOG_STRINGIFY(mTransaction),
1898
0
                 IDB_LOG_STRINGIFY(this),
1899
0
                 IDB_LOG_STRINGIFY(keyRange),
1900
0
                 IDB_LOG_STRINGIFY(aLimit));
1901
0
  }
1902
0
1903
0
  mTransaction->StartRequest(request, params);
1904
0
1905
0
  return request.forget();
1906
0
}
1907
1908
already_AddRefed<IDBRequest>
1909
IDBObjectStore::Clear(JSContext* aCx, ErrorResult& aRv)
1910
0
{
1911
0
  AssertIsOnOwningThread();
1912
0
1913
0
  if (mDeletedSpec) {
1914
0
    aRv.Throw(NS_ERROR_DOM_INDEXEDDB_NOT_ALLOWED_ERR);
1915
0
    return nullptr;
1916
0
  }
1917
0
1918
0
  if (!mTransaction->IsOpen()) {
1919
0
    aRv.Throw(NS_ERROR_DOM_INDEXEDDB_TRANSACTION_INACTIVE_ERR);
1920
0
    return nullptr;
1921
0
  }
1922
0
1923
0
  if (!mTransaction->IsWriteAllowed()) {
1924
0
    aRv.Throw(NS_ERROR_DOM_INDEXEDDB_READ_ONLY_ERR);
1925
0
    return nullptr;
1926
0
  }
1927
0
1928
0
  ObjectStoreClearParams params;
1929
0
  params.objectStoreId() = Id();
1930
0
1931
0
  RefPtr<IDBRequest> request = GenerateRequest(aCx, this);
1932
0
  MOZ_ASSERT(request);
1933
0
1934
0
  IDB_LOG_MARK("IndexedDB %s: Child  Transaction[%lld] Request[%llu]: "
1935
0
                 "database(%s).transaction(%s).objectStore(%s).clear()",
1936
0
               "IndexedDB %s: C T[%lld] R[%llu]: IDBObjectStore.clear()",
1937
0
               IDB_LOG_ID_STRING(),
1938
0
               mTransaction->LoggingSerialNumber(),
1939
0
               request->LoggingSerialNumber(),
1940
0
               IDB_LOG_STRINGIFY(mTransaction->Database()),
1941
0
               IDB_LOG_STRINGIFY(mTransaction),
1942
0
               IDB_LOG_STRINGIFY(this));
1943
0
1944
0
  mTransaction->StartRequest(request, params);
1945
0
1946
0
  return request.forget();
1947
0
}
1948
1949
already_AddRefed<IDBIndex>
1950
IDBObjectStore::Index(const nsAString& aName, ErrorResult &aRv)
1951
0
{
1952
0
  AssertIsOnOwningThread();
1953
0
1954
0
  if (mTransaction->IsCommittingOrDone() || mDeletedSpec) {
1955
0
    aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
1956
0
    return nullptr;
1957
0
  }
1958
0
1959
0
  const nsTArray<IndexMetadata>& indexes = mSpec->indexes();
1960
0
1961
0
  const IndexMetadata* metadata = nullptr;
1962
0
1963
0
  for (uint32_t idxCount = indexes.Length(), idxIndex = 0;
1964
0
       idxIndex < idxCount;
1965
0
       idxIndex++) {
1966
0
    const IndexMetadata& index = indexes[idxIndex];
1967
0
    if (index.name() == aName) {
1968
0
      metadata = &index;
1969
0
      break;
1970
0
    }
1971
0
  }
1972
0
1973
0
  if (!metadata) {
1974
0
    aRv.Throw(NS_ERROR_DOM_INDEXEDDB_NOT_FOUND_ERR);
1975
0
    return nullptr;
1976
0
  }
1977
0
1978
0
  const int64_t desiredId = metadata->id();
1979
0
1980
0
  RefPtr<IDBIndex> index;
1981
0
1982
0
  for (uint32_t idxCount = mIndexes.Length(), idxIndex = 0;
1983
0
       idxIndex < idxCount;
1984
0
       idxIndex++) {
1985
0
    RefPtr<IDBIndex>& existingIndex = mIndexes[idxIndex];
1986
0
1987
0
    if (existingIndex->Id() == desiredId) {
1988
0
      index = existingIndex;
1989
0
      break;
1990
0
    }
1991
0
  }
1992
0
1993
0
  if (!index) {
1994
0
    index = IDBIndex::Create(this, *metadata);
1995
0
    MOZ_ASSERT(index);
1996
0
1997
0
    mIndexes.AppendElement(index);
1998
0
  }
1999
0
2000
0
  return index.forget();
2001
0
}
2002
2003
NS_IMPL_CYCLE_COLLECTION_CLASS(IDBObjectStore)
2004
2005
0
NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(IDBObjectStore)
2006
0
  NS_IMPL_CYCLE_COLLECTION_TRACE_PRESERVED_WRAPPER
2007
0
  NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mCachedKeyPath)
2008
0
NS_IMPL_CYCLE_COLLECTION_TRACE_END
2009
2010
0
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(IDBObjectStore)
2011
0
  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mTransaction)
2012
0
  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mIndexes)
2013
0
  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDeletedIndexes)
2014
0
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
2015
2016
0
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(IDBObjectStore)
2017
0
  NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
2018
0
2019
0
  // Don't unlink mTransaction!
2020
0
2021
0
  NS_IMPL_CYCLE_COLLECTION_UNLINK(mIndexes)
2022
0
  NS_IMPL_CYCLE_COLLECTION_UNLINK(mDeletedIndexes)
2023
0
2024
0
  tmp->mCachedKeyPath.setUndefined();
2025
0
2026
0
  if (tmp->mRooted) {
2027
0
    mozilla::DropJSObjects(tmp);
2028
0
    tmp->mRooted = false;
2029
0
  }
2030
0
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
2031
2032
0
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(IDBObjectStore)
2033
0
  NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
2034
0
  NS_INTERFACE_MAP_ENTRY(nsISupports)
2035
0
NS_INTERFACE_MAP_END
2036
2037
NS_IMPL_CYCLE_COLLECTING_ADDREF(IDBObjectStore)
2038
NS_IMPL_CYCLE_COLLECTING_RELEASE(IDBObjectStore)
2039
2040
JSObject*
2041
IDBObjectStore::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
2042
0
{
2043
0
  return IDBObjectStore_Binding::Wrap(aCx, this, aGivenProto);
2044
0
}
2045
2046
nsPIDOMWindowInner*
2047
IDBObjectStore::GetParentObject() const
2048
0
{
2049
0
  return mTransaction->GetParentObject();
2050
0
}
2051
2052
void
2053
IDBObjectStore::GetKeyPath(JSContext* aCx,
2054
                           JS::MutableHandle<JS::Value> aResult,
2055
                           ErrorResult& aRv)
2056
0
{
2057
0
  if (!mCachedKeyPath.isUndefined()) {
2058
0
    aResult.set(mCachedKeyPath);
2059
0
    return;
2060
0
  }
2061
0
2062
0
  aRv = GetKeyPath().ToJSVal(aCx, mCachedKeyPath);
2063
0
  if (NS_WARN_IF(aRv.Failed())) {
2064
0
    return;
2065
0
  }
2066
0
2067
0
  if (mCachedKeyPath.isGCThing()) {
2068
0
    mozilla::HoldJSObjects(this);
2069
0
    mRooted = true;
2070
0
  }
2071
0
2072
0
  aResult.set(mCachedKeyPath);
2073
0
}
2074
2075
already_AddRefed<DOMStringList>
2076
IDBObjectStore::IndexNames()
2077
0
{
2078
0
  AssertIsOnOwningThread();
2079
0
2080
0
  const nsTArray<IndexMetadata>& indexes = mSpec->indexes();
2081
0
2082
0
  RefPtr<DOMStringList> list = new DOMStringList();
2083
0
2084
0
  if (!indexes.IsEmpty()) {
2085
0
    nsTArray<nsString>& listNames = list->StringArray();
2086
0
    listNames.SetCapacity(indexes.Length());
2087
0
2088
0
    for (uint32_t index = 0; index < indexes.Length(); index++) {
2089
0
      listNames.InsertElementSorted(indexes[index].name());
2090
0
    }
2091
0
  }
2092
0
2093
0
  return list.forget();
2094
0
}
2095
2096
already_AddRefed<IDBRequest>
2097
IDBObjectStore::GetInternal(bool aKeyOnly,
2098
                            JSContext* aCx,
2099
                            JS::Handle<JS::Value> aKey,
2100
                            ErrorResult& aRv)
2101
0
{
2102
0
  AssertIsOnOwningThread();
2103
0
2104
0
  if (mDeletedSpec) {
2105
0
    aRv.Throw(NS_ERROR_DOM_INDEXEDDB_NOT_ALLOWED_ERR);
2106
0
    return nullptr;
2107
0
  }
2108
0
2109
0
  if (!mTransaction->IsOpen()) {
2110
0
    aRv.Throw(NS_ERROR_DOM_INDEXEDDB_TRANSACTION_INACTIVE_ERR);
2111
0
    return nullptr;
2112
0
  }
2113
0
2114
0
  RefPtr<IDBKeyRange> keyRange;
2115
0
  aRv = IDBKeyRange::FromJSVal(aCx, aKey, getter_AddRefs(keyRange));
2116
0
  if (aRv.Failed()) {
2117
0
    return nullptr;
2118
0
  }
2119
0
2120
0
  if (!keyRange) {
2121
0
    // Must specify a key or keyRange for get().
2122
0
    aRv.Throw(NS_ERROR_DOM_INDEXEDDB_KEY_ERR);
2123
0
    return nullptr;
2124
0
  }
2125
0
2126
0
  const int64_t id = Id();
2127
0
2128
0
  SerializedKeyRange serializedKeyRange;
2129
0
  keyRange->ToSerialized(serializedKeyRange);
2130
0
2131
0
  RequestParams params;
2132
0
  if (aKeyOnly) {
2133
0
    params = ObjectStoreGetKeyParams(id, serializedKeyRange);
2134
0
  } else {
2135
0
    params = ObjectStoreGetParams(id, serializedKeyRange);
2136
0
  }
2137
0
2138
0
  RefPtr<IDBRequest> request = GenerateRequest(aCx, this);
2139
0
  MOZ_ASSERT(request);
2140
0
2141
0
  IDB_LOG_MARK("IndexedDB %s: Child  Transaction[%lld] Request[%llu]: "
2142
0
                 "database(%s).transaction(%s).objectStore(%s).get(%s)",
2143
0
               "IndexedDB %s: C T[%lld] R[%llu]: IDBObjectStore.get()",
2144
0
               IDB_LOG_ID_STRING(),
2145
0
               mTransaction->LoggingSerialNumber(),
2146
0
               request->LoggingSerialNumber(),
2147
0
               IDB_LOG_STRINGIFY(mTransaction->Database()),
2148
0
               IDB_LOG_STRINGIFY(mTransaction),
2149
0
               IDB_LOG_STRINGIFY(this),
2150
0
               IDB_LOG_STRINGIFY(keyRange));
2151
0
2152
0
  mTransaction->StartRequest(request, params);
2153
0
2154
0
  return request.forget();
2155
0
}
2156
2157
already_AddRefed<IDBRequest>
2158
IDBObjectStore::DeleteInternal(JSContext* aCx,
2159
                               JS::Handle<JS::Value> aKey,
2160
                               bool aFromCursor,
2161
                               ErrorResult& aRv)
2162
0
{
2163
0
  AssertIsOnOwningThread();
2164
0
2165
0
  if (mDeletedSpec) {
2166
0
    aRv.Throw(NS_ERROR_DOM_INDEXEDDB_NOT_ALLOWED_ERR);
2167
0
    return nullptr;
2168
0
  }
2169
0
2170
0
  if (!mTransaction->IsOpen()) {
2171
0
    aRv.Throw(NS_ERROR_DOM_INDEXEDDB_TRANSACTION_INACTIVE_ERR);
2172
0
    return nullptr;
2173
0
  }
2174
0
2175
0
  if (!mTransaction->IsWriteAllowed()) {
2176
0
    aRv.Throw(NS_ERROR_DOM_INDEXEDDB_READ_ONLY_ERR);
2177
0
    return nullptr;
2178
0
  }
2179
0
2180
0
  RefPtr<IDBKeyRange> keyRange;
2181
0
  aRv = IDBKeyRange::FromJSVal(aCx, aKey, getter_AddRefs(keyRange));
2182
0
  if (NS_WARN_IF((aRv.Failed()))) {
2183
0
    return nullptr;
2184
0
  }
2185
0
2186
0
  if (!keyRange) {
2187
0
    // Must specify a key or keyRange for delete().
2188
0
    aRv.Throw(NS_ERROR_DOM_INDEXEDDB_KEY_ERR);
2189
0
    return nullptr;
2190
0
  }
2191
0
2192
0
  ObjectStoreDeleteParams params;
2193
0
  params.objectStoreId() = Id();
2194
0
  keyRange->ToSerialized(params.keyRange());
2195
0
2196
0
  RefPtr<IDBRequest> request = GenerateRequest(aCx, this);
2197
0
  MOZ_ASSERT(request);
2198
0
2199
0
  if (!aFromCursor) {
2200
0
    IDB_LOG_MARK("IndexedDB %s: Child  Transaction[%lld] Request[%llu]: "
2201
0
                   "database(%s).transaction(%s).objectStore(%s).delete(%s)",
2202
0
                 "IndexedDB %s: C T[%lld] R[%llu]: IDBObjectStore.delete()",
2203
0
                 IDB_LOG_ID_STRING(),
2204
0
                 mTransaction->LoggingSerialNumber(),
2205
0
                 request->LoggingSerialNumber(),
2206
0
                 IDB_LOG_STRINGIFY(mTransaction->Database()),
2207
0
                 IDB_LOG_STRINGIFY(mTransaction),
2208
0
                 IDB_LOG_STRINGIFY(this),
2209
0
                 IDB_LOG_STRINGIFY(keyRange));
2210
0
  }
2211
0
2212
0
  mTransaction->StartRequest(request, params);
2213
0
2214
0
  return request.forget();
2215
0
}
2216
2217
already_AddRefed<IDBIndex>
2218
IDBObjectStore::CreateIndex(const nsAString& aName,
2219
                            const StringOrStringSequence& aKeyPath,
2220
                            const IDBIndexParameters& aOptionalParameters,
2221
                            ErrorResult& aRv)
2222
0
{
2223
0
  AssertIsOnOwningThread();
2224
0
2225
0
  if (mTransaction->GetMode() != IDBTransaction::VERSION_CHANGE ||
2226
0
      mDeletedSpec) {
2227
0
    aRv.Throw(NS_ERROR_DOM_INDEXEDDB_NOT_ALLOWED_ERR);
2228
0
    return nullptr;
2229
0
  }
2230
0
2231
0
  IDBTransaction* transaction = IDBTransaction::GetCurrent();
2232
0
  if (!transaction || transaction != mTransaction || !transaction->IsOpen()) {
2233
0
    aRv.Throw(NS_ERROR_DOM_INDEXEDDB_TRANSACTION_INACTIVE_ERR);
2234
0
    return nullptr;
2235
0
  }
2236
0
2237
0
  auto& indexes = const_cast<nsTArray<IndexMetadata>&>(mSpec->indexes());
2238
0
  for (uint32_t count = indexes.Length(), index = 0;
2239
0
       index < count;
2240
0
       index++) {
2241
0
    if (aName == indexes[index].name()) {
2242
0
      aRv.Throw(NS_ERROR_DOM_INDEXEDDB_CONSTRAINT_ERR);
2243
0
      return nullptr;
2244
0
    }
2245
0
  }
2246
0
2247
0
  KeyPath keyPath(0);
2248
0
  if (aKeyPath.IsString()) {
2249
0
    if (NS_FAILED(KeyPath::Parse(aKeyPath.GetAsString(), &keyPath)) ||
2250
0
        !keyPath.IsValid()) {
2251
0
      aRv.Throw(NS_ERROR_DOM_SYNTAX_ERR);
2252
0
      return nullptr;
2253
0
    }
2254
0
  } else {
2255
0
    MOZ_ASSERT(aKeyPath.IsStringSequence());
2256
0
    if (aKeyPath.GetAsStringSequence().IsEmpty() ||
2257
0
        NS_FAILED(KeyPath::Parse(aKeyPath.GetAsStringSequence(), &keyPath)) ||
2258
0
        !keyPath.IsValid()) {
2259
0
      aRv.Throw(NS_ERROR_DOM_SYNTAX_ERR);
2260
0
      return nullptr;
2261
0
    }
2262
0
  }
2263
0
2264
0
  if (aOptionalParameters.mMultiEntry && keyPath.IsArray()) {
2265
0
    aRv.Throw(NS_ERROR_DOM_INVALID_ACCESS_ERR);
2266
0
    return nullptr;
2267
0
  }
2268
0
2269
#ifdef DEBUG
2270
  for (uint32_t count = mIndexes.Length(), index = 0;
2271
       index < count;
2272
       index++) {
2273
    MOZ_ASSERT(mIndexes[index]->Name() != aName);
2274
  }
2275
#endif
2276
2277
0
  const IndexMetadata* oldMetadataElements =
2278
0
    indexes.IsEmpty() ? nullptr : indexes.Elements();
2279
0
2280
0
  // With this setup we only validate the passed in locale name by the time we
2281
0
  // get to encoding Keys. Maybe we should do it here right away and error out.
2282
0
2283
0
  // Valid locale names are always ASCII as per BCP-47.
2284
0
  nsCString locale = NS_LossyConvertUTF16toASCII(aOptionalParameters.mLocale);
2285
0
  bool autoLocale = locale.EqualsASCII("auto");
2286
0
  if (autoLocale) {
2287
0
    locale = IndexedDatabaseManager::GetLocale();
2288
0
  }
2289
0
2290
0
  IndexMetadata* metadata = indexes.AppendElement(
2291
0
    IndexMetadata(transaction->NextIndexId(), nsString(aName), keyPath,
2292
0
                  locale,
2293
0
                  aOptionalParameters.mUnique,
2294
0
                  aOptionalParameters.mMultiEntry,
2295
0
                  autoLocale));
2296
0
2297
0
  if (oldMetadataElements &&
2298
0
      oldMetadataElements != indexes.Elements()) {
2299
0
    MOZ_ASSERT(indexes.Length() > 1);
2300
0
2301
0
    // Array got moved, update the spec pointers for all live indexes.
2302
0
    RefreshSpec(/* aMayDelete */ false);
2303
0
  }
2304
0
2305
0
  transaction->CreateIndex(this, *metadata);
2306
0
2307
0
  RefPtr<IDBIndex> index = IDBIndex::Create(this, *metadata);
2308
0
  MOZ_ASSERT(index);
2309
0
2310
0
  mIndexes.AppendElement(index);
2311
0
2312
0
  // Don't do this in the macro because we always need to increment the serial
2313
0
  // number to keep in sync with the parent.
2314
0
  const uint64_t requestSerialNumber = IDBRequest::NextSerialNumber();
2315
0
2316
0
  IDB_LOG_MARK("IndexedDB %s: Child  Transaction[%lld] Request[%llu]: "
2317
0
                 "database(%s).transaction(%s).objectStore(%s).createIndex(%s)",
2318
0
               "IndexedDB %s: C T[%lld] R[%llu]: IDBObjectStore.createIndex()",
2319
0
               IDB_LOG_ID_STRING(),
2320
0
               mTransaction->LoggingSerialNumber(),
2321
0
               requestSerialNumber,
2322
0
               IDB_LOG_STRINGIFY(mTransaction->Database()),
2323
0
               IDB_LOG_STRINGIFY(mTransaction),
2324
0
               IDB_LOG_STRINGIFY(this),
2325
0
               IDB_LOG_STRINGIFY(index));
2326
0
2327
0
  return index.forget();
2328
0
}
2329
2330
void
2331
IDBObjectStore::DeleteIndex(const nsAString& aName, ErrorResult& aRv)
2332
0
{
2333
0
  AssertIsOnOwningThread();
2334
0
2335
0
  if (mTransaction->GetMode() != IDBTransaction::VERSION_CHANGE ||
2336
0
      mDeletedSpec) {
2337
0
    aRv.Throw(NS_ERROR_DOM_INDEXEDDB_NOT_ALLOWED_ERR);
2338
0
    return;
2339
0
  }
2340
0
2341
0
  IDBTransaction* transaction = IDBTransaction::GetCurrent();
2342
0
  if (!transaction || transaction != mTransaction || !transaction->IsOpen()) {
2343
0
    aRv.Throw(NS_ERROR_DOM_INDEXEDDB_TRANSACTION_INACTIVE_ERR);
2344
0
    return;
2345
0
  }
2346
0
2347
0
  auto& metadataArray = const_cast<nsTArray<IndexMetadata>&>(mSpec->indexes());
2348
0
2349
0
  int64_t foundId = 0;
2350
0
2351
0
  for (uint32_t metadataCount = metadataArray.Length(), metadataIndex = 0;
2352
0
       metadataIndex < metadataCount;
2353
0
       metadataIndex++) {
2354
0
    const IndexMetadata& metadata = metadataArray[metadataIndex];
2355
0
    MOZ_ASSERT(metadata.id());
2356
0
2357
0
    if (aName == metadata.name()) {
2358
0
      foundId = metadata.id();
2359
0
2360
0
      // Must do this before altering the metadata array!
2361
0
      for (uint32_t indexCount = mIndexes.Length(), indexIndex = 0;
2362
0
           indexIndex < indexCount;
2363
0
           indexIndex++) {
2364
0
        RefPtr<IDBIndex>& index = mIndexes[indexIndex];
2365
0
2366
0
        if (index->Id() == foundId) {
2367
0
          index->NoteDeletion();
2368
0
2369
0
          RefPtr<IDBIndex>* deletedIndex =
2370
0
            mDeletedIndexes.AppendElement();
2371
0
          deletedIndex->swap(mIndexes[indexIndex]);
2372
0
2373
0
          mIndexes.RemoveElementAt(indexIndex);
2374
0
          break;
2375
0
        }
2376
0
      }
2377
0
2378
0
      metadataArray.RemoveElementAt(metadataIndex);
2379
0
2380
0
      RefreshSpec(/* aMayDelete */ false);
2381
0
      break;
2382
0
    }
2383
0
  }
2384
0
2385
0
  if (!foundId) {
2386
0
    aRv.Throw(NS_ERROR_DOM_INDEXEDDB_NOT_FOUND_ERR);
2387
0
    return;
2388
0
  }
2389
0
2390
0
  // Don't do this in the macro because we always need to increment the serial
2391
0
  // number to keep in sync with the parent.
2392
0
  const uint64_t requestSerialNumber = IDBRequest::NextSerialNumber();
2393
0
2394
0
  IDB_LOG_MARK("IndexedDB %s: Child  Transaction[%lld] Request[%llu]: "
2395
0
                 "database(%s).transaction(%s).objectStore(%s)."
2396
0
                 "deleteIndex(\"%s\")",
2397
0
               "IndexedDB %s: C T[%lld] R[%llu]: IDBObjectStore.deleteIndex()",
2398
0
               IDB_LOG_ID_STRING(),
2399
0
               mTransaction->LoggingSerialNumber(),
2400
0
               requestSerialNumber,
2401
0
               IDB_LOG_STRINGIFY(mTransaction->Database()),
2402
0
               IDB_LOG_STRINGIFY(mTransaction),
2403
0
               IDB_LOG_STRINGIFY(this),
2404
0
               NS_ConvertUTF16toUTF8(aName).get());
2405
0
2406
0
  transaction->DeleteIndex(this, foundId);
2407
0
}
2408
2409
already_AddRefed<IDBRequest>
2410
IDBObjectStore::Count(JSContext* aCx,
2411
                      JS::Handle<JS::Value> aKey,
2412
                      ErrorResult& aRv)
2413
0
{
2414
0
  AssertIsOnOwningThread();
2415
0
2416
0
  if (mDeletedSpec) {
2417
0
    aRv.Throw(NS_ERROR_DOM_INDEXEDDB_NOT_ALLOWED_ERR);
2418
0
    return nullptr;
2419
0
  }
2420
0
2421
0
  if (!mTransaction->IsOpen()) {
2422
0
    aRv.Throw(NS_ERROR_DOM_INDEXEDDB_TRANSACTION_INACTIVE_ERR);
2423
0
    return nullptr;
2424
0
  }
2425
0
2426
0
  RefPtr<IDBKeyRange> keyRange;
2427
0
  aRv = IDBKeyRange::FromJSVal(aCx, aKey, getter_AddRefs(keyRange));
2428
0
  if (aRv.Failed()) {
2429
0
    return nullptr;
2430
0
  }
2431
0
2432
0
  ObjectStoreCountParams params;
2433
0
  params.objectStoreId() = Id();
2434
0
2435
0
  if (keyRange) {
2436
0
    SerializedKeyRange serializedKeyRange;
2437
0
    keyRange->ToSerialized(serializedKeyRange);
2438
0
    params.optionalKeyRange() = serializedKeyRange;
2439
0
  } else {
2440
0
    params.optionalKeyRange() = void_t();
2441
0
  }
2442
0
2443
0
  RefPtr<IDBRequest> request = GenerateRequest(aCx, this);
2444
0
  MOZ_ASSERT(request);
2445
0
2446
0
  IDB_LOG_MARK("IndexedDB %s: Child  Transaction[%lld] Request[%llu]: "
2447
0
                 "database(%s).transaction(%s).objectStore(%s).count(%s)",
2448
0
               "IndexedDB %s: C T[%lld] R[%llu]: IDBObjectStore.count()",
2449
0
               IDB_LOG_ID_STRING(),
2450
0
               mTransaction->LoggingSerialNumber(),
2451
0
               request->LoggingSerialNumber(),
2452
0
               IDB_LOG_STRINGIFY(mTransaction->Database()),
2453
0
               IDB_LOG_STRINGIFY(mTransaction),
2454
0
               IDB_LOG_STRINGIFY(this),
2455
0
               IDB_LOG_STRINGIFY(keyRange));
2456
0
2457
0
  mTransaction->StartRequest(request, params);
2458
0
2459
0
  return request.forget();
2460
0
}
2461
2462
already_AddRefed<IDBRequest>
2463
IDBObjectStore::OpenCursorInternal(bool aKeysOnly,
2464
                                   JSContext* aCx,
2465
                                   JS::Handle<JS::Value> aRange,
2466
                                   IDBCursorDirection aDirection,
2467
                                   ErrorResult& aRv)
2468
0
{
2469
0
  AssertIsOnOwningThread();
2470
0
  MOZ_ASSERT(aCx);
2471
0
2472
0
  if (mDeletedSpec) {
2473
0
    aRv.Throw(NS_ERROR_DOM_INDEXEDDB_NOT_ALLOWED_ERR);
2474
0
    return nullptr;
2475
0
  }
2476
0
2477
0
  if (!mTransaction->IsOpen()) {
2478
0
    aRv.Throw(NS_ERROR_DOM_INDEXEDDB_TRANSACTION_INACTIVE_ERR);
2479
0
    return nullptr;
2480
0
  }
2481
0
2482
0
  RefPtr<IDBKeyRange> keyRange;
2483
0
  aRv = IDBKeyRange::FromJSVal(aCx, aRange, getter_AddRefs(keyRange));
2484
0
  if (NS_WARN_IF(aRv.Failed())) {
2485
0
    return nullptr;
2486
0
  }
2487
0
2488
0
  int64_t objectStoreId = Id();
2489
0
2490
0
  OptionalKeyRange optionalKeyRange;
2491
0
2492
0
  if (keyRange) {
2493
0
    SerializedKeyRange serializedKeyRange;
2494
0
    keyRange->ToSerialized(serializedKeyRange);
2495
0
2496
0
    optionalKeyRange = std::move(serializedKeyRange);
2497
0
  } else {
2498
0
    optionalKeyRange = void_t();
2499
0
  }
2500
0
2501
0
  IDBCursor::Direction direction = IDBCursor::ConvertDirection(aDirection);
2502
0
2503
0
  OpenCursorParams params;
2504
0
  if (aKeysOnly) {
2505
0
    ObjectStoreOpenKeyCursorParams openParams;
2506
0
    openParams.objectStoreId() = objectStoreId;
2507
0
    openParams.optionalKeyRange() = std::move(optionalKeyRange);
2508
0
    openParams.direction() = direction;
2509
0
2510
0
    params = std::move(openParams);
2511
0
  } else {
2512
0
    ObjectStoreOpenCursorParams openParams;
2513
0
    openParams.objectStoreId() = objectStoreId;
2514
0
    openParams.optionalKeyRange() = std::move(optionalKeyRange);
2515
0
    openParams.direction() = direction;
2516
0
2517
0
    params = std::move(openParams);
2518
0
  }
2519
0
2520
0
  RefPtr<IDBRequest> request = GenerateRequest(aCx, this);
2521
0
  MOZ_ASSERT(request);
2522
0
2523
0
  if (aKeysOnly) {
2524
0
    IDB_LOG_MARK("IndexedDB %s: Child  Transaction[%lld] Request[%llu]: "
2525
0
                   "database(%s).transaction(%s).objectStore(%s)."
2526
0
                   "openKeyCursor(%s, %s)",
2527
0
                 "IndexedDB %s: C T[%lld] R[%llu]: "
2528
0
                   "IDBObjectStore.openKeyCursor()",
2529
0
                 IDB_LOG_ID_STRING(),
2530
0
                 mTransaction->LoggingSerialNumber(),
2531
0
                 request->LoggingSerialNumber(),
2532
0
                 IDB_LOG_STRINGIFY(mTransaction->Database()),
2533
0
                 IDB_LOG_STRINGIFY(mTransaction),
2534
0
                 IDB_LOG_STRINGIFY(this),
2535
0
                 IDB_LOG_STRINGIFY(keyRange),
2536
0
                 IDB_LOG_STRINGIFY(direction));
2537
0
  } else {
2538
0
    IDB_LOG_MARK("IndexedDB %s: Child  Transaction[%lld] Request[%llu]: "
2539
0
                   "database(%s).transaction(%s).objectStore(%s)."
2540
0
                   "openCursor(%s, %s)",
2541
0
                 "IndexedDB %s: C T[%lld] R[%llu]: IDBObjectStore.openCursor()",
2542
0
                 IDB_LOG_ID_STRING(),
2543
0
                 mTransaction->LoggingSerialNumber(),
2544
0
                 request->LoggingSerialNumber(),
2545
0
                 IDB_LOG_STRINGIFY(mTransaction->Database()),
2546
0
                 IDB_LOG_STRINGIFY(mTransaction),
2547
0
                 IDB_LOG_STRINGIFY(this),
2548
0
                 IDB_LOG_STRINGIFY(keyRange),
2549
0
                 IDB_LOG_STRINGIFY(direction));
2550
0
  }
2551
0
2552
0
  BackgroundCursorChild* actor =
2553
0
    new BackgroundCursorChild(request, this, direction);
2554
0
2555
0
  mTransaction->OpenCursor(actor, params);
2556
0
2557
0
  return request.forget();
2558
0
}
2559
2560
void
2561
IDBObjectStore::RefreshSpec(bool aMayDelete)
2562
0
{
2563
0
  AssertIsOnOwningThread();
2564
0
  MOZ_ASSERT_IF(mDeletedSpec, mSpec == mDeletedSpec);
2565
0
2566
0
  const DatabaseSpec* dbSpec = mTransaction->Database()->Spec();
2567
0
  MOZ_ASSERT(dbSpec);
2568
0
2569
0
  const nsTArray<ObjectStoreSpec>& objectStores = dbSpec->objectStores();
2570
0
2571
0
  bool found = false;
2572
0
2573
0
  for (uint32_t objCount = objectStores.Length(), objIndex = 0;
2574
0
       objIndex < objCount;
2575
0
       objIndex++) {
2576
0
    const ObjectStoreSpec& objSpec = objectStores[objIndex];
2577
0
2578
0
    if (objSpec.metadata().id() == Id()) {
2579
0
      mSpec = &objSpec;
2580
0
2581
0
      for (uint32_t idxCount = mIndexes.Length(), idxIndex = 0;
2582
0
           idxIndex < idxCount;
2583
0
           idxIndex++) {
2584
0
        mIndexes[idxIndex]->RefreshMetadata(aMayDelete);
2585
0
      }
2586
0
2587
0
      for (uint32_t idxCount = mDeletedIndexes.Length(), idxIndex = 0;
2588
0
           idxIndex < idxCount;
2589
0
           idxIndex++) {
2590
0
        mDeletedIndexes[idxIndex]->RefreshMetadata(false);
2591
0
      }
2592
0
2593
0
      found = true;
2594
0
      break;
2595
0
    }
2596
0
  }
2597
0
2598
0
  MOZ_ASSERT_IF(!aMayDelete && !mDeletedSpec, found);
2599
0
2600
0
  if (found) {
2601
0
    MOZ_ASSERT(mSpec != mDeletedSpec);
2602
0
    mDeletedSpec = nullptr;
2603
0
  } else {
2604
0
    NoteDeletion();
2605
0
  }
2606
0
}
2607
2608
const ObjectStoreSpec&
2609
IDBObjectStore::Spec() const
2610
0
{
2611
0
  AssertIsOnOwningThread();
2612
0
  MOZ_ASSERT(mSpec);
2613
0
2614
0
  return *mSpec;
2615
0
}
2616
2617
void
2618
IDBObjectStore::NoteDeletion()
2619
0
{
2620
0
  AssertIsOnOwningThread();
2621
0
  MOZ_ASSERT(mSpec);
2622
0
  MOZ_ASSERT(Id() == mSpec->metadata().id());
2623
0
2624
0
  if (mDeletedSpec) {
2625
0
    MOZ_ASSERT(mDeletedSpec == mSpec);
2626
0
    return;
2627
0
  }
2628
0
2629
0
  // Copy the spec here.
2630
0
  mDeletedSpec = new ObjectStoreSpec(*mSpec);
2631
0
  mDeletedSpec->indexes().Clear();
2632
0
2633
0
  mSpec = mDeletedSpec;
2634
0
2635
0
  if (!mIndexes.IsEmpty()) {
2636
0
    for (uint32_t count = mIndexes.Length(), index = 0;
2637
0
         index < count;
2638
0
         index++) {
2639
0
      mIndexes[index]->NoteDeletion();
2640
0
    }
2641
0
  }
2642
0
}
2643
2644
const nsString&
2645
IDBObjectStore::Name() const
2646
0
{
2647
0
  AssertIsOnOwningThread();
2648
0
  MOZ_ASSERT(mSpec);
2649
0
2650
0
  return mSpec->metadata().name();
2651
0
}
2652
2653
void
2654
IDBObjectStore::SetName(const nsAString& aName, ErrorResult& aRv)
2655
0
{
2656
0
  AssertIsOnOwningThread();
2657
0
2658
0
  if (mTransaction->GetMode() != IDBTransaction::VERSION_CHANGE ||
2659
0
      mDeletedSpec) {
2660
0
    aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
2661
0
    return;
2662
0
  }
2663
0
2664
0
  IDBTransaction* transaction = IDBTransaction::GetCurrent();
2665
0
  if (!transaction || transaction != mTransaction || !transaction->IsOpen()) {
2666
0
    aRv.Throw(NS_ERROR_DOM_INDEXEDDB_TRANSACTION_INACTIVE_ERR);
2667
0
    return;
2668
0
  }
2669
0
2670
0
  if (aName == mSpec->metadata().name()) {
2671
0
    return;
2672
0
  }
2673
0
2674
0
  // Cache logging string of this object store before renaming.
2675
0
  const LoggingString loggingOldObjectStore(this);
2676
0
2677
0
  nsresult rv =
2678
0
    transaction->Database()->RenameObjectStore(mSpec->metadata().id(),
2679
0
                                               aName);
2680
0
2681
0
  if (NS_FAILED(rv)) {
2682
0
    aRv.Throw(rv);
2683
0
    return;
2684
0
  }
2685
0
2686
0
  // Don't do this in the macro because we always need to increment the serial
2687
0
  // number to keep in sync with the parent.
2688
0
  const uint64_t requestSerialNumber = IDBRequest::NextSerialNumber();
2689
0
2690
0
  IDB_LOG_MARK("IndexedDB %s: Child  Transaction[%lld] Request[%llu]: "
2691
0
                 "database(%s).transaction(%s).objectStore(%s).rename(%s)",
2692
0
               "IndexedDB %s: C T[%lld] R[%llu]: IDBObjectStore.rename()",
2693
0
               IDB_LOG_ID_STRING(),
2694
0
               mTransaction->LoggingSerialNumber(),
2695
0
               requestSerialNumber,
2696
0
               IDB_LOG_STRINGIFY(mTransaction->Database()),
2697
0
               IDB_LOG_STRINGIFY(mTransaction),
2698
0
               loggingOldObjectStore.get(),
2699
0
               IDB_LOG_STRINGIFY(this));
2700
0
2701
0
  transaction->RenameObjectStore(mSpec->metadata().id(), aName);
2702
0
}
2703
2704
bool
2705
IDBObjectStore::AutoIncrement() const
2706
0
{
2707
0
  AssertIsOnOwningThread();
2708
0
  MOZ_ASSERT(mSpec);
2709
0
2710
0
  return mSpec->metadata().autoIncrement();
2711
0
}
2712
2713
const indexedDB::KeyPath&
2714
IDBObjectStore::GetKeyPath() const
2715
0
{
2716
0
  AssertIsOnOwningThread();
2717
0
  MOZ_ASSERT(mSpec);
2718
0
2719
0
  return mSpec->metadata().keyPath();
2720
0
}
2721
2722
bool
2723
IDBObjectStore::HasValidKeyPath() const
2724
0
{
2725
0
  AssertIsOnOwningThread();
2726
0
  MOZ_ASSERT(mSpec);
2727
0
2728
0
  return GetKeyPath().IsValid();
2729
0
}
2730
2731
bool
2732
IDBObjectStore::
2733
ValueWrapper::Clone(JSContext* aCx)
2734
0
{
2735
0
  if (mCloned) {
2736
0
    return true;
2737
0
  }
2738
0
2739
0
  static const JSStructuredCloneCallbacks callbacks = {
2740
0
    CopyingStructuredCloneReadCallback /* read */,
2741
0
    CopyingStructuredCloneWriteCallback /* write */,
2742
0
    nullptr /* reportError */,
2743
0
    nullptr /* readTransfer */,
2744
0
    nullptr /* writeTransfer */,
2745
0
    nullptr /* freeTransfer */,
2746
0
    nullptr /* canTransfer */
2747
0
  };
2748
0
2749
0
  StructuredCloneInfo cloneInfo;
2750
0
2751
0
  JS::Rooted<JS::Value> clonedValue(aCx);
2752
0
  if (!JS_StructuredClone(aCx,
2753
0
                          mValue,
2754
0
                          &clonedValue,
2755
0
                          &callbacks,
2756
0
                          &cloneInfo)) {
2757
0
    return false;
2758
0
  }
2759
0
2760
0
  mValue = clonedValue;
2761
0
2762
0
  mCloned = true;
2763
0
2764
0
  return true;
2765
0
}
2766
2767
} // namespace dom
2768
} // namespace mozilla