Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/dom/indexedDB/IDBFactory.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 "IDBFactory.h"
8
9
#include "BackgroundChildImpl.h"
10
#include "IDBRequest.h"
11
#include "IndexedDatabaseManager.h"
12
#include "mozilla/ErrorResult.h"
13
#include "mozilla/Preferences.h"
14
#include "mozilla/SystemGroup.h"
15
#include "mozilla/dom/BindingDeclarations.h"
16
#include "mozilla/dom/IDBFactoryBinding.h"
17
#include "mozilla/dom/quota/QuotaManager.h"
18
#include "mozilla/dom/TabChild.h"
19
#include "mozilla/ipc/BackgroundChild.h"
20
#include "mozilla/ipc/BackgroundUtils.h"
21
#include "mozilla/ipc/PBackground.h"
22
#include "mozilla/ipc/PBackgroundChild.h"
23
#include "mozilla/StaticPrefs.h"
24
#include "mozilla/Telemetry.h"
25
#include "mozIThirdPartyUtil.h"
26
#include "nsAboutProtocolUtils.h"
27
#include "nsContentUtils.h"
28
#include "nsGlobalWindow.h"
29
#include "nsIAboutModule.h"
30
#include "nsILoadContext.h"
31
#include "nsIPrincipal.h"
32
#include "nsIURI.h"
33
#include "nsIUUIDGenerator.h"
34
#include "nsIWebNavigation.h"
35
#include "nsSandboxFlags.h"
36
#include "nsServiceManagerUtils.h"
37
#include "ProfilerHelpers.h"
38
#include "ReportInternalError.h"
39
40
// Include this last to avoid path problems on Windows.
41
#include "ActorsChild.h"
42
43
#ifdef DEBUG
44
#include "nsContentUtils.h" // For assertions.
45
#endif
46
47
namespace mozilla {
48
namespace dom {
49
50
using namespace mozilla::dom::indexedDB;
51
using namespace mozilla::dom::quota;
52
using namespace mozilla::ipc;
53
54
namespace {
55
56
const char kPrefIndexedDBEnabled[] = "dom.indexedDB.enabled";
57
58
} // namespace
59
60
struct IDBFactory::PendingRequestInfo
61
{
62
  RefPtr<IDBOpenDBRequest> mRequest;
63
  FactoryRequestParams mParams;
64
65
  PendingRequestInfo(IDBOpenDBRequest* aRequest,
66
                     const FactoryRequestParams& aParams)
67
  : mRequest(aRequest), mParams(aParams)
68
0
  {
69
0
    MOZ_ASSERT(aRequest);
70
0
    MOZ_ASSERT(aParams.type() != FactoryRequestParams::T__None);
71
0
  }
72
};
73
74
IDBFactory::IDBFactory()
75
  : mOwningObject(nullptr)
76
  , mBackgroundActor(nullptr)
77
  , mInnerWindowID(0)
78
  , mActiveTransactionCount(0)
79
  , mActiveDatabaseCount(0)
80
  , mBackgroundActorFailed(false)
81
  , mPrivateBrowsingMode(false)
82
0
{
83
0
  AssertIsOnOwningThread();
84
0
}
85
86
IDBFactory::~IDBFactory()
87
0
{
88
0
  MOZ_ASSERT_IF(mBackgroundActorFailed, !mBackgroundActor);
89
0
90
0
  mOwningObject = nullptr;
91
0
  mozilla::DropJSObjects(this);
92
0
93
0
  if (mBackgroundActor) {
94
0
    mBackgroundActor->SendDeleteMeInternal();
95
0
    MOZ_ASSERT(!mBackgroundActor, "SendDeleteMeInternal should have cleared!");
96
0
  }
97
0
}
98
99
// static
100
nsresult
101
IDBFactory::CreateForWindow(nsPIDOMWindowInner* aWindow,
102
                            IDBFactory** aFactory)
103
0
{
104
0
  MOZ_ASSERT(NS_IsMainThread());
105
0
  MOZ_ASSERT(aWindow);
106
0
  MOZ_ASSERT(aFactory);
107
0
108
0
  nsCOMPtr<nsIPrincipal> principal;
109
0
  nsresult rv = AllowedForWindowInternal(aWindow, getter_AddRefs(principal));
110
0
111
0
  if (!(NS_SUCCEEDED(rv) && nsContentUtils::IsSystemPrincipal(principal)) &&
112
0
      NS_WARN_IF(!Preferences::GetBool(kPrefIndexedDBEnabled, false))) {
113
0
    *aFactory = nullptr;
114
0
    return NS_ERROR_DOM_INDEXEDDB_NOT_ALLOWED_ERR;
115
0
  }
116
0
117
0
  if (rv == NS_ERROR_DOM_NOT_SUPPORTED_ERR) {
118
0
    NS_WARNING("IndexedDB is not permitted in a third-party window.");
119
0
    *aFactory = nullptr;
120
0
    return NS_OK;
121
0
  }
122
0
123
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
124
0
    if (rv == NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR) {
125
0
      IDB_REPORT_INTERNAL_ERR();
126
0
    }
127
0
    return rv;
128
0
  }
129
0
130
0
  MOZ_ASSERT(principal);
131
0
132
0
  nsAutoPtr<PrincipalInfo> principalInfo(new PrincipalInfo());
133
0
  rv = PrincipalToPrincipalInfo(principal, principalInfo);
134
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
135
0
    IDB_REPORT_INTERNAL_ERR();
136
0
    return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
137
0
  }
138
0
139
0
  MOZ_ASSERT(principalInfo->type() == PrincipalInfo::TContentPrincipalInfo ||
140
0
             principalInfo->type() == PrincipalInfo::TSystemPrincipalInfo);
141
0
142
0
  nsCOMPtr<nsIWebNavigation> webNav = do_GetInterface(aWindow);
143
0
  nsCOMPtr<nsILoadContext> loadContext = do_QueryInterface(webNav);
144
0
145
0
  RefPtr<IDBFactory> factory = new IDBFactory();
146
0
  factory->mPrincipalInfo = std::move(principalInfo);
147
0
  factory->mWindow = aWindow;
148
0
  factory->mTabChild = TabChild::GetFrom(aWindow);
149
0
  factory->mEventTarget =
150
0
    nsGlobalWindowInner::Cast(aWindow)->EventTargetFor(TaskCategory::Other);
151
0
  factory->mInnerWindowID = aWindow->WindowID();
152
0
  factory->mPrivateBrowsingMode =
153
0
    loadContext && loadContext->UsePrivateBrowsing();
154
0
155
0
  factory.forget(aFactory);
156
0
  return NS_OK;
157
0
}
158
159
// static
160
nsresult
161
IDBFactory::CreateForMainThreadJS(JSContext* aCx,
162
                                  JS::Handle<JSObject*> aOwningObject,
163
                                  IDBFactory** aFactory)
