2017-03-15 13:11:33 +00:00
|
|
|
// using ES5 syntax because ES6 is fast only since around Chrome 55
|
|
|
|
// so we'll wait until Chrome 60 arguably before converting
|
|
|
|
|
2015-04-30 12:32:19 +00:00
|
|
|
var g_disableAll = false;
|
|
|
|
var g_styleElements = {};
|
|
|
|
var iframeObserver;
|
2015-07-12 20:02:17 +00:00
|
|
|
var retiredStyleIds = [];
|
2015-04-30 12:32:19 +00:00
|
|
|
|
|
|
|
initObserver();
|
2015-05-06 16:53:41 +00:00
|
|
|
requestStyles();
|
|
|
|
|
Improve style caching, cache requests too, add code:false mode
Previously, when a cache was invalidated and every tab/iframe issued a getStyles request, we previous needlessly accessed IndexedDB for each of these requests. It happened because 1) the global cachedStyles was created only at the end of the async DB-reading, 2) and each style record is retrieved asynchronously so the single threaded JS engine interleaved all these operations. It could easily span a few seconds when many tabs are open and you have like 100 styles.
Now, in getStyles: all requests issued while cachedStyles is being populated are queued and invoked at the end.
Now, in filterStyles: all requests are cached using the request's options combined in a string as a key. It also helps on each navigation because we monitor page loading process at different stages: before, when committed, history traversal, requesting applicable styles by a content script. Icon badge update also may issue a copy of the just issued request by one of the navigation listeners.
Now, the caches are invalidated smartly: style add/update/delete/toggle only purges filtering cache, and modifies style cache in-place without re-reading the entire IndexedDB.
Now, code:false mode for manage page that only needs style meta. It reduces the transferred message size 10-100 times thus reducing the overhead caused by to internal JSON-fication in the extensions API.
Also fast&direct getStylesSafe for own pages; code cosmetics
2017-03-17 22:50:35 +00:00
|
|
|
function requestStyles(options = {}) {
|
2015-05-06 16:53:41 +00:00
|
|
|
// If this is a Stylish page (Edit Style or Manage Styles),
|
|
|
|
// we'll request the styles directly to minimize delay and flicker,
|
|
|
|
// unless Chrome still starts up and the background page isn't fully loaded.
|
|
|
|
// (Note: in this case the function may be invoked again from applyStyles.)
|
Improve style caching, cache requests too, add code:false mode
Previously, when a cache was invalidated and every tab/iframe issued a getStyles request, we previous needlessly accessed IndexedDB for each of these requests. It happened because 1) the global cachedStyles was created only at the end of the async DB-reading, 2) and each style record is retrieved asynchronously so the single threaded JS engine interleaved all these operations. It could easily span a few seconds when many tabs are open and you have like 100 styles.
Now, in getStyles: all requests issued while cachedStyles is being populated are queued and invoked at the end.
Now, in filterStyles: all requests are cached using the request's options combined in a string as a key. It also helps on each navigation because we monitor page loading process at different stages: before, when committed, history traversal, requesting applicable styles by a content script. Icon badge update also may issue a copy of the just issued request by one of the navigation listeners.
Now, the caches are invalidated smartly: style add/update/delete/toggle only purges filtering cache, and modifies style cache in-place without re-reading the entire IndexedDB.
Now, code:false mode for manage page that only needs style meta. It reduces the transferred message size 10-100 times thus reducing the overhead caused by to internal JSON-fication in the extensions API.
Also fast&direct getStylesSafe for own pages; code cosmetics
2017-03-17 22:50:35 +00:00
|
|
|
var request = Object.assign({
|
|
|
|
method: "getStyles",
|
|
|
|
matchUrl: location.href,
|
|
|
|
enabled: true,
|
|
|
|
asHash: true,
|
|
|
|
}, options);
|
|
|
|
if (typeof getStylesSafe !== 'undefined') {
|
|
|
|
getStylesSafe(request).then(applyStyles);
|
|
|
|
} else {
|
|
|
|
chrome.runtime.sendMessage(request, applyStyles);
|
2015-05-06 16:53:41 +00:00
|
|
|
}
|
2015-03-31 21:35:06 +00:00
|
|
|
}
|
2015-01-30 03:32:14 +00:00
|
|
|
|
2017-03-14 12:18:58 +00:00
|
|
|
chrome.runtime.onMessage.addListener(applyOnMessage);
|
|
|
|
|
|
|
|
function applyOnMessage(request, sender, sendResponse) {
|
2015-04-07 17:07:45 +00:00
|
|
|
// Also handle special request just for the pop-up
|
|
|
|
switch (request.method == "updatePopup" ? request.reason : request.method) {
|
2015-01-30 03:32:14 +00:00
|
|
|
case "styleDeleted":
|
2015-03-03 16:48:29 +00:00
|
|
|
removeStyle(request.id, document);
|
2015-01-30 03:32:14 +00:00
|
|
|
break;
|
|
|
|
case "styleUpdated":
|
Improve style caching, cache requests too, add code:false mode
Previously, when a cache was invalidated and every tab/iframe issued a getStyles request, we previous needlessly accessed IndexedDB for each of these requests. It happened because 1) the global cachedStyles was created only at the end of the async DB-reading, 2) and each style record is retrieved asynchronously so the single threaded JS engine interleaved all these operations. It could easily span a few seconds when many tabs are open and you have like 100 styles.
Now, in getStyles: all requests issued while cachedStyles is being populated are queued and invoked at the end.
Now, in filterStyles: all requests are cached using the request's options combined in a string as a key. It also helps on each navigation because we monitor page loading process at different stages: before, when committed, history traversal, requesting applicable styles by a content script. Icon badge update also may issue a copy of the just issued request by one of the navigation listeners.
Now, the caches are invalidated smartly: style add/update/delete/toggle only purges filtering cache, and modifies style cache in-place without re-reading the entire IndexedDB.
Now, code:false mode for manage page that only needs style meta. It reduces the transferred message size 10-100 times thus reducing the overhead caused by to internal JSON-fication in the extensions API.
Also fast&direct getStylesSafe for own pages; code cosmetics
2017-03-17 22:50:35 +00:00
|
|
|
if (request.codeIsUpdated === false) {
|
|
|
|
applyStyleState(request.style.id, request.style.enabled, document);
|
|
|
|
break;
|
|
|
|
}
|
2016-03-07 02:27:17 +00:00
|
|
|
if (request.style.enabled) {
|
2015-07-12 20:02:17 +00:00
|
|
|
retireStyle(request.style.id);
|
|
|
|
// fallthrough to "styleAdded"
|
|
|
|
} else {
|
|
|
|
removeStyle(request.style.id, document);
|
|
|
|
break;
|
|
|
|
}
|
2015-01-30 03:32:14 +00:00
|
|
|
case "styleAdded":
|
2016-03-07 02:27:17 +00:00
|
|
|
if (request.style.enabled) {
|
2016-01-30 23:08:10 +00:00
|
|
|
chrome.runtime.sendMessage({method: "getStyles", matchUrl: location.href, enabled: true, id: request.style.id, asHash: true}, applyStyles);
|
2015-01-30 16:36:46 +00:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
case "styleApply":
|
2015-03-17 18:03:20 +00:00
|
|
|
applyStyles(request.styles);
|
2015-02-23 05:14:22 +00:00
|
|
|
break;
|
|
|
|
case "styleReplaceAll":
|
2015-03-05 18:02:26 +00:00
|
|
|
replaceAll(request.styles, document);
|
2015-02-23 05:14:22 +00:00
|
|
|
break;
|
2015-03-17 18:03:20 +00:00
|
|
|
case "styleDisableAll":
|
|
|
|
disableAll(request.disableAll);
|
2015-03-23 18:56:11 +00:00
|
|
|
break;
|
2017-03-15 12:41:39 +00:00
|
|
|
case "ping":
|
|
|
|
sendResponse(true);
|
|
|
|
break;
|
2015-01-30 03:32:14 +00:00
|
|
|
}
|
2017-03-14 12:18:58 +00:00
|
|
|
}
|
2015-01-30 03:32:14 +00:00
|
|
|
|
2015-03-17 18:03:20 +00:00
|
|
|
function disableAll(disable) {
|
2015-04-30 12:32:19 +00:00
|
|
|
if (!disable === !g_disableAll) {
|
|
|
|
return;
|
|
|
|
}
|
2015-03-17 18:03:20 +00:00
|
|
|
g_disableAll = disable;
|
2015-04-30 12:32:19 +00:00
|
|
|
if (g_disableAll) {
|
|
|
|
iframeObserver.disconnect();
|
|
|
|
}
|
|
|
|
|
2015-03-17 18:03:20 +00:00
|
|
|
disableSheets(g_disableAll, document);
|
|
|
|
|
2015-04-30 12:32:19 +00:00
|
|
|
if (!g_disableAll && document.readyState != "loading") {
|
|
|
|
iframeObserver.start();
|
|
|
|
}
|
|
|
|
|
2015-03-17 18:03:20 +00:00
|
|
|
function disableSheets(disable, doc) {
|
|
|
|
Array.prototype.forEach.call(doc.styleSheets, function(stylesheet) {
|
2017-02-13 06:17:53 +00:00
|
|
|
if (stylesheet.ownerNode.classList.contains("stylus")) {
|
2015-03-17 18:03:20 +00:00
|
|
|
stylesheet.disabled = disable;
|
|
|
|
}
|
|
|
|
});
|
|
|
|
getDynamicIFrames(doc).forEach(function(iframe) {
|
2015-04-30 12:32:19 +00:00
|
|
|
if (!disable) {
|
|
|
|
// update the IFRAME if it was created while the observer was disconnected
|
|
|
|
addDocumentStylesToIFrame(iframe);
|
|
|
|
}
|
2015-03-17 18:03:20 +00:00
|
|
|
disableSheets(disable, iframe.contentDocument);
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
Improve style caching, cache requests too, add code:false mode
Previously, when a cache was invalidated and every tab/iframe issued a getStyles request, we previous needlessly accessed IndexedDB for each of these requests. It happened because 1) the global cachedStyles was created only at the end of the async DB-reading, 2) and each style record is retrieved asynchronously so the single threaded JS engine interleaved all these operations. It could easily span a few seconds when many tabs are open and you have like 100 styles.
Now, in getStyles: all requests issued while cachedStyles is being populated are queued and invoked at the end.
Now, in filterStyles: all requests are cached using the request's options combined in a string as a key. It also helps on each navigation because we monitor page loading process at different stages: before, when committed, history traversal, requesting applicable styles by a content script. Icon badge update also may issue a copy of the just issued request by one of the navigation listeners.
Now, the caches are invalidated smartly: style add/update/delete/toggle only purges filtering cache, and modifies style cache in-place without re-reading the entire IndexedDB.
Now, code:false mode for manage page that only needs style meta. It reduces the transferred message size 10-100 times thus reducing the overhead caused by to internal JSON-fication in the extensions API.
Also fast&direct getStylesSafe for own pages; code cosmetics
2017-03-17 22:50:35 +00:00
|
|
|
function applyStyleState(id, enabled, doc) {
|
|
|
|
var e = doc.getElementById("stylus-" + id);
|
|
|
|
if (!e) {
|
|
|
|
if (enabled) {
|
|
|
|
requestStyles({id});
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
e.sheet.disabled = !enabled;
|
|
|
|
getDynamicIFrames(doc).forEach(function(iframe) {
|
|
|
|
applyStyleState(id, iframe.contentDocument);
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-03-03 16:48:29 +00:00
|
|
|
function removeStyle(id, doc) {
|
2017-02-13 06:17:53 +00:00
|
|
|
var e = doc.getElementById("stylus-" + id);
|
|
|
|
delete g_styleElements["stylus-" + id];
|
2015-01-30 03:32:14 +00:00
|
|
|
if (e) {
|
2015-04-30 12:32:19 +00:00
|
|
|
e.remove();
|
|
|
|
}
|
|
|
|
if (doc == document && Object.keys(g_styleElements).length == 0) {
|
|
|
|
iframeObserver.disconnect();
|
2015-01-30 03:32:14 +00:00
|
|
|
}
|
2015-03-03 16:48:29 +00:00
|
|
|
getDynamicIFrames(doc).forEach(function(iframe) {
|
|
|
|
removeStyle(id, iframe.contentDocument);
|
|
|
|
});
|
2015-01-30 03:32:14 +00:00
|
|
|
}
|
|
|
|
|
2015-07-12 20:02:17 +00:00
|
|
|
// 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
|
|
|
|
function retireStyle(id, doc) {
|
|
|
|
var deadID = "ghost-" + id;
|
|
|
|
if (!doc) {
|
|
|
|
doc = document;
|
|
|
|
retiredStyleIds.push(deadID);
|
2017-02-13 06:17:53 +00:00
|
|
|
delete g_styleElements["stylus-" + id];
|
2015-07-12 20:02:17 +00:00
|
|
|
// in case something went wrong and new style was never applied
|
|
|
|
setTimeout(removeStyle.bind(null, deadID, doc), 1000);
|
|
|
|
}
|
2017-02-13 06:17:53 +00:00
|
|
|
var e = doc.getElementById("stylus-" + id);
|
2015-07-12 20:02:17 +00:00
|
|
|
if (e) {
|
2017-02-13 06:17:53 +00:00
|
|
|
e.id = "stylus-" + deadID;
|
2015-07-12 20:02:17 +00:00
|
|
|
}
|
|
|
|
getDynamicIFrames(doc).forEach(function(iframe) {
|
|
|
|
retireStyle(id, iframe.contentDocument);
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2015-01-30 16:36:46 +00:00
|
|
|
function applyStyles(styleHash) {
|
2015-05-06 16:53:41 +00:00
|
|
|
if (!styleHash) { // Chrome is starting up
|
|
|
|
requestStyles();
|
|
|
|
return;
|
|
|
|
}
|
2015-03-17 18:03:20 +00:00
|
|
|
if ("disableAll" in styleHash) {
|
|
|
|
disableAll(styleHash.disableAll);
|
|
|
|
delete styleHash.disableAll;
|
|
|
|
}
|
|
|
|
|
2015-01-30 16:36:46 +00:00
|
|
|
for (var styleId in styleHash) {
|
|
|
|
applySections(styleId, styleHash[styleId]);
|
|
|
|
}
|
2015-04-30 12:32:19 +00:00
|
|
|
|
|
|
|
if (Object.keys(g_styleElements).length) {
|
2016-01-25 04:07:43 +00:00
|
|
|
// when site response is application/xml Chrome displays our style elements
|
|
|
|
// under document.documentElement as plain text so we need to move them into HEAD
|
|
|
|
// (which already is autogenerated at this moment for the xml response)
|
|
|
|
if (document.head && document.head.firstChild && document.head.firstChild.id == "xml-viewer-style") {
|
|
|
|
for (var id in g_styleElements) {
|
|
|
|
document.head.appendChild(document.getElementById(id));
|
|
|
|
}
|
|
|
|
}
|
2017-03-19 02:23:58 +00:00
|
|
|
document.addEventListener("DOMContentLoaded", onDOMContentLoaded);
|
2015-04-30 12:32:19 +00:00
|
|
|
}
|
2015-07-12 20:02:17 +00:00
|
|
|
|
|
|
|
if (retiredStyleIds.length) {
|
|
|
|
setTimeout(function() {
|
|
|
|
while (retiredStyleIds.length) {
|
|
|
|
removeStyle(retiredStyleIds.shift(), document);
|
|
|
|
}
|
|
|
|
}, 0);
|
|
|
|
}
|
2015-01-30 03:32:14 +00:00
|
|
|
}
|
|
|
|
|
2017-03-19 02:23:58 +00:00
|
|
|
function onDOMContentLoaded() {
|
|
|
|
addDocumentStylesToAllIFrames();
|
|
|
|
iframeObserver.start();
|
|
|
|
}
|
|
|
|
|
2015-01-30 16:36:46 +00:00
|
|
|
function applySections(styleId, sections) {
|
2017-02-13 06:17:53 +00:00
|
|
|
var styleElement = document.getElementById("stylus-" + styleId);
|
2015-01-30 16:36:46 +00:00
|
|
|
// Already there.
|
|
|
|
if (styleElement) {
|
|
|
|
return;
|
|
|
|
}
|
2015-01-30 03:32:14 +00:00
|
|
|
if (document.documentElement instanceof SVGSVGElement) {
|
|
|
|
// SVG document, make an SVG style element.
|
|
|
|
styleElement = document.createElementNS("http://www.w3.org/2000/svg", "style");
|
|
|
|
} else {
|
|
|
|
// This will make an HTML style element. If there's SVG embedded in an HTML document, this works on the SVG too.
|
|
|
|
styleElement = document.createElement("style");
|
|
|
|
}
|
2017-02-13 06:17:53 +00:00
|
|
|
styleElement.setAttribute("id", "stylus-" + styleId);
|
|
|
|
styleElement.setAttribute("class", "stylus");
|
2015-01-30 03:32:14 +00:00
|
|
|
styleElement.setAttribute("type", "text/css");
|
|
|
|
styleElement.appendChild(document.createTextNode(sections.map(function(section) {
|
|
|
|
return section.code;
|
|
|
|
}).join("\n")));
|
2015-03-03 16:48:29 +00:00
|
|
|
addStyleElement(styleElement, document);
|
2015-04-30 12:32:19 +00:00
|
|
|
g_styleElements[styleElement.id] = styleElement;
|
2015-03-03 16:48:29 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
function addStyleElement(styleElement, doc) {
|
2015-04-30 12:32:19 +00:00
|
|
|
if (!doc.documentElement || doc.getElementById(styleElement.id)) {
|
|
|
|
return;
|
|
|
|
}
|
2015-03-17 18:03:20 +00:00
|
|
|
doc.documentElement.appendChild(doc.importNode(styleElement, true))
|
|
|
|
.disabled = g_disableAll;
|
2015-03-03 16:48:29 +00:00
|
|
|
getDynamicIFrames(doc).forEach(function(iframe) {
|
2015-04-30 12:32:19 +00:00
|
|
|
if (iframeIsLoadingSrcDoc(iframe)) {
|
|
|
|
addStyleToIFrameSrcDoc(iframe, styleElement);
|
|
|
|
} else {
|
|
|
|
addStyleElement(styleElement, iframe.contentDocument);
|
|
|
|
}
|
2015-03-03 16:48:29 +00:00
|
|
|
});
|
2015-01-30 03:32:14 +00:00
|
|
|
}
|
2015-02-23 05:14:22 +00:00
|
|
|
|
2015-04-30 12:32:19 +00:00
|
|
|
function addDocumentStylesToIFrame(iframe) {
|
|
|
|
var doc = iframe.contentDocument;
|
|
|
|
var srcDocIsLoading = iframeIsLoadingSrcDoc(iframe);
|
|
|
|
for (var id in g_styleElements) {
|
|
|
|
if (srcDocIsLoading) {
|
|
|
|
addStyleToIFrameSrcDoc(iframe, g_styleElements[id]);
|
|
|
|
} else {
|
|
|
|
addStyleElement(g_styleElements[id], doc);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-12-24 21:39:50 +00:00
|
|
|
function addDocumentStylesToAllIFrames() {
|
|
|
|
getDynamicIFrames(document).forEach(addDocumentStylesToIFrame);
|
|
|
|
}
|
|
|
|
|
2015-03-03 16:48:29 +00:00
|
|
|
// Only dynamic iframes get the parent document's styles. Other ones should get styles based on their own URLs.
|
|
|
|
function getDynamicIFrames(doc) {
|
|
|
|
return Array.prototype.filter.call(doc.getElementsByTagName('iframe'), iframeIsDynamic);
|
|
|
|
}
|
|
|
|
|
|
|
|
function iframeIsDynamic(f) {
|
|
|
|
var href;
|
|
|
|
try {
|
|
|
|
href = f.contentDocument.location.href;
|
|
|
|
} catch (ex) {
|
|
|
|
// Cross-origin, so it's not a dynamic iframe
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
return href == document.location.href || href.indexOf("about:") == 0;
|
|
|
|
}
|
|
|
|
|
2015-04-30 12:32:19 +00:00
|
|
|
function iframeIsLoadingSrcDoc(f) {
|
|
|
|
return f.srcdoc && f.contentDocument.all.length <= 3;
|
|
|
|
// 3 nodes or less in total (html, head, body) == new empty iframe about to be overwritten by its 'srcdoc'
|
|
|
|
}
|
|
|
|
|
|
|
|
function addStyleToIFrameSrcDoc(iframe, styleElement) {
|
|
|
|
if (g_disableAll) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
iframe.srcdoc += styleElement.outerHTML;
|
|
|
|
// make sure the style is added in case srcdoc was malformed
|
|
|
|
setTimeout(addStyleElement.bind(null, styleElement, iframe.contentDocument), 100);
|
|
|
|
}
|
|
|
|
|
2015-07-12 20:02:17 +00:00
|
|
|
function replaceAll(newStyles, doc, pass2) {
|
2017-02-13 06:17:53 +00:00
|
|
|
var oldStyles = [].slice.call(doc.querySelectorAll("STYLE.stylus" + (pass2 ? "[id$='-ghost']" : "")));
|
2015-07-12 20:02:17 +00:00
|
|
|
if (!pass2) {
|
|
|
|
oldStyles.forEach(function(style) { style.id += "-ghost"; });
|
|
|
|
}
|
|
|
|
getDynamicIFrames(doc).forEach(function(iframe) {
|
|
|
|
replaceAll(newStyles, iframe.contentDocument, pass2);
|
2015-02-23 05:14:22 +00:00
|
|
|
});
|
2015-07-12 20:02:17 +00:00
|
|
|
if (doc == document && !pass2) {
|
2015-04-30 12:32:19 +00:00
|
|
|
g_styleElements = {};
|
|
|
|
applyStyles(newStyles);
|
2015-07-12 20:02:17 +00:00
|
|
|
replaceAll(newStyles, doc, true);
|
|
|
|
}
|
|
|
|
if (pass2) {
|
|
|
|
oldStyles.forEach(function(style) { style.remove(); });
|
2015-04-30 12:32:19 +00:00
|
|
|
}
|
2015-02-23 05:14:22 +00:00
|
|
|
}
|
2015-03-03 16:48:29 +00:00
|
|
|
|
|
|
|
// Observe dynamic IFRAMEs being added
|
2015-04-30 12:32:19 +00:00
|
|
|
function initObserver() {
|
2017-03-15 13:11:33 +00:00
|
|
|
var orphanCheckTimer;
|
|
|
|
|
2015-04-30 12:32:19 +00:00
|
|
|
iframeObserver = new MutationObserver(function(mutations) {
|
2017-03-15 13:11:33 +00:00
|
|
|
clearTimeout(orphanCheckTimer);
|
|
|
|
// MutationObserver runs as a microtask so the timer won't fire until all queued mutations are fired
|
|
|
|
orphanCheckTimer = setTimeout(orphanCheck, 0);
|
|
|
|
|
2015-04-30 12:32:19 +00:00
|
|
|
if (mutations.length > 1000) {
|
|
|
|
// use a much faster method for very complex pages with 100,000 mutations
|
|
|
|
// (observer usually receives 1k-10k mutations per call)
|
2015-12-24 21:39:50 +00:00
|
|
|
addDocumentStylesToAllIFrames();
|
2015-04-30 12:32:19 +00:00
|
|
|
return;
|
|
|
|
}
|
2015-08-04 18:54:56 +00:00
|
|
|
// move the check out of current execution context
|
|
|
|
// because some same-domain (!) iframes fail to load when their "contentDocument" is accessed (!)
|
|
|
|
// namely gmail's old chat iframe talkgadget.google.com
|
|
|
|
setTimeout(process.bind(null, mutations), 0);
|
|
|
|
});
|
|
|
|
|
|
|
|
function process(mutations) {
|
2015-04-30 12:32:19 +00:00
|
|
|
for (var m = 0, ml = mutations.length; m < ml; m++) {
|
|
|
|
var mutation = mutations[m];
|
|
|
|
if (mutation.type === "childList") {
|
|
|
|
for (var n = 0, nodes = mutation.addedNodes, nl = nodes.length; n < nl; n++) {
|
|
|
|
var node = nodes[n];
|
|
|
|
if (node.localName === "iframe" && iframeIsDynamic(node)) {
|
|
|
|
addDocumentStylesToIFrame(node);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2015-08-04 18:54:56 +00:00
|
|
|
}
|
2015-04-30 12:32:19 +00:00
|
|
|
|
|
|
|
iframeObserver.start = function() {
|
|
|
|
// will be ignored by browser if already observing
|
|
|
|
iframeObserver.observe(document, {childList: true, subtree: true});
|
|
|
|
}
|
2017-03-15 13:11:33 +00:00
|
|
|
|
|
|
|
function orphanCheck() {
|
|
|
|
orphanCheckTimer = 0;
|
|
|
|
var port = chrome.runtime.connect();
|
|
|
|
if (port) {
|
|
|
|
port.disconnect();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// we're orphaned due to an extension update
|
|
|
|
// we can detach the mutation observer
|
2017-03-19 02:23:58 +00:00
|
|
|
iframeObserver.takeRecords();
|
2017-03-15 13:11:33 +00:00
|
|
|
iframeObserver.disconnect();
|
2017-03-19 01:59:28 +00:00
|
|
|
iframeObserver = null;
|
2017-03-19 02:23:58 +00:00
|
|
|
// we can detach event listeners
|
|
|
|
document.removeEventListener("DOMContentLoaded", onDOMContentLoaded);
|
|
|
|
// we can't detach chrome.runtime.onMessage because it's no longer connected internally
|
2017-03-15 13:11:33 +00:00
|
|
|
|
|
|
|
// we can destroy global functions in this context to free up memory
|
2017-03-16 12:47:58 +00:00
|
|
|
[
|
|
|
|
'addDocumentStylesToAllIFrames',
|
|
|
|
'addDocumentStylesToIFrame',
|
|
|
|
'addStyleElement',
|
|
|
|
'addStyleToIFrameSrcDoc',
|
|
|
|
'applyOnMessage',
|
|
|
|
'applySections',
|
|
|
|
'applyStyles',
|
|
|
|
'disableAll',
|
|
|
|
'getDynamicIFrames',
|
|
|
|
'iframeIsDynamic',
|
|
|
|
'iframeIsLoadingSrcDoc',
|
|
|
|
'initObserver',
|
|
|
|
'removeStyle',
|
|
|
|
'replaceAll',
|
|
|
|
'requestStyles',
|
|
|
|
'retireStyle'
|
|
|
|
].forEach(fn => window[fn] = null);
|
2017-03-15 13:11:33 +00:00
|
|
|
|
|
|
|
// we can destroy global variables
|
|
|
|
g_styleElements = iframeObserver = retiredStyleIds = null;
|
|
|
|
}
|
2015-04-30 12:32:19 +00:00
|
|
|
}
|