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: Addressed Wladimir's feedback Created March 15, 2017, 4:51 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
« no previous file with comments | « ext/common.js ('k') | lib/compat.js » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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 = new RealWebSocket(url, ...args);
400 if (arguments.length == 1)
401 websocket = new RealWebSocket(url);
402 else
403 websocket = new RealWebSocket(url, arguments[1]);
404 395
405 checkRequest(websocket.url, blocked => 396 checkRequest(websocket.url, blocked =>
406 { 397 {
407 if (blocked) 398 if (blocked)
408 closeWebSocket(websocket); 399 closeWebSocket(websocket);
409 }); 400 });
410 401
411 return websocket; 402 return websocket;
412 } 403 }
413 WrappedWebSocket.prototype = RealWebSocket.prototype; 404 WrappedWebSocket.prototype = RealWebSocket.prototype;
414 WebSocket = WrappedWebSocket.bind(); 405 window.WebSocket = WrappedWebSocket.bind();
415 Object.defineProperties(WebSocket, { 406 Object.defineProperties(WebSocket, {
416 CONNECTING: {value: RealWebSocket.CONNECTING, enumerable: true}, 407 CONNECTING: {value: RealWebSocket.CONNECTING, enumerable: true},
417 OPEN: {value: RealWebSocket.OPEN, enumerable: true}, 408 OPEN: {value: RealWebSocket.OPEN, enumerable: true},
418 CLOSING: {value: RealWebSocket.CLOSING, enumerable: true}, 409 CLOSING: {value: RealWebSocket.CLOSING, enumerable: true},
419 CLOSED: {value: RealWebSocket.CLOSED, enumerable: true}, 410 CLOSED: {value: RealWebSocket.CLOSED, enumerable: true},
420 prototype: {value: RealWebSocket.prototype} 411 prototype: {value: RealWebSocket.prototype}
421 }); 412 });
422 413
423 RealWebSocket.prototype.constructor = WebSocket; 414 RealWebSocket.prototype.constructor = WebSocket;
424 }, eventName); 415 }, randomEventName);
425 } 416 }
426 417
427 function ElemHide() 418 function ElemHide()
428 { 419 {
429 this.shadow = this.createShadowTree(); 420 this.shadow = this.createShadowTree();
430 this.style = null; 421 this.style = null;
431 this.tracer = null; 422 this.tracer = null;
432 423
433 this.elemHideEmulation = new ElemHideEmulation( 424 this.elemHideEmulation = new ElemHideEmulation(
434 window, 425 window,
(...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after
467 shadow.appendChild(document.createElement("shadow")); 458 shadow.appendChild(document.createElement("shadow"));
468 459
469 // Stop the website from messing with our shadow root (#4191, #4298). 460 // Stop the website from messing with our shadow root (#4191, #4298).
470 if ("shadowRoot" in Element.prototype) 461 if ("shadowRoot" in Element.prototype)
471 { 462 {
472 runInPageContext(() => 463 runInPageContext(() =>
473 { 464 {
474 let ourShadowRoot = document.documentElement.shadowRoot; 465 let ourShadowRoot = document.documentElement.shadowRoot;
475 if (!ourShadowRoot) 466 if (!ourShadowRoot)
476 return; 467 return;
477 let desc = Object.getOwnPropertyDescriptor(Element.prototype, "shadowRoo t"); 468 let desc = Object.getOwnPropertyDescriptor(Element.prototype,
469 "shadowRoot");
478 let shadowRoot = Function.prototype.call.bind(desc.get); 470 let shadowRoot = Function.prototype.call.bind(desc.get);
479 471
480 Object.defineProperty(Element.prototype, "shadowRoot", { 472 Object.defineProperty(Element.prototype, "shadowRoot", {
481 configurable: true, enumerable: true, get() 473 configurable: true, enumerable: true, get()
482 { 474 {
483 let shadow = shadowRoot(this); 475 let thisShadow = shadowRoot(this);
484 return shadow == ourShadowRoot ? null : shadow; 476 return thisShadow == ourShadowRoot ? null : thisShadow;
485 } 477 }
486 }); 478 });
487 }, null); 479 }, null);
488 } 480 }
489 481
490 return shadow; 482 return shadow;
491 }, 483 },
492 484
493 addSelectors(selectors, filters) 485 addSelectors(selectors, filters)
494 { 486 {
495 if (selectors.length == 0) 487 if (selectors.length == 0)
496 return; 488 return;
497 489
498 if (!this.style) 490 if (!this.style)
499 { 491 {
500 // Create <style> element lazily, only if we add styles. Add it to 492 // Create <style> element lazily, only if we add styles. Add it to
501 // the shadow DOM if possible. Otherwise fallback to the <head> or 493 // the shadow DOM if possible. Otherwise fallback to the <head> or
502 // <html> element. If we have injected a style element before that 494 // <html> element. If we have injected a style element before that
503 // has been removed (the sheet property is null), create a new one. 495 // has been removed (the sheet property is null), create a new one.
504 this.style = document.createElement("style"); 496 this.style = document.createElement("style");
505 (this.shadow || document.head 497 (this.shadow || document.head ||
506 || document.documentElement).appendChild(this.style); 498 document.documentElement).appendChild(this.style);
507 499
508 // It can happen that the frame already navigated to a different 500 // It can happen that the frame already navigated to a different
509 // document while we were waiting for the background page to respond. 501 // document while we were waiting for the background page to respond.
510 // In that case the sheet property will stay null, after addind the 502 // In that case the sheet property will stay null, after addind the
511 // <style> element to the shadow DOM. 503 // <style> element to the shadow DOM.
512 if (!this.style.sheet) 504 if (!this.style.sheet)
513 return; 505 return;
514 } 506 }
515 507
516 // If using shadow DOM, we have to add the ::content pseudo-element 508 // If using shadow DOM, we have to add the ::content pseudo-element
517 // before each selector, in order to match elements within the 509 // before each selector, in order to match elements within the
518 // insertion point. 510 // insertion point.
519 let preparedSelectors = []; 511 let preparedSelectors = [];
520 if (this.shadow) 512 if (this.shadow)
521 { 513 {
522 for (let selector of selectors) 514 for (let selector of selectors)
523 { 515 {
524 let subSelectors = splitSelector(selector); 516 let subSelectors = splitSelector(selector);
525 for (let subSelector of subSelectors) 517 for (let subSelector of subSelectors)
526 preparedSelectors.push("::content " + subSelector); 518 preparedSelectors.push("::content " + subSelector);
527 } 519 }
528 } 520 }
529 else 521 else
530 {
531 preparedSelectors = selectors; 522 preparedSelectors = selectors;
532 }
533 523
534 // Safari only allows 8192 primitive selectors to be injected at once[1], we 524 // 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. 525 // 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 526 // (Chrome also has a limit, larger... but we're not certain exactly what it
537 // is! Edge apparently has no such limit.) 527 // is! Edge apparently has no such limit.)
538 // [1] - https://github.com/WebKit/webkit/blob/1cb2227f6b2a1035f7bdc46e5ab69 debb75fc1de/Source/WebCore/css/RuleSet.h#L68 528 // [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) 529 for (let i = 0; i < preparedSelectors.length; i += this.selectorGroupSize)
540 { 530 {
541 let selector = preparedSelectors.slice(i, i + this.selectorGroupSize).join (", "); 531 let selector = preparedSelectors.slice(
532 i, i + this.selectorGroupSize
533 ).join(", ");
542 this.style.sheet.insertRule(selector + "{display: none !important;}", 534 this.style.sheet.insertRule(selector + "{display: none !important;}",
543 this.style.sheet.cssRules.length); 535 this.style.sheet.cssRules.length);
544 } 536 }
545 537
546 if (this.tracer) 538 if (this.tracer)
547 this.tracer.addSelectors(selectors, filters || selectors); 539 this.tracer.addSelectors(selectors, filters || selectors);
548 }, 540 },
549 541
550 apply() 542 apply()
551 { 543 {
(...skipping 14 matching lines...) Expand all
566 this.elemHideEmulation.apply(); 558 this.elemHideEmulation.apply();
567 }); 559 });
568 } 560 }
569 }; 561 };
570 562
571 if (document instanceof HTMLDocument) 563 if (document instanceof HTMLDocument)
572 { 564 {
573 checkSitekey(); 565 checkSitekey();
574 wrapWebSocket(); 566 wrapWebSocket();
575 567
576 // This variable is also used by our other content scripts, outside of the 568 elemhide = new ElemHide();
577 // current scope.
578 var elemhide = new ElemHide();
579 elemhide.apply(); 569 elemhide.apply();
580 570
581 document.addEventListener("error", event => 571 document.addEventListener("error", event =>
582 { 572 {
583 checkCollapse(event.target); 573 checkCollapse(event.target);
584 }, true); 574 }, true);
585 575
586 document.addEventListener("load", event => 576 document.addEventListener("load", event =>
587 { 577 {
588 let element = event.target; 578 let element = event.target;
589 if (/^i?frame$/.test(element.localName)) 579 if (/^i?frame$/.test(element.localName))
590 checkCollapse(element); 580 checkCollapse(element);
591 }, true); 581 }, true);
592 } 582 }
OLDNEW
« no previous file with comments | « ext/common.js ('k') | lib/compat.js » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld