tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

async-storage.js (7235B)


      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/. */
      4 
      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 */
     46 
     47 "use strict";
     48 
     49 const DBNAME = "devtools-async-storage";
     50 const DBVERSION = 1;
     51 const STORENAME = "keyvaluepairs";
     52 var db = null;
     53 
     54 loader.lazyRequireGetter(
     55  this,
     56  "indexedDB",
     57  "resource://devtools/shared/indexed-db.js"
     58 );
     59 
     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  }
     81 }
     82 
     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(
    100            new Error(
    101              `async-storage failed to get item for key: ${itemKey}, error: ${req.error}`
    102            )
    103          );
    104        };
    105      },
    106      reject
    107    );
    108  });
    109 }
    110 
    111 function setItem(itemKey, value) {
    112  return new Promise((resolve, reject) => {
    113    withStore(
    114      "readwrite",
    115      store => {
    116        store.transaction.oncomplete = resolve;
    117        const req = store.put(value, itemKey);
    118        req.onerror = function setItemOnError() {
    119          console.error("Error in asyncStorage.setItem():", req.error.name);
    120          reject(
    121            new Error(
    122              `async-storage failed to set item for key: ${itemKey}, error: ${req.error}`
    123            )
    124          );
    125        };
    126      },
    127      reject
    128    );
    129  });
    130 }
    131 
    132 function removeItem(itemKey) {
    133  return new Promise((resolve, reject) => {
    134    withStore(
    135      "readwrite",
    136      store => {
    137        store.transaction.oncomplete = resolve;
    138        const req = store.delete(itemKey);
    139        req.onerror = function removeItemOnError() {
    140          console.error("Error in asyncStorage.removeItem():", req.error.name);
    141          reject(
    142            new Error(
    143              `async-storage failed to remove item for key: ${itemKey}, error: ${req.error}`
    144            )
    145          );
    146        };
    147      },
    148      reject
    149    );
    150  });
    151 }
    152 
    153 function clear() {
    154  return new Promise((resolve, reject) => {
    155    withStore(
    156      "readwrite",
    157      store => {
    158        store.transaction.oncomplete = resolve;
    159        const req = store.clear();
    160        req.onerror = function clearOnError() {
    161          console.error("Error in asyncStorage.clear():", req.error.name);
    162          reject(
    163            new Error(`async-storage failed to clear, error: ${req.error}`)
    164          );
    165        };
    166      },
    167      reject
    168    );
    169  });
    170 }
    171 
    172 function length() {
    173  return new Promise((resolve, reject) => {
    174    let req;
    175    withStore(
    176      "readonly",
    177      store => {
    178        store.transaction.oncomplete = function onComplete() {
    179          resolve(req.result);
    180        };
    181        req = store.count();
    182        req.onerror = function lengthOnError() {
    183          console.error("Error in asyncStorage.length():", req.error.name);
    184          reject(
    185            new Error(
    186              `async-storage failed to retrieve length, error: ${req.error}`
    187            )
    188          );
    189        };
    190      },
    191      reject
    192    );
    193  });
    194 }
    195 
    196 function key(n) {
    197  return new Promise((resolve, reject) => {
    198    if (n < 0) {
    199      resolve(null);
    200      return;
    201    }
    202 
    203    let req;
    204    withStore(
    205      "readonly",
    206      store => {
    207        store.transaction.oncomplete = function onComplete() {
    208          const cursor = req.result;
    209          resolve(cursor ? cursor.key : null);
    210        };
    211        let advanced = false;
    212        req = store.openCursor();
    213        req.onsuccess = function keyOnSuccess() {
    214          const cursor = req.result;
    215          if (!cursor) {
    216            // this means there weren"t enough keys
    217            return;
    218          }
    219          if (n === 0 || advanced) {
    220            // Either 1) we have the first key, return it if that's what they
    221            // wanted, or 2) we"ve got the nth key.
    222            return;
    223          }
    224 
    225          // Otherwise, ask the cursor to skip ahead n records
    226          advanced = true;
    227          cursor.advance(n);
    228        };
    229        req.onerror = function keyOnError() {
    230          console.error("Error in asyncStorage.key():", req.error.name);
    231          reject(
    232            new Error(
    233              `async-storage failed to retrieve key with n: ${n}, error: ${req.error}`
    234            )
    235          );
    236        };
    237      },
    238      reject
    239    );
    240  });
    241 }
    242 
    243 exports.getItem = getItem;
    244 exports.setItem = setItem;
    245 exports.removeItem = removeItem;
    246 exports.clear = clear;
    247 exports.length = length;
    248 exports.key = key;