Bug 1892041 - Part 1: Update test262 features. r=spidermonkey-reviewers,dminor
[gecko.git] / dom / indexedDB / IDBFactory.cpp
blob51d4c4df239dd1e310b73a7ba5abeac14db02436
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/. */
7 #include "IDBFactory.h"
9 #include "BackgroundChildImpl.h"
10 #include "IDBRequest.h"
11 #include "IndexedDatabaseManager.h"
12 #include "mozilla/BasePrincipal.h"
13 #include "mozilla/ErrorResult.h"
14 #include "mozilla/Preferences.h"
15 #include "mozilla/dom/BindingDeclarations.h"
16 #include "mozilla/dom/Document.h"
17 #include "mozilla/dom/IDBFactoryBinding.h"
18 #include "mozilla/dom/Promise.h"
19 #include "mozilla/dom/quota/QuotaManager.h"
20 #include "mozilla/dom/quota/ResultExtensions.h"
21 #include "mozilla/dom/BrowserChild.h"
22 #include "mozilla/dom/WorkerPrivate.h"
23 #include "mozilla/ipc/BackgroundChild.h"
24 #include "mozilla/ipc/BackgroundUtils.h"
25 #include "mozilla/ipc/PBackground.h"
26 #include "mozilla/ipc/PBackgroundChild.h"
27 #include "mozilla/StaticPrefs_dom.h"
28 #include "mozilla/StorageAccess.h"
29 #include "mozilla/Telemetry.h"
30 #include "nsAboutProtocolUtils.h"
31 #include "nsContentUtils.h"
32 #include "nsGlobalWindowInner.h"
33 #include "nsIAboutModule.h"
34 #include "nsILoadContext.h"
35 #include "nsIURI.h"
36 #include "nsIUUIDGenerator.h"
37 #include "nsIWebNavigation.h"
38 #include "nsNetUtil.h"
39 #include "nsSandboxFlags.h"
40 #include "nsServiceManagerUtils.h"
41 #include "ProfilerHelpers.h"
42 #include "ReportInternalError.h"
43 #include "ThreadLocal.h"
45 // Include this last to avoid path problems on Windows.
46 #include "ActorsChild.h"
48 namespace mozilla::dom {
50 using namespace mozilla::dom::indexedDB;
51 using namespace mozilla::dom::quota;
52 using namespace mozilla::ipc;
54 namespace {
56 Telemetry::LABELS_IDB_CUSTOM_OPEN_WITH_OPTIONS_COUNT IdentifyPrincipalType(
57 const mozilla::ipc::PrincipalInfo& aPrincipalInfo) {
58 switch (aPrincipalInfo.type()) {
59 case PrincipalInfo::TSystemPrincipalInfo:
60 return Telemetry::LABELS_IDB_CUSTOM_OPEN_WITH_OPTIONS_COUNT::system;
61 case PrincipalInfo::TContentPrincipalInfo: {
62 const ContentPrincipalInfo& info =
63 aPrincipalInfo.get_ContentPrincipalInfo();
65 nsCOMPtr<nsIURI> uri;
67 if (NS_WARN_IF(NS_FAILED(NS_NewURI(getter_AddRefs(uri), info.spec())))) {
68 // This could be discriminated as an extra error value, but this is
69 // extremely unlikely to fail, so we just misuse ContentOther
70 return Telemetry::LABELS_IDB_CUSTOM_OPEN_WITH_OPTIONS_COUNT::
71 content_other;
74 // TODO Are there constants defined for the schemes somewhere?
75 if (uri->SchemeIs("file")) {
76 return Telemetry::LABELS_IDB_CUSTOM_OPEN_WITH_OPTIONS_COUNT::
77 content_file;
79 if (uri->SchemeIs("http") || uri->SchemeIs("https")) {
80 return Telemetry::LABELS_IDB_CUSTOM_OPEN_WITH_OPTIONS_COUNT::
81 content_http_https;
83 if (uri->SchemeIs("moz-extension")) {
84 return Telemetry::LABELS_IDB_CUSTOM_OPEN_WITH_OPTIONS_COUNT::
85 content_moz_ext;
87 if (uri->SchemeIs("about")) {
88 return Telemetry::LABELS_IDB_CUSTOM_OPEN_WITH_OPTIONS_COUNT::
89 content_about;
91 return Telemetry::LABELS_IDB_CUSTOM_OPEN_WITH_OPTIONS_COUNT::
92 content_other;
94 case PrincipalInfo::TExpandedPrincipalInfo:
95 return Telemetry::LABELS_IDB_CUSTOM_OPEN_WITH_OPTIONS_COUNT::expanded;
96 default:
97 return Telemetry::LABELS_IDB_CUSTOM_OPEN_WITH_OPTIONS_COUNT::other;
101 } // namespace
103 struct IDBFactory::PendingRequestInfo {
104 RefPtr<IDBOpenDBRequest> mRequest;
105 FactoryRequestParams mParams;
107 PendingRequestInfo(IDBOpenDBRequest* aRequest,
108 const FactoryRequestParams& aParams)
109 : mRequest(aRequest), mParams(aParams) {
110 MOZ_ASSERT(aRequest);
111 MOZ_ASSERT(aParams.type() != FactoryRequestParams::T__None);
115 IDBFactory::IDBFactory(const IDBFactoryGuard&)
116 : mBackgroundActor(nullptr),
117 mInnerWindowID(0),
118 mActiveTransactionCount(0),
119 mActiveDatabaseCount(0),
120 mBackgroundActorFailed(false),
121 mPrivateBrowsingMode(false) {
122 AssertIsOnOwningThread();
125 IDBFactory::~IDBFactory() {
126 MOZ_ASSERT_IF(mBackgroundActorFailed, !mBackgroundActor);
128 if (mBackgroundActor) {
129 mBackgroundActor->SendDeleteMeInternal();
130 MOZ_ASSERT(!mBackgroundActor, "SendDeleteMeInternal should have cleared!");
134 // static
135 Result<RefPtr<IDBFactory>, nsresult> IDBFactory::CreateForWindow(
136 nsPIDOMWindowInner* aWindow) {
137 MOZ_ASSERT(NS_IsMainThread());
138 MOZ_ASSERT(aWindow);
140 nsCOMPtr<nsIPrincipal> principal;
141 nsresult rv = AllowedForWindowInternal(aWindow, &principal);
143 if (rv == NS_ERROR_DOM_NOT_SUPPORTED_ERR) {
144 NS_WARNING("IndexedDB is not permitted in a third-party window.");
145 return RefPtr<IDBFactory>{};
148 if (NS_WARN_IF(NS_FAILED(rv))) {
149 if (rv == NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR) {
150 IDB_REPORT_INTERNAL_ERR();
152 return Err(rv);
155 MOZ_ASSERT(principal);
157 auto principalInfo = MakeUnique<PrincipalInfo>();
158 rv = PrincipalToPrincipalInfo(principal, principalInfo.get());
159 if (NS_WARN_IF(NS_FAILED(rv))) {
160 IDB_REPORT_INTERNAL_ERR();
161 return Err(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
164 MOZ_ASSERT(principalInfo->type() == PrincipalInfo::TContentPrincipalInfo ||
165 principalInfo->type() == PrincipalInfo::TSystemPrincipalInfo);
167 if (NS_WARN_IF(!QuotaManager::IsPrincipalInfoValid(*principalInfo))) {
168 IDB_REPORT_INTERNAL_ERR();
169 return Err(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
172 nsCOMPtr<nsIWebNavigation> webNav = do_GetInterface(aWindow);
173 nsCOMPtr<nsILoadContext> loadContext = do_QueryInterface(webNav);
175 auto factory = MakeRefPtr<IDBFactory>(IDBFactoryGuard{});
176 factory->mPrincipalInfo = std::move(principalInfo);
178 factory->BindToOwner(aWindow->AsGlobal());
180 factory->mBrowserChild = BrowserChild::GetFrom(aWindow);
181 factory->mEventTarget =
182 nsGlobalWindowInner::Cast(aWindow)->SerialEventTarget();
183 factory->mInnerWindowID = aWindow->WindowID();
184 factory->mPrivateBrowsingMode =
185 loadContext && loadContext->UsePrivateBrowsing();
187 return factory;
190 // static
191 Result<RefPtr<IDBFactory>, nsresult> IDBFactory::CreateForMainThreadJS(
192 nsIGlobalObject* aGlobal) {
193 MOZ_ASSERT(NS_IsMainThread());
194 MOZ_ASSERT(aGlobal);
196 nsCOMPtr<nsIScriptObjectPrincipal> sop = do_QueryInterface(aGlobal);
197 if (NS_WARN_IF(!sop)) {
198 return Err(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
201 auto principalInfo = MakeUnique<PrincipalInfo>();
202 nsIPrincipal* principal = sop->GetEffectiveStoragePrincipal();
203 MOZ_ASSERT(principal);
204 bool isSystem;
205 if (!AllowedForPrincipal(principal, &isSystem)) {
206 return Err(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
209 nsresult rv = PrincipalToPrincipalInfo(principal, principalInfo.get());
210 if (NS_WARN_IF(NS_FAILED(rv))) {
211 return Err(rv);
214 if (NS_WARN_IF(!QuotaManager::IsPrincipalInfoValid(*principalInfo))) {
215 return Err(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
218 return CreateForMainThreadJSInternal(aGlobal, std::move(principalInfo));
221 // static
222 Result<RefPtr<IDBFactory>, nsresult> IDBFactory::CreateForWorker(
223 nsIGlobalObject* aGlobal, const PrincipalInfo& aPrincipalInfo,
224 uint64_t aInnerWindowID) {
225 MOZ_ASSERT(!NS_IsMainThread());
226 MOZ_ASSERT(aGlobal);
227 MOZ_ASSERT(aPrincipalInfo.type() != PrincipalInfo::T__None);
229 return CreateInternal(aGlobal, MakeUnique<PrincipalInfo>(aPrincipalInfo),
230 aInnerWindowID);
233 // static
234 Result<RefPtr<IDBFactory>, nsresult> IDBFactory::CreateForMainThreadJSInternal(
235 nsIGlobalObject* aGlobal, UniquePtr<PrincipalInfo> aPrincipalInfo) {
236 MOZ_ASSERT(NS_IsMainThread());
237 MOZ_ASSERT(aGlobal);
238 MOZ_ASSERT(aPrincipalInfo);
240 IndexedDatabaseManager* mgr = IndexedDatabaseManager::GetOrCreate();
241 if (NS_WARN_IF(!mgr)) {
242 IDB_REPORT_INTERNAL_ERR();
243 return Err(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
246 nsresult rv = mgr->EnsureLocale();
247 if (NS_WARN_IF(NS_FAILED(rv))) {
248 IDB_REPORT_INTERNAL_ERR();
249 return Err(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
252 return CreateInternal(aGlobal, std::move(aPrincipalInfo),
253 /* aInnerWindowID */ 0);
256 // static
257 Result<RefPtr<IDBFactory>, nsresult> IDBFactory::CreateInternal(
258 nsIGlobalObject* aGlobal, UniquePtr<PrincipalInfo> aPrincipalInfo,
259 uint64_t aInnerWindowID) {
260 MOZ_ASSERT(aGlobal);
261 MOZ_ASSERT(aPrincipalInfo);
262 MOZ_ASSERT(aPrincipalInfo->type() != PrincipalInfo::T__None);
264 if (aPrincipalInfo->type() != PrincipalInfo::TContentPrincipalInfo &&
265 aPrincipalInfo->type() != PrincipalInfo::TSystemPrincipalInfo) {
266 NS_WARNING("IndexedDB not allowed for this principal!");
267 return RefPtr<IDBFactory>{};
270 auto factory = MakeRefPtr<IDBFactory>(IDBFactoryGuard{});
271 factory->mPrincipalInfo = std::move(aPrincipalInfo);
272 factory->BindToOwner(aGlobal);
273 factory->mEventTarget = GetCurrentSerialEventTarget();
274 factory->mInnerWindowID = aInnerWindowID;
276 return factory;
279 // static
280 bool IDBFactory::AllowedForWindow(nsPIDOMWindowInner* aWindow) {
281 MOZ_ASSERT(NS_IsMainThread());
282 MOZ_ASSERT(aWindow);
284 return !NS_WARN_IF(NS_FAILED(AllowedForWindowInternal(aWindow, nullptr)));
287 // static
288 nsresult IDBFactory::AllowedForWindowInternal(
289 nsPIDOMWindowInner* aWindow, nsCOMPtr<nsIPrincipal>* aPrincipal) {
290 MOZ_ASSERT(NS_IsMainThread());
291 MOZ_ASSERT(aWindow);
293 IndexedDatabaseManager* mgr = IndexedDatabaseManager::GetOrCreate();
294 if (NS_WARN_IF(!mgr)) {
295 return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
298 nsresult rv = mgr->EnsureLocale();
299 if (NS_WARN_IF(NS_FAILED(rv))) {
300 IDB_REPORT_INTERNAL_ERR();
301 return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
304 StorageAccess access = StorageAllowedForWindow(aWindow);
306 // the factory callsite records whether the browser is in private browsing.
307 // and thus we don't have to respect that setting here. IndexedDB has no
308 // concept of session-local storage, and thus ignores it.
309 if (access == StorageAccess::eDeny) {
310 return NS_ERROR_DOM_SECURITY_ERR;
313 if (ShouldPartitionStorage(access) &&
314 !StoragePartitioningEnabled(
315 access, aWindow->GetExtantDoc()->CookieJarSettings())) {
316 return NS_ERROR_DOM_SECURITY_ERR;
319 nsCOMPtr<nsIScriptObjectPrincipal> sop = do_QueryInterface(aWindow);
320 MOZ_ASSERT(sop);
322 nsCOMPtr<nsIPrincipal> principal = sop->GetEffectiveStoragePrincipal();
323 if (NS_WARN_IF(!principal)) {
324 return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
327 if (principal->IsSystemPrincipal()) {
328 *aPrincipal = std::move(principal);
329 return NS_OK;
332 // About URIs shouldn't be able to access IndexedDB unless they have the
333 // nsIAboutModule::ENABLE_INDEXED_DB flag set on them.
335 if (principal->SchemeIs("about")) {
336 uint32_t flags;
337 if (NS_SUCCEEDED(principal->GetAboutModuleFlags(&flags))) {
338 if (!(flags & nsIAboutModule::ENABLE_INDEXED_DB)) {
339 return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
341 } else {
342 return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
346 if (aPrincipal) {
347 *aPrincipal = std::move(principal);
349 return NS_OK;
352 // static
353 bool IDBFactory::AllowedForPrincipal(nsIPrincipal* aPrincipal,
354 bool* aIsSystemPrincipal) {
355 MOZ_ASSERT(NS_IsMainThread());
356 MOZ_ASSERT(aPrincipal);
358 IndexedDatabaseManager* mgr = IndexedDatabaseManager::GetOrCreate();
359 if (NS_WARN_IF(!mgr)) {
360 return false;
363 nsresult rv = mgr->EnsureLocale();
364 if (NS_WARN_IF(NS_FAILED(rv))) {
365 return false;
368 if (aPrincipal->IsSystemPrincipal()) {
369 if (aIsSystemPrincipal) {
370 *aIsSystemPrincipal = true;
372 return true;
375 if (aIsSystemPrincipal) {
376 *aIsSystemPrincipal = false;
379 return !aPrincipal->GetIsNullPrincipal();
382 // static
383 PersistenceType IDBFactory::GetPersistenceType(
384 const PrincipalInfo& aPrincipalInfo) {
385 if (aPrincipalInfo.type() == PrincipalInfo::TSystemPrincipalInfo) {
386 // Chrome privilege always gets persistent storage.
387 return PERSISTENCE_TYPE_PERSISTENT;
390 if (aPrincipalInfo.type() == PrincipalInfo::TContentPrincipalInfo) {
391 nsCString origin =
392 aPrincipalInfo.get_ContentPrincipalInfo().originNoSuffix();
394 if (QuotaManager::IsOriginInternal(origin)) {
395 // Internal origins always get persistent storage.
396 return PERSISTENCE_TYPE_PERSISTENT;
399 if (aPrincipalInfo.get_ContentPrincipalInfo().attrs().mPrivateBrowsingId >
400 0) {
401 return PERSISTENCE_TYPE_PRIVATE;
405 return PERSISTENCE_TYPE_DEFAULT;
408 void IDBFactory::UpdateActiveTransactionCount(int32_t aDelta) {
409 AssertIsOnOwningThread();
410 MOZ_DIAGNOSTIC_ASSERT(aDelta > 0 || (mActiveTransactionCount + aDelta) <
411 mActiveTransactionCount);
412 mActiveTransactionCount += aDelta;
415 void IDBFactory::UpdateActiveDatabaseCount(int32_t aDelta) {
416 AssertIsOnOwningThread();
417 MOZ_DIAGNOSTIC_ASSERT(aDelta > 0 ||
418 (mActiveDatabaseCount + aDelta) < mActiveDatabaseCount);
419 mActiveDatabaseCount += aDelta;
421 if (GetOwner()) {
422 GetOwner()->UpdateActiveIndexedDBDatabaseCount(aDelta);
426 bool IDBFactory::IsChrome() const {
427 AssertIsOnOwningThread();
428 MOZ_ASSERT(mPrincipalInfo);
430 return mPrincipalInfo->type() == PrincipalInfo::TSystemPrincipalInfo;
433 RefPtr<IDBOpenDBRequest> IDBFactory::Open(JSContext* aCx,
434 const nsAString& aName,
435 uint64_t aVersion,
436 CallerType aCallerType,
437 ErrorResult& aRv) {
438 return OpenInternal(aCx,
439 /* aPrincipal */ nullptr, aName,
440 Optional<uint64_t>(aVersion),
441 /* aDeleting */ false, aCallerType, aRv);
444 RefPtr<IDBOpenDBRequest> IDBFactory::Open(JSContext* aCx,
445 const nsAString& aName,
446 const IDBOpenDBOptions& aOptions,
447 CallerType aCallerType,
448 ErrorResult& aRv) {
449 // This overload is nonstandard, see bug 1275496.
450 // Ignore calls with empty options for telemetry of usage count.
451 // Unfortunately, we cannot distinguish between the use of the method with
452 // only a single argument (which actually is a standard overload we don't want
453 // to count) an empty dictionary passed explicitly (which is the custom
454 // overload we would like to count). However, we assume that the latter is so
455 // rare that it can be neglected.
456 if (aOptions.IsAnyMemberPresent()) {
457 Telemetry::AccumulateCategorical(IdentifyPrincipalType(*mPrincipalInfo));
460 return OpenInternal(aCx,
461 /* aPrincipal */ nullptr, aName, aOptions.mVersion,
462 /* aDeleting */ false, aCallerType, aRv);
465 RefPtr<IDBOpenDBRequest> IDBFactory::DeleteDatabase(
466 JSContext* aCx, const nsAString& aName, const IDBOpenDBOptions& aOptions,
467 CallerType aCallerType, ErrorResult& aRv) {
468 return OpenInternal(aCx,
469 /* aPrincipal */ nullptr, aName, Optional<uint64_t>(),
470 /* aDeleting */ true, aCallerType, aRv);
473 already_AddRefed<Promise> IDBFactory::Databases(JSContext* const aCx) {
474 RefPtr<Promise> promise = Promise::CreateInfallible(GetOwnerGlobal());
476 // Nothing can be done here if we have previously failed to create a
477 // background actor.
478 if (mBackgroundActorFailed) {
479 promise->MaybeReject(NS_ERROR_FAILURE);
480 return promise.forget();
483 PersistenceType persistenceType = GetPersistenceType(*mPrincipalInfo);
485 QM_TRY(MOZ_TO_RESULT(EnsureBackgroundActor()), [&promise](const nsresult rv) {
486 promise->MaybeReject(rv);
487 return promise.forget();
490 mBackgroundActor->SendGetDatabases(persistenceType, *mPrincipalInfo)
491 ->Then(
492 GetCurrentSerialEventTarget(), __func__,
493 [promise](const PBackgroundIDBFactoryChild::GetDatabasesPromise::
494 ResolveOrRejectValue& aValue) {
495 if (aValue.IsReject()) {
496 promise->MaybeReject(NS_ERROR_FAILURE);
497 return;
500 const GetDatabasesResponse& response = aValue.ResolveValue();
502 switch (response.type()) {
503 case GetDatabasesResponse::Tnsresult:
504 promise->MaybeReject(response.get_nsresult());
506 break;
508 case GetDatabasesResponse::TArrayOfDatabaseMetadata: {
509 const auto& array = response.get_ArrayOfDatabaseMetadata();
511 Sequence<IDBDatabaseInfo> databaseInfos;
513 for (const auto& databaseMetadata : array) {
514 IDBDatabaseInfo databaseInfo;
516 databaseInfo.mName.Construct(databaseMetadata.name());
517 databaseInfo.mVersion.Construct(databaseMetadata.version());
519 if (!databaseInfos.AppendElement(std::move(databaseInfo),
520 fallible)) {
521 promise->MaybeRejectWithTypeError("Out of memory");
522 return;
526 promise->MaybeResolve(databaseInfos);
528 break;
530 default:
531 MOZ_CRASH("Unknown response type!");
535 return promise.forget();
538 int16_t IDBFactory::Cmp(JSContext* aCx, JS::Handle<JS::Value> aFirst,
539 JS::Handle<JS::Value> aSecond, ErrorResult& aRv) {
540 Key first, second;
541 auto result = first.SetFromJSVal(aCx, aFirst);
542 if (result.isErr()) {
543 aRv = result.unwrapErr().ExtractErrorResult(
544 InvalidMapsTo<NS_ERROR_DOM_INDEXEDDB_DATA_ERR>);
545 return 0;
548 result = second.SetFromJSVal(aCx, aSecond);
549 if (result.isErr()) {
550 aRv = result.unwrapErr().ExtractErrorResult(
551 InvalidMapsTo<NS_ERROR_DOM_INDEXEDDB_DATA_ERR>);
552 return 0;
555 if (first.IsUnset() || second.IsUnset()) {
556 aRv.Throw(NS_ERROR_DOM_INDEXEDDB_DATA_ERR);
557 return 0;
560 return Key::CompareKeys(first, second);
563 RefPtr<IDBOpenDBRequest> IDBFactory::OpenForPrincipal(
564 JSContext* aCx, nsIPrincipal* aPrincipal, const nsAString& aName,
565 uint64_t aVersion, SystemCallerGuarantee aGuarantee, ErrorResult& aRv) {
566 MOZ_ASSERT(aPrincipal);
567 if (!NS_IsMainThread()) {
568 MOZ_CRASH(
569 "Figure out security checks for workers! What's this aPrincipal "
570 "we have on a worker thread?");
573 return OpenInternal(aCx, aPrincipal, aName, Optional<uint64_t>(aVersion),
574 /* aDeleting */ false, aGuarantee, aRv);
577 RefPtr<IDBOpenDBRequest> IDBFactory::OpenForPrincipal(
578 JSContext* aCx, nsIPrincipal* aPrincipal, const nsAString& aName,
579 const IDBOpenDBOptions& aOptions, SystemCallerGuarantee aGuarantee,
580 ErrorResult& aRv) {
581 MOZ_ASSERT(aPrincipal);
582 if (!NS_IsMainThread()) {
583 MOZ_CRASH(
584 "Figure out security checks for workers! What's this aPrincipal "
585 "we have on a worker thread?");
588 return OpenInternal(aCx, aPrincipal, aName, aOptions.mVersion,
589 /* aDeleting */ false, aGuarantee, aRv);
592 RefPtr<IDBOpenDBRequest> IDBFactory::DeleteForPrincipal(
593 JSContext* aCx, nsIPrincipal* aPrincipal, const nsAString& aName,
594 const IDBOpenDBOptions& aOptions, SystemCallerGuarantee aGuarantee,
595 ErrorResult& aRv) {
596 MOZ_ASSERT(aPrincipal);
597 if (!NS_IsMainThread()) {
598 MOZ_CRASH(
599 "Figure out security checks for workers! What's this aPrincipal "
600 "we have on a worker thread?");
603 return OpenInternal(aCx, aPrincipal, aName, Optional<uint64_t>(),
604 /* aDeleting */ true, aGuarantee, aRv);
607 nsresult IDBFactory::EnsureBackgroundActor() {
608 if (mBackgroundActor) {
609 return NS_OK;
612 BackgroundChildImpl::ThreadLocal* threadLocal =
613 BackgroundChildImpl::GetThreadLocalForCurrentThread();
615 UniquePtr<ThreadLocal> newIDBThreadLocal;
616 ThreadLocal* idbThreadLocal;
618 if (threadLocal && threadLocal->mIndexedDBThreadLocal) {
619 idbThreadLocal = threadLocal->mIndexedDBThreadLocal.get();
620 } else {
621 nsCOMPtr<nsIUUIDGenerator> uuidGen =
622 do_GetService("@mozilla.org/uuid-generator;1");
623 MOZ_ASSERT(uuidGen);
625 nsID id{};
626 MOZ_ALWAYS_SUCCEEDS(uuidGen->GenerateUUIDInPlace(&id));
628 newIDBThreadLocal = WrapUnique(new ThreadLocal(id));
629 idbThreadLocal = newIDBThreadLocal.get();
632 PBackgroundChild* backgroundActor =
633 BackgroundChild::GetOrCreateForCurrentThread();
634 if (NS_WARN_IF(!backgroundActor)) {
635 return NS_ERROR_FAILURE;
639 BackgroundFactoryChild* actor = new BackgroundFactoryChild(*this);
641 mBackgroundActor = static_cast<BackgroundFactoryChild*>(
642 backgroundActor->SendPBackgroundIDBFactoryConstructor(
643 actor, idbThreadLocal->GetLoggingInfo(),
644 IndexedDatabaseManager::GetLocale()));
646 if (NS_WARN_IF(!mBackgroundActor)) {
647 return NS_ERROR_FAILURE;
651 if (newIDBThreadLocal) {
652 if (!threadLocal) {
653 threadLocal = BackgroundChildImpl::GetThreadLocalForCurrentThread();
655 MOZ_ASSERT(threadLocal);
656 MOZ_ASSERT(!threadLocal->mIndexedDBThreadLocal);
658 threadLocal->mIndexedDBThreadLocal = std::move(newIDBThreadLocal);
661 return NS_OK;
664 RefPtr<IDBOpenDBRequest> IDBFactory::OpenInternal(
665 JSContext* aCx, nsIPrincipal* aPrincipal, const nsAString& aName,
666 const Optional<uint64_t>& aVersion, bool aDeleting, CallerType aCallerType,
667 ErrorResult& aRv) {
668 if (NS_WARN_IF(!GetOwnerGlobal())) {
669 aRv.Throw(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
670 return nullptr;
673 CommonFactoryRequestParams commonParams;
675 PrincipalInfo& principalInfo = commonParams.principalInfo();
677 if (aPrincipal) {
678 if (!NS_IsMainThread()) {
679 MOZ_CRASH(
680 "Figure out security checks for workers! What's this "
681 "aPrincipal we have on a worker thread?");
683 MOZ_ASSERT(aCallerType == CallerType::System);
684 MOZ_DIAGNOSTIC_ASSERT(mPrivateBrowsingMode ==
685 (aPrincipal->GetPrivateBrowsingId() > 0));
687 if (NS_WARN_IF(
688 NS_FAILED(PrincipalToPrincipalInfo(aPrincipal, &principalInfo)))) {
689 IDB_REPORT_INTERNAL_ERR();
690 aRv.Throw(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
691 return nullptr;
694 if (principalInfo.type() != PrincipalInfo::TContentPrincipalInfo &&
695 principalInfo.type() != PrincipalInfo::TSystemPrincipalInfo) {
696 IDB_REPORT_INTERNAL_ERR();
697 aRv.Throw(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
698 return nullptr;
701 if (NS_WARN_IF(!QuotaManager::IsPrincipalInfoValid(principalInfo))) {
702 IDB_REPORT_INTERNAL_ERR();
703 aRv.Throw(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
704 return nullptr;
706 } else {
707 if (GetOwnerGlobal()->GetStorageAccess() ==
708 StorageAccess::ePrivateBrowsing) {
709 if (NS_IsMainThread()) {
710 SetUseCounter(
711 GetOwnerGlobal()->GetGlobalJSObject(),
712 aDeleting
713 ? eUseCounter_custom_PrivateBrowsingIDBFactoryOpen
714 : eUseCounter_custom_PrivateBrowsingIDBFactoryDeleteDatabase);
715 } else {
716 SetUseCounter(
717 aDeleting ? UseCounterWorker::Custom_PrivateBrowsingIDBFactoryOpen
718 : UseCounterWorker::
719 Custom_PrivateBrowsingIDBFactoryDeleteDatabase);
722 principalInfo = *mPrincipalInfo;
725 uint64_t version = 0;
726 if (!aDeleting && aVersion.WasPassed()) {
727 if (aVersion.Value() < 1) {
728 aRv.ThrowTypeError("0 (Zero) is not a valid database version.");
729 return nullptr;
731 version = aVersion.Value();
734 // Nothing can be done here if we have previously failed to create a
735 // background actor.
736 if (mBackgroundActorFailed) {
737 IDB_REPORT_INTERNAL_ERR();
738 aRv.Throw(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
739 return nullptr;
742 PersistenceType persistenceType = GetPersistenceType(principalInfo);
744 DatabaseMetadata& metadata = commonParams.metadata();
745 metadata.name() = aName;
746 metadata.persistenceType() = persistenceType;
748 FactoryRequestParams params;
749 if (aDeleting) {
750 metadata.version() = 0;
751 params = DeleteDatabaseRequestParams(commonParams);
752 } else {
753 metadata.version() = version;
754 params = OpenDatabaseRequestParams(commonParams);
757 nsresult rv = EnsureBackgroundActor();
758 if (NS_WARN_IF(NS_FAILED(rv))) {
759 IDB_REPORT_INTERNAL_ERR();
760 aRv.Throw(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
761 return nullptr;
764 RefPtr<IDBOpenDBRequest> request = IDBOpenDBRequest::Create(
765 aCx, SafeRefPtr{this, AcquireStrongRefFromRawPtr{}}, GetOwnerGlobal());
766 if (!request) {
767 MOZ_ASSERT(!NS_IsMainThread());
768 aRv.ThrowUncatchableException();
769 return nullptr;
772 MOZ_ASSERT(request);
774 if (aDeleting) {
775 IDB_LOG_MARK_CHILD_REQUEST(
776 "indexedDB.deleteDatabase(\"%s\")", "IDBFactory.deleteDatabase(%.0s)",
777 request->LoggingSerialNumber(), NS_ConvertUTF16toUTF8(aName).get());
778 } else {
779 IDB_LOG_MARK_CHILD_REQUEST(
780 "indexedDB.open(\"%s\", %s)", "IDBFactory.open(%.0s%.0s)",
781 request->LoggingSerialNumber(), NS_ConvertUTF16toUTF8(aName).get(),
782 IDB_LOG_STRINGIFY(aVersion));
785 rv = InitiateRequest(WrapNotNull(request), params);
786 if (NS_WARN_IF(NS_FAILED(rv))) {
787 IDB_REPORT_INTERNAL_ERR();
788 aRv.Throw(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
789 return nullptr;
792 return request;
795 nsresult IDBFactory::InitiateRequest(
796 const NotNull<RefPtr<IDBOpenDBRequest>>& aRequest,
797 const FactoryRequestParams& aParams) {
798 MOZ_ASSERT(mBackgroundActor);
799 MOZ_ASSERT(!mBackgroundActorFailed);
801 bool deleting;
802 uint64_t requestedVersion;
804 switch (aParams.type()) {
805 case FactoryRequestParams::TDeleteDatabaseRequestParams: {
806 const DatabaseMetadata& metadata =
807 aParams.get_DeleteDatabaseRequestParams().commonParams().metadata();
808 deleting = true;
809 requestedVersion = metadata.version();
810 break;
813 case FactoryRequestParams::TOpenDatabaseRequestParams: {
814 const DatabaseMetadata& metadata =
815 aParams.get_OpenDatabaseRequestParams().commonParams().metadata();
816 deleting = false;
817 requestedVersion = metadata.version();
818 break;
821 default:
822 MOZ_CRASH("Should never get here!");
825 auto actor = new BackgroundFactoryRequestChild(
826 SafeRefPtr{this, AcquireStrongRefFromRawPtr{}}, aRequest, deleting,
827 requestedVersion);
829 if (!mBackgroundActor->SendPBackgroundIDBFactoryRequestConstructor(actor,
830 aParams)) {
831 aRequest->DispatchNonTransactionError(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
832 return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
835 return NS_OK;
838 NS_IMPL_CYCLE_COLLECTING_ADDREF(IDBFactory)
839 NS_IMPL_CYCLE_COLLECTING_RELEASE(IDBFactory)
841 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(IDBFactory)
842 NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
843 NS_INTERFACE_MAP_ENTRY(nsISupports)
844 NS_INTERFACE_MAP_END
846 NS_IMPL_CYCLE_COLLECTION_CLASS(IDBFactory)
848 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(IDBFactory)
849 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mBrowserChild)
850 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mEventTarget)
851 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
853 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(IDBFactory)
854 NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
855 NS_IMPL_CYCLE_COLLECTION_UNLINK(mBrowserChild)
856 NS_IMPL_CYCLE_COLLECTION_UNLINK(mEventTarget)
857 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
859 NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(IDBFactory)
860 NS_IMPL_CYCLE_COLLECTION_TRACE_PRESERVED_WRAPPER
861 NS_IMPL_CYCLE_COLLECTION_TRACE_END
863 JSObject* IDBFactory::WrapObject(JSContext* aCx,
864 JS::Handle<JSObject*> aGivenProto) {
865 return IDBFactory_Binding::Wrap(aCx, this, aGivenProto);
868 } // namespace mozilla::dom