This commit is contained in:
eight 2018-10-04 12:46:19 +08:00
parent 0f148eac32
commit 6d32ffb76b
9 changed files with 162 additions and 140 deletions

View File

@ -4,15 +4,17 @@ global handleCssTransitionBug detectSloppyRegexps
global openEditor global openEditor
global styleViaAPI global styleViaAPI
global loadScript global loadScript
global usercss global usercss styleManager
*/ */
'use strict'; 'use strict';
window.API_METHODS = Object.assign(window.API_METHODS || {}, { window.API_METHODS = Object.assign(window.API_METHODS || {}, {
getStyles, // getStyles,
saveStyle, getSectionsByUrl: styleManager.getSectionsByUrl,
deleteStyle, getSectionsById: styleManager.getSectionsById,
// saveStyle,
// deleteStyle,
getStyleFromDB: id => getStyleFromDB: id =>
dbExec('get', id).then(event => event.target.result), dbExec('get', id).then(event => event.target.result),
@ -495,25 +497,33 @@ function updateIcon({tab, styles}) {
function onRuntimeMessage(msg, sender, sendResponse) { function onRuntimeMessage(msg, sender, sendResponse) {
const fn = window.API_METHODS[msg.method]; if (msg.method !== 'invokeAPI') {
if (!fn) return; // 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;
// wrap 'Error' object instance as {__ERROR__: message}, function invoke() {
// which will be unwrapped by sendMessage, try {
// and prevent exceptions on sending to a closed tab const fn = window.API_METHODS[msg.name];
const respond = data => if (!fn) {
tryCatch(sendResponse, throw new Error(`unknown API: ${msg.name}`);
data instanceof Error ? {__ERROR__: data.message} : data); }
const context = {msg, sender};
const result = fn(msg, sender, respond); return Promise.resolve(fn.apply(context, msg.args));
if (result instanceof Promise) { } catch (err) {
result return Promise.reject(err);
.catch(e => ({__ERROR__: e instanceof Error ? e.message : e})) }
.then(respond);
return KEEP_CHANNEL_OPEN;
} else if (result === KEEP_CHANNEL_OPEN) {
return KEEP_CHANNEL_OPEN;
} else if (result !== undefined) {
respond(result);
} }
} }

View File

@ -1,3 +1,5 @@
'use strict';
const db = (() => { const db = (() => {
let exec; let exec;
const preparing = prepare(); const preparing = prepare();
@ -103,9 +105,9 @@ const db = (() => {
case 'put': case 'put':
if (!data.id) { if (!data.id) {
return getStyles().then(() => { return getAllStyles().then(styles => {
data.id = 1; data.id = 1;
for (const style of cachedStyles.list) { for (const style of styles) {
data.id = Math.max(data.id, style.id + 1); data.id = Math.max(data.id, style.id + 1);
} }
return dbExecChromeStorage('put', data); return dbExecChromeStorage('put', data);
@ -118,17 +120,22 @@ const db = (() => {
return chromeLocal.remove(STYLE_KEY_PREFIX + data); return chromeLocal.remove(STYLE_KEY_PREFIX + data);
case 'getAll': case 'getAll':
return chromeLocal.get(null).then(storage => { return getAllStyles()
const styles = []; .then(styles => ({target: {result: styles}}));
for (const key in storage) {
if (key.startsWith(STYLE_KEY_PREFIX) &&
Number(key.substr(STYLE_KEY_PREFIX.length))) {
styles.push(storage[key]);
}
}
return {target: {result: styles}};
});
} }
return Promise.reject(); return Promise.reject();
function getAllStyles() {
return chromeLocal.get(null).then(storage => {
const styles = [];
for (const key in storage) {
if (key.startsWith(STYLE_KEY_PREFIX) &&
Number(key.substr(STYLE_KEY_PREFIX.length))) {
styles.push(storage[key]);
}
}
return styles;
});
}
} }
})(); })();

View File

@ -1,6 +1,9 @@
/* global createCache db calcStyleDigest normalizeStyleSections db */
'use strict';
const styleManager = (() => { const styleManager = (() => {
const preparing = prepare(); const preparing = prepare();
const styles = new Map; const styles = new Map();
const cachedStyleForUrl = createCache(); const cachedStyleForUrl = createCache();
const compiledRe = createCache(); const compiledRe = createCache();
const compiledExclusion = createCache(); const compiledExclusion = createCache();
@ -50,6 +53,7 @@ const styleManager = (() => {
cachedStyleForUrl.clear(); cachedStyleForUrl.clear();
// FIXME: invalid signature // FIXME: invalid signature
notifyAllTabs(); notifyAllTabs();
return style;
}); });
} }
@ -68,7 +72,7 @@ const styleManager = (() => {
// FIXME: update installDate? // FIXME: update installDate?
style = Object.assign(oldStyle, style); style = Object.assign(oldStyle, style);
style.sections = normalizeStyleSections(style); style.sections = normalizeStyleSections(style);
return dbExec('put', style); return db.exec('put', style);
}) })
.then(event => { .then(event => {
if (style.id == null) { if (style.id == null) {
@ -142,7 +146,7 @@ const styleManager = (() => {
} }
function urlMatchStyle(url, style) { function urlMatchStyle(url, style) {
if (style.exclusions && style.exclusions.some(e => compileExclusion(e).test(url)) { if (style.exclusions && style.exclusions.some(e => compileExclusion(e).test(url))) {
return false; return false;
} }
return true; return true;

View File

@ -22,10 +22,10 @@ API_METHODS.styleViaAPI = !CHROME && (() => {
let observingTabs = false; let observingTabs = false;
return (request, sender) => { return function (request) {
const action = ACTIONS[request.action]; const action = ACTIONS[request.action];
return !action ? NOP : return !action ? NOP :
action(request, sender) action(request, this.sender)
.catch(onError) .catch(onError)
.then(maybeToggleObserver); .then(maybeToggleObserver);
}; };

55
content/api.js Normal file
View File

@ -0,0 +1,55 @@
'use strict';
const API = (() => {
const preparing = promisify(chrome.runtime.getBackgroundPage.bind(chrome.runtime))()
.catch(() => null);
const runtimeSendMessage = promisify(chrome.runtime.sendMessage.bind(chrome.runtime));
return new Proxy(() => {}, {
get: (target, name) =>
(...args) => invokeBG(name, args),
});
function sendMessage(msg) {
return runtimeSendMessage(msg)
.then(result => {
if (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) {
return sendMessage({
method: 'invokeAPI',
name,
args
});
}
// FIXME: why deep-copying input/output?
if (BG !== window) {
args = BG.deepCopy(args);
}
return BG.API_METHODS[name](...args)
.then(BG.deepCopy);
});
}
})();

View File

@ -25,7 +25,13 @@
const FF_BUG461 = !CHROME && !isOwnPage && !Event.prototype.getPreventDefault; const FF_BUG461 = !CHROME && !isOwnPage && !Event.prototype.getPreventDefault;
const pageContextQueue = []; const pageContextQueue = [];
requestStyles(); requestStyles({}, styles => {
// FIXME: need transition patch?
// if (needTransitionPatch(styles)) {
// applyTransitionPatch();
// }
applyStyles(styles);
});
chrome.runtime.onMessage.addListener(applyOnMessage); chrome.runtime.onMessage.addListener(applyOnMessage);
window.applyOnMessage = applyOnMessage; window.applyOnMessage = applyOnMessage;
@ -36,7 +42,7 @@
function requestStyles(options, callback = applyStyles) { function requestStyles(options, callback = applyStyles) {
if (!chrome.app && document instanceof XMLDocument) { if (!chrome.app && document instanceof XMLDocument) {
chrome.runtime.sendMessage({method: 'styleViaAPI', action: 'styleApply'}); API.styleViaAPI({action: 'styleApply'});
return; return;
} }
var matchUrl = location.href; var matchUrl = location.href;
@ -49,19 +55,15 @@
} }
} catch (e) {} } catch (e) {}
} }
const request = Object.assign({ // const request = Object.assign({
method: 'getStylesForFrame', // method: 'getStylesForFrame',
asHash: true, // asHash: true,
matchUrl, // matchUrl,
}, options); // }, options);
// On own pages we request the styles directly to minimize delay and flicker // FIXME: options?
if (typeof API === 'function') { // FIXME: getStylesFallback?
API.getStyles(request).then(callback); API.getSectionsByUrl(matchUrl)
} else if (!CHROME && getStylesFallback(request)) { .then(callback);
// NOP
} else {
chrome.runtime.sendMessage(request, callback);
}
} }
/** /**
@ -99,12 +101,12 @@
if (!chrome.app && document instanceof XMLDocument && request.method !== 'ping') { if (!chrome.app && document instanceof XMLDocument && request.method !== 'ping') {
request.action = request.method; request.action = request.method;
request.method = 'styleViaAPI'; request.method = null;
request.styles = null; request.styles = null;
if (request.style) { if (request.style) {
request.style.sections = null; request.style.sections = null;
} }
chrome.runtime.sendMessage(request); API.styleViaAPI(request);
return; return;
} }
@ -120,7 +122,7 @@
} }
if (request.style.enabled) { if (request.style.enabled) {
removeStyle({id: request.style.id, retire: true}); removeStyle({id: request.style.id, retire: true});
requestStyles({id: request.style.id}); API.getSectionsById(request.style.id).then(applyStyles);
} else { } else {
removeStyle(request.style); removeStyle(request.style);
} }
@ -128,7 +130,7 @@
case 'styleAdded': case 'styleAdded':
if (request.style.enabled) { if (request.style.enabled) {
requestStyles({id: request.style.id}); API.getSectionsById(request.style.id).then(applyStyles);
} }
break; break;
@ -193,7 +195,7 @@
addStyleElement(inCache); addStyleElement(inCache);
disabledElements.delete(id); disabledElements.delete(id);
} else { } else {
requestStyles({id}); API.getSectionsById(id).then(applyStyles);
} }
} else { } else {
if (inDoc) { if (inDoc) {
@ -224,11 +226,11 @@
} }
function applyStyles(styles) { function applyStyles(styles) {
if (!styles) { // if (!styles) {
// Chrome is starting up // Chrome is starting up
requestStyles(); // requestStyles();
return; // return;
} // }
if (!document.documentElement) { if (!document.documentElement) {
new MutationObserver((mutations, observer) => { new MutationObserver((mutations, observer) => {
@ -240,12 +242,13 @@
return; return;
} }
if ('disableAll' in styles) { // FIXME: switch to prefs
doDisableAll(styles.disableAll); // if ('disableAll' in styles) {
} // doDisableAll(styles.disableAll);
if ('exposeIframes' in styles) { // }
doExposeIframes(styles.exposeIframes); // if ('exposeIframes' in styles) {
} // doExposeIframes(styles.exposeIframes);
// }
const gotNewStyles = styles.length || styles.needTransitionPatch; const gotNewStyles = styles.length || styles.needTransitionPatch;
if (gotNewStyles) { if (gotNewStyles) {
@ -256,22 +259,17 @@
} }
} }
if (styles.needTransitionPatch) {
applyTransitionPatch();
}
if (gotNewStyles) { if (gotNewStyles) {
for (const id in styles) { for (const section of styles) {
const sections = styles[id]; applySections(section.id, section.code);
if (!Array.isArray(sections)) continue;
applySections(id, sections.map(({code}) => code).join('\n'));
} }
docRootObserver.firstStart(); docRootObserver.firstStart();
} }
if (FF_BUG461 && (gotNewStyles || styles.needTransitionPatch)) { // FIXME
setContentsInPageContext(); // if (FF_BUG461 && (gotNewStyles || styles.needTransitionPatch)) {
} // setContentsInPageContext();
// }
if (!isOwnPage && !docRewriteObserver && styleElements.size) { if (!isOwnPage && !docRewriteObserver && styleElements.size) {
initDocRewriteObserver(); initDocRewriteObserver();

View File

@ -1,5 +1,7 @@
'use strict';
function createCache(size = 1000) { function createCache(size = 1000) {
const map = new Map; const map = new Map();
const buffer = Array(size); const buffer = Array(size);
let index = 0; let index = 0;
let lastIndex = 0; let lastIndex = 0;
@ -9,7 +11,9 @@ function createCache(size = 1000) {
delete: delete_, delete: delete_,
clear, clear,
has: id => map.has(id), has: id => map.has(id),
get size: () => map.size get size() {
return map.size;
}
}; };
function get(id) { function get(id) {

View File

@ -104,61 +104,6 @@ if (FIREFOX_NO_DOM_STORAGE) {
Object.defineProperty(window, 'sessionStorage', {value: {}}); Object.defineProperty(window, 'sessionStorage', {value: {}});
} }
// eslint-disable-next-line no-var
var API = (() => {
return new Proxy(() => {}, {
get: (target, name) =>
name === 'remoteCall' ?
remoteCall :
arg => invokeBG(name, arg),
});
function remoteCall(name, arg, remoteWindow) {
let thing = window[name] || window.API_METHODS[name];
if (typeof thing === 'function') {
thing = thing(arg);
}
if (!thing || typeof thing !== 'object') {
return thing;
} else if (thing instanceof Promise) {
return thing.then(product => remoteWindow.deepCopy(product));
} else {
return remoteWindow.deepCopy(thing);
}
}
function invokeBG(name, arg = {}) {
if (BG && (name in BG || name in BG.API_METHODS)) {
const call = BG !== window ?
BG.API.remoteCall(name, BG.deepCopy(arg), window) :
remoteCall(name, arg, BG);
return Promise.resolve(call);
}
if (BG && BG.getStyles) {
throw new Error('Bad API method', name, arg);
}
if (FIREFOX) {
arg.method = name;
return sendMessage(arg);
}
return onBackgroundReady().then(() => invokeBG(name, arg));
}
function onBackgroundReady() {
return BG && BG.getStyles ? Promise.resolve() : new Promise(function ping(resolve) {
sendMessage({method: 'healthCheck'}, health => {
if (health !== undefined) {
BG = chrome.extension.getBackgroundPage();
resolve();
} else {
setTimeout(ping, 0, resolve);
}
});
});
}
})();
function notifyAllTabs(msg) { function notifyAllTabs(msg) {
const originalMessage = msg; const originalMessage = msg;
const styleUpdated = msg.method === 'styleUpdated' || msg.method === 'exclusionsUpdated'; const styleUpdated = msg.method === 'styleUpdated' || msg.method === 'exclusionsUpdated';
@ -224,7 +169,6 @@ function notifyAllTabs(msg) {
} }
} }
function sendMessage(msg, callback) { function sendMessage(msg, callback) {
/* /*
Promise mode [default]: Promise mode [default]:

View File

@ -57,7 +57,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/apply.js"] "js": ["content/api.js", "content/apply.js"]
}, },
{ {
"matches": ["http://userstyles.org/*", "https://userstyles.org/*"], "matches": ["http://userstyles.org/*", "https://userstyles.org/*"],