Add: implement bug 461

This commit is contained in:
eight 2018-10-11 17:32:27 +08:00
parent 7ed39ab6ef
commit c657d7e55c
2 changed files with 106 additions and 76 deletions

View File

@ -146,6 +146,8 @@ prefs.subscribe([
'iconset',
], () => debounce(refreshAllIcons));
prefs.initializing.then(refreshIconBadgeColor);
// *************************************************************************
chrome.runtime.onInstalled.addListener(({reason}) => {
if (reason !== 'update') return;

View File

@ -1,5 +1,6 @@
/* eslint no-var: 0 */
/* global msg API prefs */
/* exported APPLY */
'use strict';
// some weird bug in new Chrome: the content script gets injected multiple times
@ -12,17 +13,9 @@ const APPLY = (() => {
var disableAll = false;
var styleElements = new Map();
var disabledElements = new Map();
var retiredStyleTimers = new Map();
var docRewriteObserver;
var docRootObserver;
// FF59+ bug workaround
// See https://github.com/openstyles/stylus/issues/461
// Since it's easy to spoof the browser version in pre-Quantum FF we're checking
// for getPreventDefault which got removed in FF59 https://bugzil.la/691151
const FF_BUG461 = !CHROME && !isOwnPage && !Event.prototype.getPreventDefault;
const pageContextQueue = [];
// FIXME: styleViaAPI
// FIXME: getStylesFallback?
if (!chrome.app && document instanceof XMLDocument) {
@ -33,16 +26,19 @@ const APPLY = (() => {
const styles = Object.values(result);
// CSS transition bug workaround: since we insert styles asynchronously,
// the browsers, especially Firefox, may apply all transitions on page load
applyStyles(styles, () => {
if (styles.some(s => s.code.includes('transition'))) {
applyTransitionPatch();
}
applyStyles(styles);
});
});
}
msg.onTab(applyOnMessage);
if (!isOwnPage) {
window.dispatchEvent(new CustomEvent(chrome.runtime.id));
window.dispatchEvent(new CustomEvent(chrome.runtime.id, {
detail: {method: 'orphan'}
}));
window.addEventListener(chrome.runtime.id, orphanCheck, true);
}
@ -54,6 +50,66 @@ const APPLY = (() => {
prefs.subscribe(['exposeIframes'], updateExposeIframes);
}
const setStyleContent = createSetStyleContent();
function createSetStyleContent() {
// FF59+ bug workaround
// See https://github.com/openstyles/stylus/issues/461
// Since it's easy to spoof the browser version in pre-Quantum FF we're checking
// for getPreventDefault which got removed in FF59 https://bugzil.la/691151
// const FF_BUG461 = !CHROME && !isOwnPage && !Event.prototype.getPreventDefault;
const EVENT_NAME = chrome.runtime.id;
if (CHROME || isOwnPage || Event.prototype.getPreventDefault || !injectPageScript()) {
return (el, content) => {
// FIXME: do we have to keep el.sheet.disabled?
el.textContent = content;
};
}
return (el, content) => {
window.dispatchEvent(EVENT_NAME, new CustomEvent({detail: {
method: 'setStyleContent',
id: el.id,
content
}}));
};
function injectPageScript() {
const script = document.createElement('script');
const scriptContent = EVENT_NAME => {
document.currentScript.remove();
window.dispatchEvent(new CustomEvent(EVENT_NAME, {
detail: {method: 'pageScriptOK'}
}));
window.addEventListener(EVENT_NAME, function handler(e) {
const {method, id, content} = e.detail;
if (method === 'setStyleContent') {
const el = document.getElementById(id);
if (!el) {
return;
}
const disabled = el.sheet.disabled;
el.textContent = content;
el.sheet.disabled = disabled;
} else if (method === 'orphan') {
window.removeEventListener(EVENT_NAME, handler);
}
}, true);
};
script.text = `(${scriptContent})(${JSON.stringify(EVENT_NAME)})`;
let ok = false;
const check = e => {
if (e.detail.method === 'pageScriptOK') {
ok = true;
}
};
window.addEventListener(EVENT_NAME, check, true);
ROOT.appendChild(script);
window.removeEventListener(EVENT_NAME, check, true);
script.remove();
return ok;
}
}
function getMatchUrl() {
var matchUrl = location.href;
if (!matchUrl.match(/^(http|file|chrome|ftp)/)) {
@ -222,30 +278,20 @@ const APPLY = (() => {
updateCount();
}
function removeStyle({id, retire = false}) {
function removeStyle({id}) {
const el = document.getElementById(ID_PREFIX + id);
if (el) {
if (retire) {
// to avoid page flicker when the style is updated
// instead of removing it immediately we rename its ID and queue it
// to be deleted in applyStyles after a new version is fetched and applied
const deadID = id + '-ghost';
el.id = ID_PREFIX + deadID;
// in case something went wrong and new style was never applied
retiredStyleTimers.set(deadID, setTimeout(removeStyle, 1000, {id: deadID}));
} else {
docRootObserver.evade(() => el.remove());
}
}
disabledElements.delete(id);
retiredStyleTimers.delete(id);
if (styleElements.delete(ID_PREFIX + id)) {
if (styleElements.delete(id)) {
updateCount();
}
}
function applyStyles(styles) {
function applyStyles(styles, done) {
if (!styles.length) {
done();
return;
}
@ -253,13 +299,12 @@ const APPLY = (() => {
new MutationObserver((mutations, observer) => {
if (document.documentElement) {
observer.disconnect();
applyStyles(styles);
applyStyles(styles, done);
}
}).observe(document, {childList: true});
return;
}
if (styles.length) {
if (docRootObserver) {
docRootObserver.stop();
} else {
@ -269,7 +314,6 @@ const APPLY = (() => {
applySections(section.id, section.code);
}
docRootObserver.firstStart();
}
// FIXME
// if (FF_BUG461 && (gotNewStyles || styles.needTransitionPatch)) {
@ -280,34 +324,17 @@ const APPLY = (() => {
initDocRewriteObserver();
}
if (retiredStyleTimers.size) {
setTimeout(() => {
for (const [id, timer] of retiredStyleTimers.entries()) {
removeStyle({id});
clearTimeout(timer);
}
});
}
updateExposeIframes();
if (styles.length) {
updateCount();
}
done();
}
function applySections(styleId, code) {
const id = ID_PREFIX + styleId;
let el = styleElements.get(id) || document.getElementById(id);
if (el && el.textContent !== code) {
if (CHROME < 3321) {
function applySections(id, code) {
let el = styleElements.get(id) || document.getElementById(ID_PREFIX + id);
if (el && CHROME < 3321) {
// workaround for Chrome devtools bug fixed in v65
el.remove();
el = null;
} else if (FF_BUG461) {
pageContextQueue.push({id: el.id, el, code});
} else {
el.textContent = code;
}
}
if (!el) {
if (document.documentElement instanceof SVGSVGElement) {
@ -320,19 +347,17 @@ const APPLY = (() => {
// HTML document style; also works on HTML-embedded SVG
el = document.createElement('style');
}
el.id = id;
el.id = ID_PREFIX + id;
el.type = 'text/css';
// SVG className is not a string, but an instance of SVGAnimatedString
el.classList.add('stylus');
if (FF_BUG461) {
pageContextQueue.push({id: el.id, el, code});
} else {
el.textContent = code;
}
addStyleElement(el);
}
if (el.textContent !== code) {
setStyleContent(el, code);
}
styleElements.set(id, el);
disabledElements.delete(Number(styleId));
disabledElements.delete(id);
return el;
}
@ -370,12 +395,12 @@ const APPLY = (() => {
oldStyles.forEach(el => (el.id += '-ghost'));
styleElements.clear();
disabledElements.clear();
[...retiredStyleTimers.values()].forEach(clearTimeout);
retiredStyleTimers.clear();
applyStyles(newStyles);
const removeOld = () => oldStyles.forEach(el => el.remove());
if (docRewriteObserver) {
docRootObserver.evade(() =>
oldStyles.forEach(el => el.remove()));
docRootObserver.evade(removeOld);
} else {
removeOld();
}
}
@ -400,7 +425,10 @@ const APPLY = (() => {
return parseInt(el.id.substr(ID_PREFIX.length));
}
function orphanCheck() {
function orphanCheck(e) {
if (e && e.detail.method !== 'orphan') {
return;
}
if (chrome.i18n && chrome.i18n.getUILanguage()) {
return true;
}