protect own style elements (100 times max to avoid deadlocks)

fixes #252
This commit is contained in:
tophf 2017-11-21 09:45:44 +03:00
parent 5d905c2952
commit 8a1908b760

View File

@ -224,8 +224,12 @@ function applyStyles(styles) {
for (const id in styles) { for (const id in styles) {
applySections(id, styles[id].map(section => section.code).join('\n')); applySections(id, styles[id].map(section => section.code).join('\n'));
} }
initDocRewriteObserver(); if (!isOwnPage && !docRewriteObserver && styleElements.size) {
initDocRootObserver(); initDocRewriteObserver();
}
if (!docRootObserver && styleElements.size) {
initDocRootObserver();
}
if (retiredStyleTimers.size) { if (retiredStyleTimers.size) {
setTimeout(() => { setTimeout(() => {
for (const [id, timer] of retiredStyleTimers.entries()) { for (const [id, timer] of retiredStyleTimers.entries()) {
@ -290,9 +294,6 @@ function replaceAll(newStyles) {
function initDocRewriteObserver() { function initDocRewriteObserver() {
if (isOwnPage || docRewriteObserver || !styleElements.size) {
return;
}
// re-add styles if we detect documentElement being recreated // re-add styles if we detect documentElement being recreated
const reinjectStyles = () => { const reinjectStyles = () => {
if (!styleElements) { if (!styleElements) {
@ -327,36 +328,57 @@ function initDocRewriteObserver() {
function initDocRootObserver() { function initDocRootObserver() {
if (!styleElements.size || document.body || docRootObserver) { let lastRestorationTime = 0;
return; let restorationCounter = 0;
docRootObserver = new MutationObserver(findMisplacedStyles);
connectObserver();
function connectObserver() {
docRootObserver.observe(ROOT, {childList: true});
} }
// wait for BODY and move all style elements after it
docRootObserver = new MutationObserver(() => { function findMisplacedStyles() {
let expectedPrevSibling = document.body || document.head; let expectedPrevSibling = document.body || document.head;
if (!expectedPrevSibling) { if (!expectedPrevSibling) {
return; return;
} }
docRootObserver.disconnect(); const list = [];
for (const el of styleElements.values()) { for (const el of styleElements.values()) {
if (el.previousElementSibling !== expectedPrevSibling) { if (el.previousElementSibling !== expectedPrevSibling) {
ROOT.insertBefore(el, expectedPrevSibling.nextSibling); list.push({el, before: expectedPrevSibling.nextSibling});
if (el.disabled !== disableAll) {
// moving an element resets its 'disabled' state
el.disabled = disableAll;
}
} }
expectedPrevSibling = el; expectedPrevSibling = el;
} }
if (document.body) { if (list.length && !restorationLimitExceeded()) {
docRootObserver = null; restoreMisplacedStyles(list);
} else {
docRootObserver.connect();
} }
}); }
docRootObserver.connect = () => {
docRootObserver.observe(ROOT, {childList: true}); function restoreMisplacedStyles(list) {
}; docRootObserver.disconnect();
docRootObserver.connect(); for (const {el, before} of list) {
ROOT.insertBefore(el, before);
if (el.disabled !== disableAll) {
// moving an element resets its 'disabled' state
el.disabled = disableAll;
}
}
connectObserver();
}
function restorationLimitExceeded() {
const t = performance.now();
if (t - lastRestorationTime > 1000) {
restorationCounter = 0;
}
lastRestorationTime = t;
if (++restorationCounter > 100) {
console.error('Stylus stopped restoring userstyle elements after 100 failed attempts.\n' +
'Please report on https://github.com/openstyles/stylus/issues');
return true;
}
}
} }