michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this file, michael@0: * You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: "use strict"; michael@0: michael@0: let DEBUG = 0; michael@0: let debug; michael@0: if (DEBUG) { michael@0: debug = function (s) { dump("-*- IndexedDBHelper: " + s + "\n"); } michael@0: } else { michael@0: debug = function (s) {} michael@0: } michael@0: michael@0: const Cu = Components.utils; michael@0: const Cc = Components.classes; michael@0: const Ci = Components.interfaces; michael@0: michael@0: this.EXPORTED_SYMBOLS = ["IndexedDBHelper"]; michael@0: michael@0: Cu.import("resource://gre/modules/XPCOMUtils.jsm"); michael@0: Cu.import("resource://gre/modules/Services.jsm"); michael@0: Cu.importGlobalProperties(["indexedDB"]); michael@0: michael@0: this.IndexedDBHelper = function IndexedDBHelper() {} michael@0: michael@0: IndexedDBHelper.prototype = { michael@0: michael@0: // Cache the database michael@0: _db: null, michael@0: michael@0: // Close the database michael@0: close: function close() { michael@0: if (this._db) { michael@0: this._db.close(); michael@0: this._db = null; michael@0: } michael@0: }, michael@0: michael@0: /** michael@0: * Open a new database. michael@0: * User has to provide upgradeSchema. michael@0: * michael@0: * @param successCb michael@0: * Success callback to call once database is open. michael@0: * @param failureCb michael@0: * Error callback to call when an error is encountered. michael@0: */ michael@0: open: function open(aSuccessCb, aFailureCb) { michael@0: let self = this; michael@0: if (DEBUG) debug("Try to open database:" + self.dbName + " " + self.dbVersion); michael@0: let req = indexedDB.open(this.dbName, this.dbVersion); michael@0: req.onsuccess = function (event) { michael@0: if (DEBUG) debug("Opened database:" + self.dbName + " " + self.dbVersion); michael@0: self._db = event.target.result; michael@0: self._db.onversionchange = function(event) { michael@0: if (DEBUG) debug("WARNING: DB modified from a different window."); michael@0: } michael@0: aSuccessCb && aSuccessCb(); michael@0: }; michael@0: michael@0: req.onupgradeneeded = function (aEvent) { michael@0: if (DEBUG) { michael@0: debug("Database needs upgrade:" + self.dbName + aEvent.oldVersion + aEvent.newVersion); michael@0: debug("Correct new database version:" + (aEvent.newVersion == this.dbVersion)); michael@0: } michael@0: michael@0: let _db = aEvent.target.result; michael@0: self.upgradeSchema(req.transaction, _db, aEvent.oldVersion, aEvent.newVersion); michael@0: }; michael@0: req.onerror = function (aEvent) { michael@0: if (DEBUG) debug("Failed to open database: " + self.dbName); michael@0: aFailureCb && aFailureCb(aEvent.target.error.name); michael@0: }; michael@0: req.onblocked = function (aEvent) { michael@0: if (DEBUG) debug("Opening database request is blocked."); michael@0: }; michael@0: }, michael@0: michael@0: /** michael@0: * Use the cached DB or open a new one. michael@0: * michael@0: * @param successCb michael@0: * Success callback to call. michael@0: * @param failureCb michael@0: * Error callback to call when an error is encountered. michael@0: */ michael@0: ensureDB: function ensureDB(aSuccessCb, aFailureCb) { michael@0: if (this._db) { michael@0: if (DEBUG) debug("ensureDB: already have a database, returning early."); michael@0: aSuccessCb && aSuccessCb(); michael@0: return; michael@0: } michael@0: this.open(aSuccessCb, aFailureCb); michael@0: }, michael@0: michael@0: /** michael@0: * Start a new transaction. michael@0: * michael@0: * @param txn_type michael@0: * Type of transaction (e.g. "readwrite") michael@0: * @param store_name michael@0: * The object store you want to be passed to the callback michael@0: * @param callback michael@0: * Function to call when the transaction is available. It will michael@0: * be invoked with the transaction and the `store' object store. michael@0: * @param successCb michael@0: * Success callback to call on a successful transaction commit. michael@0: * The result is stored in txn.result. michael@0: * @param failureCb michael@0: * Error callback to call when an error is encountered. michael@0: */ michael@0: newTxn: function newTxn(txn_type, store_name, callback, successCb, failureCb) { michael@0: this.ensureDB(function () { michael@0: if (DEBUG) debug("Starting new transaction" + txn_type); michael@0: let txn = this._db.transaction(Array.isArray(store_name) ? store_name : this.dbStoreNames, txn_type); michael@0: if (DEBUG) debug("Retrieving object store", this.dbName); michael@0: let stores; michael@0: if (Array.isArray(store_name)) { michael@0: stores = []; michael@0: for (let i = 0; i < store_name.length; ++i) { michael@0: stores.push(txn.objectStore(store_name[i])); michael@0: } michael@0: } else { michael@0: stores = txn.objectStore(store_name); michael@0: } michael@0: michael@0: txn.oncomplete = function (event) { michael@0: if (DEBUG) debug("Transaction complete. Returning to callback."); michael@0: if (successCb) { michael@0: successCb(txn.result); michael@0: } michael@0: }; michael@0: michael@0: txn.onabort = function (event) { michael@0: if (DEBUG) debug("Caught error on transaction"); michael@0: /* michael@0: * event.target.error may be null michael@0: * if txn was aborted by calling txn.abort() michael@0: */ michael@0: if (failureCb) { michael@0: if (event.target.error) { michael@0: failureCb(event.target.error.name); michael@0: } else { michael@0: failureCb("UnknownError"); michael@0: } michael@0: } michael@0: }; michael@0: callback(txn, stores); michael@0: }.bind(this), failureCb); michael@0: }, michael@0: michael@0: /** michael@0: * Initialize the DB. Does not call open. michael@0: * michael@0: * @param aDBName michael@0: * DB name for the open call. michael@0: * @param aDBVersion michael@0: * Current DB version. User has to implement upgradeSchema. michael@0: * @param aDBStoreName michael@0: * ObjectStore that is used. michael@0: */ michael@0: initDBHelper: function initDBHelper(aDBName, aDBVersion, aDBStoreNames) { michael@0: this.dbName = aDBName; michael@0: this.dbVersion = aDBVersion; michael@0: this.dbStoreNames = aDBStoreNames; michael@0: } michael@0: }