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

Side by Side Diff: include.preload.js

Issue 29374674: Issue 4864 - Start using ESLint for adblockpluschrome (Closed)
Patch Set: Fixed typo with shadowRoot getter Created March 14, 2017, 10:28 a.m.
Left:
Right:
Use n/p to move between diff chunks; N/P to move between comments.
Jump to:
View unified diff | Download patch
OLDNEW
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-2016 Eyeo GmbH 3 * Copyright (C) 2006-2016 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
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details. 12 * GNU General Public License for more details.
13 * 13 *
14 * You should have received a copy of the GNU General Public License 14 * You should have received a copy of the GNU General Public License
15 * along with Adblock Plus. If not, see <http://www.gnu.org/licenses/>. 15 * along with Adblock Plus. If not, see <http://www.gnu.org/licenses/>.
16 */ 16 */
17 17
18 /* globals ElemHideEmulation, splitSelector */
19
18 "use strict"; 20 "use strict";
19 21
20 const typeMap = { 22 // This variable is also used by our other content scripts.
21 "img": "IMAGE", 23 let elemhide;
22 "input": "IMAGE", 24
23 "picture": "IMAGE", 25 const typeMap = new Map([
24 "audio": "MEDIA", 26 ["img", "IMAGE"],
25 "video": "MEDIA", 27 ["input", "IMAGE"],
26 "frame": "SUBDOCUMENT", 28 ["picture", "IMAGE"],
27 "iframe": "SUBDOCUMENT", 29 ["audio", "MEDIA"],
28 "object": "OBJECT", 30 ["video", "MEDIA"],
29 "embed": "OBJECT" 31 ["frame", "SUBDOCUMENT"],
30 }; 32 ["iframe", "SUBDOCUMENT"],
33 ["object", "OBJECT"],
34 ["embed", "OBJECT"]
35 ]);
31 36
32 function getURLsFromObjectElement(element) 37 function getURLsFromObjectElement(element)
33 { 38 {
34 let url = element.getAttribute("data"); 39 let url = element.getAttribute("data");
35 if (url) 40 if (url)
36 return [url]; 41 return [url];
37 42
38 for (let child of element.children) 43 for (let child of element.children)
39 { 44 {
40 if (child.localName != "param") 45 if (child.localName != "param")
41 continue; 46 continue;
42 47
43 let name = child.getAttribute("name"); 48 let name = child.getAttribute("name");
44 if (name != "movie" && // Adobe Flash 49 if (name != "movie" && // Adobe Flash
45 name != "source" && // Silverlight 50 name != "source" && // Silverlight
46 name != "src" && // Real Media + Quicktime 51 name != "src" && // Real Media + Quicktime
47 name != "FileName") // Windows Media 52 name != "FileName") // Windows Media
48 continue; 53 continue;
49 54
50 let value = child.getAttribute("value"); 55 let value = child.getAttribute("value");
51 if (!value) 56 if (!value)
52 continue; 57 continue;
53 58
54 return [value]; 59 return [value];
55 } 60 }
56 61
(...skipping 20 matching lines...) Expand all
77 return urls; 82 return urls;
78 } 83 }
79 84
80 function getURLsFromMediaElement(element) 85 function getURLsFromMediaElement(element)
81 { 86 {
82 let urls = getURLsFromAttributes(element); 87 let urls = getURLsFromAttributes(element);
83 88
84 for (let child of element.children) 89 for (let child of element.children)
85 { 90 {
86 if (child.localName == "source" || child.localName == "track") 91 if (child.localName == "source" || child.localName == "track")
87 urls.push.apply(urls, getURLsFromAttributes(child)); 92 urls.push(...getURLsFromAttributes(child));
88 } 93 }
89 94
90 if (element.poster) 95 if (element.poster)
91 urls.push(element.poster); 96 urls.push(element.poster);
92 97
93 return urls; 98 return urls;
94 } 99 }
95 100
96 function getURLsFromElement(element) 101 function getURLsFromElement(element)
97 { 102 {
(...skipping 19 matching lines...) Expand all
117 { 122 {
118 if (/^(?!https?:)[\w-]+:/i.test(urls[i])) 123 if (/^(?!https?:)[\w-]+:/i.test(urls[i]))
119 urls.splice(i--, 1); 124 urls.splice(i--, 1);
120 } 125 }
121 126
122 return urls; 127 return urls;
123 } 128 }
124 129
125 function checkCollapse(element) 130 function checkCollapse(element)
126 { 131 {
127 let mediatype = typeMap[element.localName]; 132 let mediatype = typeMap.get(element.localName);
128 if (!mediatype) 133 if (!mediatype)
129 return; 134 return;
130 135
131 let urls = getURLsFromElement(element); 136 let urls = getURLsFromElement(element);
132 if (urls.length == 0) 137 if (urls.length == 0)
133 return; 138 return;
134 139
135 ext.backgroundPage.sendMessage( 140 ext.backgroundPage.sendMessage(
136 { 141 {
137 type: "filters.collapse", 142 type: "filters.collapse",
138 urls: urls, 143 urls,
139 mediatype: mediatype, 144 mediatype,
140 baseURL: document.location.href 145 baseURL: document.location.href
141 }, 146 },
142 147
143 collapse => 148 collapse =>
144 { 149 {
145 function collapseElement() 150 function collapseElement()
146 { 151 {
147 let propertyName = "display"; 152 let propertyName = "display";
148 let propertyValue = "none"; 153 let propertyValue = "none";
149 if (element.localName == "frame") 154 if (element.localName == "frame")
(...skipping 22 matching lines...) Expand all
172 ); 177 );
173 } 178 }
174 179
175 function checkSitekey() 180 function checkSitekey()
176 { 181 {
177 let attr = document.documentElement.getAttribute("data-adblockkey"); 182 let attr = document.documentElement.getAttribute("data-adblockkey");
178 if (attr) 183 if (attr)
179 ext.backgroundPage.sendMessage({type: "filters.addKey", token: attr}); 184 ext.backgroundPage.sendMessage({type: "filters.addKey", token: attr});
180 } 185 }
181 186
182 function getContentDocument(element)
183 {
184 try
185 {
186 return element.contentDocument;
187 }
188 catch (e)
189 {
190 return null;
191 }
192 }
193
194 function ElementHidingTracer() 187 function ElementHidingTracer()
195 { 188 {
196 this.selectors = []; 189 this.selectors = [];
197 this.filters = []; 190 this.filters = [];
198 191
199 this.changedNodes = []; 192 this.changedNodes = [];
200 this.timeout = null; 193 this.timeout = null;
201 194
202 this.observer = new MutationObserver(this.observe.bind(this)); 195 this.observer = new MutationObserver(this.observe.bind(this));
203 this.trace = this.trace.bind(this); 196 this.trace = this.trace.bind(this);
(...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after
235 if (getComputedStyle(element).display == "none") 228 if (getComputedStyle(element).display == "none")
236 { 229 {
237 matchedSelectors.push(filters[i].replace(/^.*?##/, "")); 230 matchedSelectors.push(filters[i].replace(/^.*?##/, ""));
238 break nodes; 231 break nodes;
239 } 232 }
240 } 233 }
241 } 234 }
242 } 235 }
243 236
244 if (matchedSelectors.length > 0) 237 if (matchedSelectors.length > 0)
238 {
245 ext.backgroundPage.sendMessage({ 239 ext.backgroundPage.sendMessage({
246 type: "devtools.traceElemHide", 240 type: "devtools.traceElemHide",
247 selectors: matchedSelectors 241 selectors: matchedSelectors
248 }); 242 });
243 }
249 }, 244 },
250 245
251 onTimeout() 246 onTimeout()
252 { 247 {
253 this.checkNodes(this.changedNodes, this.selectors, this.filters); 248 this.checkNodes(this.changedNodes, this.selectors, this.filters);
254 this.changedNodes = []; 249 this.changedNodes = [];
255 this.timeout = null; 250 this.timeout = null;
256 }, 251 },
257 252
258 observe(mutations) 253 observe(mutations)
(...skipping 83 matching lines...) Expand 10 before | Expand all | Expand 10 after
342 document.documentElement.removeChild(script); 337 document.documentElement.removeChild(script);
343 } 338 }
344 339
345 // Chrome doesn't allow us to intercept WebSockets[1], and therefore 340 // Chrome doesn't allow us to intercept WebSockets[1], and therefore
346 // some ad networks are misusing them as a way to serve adverts and circumvent 341 // some ad networks are misusing them as a way to serve adverts and circumvent
347 // us. As a workaround we wrap WebSocket, preventing blocked WebSocket 342 // us. As a workaround we wrap WebSocket, preventing blocked WebSocket
348 // connections from being opened. 343 // connections from being opened.
349 // [1] - https://bugs.chromium.org/p/chromium/issues/detail?id=129353 344 // [1] - https://bugs.chromium.org/p/chromium/issues/detail?id=129353
350 function wrapWebSocket() 345 function wrapWebSocket()
351 { 346 {
352 let eventName = "abpws-" + Math.random().toString(36).substr(2); 347 let randomEventName = "abpws-" + Math.random().toString(36).substr(2);
353 348
354 document.addEventListener(eventName, event => 349 document.addEventListener(randomEventName, event =>
355 { 350 {
356 ext.backgroundPage.sendMessage({ 351 ext.backgroundPage.sendMessage({
357 type: "request.websocket", 352 type: "request.websocket",
358 url: event.detail.url 353 url: event.detail.url
359 }, block => 354 }, block =>
360 { 355 {
361 document.dispatchEvent( 356 document.dispatchEvent(new CustomEvent(
362 new CustomEvent(eventName + "-" + event.detail.url, {detail: block}) 357 randomEventName + "-" + event.detail.url, {detail: block}
363 ); 358 ));
364 }); 359 });
365 }); 360 });
366 361
367 runInPageContext(eventName => 362 runInPageContext(eventName =>
368 { 363 {
369 // As far as possible we must track everything we use that could be 364 // As far as possible we must track everything we use that could be
370 // sabotaged by the website later in order to circumvent us. 365 // sabotaged by the website later in order to circumvent us.
371 let RealWebSocket = WebSocket; 366 let RealWebSocket = WebSocket;
372 let closeWebSocket = Function.prototype.call.bind(RealWebSocket.prototype.cl ose); 367 let RealCustomEvent = window.CustomEvent;
368 let closeWebSocket = Function.prototype.call.bind(
369 RealWebSocket.prototype.close
370 );
373 let addEventListener = document.addEventListener.bind(document); 371 let addEventListener = document.addEventListener.bind(document);
374 let removeEventListener = document.removeEventListener.bind(document); 372 let removeEventListener = document.removeEventListener.bind(document);
375 let dispatchEvent = document.dispatchEvent.bind(document); 373 let dispatchEvent = document.dispatchEvent.bind(document);
376 let CustomEvent = window.CustomEvent;
377 374
378 function checkRequest(url, callback) 375 function checkRequest(url, callback)
379 { 376 {
380 let incomingEventName = eventName + "-" + url; 377 let incomingEventName = eventName + "-" + url;
381 function listener(event) 378 function listener(event)
382 { 379 {
383 callback(event.detail); 380 callback(event.detail);
384 removeEventListener(incomingEventName, listener); 381 removeEventListener(incomingEventName, listener);
385 } 382 }
386 addEventListener(incomingEventName, listener); 383 addEventListener(incomingEventName, listener);
387 384
388 dispatchEvent(new CustomEvent(eventName, { 385 dispatchEvent(new RealCustomEvent(eventName, {detail: {url}}));
389 detail: {url: url}
390 }));
391 } 386 }
392 387
393 function WrappedWebSocket(url) 388 function WrappedWebSocket(url, ...args)
394 { 389 {
395 // Throw correct exceptions if the constructor is used improperly. 390 // Throw correct exceptions if the constructor is used improperly.
396 if (!(this instanceof WrappedWebSocket)) return RealWebSocket(); 391 if (!(this instanceof WrappedWebSocket)) return RealWebSocket();
397 if (arguments.length < 1) return new RealWebSocket(); 392 if (arguments.length < 1) return new RealWebSocket();
398 393
399 let websocket; 394 let websocket;
400 if (arguments.length == 1) 395 if (arguments.length == 1)
401 websocket = new RealWebSocket(url); 396 websocket = new RealWebSocket(url);
402 else 397 else
403 websocket = new RealWebSocket(url, arguments[1]); 398 websocket = new RealWebSocket(url, args[0]);
Wladimir Palant 2017/03/14 13:03:24 From the look of it, this should really be: let
kzar 2017/03/15 04:57:45 Done.
404 399
405 checkRequest(websocket.url, blocked => 400 checkRequest(websocket.url, blocked =>
406 { 401 {
407 if (blocked) 402 if (blocked)
408 closeWebSocket(websocket); 403 closeWebSocket(websocket);
409 }); 404 });
410 405
411 return websocket; 406 return websocket;
412 } 407 }
413 WrappedWebSocket.prototype = RealWebSocket.prototype; 408 WrappedWebSocket.prototype = RealWebSocket.prototype;
414 WebSocket = WrappedWebSocket.bind(); 409 window.WebSocket = WrappedWebSocket.bind();
415 Object.defineProperties(WebSocket, { 410 Object.defineProperties(WebSocket, {
416 CONNECTING: {value: RealWebSocket.CONNECTING, enumerable: true}, 411 CONNECTING: {value: RealWebSocket.CONNECTING, enumerable: true},
417 OPEN: {value: RealWebSocket.OPEN, enumerable: true}, 412 OPEN: {value: RealWebSocket.OPEN, enumerable: true},
418 CLOSING: {value: RealWebSocket.CLOSING, enumerable: true}, 413 CLOSING: {value: RealWebSocket.CLOSING, enumerable: true},
419 CLOSED: {value: RealWebSocket.CLOSED, enumerable: true}, 414 CLOSED: {value: RealWebSocket.CLOSED, enumerable: true},
420 prototype: {value: RealWebSocket.prototype} 415 prototype: {value: RealWebSocket.prototype}
421 }); 416 });
422 417
423 RealWebSocket.prototype.constructor = WebSocket; 418 RealWebSocket.prototype.constructor = WebSocket;
424 }, eventName); 419 }, randomEventName);
425 } 420 }
426 421
427 function ElemHide() 422 function ElemHide()
428 { 423 {
429 this.shadow = this.createShadowTree(); 424 this.shadow = this.createShadowTree();
430 this.style = null; 425 this.style = null;
431 this.tracer = null; 426 this.tracer = null;
432 427
433 this.elemHideEmulation = new ElemHideEmulation( 428 this.elemHideEmulation = new ElemHideEmulation(
434 window, 429 window,
(...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after
467 shadow.appendChild(document.createElement("shadow")); 462 shadow.appendChild(document.createElement("shadow"));
468 463
469 // Stop the website from messing with our shadow root (#4191, #4298). 464 // Stop the website from messing with our shadow root (#4191, #4298).
470 if ("shadowRoot" in Element.prototype) 465 if ("shadowRoot" in Element.prototype)
471 { 466 {
472 runInPageContext(() => 467 runInPageContext(() =>
473 { 468 {
474 let ourShadowRoot = document.documentElement.shadowRoot; 469 let ourShadowRoot = document.documentElement.shadowRoot;
475 if (!ourShadowRoot) 470 if (!ourShadowRoot)
476 return; 471 return;
477 let desc = Object.getOwnPropertyDescriptor(Element.prototype, "shadowRoo t"); 472 let desc = Object.getOwnPropertyDescriptor(Element.prototype,
473 "shadowRoot");
478 let shadowRoot = Function.prototype.call.bind(desc.get); 474 let shadowRoot = Function.prototype.call.bind(desc.get);
479 475
480 Object.defineProperty(Element.prototype, "shadowRoot", { 476 Object.defineProperty(Element.prototype, "shadowRoot", {
481 configurable: true, enumerable: true, get() 477 configurable: true, enumerable: true, get()
482 { 478 {
483 let shadow = shadowRoot(this); 479 let thisShadow = shadowRoot(this);
484 return shadow == ourShadowRoot ? null : shadow; 480 return thisShadow == ourShadowRoot ? null : thisShadow;
485 } 481 }
486 }); 482 });
487 }, null); 483 }, null);
488 } 484 }
489 485
490 return shadow; 486 return shadow;
491 }, 487 },
492 488
493 addSelectors(selectors, filters) 489 addSelectors(selectors, filters)
494 { 490 {
495 if (selectors.length == 0) 491 if (selectors.length == 0)
496 return; 492 return;
497 493
498 if (!this.style) 494 if (!this.style)
499 { 495 {
500 // Create <style> element lazily, only if we add styles. Add it to 496 // Create <style> element lazily, only if we add styles. Add it to
501 // the shadow DOM if possible. Otherwise fallback to the <head> or 497 // the shadow DOM if possible. Otherwise fallback to the <head> or
502 // <html> element. If we have injected a style element before that 498 // <html> element. If we have injected a style element before that
503 // has been removed (the sheet property is null), create a new one. 499 // has been removed (the sheet property is null), create a new one.
504 this.style = document.createElement("style"); 500 this.style = document.createElement("style");
505 (this.shadow || document.head 501 (this.shadow || document.head ||
506 || document.documentElement).appendChild(this.style); 502 document.documentElement).appendChild(this.style);
Wladimir Palant 2017/03/14 13:03:24 Please don't use one-space indentation, it's too e
kzar 2017/03/15 04:57:46 How about this?
507 503
508 // It can happen that the frame already navigated to a different 504 // It can happen that the frame already navigated to a different
509 // document while we were waiting for the background page to respond. 505 // document while we were waiting for the background page to respond.
510 // In that case the sheet property will stay null, after addind the 506 // In that case the sheet property will stay null, after addind the
511 // <style> element to the shadow DOM. 507 // <style> element to the shadow DOM.
512 if (!this.style.sheet) 508 if (!this.style.sheet)
513 return; 509 return;
514 } 510 }
515 511
516 // If using shadow DOM, we have to add the ::content pseudo-element 512 // If using shadow DOM, we have to add the ::content pseudo-element
517 // before each selector, in order to match elements within the 513 // before each selector, in order to match elements within the
518 // insertion point. 514 // insertion point.
519 let preparedSelectors = []; 515 let preparedSelectors = [];
520 if (this.shadow) 516 if (this.shadow)
521 { 517 {
522 for (let selector of selectors) 518 for (let selector of selectors)
523 { 519 {
524 let subSelectors = splitSelector(selector); 520 let subSelectors = splitSelector(selector);
525 for (let subSelector of subSelectors) 521 for (let subSelector of subSelectors)
526 preparedSelectors.push("::content " + subSelector); 522 preparedSelectors.push("::content " + subSelector);
527 } 523 }
528 } 524 }
529 else 525 else
530 {
531 preparedSelectors = selectors; 526 preparedSelectors = selectors;
532 }
533 527
534 // Safari only allows 8192 primitive selectors to be injected at once[1], we 528 // Safari only allows 8192 primitive selectors to be injected at once[1], we
535 // therefore chunk the inserted selectors into groups of 200 to be safe. 529 // therefore chunk the inserted selectors into groups of 200 to be safe.
536 // (Chrome also has a limit, larger... but we're not certain exactly what it 530 // (Chrome also has a limit, larger... but we're not certain exactly what it
537 // is! Edge apparently has no such limit.) 531 // is! Edge apparently has no such limit.)
538 // [1] - https://github.com/WebKit/webkit/blob/1cb2227f6b2a1035f7bdc46e5ab69 debb75fc1de/Source/WebCore/css/RuleSet.h#L68 532 // [1] - https://github.com/WebKit/webkit/blob/1cb2227f6b2a1035f7bdc46e5ab69 debb75fc1de/Source/WebCore/css/RuleSet.h#L68
539 for (let i = 0; i < preparedSelectors.length; i += this.selectorGroupSize) 533 for (let i = 0; i < preparedSelectors.length; i += this.selectorGroupSize)
540 { 534 {
541 let selector = preparedSelectors.slice(i, i + this.selectorGroupSize).join (", "); 535 let selector = preparedSelectors.slice(
536 i, i + this.selectorGroupSize
537 ).join(", ");
542 this.style.sheet.insertRule(selector + "{display: none !important;}", 538 this.style.sheet.insertRule(selector + "{display: none !important;}",
543 this.style.sheet.cssRules.length); 539 this.style.sheet.cssRules.length);
544 } 540 }
545 541
546 if (this.tracer) 542 if (this.tracer)
547 this.tracer.addSelectors(selectors, filters || selectors); 543 this.tracer.addSelectors(selectors, filters || selectors);
548 }, 544 },
549 545
550 apply() 546 apply()
551 { 547 {
(...skipping 14 matching lines...) Expand all
566 this.elemHideEmulation.apply(); 562 this.elemHideEmulation.apply();
567 }); 563 });
568 } 564 }
569 }; 565 };
570 566
571 if (document instanceof HTMLDocument) 567 if (document instanceof HTMLDocument)
572 { 568 {
573 checkSitekey(); 569 checkSitekey();
574 wrapWebSocket(); 570 wrapWebSocket();
575 571
576 // This variable is also used by our other content scripts, outside of the 572 elemhide = new ElemHide();
577 // current scope.
578 var elemhide = new ElemHide();
579 elemhide.apply(); 573 elemhide.apply();
580 574
581 document.addEventListener("error", event => 575 document.addEventListener("error", event =>
582 { 576 {
583 checkCollapse(event.target); 577 checkCollapse(event.target);
584 }, true); 578 }, true);
585 579
586 document.addEventListener("load", event => 580 document.addEventListener("load", event =>
587 { 581 {
588 let element = event.target; 582 let element = event.target;
589 if (/^i?frame$/.test(element.localName)) 583 if (/^i?frame$/.test(element.localName))
590 checkCollapse(element); 584 checkCollapse(element);
591 }, true); 585 }, true);
592 } 586 }
OLDNEW

Powered by Google App Engine
This is Rietveld