WIP: broadcastMessage

This commit is contained in:
eight 2018-10-05 01:03:40 +08:00
parent c01f93f62c
commit f1639cc33e
9 changed files with 134 additions and 96 deletions

View File

@ -13,6 +13,7 @@ window.API_METHODS = Object.assign(window.API_METHODS || {}, {
// getStyles, // getStyles,
getSectionsByUrl: styleManager.getSectionsByUrl, getSectionsByUrl: styleManager.getSectionsByUrl,
getSectionsById: styleManager.getSectionsById, getSectionsById: styleManager.getSectionsById,
getStylesInfo: styleManager.getStylesInfo,
// saveStyle, // saveStyle,
// deleteStyle, // deleteStyle,

View File

@ -1,4 +1,4 @@
/* global createCache db calcStyleDigest normalizeStyleSections db */ /* global createCache db calcStyleDigest normalizeStyleSections db promisify */
'use strict'; 'use strict';
const styleManager = (() => { const styleManager = (() => {
@ -8,18 +8,37 @@ const styleManager = (() => {
const compiledRe = createCache(); const compiledRe = createCache();
const compiledExclusion = createCache(); const compiledExclusion = createCache();
const BAD_MATCHER = {test: () => false}; 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? // FIXME: do we have to prepare `styles` map for all methods?
return ensurePrepared({ return ensurePrepared({
getSectionsForURL, styles,
cachedStyleForUrl,
getStylesInfo,
getSectionsByUrl,
installStyle, installStyle,
deleteStyle, deleteStyle,
setStyleExclusions, setStyleExclusions,
editSave editSave,
toggleStyle
// TODO: get all styles API? // TODO: get all styles API?
// TODO: get style by ID? // 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 editSave() {}
function setStyleExclusions() {} function setStyleExclusions() {}
@ -105,14 +124,14 @@ const styleManager = (() => {
} }
} }
function getSectionsForURL(url) { function getSectionsByUrl(url) {
// if (!URLS.supported(url) || prefs.get('disableAll')) { // if (!URLS.supported(url) || prefs.get('disableAll')) {
// return []; // return [];
// } // }
let result = cachedStyleForUrl.get(url); let result = cachedStyleForUrl.get(url);
if (!result) { if (!result) {
result = []; result = [];
for (const style of styles) { for (const style of styles.values()) {
if (!urlMatchStyle(url, style)) { if (!urlMatchStyle(url, style)) {
continue; continue;
} }
@ -213,4 +232,81 @@ const styleManager = (() => {
function getUrlNoHash(url) { function getUrlNoHash(url) {
return url.split('#')[0]; 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);
}
}
})(); })();

View File

@ -1,3 +1,4 @@
/* global promisify */
'use strict'; 'use strict';
const API = (() => { const API = (() => {
@ -12,29 +13,13 @@ const API = (() => {
function sendMessage(msg) { function sendMessage(msg) {
return runtimeSendMessage(msg) return runtimeSendMessage(msg)
.then(result => { .then(result => {
if (result.__ERROR__) { if (result && result.__ERROR__) {
throw new Error(result.__ERROR__); throw new Error(result.__ERROR__);
} }
return result; 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) { function invokeBG(name, args) {
return preparing.then(BG => { return preparing.then(BG => {
if (!BG) { if (!BG) {
@ -48,7 +33,11 @@ const API = (() => {
if (BG !== window) { if (BG !== window) {
args = BG.deepCopy(args); 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); .then(BG.deepCopy);
}); });
} }

View File

@ -116,7 +116,7 @@
break; break;
case 'styleUpdated': case 'styleUpdated':
if (request.codeIsUpdated === false) { if (!request.codeIsUpdated) {
applyStyleState(request.style); applyStyleState(request.style);
break; break;
} }

View File

@ -104,71 +104,6 @@ if (FIREFOX_NO_DOM_STORAGE) {
Object.defineProperty(window, 'sessionStorage', {value: {}}); 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) { function sendMessage(msg, callback) {
/* /*
Promise mode [default]: Promise mode [default]:

17
js/promisify.js Normal file
View File

@ -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
);
});
});
}

View File

@ -153,9 +153,11 @@
</p> </p>
</template> </template>
<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>
<script src="content/api.js"></script>
<script src="content/apply.js"></script> <script src="content/apply.js"></script>
<script src="js/localization.js"></script> <script src="js/localization.js"></script>
<script src="manage/filters.js"></script> <script src="manage/filters.js"></script>

View File

@ -32,7 +32,7 @@ const OWN_ICON = chrome.runtime.getManifest().icons['16'];
const handleEvent = {}; const handleEvent = {};
Promise.all([ Promise.all([
API.getStyles({omitCode: !BG}), API.getStylesInfo(),
urlFilterParam && API.searchDB({query: 'url:' + urlFilterParam}), urlFilterParam && API.searchDB({query: 'url:' + urlFilterParam}),
onDOMready().then(initGlobalEvents), onDOMready().then(initGlobalEvents),
]).then(args => { ]).then(args => {
@ -195,7 +195,7 @@ function createStyleElement({style, name}) {
}; };
} }
const parts = createStyleElement.parts; 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.checker.checked = style.enabled;
parts.nameLink.textContent = tWordBreak(style.name); parts.nameLink.textContent = tWordBreak(style.name);
parts.nameLink.href = parts.editLink.href = parts.editHrefBase + style.id; parts.nameLink.href = parts.editLink.href = parts.editHrefBase + style.id;
@ -403,10 +403,7 @@ Object.assign(handleEvent, {
}, },
toggle(event, entry) { toggle(event, entry) {
API.saveStyle({ API.toggleStyle(entry.styleId, this.matches('.enable') || this.checked);
id: entry.styleId,
enabled: this.matches('.enable') || this.checked,
});
}, },
check(event, entry) { check(event, entry) {

View File

@ -23,6 +23,7 @@
], ],
"background": { "background": {
"scripts": [ "scripts": [
"js/promisify.js",
"js/messaging.js", "js/messaging.js",
"js/storage-util.js", "js/storage-util.js",
"js/sections-equal.js", "js/sections-equal.js",
@ -60,7 +61,7 @@
"run_at": "document_start", "run_at": "document_start",
"all_frames": true, "all_frames": true,
"match_about_blank": 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/*"], "matches": ["http://userstyles.org/*", "https://userstyles.org/*"],