Enhance: promisify chrome into browser, drop promisify (#866)

* promisify `chrome` into `browser`

* comment

* comment

* comment

* Add: a naive browser polyfill

* Fix: polyfill doesn't detect content script env correctly

Co-authored-by: eight04 <eight04@gmail.com>
This commit is contained in:
tophf 2020-08-14 15:16:01 +03:00 committed by GitHub
parent 3d94c641b3
commit 54b1f218e0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
23 changed files with 153 additions and 162 deletions

View File

@ -1,7 +1,7 @@
/* global download prefs openURL FIREFOX CHROME /* global download prefs openURL FIREFOX CHROME
URLS ignoreChromeError usercssHelper URLS ignoreChromeError usercssHelper
styleManager msg navigatorUtil workerUtil contentScripts sync styleManager msg navigatorUtil workerUtil contentScripts sync
findExistingTab createTab activateTab isTabReplaceable getActiveTab findExistingTab activateTab isTabReplaceable getActiveTab
tabManager */ tabManager */
'use strict'; 'use strict';
@ -336,7 +336,7 @@ function openManage({options = false, search} = {}) {
if (isTabReplaceable(tab, url)) { if (isTabReplaceable(tab, url)) {
return activateTab(tab, {url}); return activateTab(tab, {url});
} }
return createTab({url}); return browser.tabs.create({url});
}); });
}); });
} }

View File

