diff --git a/background/db-chrome-storage.js b/background/db-chrome-storage.js new file mode 100644 index 00000000..46cfadb1 --- /dev/null +++ b/background/db-chrome-storage.js @@ -0,0 +1,84 @@ +/* global promisify */ +/* exported createChromeStorageDB */ +'use strict'; + +function createChromeStorageDB() { + const get = promisify(chrome.storage.local.get.bind(chrome.storage.local)); + const set = promisify(chrome.storage.local.set.bind(chrome.storage.local)); + const remove = promisify(chrome.storage.local.remove.bind(chrome.storage.local)); + + let INC; + + const PREFIX = 'style-'; + const METHODS = { + // FIXME: we don't use this method at all. Should we remove this? + get: id => get(PREFIX + id) + .then(result => result[PREFIX + id]), + put: obj => Promise.resolve() + .then(() => { + if (!obj.id) { + return prepareInc() + .then(() => { + // FIXME: should we clone the object? + obj.id = INC++; + }); + } + }) + .then(() => set({[PREFIX + obj.id]: obj})) + .then(() => obj.id), + putMany: items => prepareInc() + .then(() => { + for (const item of items) { + if (!item.id) { + item.id = INC++; + } + } + return set(items.reduce((obj, curr) => { + obj[PREFIX + curr.id] = curr; + return obj; + }, {})); + }) + .then(() => items.map(i => i.id)), + delete: id => remove(PREFIX + id), + getAll: () => get(null) + .then(result => { + const output = []; + for (const key in result) { + if (key.startsWith(PREFIX) && Number(key.slice(PREFIX.length))) { + output.push(result[key]); + } + } + return output; + }) + }; + + return {exec}; + + function exec(method, ...args) { + if (METHODS[method]) { + return METHODS[method](...args) + .then(result => { + if (method === 'putMany' && result.map) { + return result.map(r => ({target: {result: r}})); + } + return {target: {result}}; + }); + } + return Promise.reject(new Error(`unknown DB method ${method}`)); + } + + function prepareInc() { + if (INC) return Promise.resolve(); + return get(null).then(result => { + INC = 1; + for (const key in result) { + if (key.startsWith(PREFIX)) { + const id = Number(key.slice(PREFIX.length)); + if (id >= INC) { + INC = id + 1; + } + } + } + }); + } +} diff --git a/background/db.js b/background/db.js index 46fd0c9b..2549a3ce 100644 --- a/background/db.js +++ b/background/db.js @@ -1,4 +1,4 @@ -/* global chromeLocal ignoreChromeError workerUtil */ +/* global chromeLocal ignoreChromeError workerUtil createChromeStorageDB */ /* exported db */ /* Initialize a database. There are some problems using IndexedDB in Firefox: @@ -94,7 +94,7 @@ const db = (() => { } function useChromeStorage(err) { - exec = dbExecChromeStorage; + exec = createChromeStorageDB().exec; chromeLocal.set({dbInChromeStorage: true}, ignoreChromeError); if (err) { chromeLocal.setValue('dbInChromeStorageReason', workerUtil.cloneError(err)); @@ -153,75 +153,4 @@ const db = (() => { return Promise.all(items.map(item => storeRequest(store, 'put', item))); } } - - function dbExecChromeStorage(method, data) { - const STYLE_KEY_PREFIX = 'style-'; - switch (method) { - case 'get': - return chromeLocal.getValue(STYLE_KEY_PREFIX + data) - .then(result => ({target: {result}})); - - case 'put': - if (!data.id) { - return getMaxId().then(id => { - data.id = id + 1; - return dbExecChromeStorage('put', data); - }); - } - return chromeLocal.setValue(STYLE_KEY_PREFIX + data.id, data) - .then(() => (chrome.runtime.lastError ? Promise.reject() : data.id)); - - case 'putMany': { - const newItems = data.filter(i => !i.id); - const doPut = () => - chromeLocal.set(data.reduce((o, item) => { - o[STYLE_KEY_PREFIX + item.id] = item; - return o; - }, {})) - .then(() => data.map(d => ({target: {result: d.id}}))); - if (newItems.length) { - return getMaxId().then(id => { - for (const item of newItems) { - item.id = ++id; - } - return doPut(); - }); - } - return doPut(); - } - - case 'delete': - return chromeLocal.remove(STYLE_KEY_PREFIX + data); - - case 'getAll': - return getAllStyles() - .then(styles => ({target: {result: styles}})); - } - return Promise.reject(); - - function getAllStyles() { - return chromeLocal.get(null).then(storage => { - const styles = []; - for (const key in storage) { - if (key.startsWith(STYLE_KEY_PREFIX) && - Number(key.substr(STYLE_KEY_PREFIX.length))) { - styles.push(storage[key]); - } - } - return styles; - }); - } - - function getMaxId() { - return getAllStyles().then(styles => { - let result = 0; - for (const style of styles) { - if (style.id > result) { - result = style.id; - } - } - return result; - }); - } - } })(); diff --git a/manifest.json b/manifest.json index c250caf1..fc7e94a6 100644 --- a/manifest.json +++ b/manifest.json @@ -41,6 +41,7 @@ "background/token-manager.js", "background/sync.js", "background/content-scripts.js", + "background/db-chrome-storage.js", "background/db.js", "background/style-manager.js", "background/navigator-util.js",