diff options
Diffstat (limited to 'devtools/shared/async-storage.js')
-rw-r--r-- | devtools/shared/async-storage.js | 226 |
1 files changed, 226 insertions, 0 deletions
diff --git a/devtools/shared/async-storage.js b/devtools/shared/async-storage.js new file mode 100644 index 0000000000..dd7ee0674e --- /dev/null +++ b/devtools/shared/async-storage.js @@ -0,0 +1,226 @@ +/* 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/. */ + +/** + * + * Adapted from https://github.com/mozilla-b2g/gaia/blob/f09993563fb5fec4393eb71816ce76cb00463190/shared/js/async_storage.js + * (converted to use Promises instead of callbacks). + * + * This file defines an asynchronous version of the localStorage API, backed by + * an IndexedDB database. It creates a global asyncStorage object that has + * methods like the localStorage object. + * + * To store a value use setItem: + * + * asyncStorage.setItem("key", "value"); + * + * This returns a promise in case you want confirmation that the value has been stored. + * + * asyncStorage.setItem("key", "newvalue").then(function() { + * console.log("new value stored"); + * }); + * + * To read a value, call getItem(), but note that you must wait for a promise + * resolution for the value to be retrieved. + * + * asyncStorage.getItem("key").then(function(value) { + * console.log("The value of key is:", value); + * }); + * + * Note that unlike localStorage, asyncStorage does not allow you to store and + * retrieve values by setting and querying properties directly. You cannot just + * write asyncStorage.key; you have to explicitly call setItem() or getItem(). + * + * removeItem(), clear(), length(), and key() are like the same-named methods of + * localStorage, and all return a promise. + * + * The asynchronous nature of getItem() makes it tricky to retrieve multiple + * values. But unlike localStorage, asyncStorage does not require the values you + * store to be strings. So if you need to save multiple values and want to + * retrieve them together, in a single asynchronous operation, just group the + * values into a single object. The properties of this object may not include + * DOM elements, but they may include things like Blobs and typed arrays. + * + */ + +"use strict"; + +const DBNAME = "devtools-async-storage"; +const DBVERSION = 1; +const STORENAME = "keyvaluepairs"; +var db = null; + +loader.lazyRequireGetter( + this, + "indexedDB", + "resource://devtools/shared/indexed-db.js" +); + +function withStore(type, onsuccess, onerror) { + if (db) { + const transaction = db.transaction(STORENAME, type); + const store = transaction.objectStore(STORENAME); + onsuccess(store); + } else { + const openreq = indexedDB.open(DBNAME, DBVERSION); + openreq.onerror = function withStoreOnError() { + onerror(); + }; + openreq.onupgradeneeded = function withStoreOnUpgradeNeeded() { + // First time setup: create an empty object store + openreq.result.createObjectStore(STORENAME); + }; + openreq.onsuccess = function withStoreOnSuccess() { + db = openreq.result; + const transaction = db.transaction(STORENAME, type); + const store = transaction.objectStore(STORENAME); + onsuccess(store); + }; + } +} + +function getItem(itemKey) { + return new Promise((resolve, reject) => { + let req; + withStore( + "readonly", + store => { + store.transaction.oncomplete = function onComplete() { + let value = req.result; + if (value === undefined) { + value = null; + } + resolve(value); + }; + req = store.get(itemKey); + req.onerror = function getItemOnError() { + console.error("Error in asyncStorage.getItem():", req.error.name); + reject(req.error); + }; + }, + reject + ); + }); +} + +function setItem(itemKey, value) { + return new Promise((resolve, reject) => { + withStore( + "readwrite", + store => { + store.transaction.oncomplete = resolve; + const req = store.put(value, itemKey); + req.onerror = function setItemOnError() { + console.error("Error in asyncStorage.setItem():", req.error.name); + reject(req.error); + }; + }, + reject + ); + }); +} + +function removeItem(itemKey) { + return new Promise((resolve, reject) => { + withStore( + "readwrite", + store => { + store.transaction.oncomplete = resolve; + const req = store.delete(itemKey); + req.onerror = function removeItemOnError() { + console.error("Error in asyncStorage.removeItem():", req.error.name); + reject(req.error); + }; + }, + reject + ); + }); +} + +function clear() { + return new Promise((resolve, reject) => { + withStore( + "readwrite", + store => { + store.transaction.oncomplete = resolve; + const req = store.clear(); + req.onerror = function clearOnError() { + console.error("Error in asyncStorage.clear():", req.error.name); + reject(req.error); + }; + }, + reject + ); + }); +} + +function length() { + return new Promise((resolve, reject) => { + let req; + withStore( + "readonly", + store => { + store.transaction.oncomplete = function onComplete() { + resolve(req.result); + }; + req = store.count(); + req.onerror = function lengthOnError() { + console.error("Error in asyncStorage.length():", req.error.name); + reject(req.error.name); + }; + }, + reject + ); + }); +} + +function key(n) { + return new Promise((resolve, reject) => { + if (n < 0) { + resolve(null); + return; + } + + let req; + withStore( + "readonly", + store => { + store.transaction.oncomplete = function onComplete() { + const cursor = req.result; + resolve(cursor ? cursor.key : null); + }; + let advanced = false; + req = store.openCursor(); + req.onsuccess = function keyOnSuccess() { + const cursor = req.result; + if (!cursor) { + // this means there weren"t enough keys + return; + } + if (n === 0 || advanced) { + // Either 1) we have the first key, return it if that's what they + // wanted, or 2) we"ve got the nth key. + return; + } + + // Otherwise, ask the cursor to skip ahead n records + advanced = true; + cursor.advance(n); + }; + req.onerror = function keyOnError() { + console.error("Error in asyncStorage.key():", req.error.name); + reject(req.error); + }; + }, + reject + ); + }); +} + +exports.getItem = getItem; +exports.setItem = setItem; +exports.removeItem = removeItem; +exports.clear = clear; +exports.length = length; +exports.key = key; |