diff --git a/background/background.js b/background/background.js
index 58c741a9..1909b136 100644
--- a/background/background.js
+++ b/background/background.js
@@ -53,7 +53,7 @@ window.API_METHODS = Object.assign(window.API_METHODS || {}, {
optionsCustomizeHotkeys() {
return browser.runtime.openOptionsPage()
.then(() => new Promise(resolve => setTimeout(resolve, 100)))
- .then(() => sendMessage({method: 'optionsCustomizeHotkeys'}));
+ .then(() => msg.broadcastExtension({method: 'optionsCustomizeHotkeys'}));
},
});
@@ -62,7 +62,7 @@ var browserCommands, contextMenus;
// *************************************************************************
// register all listeners
-chrome.runtime.onMessage.addListener(onRuntimeMessage);
+msg.on(onRuntimeMessage);
if (FIREFOX) {
// see notes in apply.js for getStylesFallback
@@ -196,7 +196,7 @@ contextMenus = {
contexts: ['editable'],
documentUrlPatterns: [URLS.ownOrigin + 'edit*'],
click: (info, tab) => {
- sendMessage({tabId: tab.id, method: 'editDeleteText'});
+ msg.sendTab(tab.id, {method: 'editDeleteText'});
},
}
};
@@ -280,11 +280,12 @@ window.addEventListener('storageReady', function _() {
};
const pingCS = (cs, {id, url}) => {
- const maybeInject = pong => !pong && injectCS(cs, id);
+ const maybeInject = ;
cs.matches.some(match => {
if ((match === ALL_URLS || url.match(match)) &&
(!url.startsWith('chrome') || url === NTP)) {
- sendMessage({method: 'ping', tabId: id}, maybeInject);
+ msg.sendTab(id, {method: 'ping'})
+ .then(pong => !pong && injectCS(cs, id));
return true;
}
});
@@ -332,13 +333,15 @@ function webNavigationListener(method, {url, tabId, frameId}) {
handleCssTransitionBug({tabId, frameId, url, styles});
}
if (tab) styles.exposeIframes = tab.url.replace(/(\/\/[^/]*).*/, '$1');
- sendMessage({
+ msg.sendTab(
tabId,
- frameId,
- method,
- // ping own page so it retrieves the styles directly
- styles: url.startsWith(URLS.ownOrigin) ? 'DIY' : styles,
- });
+ {
+ method,
+ // ping own page so it retrieves the styles directly
+ styles: url.startsWith(URLS.ownOrigin) ? 'DIY' : styles,
+ },
+ {frameId}
+ );
}
// main page frame id is 0
if (frameId === 0) {
@@ -370,7 +373,7 @@ function webNavigationListenerChrome(method, data) {
function webNavUsercssInstallerFF(data) {
const {tabId} = data;
Promise.all([
- sendMessage({tabId, method: 'ping'}),
+ msg.sendTab(tabId, {method: 'ping'}),
// we need tab index to open the installer next to the original one
// and also to skip the double-invocation in FF which assigns tab url later
getTab(tabId),
@@ -384,15 +387,15 @@ function webNavUsercssInstallerFF(data) {
function webNavIframeHelperFF({tabId, frameId}) {
if (!frameId) return;
- sendMessage({method: 'ping', tabId, frameId}, pong => {
- ignoreChromeError();
- if (pong) return;
- chrome.tabs.executeScript(tabId, {
- frameId,
- file: '/content/apply.js',
- matchAboutBlank: true,
- }, ignoreChromeError);
- });
+ msg.sendTab(tabId, {method: 'ping'}, {frameId})
+ .then(pong => {
+ if (pong) return;
+ chrome.tabs.executeScript(tabId, {
+ frameId,
+ file: '/content/apply.js',
+ matchAboutBlank: true,
+ }, ignoreChromeError);
+ });
}
@@ -498,35 +501,14 @@ function updateIcon({tab, styles}) {
}
}
-
-function onRuntimeMessage(msg, sender, sendResponse) {
+function onRuntimeMessage(msg, sender) {
if (msg.method !== 'invokeAPI') {
- // FIXME: switch everything to api.js then throw an error when msg.method is unknown.
return;
}
- invoke()
- .catch(err =>
- // wrap 'Error' object instance as {__ERROR__: message},
- // which will be unwrapped by api.js,
- ({
- __ERROR__: err.message || String(err)
- })
- )
- // prevent exceptions on sending to a closed tab
- .then(output => tryCatch(sendResponse, output));
- // keep channel open
- return true;
-
- function invoke() {
- try {
- const fn = window.API_METHODS[msg.name];
- if (!fn) {
- throw new Error(`unknown API: ${msg.name}`);
- }
- const context = {msg, sender};
- return Promise.resolve(fn.apply(context, msg.args));
- } catch (err) {
- return Promise.reject(err);
- }
+ const fn = window.API_METHODS[msg.name];
+ if (!fn) {
+ throw new Error(`unknown API: ${msg.name}`);
}
+ const context = {msg, sender};
+ return fn.apply(context, msg.args);
}
diff --git a/background/style-manager.js b/background/style-manager.js
index d8c2f6e1..c90b9286 100644
--- a/background/style-manager.js
+++ b/background/style-manager.js
@@ -1,6 +1,8 @@
/* eslint no-eq-null: 0, eqeqeq: [2, "smart"] */
-/* global createCache db calcStyleDigest normalizeStyleSections db promisify
- getStyleWithNoCode */
+/*
+ global createCache db calcStyleDigest normalizeStyleSections db promisify
+ getStyleWithNoCode msg
+*/
'use strict';
const styleManager = (() => {
@@ -10,9 +12,6 @@ const styleManager = (() => {
const compiledRe = createCache();
const compiledExclusion = createCache();
const BAD_MATCHER = {test: () => false};
- const tabQuery = promisify(chrome.tabs.query.bind(chrome.tabs));
- const tabSendMessage = promisify(chrome.tabs.sendMessage.bind(chrome.tabs));
- const runtimeSendMessage = promisify(chrome.runtime.sendMessage.bind(chrome.runtime));
// FIXME: do we have to prepare `styles` map for all methods?
return ensurePrepared({
@@ -42,11 +41,15 @@ const styleManager = (() => {
return saveStyle(newData)
.then(newData => {
style.data = newData;
- return emitChanges({
+ const message = {
method: 'styleUpdated',
codeIsUpdated: false,
style: {id, enabled}
- }, style.appliesTo);
+ };
+ if ([...style.appliesTo].every(isExtensionUrl)) {
+ return msg.broadcastExtension(message);
+ }
+ return msg.broadcast(message);
})
.then(() => id);
}
@@ -120,6 +123,7 @@ const styleManager = (() => {
}
function editSave(style) {
+ // const style =
return saveStyle(style);
}
@@ -142,9 +146,9 @@ const styleManager = (() => {
delete cache[id];
}
styles.delete(id);
- return emitChanges({
+ return msg.broadcast({
method: 'styleDeleted',
- data: {id}
+ style: {id}
});
})
.then(() => id);
@@ -182,28 +186,9 @@ const styleManager = (() => {
appliesTo,
data: newData
});
- return Promise.all([
- // manager and editor might be notified twice...
- runtimeSendMessage({method: 'styleAdded', style: getStyleWithNoCode(newData)}),
- emitChangesToTabs(tab => {
- const code = getAppliedCode(tab.url, newData);
- if (!code) {
- return;
- }
- const cache = cachedStyleForUrl.get(tab.url);
- if (cache) {
- cache[newData.id] = code;
- }
- appliesTo.add(tab.url);
- return {
- method: 'styleAdded',
- style: {
- id: newData.id,
- enabled: newData.enabled,
- sections: code
- }
- };
- })
+ return Promis.all([
+ msg.broadcastExtension({method: 'styleAdded', style: getStyleWithNoCode(newData)}),
+ msg.broadcastTab(tab => emitStyleAdded(tab, newData, appliesTo))
]);
} else {
const excluded = new Set();
@@ -223,14 +208,51 @@ const styleManager = (() => {
}
style.appliesTo = new Set(updated.keys());
return Promise.all([
- // FIXME: don't sendMessage
- runtimeSendMessage({method: 'styleUpdated', })
+ msg.broadcastExtension({method: 'styleUpdated', style: getStyleWithNoCode(newData)})
+ msg.broadcastTab(tab => {
+ if (excluded.has(tab.url)) {
+ return {
+ method: 'styleDeleted',
+ style: {id: newData.id}
+ };
+ }
+ if (updated.has(tab.url)) {
+ return {
+ method: 'styleUpdated',
+ style: {
+ id: newData.id,
+ sections: updated.get(tab.url)
+ };
+ };
+ }
+ return emitStyleAdded(tab, newData, style.appliesTo);
+ })
])
}
return style;
});
}
+ function emitStyleAdded(tab, data, appliesTo) {
+ const code = getAppliedCode(tab.url, data);
+ if (!code) {
+ return;
+ }
+ const cache = cachedStyleForUrl.get(tab.url);
+ if (cache) {
+ cache[data.id] = code;
+ }
+ appliesTo.add(tab.url);
+ return {
+ method: 'styleAdded',
+ style: {
+ id: data.id,
+ enabled: data.enabled,
+ sections: code
+ }
+ };
+ }
+
function importStyle(style) {
// FIXME: move this to importer
// style.originalDigest = style.originalDigest || style.styleDigest; // TODO: remove in the future
@@ -241,35 +263,16 @@ const styleManager = (() => {
}
function saveStyle(style) {
- return (style.id == null ? getNewStyle() : getOldStyle())
- .then(oldStyle => {
- style = Object.assign(oldStyle, style);
- // FIXME: why we always run `normalizeStyleSections` at each `saveStyle`?
- style.sections = normalizeStyleSections(style);
- return db.exec('put', style);
- })
+ if (!style.name) {
+ throw new Error('style name is empty');
+ }
+ return db.exec('put', style);
.then(event => {
if (style.id == null) {
style.id = event.target.result;
}
return style;
});
-
- function getOldStyle() {
- return db.exec('get', style.id)
- .then((event, store) => {
- if (!event.target.result) {
- throw new Error(`Unknown style id: ${style.id}`);
- }
- return event.target.result;
- });
- }
-
- // FIXME: don't overwrite style name when the name is empty
-
- function getNewStyle() {
- return Promise.resolve();
- }
}
function getStylesInfoForUrl(url) {
diff --git a/content/apply.js b/content/apply.js
index 45d44e06..bf7a0834 100644
--- a/content/apply.js
+++ b/content/apply.js
@@ -32,7 +32,7 @@
// }
applyStyles(styles);
});
- chrome.runtime.onMessage.addListener(applyOnMessage);
+ msg.onTab(applyOnMessage);
window.applyOnMessage = applyOnMessage;
if (!isOwnPage) {
@@ -92,7 +92,7 @@
}
}
- function applyOnMessage(request, sender, sendResponse) {
+ function applyOnMessage(request) {
if (request.styles === 'DIY') {
// Do-It-Yourself tells our built-in pages to fetch the styles directly
// which is faster because IPC messaging JSON-ifies everything internally
@@ -160,8 +160,7 @@
break;
case 'ping':
- sendResponse(true);
- break;
+ return true;
}
}
@@ -447,7 +446,7 @@
[docRewriteObserver, docRootObserver].forEach(ob => ob && ob.disconnect());
window.removeEventListener(chrome.runtime.id, orphanCheck, true);
try {
- chrome.runtime.onMessage.removeListener(applyOnMessage);
+ msg.off(applyOnMessage);
} catch (e) {}
}
diff --git a/content/install-hook-userstyles.js b/content/install-hook-userstyles.js
index 40097b3d..0ae2a4f6 100644
--- a/content/install-hook-userstyles.js
+++ b/content/install-hook-userstyles.js
@@ -8,7 +8,7 @@
document.addEventListener('stylishInstallChrome', onClick);
document.addEventListener('stylishUpdateChrome', onClick);
- chrome.runtime.onMessage.addListener(onMessage);
+ msg.on(onMessage);
onDOMready().then(() => {
window.postMessage({
@@ -43,16 +43,14 @@
}
}
- function onMessage(msg, sender, sendResponse) {
+ function onMessage(msg) {
switch (msg.method) {
case 'ping':
// orphaned content script check
- sendResponse(true);
- break;
+ return true;
case 'openSettings':
openSettings();
- sendResponse(true);
- break;
+ return true;
}
}
@@ -328,7 +326,7 @@
document.removeEventListener('stylishInstallChrome', onClick);
document.removeEventListener('stylishUpdateChrome', onClick);
try {
- chrome.runtime.onMessage.removeListener(onMessage);
+ msg.off(onMessage);
} catch (e) {}
}
})();
diff --git a/edit/applies-to-line-widget.js b/edit/applies-to-line-widget.js
index fe995be7..62c7b506 100644
--- a/edit/applies-to-line-widget.js
+++ b/edit/applies-to-line-widget.js
@@ -131,7 +131,7 @@ function createAppliesToLineWidget(cm) {
cm.on('change', onChange);
cm.on('optionChange', onOptionChange);
- chrome.runtime.onMessage.addListener(onRuntimeMessage);
+ msg.onExtension(onRuntimeMessage);
requestAnimationFrame(updateWidgetStyle);
update();
@@ -144,7 +144,7 @@ function createAppliesToLineWidget(cm) {
widgets.length = 0;
cm.off('change', onChange);
cm.off('optionChange', onOptionChange);
- chrome.runtime.onMessage.removeListener(onRuntimeMessage);
+ msg.off(onRuntimeMessage);
actualStyle.remove();
}
diff --git a/edit/edit.js b/edit/edit.js
index 5b307aa8..31aa9e4b 100644
--- a/edit/edit.js
+++ b/edit/edit.js
@@ -27,7 +27,7 @@ let editor;
document.addEventListener('visibilitychange', beforeUnload);
-chrome.runtime.onMessage.addListener(onRuntimeMessage);
+msg.on(onRuntimeMessage);
preinit();
diff --git a/js/msg.js b/js/msg.js
index 0c85db35..20a8dc9a 100644
--- a/js/msg.js
+++ b/js/msg.js
@@ -34,7 +34,8 @@ const msg = (() => {
broadcastExtension: send, // alias of send
on,
onTab,
- onExtension
+ onExtension,
+ off
};
function send(data, target = 'extension') {
@@ -67,6 +68,7 @@ const msg = (() => {
throw new Error('there is no bg handler');
}
const handlers = bg._msg.handler.extension.concat(bg._msg.handler.both);
+ // FIXME: do we want to deepCopy `data`?
return Promise.resolve(executeCallbacks(handlers, data, {url: location.href}))
.then(deepCopy);
}
@@ -88,6 +90,7 @@ const msg = (() => {
for (const tab of tabs) {
const isExtension = tab.url.startsWith(EXTENSION_URL);
if (
+ tab.discarded ||
!/^(http|ftp|file)/.test(tab.url) &&
!tab.url.startsWith('chrome://newtab/') &&
!isExtension ||
@@ -129,6 +132,15 @@ const msg = (() => {
handler.extension.push(fn);
}
+ function off(fn) {
+ for (const type of ['both', 'tab', 'extension']) {
+ const index = handler[type].indexOf(fn);
+ if (index >= 0) {
+ handler[type].splice(index, 1);
+ }
+ }
+ }
+
function initHandler() {
if (handler) {
return;
@@ -266,7 +278,7 @@ const msg = (() => {
const API = new Proxy({}, {
get: (target, name) =>
- (...args) => msg.send({
+ (...args) => msg.sendBg({
method: 'invokeAPI',
name,
args
diff --git a/manage/manage.js b/manage/manage.js
index 004398fa..aa55af4b 100644
--- a/manage/manage.js
+++ b/manage/manage.js
@@ -39,7 +39,7 @@ Promise.all([
showStyles(...args);
});
-chrome.runtime.onMessage.addListener(onRuntimeMessage);
+msg.onExtension(onRuntimeMessage);
function onRuntimeMessage(msg) {
switch (msg.method) {
diff --git a/manifest.json b/manifest.json
index 31ba8391..31840ba8 100644
--- a/manifest.json
+++ b/manifest.json
@@ -25,6 +25,7 @@
"scripts": [
"js/promisify.js",
"js/messaging.js",
+ "js/msg.js",
"js/storage-util.js",
"js/sections-equal.js",
"background/storage-dummy.js",
diff --git a/options.html b/options.html
index edf5850e..996ca894 100644
--- a/options.html
+++ b/options.html
@@ -21,6 +21,8 @@
+
+
diff --git a/options/options.js b/options/options.js
index fba0903c..ee150239 100644
--- a/options/options.js
+++ b/options/options.js
@@ -21,7 +21,7 @@ if (!FIREFOX && !OPERA && CHROME < 3343) {
if (FIREFOX && 'update' in (chrome.commands || {})) {
$('[data-cmd="open-keyboard"]').classList.remove('chromium-only');
- chrome.runtime.onMessage.addListener(msg => {
+ msg.onExtension(msg => {
if (msg.method === 'optionsCustomizeHotkeys') {
customizeHotkeys();
}
diff --git a/popup/popup.js b/popup/popup.js
index c999ba28..81516e02 100644
--- a/popup/popup.js
+++ b/popup/popup.js
@@ -26,7 +26,7 @@ getActiveTab().then(tab =>
showStyles(styles);
});
-chrome.runtime.onMessage.addListener(onRuntimeMessage);
+msg.onExtension(onRuntimeMessage);
function onRuntimeMessage(msg) {
switch (msg.method) {
@@ -112,8 +112,7 @@ function initPopup() {
}
getActiveTab().then(function ping(tab, retryCountdown = 10) {
- const sendMessage = promisify(chrome.tabs.sendMessage.bind(chrome.tabs));
- sendMessage(tab.id, {method: 'ping'}, {frameId: 0}).then(pong => {
+ msg.sendTab(tab.id, {method: 'ping'}, {frameId: 0}).then(pong => {
if (pong) {
return;
}
@@ -459,9 +458,8 @@ Object.assign(handleEvent, {
}))
.then(tab => {
if (message) {
- const sendMessage = promisify(chrome.tabs.sendMessage.bind(chrome.tabs.sendMessage));
return onTabReady(tab)
- .then(() => sendMessage(tab.id, message));
+ .then(() => msg.sendTab(tab.id, message));
}
})
.then(window.close);