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

Delta Between Two Patch Sets: include.preload.js

Issue 29401596: Issue 5094 - Implement support for :has() in chrome extension (Closed) Base URL: https://hg.adblockplus.org/adblockpluschrome/
Left Patch Set: Rebased (conflicts resolved) Created April 7, 2017, 12:24 p.m.
Right Patch Set: Update dependencies to the latest. Created June 19, 2017, 2:12 p.m.
Left:
Right:
Use n/p to move between diff chunks; N/P to move between comments.
Jump to:
Left: Side by side diff | Download
Right: Side by side diff | Download
« no previous file with change/comment | « dependencies ('k') | lib/filterValidation.js » ('j') | no next file with change/comment »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
LEFTRIGHT
1 /* 1 /*
2 * This file is part of Adblock Plus <https://adblockplus.org/>, 2 * This file is part of Adblock Plus <https://adblockplus.org/>,
3 * Copyright (C) 2006-2017 eyeo GmbH 3 * Copyright (C) 2006-2017 eyeo GmbH
4 * 4 *
5 * Adblock Plus is free software: you can redistribute it and/or modify 5 * Adblock Plus is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License version 3 as 6 * it under the terms of the GNU General Public License version 3 as
7 * published by the Free Software Foundation. 7 * published by the Free Software Foundation.
8 * 8 *
9 * Adblock Plus is distributed in the hope that it will be useful, 9 * Adblock Plus is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of 10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
(...skipping 111 matching lines...) Expand 10 before | Expand all | Expand 10 after
122 { 122 {
123 if (/^(?!https?:)[\w-]+:/i.test(urls[i])) 123 if (/^(?!https?:)[\w-]+:/i.test(urls[i]))
124 urls.splice(i--, 1); 124 urls.splice(i--, 1);
125 } 125 }
126 126
127 return urls; 127 return urls;
128 } 128 }
129 129
130 function hideElement(element) 130 function hideElement(element)
131 { 131 {
132 function doHide(element) 132 function doHide(el)
133 { 133 {
134 let propertyName = "display"; 134 let propertyName = "display";
135 let propertyValue = "none"; 135 let propertyValue = "none";
136 if (element.localName == "frame") 136 if (el.localName == "frame")
137 { 137 {
138 propertyName = "visibility"; 138 propertyName = "visibility";
139 propertyValue = "hidden"; 139 propertyValue = "hidden";
140 } 140 }
141 141
142 if (element.style.getPropertyValue(propertyName) != propertyValue || 142 if (el.style.getPropertyValue(propertyName) != propertyValue ||
143 element.style.getPropertyPriority(propertyName) != "important") 143 el.style.getPropertyPriority(propertyName) != "important")
144 element.style.setProperty(propertyName, propertyValue, "important"); 144 el.style.setProperty(propertyName, propertyValue, "important");
145 } 145 }
146 146
147 doHide(element); 147 doHide(element);
148 148
149 new MutationObserver(doHide).observe( 149 new MutationObserver(doHide).observe(
150 element, { 150 element, {
151 attributes: true, 151 attributes: true,
152 attributeFilter: ["style"] 152 attributeFilter: ["style"]
153 } 153 }
154 ); 154 );
(...skipping 49 matching lines...) Expand 10 before | Expand all | Expand 10 after
204 } 204 }
205 ElementHidingTracer.prototype = { 205 ElementHidingTracer.prototype = {
206 addSelectors(selectors, filters) 206 addSelectors(selectors, filters)
207 { 207 {
208 let pairs = selectors.map((sel, i) => [sel, filters && filters[i]]); 208 let pairs = selectors.map((sel, i) => [sel, filters && filters[i]]);
209 209
210 if (document.readyState != "loading") 210 if (document.readyState != "loading")
211 this.checkNodes([document], pairs); 211 this.checkNodes([document], pairs);
212 212
213 this.selectors.push(...pairs); 213 this.selectors.push(...pairs);
214 },
215
216 reportFilters(selectors, filters)
217 {
218 let matchedSelectors = [];
219 for (let filter of filters)
220 matchedSelectors.push(filter.replace(/^.*?##/, ""));
Sebastian Noack 2017/04/07 13:14:47 This is essentially dead code. Now after rebasing
hub 2017/04/07 15:50:01 ah ok. done now.
221
222 ext.backgroundPage.sendMessage({
223 type: "devtools.traceElemHide",
224 selectors, filters
225 });
226 }, 214 },
227 215
228 checkNodes(nodes, pairs) 216 checkNodes(nodes, pairs)
229 { 217 {
230 let selectors = []; 218 let selectors = [];
231 let filters = []; 219 let filters = [];
232 220
233 for (let [selector, filter] of pairs) 221 for (let [selector, filter] of pairs)
234 { 222 {
235 nodes: for (let node of nodes) 223 nodes: for (let node of nodes)
(...skipping 15 matching lines...) Expand all
251 else 239 else
252 selectors.push(selector); 240 selectors.push(selector);
253 241
254 break nodes; 242 break nodes;
255 } 243 }
256 } 244 }
257 } 245 }
258 } 246 }
259 247
260 if (selectors.length > 0 || filters.length > 0) 248 if (selectors.length > 0 || filters.length > 0)
261 this.reportFilters(selectors, filters); 249 {
250 ext.backgroundPage.sendMessage({
251 type: "devtools.traceElemHide",
252 selectors, filters
253 });
254 }
262 }, 255 },
263 256
264 onTimeout() 257 onTimeout()
265 { 258 {
266 this.checkNodes(this.changedNodes, this.selectors); 259 this.checkNodes(this.changedNodes, this.selectors);
267 this.changedNodes = []; 260 this.changedNodes = [];
268 this.timeout = null; 261 this.timeout = null;
269 }, 262 },
270 263
271 observe(mutations) 264 observe(mutations)
(...skipping 66 matching lines...) Expand 10 before | Expand all | Expand 10 after
338 }, 331 },
339 332
340 disconnect() 333 disconnect()
341 { 334 {
342 document.removeEventListener("DOMContentLoaded", this.trace); 335 document.removeEventListener("DOMContentLoaded", this.trace);
343 this.observer.disconnect(); 336 this.observer.disconnect();
344 clearTimeout(this.timeout); 337 clearTimeout(this.timeout);
345 } 338 }
346 }; 339 };
347 340
348 function runInPageContext(fn, arg)
349 {
350 let script = document.createElement("script");
351 script.type = "application/javascript";
352 script.async = false;
353 script.textContent = "(" + fn + ")(" + JSON.stringify(arg) + ");";
354 document.documentElement.appendChild(script);
355 document.documentElement.removeChild(script);
356 }
357
358 // Before Chrome 58 the webRequest API didn't allow us to intercept
359 // WebSockets[1], and therefore some ad networks are misusing them as a way to
360 // serve adverts and circumvent us. As a workaround we wrap WebSocket,
361 // preventing blocked WebSocket connections from being opened.
362 // [1] - https://bugs.chromium.org/p/chromium/issues/detail?id=129353
363 function wrapWebSocket()
364 {
365 let randomEventName = "abpws-" + Math.random().toString(36).substr(2);
366
367 document.addEventListener(randomEventName, event =>
368 {
369 ext.backgroundPage.sendMessage({
370 type: "request.websocket",
371 url: event.detail.url
372 }, block =>
373 {
374 document.dispatchEvent(new CustomEvent(
375 randomEventName + "-" + event.detail.url, {detail: block}
376 ));
377 });
378 });
379
380 runInPageContext(eventName =>
381 {
382 // As far as possible we must track everything we use that could be
383 // sabotaged by the website later in order to circumvent us.
384 let RealWebSocket = WebSocket;
385 let RealCustomEvent = window.CustomEvent;
386 let closeWebSocket = Function.prototype.call.bind(
387 RealWebSocket.prototype.close
388 );
389 let addEventListener = document.addEventListener.bind(document);
390 let removeEventListener = document.removeEventListener.bind(document);
391 let dispatchEvent = document.dispatchEvent.bind(document);
392
393 function checkRequest(url, callback)
394 {
395 let incomingEventName = eventName + "-" + url;
396 function listener(event)
397 {
398 callback(event.detail);
399 removeEventListener(incomingEventName, listener);
400 }
401 addEventListener(incomingEventName, listener);
402
403 dispatchEvent(new RealCustomEvent(eventName, {detail: {url}}));
404 }
405
406 function WrappedWebSocket(url, ...args)
407 {
408 // Throw correct exceptions if the constructor is used improperly.
409 if (!(this instanceof WrappedWebSocket)) return RealWebSocket();
410 if (arguments.length < 1) return new RealWebSocket();
411
412 let websocket = new RealWebSocket(url, ...args);
413
414 checkRequest(websocket.url, blocked =>
415 {
416 if (blocked)
417 closeWebSocket(websocket);
418 });
419
420 return websocket;
421 }
422 WrappedWebSocket.prototype = RealWebSocket.prototype;
423 window.WebSocket = WrappedWebSocket.bind();
424 Object.defineProperties(WebSocket, {
425 CONNECTING: {value: RealWebSocket.CONNECTING, enumerable: true},
426 OPEN: {value: RealWebSocket.OPEN, enumerable: true},
427 CLOSING: {value: RealWebSocket.CLOSING, enumerable: true},
428 CLOSED: {value: RealWebSocket.CLOSED, enumerable: true},
429 prototype: {value: RealWebSocket.prototype}
430 });
431
432 RealWebSocket.prototype.constructor = WebSocket;
433 }, randomEventName);
434 }
435
436 function ElemHide() 341 function ElemHide()
437 { 342 {
438 this.shadow = this.createShadowTree(); 343 this.shadow = this.createShadowTree();
439 this.style = null; 344 this.style = null;
440 this.tracer = null; 345 this.tracer = null;
441 346
442 this.elemHideEmulation = new ElemHideEmulation( 347 this.elemHideEmulation = new ElemHideEmulation(
443 window, 348 window,
444 callback => 349 callback =>
445 { 350 {
(...skipping 23 matching lines...) Expand all
469 if (/\.(?:google|blogger)\.com$/.test(document.domain)) 374 if (/\.(?:google|blogger)\.com$/.test(document.domain))
470 return null; 375 return null;
471 376
472 // Finally since some users have both AdBlock and Adblock Plus installed we 377 // Finally since some users have both AdBlock and Adblock Plus installed we
473 // have to consider how the two extensions interact. For example we want to 378 // have to consider how the two extensions interact. For example we want to
474 // avoid creating the shadowRoot twice. 379 // avoid creating the shadowRoot twice.
475 let shadow = document.documentElement.shadowRoot || 380 let shadow = document.documentElement.shadowRoot ||
476 document.documentElement.createShadowRoot(); 381 document.documentElement.createShadowRoot();
477 shadow.appendChild(document.createElement("shadow")); 382 shadow.appendChild(document.createElement("shadow"));
478 383
479 // Stop the website from messing with our shadow root (#4191, #4298).
480 if ("shadowRoot" in Element.prototype)
481 {
482 runInPageContext(() =>
483 {
484 let ourShadowRoot = document.documentElement.shadowRoot;
485 if (!ourShadowRoot)
486 return;
487 let desc = Object.getOwnPropertyDescriptor(Element.prototype,
488 "shadowRoot");
489 let shadowRoot = Function.prototype.call.bind(desc.get);
490
491 Object.defineProperty(Element.prototype, "shadowRoot", {
492 configurable: true, enumerable: true, get()
493 {
494 let thisShadow = shadowRoot(this);
495 return thisShadow == ourShadowRoot ? null : thisShadow;
496 }
497 });
498 }, null);
499 }
500
501 return shadow; 384 return shadow;
502 }, 385 },
503 386
504 addSelectors(selectors, filters) 387 addSelectors(selectors, filters)
505 { 388 {
506 if (selectors.length == 0) 389 if (selectors.length == 0)
507 return; 390 return;
508 391
509 if (!this.style) 392 if (!this.style)
510 { 393 {
(...skipping 49 matching lines...) Expand 10 before | Expand all | Expand 10 after
560 this.tracer.addSelectors(selectors, filters); 443 this.tracer.addSelectors(selectors, filters);
561 }, 444 },
562 445
563 hideElements(elements, filters) 446 hideElements(elements, filters)
564 { 447 {
565 for (let element of elements) 448 for (let element of elements)
566 hideElement(element); 449 hideElement(element);
567 450
568 if (this.tracer) 451 if (this.tracer)
569 { 452 {
570 let selectors = []; 453 ext.backgroundPage.sendMessage({
571 for (let filter of filters) 454 type: "devtools.traceElemHide",
572 selectors.push(filter.replace(/^.*?##/, "")); 455 selectors: [],
573 this.tracer.reportFilters(selectors, filters); 456 filters
457 });
574 } 458 }
575 }, 459 },
576 460
577 apply() 461 apply()
578 { 462 {
579 ext.backgroundPage.sendMessage({type: "get-selectors"}, response => 463 ext.backgroundPage.sendMessage({type: "get-selectors"}, response =>
580 { 464 {
581 if (this.tracer) 465 if (this.tracer)
582 this.tracer.disconnect(); 466 this.tracer.disconnect();
583 this.tracer = null; 467 this.tracer = null;
584 468
585 if (this.style && this.style.parentElement) 469 if (this.style && this.style.parentElement)
586 this.style.parentElement.removeChild(this.style); 470 this.style.parentElement.removeChild(this.style);
587 this.style = null; 471 this.style = null;
588 472
589 if (response.trace) 473 if (response.trace)
590 this.tracer = new ElementHidingTracer(); 474 this.tracer = new ElementHidingTracer();
591 475
592 this.addSelectors(response.selectors); 476 this.addSelectors(response.selectors);
593 this.elemHideEmulation.apply(); 477 this.elemHideEmulation.apply();
594 }); 478 });
595 } 479 }
596 }; 480 };
597 481
598 if (document instanceof HTMLDocument) 482 if (document instanceof HTMLDocument)
599 { 483 {
600 checkSitekey(); 484 checkSitekey();
601 wrapWebSocket();
602 485
603 elemhide = new ElemHide(); 486 elemhide = new ElemHide();
604 elemhide.apply(); 487 elemhide.apply();
605 488
606 document.addEventListener("error", event => 489 document.addEventListener("error", event =>
607 { 490 {
608 checkCollapse(event.target); 491 checkCollapse(event.target);
609 }, true); 492 }, true);
610 493
611 document.addEventListener("load", event => 494 document.addEventListener("load", event =>
612 { 495 {
613 let element = event.target; 496 let element = event.target;
614 if (/^i?frame$/.test(element.localName)) 497 if (/^i?frame$/.test(element.localName))
615 checkCollapse(element); 498 checkCollapse(element);
616 }, true); 499 }, true);
617 } 500 }
LEFTRIGHT

Powered by Google App Engine
This is Rietveld