164
0
{
165
0
  MOZ_ASSERT(NS_IsMainThread());
166
0
167
0
  nsAutoPtr<PrincipalInfo> principalInfo(new PrincipalInfo());
168
0
  nsIPrincipal* principal = nsContentUtils::ObjectPrincipal(aOwningObject);
169
0
  MOZ_ASSERT(principal);
170
0
  bool isSystem;
171
0
  if (!AllowedForPrincipal(principal, &isSystem)) {
172
0
    return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
173
0
  }
174
0
175
0
  nsresult rv = PrincipalToPrincipalInfo(principal, principalInfo);
176
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
177
0
    return rv;
178
0
  }
179
0
180
0
  rv = CreateForMainThreadJSInternal(aCx, aOwningObject, principalInfo, aFactory);
181
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
182
0
    return rv;
183
0
  }
184
0
185
0
  MOZ_ASSERT(!principalInfo);
186
0
187
0
  return NS_OK;
188
0
}
189
190
// static
191
nsresult
192
IDBFactory::CreateForWorker(JSContext* aCx,
193
                            JS::Handle<JSObject*> aOwningObject,
194
                            const PrincipalInfo& aPrincipalInfo,
195
                            uint64_t aInnerWindowID,
196
                            IDBFactory** aFactory)
197
0
{
198
0
  MOZ_ASSERT(!NS_IsMainThread());
199
0
  MOZ_ASSERT(aPrincipalInfo.type() != PrincipalInfo::T__None);
200
0
201
0
  nsAutoPtr<PrincipalInfo> principalInfo(new PrincipalInfo(aPrincipalInfo));
202
0
203
0
  nsresult rv =
204
0
    CreateForJSInternal(aCx,
205
0
                        aOwningObject,
206
0
                        principalInfo,
207
0
                        aInnerWindowID,
208
0
                        aFactory);
209
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
210
0
    return rv;
211
0
  }
212
0
213
0
  MOZ_ASSERT(!principalInfo);
214
0
215
0
  return NS_OK;
216
0
}
217
218
// static
219
nsresult
220
IDBFactory::CreateForMainThreadJSInternal(
221
                                       JSContext* aCx,
222
                                       JS::Handle<JSObject*> aOwningObject,
223
                                       nsAutoPtr<PrincipalInfo>& aPrincipalInfo,
224
                                       IDBFactory** aFactory)
225
0
{
226
0
  MOZ_ASSERT(NS_IsMainThread());
227
0
  MOZ_ASSERT(aPrincipalInfo);
228
0
229
0
  if (aPrincipalInfo->type() != PrincipalInfo::TSystemPrincipalInfo &&
230
0
      NS_WARN_IF(!Preferences::GetBool(kPrefIndexedDBEnabled, false))) {
231
0
    *aFactory = nullptr;
232
0
    return NS_ERROR_DOM_INDEXEDDB_NOT_ALLOWED_ERR;
233
0
  }
234
0
235
0
  IndexedDatabaseManager* mgr = IndexedDatabaseManager::GetOrCreate();
236
0
  if (NS_WARN_IF(!mgr)) {
237
0
    IDB_REPORT_INTERNAL_ERR();
238
0
    return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
239
0
  }
240
0
241
0
  nsresult rv =
242
0
    CreateForJSInternal(aCx,
243
0
                        aOwningObject,
244
0
                        aPrincipalInfo,
245
0
                        /* aInnerWindowID */ 0,
246
0
                        aFactory);
247
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
248
0
    return rv;
249
0
  }
250
0
251
0
  return NS_OK;
252
0
}
253
254
// static
255
nsresult
256
IDBFactory::CreateForJSInternal(JSContext* aCx,
257
                                JS::Handle<JSObject*> aOwningObject,
258
                                nsAutoPtr<PrincipalInfo>& aPrincipalInfo,
259
                                uint64_t aInnerWindowID,
260
                                IDBFactory** aFactory)
261
0
{
262
0
  MOZ_ASSERT(aCx);
263
0
  MOZ_ASSERT(aOwningObject);
264
0
  MOZ_ASSERT(aPrincipalInfo);
265
0
  MOZ_ASSERT(aPrincipalInfo->type() != PrincipalInfo::T__None);
266
0
  MOZ_ASSERT(aFactory);
267
0
  MOZ_ASSERT(JS_IsGlobalObject(aOwningObject));
268
0
269
0
  if (aPrincipalInfo->type() != PrincipalInfo::TContentPrincipalInfo &&
270
0
      aPrincipalInfo->type() != PrincipalInfo::TSystemPrincipalInfo) {
271
0
    NS_WARNING("IndexedDB not allowed for this principal!");
272
0
    aPrincipalInfo = nullptr;
273
0
    *aFactory = nullptr;
274
0
    return NS_OK;
275
0
  }
276
0
277
0
  RefPtr<IDBFactory> factory = new IDBFactory();
278
0
  factory->mPrincipalInfo = aPrincipalInfo.forget();
279
0
  factory->mOwningObject = aOwningObject;
280
0
  mozilla::HoldJSObjects(factory.get());
281
0
  factory->mEventTarget = GetCurrentThreadEventTarget();
282
0
  factory->mInnerWindowID = aInnerWindowID;
283
0
284
0
  factory.forget(aFactory);
285
0
  return NS_OK;
286
0
}
287
288
// static
289
bool
290
IDBFactory::AllowedForWindow(nsPIDOMWindowInner* aWindow)
291
0
{
292
0
  MOZ_ASSERT(NS_IsMainThread());
293
0
  MOZ_ASSERT(aWindow);
294
0
295
0
  nsCOMPtr<nsIPrincipal> principal;
296
0
  nsresult rv = AllowedForWindowInternal(aWindow, getter_AddRefs(principal));
297
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
298
0
    return false;
299
0
  }
300
0
301
0
  return true;
302
0
}
303
304
// static
305
nsresult
306
IDBFactory::AllowedForWindowInternal(nsPIDOMWindowInner* aWindow,
307
                                     nsIPrincipal** aPrincipal)
308
0
{
309
0
  MOZ_ASSERT(NS_IsMainThread());
310
0
  MOZ_ASSERT(aWindow);
311
0
312
0
  if (NS_WARN_IF(!IndexedDatabaseManager::GetOrCreate())) {
313
0
    return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
314
0
  }
315
0
316
0
  nsContentUtils::StorageAccess access =
317
0
    nsContentUtils::StorageAllowedForWindow(aWindow);
318
0
319
0
  // the factory callsite records whether the browser is in private browsing.
320
0
  // and thus we don't have to respect that setting here. IndexedDB has no
321
0
  // concept of session-local storage, and thus ignores it.
322
0
  if (access == nsContentUtils::StorageAccess::eDeny) {
323
0
    return NS_ERROR_DOM_SECURITY_ERR;
324
0
  }
325
0
326
0
  nsCOMPtr<nsIScriptObjectPrincipal> sop = do_QueryInterface(aWindow);
327
0
  MOZ_ASSERT(sop);
328
0
329
0
  nsCOMPtr<nsIPrincipal> principal = sop->GetPrincipal();
330
0
  if (NS_WARN_IF(!principal)) {
331
0
    return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
332
0
333
0
  }
334
0
335
0
  if (nsContentUtils::IsSystemPrincipal(principal)) {
336
0
    principal.forget(aPrincipal);
337
0
    return NS_OK;
338
0
  }
339
0
340
0
  // About URIs shouldn't be able to access IndexedDB unless they have the
341
0
  // nsIAboutModule::ENABLE_INDEXED_DB flag set on them.
342
0
  nsCOMPtr<nsIURI> uri;
343
0
  MOZ_ALWAYS_SUCCEEDS(principal->GetURI(getter_AddRefs(uri)));
344
0
  MOZ_ASSERT(uri);
345
0
346
0
  bool isAbout = false;
347
0
  MOZ_ALWAYS_SUCCEEDS(uri->SchemeIs("about", &isAbout));
348
0
349
0
  if (isAbout) {
350
0
    nsCOMPtr<nsIAboutModule> module;
351
0
    if (NS_SUCCEEDED(NS_GetAboutModule(uri, getter_AddRefs(module)))) {
352
0
      uint32_t flags;
353
0
      if (NS_SUCCEEDED(module->GetURIFlags(uri, &flags))) {
354
0
        if (!(flags & nsIAboutModule::ENABLE_INDEXED_DB)) {
355
0
          return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
356
0
        }
357
0
      } else {
358
0
        return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
359
0
      }
360
0
    } else {
361
0
      return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
362
0
    }
363
0
  }
364
0
365
0
  principal.forget(aPrincipal);
366
0
  return NS_OK;
367
0
}
368
369
// static
370
bool
371
IDBFactory::AllowedForPrincipal(nsIPrincipal* aPrincipal,
372
                                bool* aIsSystemPrincipal)
373
0
{
374
0
  MOZ_ASSERT(NS_IsMainThread());
375
0
  MOZ_ASSERT(aPrincipal);
376
0
377
0
  if (NS_WARN_IF(!IndexedDatabaseManager::GetOrCreate())) {
378
0
    return false;
379
0
  }
380
0
381
0
  if (nsContentUtils::IsSystemPrincipal(aPrincipal)) {
382
0
    if (aIsSystemPrincipal) {
383
0
      *aIsSystemPrincipal = true;
384
0
    }
385
0
    return true;
386
0
  } else if (aIsSystemPrincipal) {
387
0
    *aIsSystemPrincipal = false;
388
0
  }
389
0
390
0
  if (aPrincipal->GetIsNullPrincipal()) {
391
0
    return false;
392
0
  }
393
0
394
0
  return true;
395
0
}
396
397
void
398
IDBFactory::UpdateActiveTransactionCount(int32_t aDelta)
399
0
{
400
0
  AssertIsOnOwningThread();
401
0
  MOZ_DIAGNOSTIC_ASSERT(aDelta > 0 ||
402
0
                        (mActiveTransactionCount + aDelta) < mActiveTransactionCount);
403
0
  mActiveTransactionCount += aDelta;
404
0
  if (mWindow) {
405
0
    mWindow->UpdateActiveIndexedDBTransactionCount(aDelta);
406
0
  }
407
0
}
408
409
void
410
IDBFactory::UpdateActiveDatabaseCount(int32_t aDelta)
411
0
{
412
0
  AssertIsOnOwningThread();
413
0
  MOZ_DIAGNOSTIC_ASSERT(aDelta > 0 ||
414
0
                        (mActiveDatabaseCount + aDelta) < mActiveDatabaseCount);
415
0
  mActiveDatabaseCount += aDelta;
416
0
  if (mWindow) {
417
0
    mWindow->UpdateActiveIndexedDBDatabaseCount(aDelta);
418
0
  }
419
0
}
420
421
bool
422
IDBFactory::IsChrome() const
423
0
{
424
0
  AssertIsOnOwningThread();
425
0
  MOZ_ASSERT(mPrincipalInfo);
426
0
427
0
  return mPrincipalInfo->type() == PrincipalInfo::TSystemPrincipalInfo;
428
0
}
429
430
void
431
IDBFactory::IncrementParentLoggingRequestSerialNumber()
432
0
{
433
0
  AssertIsOnOwningThread();
434
0
  MOZ_ASSERT(mBackgroundActor);
435
0
436
0
  mBackgroundActor->SendIncrementLoggingRequestSerialNumber();
437
0
}
438
439
already_AddRefed<IDBOpenDBRequest>
440
IDBFactory::Open(JSContext* aCx,
441
                 const nsAString& aName,
442
                 uint64_t aVersion,
443
                 CallerType aCallerType,
444
                 ErrorResult& aRv)
445
0
{
446
0
  return OpenInternal(aCx,
447
0
                      /* aPrincipal */ nullptr,
448
0
                      aName,
449
0
                      Optional<uint64_t>(aVersion),
450
0
                      Optional<StorageType>(),
451
0
                      /* aDeleting */ false,
452
0
                      aCallerType,
453
0
                      aRv);
454
0
}
455
456
already_AddRefed<IDBOpenDBRequest>
457
IDBFactory::Open(JSContext* aCx,
458
                 const nsAString& aName,
459
                 const IDBOpenDBOptions& aOptions,
460
                 CallerType aCallerType,
461
                 ErrorResult& aRv)
462
0
{
463
0
  if (!IsChrome() &&
464
0
      aOptions.mStorage.WasPassed()) {
465
0
466
0
    if (mWindow && mWindow->GetExtantDoc()) {
467
0
      mWindow->GetExtantDoc()->WarnOnceAbout(nsIDocument::eIDBOpenDBOptions_StorageType);
468
0
    } else if (!NS_IsMainThread()) {
469
0
      // The method below reports on the main thread too, so we need to make sure we're on a worker.
470
0
      // Workers don't have a WarnOnceAbout mechanism, so this will be reported every time.
471
0
      WorkerPrivate::ReportErrorToConsole("IDBOpenDBOptions_StorageType");
472
0
    }
473
0
474
0
    bool ignore = false;
475
0
    // Ignore internal usage on about: pages.
476
0
    if (NS_IsMainThread()) {
477
0
      nsCOMPtr<nsIPrincipal> principal = PrincipalInfoToPrincipal(*mPrincipalInfo);
478
0
      if (principal) {
479
0
        nsCOMPtr<nsIURI> uri;
480
0
        nsresult rv = principal->GetURI(getter_AddRefs(uri));
481
0
        if (NS_SUCCEEDED(rv) && uri) {
482
0
          bool isAbout;
483
0
          rv = uri->SchemeIs("about", &isAbout);
484
0
          if (NS_SUCCEEDED(rv) && isAbout) {
485
0
            ignore = true;
486
0
          }
487
0
        }
488
0
      }
489
0
    }
490
0
491
0
    if (!ignore) {
492
0
      switch (aOptions.mStorage.Value()) {
493
0
        case StorageType::Persistent: {
494
0
          Telemetry::ScalarAdd(Telemetry::ScalarID::IDB_TYPE_PERSISTENT_COUNT, 1);
495
0
          break;
496
0
        }
497
0
498
0
        case StorageType::Temporary: {
499
0
          Telemetry::ScalarAdd(Telemetry::ScalarID::IDB_TYPE_TEMPORARY_COUNT, 1);
500
0
          break;
501
0
        }
502
0
503
0
        case StorageType::Default:
504
0
        case StorageType::EndGuard_:
505
0
          break;
506
0
507
0
        default:
508
0
          MOZ_CRASH("Invalid storage type!");
509
0
      }
510
0
    }
511
0
  }
512
0
513
0
  return OpenInternal(aCx,
514
0
                      /* aPrincipal */ nullptr,
515
0
                      aName,
516
0
                      aOptions.mVersion,
517
0
                      aOptions.mStorage,
518
0
                      /* aDeleting */ false,
519
0
                      aCallerType,
520
0
                      aRv);
521
0
}
522
523
already_AddRefed<IDBOpenDBRequest>
524
IDBFactory::DeleteDatabase(JSContext* aCx,
525
                           const nsAString& aName,
526
                           const IDBOpenDBOptions& aOptions,
527
                           CallerType aCallerType,
528
                           ErrorResult& aRv)
529
0
{
530
0
  return OpenInternal(aCx,
531
0
                      /* aPrincipal */ nullptr,
532
0
                      aName,
533
0
                      Optional<uint64_t>(),
534
0
                      aOptions.mStorage,
535
0
                      /* aDeleting */ true,
536
0
                      aCallerType,
537
0
                      aRv);
538
0
}
539
540
int16_t
541
IDBFactory::Cmp(JSContext* aCx, JS::Handle<JS::Value> aFirst,
542
                JS::Handle<JS::Value> aSecond, ErrorResult& aRv)
543
0
{
544
0
  Key first, second;
545
0
  nsresult rv = first.SetFromJSVal(aCx, aFirst);
546
0
  if (NS_FAILED(rv)) {
547
0
    aRv.Throw(rv);
548
0
    return 0;
549
0
  }
550
0
551
0
  rv = second.SetFromJSVal(aCx, aSecond);
552
0
  if (NS_FAILED(rv)) {
553
0
    aRv.Throw(rv);
554
0
    return 0;
555
0
  }
556
0
557
0
  if (first.IsUnset() || second.IsUnset()) {
558
0
    aRv.Throw(NS_ERROR_DOM_INDEXEDDB_DATA_ERR);
559
0
    return 0;
560
0
  }
561
0
562
0
  return Key::CompareKeys(first, second);
563
0
}
564
565
already_AddRefed<IDBOpenDBRequest>
566
IDBFactory::OpenForPrincipal(JSContext* aCx,
567
                             nsIPrincipal* aPrincipal,
568
                             const nsAString& aName,
569
                             uint64_t aVersion,
570
                             SystemCallerGuarantee aGuarantee,
571
                             ErrorResult& aRv)
572
0
{
573
0
  MOZ_ASSERT(aPrincipal);
574
0
  if (!NS_IsMainThread()) {
575
0
    MOZ_CRASH("Figure out security checks for workers!  What's this aPrincipal "
576
0
              "we have on a worker thread?");
577
0
  }
578
0
579
0
  return OpenInternal(aCx,
580
0
                      aPrincipal,
581
0
                      aName,
582
0
                      Optional<uint64_t>(aVersion),
583
0
                      Optional<StorageType>(),
584
0
                      /* aDeleting */ false,
585
0
                      aGuarantee,
586
0
                      aRv);
587
0
}
588
589
already_AddRefed<IDBOpenDBRequest>
590
IDBFactory::OpenForPrincipal(JSContext* aCx,
591
                             nsIPrincipal* aPrincipal,
592
                             const nsAString& aName,
593
                             const IDBOpenDBOptions& aOptions,
594
                             SystemCallerGuarantee aGuarantee,
595
                             ErrorResult& aRv)
596
0
{
597
0
  MOZ_ASSERT(aPrincipal);
598
0
  if (!NS_IsMainThread()) {
599
0
    MOZ_CRASH("Figure out security checks for workers!  What's this aPrincipal "
600
0
              "we have on a worker thread?");
601
0
  }
602
0
603
0
  return OpenInternal(aCx,
604
0
                      aPrincipal,
605
0
                      aName,
606
0
                      aOptions.mVersion,
607
0
                      aOptions.mStorage,
608
0
                      /* aDeleting */ false,
609
0
                      aGuarantee,
610
0
                      aRv);
611
0
}
612
613
already_AddRefed<IDBOpenDBRequest>
614
IDBFactory::DeleteForPrincipal(JSContext* aCx,
615
                               nsIPrincipal* aPrincipal,
616
                               const nsAString& aName,
617
                               const IDBOpenDBOptions& aOptions,
618
                               SystemCallerGuarantee aGuarantee,
619
                               ErrorResult& aRv)
