| 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 runInDocument(fn, arg) |
|
kzar
2016/08/30 13:14:09
Since this no longer takes a document argument per
Sebastian Noack
2016/09/09 14:39:50
How about runInPageContext()? No preference though
kzar
2016/09/13 12:30:01
Sounds good to me.
|
| { |
| 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) |
| + runInDocument(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(); |
|
kzar
2016/08/30 13:14:09
I don't mind too much but perhaps this.shadow shou
Sebastian Noack
2016/09/09 14:39:51
Why adding more code just for the matter of using
kzar
2016/09/13 12:30:01
Acknowledged.
|
| + 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 to don't mess with web pages that rely |
|
kzar
2016/08/30 13:14:09
Nit: Should be "...if available as to not mess wit
Sebastian Noack
2016/09/09 14:39:51
Done.
|
| + // 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(); |
|
kzar
2016/08/30 13:14:09
If we don't turn shadow into a getter perhaps we s
Sebastian Noack
2016/09/09 14:39:50
We still need to indicate whether we are using sha
kzar
2016/09/13 12:30:01
Acknowledged.
|
| 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() |
| + runInDocument(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); |
| -} |