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/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/quota/QuotaManager.h"
19 #include "mozilla/dom/BrowserChild.h"
20 #include "mozilla/dom/WorkerPrivate.h"
21 #include "mozilla/ipc/BackgroundChild.h"
22 #include "mozilla/ipc/BackgroundUtils.h"
23 #include "mozilla/ipc/PBackground.h"
24 #include "mozilla/ipc/PBackgroundChild.h"
25 #include "mozilla/StaticPrefs_dom.h"
26 #include "mozilla/StorageAccess.h"
27 #include "mozilla/Telemetry.h"
28 #include "nsAboutProtocolUtils.h"
29 #include "nsContentUtils.h"
30 #include "nsGlobalWindow.h"
31 #include "nsIAboutModule.h"
32 #include "nsILoadContext.h"
33 #include "nsIURI.h"
34 #include "nsIUUIDGenerator.h"
35 #include "nsIWebNavigation.h"
36 #include "nsNetUtil.h"
37 #include "nsSandboxFlags.h"
38 #include "nsServiceManagerUtils.h"
39 #include "ProfilerHelpers.h"
40 #include "ReportInternalError.h"
41 #include "ThreadLocal.h"
42
43 // Include this last to avoid path problems on Windows.
44 #include "ActorsChild.h"
45
46 #ifdef DEBUG
47 # include "nsContentUtils.h" // For assertions.
48 #endif
49
50 namespace mozilla::dom {
51
52 using namespace mozilla::dom::indexedDB;
53 using namespace mozilla::dom::quota;
54 using namespace mozilla::ipc;
55
56 namespace {
57
IdentifyPrincipalType(const mozilla::ipc::PrincipalInfo & aPrincipalInfo)58 Telemetry::LABELS_IDB_CUSTOM_OPEN_WITH_OPTIONS_COUNT IdentifyPrincipalType(
59 const mozilla::ipc::PrincipalInfo& aPrincipalInfo) {
60 switch (aPrincipalInfo.type()) {
61 case PrincipalInfo::TSystemPrincipalInfo:
62 return Telemetry::LABELS_IDB_CUSTOM_OPEN_WITH_OPTIONS_COUNT::system;
63 case PrincipalInfo::TContentPrincipalInfo: {
64 const ContentPrincipalInfo& info =
65 aPrincipalInfo.get_ContentPrincipalInfo();
66
67 nsCOMPtr<nsIURI> uri;
68
69 if (NS_WARN_IF(NS_FAILED(NS_NewURI(getter_AddRefs(uri), info.spec())))) {
70 // This could be discriminated as an extra error value, but this is
71 // extremely unlikely to fail, so we just misuse ContentOther
72 return Telemetry::LABELS_IDB_CUSTOM_OPEN_WITH_OPTIONS_COUNT::
73 content_other;
74 }
75
76 // TODO Are there constants defined for the schemes somewhere?
77 if (uri->SchemeIs("file")) {
78 return Telemetry::LABELS_IDB_CUSTOM_OPEN_WITH_OPTIONS_COUNT::
79 content_file;
80 }
81 if (uri->SchemeIs("http") || uri->SchemeIs("https")) {
82 return Telemetry::LABELS_IDB_CUSTOM_OPEN_WITH_OPTIONS_COUNT::
83 content_http_https;
84 }
85 if (uri->SchemeIs("moz-extension")) {
86 return Telemetry::LABELS_IDB_CUSTOM_OPEN_WITH_OPTIONS_COUNT::
87 content_moz_ext;
88 }
89 if (uri->SchemeIs("about")) {
90 return Telemetry::LABELS_IDB_CUSTOM_OPEN_WITH_OPTIONS_COUNT::
91 content_about;
92 }
93 return Telemetry::LABELS_IDB_CUSTOM_OPEN_WITH_OPTIONS_COUNT::
94 content_other;
95 }
96 case PrincipalInfo::TExpandedPrincipalInfo:
97 return Telemetry::LABELS_IDB_CUSTOM_OPEN_WITH_OPTIONS_COUNT::expanded;
98 default:
99 return Telemetry::LABELS_IDB_CUSTOM_OPEN_WITH_OPTIONS_COUNT::other;
100 }
101 }
102
103 } // namespace
104
105 struct IDBFactory::PendingRequestInfo {
106 RefPtr<IDBOpenDBRequest> mRequest;
107 FactoryRequestParams mParams;
108
PendingRequestInfomozilla::dom::IDBFactory::PendingRequestInfo109 PendingRequestInfo(IDBOpenDBRequest* aRequest,
110 const FactoryRequestParams& aParams)
111 : mRequest(aRequest), mParams(aParams) {
112 MOZ_ASSERT(aRequest);
113 MOZ_ASSERT(aParams.type() != FactoryRequestParams::T__None);
114 }
115 };
116
IDBFactory(const IDBFactoryGuard &)117 IDBFactory::IDBFactory(const IDBFactoryGuard&)
118 : mBackgroundActor(nullptr),
119 mInnerWindowID(0),
120 mActiveTransactionCount(0),
121 mActiveDatabaseCount(0),
122 mBackgroundActorFailed(false),
123 mPrivateBrowsingMode(false) {
124 AssertIsOnOwningThread();
125 }
126
~IDBFactory()127 IDBFactory::~IDBFactory() {
128 MOZ_ASSERT_IF(mBackgroundActorFailed, !mBackgroundActor);
129
130 if (mBackgroundActor) {
131 mBackgroundActor->SendDeleteMeInternal();
132 MOZ_ASSERT(!mBackgroundActor, "SendDeleteMeInternal should have cleared!");
133 }
134 }
135
136 // static
CreateForWindow(nsPIDOMWindowInner * aWindow)137 Result<RefPtr<IDBFactory>, nsresult> IDBFactory::CreateForWindow(
138 nsPIDOMWindowInner* aWindow) {
139 MOZ_ASSERT(NS_IsMainThread());
140 MOZ_ASSERT(aWindow);
141
142 nsCOMPtr<nsIPrincipal> principal;
143 nsresult rv = AllowedForWindowInternal(aWindow, &principal);
144
145 if (rv == NS_ERROR_DOM_NOT_SUPPORTED_ERR) {
146 NS_WARNING("IndexedDB is not permitted in a third-party window.");
147 return RefPtr<IDBFactory>{};
148 }
149
150 if (NS_WARN_IF(NS_FAILED(rv))) {
151 if (rv == NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR) {
152 IDB_REPORT_INTERNAL_ERR();
153 }
154 return Err(rv);
155 }
156
157 MOZ_ASSERT(principal);
158
159 auto principalInfo = MakeUnique<PrincipalInfo>();
160 rv = PrincipalToPrincipalInfo(principal, principalInfo.get());
161 if (NS_WARN_IF(NS_FAILED(rv))) {
162 IDB_REPORT_INTERNAL_ERR();
163 return Err(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
164 }
165
166 MOZ_ASSERT(principalInfo->type() == PrincipalInfo::TContentPrincipalInfo ||
167 principalInfo->type() == PrincipalInfo::TSystemPrincipalInfo);
168
169 if (NS_WARN_IF(!QuotaManager::IsPrincipalInfoValid(*principalInfo))) {
170 IDB_REPORT_INTERNAL_ERR();
171 return Err(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
172 }
173
174 nsCOMPtr<nsIWebNavigation> webNav = do_GetInterface(aWindow);
175 nsCOMPtr<nsILoadContext> loadContext = do_QueryInterface(webNav);
176
177 auto factory = MakeRefPtr<IDBFactory>(IDBFactoryGuard{});
178 factory->mPrincipalInfo = std::move(principalInfo);
179
180 factory->mGlobal = do_QueryInterface(aWindow);
181 MOZ_ASSERT(factory->mGlobal);
182
183 factory->mBrowserChild = BrowserChild::GetFrom(aWindow);
184 factory->mEventTarget =
185 nsGlobalWindowInner::Cast(aWindow)->EventTargetFor(TaskCategory::Other);
186 factory->mInnerWindowID = aWindow->WindowID();
187 factory->mPrivateBrowsingMode =
188 loadContext && loadContext->UsePrivateBrowsing();
189
190 return factory;
191 }
192
193 // static
CreateForMainThreadJS(nsIGlobalObject * aGlobal)194 Result<RefPtr<IDBFactory>, nsresult> IDBFactory::CreateForMainThreadJS(
195 nsIGlobalObject* aGlobal) {
196 MOZ_ASSERT(NS_IsMainThread());
197 MOZ_ASSERT(aGlobal);
198
199 nsCOMPtr<nsIScriptObjectPrincipal> sop = do_QueryInterface(aGlobal);
200 if (NS_WARN_IF(!sop)) {
201 return Err(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
202 }
203
204 auto principalInfo = MakeUnique<PrincipalInfo>();
205 nsIPrincipal* principal = sop->GetEffectiveStoragePrincipal();
206 MOZ_ASSERT(principal);
207 bool isSystem;
208 if (!AllowedForPrincipal(principal, &isSystem)) {
209 return Err(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
210 }
211
212 nsresult rv = PrincipalToPrincipalInfo(principal, principalInfo.get());
213 if (NS_WARN_IF(NS_FAILED(rv))) {
214 return Err(rv);
215 }
216
217 if (NS_WARN_IF(!QuotaManager::IsPrincipalInfoValid(*principalInfo))) {
218 return Err(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
219 }
220
221 return CreateForMainThreadJSInternal(aGlobal, std::move(principalInfo));
222 }
223
224 // static
CreateForWorker(nsIGlobalObject * aGlobal,const PrincipalInfo & aPrincipalInfo,uint64_t aInnerWindowID)225 Result<RefPtr<IDBFactory>, nsresult> IDBFactory::CreateForWorker(
226 nsIGlobalObject* aGlobal, const PrincipalInfo& aPrincipalInfo,
227 uint64_t aInnerWindowID) {
228 MOZ_ASSERT(!NS_IsMainThread());
229 MOZ_ASSERT(aGlobal);
230 MOZ_ASSERT(aPrincipalInfo.type() != PrincipalInfo::T__None);
231
232 return CreateInternal(aGlobal, MakeUnique<PrincipalInfo>(aPrincipalInfo),
233 aInnerWindowID);
234 }
235
236 // static
CreateForMainThreadJSInternal(nsIGlobalObject * aGlobal,UniquePtr<PrincipalInfo> aPrincipalInfo)237 Result<RefPtr<IDBFactory>, nsresult> IDBFactory::CreateForMainThreadJSInternal(
238 nsIGlobalObject* aGlobal, UniquePtr<PrincipalInfo> aPrincipalInfo) {
239 MOZ_ASSERT(NS_IsMainThread());
240 MOZ_ASSERT(aGlobal);
241 MOZ_ASSERT(aPrincipalInfo);
242
243 IndexedDatabaseManager* mgr = IndexedDatabaseManager::GetOrCreate();
244 if (NS_WARN_IF(!mgr)) {
245 IDB_REPORT_INTERNAL_ERR();
246 return Err(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
247 }
248
249 return CreateInternal(aGlobal, std::move(aPrincipalInfo),
250 /* aInnerWindowID */ 0);
251 }
252
253 // static
CreateInternal(nsIGlobalObject * aGlobal,UniquePtr<PrincipalInfo> aPrincipalInfo,uint64_t aInnerWindowID)254 Result<RefPtr<IDBFactory>, nsresult> IDBFactory::CreateInternal(
255 nsIGlobalObject* aGlobal, UniquePtr<PrincipalInfo> aPrincipalInfo,
256 uint64_t aInnerWindowID) {
257 MOZ_ASSERT(aGlobal);
258 MOZ_ASSERT(aPrincipalInfo);
259 MOZ_ASSERT(aPrincipalInfo->type() != PrincipalInfo::T__None);
260
261 if (aPrincipalInfo->type() != PrincipalInfo::TContentPrincipalInfo &&
262 aPrincipalInfo->type() != PrincipalInfo::TSystemPrincipalInfo) {
263 NS_WARNING("IndexedDB not allowed for this principal!");
264 return RefPtr<IDBFactory>{};
265 }
266
267 auto factory = MakeRefPtr<IDBFactory>(IDBFactoryGuard{});
268 factory->mPrincipalInfo = std::move(aPrincipalInfo);
269 factory->mGlobal = aGlobal;
270 factory->mEventTarget = GetCurrentSerialEventTarget();
271 factory->mInnerWindowID = aInnerWindowID;
272
273 return factory;
274 }
275
276 // static
AllowedForWindow(nsPIDOMWindowInner * aWindow)277 bool IDBFactory::AllowedForWindow(nsPIDOMWindowInner* aWindow) {
278 MOZ_ASSERT(NS_IsMainThread());
279 MOZ_ASSERT(aWindow);
280
281 return !NS_WARN_IF(NS_FAILED(AllowedForWindowInternal(aWindow, nullptr)));
282 }
283
284 // static
AllowedForWindowInternal(nsPIDOMWindowInner * aWindow,nsCOMPtr<nsIPrincipal> * aPrincipal)285 nsresult IDBFactory::AllowedForWindowInternal(
286 nsPIDOMWindowInner* aWindow, nsCOMPtr<nsIPrincipal>* aPrincipal) {
287 MOZ_ASSERT(NS_IsMainThread());
288 MOZ_ASSERT(aWindow);
289
290 if (NS_WARN_IF(!IndexedDatabaseManager::GetOrCreate())) {
291 return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
292 }
293
294 StorageAccess access = StorageAllowedForWindow(aWindow);
295
296 // the factory callsite records whether the browser is in private browsing.
297 // and thus we don't have to respect that setting here. IndexedDB has no
298 // concept of session-local storage, and thus ignores it.
299 if (access == StorageAccess::eDeny) {
300 return NS_ERROR_DOM_SECURITY_ERR;
301 }
302
303 if (ShouldPartitionStorage(access) &&
304 !StoragePartitioningEnabled(
305 access, aWindow->GetExtantDoc()->CookieJarSettings())) {
306 return NS_ERROR_DOM_SECURITY_ERR;
307 }
308
309 nsCOMPtr<nsIScriptObjectPrincipal> sop = do_QueryInterface(aWindow);
310 MOZ_ASSERT(sop);
311
312 nsCOMPtr<nsIPrincipal> principal = sop->GetEffectiveStoragePrincipal();
313 if (NS_WARN_IF(!principal)) {
314 return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
315 }
316
317 if (principal->IsSystemPrincipal()) {
318 *aPrincipal = std::move(principal);
319 return NS_OK;
320 }
321
322 // About URIs shouldn't be able to access IndexedDB unless they have the
323 // nsIAboutModule::ENABLE_INDEXED_DB flag set on them.
324
325 if (principal->SchemeIs("about")) {
326 uint32_t flags;
327 if (NS_SUCCEEDED(principal->GetAboutModuleFlags(&flags))) {
328 if (!(flags & nsIAboutModule::ENABLE_INDEXED_DB)) {
329 return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
330 }
331 } else {
332 return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
333 }
334 }
335
336 if (aPrincipal) {
337 *aPrincipal = std::move(principal);
338 }
339 return NS_OK;
340 }
341
342 // static
AllowedForPrincipal(nsIPrincipal * aPrincipal,bool * aIsSystemPrincipal)343 bool IDBFactory::AllowedForPrincipal(nsIPrincipal* aPrincipal,
344 bool* aIsSystemPrincipal) {
345 MOZ_ASSERT(NS_IsMainThread());
346 MOZ_ASSERT(aPrincipal);
347
348 if (NS_WARN_IF(!IndexedDatabaseManager::GetOrCreate())) {
349 return false;
350 }
351
352 if (aPrincipal->IsSystemPrincipal()) {
353 if (aIsSystemPrincipal) {
354 *aIsSystemPrincipal = true;
355 }
356 return true;
357 }
358
359 if (aIsSystemPrincipal) {
360 *aIsSystemPrincipal = false;
361 }
362
363 return !aPrincipal->GetIsNullPrincipal();
364 }
365
UpdateActiveTransactionCount(int32_t aDelta)366 void IDBFactory::UpdateActiveTransactionCount(int32_t aDelta) {
367 AssertIsOnOwningThread();
368 MOZ_DIAGNOSTIC_ASSERT(aDelta > 0 || (mActiveTransactionCount + aDelta) <
369 mActiveTransactionCount);
370 mActiveTransactionCount += aDelta;
371 }
372
UpdateActiveDatabaseCount(int32_t aDelta)373 void IDBFactory::UpdateActiveDatabaseCount(int32_t aDelta) {
374 AssertIsOnOwningThread();
375 MOZ_DIAGNOSTIC_ASSERT(aDelta > 0 ||
376 (mActiveDatabaseCount + aDelta) < mActiveDatabaseCount);
377 mActiveDatabaseCount += aDelta;
378
379 nsCOMPtr<nsPIDOMWindowInner> window = do_QueryInterface(mGlobal);
380 if (window) {
381 window->UpdateActiveIndexedDBDatabaseCount(aDelta);
382 }
383 }
384
IsChrome() const385 bool IDBFactory::IsChrome() const {
386 AssertIsOnOwningThread();
387 MOZ_ASSERT(mPrincipalInfo);
388
389 return mPrincipalInfo->type() == PrincipalInfo::TSystemPrincipalInfo;
390 }
391
Open(JSContext * aCx,const nsAString & aName,uint64_t aVersion,CallerType aCallerType,ErrorResult & aRv)392 RefPtr<IDBOpenDBRequest> IDBFactory::Open(JSContext* aCx,
393 const nsAString& aName,
394 uint64_t aVersion,
395 CallerType aCallerType,
396 ErrorResult& aRv) {
397 return OpenInternal(aCx,
398 /* aPrincipal */ nullptr, aName,
399 Optional<uint64_t>(aVersion), Optional<StorageType>(),
400 /* aDeleting */ false, aCallerType, aRv);
401 }
402
Open(JSContext * aCx,const nsAString & aName,const IDBOpenDBOptions & aOptions,CallerType aCallerType,ErrorResult & aRv)403 RefPtr<IDBOpenDBRequest> IDBFactory::Open(JSContext* aCx,
404 const nsAString& aName,
405 const IDBOpenDBOptions& aOptions,
406 CallerType aCallerType,
407 ErrorResult& aRv) {
408 if (!IsChrome() && aOptions.mStorage.WasPassed()) {
409 nsCOMPtr<nsPIDOMWindowInner> window = do_QueryInterface(mGlobal);
410 if (window && window->GetExtantDoc()) {
411 window->GetExtantDoc()->WarnOnceAbout(
412 DeprecatedOperations::eIDBOpenDBOptions_StorageType);
413 } else if (!NS_IsMainThread()) {
414 // The method below reports on the main thread too, so we need to make
415 // sure we're on a worker. Workers don't have a WarnOnceAbout mechanism,
416 // so this will be reported every time.
417 WorkerPrivate::ReportErrorToConsole("IDBOpenDBOptions_StorageType");
418 }
419 }
420
421 // Ignore calls with empty options for telemetry of usage count.
422 // Unfortunately, we cannot distinguish between the use of the method with
423 // only a single argument (which actually is a standard overload we don't want
424 // to count) an empty dictionary passed explicitly (which is the custom
425 // overload we would like to count). However, we assume that the latter is so
426 // rare that it can be neglected.
427 if (aOptions.IsAnyMemberPresent()) {
428 Telemetry::AccumulateCategorical(IdentifyPrincipalType(*mPrincipalInfo));
429 }
430
431 return OpenInternal(aCx,
432 /* aPrincipal */ nullptr, aName, aOptions.mVersion,
433 aOptions.mStorage,
434 /* aDeleting */ false, aCallerType, aRv);
435 }
436
DeleteDatabase(JSContext * aCx,const nsAString & aName,const IDBOpenDBOptions & aOptions,CallerType aCallerType,ErrorResult & aRv)437 RefPtr<IDBOpenDBRequest> IDBFactory::DeleteDatabase(
438 JSContext* aCx, const nsAString& aName, const IDBOpenDBOptions& aOptions,
439 CallerType aCallerType, ErrorResult& aRv) {
440 return OpenInternal(aCx,
441 /* aPrincipal */ nullptr, aName, Optional<uint64_t>(),
442 aOptions.mStorage,
443 /* aDeleting */ true, aCallerType, aRv);
444 }
445
Cmp(JSContext * aCx,JS::Handle<JS::Value> aFirst,JS::Handle<JS::Value> aSecond,ErrorResult & aRv)446 int16_t IDBFactory::Cmp(JSContext* aCx, JS::Handle<JS::Value> aFirst,
447 JS::Handle<JS::Value> aSecond, ErrorResult& aRv) {
448 Key first, second;
449 auto result = first.SetFromJSVal(aCx, aFirst);
450 if (result.isErr()) {
451 aRv = result.unwrapErr().ExtractErrorResult(
452 InvalidMapsTo<NS_ERROR_DOM_INDEXEDDB_DATA_ERR>);
453 return 0;
454 }
455
456 result = second.SetFromJSVal(aCx, aSecond);
457 if (result.isErr()) {
458 aRv = result.unwrapErr().ExtractErrorResult(
459 InvalidMapsTo<NS_ERROR_DOM_INDEXEDDB_DATA_ERR>);
460 return 0;
461 }
462
463 if (first.IsUnset() || second.IsUnset()) {
464 aRv.Throw(NS_ERROR_DOM_INDEXEDDB_DATA_ERR);
465 return 0;
466 }
467
468 return Key::CompareKeys(first, second);
469 }
470
OpenForPrincipal(JSContext * aCx,nsIPrincipal * aPrincipal,const nsAString & aName,uint64_t aVersion,SystemCallerGuarantee aGuarantee,ErrorResult & aRv)471 RefPtr<IDBOpenDBRequest> IDBFactory::OpenForPrincipal(
472 JSContext* aCx, nsIPrincipal* aPrincipal, const nsAString& aName,
473 uint64_t aVersion, SystemCallerGuarantee aGuarantee, ErrorResult& aRv) {
474 MOZ_ASSERT(aPrincipal);
475 if (!NS_IsMainThread()) {
476 MOZ_CRASH(
477 "Figure out security checks for workers! What's this aPrincipal "
478 "we have on a worker thread?");
479 }
480
481 return OpenInternal(aCx, aPrincipal, aName, Optional<uint64_t>(aVersion),
482 Optional<StorageType>(),
483 /* aDeleting */ false, aGuarantee, aRv);
484 }
485
OpenForPrincipal(JSContext * aCx,nsIPrincipal * aPrincipal,const nsAString & aName,const IDBOpenDBOptions & aOptions,SystemCallerGuarantee aGuarantee,ErrorResult & aRv)486 RefPtr<IDBOpenDBRequest> IDBFactory::OpenForPrincipal(
487 JSContext* aCx, nsIPrincipal* aPrincipal, const nsAString& aName,
488 const IDBOpenDBOptions& aOptions, SystemCallerGuarantee aGuarantee,
489 ErrorResult& aRv) {
490 MOZ_ASSERT(aPrincipal);
491 if (!NS_IsMainThread()) {
492 MOZ_CRASH(
493 "Figure out security checks for workers! What's this aPrincipal "
494 "we have on a worker thread?");
495 }
496
497 return OpenInternal(aCx, aPrincipal, aName, aOptions.mVersion,
498 aOptions.mStorage,
499 /* aDeleting */ false, aGuarantee, aRv);
500 }
501
DeleteForPrincipal(JSContext * aCx,nsIPrincipal * aPrincipal,const nsAString & aName,const IDBOpenDBOptions & aOptions,SystemCallerGuarantee aGuarantee,ErrorResult & aRv)502 RefPtr<IDBOpenDBRequest> IDBFactory::DeleteForPrincipal(
503 JSContext* aCx, nsIPrincipal* aPrincipal, const nsAString& aName,
504 const IDBOpenDBOptions& aOptions, SystemCallerGuarantee aGuarantee,
505 ErrorResult& aRv) {
506 MOZ_ASSERT(aPrincipal);
507 if (!NS_IsMainThread()) {
508 MOZ_CRASH(
509 "Figure out security checks for workers! What's this aPrincipal "
510 "we have on a worker thread?");
511 }
512
513 return OpenInternal(aCx, aPrincipal, aName, Optional<uint64_t>(),
514 aOptions.mStorage,
515 /* aDeleting */ true, aGuarantee, aRv);
516 }
517
OpenInternal(JSContext * aCx,nsIPrincipal * aPrincipal,const nsAString & aName,const Optional<uint64_t> & aVersion,const Optional<StorageType> & aStorageType,bool aDeleting,CallerType aCallerType,ErrorResult & aRv)518 RefPtr<IDBOpenDBRequest> IDBFactory::OpenInternal(
519 JSContext* aCx, nsIPrincipal* aPrincipal, const nsAString& aName,
520 const Optional<uint64_t>& aVersion,
521 const Optional<StorageType>& aStorageType, bool aDeleting,
522 CallerType aCallerType, ErrorResult& aRv) {
523 if (NS_WARN_IF(!mGlobal)) {
524 aRv.Throw(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
525 return nullptr;
526 }
527
528 CommonFactoryRequestParams commonParams;
529
530 PrincipalInfo& principalInfo = commonParams.principalInfo();
531
532 if (aPrincipal) {
533 if (!NS_IsMainThread()) {
534 MOZ_CRASH(
535 "Figure out security checks for workers! What's this "
536 "aPrincipal we have on a worker thread?");
537 }
538 MOZ_ASSERT(aCallerType == CallerType::System);
539 MOZ_DIAGNOSTIC_ASSERT(mPrivateBrowsingMode ==
540 (aPrincipal->GetPrivateBrowsingId() > 0));
541
542 if (NS_WARN_IF(
543 NS_FAILED(PrincipalToPrincipalInfo(aPrincipal, &principalInfo)))) {
544 IDB_REPORT_INTERNAL_ERR();
545 aRv.Throw(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
546 return nullptr;
547 }
548
549 if (principalInfo.type() != PrincipalInfo::TContentPrincipalInfo &&
550 principalInfo.type() != PrincipalInfo::TSystemPrincipalInfo) {
551 IDB_REPORT_INTERNAL_ERR();
552 aRv.Throw(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
553 return nullptr;
554 }
555
556 if (NS_WARN_IF(!QuotaManager::IsPrincipalInfoValid(principalInfo))) {
557 IDB_REPORT_INTERNAL_ERR();
558 aRv.Throw(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
559 return nullptr;
560 }
561 } else {
562 principalInfo = *mPrincipalInfo;
563 }
564
565 uint64_t version = 0;
566 if (!aDeleting && aVersion.WasPassed()) {
567 if (aVersion.Value() < 1) {
568 aRv.ThrowTypeError("0 (Zero) is not a valid database version.");
569 return nullptr;
570 }
571 version = aVersion.Value();
572 }
573
574 // Nothing can be done here if we have previously failed to create a
575 // background actor.
576 if (mBackgroundActorFailed) {
577 IDB_REPORT_INTERNAL_ERR();
578 aRv.Throw(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
579 return nullptr;
580 }
581
582 PersistenceType persistenceType;
583
584 bool isInternal = principalInfo.type() == PrincipalInfo::TSystemPrincipalInfo;
585 if (!isInternal &&
586 principalInfo.type() == PrincipalInfo::TContentPrincipalInfo) {
587 nsCString origin =
588 principalInfo.get_ContentPrincipalInfo().originNoSuffix();
589 isInternal = QuotaManager::IsOriginInternal(origin);
590 }
591
592 // Allow storage attributes for add-ons independent of the pref.
593 // This works in the main thread only, workers don't have the principal.
594 bool isAddon = false;
595 if (NS_IsMainThread()) {
596 // aPrincipal is passed inconsistently, so even when we are already on
597 // the main thread, we may have been passed a null aPrincipal.
598 auto principalOrErr = PrincipalInfoToPrincipal(principalInfo);
599 if (principalOrErr.isOk()) {
600 nsAutoString addonId;
601 Unused << NS_WARN_IF(
602 NS_FAILED(principalOrErr.unwrap()->GetAddonId(addonId)));
603 isAddon = !addonId.IsEmpty();
604 }
605 }
606
607 if (isInternal) {
608 // Chrome privilege and internal origins always get persistent storage.
609 persistenceType = PERSISTENCE_TYPE_PERSISTENT;
610 } else if ((isAddon || StaticPrefs::dom_indexedDB_storageOption_enabled()) &&
611 aStorageType.WasPassed()) {
612 persistenceType = PersistenceTypeFromStorageType(aStorageType.Value());
613 } else {
614 persistenceType = PERSISTENCE_TYPE_DEFAULT;
615 }
616
617 DatabaseMetadata& metadata = commonParams.metadata();
618 metadata.name() = aName;
619 metadata.persistenceType() = persistenceType;
620
621 FactoryRequestParams params;
622 if (aDeleting) {
623 metadata.version() = 0;
624 params = DeleteDatabaseRequestParams(commonParams);
625 } else {
626 metadata.version() = version;
627 params = OpenDatabaseRequestParams(commonParams);
628 }
629
630 if (!mBackgroundActor) {
631 BackgroundChildImpl::ThreadLocal* threadLocal =
632 BackgroundChildImpl::GetThreadLocalForCurrentThread();
633
634 UniquePtr<ThreadLocal> newIDBThreadLocal;
635 ThreadLocal* idbThreadLocal;
636
637 if (threadLocal && threadLocal->mIndexedDBThreadLocal) {
638 idbThreadLocal = threadLocal->mIndexedDBThreadLocal.get();
639 } else {
640 nsCOMPtr<nsIUUIDGenerator> uuidGen =
641 do_GetService("@mozilla.org/uuid-generator;1");
642 MOZ_ASSERT(uuidGen);
643
644 nsID id;
645 MOZ_ALWAYS_SUCCEEDS(uuidGen->GenerateUUIDInPlace(&id));
646
647 newIDBThreadLocal = WrapUnique(new ThreadLocal(id));
648 idbThreadLocal = newIDBThreadLocal.get();
649 }
650
651 PBackgroundChild* backgroundActor =
652 BackgroundChild::GetOrCreateForCurrentThread();
653 if (NS_WARN_IF(!backgroundActor)) {
654 IDB_REPORT_INTERNAL_ERR();
655 aRv.Throw(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
656 return nullptr;
657 }
658
659 {
660 BackgroundFactoryChild* actor = new BackgroundFactoryChild(*this);
661
662 // Set EventTarget for the top-level actor.
663 // All child actors created later inherit the same event target.
664 backgroundActor->SetEventTargetForActor(actor, EventTarget());
665 MOZ_ASSERT(actor->GetActorEventTarget());
666 mBackgroundActor = static_cast<BackgroundFactoryChild*>(
667 backgroundActor->SendPBackgroundIDBFactoryConstructor(
668 actor, idbThreadLocal->GetLoggingInfo()));
669
670 if (NS_WARN_IF(!mBackgroundActor)) {
671 mBackgroundActorFailed = true;
672 IDB_REPORT_INTERNAL_ERR();
673 aRv.Throw(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
674 return nullptr;
675 }
676 }
677
678 if (newIDBThreadLocal) {
679 if (!threadLocal) {
680 threadLocal = BackgroundChildImpl::GetThreadLocalForCurrentThread();
681 }
682 MOZ_ASSERT(threadLocal);
683 MOZ_ASSERT(!threadLocal->mIndexedDBThreadLocal);
684
685 threadLocal->mIndexedDBThreadLocal = std::move(newIDBThreadLocal);
686 }
687 }
688
689 RefPtr<IDBOpenDBRequest> request = IDBOpenDBRequest::Create(
690 aCx, SafeRefPtr{this, AcquireStrongRefFromRawPtr{}}, mGlobal);
691 if (!request) {
692 MOZ_ASSERT(!NS_IsMainThread());
693 aRv.ThrowUncatchableException();
694 return nullptr;
695 }
696
697 MOZ_ASSERT(request);
698
699 if (aDeleting) {
700 IDB_LOG_MARK_CHILD_REQUEST(
701 "indexedDB.deleteDatabase(\"%s\")", "IDBFactory.deleteDatabase(%.0s)",
702 request->LoggingSerialNumber(), NS_ConvertUTF16toUTF8(aName).get());
703 } else {
704 IDB_LOG_MARK_CHILD_REQUEST(
705 "indexedDB.open(\"%s\", %s)", "IDBFactory.open(%.0s%.0s)",
706 request->LoggingSerialNumber(), NS_ConvertUTF16toUTF8(aName).get(),
707 IDB_LOG_STRINGIFY(aVersion));
708 }
709
710 nsresult rv = InitiateRequest(WrapNotNull(request), params);
711 if (NS_WARN_IF(NS_FAILED(rv))) {
712 IDB_REPORT_INTERNAL_ERR();
713 aRv.Throw(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
714 return nullptr;
715 }
716
717 return request;
718 }
719
InitiateRequest(const NotNull<RefPtr<IDBOpenDBRequest>> & aRequest,const FactoryRequestParams & aParams)720 nsresult IDBFactory::InitiateRequest(
721 const NotNull<RefPtr<IDBOpenDBRequest>>& aRequest,
722 const FactoryRequestParams& aParams) {
723 MOZ_ASSERT(mBackgroundActor);
724 MOZ_ASSERT(!mBackgroundActorFailed);
725
726 bool deleting;
727 uint64_t requestedVersion;
728
729 switch (aParams.type()) {
730 case FactoryRequestParams::TDeleteDatabaseRequestParams: {
731 const DatabaseMetadata& metadata =
732 aParams.get_DeleteDatabaseRequestParams().commonParams().metadata();
733 deleting = true;
734 requestedVersion = metadata.version();
735 break;
736 }
737
738 case FactoryRequestParams::TOpenDatabaseRequestParams: {
739 const DatabaseMetadata& metadata =
740 aParams.get_OpenDatabaseRequestParams().commonParams().metadata();
741 deleting = false;
742 requestedVersion = metadata.version();
743 break;
744 }
745
746 default:
747 MOZ_CRASH("Should never get here!");
748 }
749
750 auto actor = new BackgroundFactoryRequestChild(
751 SafeRefPtr{this, AcquireStrongRefFromRawPtr{}}, aRequest, deleting,
752 requestedVersion);
753
754 if (!mBackgroundActor->SendPBackgroundIDBFactoryRequestConstructor(actor,
755 aParams)) {
756 aRequest->DispatchNonTransactionError(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
757 return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
758 }
759
760 MOZ_ASSERT(actor->GetActorEventTarget(),
761 "The event target shall be inherited from its manager actor.");
762
763 return NS_OK;
764 }
765
DisconnectFromGlobal(nsIGlobalObject * aOldGlobal)766 void IDBFactory::DisconnectFromGlobal(nsIGlobalObject* aOldGlobal) {
767 MOZ_DIAGNOSTIC_ASSERT(aOldGlobal);
768 // If CC unlinks us first, then mGlobal might be nullptr
769 MOZ_DIAGNOSTIC_ASSERT(!mGlobal || mGlobal == aOldGlobal);
770
771 mGlobal = nullptr;
772 }
773
774 NS_IMPL_CYCLE_COLLECTING_ADDREF(IDBFactory)
NS_IMPL_CYCLE_COLLECTING_RELEASE(IDBFactory)775 NS_IMPL_CYCLE_COLLECTING_RELEASE(IDBFactory)
776
777 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(IDBFactory)
778 NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
779 NS_INTERFACE_MAP_ENTRY(nsISupports)
780 NS_INTERFACE_MAP_END
781
782 NS_IMPL_CYCLE_COLLECTION_CLASS(IDBFactory)
783
784 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(IDBFactory)
785 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mGlobal)
786 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mBrowserChild)
787 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mEventTarget)
788 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
789
790 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(IDBFactory)
791 NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
792 NS_IMPL_CYCLE_COLLECTION_UNLINK(mGlobal)
793 NS_IMPL_CYCLE_COLLECTION_UNLINK(mBrowserChild)
794 NS_IMPL_CYCLE_COLLECTION_UNLINK(mEventTarget)
795 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
796
797 NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(IDBFactory)
798 NS_IMPL_CYCLE_COLLECTION_TRACE_PRESERVED_WRAPPER
799 NS_IMPL_CYCLE_COLLECTION_TRACE_END
800
801 JSObject* IDBFactory::WrapObject(JSContext* aCx,
802 JS::Handle<JSObject*> aGivenProto) {
803 return IDBFactory_Binding::Wrap(aCx, this, aGivenProto);
804 }
805
806 } // namespace mozilla::dom
807