diff --git a/.eslintrc b/.eslintrc index b72d8204..3d844c56 100644 --- a/.eslintrc +++ b/.eslintrc @@ -17,6 +17,7 @@ globals: URLS: false BG: false notifyAllTabs: false + sendMessage: false queryTabs: false getTab: false getOwnTab: false diff --git a/background/background.js b/background/background.js index 2a76f8ed..7bff2213 100644 --- a/background/background.js +++ b/background/background.js @@ -110,7 +110,7 @@ contextMenus = Object.assign({ contexts: ['editable'], documentUrlPatterns: [URLS.ownOrigin + 'edit*'], click: (info, tab) => { - chrome.tabs.sendMessage(tab.id, {method: 'editDeleteText'}); + sendMessage(tab.id, {method: 'editDeleteText'}); }, } }); @@ -179,15 +179,12 @@ window.addEventListener('storageReady', function _() { }; const pingCS = (cs, {id, url}) => { + const maybeInject = pong => !pong && injectCS(cs, PING.tabId); cs.matches.some(match => { - if ((match === ALL_URLS || url.match(match)) - && (!url.startsWith('chrome') || url === NTP)) { - chrome.tabs.sendMessage(id, PING, pong => { - if (!pong) { - injectCS(cs, id); - } - ignoreChromeError(); - }); + if ((match === ALL_URLS || url.match(match)) && + (!url.startsWith('chrome') || url === NTP)) { + PING.tabId = id; + sendMessage(PING).then(maybeInject); return true; } }); @@ -211,13 +208,13 @@ function webNavigationListener(method, {url, tabId, frameId}) { if (method === 'styleApply') { handleCssTransitionBug({tabId, frameId, url, styles}); } - chrome.tabs.sendMessage(tabId, { + sendMessage({ + tabId, + frameId, method, // ping own page so it retrieves the styles directly styles: url.startsWith(URLS.ownOrigin) ? 'DIY' : styles, - }, { - frameId - }, ignoreChromeError); + }); } // main page frame id is 0 if (frameId === 0) { @@ -303,9 +300,15 @@ function updateIcon(tab, styles) { } -function onRuntimeMessage(request, sender, sendResponse) { - // prevent browser exception bug on sending a response to a closed tab - sendResponse = (send => data => tryCatch(send, data))(sendResponse); +function onRuntimeMessage(request, sender, sendResponseInternal) { + const sendResponse = data => { + // wrap Error object instance as {__ERROR__: message} - will be unwrapped in sendMessage + if (data instanceof Error) { + data = {__ERROR__: data.message}; + } + // prevent browser exception bug on sending a response to a closed tab + tryCatch(sendResponseInternal, data); + }; switch (request.method) { case 'getStyles': getStyles(request).then(sendResponse); @@ -353,6 +356,7 @@ function onRuntimeMessage(request, sender, sendResponse) { } } + function closeTab(tabId, request) { return new Promise(resolve => { if (request.tabId) { diff --git a/background/usercss-helper.js b/background/usercss-helper.js index dccc7a76..7edbeb96 100644 --- a/background/usercss-helper.js +++ b/background/usercss-helper.js @@ -23,11 +23,7 @@ var usercssHelper = (() => { function wrapReject(pending) { return pending - .then(result => ({success: true, result})) - .catch(err => ({ - success: false, - result: Array.isArray(err) ? err.join('\n') : err.message || String(err), - })); + .catch(err => new Error(Array.isArray(err) ? err.join('\n') : err.message || String(err))); } // Parse the source and find the duplication diff --git a/content/install-user-css.js b/content/install-hook-usercss.js similarity index 94% rename from content/install-user-css.js rename to content/install-hook-usercss.js index 150199c2..60156aff 100644 --- a/content/install-user-css.js +++ b/content/install-hook-usercss.js @@ -1,4 +1,3 @@ -/* global runtimeSend */ 'use strict'; function createSourceLoader() { @@ -95,16 +94,16 @@ function initUsercssInstall() { if (history.length > 1) { history.back(); } else { - runtimeSend({method: 'closeTab'}); + chrome.runtime.sendMessage({method: 'closeTab'}); } break; } }); }); - return runtimeSend({ + chrome.runtime.sendMessage({ method: 'openUsercssInstallPage', - updateUrl: location.href - }).catch(alert); + updateUrl: location.href, + }, r => r && r.__ERROR__ && alert(r.__ERROR__)); } function isUsercss() { diff --git a/content/install.js b/content/install-hook-userstyles.js similarity index 100% rename from content/install.js rename to content/install-hook-userstyles.js diff --git a/content/util.js b/content/util.js deleted file mode 100644 index 34160e87..00000000 --- a/content/util.js +++ /dev/null @@ -1,10 +0,0 @@ -'use strict'; - -function runtimeSend(request) { - return new Promise((resolve, reject) => { - chrome.runtime.sendMessage( - request, - ({success, result}) => (success ? resolve : reject)(result) - ); - }); -} diff --git a/install-usercss.html b/install-usercss.html index 7167462d..73c5cf79 100644 --- a/install-usercss.html +++ b/install-usercss.html @@ -85,7 +85,6 @@
- diff --git a/install-usercss/install-usercss.js b/install-usercss/install-usercss.js index da5fc168..0359327c 100644 --- a/install-usercss/install-usercss.js +++ b/install-usercss/install-usercss.js @@ -1,4 +1,4 @@ -/* global CodeMirror semverCompare makeLink closeCurrentTab runtimeSend */ +/* global CodeMirror semverCompare makeLink closeCurrentTab */ /* global messageBox */ 'use strict'; @@ -44,7 +44,7 @@ cm.setCursor(cursor); cm.scrollTo(scrollInfo.left, scrollInfo.top); - return runtimeSend({ + return sendMessage({ id: installed.id, method: 'saveUsercss', reason: 'update', @@ -168,50 +168,36 @@ } function install(style) { - const request = Object.assign(style, { - method: 'saveUsercss', - reason: 'update' - }); - return runtimeSend(request) - .then(result => { - installed = result; + installed = style; - $$('.warning') - .forEach(el => el.remove()); - $('.install').disabled = true; - $('.install').classList.add('installed'); - $('.set-update-url input[type=checkbox]').disabled = true; - $('.set-update-url').title = result.updateUrl ? - t('installUpdateFrom', result.updateUrl) : ''; + $$('.warning') + .forEach(el => el.remove()); + $('.install').disabled = true; + $('.install').classList.add('installed'); + $('.set-update-url input[type=checkbox]').disabled = true; + $('.set-update-url').title = style.updateUrl ? + t('installUpdateFrom', style.updateUrl) : ''; - updateMeta(result); + updateMeta(style); - chrome.runtime.sendMessage({method: 'openEditor', id: result.id}); + sendMessage({method: 'openEditor', id: style.id}); - if (!liveReload) { - port.postMessage({method: 'closeTab'}); - } + if (!liveReload) { + port.postMessage({method: 'closeTab'}); + } - window.dispatchEvent(new CustomEvent('installed')); - }) - .catch(err => { - messageBox.alert(chrome.i18n.getMessage('styleInstallFailed', String(err))); - }); + window.dispatchEvent(new CustomEvent('installed')); } function initSourceCode(sourceCode) { cm.setValue(sourceCode); cm.refresh(); - runtimeSend({ - method: 'buildUsercss', - sourceCode, - checkDup: true - }).then(init, onInitError); - } - - function onInitError(err) { - $('.header').classList.add('meta-init-error'); - showError(err); + sendMessage({method: 'buildUsercss', sourceCode, checkDup: true}) + .then(init) + .catch(err => { + $('.header').classList.add('meta-init-error'); + showError(err); + }); } function buildWarning(err) { @@ -236,17 +222,14 @@ ); } $('button.install').onclick = () => { - const message = dup ? - chrome.i18n.getMessage('styleInstallOverwrite', [ - data.name, dupData.version, data.version - ]) : - chrome.i18n.getMessage('styleInstall', [data.name]); - - messageBox.confirm(message).then(result => { - if (result) { - return install(style); - } - }); + messageBox.confirm(dup ? + t('styleInstallOverwrite', [data.name, dupData.version, data.version]) : + t('styleInstall', [data.name]) + ).then(ok => ok && + sendMessage(Object.assign(style, {method: 'saveUsercss', reason: 'update'})) + .then(install) + .catch(err => messageBox.alert(t('styleInstallFailed', err))) + ); }; // set updateUrl diff --git a/js/messaging.js b/js/messaging.js index cc1d56e1..e597a95c 100644 --- a/js/messaging.js +++ b/js/messaging.js @@ -87,7 +87,6 @@ function notifyAllTabs(msg) { style: getStyleWithNoCode(msg.style) }); } - const maybeIgnoreLastError = FIREFOX ? ignoreChromeError : undefined; const affectsAll = !msg.affects || msg.affects.all; const affectsOwnOriginOnly = !affectsAll && (msg.affects.editor || msg.affects.manager); const affectsTabs = affectsAll || affectsOwnOriginOnly; @@ -101,7 +100,8 @@ function notifyAllTabs(msg) { && !(affectsSelf && tab.url.startsWith(URLS.ownOrigin)) // skip lazy-loaded aka unloaded tabs that seem to start loading on message in FF && (!FIREFOX || tab.width)) { - chrome.tabs.sendMessage(tab.id, msg, maybeIgnoreLastError); + msg.tabId = tab.id; + sendMessage(msg, ignoreChromeError); } if (affectsIcon && BG) { BG.updateIcon(tab); @@ -128,7 +128,34 @@ function notifyAllTabs(msg) { } // notify background page and all open popups if (affectsSelf) { - chrome.runtime.sendMessage(msg, maybeIgnoreLastError); + msg.tabId = null; + sendMessage(msg, ignoreChromeError); + } +} + + +function sendMessage(msg, callback) { + /* + Promise mode [default]: + - rejects on receiving {__ERROR__: message} created by background.js::onRuntimeMessage + - automatically suppresses chrome.runtime.lastError because it's autogenerated + by browserAction.setText which lacks a callback param in chrome API + Standard callback mode: + - enabled by passing a second param + */ + const {tabId, frameId} = msg; + const fn = tabId ? chrome.tabs.sendMessage : chrome.runtime.sendMessage; + const args = tabId ? [tabId, msg, {frameId}] : [msg]; + if (callback) { + fn(...args, callback); + } else { + return new Promise((resolve, reject) => { + fn(...args, r => { + const err = r && r.__ERROR__; + (err ? reject : resolve)(err || r); + chrome.runtime.lastError; // eslint-disable-line no-unused-expressions + }); + }); } } @@ -338,7 +365,7 @@ function sessionStorageHash(name) { function onBackgroundReady() { return BG && BG.getStyles ? Promise.resolve() : new Promise(function ping(resolve) { - chrome.runtime.sendMessage({method: 'healthCheck'}, health => { + sendMessage({method: 'healthCheck'}, health => { if (health !== undefined) { BG = chrome.extension.getBackgroundPage(); resolve(); diff --git a/manage/fileSaveLoad.js b/manage/fileSaveLoad.js index e891400d..b7e80031 100644 --- a/manage/fileSaveLoad.js +++ b/manage/fileSaveLoad.js @@ -288,8 +288,8 @@ function importFromString(jsonString) { if (tab.id === ownTab.id) { applyOnMessage(message); } else { - invokeOrPostpone(tab.id === activeTab.id, - chrome.tabs.sendMessage, tab.id, message, ignoreChromeError); + message.tabId = tab.id; + invokeOrPostpone(tab.id === activeTab.id, sendMessage, message, ignoreChromeError); } setTimeout(BG.updateIcon, 0, tab, styles); if (tab === lastTab) { diff --git a/manage/manage.js b/manage/manage.js index 8bbdb5cf..38f2092c 100644 --- a/manage/manage.js +++ b/manage/manage.js @@ -581,10 +581,10 @@ function dieOnNullBackground() { if (!FIREFOX || BG) { return; } - chrome.runtime.sendMessage({method: 'healthCheck'}, health => { + sendMessage({method: 'healthCheck'}, health => { if (health && !chrome.extension.getBackgroundPage()) { onDOMready().then(() => { - chrome.runtime.sendMessage({method: 'getStyles'}, showStyles); + sendMessage({method: 'getStyles'}, showStyles); messageBox({ title: 'Stylus', className: 'danger center', diff --git a/manifest.json b/manifest.json index f2402676..6865762c 100644 --- a/manifest.json +++ b/manifest.json @@ -54,14 +54,14 @@ "matches": ["http://userstyles.org/*", "https://userstyles.org/*"], "run_at": "document_start", "all_frames": false, - "js": ["content/install.js"] + "js": ["content/install-hook-userstyles.js"] }, { "matches": [""], "include_globs": ["*.user.css", "*.user.styl"], "run_at": "document_idle", "all_frames": false, - "js": ["content/util.js", "content/install-user-css.js"] + "js": ["content/install-hook-usercss.js"] } ], "browser_action": { diff --git a/popup/popup.js b/popup/popup.js index fc9f0ef1..7be2220b 100644 --- a/popup/popup.js +++ b/popup/popup.js @@ -119,7 +119,7 @@ function initPopup(url) { } getActiveTab().then(function ping(tab, retryCountdown = 10) { - chrome.tabs.sendMessage(tab.id, {method: 'ping'}, {frameId: 0}, pong => { + sendMessage({tabId: tab.id, method: 'ping', frameId: 0}, pong => { if (pong) { return; }