620
0
{
621
0
  MOZ_ASSERT(aPrincipal);
622
0
  if (!NS_IsMainThread()) {
623
0
    MOZ_CRASH("Figure out security checks for workers!  What's this aPrincipal "
624
0
              "we have on a worker thread?");
625
0
  }
626
0
627
0
  return OpenInternal(aCx,
628
0
                      aPrincipal,
629
0
                      aName,
630
0
                      Optional<uint64_t>(),
631
0
                      aOptions.mStorage,
632
0
                      /* aDeleting */ true,
633
0
                      aGuarantee,
634
0
                      aRv);
635
0
}
636
637
already_AddRefed<IDBOpenDBRequest>
638
IDBFactory::OpenInternal(JSContext* aCx,
639
                         nsIPrincipal* aPrincipal,
640
                         const nsAString& aName,
641
                         const Optional<uint64_t>& aVersion,
642
                         const Optional<StorageType>& aStorageType,
643
                         bool aDeleting,
644
                         CallerType aCallerType,
645
                         ErrorResult& aRv)
646
0
{
647
0
  MOZ_ASSERT(mWindow || mOwningObject);
648
0
  MOZ_ASSERT_IF(!mWindow, !mPrivateBrowsingMode);
649
0
650
0
  CommonFactoryRequestParams commonParams;
651
0
652
0
  PrincipalInfo& principalInfo = commonParams.principalInfo();
653
0
654
0
  if (aPrincipal) {
655
0
    if (!NS_IsMainThread()) {
656
0
      MOZ_CRASH("Figure out security checks for workers!  What's this "
657
0
                "aPrincipal we have on a worker thread?");
658
0
    }
659
0
    MOZ_ASSERT(aCallerType == CallerType::System);
660
0
    MOZ_DIAGNOSTIC_ASSERT(mPrivateBrowsingMode == (aPrincipal->GetPrivateBrowsingId() > 0));
661
0
662
0
    if (NS_WARN_IF(NS_FAILED(PrincipalToPrincipalInfo(aPrincipal,
663
0
                                                      &principalInfo)))) {
664
0
      IDB_REPORT_INTERNAL_ERR();
665
0
      aRv.Throw(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
666
0
      return nullptr;
667
0
    }
668
0
669
0
    if (principalInfo.type() != PrincipalInfo::TContentPrincipalInfo &&
670
0
        principalInfo.type() != PrincipalInfo::TSystemPrincipalInfo) {
671
0
      IDB_REPORT_INTERNAL_ERR();
672
0
      aRv.Throw(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
673
0
      return nullptr;
674
0
    }
675
0
  } else {
676
0
    principalInfo = *mPrincipalInfo;
677
0
  }
678
0
679
0
  uint64_t version = 0;
680
0
  if (!aDeleting && aVersion.WasPassed()) {
681
0
    if (aVersion.Value() < 1) {
682
0
      aRv.ThrowTypeError<MSG_INVALID_VERSION>();
683
0
      return nullptr;
684
0
    }
685
0
    version = aVersion.Value();
686
0
  }
687
0
688
0
  // Nothing can be done here if we have previously failed to create a
689
0
  // background actor.
690
0
  if (mBackgroundActorFailed) {
691
0
    IDB_REPORT_INTERNAL_ERR();
692
0
    aRv.Throw(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
693
0
    return nullptr;
694
0
  }
695
0
696
0
  PersistenceType persistenceType;
697
0
698
0
  bool isInternal = principalInfo.type() == PrincipalInfo::TSystemPrincipalInfo;
699
0
  if (!isInternal && principalInfo.type() == PrincipalInfo::TContentPrincipalInfo) {
700
0
    nsCString origin = principalInfo.get_ContentPrincipalInfo().originNoSuffix();
701
0
    isInternal = QuotaManager::IsOriginInternal(origin);
702
0
  }
703
0
704
0
  // Allow storage attributes for add-ons independent of the pref.
705
0
  // This works in the main thread only, workers don't have the principal.
706
0
  bool isAddon = false;
707
0
  if (NS_IsMainThread()) {
708
0
    // aPrincipal is passed inconsistently, so even when we are already on
709
0
    // the main thread, we may have been passed a null aPrincipal.
710
0
    nsCOMPtr<nsIPrincipal> principal = PrincipalInfoToPrincipal(principalInfo);
711
0
    if (principal) {
712
0
      nsAutoString addonId;
713
0
      Unused << NS_WARN_IF(NS_FAILED(principal->GetAddonId(addonId)));
714
0
      isAddon = !addonId.IsEmpty();
715
0
    }
716
0
  }
717
0
718
0
  if (isInternal) {
719
0
    // Chrome privilege and internal origins always get persistent storage.
720
0
    persistenceType = PERSISTENCE_TYPE_PERSISTENT;
721
0
  } else if (isAddon || StaticPrefs::dom_indexedDB_storageOption_enabled()) {
722
0
    persistenceType = PersistenceTypeFromStorage(aStorageType);
723
0
  } else {
724
0
    persistenceType = PERSISTENCE_TYPE_DEFAULT;
725
0
  }
726
0
727
0
  DatabaseMetadata& metadata = commonParams.metadata();
728
0
  metadata.name() = aName;
729
0
  metadata.persistenceType() = persistenceType;
730
0
731
0
  FactoryRequestParams params;
732
0
  if (aDeleting) {
733
0
    metadata.version() = 0;
734
0
    params = DeleteDatabaseRequestParams(commonParams);
735
0
  } else {
736
0
    metadata.version() = version;
737
0
    params = OpenDatabaseRequestParams(commonParams);
738
0
  }
739
0
740
0
  if (!mBackgroundActor) {
741
0
    BackgroundChildImpl::ThreadLocal* threadLocal =
742
0
      BackgroundChildImpl::GetThreadLocalForCurrentThread();
743
0
744
0
    nsAutoPtr<ThreadLocal> newIDBThreadLocal;
745
0
    ThreadLocal* idbThreadLocal;
746
0
747
0
    if (threadLocal && threadLocal->mIndexedDBThreadLocal) {
748
0
      idbThreadLocal = threadLocal->mIndexedDBThreadLocal;
749
0
    } else {
750
0
      nsCOMPtr<nsIUUIDGenerator> uuidGen =
751
0
        do_GetService("@mozilla.org/uuid-generator;1");
752
0
      MOZ_ASSERT(uuidGen);
753
0
754
0
      nsID id;
755
0
      MOZ_ALWAYS_SUCCEEDS(uuidGen->GenerateUUIDInPlace(&id));
756
0
757
0
      newIDBThreadLocal = idbThreadLocal = new ThreadLocal(id);
758
0
    }
759
0
760
0
    PBackgroundChild* backgroundActor =
761
0
      BackgroundChild::GetOrCreateForCurrentThread();
762
0
    if (NS_WARN_IF(!backgroundActor)) {
763
0
      IDB_REPORT_INTERNAL_ERR();
764
0
      aRv.Throw(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
765
0
      return nullptr;
766
0
    }
767
0
768
0
    {
769
0
      BackgroundFactoryChild* actor = new BackgroundFactoryChild(this);
770
0
771
0
      // Set EventTarget for the top-level actor.
772
0
      // All child actors created later inherit the same event target.
773
0
      backgroundActor->SetEventTargetForActor(actor, EventTarget());
774
0
      MOZ_ASSERT(actor->GetActorEventTarget());
775
0
      mBackgroundActor =
776
0
        static_cast<BackgroundFactoryChild*>(
777
0
          backgroundActor->SendPBackgroundIDBFactoryConstructor(actor,
778
0
                                                                idbThreadLocal->GetLoggingInfo()));
779
0
780
0
      if (NS_WARN_IF(!mBackgroundActor)) {
781
0
        mBackgroundActorFailed = true;
782
0
        IDB_REPORT_INTERNAL_ERR();
783
0
        aRv.Throw(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
784
0
        return nullptr;
785
0
      }
786
0
    }
787
0
788
0
    if (newIDBThreadLocal) {
789
0
      if (!threadLocal) {
790
0
        threadLocal = BackgroundChildImpl::GetThreadLocalForCurrentThread();
791
0
      }
792
0
      MOZ_ASSERT(threadLocal);
793
0
      MOZ_ASSERT(!threadLocal->mIndexedDBThreadLocal);
794
0
795
0
      threadLocal->mIndexedDBThreadLocal = newIDBThreadLocal.forget();
796
0
    }
797
0
  }
798
0
799
0
  RefPtr<IDBOpenDBRequest> request;
800
0
801
0
  if (mWindow) {
802
0
    JS::Rooted<JSObject*> scriptOwner(aCx,
803
0
                                      nsGlobalWindowInner::Cast(mWindow.get())->FastGetGlobalJSObject());
804
0
    MOZ_ASSERT(scriptOwner);
805
0
806
0
    request = IDBOpenDBRequest::CreateForWindow(aCx, this, mWindow, scriptOwner);
807
0
  } else {
808
0
    JS::Rooted<JSObject*> scriptOwner(aCx, mOwningObject);
809
0
810
0
    request = IDBOpenDBRequest::CreateForJS(aCx, this, scriptOwner);
811
0
    if (!request) {
812
0
      MOZ_ASSERT(!NS_IsMainThread());
813
0
      aRv.ThrowUncatchableException();
814
0
      return nullptr;
815
0
    }
816
0
  }
817
0
818
0
  MOZ_ASSERT(request);
819
0
820
0
  if (aDeleting) {
821
0
    IDB_LOG_MARK("IndexedDB %s: Child  Request[%llu]: "
822
0
                   "indexedDB.deleteDatabase(\"%s\")",
823
0
                 "IndexedDB %s: C R[%llu]: IDBFactory.deleteDatabase()",
824
0
                 IDB_LOG_ID_STRING(),
825
0
                 request->LoggingSerialNumber(),
826
0
                 NS_ConvertUTF16toUTF8(aName).get());
827
0
  } else {
828
0
    IDB_LOG_MARK("IndexedDB %s: Child  Request[%llu]: "
829
0
                   "indexedDB.open(\"%s\", %s)",
830
0
                 "IndexedDB %s: C R[%llu]: IDBFactory.open()",
831
0
                 IDB_LOG_ID_STRING(),
832
0
                 request->LoggingSerialNumber(),
833
0
                 NS_ConvertUTF16toUTF8(aName).get(),
834
0
                 IDB_LOG_STRINGIFY(aVersion));
835
0
  }
836
0
837
0
  nsresult rv = InitiateRequest(request, params);
838
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
839
0
    IDB_REPORT_INTERNAL_ERR();
840
0
    aRv.Throw(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
841
0
    return nullptr;
842
0
  }
843
0
844
0
  return request.forget();
845
0
}
846
847
nsresult
848
IDBFactory::InitiateRequest(IDBOpenDBRequest* aRequest,
849
                            const FactoryRequestParams& aParams)
850
0
{
851
0
  MOZ_ASSERT(aRequest);
852
0
  MOZ_ASSERT(mBackgroundActor);
853
0
  MOZ_ASSERT(!mBackgroundActorFailed);
854
0
855
0
  bool deleting;
856
0
  uint64_t requestedVersion;
857
0
858
0
  switch (aParams.type()) {
859
0
    case FactoryRequestParams::TDeleteDatabaseRequestParams: {
860
0
      const DatabaseMetadata& metadata =
861
0
        aParams.get_DeleteDatabaseRequestParams().commonParams().metadata();
862
0
      deleting = true;
863
0
      requestedVersion = metadata.version();
864
0
      break;
865
0
    }
866
0
867
0
    case FactoryRequestParams::TOpenDatabaseRequestParams: {
868
0
      const DatabaseMetadata& metadata =
869
0
        aParams.get_OpenDatabaseRequestParams().commonParams().metadata();
870
0
      deleting = false;
871
0
      requestedVersion = metadata.version();
872
0
      break;
873
0
    }
874
0
875
0
    default:
876
0
      MOZ_CRASH("Should never get here!");
877
0
  }
878
0
879
0
  auto actor =
880
0
    new BackgroundFactoryRequestChild(this,
881
0
                                      aRequest,
882
0
                                      deleting,
883
0
                                      requestedVersion);
884
0
885
0
  if (!mBackgroundActor->SendPBackgroundIDBFactoryRequestConstructor(actor,
886
0
                                                                     aParams)) {
887
0
    aRequest->DispatchNonTransactionError(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
888
0
    return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
889
0
  }
890
0
891
0
  MOZ_ASSERT(actor->GetActorEventTarget(),
892
0
    "The event target shall be inherited from its manager actor.");
893
0
894
0
  return NS_OK;
895
0
}
896
897
void
898
IDBFactory::RebindToNewWindow(nsPIDOMWindowInner* aNewWindow)
899
0
{
900
0
  MOZ_DIAGNOSTIC_ASSERT(aNewWindow);
901
0
  MOZ_DIAGNOSTIC_ASSERT(mWindow);
902
0
  MOZ_DIAGNOSTIC_ASSERT(aNewWindow != mWindow);
903
0
904
0
  mWindow->UpdateActiveIndexedDBTransactionCount(-1 * mActiveTransactionCount);
905
0
  mWindow->UpdateActiveIndexedDBDatabaseCount(-1 * mActiveDatabaseCount);
906
0
907
0
  mWindow = aNewWindow;
908
0
909
0
  mInnerWindowID = aNewWindow->WindowID();
910
0
  mWindow->UpdateActiveIndexedDBTransactionCount(mActiveTransactionCount);
911
0
  mWindow->UpdateActiveIndexedDBDatabaseCount(mActiveDatabaseCount);
912
0
}
913
914
void
915
IDBFactory::DisconnectFromWindow(nsPIDOMWindowInner* aOldWindow)
916
0
{
917
0
  MOZ_DIAGNOSTIC_ASSERT(aOldWindow);
918
0
  // If CC unlinks us first, then mWindow might be nullptr
919
0
  MOZ_DIAGNOSTIC_ASSERT(!mWindow || mWindow == aOldWindow);
920
0
921
0
  mWindow = nullptr;
922
0
}
923
924
NS_IMPL_CYCLE_COLLECTING_ADDREF(IDBFactory)
925
NS_IMPL_CYCLE_COLLECTING_RELEASE(IDBFactory)
926
927
0
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(IDBFactory)
928
0
  NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
929
0
  NS_INTERFACE_MAP_ENTRY(nsISupports)
930
0
NS_INTERFACE_MAP_END
931
932
NS_IMPL_CYCLE_COLLECTION_CLASS(IDBFactory)
933
934
0
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(IDBFactory)
935
0
  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mWindow)
936
0
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
937
938
0
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(IDBFactory)
939
0
  NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
940
0
  tmp->mOwningObject = nullptr;
941
0
  NS_IMPL_CYCLE_COLLECTION_UNLINK(mWindow)
942
0
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
943
944
0
NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(IDBFactory)
945
0
  NS_IMPL_CYCLE_COLLECTION_TRACE_PRESERVED_WRAPPER
946
0
  NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mOwningObject)
947
0
NS_IMPL_CYCLE_COLLECTION_TRACE_END
948
949
JSObject*
950
IDBFactory::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
951
0
{
952
0
  return IDBFactory_Binding::Wrap(aCx, this, aGivenProto);
953
0
}
954
955
} // namespace dom
956
} // namespace mozilla