2012-04-27 02:10:04 +04:00
|
|
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
|
|
|
* You can obtain one at http://mozilla.org/MPL/2.0/. */
|
|
|
|
|
2013-02-03 16:05:51 +04:00
|
|
|
"use strict";
|
2012-04-27 02:10:04 +04:00
|
|
|
|
2015-09-15 21:19:45 +03:00
|
|
|
var DEBUG = 0;
|
|
|
|
var debug;
|
2012-04-27 02:10:04 +04:00
|
|
|
if (DEBUG) {
|
|
|
|
debug = function (s) { dump("-*- IndexedDBHelper: " + s + "\n"); }
|
|
|
|
} else {
|
|
|
|
debug = function (s) {}
|
|
|
|
}
|
|
|
|
|
2018-02-23 22:50:01 +03:00
|
|
|
var EXPORTED_SYMBOLS = ["IndexedDBHelper"];
|
2012-04-27 02:10:04 +04:00
|
|
|
|
2013-11-19 11:36:12 +04:00
|
|
|
Cu.importGlobalProperties(["indexedDB"]);
|
2012-04-27 02:10:04 +04:00
|
|
|
|
2018-01-30 02:20:18 +03:00
|
|
|
ChromeUtils.defineModuleGetter(this, 'Services',
|
2014-05-07 01:14:23 +04:00
|
|
|
'resource://gre/modules/Services.jsm');
|
|
|
|
|
2016-04-14 20:00:42 +03:00
|
|
|
function getErrorName(err) {
|
|
|
|
return err && err.name || "UnknownError";
|
|
|
|
}
|
|
|
|
|
2018-02-23 22:50:01 +03:00
|
|
|
function IndexedDBHelper() {
|
2015-10-24 00:40:52 +03:00
|
|
|
}
|
2012-04-27 02:10:04 +04:00
|
|
|
|
|
|
|
IndexedDBHelper.prototype = {
|
|
|
|
// Close the database
|
|
|
|
close: function close() {
|
|
|
|
if (this._db) {
|
|
|
|
this._db.close();
|
2013-08-13 04:58:38 +04:00
|
|
|
this._db = null;
|
2012-04-27 02:10:04 +04:00
|
|
|
}
|
|
|
|
},
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Open a new database.
|
2012-05-11 08:51:48 +04:00
|
|
|
* User has to provide upgradeSchema.
|
2013-01-31 06:03:46 +04:00
|
|
|
*
|
2012-04-27 02:10:04 +04:00
|
|
|
* @param successCb
|
|
|
|
* Success callback to call once database is open.
|
|
|
|
* @param failureCb
|
|
|
|
* Error callback to call when an error is encountered.
|
|
|
|
*/
|
2015-10-24 00:40:52 +03:00
|
|
|
open: function open(aCallback) {
|
|
|
|
if (aCallback && !this._waitForOpenCallbacks.has(aCallback)) {
|
|
|
|
this._waitForOpenCallbacks.add(aCallback);
|
|
|
|
if (this._waitForOpenCallbacks.size !== 1) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-04-27 02:10:04 +04:00
|
|
|
let self = this;
|
2015-10-24 00:40:52 +03:00
|
|
|
let invokeCallbacks = err => {
|
|
|
|
for (let callback of self._waitForOpenCallbacks) {
|
|
|
|
callback(err);
|
|
|
|
}
|
|
|
|
self._waitForOpenCallbacks.clear();
|
|
|
|
};
|
|
|
|
|
2013-01-31 05:17:07 +04:00
|
|
|
if (DEBUG) debug("Try to open database:" + self.dbName + " " + self.dbVersion);
|
2016-04-14 20:00:42 +03:00
|
|
|
let req;
|
|
|
|
try {
|
|
|
|
req = indexedDB.open(this.dbName, this.dbVersion);
|
|
|
|
} catch (e) {
|
|
|
|
if (DEBUG) debug("Error opening database: " + self.dbName);
|
2017-04-14 19:29:12 +03:00
|
|
|
Services.tm.dispatchToMainThread(() => invokeCallbacks(getErrorName(e)));
|
2016-04-14 20:00:42 +03:00
|
|
|
return;
|
|
|
|
}
|
2012-04-27 02:10:04 +04:00
|
|
|
req.onsuccess = function (event) {
|
2013-02-03 16:05:51 +04:00
|
|
|
if (DEBUG) debug("Opened database:" + self.dbName + " " + self.dbVersion);
|
2012-04-27 02:10:04 +04:00
|
|
|
self._db = event.target.result;
|
|
|
|
self._db.onversionchange = function(event) {
|
2013-01-31 05:17:07 +04:00
|
|
|
if (DEBUG) debug("WARNING: DB modified from a different window.");
|
2012-04-27 02:10:04 +04:00
|
|
|
}
|
2015-10-24 00:40:52 +03:00
|
|
|
invokeCallbacks();
|
2012-04-27 02:10:04 +04:00
|
|
|
};
|
|
|
|
|
|
|
|
req.onupgradeneeded = function (aEvent) {
|
2013-01-31 05:17:07 +04:00
|
|
|
if (DEBUG) {
|
|
|
|
debug("Database needs upgrade:" + self.dbName + aEvent.oldVersion + aEvent.newVersion);
|
2013-02-03 16:05:51 +04:00
|
|
|
debug("Correct new database version:" + (aEvent.newVersion == this.dbVersion));
|
2013-01-31 05:17:07 +04:00
|
|
|
}
|
2012-04-27 02:10:04 +04:00
|
|
|
|
|
|
|
let _db = aEvent.target.result;
|
2012-05-11 08:51:48 +04:00
|
|
|
self.upgradeSchema(req.transaction, _db, aEvent.oldVersion, aEvent.newVersion);
|
2012-04-27 02:10:04 +04:00
|
|
|
};
|
|
|
|
req.onerror = function (aEvent) {
|
2013-08-22 23:05:01 +04:00
|
|
|
if (DEBUG) debug("Failed to open database: " + self.dbName);
|
2016-04-14 20:00:42 +03:00
|
|
|
invokeCallbacks(getErrorName(aEvent.target.error));
|
2012-04-27 02:10:04 +04:00
|
|
|
};
|
|
|
|
req.onblocked = function (aEvent) {
|
2013-01-31 05:17:07 +04:00
|
|
|
if (DEBUG) debug("Opening database request is blocked.");
|
2012-04-27 02:10:04 +04:00
|
|
|
};
|
|
|
|
},
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Use the cached DB or open a new one.
|
2013-01-31 06:03:46 +04:00
|
|
|
*
|
2012-04-27 02:10:04 +04:00
|
|
|
* @param successCb
|
|
|
|
* Success callback to call.
|
|
|
|
* @param failureCb
|
|
|
|
* Error callback to call when an error is encountered.
|
|
|
|
*/
|
|
|
|
ensureDB: function ensureDB(aSuccessCb, aFailureCb) {
|
|
|
|
if (this._db) {
|
2013-01-31 05:17:07 +04:00
|
|
|
if (DEBUG) debug("ensureDB: already have a database, returning early.");
|
2014-05-07 01:14:23 +04:00
|
|
|
if (aSuccessCb) {
|
2017-04-14 19:29:12 +03:00
|
|
|
Services.tm.dispatchToMainThread(aSuccessCb);
|
2014-05-07 01:14:23 +04:00
|
|
|
}
|
2012-04-27 02:10:04 +04:00
|
|
|
return;
|
|
|
|
}
|
2015-10-24 00:40:52 +03:00
|
|
|
this.open(aError => {
|
|
|
|
if (aError) {
|
|
|
|
aFailureCb && aFailureCb(aError);
|
|
|
|
} else {
|
|
|
|
aSuccessCb && aSuccessCb();
|
|
|
|
}
|
|
|
|
});
|
2012-04-27 02:10:04 +04:00
|
|
|
},
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Start a new transaction.
|
2013-01-31 06:03:46 +04:00
|
|
|
*
|
2012-04-27 02:10:04 +04:00
|
|
|
* @param txn_type
|
|
|
|
* Type of transaction (e.g. "readwrite")
|
2013-01-31 06:03:46 +04:00
|
|
|
* @param store_name
|
|
|
|
* The object store you want to be passed to the callback
|
2012-04-27 02:10:04 +04:00
|
|
|
* @param callback
|
|
|
|
* Function to call when the transaction is available. It will
|
2013-01-31 06:03:46 +04:00
|
|
|
* be invoked with the transaction and the `store' object store.
|
2012-04-27 02:10:04 +04:00
|
|
|
* @param successCb
|
|
|
|
* Success callback to call on a successful transaction commit.
|
2018-08-20 12:46:24 +03:00
|
|
|
* The result is stored in txn.result (in the callback function).
|
2012-04-27 02:10:04 +04:00
|
|
|
* @param failureCb
|
|
|
|
* Error callback to call when an error is encountered.
|
|
|
|
*/
|
2013-01-31 06:03:46 +04:00
|
|
|
newTxn: function newTxn(txn_type, store_name, callback, successCb, failureCb) {
|
2017-04-27 01:25:45 +03:00
|
|
|
this.ensureDB(() => {
|
2013-01-31 05:17:07 +04:00
|
|
|
if (DEBUG) debug("Starting new transaction" + txn_type);
|
2016-04-14 20:00:42 +03:00
|
|
|
let txn;
|
|
|
|
try {
|
|
|
|
txn = this._db.transaction(Array.isArray(store_name) ? store_name : this.dbStoreNames, txn_type);
|
|
|
|
} catch (e) {
|
|
|
|
if (DEBUG) debug("Error starting transaction: " + this.dbName);
|
|
|
|
failureCb(getErrorName(e));
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (DEBUG) debug("Retrieving object store: " + this.dbName);
|
2013-10-02 21:27:11 +04:00
|
|
|
let stores;
|
|
|
|
if (Array.isArray(store_name)) {
|
|
|
|
stores = [];
|
|
|
|
for (let i = 0; i < store_name.length; ++i) {
|
|
|
|
stores.push(txn.objectStore(store_name[i]));
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
stores = txn.objectStore(store_name);
|
|
|
|
}
|
2012-04-27 02:10:04 +04:00
|
|
|
|
2018-08-16 17:09:40 +03:00
|
|
|
txn.oncomplete = function () {
|
2013-01-31 05:17:07 +04:00
|
|
|
if (DEBUG) debug("Transaction complete. Returning to callback.");
|
2018-08-20 12:46:24 +03:00
|
|
|
/*
|
|
|
|
* txn.result property is not part of the transaction object returned
|
|
|
|
* by this._db.transaction method called above.
|
|
|
|
* The property is expected to be set in the callback function.
|
|
|
|
* However, it can happen that the property is not set for some reason,
|
|
|
|
* so we have to check if the property exists before calling the
|
|
|
|
* success callback.
|
|
|
|
*/
|
2013-02-03 16:05:51 +04:00
|
|
|
if (successCb) {
|
2018-08-16 17:09:40 +03:00
|
|
|
if ("result" in txn) {
|
|
|
|
successCb(txn.result);
|
|
|
|
} else {
|
|
|
|
successCb();
|
|
|
|
}
|
2013-02-03 16:05:51 +04:00
|
|
|
}
|
2012-04-27 02:10:04 +04:00
|
|
|
};
|
|
|
|
|
2018-08-16 17:09:40 +03:00
|
|
|
txn.onabort = function () {
|
2013-01-31 05:17:07 +04:00
|
|
|
if (DEBUG) debug("Caught error on transaction");
|
2018-08-20 12:46:24 +03:00
|
|
|
/*
|
|
|
|
* txn.error property is part of the transaction object returned by
|
|
|
|
* this._db.transaction method called above.
|
|
|
|
* The attribute is defined in IDBTranscation WebIDL interface.
|
|
|
|
* It may be null.
|
|
|
|
*/
|
2013-02-03 16:05:51 +04:00
|
|
|
if (failureCb) {
|
2018-08-16 17:09:40 +03:00
|
|
|
failureCb(getErrorName(txn.error));
|
2013-02-03 16:05:51 +04:00
|
|
|
}
|
2012-04-27 02:10:04 +04:00
|
|
|
};
|
2013-10-02 21:27:11 +04:00
|
|
|
callback(txn, stores);
|
2017-04-27 01:25:45 +03:00
|
|
|
}, failureCb);
|
2012-04-27 02:10:04 +04:00
|
|
|
},
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Initialize the DB. Does not call open.
|
2013-01-31 06:03:46 +04:00
|
|
|
*
|
2012-04-27 02:10:04 +04:00
|
|
|
* @param aDBName
|
|
|
|
* DB name for the open call.
|
|
|
|
* @param aDBVersion
|
2012-05-11 08:51:48 +04:00
|
|
|
* Current DB version. User has to implement upgradeSchema.
|
2012-04-27 02:10:04 +04:00
|
|
|
* @param aDBStoreName
|
|
|
|
* ObjectStore that is used.
|
|
|
|
*/
|
2013-09-28 15:25:46 +04:00
|
|
|
initDBHelper: function initDBHelper(aDBName, aDBVersion, aDBStoreNames) {
|
2012-04-27 02:10:04 +04:00
|
|
|
this.dbName = aDBName;
|
|
|
|
this.dbVersion = aDBVersion;
|
2013-01-31 06:03:46 +04:00
|
|
|
this.dbStoreNames = aDBStoreNames;
|
2015-10-24 00:40:52 +03:00
|
|
|
// Cache the database.
|
|
|
|
this._db = null;
|
|
|
|
this._waitForOpenCallbacks = new Set();
|
2012-04-27 02:10:04 +04:00
|
|
|
}
|
|
|
|
}
|