@ -1,4 +1,4 @@
/* global msg queryTabs ignoreChromeError URLS */ /* global msg ignoreChromeError URLS */
/* exported contentScripts */ /* exported contentScripts */
'use strict'; 'use strict';
@ -55,7 +55,7 @@ const contentScripts = (() => {
} }
function injectToAllTabs() { function injectToAllTabs() {
return queryTabs({}).then(tabs => { return browser.tabs.query({}).then(tabs => {
for (const tab of tabs) { for (const tab of tabs) {
// skip unloaded/discarded/chrome tabs // skip unloaded/discarded/chrome tabs
if (!tab.width || tab.discarded || !URLS.supported(tab.url)) continue; if (!tab.width || tab.discarded || !URLS.supported(tab.url)) continue;

View File

@ -1,46 +1,29 @@
/* global promisify */ /* global chromeLocal */
/* exported createChromeStorageDB */ /* exported createChromeStorageDB */
'use strict'; 'use strict';
function createChromeStorageDB() { 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; let INC;
const PREFIX = 'style-'; const PREFIX = 'style-';
const METHODS = { const METHODS = {
// FIXME: we don't use this method at all. Should we remove this? // FIXME: we don't use this method at all. Should we remove this?
get: id => get(PREFIX + id) get: id => chromeLocal.getValue(PREFIX + id),
.then(result => result[PREFIX + id]), put: obj =>
put: obj => Promise.resolve() // FIXME: should we clone the object?
.then(() => { Promise.resolve(!obj.id && prepareInc().then(() => Object.assign(obj, {id: INC++})))
if (!obj.id) { .then(() => chromeLocal.setValue(PREFIX + obj.id, obj))
return prepareInc() .then(() => obj.id),
.then(() => {
// FIXME: should we clone the object?
obj.id = INC++;
});
}
})
.then(() => set({[PREFIX + obj.id]: obj}))
.then(() => obj.id),
putMany: items => prepareInc() putMany: items => prepareInc()
.then(() => { .then(() =>
for (const item of items) { chromeLocal.set(items.reduce((data, item) => {
if (!item.id) { if (!item.id) item.id = INC++;
item.id = INC++; data[PREFIX + item.id] = item;
} return data;
} }, {})))
return set(items.reduce((obj, curr) => {
obj[PREFIX + curr.id] = curr;
return obj;
}, {}));
})
.then(() => items.map(i => i.id)), .then(() => items.map(i => i.id)),
delete: id => remove(PREFIX + id), delete: id => chromeLocal.remove(PREFIX + id),
getAll: () => get(null) getAll: () => chromeLocal.get()
.then(result => { .then(result => {
const output = []; const output = [];
for (const key in result) { for (const key in result) {
@ -69,7 +52,7 @@ function createChromeStorageDB() {
function prepareInc() { function prepareInc() {
if (INC) return Promise.resolve(); if (INC) return Promise.resolve();
return get(null).then(result => { return chromeLocal.get().then(result => {
INC = 1; INC = 1;
for (const key in result) { for (const key in result) {
if (key.startsWith(PREFIX)) { if (key.startsWith(PREFIX)) {

View File

@ -1,4 +1,4 @@
/* global promisify CHROME URLS */ /* global CHROME URLS */
/* exported navigatorUtil */ /* exported navigatorUtil */
'use strict'; 'use strict';
@ -6,7 +6,6 @@ const navigatorUtil = (() => {
const handler = { const handler = {
urlChange: null urlChange: null
}; };
const tabGet = promisify(chrome.tabs.get.bind(chrome.tabs));
return extendNative({onUrlChange}); return extendNative({onUrlChange});
function onUrlChange(fn) { function onUrlChange(fn) {
@ -48,7 +47,7 @@ const navigatorUtil = (() => {
) { ) {
return Promise.resolve(); return Promise.resolve();
} }
return tabGet(data.tabId) return browser.tabs.get(data.tabId)
.then(tab => { .then(tab => {
if (tab.url === 'chrome://newtab/') { if (tab.url === 'chrome://newtab/') {
data.url = tab.url; data.url = tab.url;

View File

@ -1,9 +1,11 @@
/* global chromeLocal promisify FIREFOX */ /* global chromeLocal promisifyChrome FIREFOX */
/* exported tokenManager */ /* exported tokenManager */
'use strict'; 'use strict';
const tokenManager = (() => { const tokenManager = (() => {
const launchWebAuthFlow = promisify(chrome.identity.launchWebAuthFlow.bind(chrome.identity)); promisifyChrome({
identity: ['launchWebAuthFlow'],
});
const AUTH = { const AUTH = {
dropbox: { dropbox: {
flow: 'token', flow: 'token',
@ -158,7 +160,7 @@ const tokenManager = (() => {
Object.assign(query, provider.authQuery); Object.assign(query, provider.authQuery);
} }
const url = `${provider.authURL}?${stringifyQuery(query)}`; const url = `${provider.authURL}?${stringifyQuery(query)}`;
return launchWebAuthFlow({ return browser.identity.launchWebAuthFlow({
url, url,
interactive interactive
}) })

View File

@ -1,8 +1,7 @@
/* global API_METHODS usercss styleManager deepCopy openURL download URLS getTab */ /* global API_METHODS usercss styleManager deepCopy openURL download URLS */
/* exports usercssHelper */ /* exported usercssHelper */
'use strict'; 'use strict';
// eslint-disable-next-line no-unused-vars
const usercssHelper = (() => { const usercssHelper = (() => {
const installCodeCache = {}; const installCodeCache = {};
const clearInstallCode = url => delete installCodeCache[url]; const clearInstallCode = url => delete installCodeCache[url];
@ -46,7 +45,7 @@ const usercssHelper = (() => {
openInstallerPage(tabId, url, {code, inTab} = {}) { openInstallerPage(tabId, url, {code, inTab} = {}) {
const newUrl = `${URLS.installUsercss}?updateUrl=${encodeURIComponent(url)}`; const newUrl = `${URLS.installUsercss}?updateUrl=${encodeURIComponent(url)}`;
if (inTab) { if (inTab) {
getTab(tabId).then(tab => browser.tabs.get(tabId).then(tab =>
openURL({ openURL({
url: `${newUrl}&tabId=${tabId}`, url: `${newUrl}&tabId=${tabId}`,
active: tab.active, active: tab.active,

View File

@ -64,7 +64,6 @@
<script src="vendor-overwrites/codemirror-addon/match-highlighter.js"></script> <script src="vendor-overwrites/codemirror-addon/match-highlighter.js"></script>
<script src="js/polyfill.js"></script> <script src="js/polyfill.js"></script>
<script src="js/promisify.js"></script>
<script src="js/dom.js"></script> <script src="js/dom.js"></script>
<script src="js/messaging.js"></script> <script src="js/messaging.js"></script>
<script src="js/prefs.js"></script> <script src="js/prefs.js"></script>

View File

@ -1,5 +1,5 @@
/* global CodeMirror onDOMready prefs setupLivePrefs $ $$ $create t tHTML /* global CodeMirror onDOMready prefs setupLivePrefs $ $$ $create t tHTML
createSourceEditor queryTabs sessionStorageHash getOwnTab FIREFOX API tryCatch createSourceEditor sessionStorageHash getOwnTab FIREFOX API tryCatch
closeCurrentTab messageBox debounce workerUtil closeCurrentTab messageBox debounce workerUtil
initBeautifyButton ignoreChromeError initBeautifyButton ignoreChromeError
moveFocus msg createSectionsEditor rerouteHotkeys CODEMIRROR_THEMES */ moveFocus msg createSectionsEditor rerouteHotkeys CODEMIRROR_THEMES */
@ -226,7 +226,7 @@ function preinit() {
}).observe(document, {subtree: true, childList: true}); }).observe(document, {subtree: true, childList: true});
if (chrome.windows) { if (chrome.windows) {
queryTabs({currentWindow: true}).then(tabs => { browser.tabs.query({currentWindow: true}).then(tabs => {
const windowId = tabs[0].windowId; const windowId = tabs[0].windowId;
if (prefs.get('openEditInWindow')) { if (prefs.get('openEditInWindow')) {
if ( if (

View File

@ -1,4 +1,4 @@
/* global CodeMirror focusAccessibility colorMimicry editor /* global CodeMirror focusAccessibility colorMimicry editor chromeLocal
onDOMready $ $$ $create t debounce tryRegExp stringAsRegExp template */ onDOMready $ $$ $create t debounce tryRegExp stringAsRegExp template */
'use strict'; 'use strict';
@ -915,7 +915,7 @@ onDOMready().then(() => {
function readStorage() { function readStorage() {
chrome.storage.local.get('editor', ({editor = {}}) => { chromeLocal.getValue('editor').then((editor = {}) => {
state.find = editor.find || ''; state.find = editor.find || '';
state.replace = editor.replace || ''; state.replace = editor.replace || '';
state.icase = editor.icase || state.icase; state.icase = editor.icase || state.icase;
@ -924,14 +924,12 @@ onDOMready().then(() => {
function writeStorage() { function writeStorage() {
chrome.storage.local.get('editor', ({editor}) => chromeLocal.getValue('editor').then((editor = {}) =>
chrome.storage.local.set({ chromeLocal.setValue('editor', Object.assign(editor, {
editor: Object.assign(editor || {}, { find: state.find,
find: state.find, replace: state.replace,
replace: state.replace, icase: state.icase,
icase: state.icase, })));
})
}));
} }

View File

@ -1,4 +1,4 @@
/* global showHelp $ $create tryRegExp queryTabs URLS t template openURL */ /* global showHelp $ $create tryRegExp URLS t template openURL */
/* exported regExpTester */ /* exported regExpTester */
'use strict'; 'use strict';
@ -66,7 +66,7 @@ const regExpTester = (() => {
return rxData; return rxData;
}); });
const getMatchInfo = m => m && {text: m[0], pos: m.index}; const getMatchInfo = m => m && {text: m[0], pos: m.index};
queryTabs({}).then(tabs => { browser.tabs.query({}).then(tabs => {
const supported = tabs.map(tab => tab.url) const supported = tabs.map(tab => tab.url)
.filter(url => URLS.supported(url)); .filter(url => URLS.supported(url));
const unique = [...new Set(supported).values()]; const unique = [...new Set(supported).values()];

View File

@ -10,7 +10,6 @@
<link href="install-usercss/install-usercss.css" rel="stylesheet"> <link href="install-usercss/install-usercss.css" rel="stylesheet">
<script src="js/polyfill.js"></script> <script src="js/polyfill.js"></script>
<script src="js/promisify.js"></script>
<script src="js/msg.js"></script> <script src="js/msg.js"></script>
<script src="js/messaging.js"></script> <script src="js/messaging.js"></script>
<script src="js/prefs.js"></script> <script src="js/prefs.js"></script>

View File

@ -394,13 +394,9 @@
} }
}); });
port.onDisconnect.addListener(() => { port.onDisconnect.addListener(() => {
chrome.tabs.get(tabId, tab => { browser.tabs.get(tabId)
if (chrome.runtime.lastError) { .then(tab => tab.url === initialUrl && location.reload())
closeCurrentTab(); .catch(closeCurrentTab);
} else if (tab.url === initialUrl) {
location.reload();
}
});
}); });
return ({timer = true} = {}) => new Promise((resolve, reject) => { return ({timer = true} = {}) => new Promise((resolve, reject) => {
const id = performance.now(); const id = performance.now();

View File

@ -1,7 +1,7 @@
/* exported getTab getActiveTab onTabReady stringAsRegExp openURL ignoreChromeError /* exported getTab getActiveTab onTabReady stringAsRegExp openURL ignoreChromeError
getStyleWithNoCode tryRegExp sessionStorageHash download deepEqual getStyleWithNoCode tryRegExp sessionStorageHash download deepEqual
closeCurrentTab capitalize CHROME_HAS_BORDER_BUG */ closeCurrentTab capitalize CHROME_HAS_BORDER_BUG */
/* global promisify */ /* global promisifyChrome */
'use strict'; 'use strict';
const CHROME = Boolean(chrome.app) && parseInt(navigator.userAgent.match(/Chrom\w+\/(\d+)|$/)[1]); const CHROME = Boolean(chrome.app) && parseInt(navigator.userAgent.match(/Chrom\w+\/(\d+)|$/)[1]);
@ -93,33 +93,20 @@ if (IS_BG) {
// Object.defineProperty(window, 'localStorage', {value: {}}); // Object.defineProperty(window, 'localStorage', {value: {}});
// Object.defineProperty(window, 'sessionStorage', {value: {}}); // Object.defineProperty(window, 'sessionStorage', {value: {}});
const createTab = promisify(chrome.tabs.create.bind(chrome.tabs)); promisifyChrome({
const queryTabs = promisify(chrome.tabs.query.bind(chrome.tabs)); tabs: ['create', 'get', 'getCurrent', 'move', 'query', 'update'],
const updateTab = promisify(chrome.tabs.update.bind(chrome.tabs)); windows: ['create', 'update'], // Android doesn't have chrome.windows
const moveTabs = promisify(chrome.tabs.move.bind(chrome.tabs)); });
// Android doesn't have chrome.windows
const updateWindow = chrome.windows && promisify(chrome.windows.update.bind(chrome.windows));
const createWindow = chrome.windows && promisify(chrome.windows.create.bind(chrome.windows));
// FF57+ supports openerTabId, but not in Android // FF57+ supports openerTabId, but not in Android
// (detecting FF57 by the feature it added, not navigator.ua which may be spoofed in about:config) // (detecting FF57 by the feature it added, not navigator.ua which may be spoofed in about:config)
const openerTabIdSupported = (!FIREFOX || window.AbortController) && chrome.windows != null; const openerTabIdSupported = (!FIREFOX || window.AbortController) && chrome.windows != null;
function getTab(id) {
return new Promise(resolve =>
chrome.tabs.get(id, tab =>
!chrome.runtime.lastError && resolve(tab)));
}
function getOwnTab() { function getOwnTab() {
return new Promise(resolve => return browser.tabs.getCurrent();
chrome.tabs.getCurrent(tab => resolve(tab)));
} }
function getActiveTab() { function getActiveTab() {
return queryTabs({currentWindow: true, active: true}) return browser.tabs.query({currentWindow: true, active: true})
.then(tabs => tabs[0]); .then(tabs => tabs[0]);
} }
@ -140,7 +127,7 @@ function urlToMatchPattern(url, ignoreSearch) {
function findExistingTab({url, currentWindow, ignoreHash = true, ignoreSearch = false}) { function findExistingTab({url, currentWindow, ignoreHash = true, ignoreSearch = false}) {
url = new URL(url); url = new URL(url);
return queryTabs({url: urlToMatchPattern(url, ignoreSearch), currentWindow}) return browser.tabs.query({url: urlToMatchPattern(url, ignoreSearch), currentWindow})
// FIXME: is tab.url always normalized? // FIXME: is tab.url always normalized?
.then(tabs => tabs.find(matchTab)); .then(tabs => tabs.find(matchTab));
@ -191,8 +178,8 @@ function openURL({
url: url !== tab.url && url.includes('#') ? url : undefined, url: url !== tab.url && url.includes('#') ? url : undefined,
}); });
} }
if (newWindow && createWindow) { if (newWindow && browser.windows) {
return createWindow(Object.assign({url}, windowPosition)) return browser.windows.create(Object.assign({url}, windowPosition))
.then(wnd => wnd.tabs[0]); .then(wnd => wnd.tabs[0]);
} }
return getActiveTab().then((activeTab = {url: ''}) => return getActiveTab().then((activeTab = {url: ''}) =>
@ -205,7 +192,7 @@ function openURL({
if (id != null && !openerTab.incognito && openerTabIdSupported) { if (id != null && !openerTab.incognito && openerTabIdSupported) {
options.openerTabId = id; options.openerTabId = id;
} }
return createTab(options); return browser.tabs.create(options);
} }
} }
@ -232,9 +219,9 @@ function activateTab(tab, {url, index, openerTabId} = {}) {
options.openerTabId = openerTabId; options.openerTabId = openerTabId;
} }
return Promise.all([ return Promise.all([
updateTab(tab.id, options), browser.tabs.update(tab.id, options),
updateWindow && updateWindow(tab.windowId, {focused: true}), browser.windows && browser.windows.update(tab.windowId, {focused: true}),
index != null && moveTabs(tab.id, {index}) index != null && browser.tabs.move(tab.id, {index})
]) ])
.then(() => tab); .then(() => tab);
} }

View File

@ -1,12 +1,12 @@
/* global promisify deepCopy */ /* global promisifyChrome deepCopy */
// deepCopy is only used if the script is executed in extension pages. // deepCopy is only used if the script is executed in extension pages.
'use strict'; 'use strict';
self.msg = self.INJECTED === 1 ? self.msg : (() => { self.msg = self.INJECTED === 1 ? self.msg : (() => {
const runtimeSend = promisify(chrome.runtime.sendMessage.bind(chrome.runtime)); promisifyChrome({
const tabSend = chrome.tabs && promisify(chrome.tabs.sendMessage.bind(chrome.tabs)); runtime: ['sendMessage'],
const tabQuery = chrome.tabs && promisify(chrome.tabs.query.bind(chrome.tabs)); tabs: ['sendMessage', 'query'],
});
const isBg = chrome.extension.getBackgroundPage && chrome.extension.getBackgroundPage() === window; const isBg = chrome.extension.getBackgroundPage && chrome.extension.getBackgroundPage() === window;
if (isBg) { if (isBg) {
window._msg = { window._msg = {
@ -49,19 +49,21 @@ self.msg = self.INJECTED === 1 ? self.msg : (() => {
} }
} }
if (chrome.runtime.getBackgroundPage) { if (chrome.runtime.getBackgroundPage) {
return promisify(chrome.runtime.getBackgroundPage.bind(chrome.runtime))() promisifyChrome({
.catch(() => null); runtime: ['getBackgroundPage'],
});
return browser.runtime.getBackgroundPage().catch(() => null);
} }
return Promise.resolve(null); return Promise.resolve(null);
} }
function send(data, target = 'extension') { function send(data, target = 'extension') {
const message = {data, target}; const message = {data, target};
return runtimeSend(message).then(unwrapData); return browser.runtime.sendMessage(message).then(unwrapData);
} }
function sendTab(tabId, data, options, target = 'tab') { function sendTab(tabId, data, options, target = 'tab') {
return tabSend(tabId, {data, target}, options) return browser.tabs.sendMessage(tabId, {data, target}, options)
.then(unwrapData); .then(unwrapData);
} }
@ -99,7 +101,7 @@ self.msg = self.INJECTED === 1 ? self.msg : (() => {
} }
function broadcastTab(data, filter, options, ignoreExtension = false, target = 'tab') { function broadcastTab(data, filter, options, ignoreExtension = false, target = 'tab') {
return tabQuery({}) return browser.tabs.query({})
// TODO: send to activated tabs first? // TODO: send to activated tabs first?
.then(tabs => { .then(tabs => {
const requests = []; const requests = [];
@ -123,7 +125,7 @@ self.msg = self.INJECTED === 1 ? self.msg : (() => {
const message = {data: dataObj, target}; const message = {data: dataObj, target};
if (tab && tab.id) { if (tab && tab.id) {
requests.push( requests.push(
tabSend(tab.id, message, options) browser.tabs.sendMessage(tab.id, message, options)
.then(unwrapData) .then(unwrapData)
.catch(ignoreError) .catch(ignoreError)
); );

View File

@ -3,6 +3,8 @@
// eslint-disable-next-line no-unused-expressions // eslint-disable-next-line no-unused-expressions
self.INJECTED !== 1 && (() => { self.INJECTED !== 1 && (() => {
// this part runs in workers, content scripts, our extension pages
if (!Object.entries) { if (!Object.entries) {
Object.entries = obj => Object.keys(obj).map(k => [k, obj[k]]); Object.entries = obj => Object.keys(obj).map(k => [k, obj[k]]);
} }
@ -10,9 +12,38 @@ self.INJECTED !== 1 && (() => {
Object.values = obj => Object.keys(obj).map(k => obj[k]); Object.values = obj => Object.keys(obj).map(k => obj[k]);
} }
// the above was shared by content scripts and workers, // don't use self.chrome. It is undefined in Firefox
// the rest is only needed for our extension pages if (typeof chrome !== 'object') return;
if (!self.chrome || !self.chrome.tabs) return; // the rest is for content scripts and our extension pages
self.browser = polyfillBrowser();
/* Promisifies the specified `chrome` methods into `browser`.
The definitions is an object like this: {
'storage.sync': ['get', 'set'], // if deeper than one level, combine the path via `.`
windows: ['create', 'update'], // items and sub-objects will only be created if present in `chrome`
} */
self.promisifyChrome = definitions => {
for (const [scopeName, methods] of Object.entries(definitions)) {
const path = scopeName.split('.');
const src = path.reduce((obj, p) => obj && obj[p], chrome);
if (!src) continue;
const dst = path.reduce((obj, p) => obj[p] || (obj[p] = {}), browser);
for (const name of methods) {
const fn = src[name];
if (!fn || dst[name] && !dst[name].isTrap) continue;
dst[name] = (...args) => new Promise((resolve, reject) =>
fn.call(src, ...args, (...results) =>
chrome.runtime.lastError ?
reject(chrome.runtime.lastError) :
resolve(results.length <= 1 ? results[0] : results)));
// a couple of callbacks have 2 parameters (we don't use those methods, but just in case)
}
}
};
if (!chrome.tabs) return;
// the rest is for our extension pages
if (typeof document === 'object') { if (typeof document === 'object') {
const ELEMENT_METH = { const ELEMENT_METH = {
@ -75,4 +106,27 @@ self.INJECTED !== 1 && (() => {
} catch (err) { } catch (err) {
Object.defineProperty(self, 'sessionStorage', {value: {}}); Object.defineProperty(self, 'sessionStorage', {value: {}});
} }
function polyfillBrowser() {
if (typeof browser === 'object' && browser.runtime) {
return browser;
}
return createTrap(chrome, null);
function createTrap(base, parent) {
const target = typeof base === 'function' ? () => {} : {};
target.isTrap = true;
return new Proxy(target, {
get: (target, prop) => {
if (target[prop]) return target[prop];
if (base[prop] && (typeof base[prop] === 'object' || typeof base[prop] === 'function')) {
target[prop] = createTrap(base[prop], base);
return target[prop];
}
return base[prop];
},
apply: (target, thisArg, args) => base.apply(parent, args)
});
}
}
})(); })();

View File

@ -1,4 +1,4 @@
/* global promisify */ /* global promisifyChrome */
'use strict'; 'use strict';
self.prefs = self.INJECTED === 1 ? self.prefs : (() => { self.prefs = self.INJECTED === 1 ? self.prefs : (() => {
@ -107,10 +107,11 @@ self.prefs = self.INJECTED === 1 ? self.prefs : (() => {
specific: new Map(), specific: new Map(),
}; };
const syncSet = promisify(chrome.storage.sync.set.bind(chrome.storage.sync)); promisifyChrome({
const syncGet = promisify(chrome.storage.sync.get.bind(chrome.storage.sync)); 'storage.sync': ['get', 'set'],
});
const initializing = syncGet('settings') const initializing = browser.storage.sync.get('settings')
.then(result => { .then(result => {
if (result.settings) { if (result.settings) {
setAll(result.settings, true); setAll(result.settings, true);
@ -237,7 +238,7 @@ self.prefs = self.INJECTED === 1 ? self.prefs : (() => {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
setTimeout(() => { setTimeout(() => {
timer = null; timer = null;
syncSet({settings: values}) browser.storage.sync.set({settings: values})
.then(resolve, reject); .then(resolve, reject);
}); });
}); });

View File

@ -1,22 +0,0 @@
'use strict';
/*
Convert chrome APIs into promises. Example:
const storageSyncGet = promisify(chrome.storage.sync.get.bind(chrome.storage.sync));
storageSyncGet(['key']).then(result => {...});
*/
self.promisify = self.INJECTED === 1 ? self.promisify : fn =>
(...args) =>
new Promise((resolve, reject) => {
fn(...args, (...result) => {
if (chrome.runtime.lastError) {
reject(chrome.runtime.lastError);
return;
}
resolve(
result.length === 0 ? undefined :
result.length === 1 ? result[0] : result
);
});
});

View File

@ -1,7 +1,12 @@
/* global loadScript tryJSONparse */ /* global loadScript tryJSONparse promisifyChrome */
/* exported chromeLocal chromeSync */ /* exported chromeLocal chromeSync */
'use strict'; 'use strict';
promisifyChrome({
'storage.local': ['get', 'remove', 'set'],
'storage.sync': ['get', 'remove', 'set'],
});
const [chromeLocal, chromeSync] = (() => { const [chromeLocal, chromeSync] = (() => {
return [ return [
createWrapper('local'), createWrapper('local'),
@ -9,11 +14,11 @@ const [chromeLocal, chromeSync] = (() => {
]; ];
function createWrapper(name) { function createWrapper(name) {
const storage = chrome.storage[name]; const storage = browser.storage[name];
const wrapper = { const wrapper = {
get: data => new Promise(resolve => storage.get(data, resolve)), get: storage.get.bind(storage),
set: data => new Promise(resolve => storage.set(data, () => resolve(data))), set: data => storage.set(data).then(() => data),
remove: data => new Promise(resolve => storage.remove(data, resolve)), remove: storage.remove.bind(storage),
/** /**
* @param {String} key * @param {String} key

View File

@ -147,7 +147,6 @@
</template> </template>
<script src="js/polyfill.js"></script> <script src="js/polyfill.js"></script>
<script src="js/promisify.js"></script>
<script src="js/dom.js"></script> <script src="js/dom.js"></script>
<script src="js/messaging.js"></script> <script src="js/messaging.js"></script>
<script src="js/prefs.js"></script> <script src="js/prefs.js"></script>

View File

@ -26,7 +26,6 @@
"background": { "background": {
"scripts": [ "scripts": [
"js/polyfill.js", "js/polyfill.js",
"js/promisify.js",
"js/messaging.js", "js/messaging.js",
"js/msg.js", "js/msg.js",
"js/storage-util.js", "js/storage-util.js",
@ -77,7 +76,6 @@
"match_about_blank": true, "match_about_blank": true,
"js": [ "js": [
"js/polyfill.js", "js/polyfill.js",
"js/promisify.js",
"js/msg.js", "js/msg.js",
"js/prefs.js", "js/prefs.js",
"content/style-injector.js", "content/style-injector.js",

View File

@ -21,7 +21,6 @@
<script src="js/polyfill.js"></script> <script src="js/polyfill.js"></script>
<script src="js/dom.js"></script> <script src="js/dom.js"></script>
<script src="js/promisify.js"></script>
<script src="js/messaging.js"></script> <script src="js/messaging.js"></script>
<script src="js/msg.js"></script> <script src="js/msg.js"></script>
<script src="js/localization.js"></script> <script src="js/localization.js"></script>

View File

@ -179,7 +179,6 @@
<script src="manage/config-dialog.js"></script> <script src="manage/config-dialog.js"></script>
<script src="js/polyfill.js"></script> <script src="js/polyfill.js"></script>
<script src="js/promisify.js"></script>
<script src="js/dom.js"></script> <script src="js/dom.js"></script>
<script src="js/messaging.js"></script> <script src="js/messaging.js"></script>
<script src="js/localization.js"></script> <script src="js/localization.js"></script>

View File

@ -1,6 +1,6 @@
/* global tabURL handleEvent $ $$ prefs template FIREFOX chromeLocal debounce /* global tabURL handleEvent $ $$ prefs template FIREFOX chromeLocal debounce
$create t API tWordBreak formatDate tryCatch tryJSONparse LZString $create t API tWordBreak formatDate tryCatch tryJSONparse LZString
ignoreChromeError download */ promisifyChrome download */
'use strict'; 'use strict';
window.addEventListener('showStyles:done', function _() { window.addEventListener('showStyles:done', function _() {
@ -88,6 +88,9 @@ window.addEventListener('showStyles:done', function _() {
return; return;
function init() { function init() {
promisifyChrome({
'storage.local': ['getBytesInUse'], // FF doesn't implement it
});
setTimeout(() => document.body.classList.add(BODY_CLASS)); setTimeout(() => document.body.classList.add(BODY_CLASS));
$('#find-styles-inline-group').classList.add('hidden'); $('#find-styles-inline-group').classList.add('hidden');
@ -711,7 +714,7 @@ window.addEventListener('showStyles:done', function _() {
return chromeLocal.loadLZStringScript().then(() => return chromeLocal.loadLZStringScript().then(() =>
tryJSONparse(LZString.decompressFromUTF16(item.payload))); tryJSONparse(LZString.decompressFromUTF16(item.payload)));
} else if (item) { } else if (item) {
chrome.storage.local.remove(key); chromeLocal.remove(key);
} }
}); });
} }
@ -742,16 +745,8 @@ window.addEventListener('showStyles:done', function _() {
function cleanupCache() { function cleanupCache() {
chromeLocal.remove(CACHE_CLEANUP_NEEDED); chromeLocal.remove(CACHE_CLEANUP_NEEDED);
if (chrome.storage.local.getBytesInUse) { Promise.resolve(!browser.storage.local.getBytesInUse ? 1e99 : browser.storage.local.getBytesInUse())
chrome.storage.local.getBytesInUse(null, size => { .then(size => size > CACHE_SIZE && chromeLocal.get().then(cleanupCacheInternal));
if (size > CACHE_SIZE) {
chrome.storage.local.get(null, cleanupCacheInternal);
}
ignoreChromeError();
});
} else {
chrome.storage.local.get(null, cleanupCacheInternal);
}
} }
function cleanupCacheInternal(storage) { function cleanupCacheInternal(storage) {
@ -764,9 +759,8 @@ window.addEventListener('showStyles:done', function _() {
sortedByTime.slice(0, sortedByTime.length / 2); sortedByTime.slice(0, sortedByTime.length / 2);
const toRemove = expired.length ? expired : sortedByTime; const toRemove = expired.length ? expired : sortedByTime;
if (toRemove.length) { if (toRemove.length) {
chrome.storage.local.remove(toRemove.map(item => item.key), ignoreChromeError); chromeLocal.remove(toRemove.map(item => item.key));
} }
ignoreChromeError();
} }
//endregion //endregion