1 /* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */
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 file,
5 * You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #include "DataStoreDB.h"
9 #include "DataStoreCallbacks.h"
11 #include "mozilla/dom/IDBDatabaseBinding.h"
12 #include "mozilla/dom/IDBFactoryBinding.h"
13 #include "mozilla/dom/IDBObjectStoreBinding.h"
14 #include "mozilla/dom/indexedDB/IDBDatabase.h"
15 #include "mozilla/dom/indexedDB/IDBEvents.h"
16 #include "mozilla/dom/indexedDB/IDBFactory.h"
17 #include "mozilla/dom/indexedDB/IDBIndex.h"
18 #include "mozilla/dom/indexedDB/IDBObjectStore.h"
19 #include "mozilla/dom/indexedDB/IDBRequest.h"
20 #include "mozilla/dom/indexedDB/IDBTransaction.h"
21 #include "nsComponentManagerUtils.h"
22 #include "nsContentUtils.h"
23 #include "nsIDOMEvent.h"
24 #include "nsIPrincipal.h"
25 #include "nsIXPConnect.h"
27 #define DATASTOREDB_VERSION 1
28 #define DATASTOREDB_NAME "DataStoreDB"
29 #define DATASTOREDB_REVISION_INDEX "revisionIndex"
31 using namespace mozilla::dom::indexedDB
;
36 class VersionChangeListener MOZ_FINAL
: public nsIDOMEventListener
41 explicit VersionChangeListener(IDBDatabase
* aDatabase
)
42 : mDatabase(aDatabase
)
45 // nsIDOMEventListener
46 NS_IMETHOD
HandleEvent(nsIDOMEvent
* aEvent
) MOZ_OVERRIDE
49 nsresult rv
= aEvent
->GetType(type
);
50 if (NS_WARN_IF(NS_FAILED(rv
))) {
54 if (!type
.EqualsASCII("versionchange")) {
55 MOZ_ASSERT_UNREACHABLE("Expected a versionchange event");
56 return NS_ERROR_FAILURE
;
59 rv
= mDatabase
->RemoveEventListener(NS_LITERAL_STRING("versionchange"),
61 if (NS_WARN_IF(NS_FAILED(rv
))) {
66 nsCOMPtr
<IDBVersionChangeEvent
> event
= do_QueryInterface(aEvent
);
69 Nullable
<uint64_t> version
= event
->GetNewVersion();
70 MOZ_ASSERT(version
.IsNull());
79 IDBDatabase
* mDatabase
;
81 ~VersionChangeListener() {}
84 NS_IMPL_ISUPPORTS(VersionChangeListener
, nsIDOMEventListener
)
86 NS_IMPL_ISUPPORTS(DataStoreDB
, nsIDOMEventListener
)
88 DataStoreDB::DataStoreDB(const nsAString
& aManifestURL
, const nsAString
& aName
)
90 , mCreatedSchema(false)
92 mDatabaseName
.Assign(aName
);
93 mDatabaseName
.Append('|');
94 mDatabaseName
.Append(aManifestURL
);
97 DataStoreDB::~DataStoreDB()
102 DataStoreDB::CreateFactoryIfNeeded()
106 nsCOMPtr
<nsIPrincipal
> principal
=
107 do_CreateInstance("@mozilla.org/nullprincipal;1", &rv
);
108 if (NS_WARN_IF(NS_FAILED(rv
))) {
112 nsIXPConnect
* xpc
= nsContentUtils::XPConnect();
115 AutoSafeJSContext cx
;
117 nsCOMPtr
<nsIXPConnectJSObjectHolder
> globalHolder
;
118 rv
= xpc
->CreateSandbox(cx
, principal
, getter_AddRefs(globalHolder
));
119 if (NS_WARN_IF(NS_FAILED(rv
))) {
123 JS::Rooted
<JSObject
*> global(cx
, globalHolder
->GetJSObject());
124 if (NS_WARN_IF(NS_FAILED(rv
))) {
125 return NS_ERROR_UNEXPECTED
;
128 // The CreateSandbox call returns a proxy to the actual sandbox object. We
129 // don't need a proxy here.
130 global
= js::UncheckedUnwrap(global
);
132 JSAutoCompartment
ac(cx
, global
);
134 rv
= IDBFactory::CreateForDatastore(cx
, global
, getter_AddRefs(mFactory
));
135 if (NS_WARN_IF(NS_FAILED(rv
))) {
144 DataStoreDB::Open(IDBTransactionMode aMode
, const Sequence
<nsString
>& aDbs
,
145 DataStoreDBCallback
* aCallback
)
147 MOZ_ASSERT(mState
== Inactive
);
149 nsresult rv
= CreateFactoryIfNeeded();
150 if (NS_WARN_IF(NS_FAILED(rv
))) {
155 mRequest
= mFactory
->Open(mDatabaseName
, DATASTOREDB_VERSION
, error
);
156 if (NS_WARN_IF(error
.Failed())) {
157 return error
.ErrorCode();
160 rv
= AddEventListeners();
161 if (NS_WARN_IF(NS_FAILED(rv
))) {
166 mTransactionMode
= aMode
;
167 mObjectStores
= aDbs
;
168 mCallback
= aCallback
;
173 DataStoreDB::HandleEvent(nsIDOMEvent
* aEvent
)
176 nsresult rv
= aEvent
->GetType(type
);
177 if (NS_WARN_IF(NS_FAILED(rv
))) {
181 if (type
.EqualsASCII("success")) {
182 RemoveEventListeners();
185 rv
= DatabaseOpened();
186 if (NS_WARN_IF(NS_FAILED(rv
))) {
187 mCallback
->Run(this, DataStoreDBCallback::Error
);
189 mCallback
->Run(this, mCreatedSchema
190 ? DataStoreDBCallback::CreatedSchema
:
191 DataStoreDBCallback::Success
);
198 if (type
.EqualsASCII("upgradeneeded")) {
199 return UpgradeSchema(aEvent
);
202 if (type
.EqualsASCII("error") || type
.EqualsASCII("blocked")) {
203 RemoveEventListeners();
205 mCallback
->Run(this, DataStoreDBCallback::Error
);
210 MOZ_CRASH("This should not happen");
214 DataStoreDB::UpgradeSchema(nsIDOMEvent
* aEvent
)
216 MOZ_ASSERT(NS_IsMainThread());
218 // This DB has been just created and we have to inform the callback about
220 mCreatedSchema
= true;
223 nsCOMPtr
<IDBVersionChangeEvent
> event
= do_QueryInterface(aEvent
);
226 Nullable
<uint64_t> version
= event
->GetNewVersion();
227 MOZ_ASSERT(!version
.IsNull());
228 MOZ_ASSERT(version
.Value() == DATASTOREDB_VERSION
);
231 AutoSafeJSContext cx
;
234 JS::Rooted
<JS::Value
> result(cx
);
235 mRequest
->GetResult(&result
, error
);
236 if (NS_WARN_IF(error
.Failed())) {
237 return error
.ErrorCode();
240 MOZ_ASSERT(result
.isObject());
242 IDBDatabase
* database
= nullptr;
243 nsresult rv
= UNWRAP_OBJECT(IDBDatabase
, &result
.toObject(), database
);
245 NS_WARNING("Didn't get the object we expected!");
250 IDBObjectStoreParameters params
;
251 params
.Init(NS_LITERAL_STRING("{ \"autoIncrement\": true }"));
252 nsRefPtr
<IDBObjectStore
> store
=
253 database
->CreateObjectStore(NS_LITERAL_STRING(DATASTOREDB_NAME
),
255 if (NS_WARN_IF(error
.Failed())) {
256 return error
.ErrorCode();
260 nsRefPtr
<IDBObjectStore
> store
;
263 IDBObjectStoreParameters params
;
264 params
.Init(NS_LITERAL_STRING("{ \"autoIncrement\": true, \"keyPath\": \"internalRevisionId\" }"));
267 database
->CreateObjectStore(NS_LITERAL_STRING(DATASTOREDB_REVISION
),
269 if (NS_WARN_IF(error
.Failed())) {
270 return error
.ErrorCode();
275 IDBIndexParameters params
;
276 params
.Init(NS_LITERAL_STRING("{ \"unique\": true }"));
277 nsRefPtr
<IDBIndex
> index
=
278 store
->CreateIndex(NS_LITERAL_STRING(DATASTOREDB_REVISION_INDEX
),
279 NS_LITERAL_STRING("revisionId"), params
, error
);
280 if (NS_WARN_IF(error
.Failed())) {
281 return error
.ErrorCode();
289 DataStoreDB::DatabaseOpened()
291 MOZ_ASSERT(NS_IsMainThread());
293 AutoSafeJSContext cx
;
296 JS::Rooted
<JS::Value
> result(cx
);
297 mRequest
->GetResult(&result
, error
);
298 if (NS_WARN_IF(error
.Failed())) {
299 return error
.ErrorCode();
302 MOZ_ASSERT(result
.isObject());
304 nsresult rv
= UNWRAP_OBJECT(IDBDatabase
, &result
.toObject(), mDatabase
);
306 NS_WARNING("Didn't get the object we expected!");
310 nsRefPtr
<VersionChangeListener
> listener
=
311 new VersionChangeListener(mDatabase
);
312 rv
= mDatabase
->EventTarget::AddEventListener(
313 NS_LITERAL_STRING("versionchange"), listener
, false);
314 if (NS_WARN_IF(NS_FAILED(rv
))) {
318 nsRefPtr
<IDBTransaction
> txn
= mDatabase
->Transaction(mObjectStores
,
321 if (NS_WARN_IF(error
.Failed())) {
322 return error
.ErrorCode();
325 mTransaction
= txn
.forget();
330 DataStoreDB::Delete()
332 MOZ_ASSERT(mState
== Inactive
);
334 nsresult rv
= CreateFactoryIfNeeded();
335 if (NS_WARN_IF(NS_FAILED(rv
))) {
339 mTransaction
= nullptr;
347 nsRefPtr
<IDBOpenDBRequest
> request
=
348 mFactory
->DeleteDatabase(mDatabaseName
, IDBOpenDBOptions(), error
);
349 if (NS_WARN_IF(error
.Failed())) {
350 return error
.ErrorCode();
356 indexedDB::IDBTransaction
*
357 DataStoreDB::Transaction() const
359 MOZ_ASSERT(mTransaction
);
360 MOZ_ASSERT(mTransaction
->IsOpen());
365 DataStoreDB::AddEventListeners()
368 rv
= mRequest
->EventTarget::AddEventListener(NS_LITERAL_STRING("success"),
370 if (NS_WARN_IF(NS_FAILED(rv
))) {
374 rv
= mRequest
->EventTarget::AddEventListener(NS_LITERAL_STRING("upgradeneeded"),
376 if (NS_WARN_IF(NS_FAILED(rv
))) {
380 rv
= mRequest
->EventTarget::AddEventListener(NS_LITERAL_STRING("error"),
382 if (NS_WARN_IF(NS_FAILED(rv
))) {
386 rv
= mRequest
->EventTarget::AddEventListener(NS_LITERAL_STRING("blocked"),
388 if (NS_WARN_IF(NS_FAILED(rv
))) {
396 DataStoreDB::RemoveEventListeners()
399 rv
= mRequest
->RemoveEventListener(NS_LITERAL_STRING("success"),
401 if (NS_WARN_IF(NS_FAILED(rv
))) {
405 rv
= mRequest
->RemoveEventListener(NS_LITERAL_STRING("upgradeneeded"),
407 if (NS_WARN_IF(NS_FAILED(rv
))) {
411 rv
= mRequest
->RemoveEventListener(NS_LITERAL_STRING("error"),
413 if (NS_WARN_IF(NS_FAILED(rv
))) {
417 rv
= mRequest
->RemoveEventListener(NS_LITERAL_STRING("blocked"),
419 if (NS_WARN_IF(NS_FAILED(rv
))) {
427 } // namespace mozilla