group style elements in one div

This commit is contained in:
tophf 2020-03-16 13:13:19 +03:00
parent 50aecffe4c
commit d4317878db

View File

@ -11,14 +11,15 @@ self.createStyleInjector = self.INJECTED === 1 ? self.createStyleInjector : ({
const IS_OWN_PAGE = Boolean(chrome.tabs); const IS_OWN_PAGE = Boolean(chrome.tabs);
// detect Chrome 65 via a feature it added since browser version can be spoofed // detect Chrome 65 via a feature it added since browser version can be spoofed
const isChromePre65 = chrome.app && typeof Worklet !== 'function'; const isChromePre65 = chrome.app && typeof Worklet !== 'function';
const docRewriteObserver = RewriteObserver(_sort);
const docRootObserver = RootObserver(_sortIfNeeded); const docRootObserver = RootObserver(_sortIfNeeded);
const docRewriteObserver = RewriteObserver(() => docRootObserver.evade(_sort));
const list = []; const list = [];
const table = new Map(); const table = new Map();
let isEnabled = true; let isEnabled = true;
let isTransitionPatched; let isTransitionPatched;
// will store the original method refs because the page can override them // will store the original method refs because the page can override them
let creationDoc, createElement, createElementNS; let creationDoc, createElement, createElementNS;
let container;
return { return {
apply, apply,
clear, clear,
@ -45,6 +46,7 @@ self.createStyleInjector = self.INJECTED === 1 ? self.createStyleInjector : ({
for (const style of list) { for (const style of list) {
style.el.remove(); style.el.remove();
} }
container.remove();
list.length = 0; list.length = 0;
table.clear(); table.clear();
_emitUpdate(); _emitUpdate();
@ -54,6 +56,10 @@ self.createStyleInjector = self.INJECTED === 1 ? self.createStyleInjector : ({
for (const el of document.querySelectorAll(`style[id^="${PREFIX}"].stylus`)) { for (const el of document.querySelectorAll(`style[id^="${PREFIX}"].stylus`)) {
const id = el.id.slice(PREFIX.length); const id = el.id.slice(PREFIX.length);
if (/^\d+$/.test(id) || id === PATCH_ID) { if (/^\d+$/.test(id) || id === PATCH_ID) {
if (el.parentNode.parentNode === document.documentElement) {
el.parentNode.remove();
return;
}
el.remove(); el.remove();
} }
} }
@ -91,14 +97,12 @@ self.createStyleInjector = self.INJECTED === 1 ? self.createStyleInjector : ({
table.set(style.id, style); table.set(style.id, style);
const nextIndex = list.findIndex(i => compare(i, style) > 0); const nextIndex = list.findIndex(i => compare(i, style) > 0);
if (nextIndex < 0) { if (nextIndex < 0) {
document.documentElement.appendChild(el); _insertElement(el);
list.push(style); list.push(style);
} else { } else {
document.documentElement.insertBefore(el, list[nextIndex].el); _insertElement(el, list[nextIndex].el);
list.splice(nextIndex, 0, style); list.splice(nextIndex, 0, style);
} }
// moving an element resets its 'disabled' state
el.disabled = !isEnabled;
return el; return el;
} }
@ -120,7 +124,7 @@ self.createStyleInjector = self.INJECTED === 1 ? self.createStyleInjector : ({
transition: none !important; transition: none !important;
} }
`); `);
document.documentElement.appendChild(el); _insertElement(el);
// wait for the next paint to complete // wait for the next paint to complete
// note: requestAnimationFrame won't fire in inactive tabs // note: requestAnimationFrame won't fire in inactive tabs
requestAnimationFrame(() => setTimeout(() => el.remove())); requestAnimationFrame(() => setTimeout(() => el.remove()));
@ -171,9 +175,10 @@ self.createStyleInjector = self.INJECTED === 1 ? self.createStyleInjector : ({
*/ */
function _initCreationDoc() { function _initCreationDoc() {
creationDoc = !Event.prototype.getPreventDefault && document.wrappedJSObject; creationDoc = !Event.prototype.getPreventDefault && document.wrappedJSObject;
container = (creationDoc || document).createElement('div');
if (creationDoc) { if (creationDoc) {
({createElement, createElementNS} = creationDoc); ({createElement, createElementNS} = creationDoc);
const el = document.documentElement.appendChild(_createStyle()); const el = _insertElement(_createStyle());
const isApplied = el.sheet; const isApplied = el.sheet;
el.remove(); el.remove();
if (isApplied) return; if (isApplied) return;
@ -182,36 +187,55 @@ self.createStyleInjector = self.INJECTED === 1 ? self.createStyleInjector : ({
({createElement, createElementNS} = document); ({createElement, createElementNS} = document);
} }
function _insertElement(el, beforeEl) {
// beforeEl being `null` or `undefined` or absent is identical to appendChild
container.insertBefore(el, beforeEl);
if (container.parentNode !== creationDoc.documentElement) {
creationDoc.documentElement.appendChild(container);
}
// moving an element resets its 'disabled' state
el.disabled = !isEnabled;
return el;
}
function _remove(id) { function _remove(id) {
const style = table.get(id); const style = table.get(id);
if (!style) return; if (!style) return;
table.delete(id); table.delete(id);
list.splice(list.indexOf(style), 1); list.splice(list.indexOf(style), 1);
style.el.remove(); style.el.remove();
if (!container.firstElementChild) container.remove();
} }
function _sort() { function _sort() {
docRootObserver.evade(() => { list.sort(compare);
list.sort(compare); for (const style of list) {
for (const style of list) { _insertElement(style.el);
// moving an element resets its 'disabled' state }
document.documentElement.appendChild(style.el);
style.el.disabled = !isEnabled;
}
});
} }
function _sortIfNeeded() { function _sortIfNeeded() {
let needsSort; let needsSort;
let el = list.length && list[0].el; let el;
if (!el) { if (!list.length) {
needsSort = false; needsSort = false;
} else if (el.parentNode !== creationDoc.documentElement) { } else if (container.parentNode !== creationDoc.documentElement) {
needsSort = true; needsSort = true;
} else { } else {
for (el = container; el; el = el.nextElementSibling) {
if (ORDERED_TAGS.has(el.localName)) {
docRootObserver.stop();
creationDoc.documentElement.appendChild(container);
needsSort = true;
break;
}
}
el = list[0].el;
let i = 0; let i = 0;
while (el) { while (!needsSort && el) {
if (i < list.length && el === list[i].el) { if (el.parentNode !== container) {
needsSort = true;
} else if (i < list.length && el === list[i].el) {
i++; i++;
} else if (ORDERED_TAGS.has(el.localName)) { } else if (ORDERED_TAGS.has(el.localName)) {
needsSort = true; needsSort = true;
@ -222,7 +246,10 @@ self.createStyleInjector = self.INJECTED === 1 ? self.createStyleInjector : ({
// some styles are not injected to the document // some styles are not injected to the document
if (i < list.length) needsSort = true; if (i < list.length) needsSort = true;
} }
if (needsSort) _sort(); if (needsSort) {
docRootObserver.evade(_sort);
docRootObserver.start();
}
return needsSort; return needsSort;
} }
@ -241,7 +268,7 @@ self.createStyleInjector = self.INJECTED === 1 ? self.createStyleInjector : ({
if (isChromePre65) { if (isChromePre65) {
const oldEl = style.el; const oldEl = style.el;
style.el = _createStyle(id, code); style.el = _createStyle(id, code);
oldEl.parentNode.insertBefore(style.el, oldEl.nextSibling); oldEl.insertAdjacentElement('afterend', style.el);
oldEl.remove(); oldEl.remove();
} else { } else {
style.el.textContent = code; style.el.textContent = code;
@ -291,7 +318,8 @@ self.createStyleInjector = self.INJECTED === 1 ? self.createStyleInjector : ({
if (performance.now() - lastCalledTime > 1000) { if (performance.now() - lastCalledTime > 1000) {
digest = 0; digest = 0;
} else if (digest > 5) { } else if (digest > 5) {
throw new Error('The page keeps generating mutations. Skip the event.'); console.debug('The page keeps generating mutations. Skipping the event.');
return;
} }
} }
if (onChange()) { if (onChange()) {
@ -311,6 +339,7 @@ self.createStyleInjector = self.INJECTED === 1 ? self.createStyleInjector : ({
function start() { function start() {
if (observing) return; if (observing) return;
observer.observe(document.documentElement, {childList: true}); observer.observe(document.documentElement, {childList: true});
observer.observe(container, {childList: true});
observing = true; observing = true;
} }