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 styleViaAPI
global loadScript
global usercss
global usercss styleManager
*/
'use strict';
window.API_METHODS = Object.assign(window.API_METHODS || {}, {
getStyles,
saveStyle,
deleteStyle,
// getStyles,
getSectionsByUrl: styleManager.getSectionsByUrl,
getSectionsById: styleManager.getSectionsById,
// saveStyle,
// deleteStyle,
getStyleFromDB: id =>
dbExec('get', id).then(event => event.target.result),
@ -495,25 +497,33 @@ function updateIcon({tab, styles}) {
function onRuntimeMessage(msg, sender, sendResponse) {
const fn = window.API_METHODS[msg.method];
if (!fn) return;
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 sendMessage,
// and prevent exceptions on sending to a closed tab
const respond = data =>
tryCatch(sendResponse,
data instanceof Error ? {__ERROR__: data.message} : data);
// 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;
const result = fn(msg, sender, respond);
if (result instanceof Promise) {
result
.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);
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);
}
}
}

View File

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

View File

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

View File

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

View File

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

View File

@ -104,61 +104,6 @@ if (FIREFOX_NO_DOM_STORAGE) {
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) {
const originalMessage = msg;
const styleUpdated = msg.method === 'styleUpdated' || msg.method === 'exclusionsUpdated';
@ -224,7 +169,6 @@ function notifyAllTabs(msg) {
}
}
function sendMessage(msg, callback) {
/*
Promise mode [default]:

View File

@ -57,7 +57,7 @@
"run_at": "document_start",
"all_frames": true,
"match_about_blank": true,
"js": ["content/apply.js"]
"js": ["content/api.js", "content/apply.js"]
},
{
"matches": ["http://userstyles.org/*", "https://userstyles.org/*"],