No bug - tagging b4d3227540c9ebc43d64aac6168fdca7019c22d8 with FIREFOX_BETA_126_BASE...
[gecko.git] / devtools / shared / async-storage.js
blobdd7ee0674e3450af72d1bb73262a8510b0625072
1 /* This Source Code Form is subject to the terms of the Mozilla Public
2  * License, v. 2.0. If a copy of the MPL was not distributed with this
3  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5 /**
6  *
7  * Adapted from https://github.com/mozilla-b2g/gaia/blob/f09993563fb5fec4393eb71816ce76cb00463190/shared/js/async_storage.js
8  * (converted to use Promises instead of callbacks).
9  *
10  * This file defines an asynchronous version of the localStorage API, backed by
11  * an IndexedDB database.  It creates a global asyncStorage object that has
12  * methods like the localStorage object.
13  *
14  * To store a value use setItem:
15  *
16  *   asyncStorage.setItem("key", "value");
17  *
18  * This returns a promise in case you want confirmation that the value has been stored.
19  *
20  *  asyncStorage.setItem("key", "newvalue").then(function() {
21  *    console.log("new value stored");
22  *  });
23  *
24  * To read a value, call getItem(), but note that you must wait for a promise
25  * resolution for the value to be retrieved.
26  *
27  *  asyncStorage.getItem("key").then(function(value) {
28  *    console.log("The value of key is:", value);
29  *  });
30  *
31  * Note that unlike localStorage, asyncStorage does not allow you to store and
32  * retrieve values by setting and querying properties directly. You cannot just
33  * write asyncStorage.key; you have to explicitly call setItem() or getItem().
34  *
35  * removeItem(), clear(), length(), and key() are like the same-named methods of
36  * localStorage, and all return a promise.
37  *
38  * The asynchronous nature of getItem() makes it tricky to retrieve multiple
39  * values. But unlike localStorage, asyncStorage does not require the values you
40  * store to be strings.  So if you need to save multiple values and want to
41  * retrieve them together, in a single asynchronous operation, just group the
42  * values into a single object. The properties of this object may not include
43  * DOM elements, but they may include things like Blobs and typed arrays.
44  *
45  */
47 "use strict";
49 const DBNAME = "devtools-async-storage";
50 const DBVERSION = 1;
51 const STORENAME = "keyvaluepairs";
52 var db = null;
54 loader.lazyRequireGetter(
55   this,
56   "indexedDB",
57   "resource://devtools/shared/indexed-db.js"
60 function withStore(type, onsuccess, onerror) {
61   if (db) {
62     const transaction = db.transaction(STORENAME, type);
63     const store = transaction.objectStore(STORENAME);
64     onsuccess(store);
65   } else {
66     const openreq = indexedDB.open(DBNAME, DBVERSION);
67     openreq.onerror = function withStoreOnError() {
68       onerror();
69     };
70     openreq.onupgradeneeded = function withStoreOnUpgradeNeeded() {
71       // First time setup: create an empty object store
72       openreq.result.createObjectStore(STORENAME);
73     };
74     openreq.onsuccess = function withStoreOnSuccess() {
75       db = openreq.result;
76       const transaction = db.transaction(STORENAME, type);
77       const store = transaction.objectStore(STORENAME);
78       onsuccess(store);
79     };
80   }
83 function getItem(itemKey) {
84   return new Promise((resolve, reject) => {
85     let req;
86     withStore(
87       "readonly",
88       store => {
89         store.transaction.oncomplete = function onComplete() {
90           let value = req.result;
91           if (value === undefined) {
92             value = null;
93           }
94           resolve(value);
95         };
96         req = store.get(itemKey);
97         req.onerror = function getItemOnError() {
98           console.error("Error in asyncStorage.getItem():", req.error.name);
99           reject(req.error);
100         };
101       },
102       reject
103     );
104   });
107 function setItem(itemKey, value) {
108   return new Promise((resolve, reject) => {
109     withStore(
110       "readwrite",
111       store => {
112         store.transaction.oncomplete = resolve;
113         const req = store.put(value, itemKey);
114         req.onerror = function setItemOnError() {
115           console.error("Error in asyncStorage.setItem():", req.error.name);
116           reject(req.error);
117         };
118       },
119       reject
120     );
121   });
124 function removeItem(itemKey) {
125   return new Promise((resolve, reject) => {
126     withStore(
127       "readwrite",
128       store => {
129         store.transaction.oncomplete = resolve;
130         const req = store.delete(itemKey);
131         req.onerror = function removeItemOnError() {
132           console.error("Error in asyncStorage.removeItem():", req.error.name);
133           reject(req.error);
134         };
135       },
136       reject
137     );
138   });
141 function clear() {
142   return new Promise((resolve, reject) => {
143     withStore(
144       "readwrite",
145       store => {
146         store.transaction.oncomplete = resolve;
147         const req = store.clear();
148         req.onerror = function clearOnError() {
149           console.error("Error in asyncStorage.clear():", req.error.name);
150           reject(req.error);
151         };
152       },
153       reject
154     );
155   });
158 function length() {
159   return new Promise((resolve, reject) => {
160     let req;
161     withStore(
162       "readonly",
163       store => {
164         store.transaction.oncomplete = function onComplete() {
165           resolve(req.result);
166         };
167         req = store.count();
168         req.onerror = function lengthOnError() {
169           console.error("Error in asyncStorage.length():", req.error.name);
170           reject(req.error.name);
171         };
172       },
173       reject
174     );
175   });
178 function key(n) {
179   return new Promise((resolve, reject) => {
180     if (n < 0) {
181       resolve(null);
182       return;
183     }
185     let req;
186     withStore(
187       "readonly",
188       store => {
189         store.transaction.oncomplete = function onComplete() {
190           const cursor = req.result;
191           resolve(cursor ? cursor.key : null);
192         };
193         let advanced = false;
194         req = store.openCursor();
195         req.onsuccess = function keyOnSuccess() {
196           const cursor = req.result;
197           if (!cursor) {
198             // this means there weren"t enough keys
199             return;
200           }
201           if (n === 0 || advanced) {
202             // Either 1) we have the first key, return it if that's what they
203             // wanted, or 2) we"ve got the nth key.
204             return;
205           }
207           // Otherwise, ask the cursor to skip ahead n records
208           advanced = true;
209           cursor.advance(n);
210         };
211         req.onerror = function keyOnError() {
212           console.error("Error in asyncStorage.key():", req.error.name);
213           reject(req.error);
214         };
215       },
216       reject
217     );
218   });
221 exports.getItem = getItem;
222 exports.setItem = setItem;
223 exports.removeItem = removeItem;
224 exports.clear = clear;
225 exports.length = length;
226 exports.key = key;