Index: include.preload.js |
=================================================================== |
--- a/include.preload.js |
+++ b/include.preload.js |
@@ -16,7 +16,6 @@ |
*/ |
var MutationObserver = window.MutationObserver || window.WebKitMutationObserver; |
-var SELECTOR_GROUP_SIZE = 200; |
var typeMap = { |
"img": "IMAGE", |
@@ -196,9 +195,8 @@ |
} |
} |
-function ElementHidingTracer(document, selectors) |
+function ElementHidingTracer(selectors) |
{ |
- this.document = document; |
this.selectors = selectors; |
this.changedNodes = []; |
@@ -264,7 +262,7 @@ |
// Forget previously changed nodes that are no longer in the DOM. |
for (var i = 0; i < this.changedNodes.length; i++) |
{ |
- if (!this.document.contains(this.changedNodes[i])) |
+ if (!document.contains(this.changedNodes[i])) |
this.changedNodes.splice(i--, 1); |
} |
@@ -274,7 +272,7 @@ |
var node = mutation.target; |
// Ignore mutations of nodes that aren't in the DOM anymore. |
- if (!this.document.contains(node)) |
+ if (!document.contains(node)) |
continue; |
// Since querySelectorAll() doesn't consider the root itself |
@@ -317,10 +315,10 @@ |
trace: function() |
{ |
- this.checkNodes([this.document]); |
+ this.checkNodes([document]); |
this.observer.observe( |
- this.document, |
+ document, |
{ |
childList: true, |
attributes: true, |
@@ -331,13 +329,13 @@ |
disconnect: function() |
{ |
- this.document.removeEventListener("DOMContentLoaded", this.trace); |
+ document.removeEventListener("DOMContentLoaded", this.trace); |
this.observer.disconnect(); |
clearTimeout(this.timeout); |
} |
}; |
-function runInDocument(document, fn, arg) |
+function runInPageContext(fn, arg) |
{ |
var script = document.createElement("script"); |
script.type = "application/javascript"; |
@@ -352,7 +350,7 @@ |
// us. As a workaround we wrap WebSocket, preventing blocked WebSocket |
// connections from being opened. |
// [1] - https://bugs.chromium.org/p/chromium/issues/detail?id=129353 |
-function wrapWebSocket(document) |
+function wrapWebSocket() |
{ |
if (typeof WebSocket == "undefined") |
return; |
@@ -372,7 +370,7 @@ |
}); |
}); |
- runInDocument(document, function(eventName) |
+ runInPageContext(function(eventName) |
{ |
// As far as possible we must track everything we use that could be |
// sabotaged by the website later in order to circumvent us. |
@@ -432,43 +430,48 @@ |
}, eventName); |
} |
-function init(document) |
+function ElemHide() |
{ |
- var shadow = null; |
- var style = null; |
- var observer = null; |
- var tracer = null; |
+ this.shadow = this.createShadowTree(); |
+ this.style = null; |
+ this.tracer = null; |
- wrapWebSocket(document); |
+ this.propertyFilters = new CSSPropertyFilters( |
+ window, |
+ function(callback) |
+ { |
+ ext.backgroundPage.sendMessage({ |
+ type: "filters.get", |
+ what: "cssproperties" |
+ }, callback); |
+ }, |
+ this.addSelectors.bind(this) |
+ ); |
+} |
+ElemHide.prototype = { |
+ selectorGroupSize: 200, |
- function getPropertyFilters(callback) |
+ createShadowTree: function() |
{ |
- ext.backgroundPage.sendMessage({ |
- type: "filters.get", |
- what: "cssproperties" |
- }, callback); |
- } |
- var propertyFilters = new CSSPropertyFilters(window, getPropertyFilters, |
- addElemHideSelectors); |
+ // Use Shadow DOM if available as to not mess with with web pages that |
+ // rely on the order of their own <style> tags (#309). However, creating |
+ // a shadow root breaks running CSS transitions. So we have to create |
+ // the shadow root before transistions might start (#452). |
+ if (!("createShadowRoot" in document.documentElement)) |
+ return null; |
- // Use Shadow DOM if available to don't mess with web pages that rely on |
- // the order of their own <style> tags (#309). |
- // |
- // However, creating a shadow root breaks running CSS transitions. So we |
- // have to create the shadow root before transistions might start (#452). |
- // |
- // Also, using shadow DOM causes issues on some Google websites, |
- // including Google Docs, Gmail and Blogger (#1770, #2602, #2687). |
- if ("createShadowRoot" in document.documentElement && |
- !/\.(?:google|blogger)\.com$/.test(document.domain)) |
- { |
- shadow = document.documentElement.createShadowRoot(); |
+ // Using shadow DOM causes issues on some Google websites, |
+ // including Google Docs, Gmail and Blogger (#1770, #2602, #2687). |
+ if (/\.(?:google|blogger)\.com$/.test(document.domain)) |
+ return null; |
+ |
+ var shadow = document.documentElement.createShadowRoot(); |
shadow.appendChild(document.createElement("shadow")); |
- // Stop the website from messing with our shadowRoot |
+ // Stop the website from messing with our shadow root (#4191, #4298). |
if ("shadowRoot" in Element.prototype) |
{ |
- runInDocument(document, function() |
+ runInPageContext(function() |
{ |
var ourShadowRoot = document.documentElement.shadowRoot; |
var desc = Object.getOwnPropertyDescriptor(Element.prototype, "shadowRoot"); |
@@ -483,34 +486,37 @@ |
}); |
}, null); |
} |
- } |
- function addElemHideSelectors(selectors) |
+ return shadow; |
+ }, |
+ |
+ addSelectors: function(selectors) |
{ |
if (selectors.length == 0) |
return; |
- if (!style) |
+ if (!this.style) |
{ |
// Create <style> element lazily, only if we add styles. Add it to |
// the shadow DOM if possible. Otherwise fallback to the <head> or |
// <html> element. If we have injected a style element before that |
// has been removed (the sheet property is null), create a new one. |
- style = document.createElement("style"); |
- (shadow || document.head || document.documentElement).appendChild(style); |
+ this.style = document.createElement("style"); |
+ (this.shadow || document.head |
+ || document.documentElement).appendChild(this.style); |
// It can happen that the frame already navigated to a different |
// document while we were waiting for the background page to respond. |
// In that case the sheet property will stay null, after addind the |
// <style> element to the shadow DOM. |
- if (!style.sheet) |
+ if (!this.style.sheet) |
return; |
} |
// If using shadow DOM, we have to add the ::content pseudo-element |
// before each selector, in order to match elements within the |
// insertion point. |
- if (shadow) |
+ if (this.shadow) |
{ |
var preparedSelectors = []; |
for (var i = 0; i < selectors.length; i++) |
@@ -527,41 +533,37 @@ |
// (Chrome also has a limit, larger... but we're not certain exactly what it |
// is! Edge apparently has no such limit.) |
// [1] - https://github.com/WebKit/webkit/blob/1cb2227f6b2a1035f7bdc46e5ab69debb75fc1de/Source/WebCore/css/RuleSet.h#L68 |
- for (var i = 0; i < selectors.length; i += SELECTOR_GROUP_SIZE) |
+ for (var i = 0; i < selectors.length; i += this.selectorGroupSize) |
{ |
- var selector = selectors.slice(i, i + SELECTOR_GROUP_SIZE).join(", "); |
- style.sheet.addRule(selector, "display: none !important;"); |
+ var selector = selectors.slice(i, i + this.selectorGroupSize).join(", "); |
+ this.style.sheet.addRule(selector, "display: none !important;"); |
} |
- }; |
+ }, |
- var updateStylesheet = function() |
+ apply: function() |
{ |
var selectors = null; |
- var CSSPropertyFiltersLoaded = false; |
+ var propertyFiltersLoaded = false; |
var checkLoaded = function() |
{ |
- if (!selectors || !CSSPropertyFiltersLoaded) |
+ if (!selectors || !propertyFiltersLoaded) |
return; |
- if (observer) |
- observer.disconnect(); |
- observer = null; |
+ if (this.tracer) |
+ this.tracer.disconnect(); |
+ this.tracer = null; |
- if (tracer) |
- tracer.disconnect(); |
- tracer = null; |
+ if (this.style && this.style.parentElement) |
+ this.style.parentElement.removeChild(this.style); |
+ this.style = null; |
- if (style && style.parentElement) |
- style.parentElement.removeChild(style); |
- style = null; |
- |
- addElemHideSelectors(selectors.selectors); |
- propertyFilters.apply(); |
+ this.addSelectors(selectors.selectors); |
+ this.propertyFilters.apply(); |
if (selectors.trace) |
- tracer = new ElementHidingTracer(document, selectors.selectors); |
- }; |
+ this.tracer = new ElementHidingTracer(selectors.selectors); |
+ }.bind(this); |
ext.backgroundPage.sendMessage({type: "get-selectors"}, function(response) |
{ |
@@ -569,14 +571,21 @@ |
checkLoaded(); |
}); |
- propertyFilters.load(function() |
+ this.propertyFilters.load(function() |
{ |
- CSSPropertyFiltersLoaded = true; |
+ propertyFiltersLoaded = true; |
checkLoaded(); |
}); |
- }; |
+ } |
+}; |
- updateStylesheet(); |
+if (document instanceof HTMLDocument) |
+{ |
+ checkSitekey(); |
+ wrapWebSocket(); |
+ |
+ var elemhide = new ElemHide(); |
+ elemhide.apply(); |
document.addEventListener("error", function(event) |
{ |
@@ -586,35 +595,7 @@ |
document.addEventListener("load", function(event) |
{ |
var element = event.target; |
- |
if (/^i?frame$/.test(element.localName)) |
checkCollapse(element); |
- |
- if (/\bChrome\//.test(navigator.userAgent)) |
- { |
- var contentDocument = getContentDocument(element); |
- if (contentDocument) |
- { |
- var contentWindow = contentDocument.defaultView; |
- if (contentDocument instanceof contentWindow.HTMLDocument) |
- { |
- // Prior to Chrome 37, content scripts cannot run in |
- // dynamically created frames. Also on Chrome 37-40 |
- // document_start content scripts (like this one) don't |
- // run either in those frames due to https://crbug.com/416907. |
- // So we have to apply element hiding from the parent frame. |
- if (!("init" in contentWindow)) |
- init(contentDocument); |
- } |
- } |
- } |
}, true); |
- |
- return updateStylesheet; |
} |
- |
-if (document instanceof HTMLDocument) |
-{ |
- checkSitekey(); |
- window.updateStylesheet = init(document); |
-} |