Source: scripts/storage.js

/*
 * Copyright (c) 2016-2017, Michael A. Updike All rights reserved.
 * Licensed under Apache 2.0
 * https://opensource.org/licenses/Apache-2.0
 * https://github.com/opus1269/chrome-extension-utils/blob/master/LICENSE.md
 */
window.Chrome = window.Chrome || {};

/**
 * Manage items in localStorage
 * @namespace
 */
Chrome.Storage = (function() {
  'use strict';

  new ExceptionHandler();

  const chromep = new ChromePromise();

  return {
    /**
     * Get a JSON parsed value from localStorage
     * @param {!string} key - key to get value for
     * @param {?Object} [def=null] - return value if key not found
     * @returns {?Object} JSON object, null if key does not exist
     * @memberOf Chrome.Storage
     */
    get: function(key, def = null) {
      let value = def;
      let item = localStorage.getItem(key);
      if (item !== null) {
        value = Chrome.JSONUtils.parse(item);
      }
      return value;
    },

    /**
     * Get integer value from localStorage
     * @param {!string} key - key to get value for
     * @param {?int} [def=null] - optional value to return, if NaN
     * @returns {int} value as integer, NaN on error
     * @memberOf Chrome.Storage
     */
    getInt: function(key, def = null) {
      let item = localStorage.getItem(key);
      let value = parseInt(item, 10);
      if (Number.isNaN(value)) {
        value = (def === null) ? value : def;
        if (def === null) {
          Chrome.GA.error(`NaN value for: ${key} equals ${item}`,
              'Storage.getInt');
        }
      }
      return value;
    },

    /**
     * Get boolean value from localStorage
     * @param {!string} key - key to get value for
     * @param {?boolean} [def=null] - return value if key not found
     * @returns {?boolean} value as boolean, null if key does not exist
     * @memberOf Chrome.Storage
     */
    getBool: function(key, def = null) {
      return Chrome.Storage.get(key, def);
    },

    /**
     * JSON stringify and save a value to localStorage
     * @param {!string} key - key to set value for
     * @param {?Object} [value=null] - new value, if null remove item
     * @memberOf Chrome.Storage
     */
    set: function(key, value = null) {
      if (value === null) {
        localStorage.removeItem(key);
      } else {
        const val = JSON.stringify(value);
        localStorage.setItem(key, val);
      }
    },

    /**
     * Save a value to localStorage only if there is enough room
     * @param {!string} key - localStorage Key
     * @param {Object} value - value to save
     * @param {string} [keyBool] - key to a boolean value
     *                 that is true if the primary key has non-empty value
     * @returns {boolean} true if value was set successfully
     * @memberOf Chrome.Storage
     */
    safeSet: function(key, value, keyBool) {
      let ret = true;
      const oldValue = Chrome.Storage.get(key);
      try {
        Chrome.Storage.set(key, value);
      } catch (e) {
        ret = false;
        if (oldValue) {
          // revert to old value
          Chrome.Storage.set(key, oldValue);
        }
        if (keyBool) {
          // revert to old value
          if (oldValue && oldValue.length) {
            Chrome.Storage.set(keyBool, true);
          } else {
            Chrome.Storage.set(keyBool, false);
          }
        }
        // notify listeners
        Chrome.Msg.send(Chrome.Msg.STORAGE_EXCEEDED).catch(() => {
        });
      }
      return ret;
    },

    /**
     * An error that can be persisted
     * Usage: const err = new LastError(message, title)
     * @param {?string} [message=''] - the message
     * @param {?string} [title='An error occurred'] - the title
     * @property {string} name - Error name
     * @property {string} stack - stack trace
     * @memberOf Chrome.Storage
     */
    LastError: function(message = '', title = 'An error occurred') {
      this.name = 'LastError';
      this.message = message;
      this.title = title;
      this.stack = (new Error).stack;
      Chrome.Storage.LastError.prototype = Object.create(Error.prototype);
      Chrome.Storage.LastError.prototype.constructor =
          Chrome.Storage.LastError;
    },

    /**
     * Get the LastError from chrome.storage.local
     * @returns {Promise<Chrome.Msg.LastError>} last error
     * @memberOf Chrome.Storage
     */
    getLastError: function() {
      return chromep.storage.local.get('lastError').then((values) => {
        if (values.lastError) {
          return Promise.resolve(values.lastError);
        }
        return new Chrome.Storage.LastError();
      });
    },

    /**
     * Save the LastError to chrome.storage.local
     * @see https://developer.chrome.com/apps/storage
     * @param {Chrome.Storage.LastError} error - the LastError
     * @returns {Promise<void>} void
     * @memberOf Chrome.Storage
     */
    setLastError: function(error) {
      // Save it using the Chrome storage API.
      return chromep.storage.local.set({'lastError': error});
    },

    /**
     * Set the LastError to an empty message in chrome.storage.local
     * @returns {Promise<void>} void
     * @memberOf Chrome.Storage
     */
    clearLastError: function() {
      return Chrome.Storage.setLastError(new Chrome.Storage.LastError());
    },
  };
})();