Rietveld Code Review Tool
Help | Bug tracker | Discussion group | Source code

Unified Diff: include.preload.js

Issue 29452181: Noissue - Merge current tip to Edge bookmark (Closed)
Patch Set: Created May 30, 2017, 3:49 p.m.
Use n/p to move between diff chunks; N/P to move between comments.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « icons/detailed/abp-48.png ('k') | inject.preload.js » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: include.preload.js
===================================================================
--- a/include.preload.js
+++ b/include.preload.js
@@ -1,6 +1,6 @@
/*
* This file is part of Adblock Plus <https://adblockplus.org/>,
- * Copyright (C) 2006-2016 Eyeo GmbH
+ * Copyright (C) 2006-2017 eyeo GmbH
*
* Adblock Plus is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 3 as
@@ -15,19 +15,24 @@
* along with Adblock Plus. If not, see <http://www.gnu.org/licenses/>.
*/
+/* globals ElemHideEmulation, splitSelector */
+
"use strict";
-const typeMap = {
- "img": "IMAGE",
- "input": "IMAGE",
- "picture": "IMAGE",
- "audio": "MEDIA",
- "video": "MEDIA",
- "frame": "SUBDOCUMENT",
- "iframe": "SUBDOCUMENT",
- "object": "OBJECT",
- "embed": "OBJECT"
-};
+// This variable is also used by our other content scripts.
+let elemhide;
+
+const typeMap = new Map([
+ ["img", "IMAGE"],
+ ["input", "IMAGE"],
+ ["picture", "IMAGE"],
+ ["audio", "MEDIA"],
+ ["video", "MEDIA"],
+ ["frame", "SUBDOCUMENT"],
+ ["iframe", "SUBDOCUMENT"],
+ ["object", "OBJECT"],
+ ["embed", "OBJECT"]
+]);
function getURLsFromObjectElement(element)
{
@@ -41,9 +46,9 @@
continue;
let name = child.getAttribute("name");
- if (name != "movie" && // Adobe Flash
+ if (name != "movie" && // Adobe Flash
name != "source" && // Silverlight
- name != "src" && // Real Media + Quicktime
+ name != "src" && // Real Media + Quicktime
name != "FileName") // Windows Media
continue;
@@ -84,7 +89,7 @@
for (let child of element.children)
{
if (child.localName == "source" || child.localName == "track")
- urls.push.apply(urls, getURLsFromAttributes(child));
+ urls.push(...getURLsFromAttributes(child));
}
if (element.poster)
@@ -124,7 +129,7 @@
function checkCollapse(element)
{
- let mediatype = typeMap[element.localName];
+ let mediatype = typeMap.get(element.localName);
if (!mediatype)
return;
@@ -135,8 +140,8 @@
ext.backgroundPage.sendMessage(
{
type: "filters.collapse",
- urls: urls,
- mediatype: mediatype,
+ urls,
+ mediatype,
baseURL: document.location.href
},
@@ -179,25 +184,11 @@
ext.backgroundPage.sendMessage({type: "filters.addKey", token: attr});
}
-function getContentDocument(element)
+function ElementHidingTracer()
{
- try
- {
- return element.contentDocument;
- }
- catch (e)
- {
- return null;
- }
-}
-
-function ElementHidingTracer(selectors)
-{
- this.selectors = selectors;
-
+ this.selectors = [];
this.changedNodes = [];
this.timeout = null;
-
this.observer = new MutationObserver(this.observe.bind(this));
this.trace = this.trace.bind(this);
@@ -207,46 +198,60 @@
this.trace();
}
ElementHidingTracer.prototype = {
- checkNodes(nodes)
+ addSelectors(selectors, filters)
{
- let matchedSelectors = [];
+ let pairs = selectors.map((sel, i) => [sel, filters && filters[i]]);
- // Find all selectors that match any hidden element inside the given nodes.
- for (let selector of this.selectors)
+ if (document.readyState != "loading")
+ this.checkNodes([document], pairs);
+
+ this.selectors.push(...pairs);
+ },
+
+ checkNodes(nodes, pairs)
+ {
+ let selectors = [];
+ let filters = [];
+
+ for (let [selector, filter] of pairs)
{
- for (let node of nodes)
+ nodes: for (let node of nodes)
{
- let elements = node.querySelectorAll(selector);
- let matched = false;
-
- for (let element of elements)
+ for (let element of node.querySelectorAll(selector))
{
// Only consider selectors that actually have an effect on the
// computed styles, and aren't overridden by rules with higher
// priority, or haven't been circumvented in a different way.
if (getComputedStyle(element).display == "none")
{
- matchedSelectors.push(selector);
- matched = true;
- break;
+ // For regular element hiding, we don't know the exact filter,
+ // but the background page can find it with the given selector.
+ // In case of element hiding emulation, the generated selector
+ // we got here is different from the selector part of the filter,
+ // but in this case we can send the whole filter text instead.
+ if (filter)
+ filters.push(filter);
+ else
+ selectors.push(selector);
+
+ break nodes;
}
}
-
- if (matched)
- break;
}
}
- if (matchedSelectors.length > 0)
+ if (selectors.length > 0 || filters.length > 0)
+ {
ext.backgroundPage.sendMessage({
type: "devtools.traceElemHide",
- selectors: matchedSelectors
+ selectors, filters
});
+ }
},
onTimeout()
{
- this.checkNodes(this.changedNodes);
+ this.checkNodes(this.changedNodes, this.selectors);
this.changedNodes = [];
this.timeout = null;
},
@@ -308,7 +313,7 @@
trace()
{
- this.checkNodes([document]);
+ this.checkNodes([document], this.selectors);
this.observer.observe(
document,
@@ -328,98 +333,6 @@
}
};
-function runInPageContext(fn, arg)
-{
- let script = document.createElement("script");
- script.type = "application/javascript";
- script.async = false;
- script.textContent = "(" + fn + ")(" + JSON.stringify(arg) + ");";
- document.documentElement.appendChild(script);
- document.documentElement.removeChild(script);
-}
-
-// Chrome doesn't allow us to intercept WebSockets[1], and therefore
-// some ad networks are misusing them as a way to serve adverts and circumvent
-// 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()
-{
- let eventName = "abpws-" + Math.random().toString(36).substr(2);
-
- document.addEventListener(eventName, event =>
- {
- ext.backgroundPage.sendMessage({
- type: "request.websocket",
- url: event.detail.url
- }, block =>
- {
- document.dispatchEvent(
- new CustomEvent(eventName + "-" + event.detail.url, {detail: block})
- );
- });
- });
-
- runInPageContext(eventName =>
- {
- // As far as possible we must track everything we use that could be
- // sabotaged by the website later in order to circumvent us.
- let RealWebSocket = WebSocket;
- let closeWebSocket = Function.prototype.call.bind(RealWebSocket.prototype.close);
- let addEventListener = document.addEventListener.bind(document);
- let removeEventListener = document.removeEventListener.bind(document);
- let dispatchEvent = document.dispatchEvent.bind(document);
- let CustomEvent = window.CustomEvent;
-
- function checkRequest(url, callback)
- {
- let incomingEventName = eventName + "-" + url;
- function listener(event)
- {
- callback(event.detail);
- removeEventListener(incomingEventName, listener);
- }
- addEventListener(incomingEventName, listener);
-
- dispatchEvent(new CustomEvent(eventName, {
- detail: {url: url}
- }));
- }
-
- function WrappedWebSocket(url)
- {
- // Throw correct exceptions if the constructor is used improperly.
- if (!(this instanceof WrappedWebSocket)) return RealWebSocket();
- if (arguments.length < 1) return new RealWebSocket();
-
- let websocket;
- if (arguments.length == 1)
- websocket = new RealWebSocket(url);
- else
- websocket = new RealWebSocket(url, arguments[1]);
-
- checkRequest(websocket.url, blocked =>
- {
- if (blocked)
- closeWebSocket(websocket);
- });
-
- return websocket;
- }
- WrappedWebSocket.prototype = RealWebSocket.prototype;
- WebSocket = WrappedWebSocket.bind();
- Object.defineProperties(WebSocket, {
- CONNECTING: {value: RealWebSocket.CONNECTING, enumerable: true},
- OPEN: {value: RealWebSocket.OPEN, enumerable: true},
- CLOSING: {value: RealWebSocket.CLOSING, enumerable: true},
- CLOSED: {value: RealWebSocket.CLOSED, enumerable: true},
- prototype: {value: RealWebSocket.prototype}
- });
-
- RealWebSocket.prototype.constructor = WebSocket;
- }, eventName);
-}
-
function ElemHide()
{
this.shadow = this.createShadowTree();
@@ -462,31 +375,10 @@
document.documentElement.createShadowRoot();
shadow.appendChild(document.createElement("shadow"));
- // Stop the website from messing with our shadow root (#4191, #4298).
- if ("shadowRoot" in Element.prototype)
- {
- runInPageContext(() =>
- {
- let ourShadowRoot = document.documentElement.shadowRoot;
- if (!ourShadowRoot)
- return;
- let desc = Object.getOwnPropertyDescriptor(Element.prototype, "shadowRoot");
- let shadowRoot = Function.prototype.call.bind(desc.get);
-
- Object.defineProperty(Element.prototype, "shadowRoot", {
- configurable: true, enumerable: true, get()
- {
- let shadow = shadowRoot(this);
- return shadow == ourShadowRoot ? null : shadow;
- }
- });
- }, null);
- }
-
return shadow;
},
- addSelectors(selectors)
+ addSelectors(selectors, filters)
{
if (selectors.length == 0)
return;
@@ -498,8 +390,8 @@
// <html> element. If we have injected a style element before that
// has been removed (the sheet property is null), create a new one.
this.style = document.createElement("style");
- (this.shadow || document.head
- || document.documentElement).appendChild(this.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.
@@ -512,16 +404,19 @@
// If using shadow DOM, we have to add the ::content pseudo-element
// before each selector, in order to match elements within the
// insertion point.
+ let preparedSelectors = [];
if (this.shadow)
{
- let preparedSelectors = [];
for (let selector of selectors)
{
let subSelectors = splitSelector(selector);
for (let subSelector of subSelectors)
preparedSelectors.push("::content " + subSelector);
}
- selectors = preparedSelectors;
+ }
+ else
+ {
+ preparedSelectors = selectors;
}
// Safari only allows 8192 primitive selectors to be injected at once[1], we
@@ -529,24 +424,23 @@
// (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 (let i = 0; i < selectors.length; i += this.selectorGroupSize)
+ for (let i = 0; i < preparedSelectors.length; i += this.selectorGroupSize)
{
- let selector = selectors.slice(i, i + this.selectorGroupSize).join(", ");
+ let selector = preparedSelectors.slice(
+ i, i + this.selectorGroupSize
+ ).join(", ");
this.style.sheet.insertRule(selector + "{display: none !important;}",
this.style.sheet.cssRules.length);
}
+
+ if (this.tracer)
+ this.tracer.addSelectors(selectors, filters);
},
apply()
{
- let selectors = null;
- let elemHideEmulationLoaded = false;
-
- let checkLoaded = function()
+ ext.backgroundPage.sendMessage({type: "get-selectors"}, response =>
{
- if (!selectors || !elemHideEmulationLoaded)
- return;
-
if (this.tracer)
this.tracer.disconnect();
this.tracer = null;
@@ -555,23 +449,11 @@
this.style.parentElement.removeChild(this.style);
this.style = null;
- this.addSelectors(selectors.selectors);
+ if (response.trace)
+ this.tracer = new ElementHidingTracer();
+
+ this.addSelectors(response.selectors);
this.elemHideEmulation.apply();
-
- if (selectors.trace)
- this.tracer = new ElementHidingTracer(selectors.selectors);
- }.bind(this);
-
- ext.backgroundPage.sendMessage({type: "get-selectors"}, response =>
- {
- selectors = response;
- checkLoaded();
- });
-
- this.elemHideEmulation.load(() =>
- {
- elemHideEmulationLoaded = true;
- checkLoaded();
});
}
};
@@ -579,11 +461,8 @@
if (document instanceof HTMLDocument)
{
checkSitekey();
- wrapWebSocket();
- // This variable is also used by our other content scripts, outside of the
- // current scope.
- var elemhide = new ElemHide();
+ elemhide = new ElemHide();
elemhide.apply();
document.addEventListener("error", event =>
« no previous file with comments | « icons/detailed/abp-48.png ('k') | inject.preload.js » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld