Thu, 22 Jan 2015 13:21:57 +0100
Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=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 "nsIFile.h"
10 #include "nsIPrincipal.h"
11 #include "nsIScriptContext.h"
12 #include "nsIScriptSecurityManager.h"
13 #include "nsIXPConnect.h"
14 #include "nsIXPCScriptable.h"
16 #include <algorithm>
17 #include "mozilla/dom/ContentParent.h"
18 #include "mozilla/dom/ContentChild.h"
19 #include "mozilla/dom/IDBFactoryBinding.h"
20 #include "mozilla/dom/PBrowserChild.h"
21 #include "mozilla/dom/quota/OriginOrPatternString.h"
22 #include "mozilla/dom/quota/QuotaManager.h"
23 #include "mozilla/dom/TabChild.h"
24 #include "mozilla/Preferences.h"
25 #include "mozilla/storage.h"
26 #include "nsComponentManagerUtils.h"
27 #include "nsCharSeparatedTokenizer.h"
28 #include "nsContentUtils.h"
29 #include "nsCxPusher.h"
30 #include "nsDOMClassInfoID.h"
31 #include "nsGlobalWindow.h"
32 #include "nsHashKeys.h"
33 #include "nsPIDOMWindow.h"
34 #include "nsServiceManagerUtils.h"
35 #include "nsThreadUtils.h"
36 #include "nsXPCOMCID.h"
38 #include "AsyncConnectionHelper.h"
39 #include "CheckPermissionsHelper.h"
40 #include "DatabaseInfo.h"
41 #include "IDBDatabase.h"
42 #include "IDBEvents.h"
43 #include "IDBKeyRange.h"
44 #include "IndexedDatabaseManager.h"
45 #include "Key.h"
46 #include "ProfilerHelpers.h"
47 #include "ReportInternalError.h"
48 #include "nsNetUtil.h"
50 #include "ipc/IndexedDBChild.h"
52 #define PREF_INDEXEDDB_ENABLED "dom.indexedDB.enabled"
54 USING_INDEXEDDB_NAMESPACE
55 USING_QUOTA_NAMESPACE
57 using mozilla::dom::ContentChild;
58 using mozilla::dom::ContentParent;
59 using mozilla::dom::IDBOpenDBOptions;
60 using mozilla::dom::NonNull;
61 using mozilla::dom::Optional;
62 using mozilla::dom::TabChild;
63 using mozilla::ErrorResult;
64 using mozilla::Preferences;
66 namespace {
68 struct ObjectStoreInfoMap
69 {
70 ObjectStoreInfoMap()
71 : id(INT64_MIN), info(nullptr) { }
73 int64_t id;
74 ObjectStoreInfo* info;
75 };
77 } // anonymous namespace
79 IDBFactory::IDBFactory()
80 : mPrivilege(Content), mDefaultPersistenceType(PERSISTENCE_TYPE_TEMPORARY),
81 mOwningObject(nullptr), mActorChild(nullptr), mActorParent(nullptr),
82 mContentParent(nullptr), mRootedOwningObject(false)
83 {
84 SetIsDOMBinding();
85 }
87 IDBFactory::~IDBFactory()
88 {
89 NS_ASSERTION(!mActorParent, "Actor parent owns us, how can we be dying?!");
90 if (mActorChild) {
91 NS_ASSERTION(!IndexedDatabaseManager::IsMainProcess(), "Wrong process!");
92 mActorChild->Send__delete__(mActorChild);
93 NS_ASSERTION(!mActorChild, "Should have cleared in Send__delete__!");
94 }
95 if (mRootedOwningObject) {
96 mOwningObject = nullptr;
97 mozilla::DropJSObjects(this);
98 }
99 }
101 // static
102 nsresult
103 IDBFactory::Create(nsPIDOMWindow* aWindow,
104 const nsACString& aGroup,
105 const nsACString& aASCIIOrigin,
106 ContentParent* aContentParent,
107 IDBFactory** aFactory)
108 {
109 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
110 NS_ASSERTION(aASCIIOrigin.IsEmpty() || nsContentUtils::IsCallerChrome(),
111 "Non-chrome may not supply their own origin!");
113 IDB_ENSURE_TRUE(aWindow, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
115 if (aWindow->IsOuterWindow()) {
116 aWindow = aWindow->GetCurrentInnerWindow();
117 IDB_ENSURE_TRUE(aWindow, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
118 }
120 // Make sure that the manager is up before we do anything here since lots of
121 // decisions depend on which process we're running in.
122 indexedDB::IndexedDatabaseManager* mgr =
123 indexedDB::IndexedDatabaseManager::GetOrCreate();
124 IDB_ENSURE_TRUE(mgr, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
126 nsresult rv;
128 nsCString group(aGroup);
129 nsCString origin(aASCIIOrigin);
130 StoragePrivilege privilege;
131 PersistenceType defaultPersistenceType;
132 if (origin.IsEmpty()) {
133 NS_ASSERTION(aGroup.IsEmpty(), "Should be empty too!");
135 rv = QuotaManager::GetInfoFromWindow(aWindow, &group, &origin, &privilege,
136 &defaultPersistenceType);
137 }
138 else {
139 rv = QuotaManager::GetInfoFromWindow(aWindow, nullptr, nullptr, &privilege,
140 &defaultPersistenceType);
141 }
142 if (NS_FAILED(rv)) {
143 // Not allowed.
144 *aFactory = nullptr;
145 return NS_OK;
146 }
148 nsRefPtr<IDBFactory> factory = new IDBFactory();
149 factory->mGroup = group;
150 factory->mASCIIOrigin = origin;
151 factory->mPrivilege = privilege;
152 factory->mDefaultPersistenceType = defaultPersistenceType;
153 factory->mWindow = aWindow;
154 factory->mContentParent = aContentParent;
156 if (!IndexedDatabaseManager::IsMainProcess()) {
157 TabChild* tabChild = TabChild::GetFrom(aWindow);
158 IDB_ENSURE_TRUE(tabChild, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
160 IndexedDBChild* actor = new IndexedDBChild(origin);
162 bool allowed;
163 tabChild->SendPIndexedDBConstructor(actor, group, origin, &allowed);
165 if (!allowed) {
166 actor->Send__delete__(actor);
167 *aFactory = nullptr;
168 return NS_OK;
169 }
171 actor->SetFactory(factory);
172 }
174 factory.forget(aFactory);
175 return NS_OK;
176 }
178 // static
179 nsresult
180 IDBFactory::Create(JSContext* aCx,
181 JS::Handle<JSObject*> aOwningObject,
182 ContentParent* aContentParent,
183 IDBFactory** aFactory)
184 {
185 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
186 NS_ASSERTION(aCx, "Null context!");
187 NS_ASSERTION(aOwningObject, "Null object!");
188 NS_ASSERTION(JS_GetGlobalForObject(aCx, aOwningObject) == aOwningObject,
189 "Not a global object!");
190 NS_ASSERTION(nsContentUtils::IsCallerChrome(), "Only for chrome!");
192 // Make sure that the manager is up before we do anything here since lots of
193 // decisions depend on which process we're running in.
194 IndexedDatabaseManager* mgr = IndexedDatabaseManager::GetOrCreate();
195 IDB_ENSURE_TRUE(mgr, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
197 nsCString group;
198 nsCString origin;
199 StoragePrivilege privilege;
200 PersistenceType defaultPersistenceType;
201 QuotaManager::GetInfoForChrome(&group, &origin, &privilege,
202 &defaultPersistenceType);
204 nsRefPtr<IDBFactory> factory = new IDBFactory();
205 factory->mGroup = group;
206 factory->mASCIIOrigin = origin;
207 factory->mPrivilege = privilege;
208 factory->mDefaultPersistenceType = defaultPersistenceType;
209 factory->mOwningObject = aOwningObject;
210 factory->mContentParent = aContentParent;
212 mozilla::HoldJSObjects(factory.get());
213 factory->mRootedOwningObject = true;
215 if (!IndexedDatabaseManager::IsMainProcess()) {
216 ContentChild* contentChild = ContentChild::GetSingleton();
217 IDB_ENSURE_TRUE(contentChild, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
219 IndexedDBChild* actor = new IndexedDBChild(origin);
221 contentChild->SendPIndexedDBConstructor(actor);
223 actor->SetFactory(factory);
224 }
226 factory.forget(aFactory);
227 return NS_OK;
228 }
230 // static
231 nsresult
232 IDBFactory::Create(ContentParent* aContentParent,
233 IDBFactory** aFactory)
234 {
235 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
236 NS_ASSERTION(IndexedDatabaseManager::IsMainProcess(), "Wrong process!");
237 NS_ASSERTION(nsContentUtils::IsCallerChrome(), "Only for chrome!");
238 NS_ASSERTION(aContentParent, "Null ContentParent!");
240 NS_ASSERTION(!nsContentUtils::GetCurrentJSContext(), "Should be called from C++");
242 // We need to get this information before we push a null principal to avoid
243 // IsCallerChrome() assertion in quota manager.
244 nsCString group;
245 nsCString origin;
246 StoragePrivilege privilege;
247 PersistenceType defaultPersistenceType;
248 QuotaManager::GetInfoForChrome(&group, &origin, &privilege,
249 &defaultPersistenceType);
251 nsCOMPtr<nsIPrincipal> principal =
252 do_CreateInstance("@mozilla.org/nullprincipal;1");
253 NS_ENSURE_TRUE(principal, NS_ERROR_FAILURE);
255 AutoSafeJSContext cx;
257 nsIXPConnect* xpc = nsContentUtils::XPConnect();
258 NS_ASSERTION(xpc, "This should never be null!");
260 nsCOMPtr<nsIXPConnectJSObjectHolder> globalHolder;
261 nsresult rv = xpc->CreateSandbox(cx, principal, getter_AddRefs(globalHolder));
262 NS_ENSURE_SUCCESS(rv, rv);
264 JS::Rooted<JSObject*> global(cx, globalHolder->GetJSObject());
265 NS_ENSURE_STATE(global);
267 // The CreateSandbox call returns a proxy to the actual sandbox object. We
268 // don't need a proxy here.
269 global = js::UncheckedUnwrap(global);
271 JSAutoCompartment ac(cx, global);
273 nsRefPtr<IDBFactory> factory = new IDBFactory();
274 factory->mGroup = group;
275 factory->mASCIIOrigin = origin;
276 factory->mPrivilege = privilege;
277 factory->mDefaultPersistenceType = defaultPersistenceType;
278 factory->mOwningObject = global;
279 factory->mContentParent = aContentParent;
281 mozilla::HoldJSObjects(factory.get());
282 factory->mRootedOwningObject = true;
284 factory.forget(aFactory);
285 return NS_OK;
286 }
288 // static
289 already_AddRefed<nsIFileURL>
290 IDBFactory::GetDatabaseFileURL(nsIFile* aDatabaseFile,
291 PersistenceType aPersistenceType,
292 const nsACString& aGroup,
293 const nsACString& aOrigin)
294 {
295 nsCOMPtr<nsIURI> uri;
296 nsresult rv = NS_NewFileURI(getter_AddRefs(uri), aDatabaseFile);
297 NS_ENSURE_SUCCESS(rv, nullptr);
299 nsCOMPtr<nsIFileURL> fileUrl = do_QueryInterface(uri);
300 NS_ASSERTION(fileUrl, "This should always succeed!");
302 nsAutoCString type;
303 PersistenceTypeToText(aPersistenceType, type);
305 rv = fileUrl->SetQuery(NS_LITERAL_CSTRING("persistenceType=") + type +
306 NS_LITERAL_CSTRING("&group=") + aGroup +
307 NS_LITERAL_CSTRING("&origin=") + aOrigin);
308 NS_ENSURE_SUCCESS(rv, nullptr);
310 return fileUrl.forget();
311 }
313 // static
314 already_AddRefed<mozIStorageConnection>
315 IDBFactory::GetConnection(const nsAString& aDatabaseFilePath,
316 PersistenceType aPersistenceType,
317 const nsACString& aGroup,
318 const nsACString& aOrigin)
319 {
320 NS_ASSERTION(IndexedDatabaseManager::IsMainProcess(), "Wrong process!");
321 NS_ASSERTION(StringEndsWith(aDatabaseFilePath, NS_LITERAL_STRING(".sqlite")),
322 "Bad file path!");
324 nsCOMPtr<nsIFile> dbFile(do_CreateInstance(NS_LOCAL_FILE_CONTRACTID));
325 NS_ENSURE_TRUE(dbFile, nullptr);
327 nsresult rv = dbFile->InitWithPath(aDatabaseFilePath);
328 NS_ENSURE_SUCCESS(rv, nullptr);
330 bool exists;
331 rv = dbFile->Exists(&exists);
332 NS_ENSURE_SUCCESS(rv, nullptr);
333 NS_ENSURE_TRUE(exists, nullptr);
335 nsCOMPtr<nsIFileURL> dbFileUrl =
336 GetDatabaseFileURL(dbFile, aPersistenceType, aGroup, aOrigin);
337 NS_ENSURE_TRUE(dbFileUrl, nullptr);
339 nsCOMPtr<mozIStorageService> ss =
340 do_GetService(MOZ_STORAGE_SERVICE_CONTRACTID);
341 NS_ENSURE_TRUE(ss, nullptr);
343 nsCOMPtr<mozIStorageConnection> connection;
344 rv = ss->OpenDatabaseWithFileURL(dbFileUrl, getter_AddRefs(connection));
345 NS_ENSURE_SUCCESS(rv, nullptr);
347 rv = SetDefaultPragmas(connection);
348 NS_ENSURE_SUCCESS(rv, nullptr);
350 return connection.forget();
351 }
353 // static
354 nsresult
355 IDBFactory::SetDefaultPragmas(mozIStorageConnection* aConnection)
356 {
357 NS_ASSERTION(aConnection, "Null connection!");
359 static const char query[] =
360 #if defined(MOZ_WIDGET_ANDROID) || defined(MOZ_WIDGET_GONK)
361 // Switch the journaling mode to TRUNCATE to avoid changing the directory
362 // structure at the conclusion of every transaction for devices with slower
363 // file systems.
364 "PRAGMA journal_mode = TRUNCATE; "
365 #endif
366 // We use foreign keys in lots of places.
367 "PRAGMA foreign_keys = ON; "
368 // The "INSERT OR REPLACE" statement doesn't fire the update trigger,
369 // instead it fires only the insert trigger. This confuses the update
370 // refcount function. This behavior changes with enabled recursive triggers,
371 // so the statement fires the delete trigger first and then the insert
372 // trigger.
373 "PRAGMA recursive_triggers = ON;";
375 nsresult rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(query));
376 IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
378 return NS_OK;
379 }
381 inline
382 bool
383 IgnoreWhitespace(char16_t c)
384 {
385 return false;
386 }
388 // static
389 nsresult
390 IDBFactory::LoadDatabaseInformation(mozIStorageConnection* aConnection,
391 const nsACString& aDatabaseId,
392 uint64_t* aVersion,
393 ObjectStoreInfoArray& aObjectStores)
394 {
395 AssertIsOnIOThread();
396 NS_ASSERTION(aConnection, "Null pointer!");
398 aObjectStores.Clear();
400 // Load object store names and ids.
401 nsCOMPtr<mozIStorageStatement> stmt;
402 nsresult rv = aConnection->CreateStatement(NS_LITERAL_CSTRING(
403 "SELECT name, id, key_path, auto_increment "
404 "FROM object_store"
405 ), getter_AddRefs(stmt));
406 NS_ENSURE_SUCCESS(rv, rv);
408 nsAutoTArray<ObjectStoreInfoMap, 20> infoMap;
410 bool hasResult;
411 while (NS_SUCCEEDED((rv = stmt->ExecuteStep(&hasResult))) && hasResult) {
412 nsRefPtr<ObjectStoreInfo>* element =
413 aObjectStores.AppendElement(new ObjectStoreInfo());
415 ObjectStoreInfo* info = element->get();
417 rv = stmt->GetString(0, info->name);
418 NS_ENSURE_SUCCESS(rv, rv);
420 info->id = stmt->AsInt64(1);
422 int32_t columnType;
423 nsresult rv = stmt->GetTypeOfIndex(2, &columnType);
424 NS_ENSURE_SUCCESS(rv, rv);
426 // NB: We don't have to handle the NULL case, since that is the default
427 // for a new KeyPath.
428 if (columnType != mozIStorageStatement::VALUE_TYPE_NULL) {
429 NS_ASSERTION(columnType == mozIStorageStatement::VALUE_TYPE_TEXT,
430 "Should be a string");
431 nsString keyPathSerialization;
432 rv = stmt->GetString(2, keyPathSerialization);
433 NS_ENSURE_SUCCESS(rv, rv);
435 info->keyPath = KeyPath::DeserializeFromString(keyPathSerialization);
436 }
438 info->nextAutoIncrementId = stmt->AsInt64(3);
439 info->comittedAutoIncrementId = info->nextAutoIncrementId;
441 info->autoIncrement = !!info->nextAutoIncrementId;
443 ObjectStoreInfoMap* mapEntry = infoMap.AppendElement();
444 NS_ENSURE_TRUE(mapEntry, NS_ERROR_OUT_OF_MEMORY);
446 mapEntry->id = info->id;
447 mapEntry->info = info;
448 }
449 NS_ENSURE_SUCCESS(rv, rv);
451 // Load index information
452 rv = aConnection->CreateStatement(NS_LITERAL_CSTRING(
453 "SELECT object_store_id, id, name, key_path, unique_index, multientry "
454 "FROM object_store_index"
455 ), getter_AddRefs(stmt));
456 NS_ENSURE_SUCCESS(rv, rv);
458 while (NS_SUCCEEDED((rv = stmt->ExecuteStep(&hasResult))) && hasResult) {
459 int64_t objectStoreId = stmt->AsInt64(0);
461 ObjectStoreInfo* objectStoreInfo = nullptr;
462 for (uint32_t index = 0; index < infoMap.Length(); index++) {
463 if (infoMap[index].id == objectStoreId) {
464 objectStoreInfo = infoMap[index].info;
465 break;
466 }
467 }
469 if (!objectStoreInfo) {
470 NS_ERROR("Index for nonexistant object store!");
471 return NS_ERROR_UNEXPECTED;
472 }
474 IndexInfo* indexInfo = objectStoreInfo->indexes.AppendElement();
475 NS_ENSURE_TRUE(indexInfo, NS_ERROR_OUT_OF_MEMORY);
477 indexInfo->id = stmt->AsInt64(1);
479 rv = stmt->GetString(2, indexInfo->name);
480 NS_ENSURE_SUCCESS(rv, rv);
482 nsString keyPathSerialization;
483 rv = stmt->GetString(3, keyPathSerialization);
484 NS_ENSURE_SUCCESS(rv, rv);
486 // XXX bent wants to assert here
487 indexInfo->keyPath = KeyPath::DeserializeFromString(keyPathSerialization);
488 indexInfo->unique = !!stmt->AsInt32(4);
489 indexInfo->multiEntry = !!stmt->AsInt32(5);
490 }
491 NS_ENSURE_SUCCESS(rv, rv);
493 // Load version information.
494 rv = aConnection->CreateStatement(NS_LITERAL_CSTRING(
495 "SELECT version "
496 "FROM database"
497 ), getter_AddRefs(stmt));
498 NS_ENSURE_SUCCESS(rv, rv);
500 rv = stmt->ExecuteStep(&hasResult);
501 NS_ENSURE_SUCCESS(rv, rv);
503 if (!hasResult) {
504 NS_ERROR("Database has no version!");
505 return NS_ERROR_UNEXPECTED;
506 }
508 int64_t version = 0;
509 rv = stmt->GetInt64(0, &version);
511 *aVersion = std::max<int64_t>(version, 0);
513 return rv;
514 }
516 // static
517 nsresult
518 IDBFactory::SetDatabaseMetadata(DatabaseInfo* aDatabaseInfo,
519 uint64_t aVersion,
520 ObjectStoreInfoArray& aObjectStores)
521 {
522 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
523 NS_ASSERTION(aDatabaseInfo, "Null pointer!");
525 ObjectStoreInfoArray objectStores;
526 objectStores.SwapElements(aObjectStores);
528 #ifdef DEBUG
529 {
530 nsTArray<nsString> existingNames;
531 aDatabaseInfo->GetObjectStoreNames(existingNames);
532 NS_ASSERTION(existingNames.IsEmpty(), "Should be an empty DatabaseInfo");
533 }
534 #endif
536 aDatabaseInfo->version = aVersion;
538 for (uint32_t index = 0; index < objectStores.Length(); index++) {
539 nsRefPtr<ObjectStoreInfo>& info = objectStores[index];
541 if (!aDatabaseInfo->PutObjectStore(info)) {
542 NS_WARNING("Out of memory!");
543 return NS_ERROR_OUT_OF_MEMORY;
544 }
545 }
547 return NS_OK;
548 }
550 NS_IMPL_CYCLE_COLLECTING_ADDREF(IDBFactory)
551 NS_IMPL_CYCLE_COLLECTING_RELEASE(IDBFactory)
553 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(IDBFactory)
554 NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
555 NS_INTERFACE_MAP_ENTRY(nsISupports)
556 NS_INTERFACE_MAP_END
558 NS_IMPL_CYCLE_COLLECTION_CLASS(IDBFactory)
560 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(IDBFactory)
561 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS
562 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mWindow)
563 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
565 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(IDBFactory)
566 NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
567 if (tmp->mOwningObject) {
568 tmp->mOwningObject = nullptr;
569 }
570 if (tmp->mRootedOwningObject) {
571 mozilla::DropJSObjects(tmp);
572 tmp->mRootedOwningObject = false;
573 }
574 NS_IMPL_CYCLE_COLLECTION_UNLINK(mWindow)
575 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
577 NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(IDBFactory)
578 NS_IMPL_CYCLE_COLLECTION_TRACE_PRESERVED_WRAPPER
579 NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mOwningObject)
580 NS_IMPL_CYCLE_COLLECTION_TRACE_END
582 nsresult
583 IDBFactory::OpenInternal(const nsAString& aName,
584 int64_t aVersion,
585 PersistenceType aPersistenceType,
586 const nsACString& aGroup,
587 const nsACString& aASCIIOrigin,
588 StoragePrivilege aPrivilege,
589 bool aDeleting,
590 IDBOpenDBRequest** _retval)
591 {
592 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
593 NS_ASSERTION(mWindow || mOwningObject, "Must have one of these!");
595 AutoJSContext cx;
596 nsCOMPtr<nsPIDOMWindow> window;
597 JS::Rooted<JSObject*> scriptOwner(cx);
599 if (mWindow) {
600 window = mWindow;
601 scriptOwner =
602 static_cast<nsGlobalWindow*>(window.get())->FastGetGlobalJSObject();
603 }
604 else {
605 scriptOwner = mOwningObject;
606 }
608 if (aPrivilege == Chrome) {
609 // Chrome privilege, ignore the persistence type parameter.
610 aPersistenceType = PERSISTENCE_TYPE_PERSISTENT;
611 }
613 nsRefPtr<IDBOpenDBRequest> request =
614 IDBOpenDBRequest::Create(this, window, scriptOwner);
615 IDB_ENSURE_TRUE(request, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
617 nsresult rv;
619 if (IndexedDatabaseManager::IsMainProcess()) {
620 nsRefPtr<OpenDatabaseHelper> openHelper =
621 new OpenDatabaseHelper(request, aName, aGroup, aASCIIOrigin, aVersion,
622 aPersistenceType, aDeleting, mContentParent,
623 aPrivilege);
625 rv = openHelper->Init();
626 IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
628 if (!Preferences::GetBool(PREF_INDEXEDDB_ENABLED)) {
629 openHelper->SetError(NS_ERROR_DOM_INDEXEDDB_NOT_ALLOWED_ERR);
630 rv = openHelper->WaitForOpenAllowed();
631 }
632 else {
633 if (mPrivilege != Chrome &&
634 aPersistenceType == PERSISTENCE_TYPE_PERSISTENT) {
635 nsRefPtr<CheckPermissionsHelper> permissionHelper =
636 new CheckPermissionsHelper(openHelper, window);
638 QuotaManager* quotaManager = QuotaManager::Get();
639 NS_ASSERTION(quotaManager, "This should never be null!");
641 rv = quotaManager->
642 WaitForOpenAllowed(OriginOrPatternString::FromOrigin(aASCIIOrigin),
643 Nullable<PersistenceType>(aPersistenceType),
644 openHelper->Id(), permissionHelper);
645 }
646 else {
647 // Chrome and temporary storage doesn't need to check the permission.
648 rv = openHelper->WaitForOpenAllowed();
649 }
650 }
651 IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
652 }
653 else if (aDeleting) {
654 nsCString databaseId;
655 QuotaManager::GetStorageId(aPersistenceType, aASCIIOrigin, Client::IDB,
656 aName, databaseId);
657 MOZ_ASSERT(!databaseId.IsEmpty());
659 IndexedDBDeleteDatabaseRequestChild* actor =
660 new IndexedDBDeleteDatabaseRequestChild(this, request, databaseId);
662 mActorChild->SendPIndexedDBDeleteDatabaseRequestConstructor(
663 actor,
664 nsString(aName),
665 aPersistenceType);
666 }
667 else {
668 IndexedDBDatabaseChild* dbActor =
669 static_cast<IndexedDBDatabaseChild*>(
670 mActorChild->SendPIndexedDBDatabaseConstructor(nsString(aName),
671 aVersion,
672 aPersistenceType));
674 dbActor->SetRequest(request);
675 }
677 #ifdef IDB_PROFILER_USE_MARKS
678 {
679 NS_ConvertUTF16toUTF8 profilerName(aName);
680 if (aDeleting) {
681 IDB_PROFILER_MARK("IndexedDB Request %llu: deleteDatabase(\"%s\")",
682 "MT IDBFactory.deleteDatabase()",
683 request->GetSerialNumber(), profilerName.get());
684 }
685 else {
686 IDB_PROFILER_MARK("IndexedDB Request %llu: open(\"%s\", %lld)",
687 "MT IDBFactory.open()",
688 request->GetSerialNumber(), profilerName.get(),
689 aVersion);
690 }
691 }
692 #endif
694 request.forget(_retval);
695 return NS_OK;
696 }
698 JSObject*
699 IDBFactory::WrapObject(JSContext* aCx)
700 {
701 return IDBFactoryBinding::Wrap(aCx, this);
702 }
704 already_AddRefed<IDBOpenDBRequest>
705 IDBFactory::Open(const nsAString& aName, const IDBOpenDBOptions& aOptions,
706 ErrorResult& aRv)
707 {
708 return Open(nullptr, aName, aOptions.mVersion, aOptions.mStorage, false, aRv);
709 }
711 already_AddRefed<IDBOpenDBRequest>
712 IDBFactory::DeleteDatabase(const nsAString& aName,
713 const IDBOpenDBOptions& aOptions,
714 ErrorResult& aRv)
715 {
716 return Open(nullptr, aName, Optional<uint64_t>(), aOptions.mStorage, true,
717 aRv);
718 }
720 int16_t
721 IDBFactory::Cmp(JSContext* aCx, JS::Handle<JS::Value> aFirst,
722 JS::Handle<JS::Value> aSecond, ErrorResult& aRv)
723 {
724 Key first, second;
725 nsresult rv = first.SetFromJSVal(aCx, aFirst);
726 if (NS_FAILED(rv)) {
727 aRv.Throw(rv);
728 return 0;
729 }
731 rv = second.SetFromJSVal(aCx, aSecond);
732 if (NS_FAILED(rv)) {
733 aRv.Throw(rv);
734 return 0;
735 }
737 if (first.IsUnset() || second.IsUnset()) {
738 aRv.Throw(NS_ERROR_DOM_INDEXEDDB_DATA_ERR);
739 return 0;
740 }
742 return Key::CompareKeys(first, second);
743 }
745 already_AddRefed<IDBOpenDBRequest>
746 IDBFactory::OpenForPrincipal(nsIPrincipal* aPrincipal, const nsAString& aName,
747 uint64_t aVersion, ErrorResult& aRv)
748 {
749 // Just to be on the extra-safe side
750 if (!nsContentUtils::IsCallerChrome()) {
751 MOZ_CRASH();
752 }
754 return Open(aPrincipal, aName, Optional<uint64_t>(aVersion),
755 Optional<mozilla::dom::StorageType>(), false, aRv);
756 }
758 already_AddRefed<IDBOpenDBRequest>
759 IDBFactory::OpenForPrincipal(nsIPrincipal* aPrincipal, const nsAString& aName,
760 const IDBOpenDBOptions& aOptions, ErrorResult& aRv)
761 {
762 // Just to be on the extra-safe side
763 if (!nsContentUtils::IsCallerChrome()) {
764 MOZ_CRASH();
765 }
767 return Open(aPrincipal, aName, aOptions.mVersion, aOptions.mStorage, false,
768 aRv);
769 }
771 already_AddRefed<IDBOpenDBRequest>
772 IDBFactory::DeleteForPrincipal(nsIPrincipal* aPrincipal, const nsAString& aName,
773 const IDBOpenDBOptions& aOptions,
774 ErrorResult& aRv)
775 {
776 // Just to be on the extra-safe side
777 if (!nsContentUtils::IsCallerChrome()) {
778 MOZ_CRASH();
779 }
781 return Open(aPrincipal, aName, Optional<uint64_t>(), aOptions.mStorage, true,
782 aRv);
783 }
785 already_AddRefed<IDBOpenDBRequest>
786 IDBFactory::Open(nsIPrincipal* aPrincipal, const nsAString& aName,
787 const Optional<uint64_t>& aVersion,
788 const Optional<mozilla::dom::StorageType>& aStorageType,
789 bool aDelete, ErrorResult& aRv)
790 {
791 nsresult rv;
793 nsCString group;
794 nsCString origin;
795 StoragePrivilege privilege;
796 PersistenceType defaultPersistenceType;
797 if (aPrincipal) {
798 rv = QuotaManager::GetInfoFromPrincipal(aPrincipal, &group, &origin,
799 &privilege,
800 &defaultPersistenceType);
801 if (NS_FAILED(rv)) {
802 IDB_REPORT_INTERNAL_ERR();
803 aRv.Throw(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
804 return nullptr;
805 }
806 }
807 else {
808 group = mGroup;
809 origin = mASCIIOrigin;
810 privilege = mPrivilege;
811 defaultPersistenceType = mDefaultPersistenceType;
812 }
814 uint64_t version = 0;
815 if (!aDelete && aVersion.WasPassed()) {
816 if (aVersion.Value() < 1) {
817 aRv.ThrowTypeError(MSG_INVALID_VERSION);
818 return nullptr;
819 }
820 version = aVersion.Value();
821 }
823 PersistenceType persistenceType =
824 PersistenceTypeFromStorage(aStorageType, defaultPersistenceType);
826 nsRefPtr<IDBOpenDBRequest> request;
827 rv = OpenInternal(aName, version, persistenceType, group, origin, privilege,
828 aDelete, getter_AddRefs(request));
829 if (NS_FAILED(rv)) {
830 aRv.Throw(rv);
831 return nullptr;
832 }
834 return request.forget();
835 }