From f1639cc33ebd68d2e596963c5f8846fe478d9b27 Mon Sep 17 00:00:00 2001
From: eight
Date: Fri, 5 Oct 2018 01:03:40 +0800
Subject: [PATCH] WIP: broadcastMessage
---
background/background.js | 1 +
background/style-manager.js | 106 ++++++++++++++++++++++++++++++++++--
content/api.js | 25 +++------
content/apply.js | 2 +-
js/messaging.js | 65 ----------------------
js/promisify.js | 17 ++++++
manage.html | 2 +
manage/manage.js | 9 +--
manifest.json | 3 +-
9 files changed, 134 insertions(+), 96 deletions(-)
create mode 100644 js/promisify.js
diff --git a/background/background.js b/background/background.js
index 50843039..cdbfd33e 100644
--- a/background/background.js
+++ b/background/background.js
@@ -13,6 +13,7 @@ window.API_METHODS = Object.assign(window.API_METHODS || {}, {
// getStyles,
getSectionsByUrl: styleManager.getSectionsByUrl,
getSectionsById: styleManager.getSectionsById,
+ getStylesInfo: styleManager.getStylesInfo,
// saveStyle,
// deleteStyle,
diff --git a/background/style-manager.js b/background/style-manager.js
index d662b217..f89696fe 100644
--- a/background/style-manager.js
+++ b/background/style-manager.js
@@ -1,4 +1,4 @@
-/* global createCache db calcStyleDigest normalizeStyleSections db */
+/* global createCache db calcStyleDigest normalizeStyleSections db promisify */
'use strict';
const styleManager = (() => {
@@ -8,18 +8,37 @@ 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({
- getSectionsForURL,
+ styles,
+ cachedStyleForUrl,
+ getStylesInfo,
+ getSectionsByUrl,
installStyle,
deleteStyle,
setStyleExclusions,
- editSave
+ editSave,
+ toggleStyle
// TODO: get all styles API?
// TODO: get style by ID?
});
+ function toggleStyle(id, enabled) {
+ const style = styles.get(id);
+ style.enabled = enabled;
+ return saveStyle(style)
+ .then(() => broadcastMessage('styleUpdated', {id, enabled}));
+ }
+
+ function getStylesInfo() {
+ // FIXME: remove code?
+ return [...styles.values()];
+ }
+
function editSave() {}
function setStyleExclusions() {}
@@ -105,14 +124,14 @@ const styleManager = (() => {
}
}
- function getSectionsForURL(url) {
+ function getSectionsByUrl(url) {
// if (!URLS.supported(url) || prefs.get('disableAll')) {
// return [];
// }
let result = cachedStyleForUrl.get(url);
if (!result) {
result = [];
- for (const style of styles) {
+ for (const style of styles.values()) {
if (!urlMatchStyle(url, style)) {
continue;
}
@@ -213,4 +232,81 @@ const styleManager = (() => {
function getUrlNoHash(url) {
return url.split('#')[0];
}
+
+ function cleanData(method, data) {
+ if (
+ (method === 'styleUpdated' || method === 'styleAdded') &&
+ (data.sections || data.sourceCode)
+ ) {
+ // apply/popup/manage use only meta for these two methods,
+ // editor may need the full code but can fetch it directly,
+ // so we send just the meta to avoid spamming lots of tabs with huge styles
+ return getStyleWithNoCode(data);
+ }
+ return output;
+ }
+
+ function isExtensionStyle(id) {
+ // TODO
+ // const style = styles.get(id);
+ // if (!style)
+ return false;
+ }
+
+ function broadcastMessage(method, data) {
+ const pendingPrivilage = runtimeSendMessage({method, cleanData(method, data)});
+ // const affectsAll = !msg.affects || msg.affects.all;
+ // const affectsOwnOriginOnly =
+ // !affectsAll && (msg.affects.editor || msg.affects.manager);
+ // const affectsTabs = affectsAll || affectsOwnOriginOnly;
+ // const affectsIcon = affectsAll || msg.affects.icon;
+ // const affectsPopup = affectsAll || msg.affects.popup;
+ // const affectsSelf = affectsPopup || msg.prefs;
+ // notify all open extension pages and popups
+ // if (affectsSelf) {
+ // msg.tabId = undefined;
+ // sendMessage(msg, ignoreChromeError);
+ // }
+ // notify tabs
+ if (affectsTabs || affectsIcon) {
+ const notifyTab = tab => {
+ if (!styleUpdated
+ && (affectsTabs || URLS.optionsUI.includes(tab.url))
+ // own pages are already notified via sendMessage
+ && !(affectsSelf && tab.url.startsWith(URLS.ownOrigin))
+ // skip lazy-loaded aka unloaded tabs that seem to start loading on message in FF
+ && (!FIREFOX || tab.width)) {
+ msg.tabId = tab.id;
+ sendMessage(msg, ignoreChromeError);
+ }
+ if (affectsIcon) {
+ // eslint-disable-next-line no-use-before-define
+ // debounce(API.updateIcon, 0, {tab});
+ }
+ };
+ // list all tabs including chrome-extension:// which can be ours
+ Promise.all([
+ queryTabs(isExtensionStyle(data.id) ? {url: URLS.ownOrigin + '*'} : {}),
+ getActiveTab(),
+ ]).then(([tabs, activeTab]) => {
+ const activeTabId = activeTab && activeTab.id;
+ for (const tab of tabs) {
+ invokeOrPostpone(tab.id === activeTabId, notifyTab, tab);
+ }
+ });
+ }
+ // notify self: the message no longer is sent to the origin in new Chrome
+ if (typeof onRuntimeMessage !== 'undefined') {
+ onRuntimeMessage(originalMessage);
+ }
+ // notify apply.js on own pages
+ if (typeof applyOnMessage !== 'undefined') {
+ applyOnMessage(originalMessage);
+ }
+ // propagate saved style state/code efficiently
+ if (styleUpdated) {
+ msg.refreshOwnTabs = false;
+ API.refreshAllTabs(msg);
+ }
+ }
})();
diff --git a/content/api.js b/content/api.js
index 6409072f..3fe85d68 100644
--- a/content/api.js
+++ b/content/api.js
@@ -1,3 +1,4 @@
+/* global promisify */
'use strict';
const API = (() => {
@@ -12,29 +13,13 @@ const API = (() => {
function sendMessage(msg) {
return runtimeSendMessage(msg)
.then(result => {
- if (result.__ERROR__) {
+ if (result && result.__ERROR__) {
throw new Error(result.__ERROR__);
}
return result;
});
}
- function promisify(fn) {
- return (...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[1] : result
- );
- });
- });
- }
-
function invokeBG(name, args) {
return preparing.then(BG => {
if (!BG) {
@@ -48,7 +33,11 @@ const API = (() => {
if (BG !== window) {
args = BG.deepCopy(args);
}
- return BG.API_METHODS[name](...args)
+ const fn = BG.API_METHODS[name];
+ if (!fn) {
+ throw new Error(`unknown API method: ${name}`);
+ }
+ return Promise.resolve(fn(...args))
.then(BG.deepCopy);
});
}
diff --git a/content/apply.js b/content/apply.js
index 968d0d0a..8c07f685 100644
--- a/content/apply.js
+++ b/content/apply.js
@@ -116,7 +116,7 @@
break;
case 'styleUpdated':
- if (request.codeIsUpdated === false) {
+ if (!request.codeIsUpdated) {
applyStyleState(request.style);
break;
}
diff --git a/js/messaging.js b/js/messaging.js
index ccca8de6..1f594fab 100644
--- a/js/messaging.js
+++ b/js/messaging.js
@@ -104,71 +104,6 @@ if (FIREFOX_NO_DOM_STORAGE) {
Object.defineProperty(window, 'sessionStorage', {value: {}});
}
-function notifyAllTabs(msg) {
- const originalMessage = msg;
- const styleUpdated = msg.method === 'styleUpdated' || msg.method === 'exclusionsUpdated';
- if (styleUpdated || msg.method === 'styleAdded') {
- // apply/popup/manage use only meta for these two methods,
- // editor may need the full code but can fetch it directly,
- // so we send just the meta to avoid spamming lots of tabs with huge styles
- msg = Object.assign({}, msg, {
- style: getStyleWithNoCode(msg.style)
- });
- }
- const affectsAll = !msg.affects || msg.affects.all;
- const affectsOwnOriginOnly = !affectsAll && (msg.affects.editor || msg.affects.manager);
- const affectsTabs = affectsAll || affectsOwnOriginOnly;
- const affectsIcon = affectsAll || msg.affects.icon;
- const affectsPopup = affectsAll || msg.affects.popup;
- const affectsSelf = affectsPopup || msg.prefs;
- // notify all open extension pages and popups
- if (affectsSelf) {
- msg.tabId = undefined;
- sendMessage(msg, ignoreChromeError);
- }
- // notify tabs
- if (affectsTabs || affectsIcon) {
- const notifyTab = tab => {
- if (!styleUpdated
- && (affectsTabs || URLS.optionsUI.includes(tab.url))
- // own pages are already notified via sendMessage
- && !(affectsSelf && tab.url.startsWith(URLS.ownOrigin))
- // skip lazy-loaded aka unloaded tabs that seem to start loading on message in FF
- && (!FIREFOX || tab.width)) {
- msg.tabId = tab.id;
- sendMessage(msg, ignoreChromeError);
- }
- if (affectsIcon) {
- // eslint-disable-next-line no-use-before-define
- debounce(API.updateIcon, 0, {tab});
- }
- };
- // list all tabs including chrome-extension:// which can be ours
- Promise.all([
- queryTabs(affectsOwnOriginOnly ? {url: URLS.ownOrigin + '*'} : {}),
- getActiveTab(),
- ]).then(([tabs, activeTab]) => {
- const activeTabId = activeTab && activeTab.id;
- for (const tab of tabs) {
- invokeOrPostpone(tab.id === activeTabId, notifyTab, tab);
- }
- });
- }
- // notify self: the message no longer is sent to the origin in new Chrome
- if (typeof onRuntimeMessage !== 'undefined') {
- onRuntimeMessage(originalMessage);
- }
- // notify apply.js on own pages
- if (typeof applyOnMessage !== 'undefined') {
- applyOnMessage(originalMessage);
- }
- // propagate saved style state/code efficiently
- if (styleUpdated) {
- msg.refreshOwnTabs = false;
- API.refreshAllTabs(msg);
- }
-}
-
function sendMessage(msg, callback) {
/*
Promise mode [default]:
diff --git a/js/promisify.js b/js/promisify.js
new file mode 100644
index 00000000..4c1ae9e3
--- /dev/null
+++ b/js/promisify.js
@@ -0,0 +1,17 @@
+'use strict';
+
+function promisify(fn) {
+ return (...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
+ );
+ });
+ });
+}
diff --git a/manage.html b/manage.html
index 20cd9618..85d5e3b7 100644
--- a/manage.html
+++ b/manage.html
@@ -153,9 +153,11 @@
+
+
diff --git a/manage/manage.js b/manage/manage.js
index 275dcba8..0430c371 100644
--- a/manage/manage.js
+++ b/manage/manage.js
@@ -32,7 +32,7 @@ const OWN_ICON = chrome.runtime.getManifest().icons['16'];
const handleEvent = {};
Promise.all([
- API.getStyles({omitCode: !BG}),
+ API.getStylesInfo(),
urlFilterParam && API.searchDB({query: 'url:' + urlFilterParam}),
onDOMready().then(initGlobalEvents),
]).then(args => {
@@ -195,7 +195,7 @@ function createStyleElement({style, name}) {
};
}
const parts = createStyleElement.parts;
- const configurable = style.usercssData && Object.keys(style.usercssData.vars).length > 0;
+ const configurable = style.usercssData && style.usercssData.vars && Object.keys(style.usercssData.vars).length > 0;
parts.checker.checked = style.enabled;
parts.nameLink.textContent = tWordBreak(style.name);
parts.nameLink.href = parts.editLink.href = parts.editHrefBase + style.id;
@@ -403,10 +403,7 @@ Object.assign(handleEvent, {
},
toggle(event, entry) {
- API.saveStyle({
- id: entry.styleId,
- enabled: this.matches('.enable') || this.checked,
- });
+ API.toggleStyle(entry.styleId, this.matches('.enable') || this.checked);
},
check(event, entry) {
diff --git a/manifest.json b/manifest.json
index 86421bc8..ced9f3a0 100644
--- a/manifest.json
+++ b/manifest.json
@@ -23,6 +23,7 @@
],
"background": {
"scripts": [
+ "js/promisify.js",
"js/messaging.js",
"js/storage-util.js",
"js/sections-equal.js",
@@ -60,7 +61,7 @@
"run_at": "document_start",
"all_frames": true,
"match_about_blank": true,
- "js": ["content/api.js", "content/apply.js"]
+ "js": ["js/promisify.js", "content/api.js", "content/apply.js"]
},
{
"matches": ["http://userstyles.org/*", "https://userstyles.org/*"],