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

Side by Side Diff: include.preload.js

Issue 29437555: Issue 4586, 5207 - Move the injected wrappers into a separate file (Closed)
Patch Set: Avoid adding an extra content script Created May 12, 2017, 12:53 p.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 | « no previous file | inject.preload.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-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 315 matching lines...) Expand 10 before | Expand all | Expand 10 after
326 }, 326 },
327 327
328 disconnect() 328 disconnect()
329 { 329 {
330 document.removeEventListener("DOMContentLoaded", this.trace); 330 document.removeEventListener("DOMContentLoaded", this.trace);
331 this.observer.disconnect(); 331 this.observer.disconnect();
332 clearTimeout(this.timeout); 332 clearTimeout(this.timeout);
333 } 333 }
334 }; 334 };
335 335
336 function runInPageContext(fn, arg)
337 {
338 let script = document.createElement("script");
339 script.type = "application/javascript";
340 script.async = false;
341 script.textContent = "(" + fn + ")(" + JSON.stringify(arg) + ");";
342 document.documentElement.appendChild(script);
343 document.documentElement.removeChild(script);
344 }
345
346 function wrapRequestAPIs()
347 {
348 let randomEventName = "abp-request-" + Math.random().toString(36).substr(2);
349
350 // Proxy "should we block?" messages from checkRequest inside the injected
351 // code to the background page and back again.
352 document.addEventListener(randomEventName, event =>
353 {
354 let {url, requestType} = event.detail;
355
356 ext.backgroundPage.sendMessage({
357 type: "request.blockedByWrapper",
358 requestType,
359 url
360 }, block =>
361 {
362 document.dispatchEvent(new CustomEvent(
363 randomEventName + "-" + requestType + "-" + url, {detail: block}
364 ));
365 });
366 });
367
368 runInPageContext(eventName =>
369 {
370 let RealCustomEvent = window.CustomEvent;
371 let addEventListener = document.addEventListener.bind(document);
372 let removeEventListener = document.removeEventListener.bind(document);
373 let dispatchEvent = document.dispatchEvent.bind(document);
374
375 function checkRequest(requestType, url, callback)
376 {
377 let incomingEventName = eventName + "-" + requestType + "-" + url;
378
379 function listener(event)
380 {
381 callback(event.detail);
382 removeEventListener(incomingEventName, listener);
383 }
384 addEventListener(incomingEventName, listener);
385
386 dispatchEvent(new RealCustomEvent(eventName,
387 {detail: {url, requestType}}));
388 }
389
390 // Only to be called before the page's code, not hardened.
391 function copyProperties(src, dest, properties)
392 {
393 for (let name of properties)
394 {
395 Object.defineProperty(dest, name,
396 Object.getOwnPropertyDescriptor(src, name));
397 }
398 }
399
400 /*
401 * WebSocket wrapper
402 *
403 * Required before Chrome 58, since the webRequest API didn't allow us to
404 * intercept WebSockets.
405 * See https://bugs.chromium.org/p/chromium/issues/detail?id=129353
406 */
407 let RealWebSocket = WebSocket;
408 let closeWebSocket = Function.prototype.call.bind(
409 RealWebSocket.prototype.close
410 );
411
412 function WrappedWebSocket(url, ...args)
413 {
414 // Throw correct exceptions if the constructor is used improperly.
415 if (!(this instanceof WrappedWebSocket)) return RealWebSocket();
416 if (arguments.length < 1) return new RealWebSocket();
417
418 let websocket = new RealWebSocket(url, ...args);
419
420 checkRequest("WEBSOCKET", websocket.url, blocked =>
421 {
422 if (blocked)
423 closeWebSocket(websocket);
424 });
425
426 return websocket;
427 }
428 WrappedWebSocket.prototype = RealWebSocket.prototype;
429 window.WebSocket = WrappedWebSocket.bind();
430 copyProperties(RealWebSocket, WebSocket,
431 ["CONNECTING", "OPEN", "CLOSING", "CLOSED", "prototype"]);
432 RealWebSocket.prototype.constructor = WebSocket;
433
434 /*
435 * RTCPeerConnection wrapper
436 *
437 * The webRequest API in Chrome does not yet allow the blocking of
438 * WebRTC connections.
439 * See https://bugs.chromium.org/p/chromium/issues/detail?id=707683
440 */
441 let RealRTCPeerConnection = window.RTCPeerConnection ||
442 window.webkitRTCPeerConnection;
443 let closeRTCPeerConnection = Function.prototype.call.bind(
444 RealRTCPeerConnection.prototype.close
445 );
446 let RealArray = Array;
447 let RealString = String;
448 let {create: createObject, defineProperty} = Object;
449
450 function normalizeUrl(url)
451 {
452 if (typeof url != "undefined")
453 return RealString(url);
454 }
455
456 function safeCopyArray(originalArray, transform)
457 {
458 if (originalArray == null || typeof originalArray != "object")
459 return originalArray;
460
461 let safeArray = RealArray(originalArray.length);
462 for (let i = 0; i < safeArray.length; i++)
463 {
464 defineProperty(safeArray, i, {
465 configurable: false, enumerable: false, writable: false,
466 value: transform(originalArray[i])
467 });
468 }
469 defineProperty(safeArray, "length", {
470 configurable: false, enumerable: false, writable: false,
471 value: safeArray.length
472 });
473 return safeArray;
474 }
475
476 // It would be much easier to use the .getConfiguration method to obtain
477 // the normalized and safe configuration from the RTCPeerConnection
478 // instance. Unfortunately its not implemented as of Chrome unstable 59.
479 // See https://www.chromestatus.com/feature/5271355306016768
480 function protectConfiguration(configuration)
481 {
482 if (configuration == null || typeof configuration != "object")
483 return configuration;
484
485 let iceServers = safeCopyArray(
486 configuration.iceServers,
487 iceServer =>
488 {
489 let {url, urls} = iceServer;
490
491 // RTCPeerConnection doesn't iterate through pseudo Arrays of urls.
492 if (typeof urls != "undefined" && !(urls instanceof RealArray))
493 urls = [urls];
494
495 return createObject(iceServer, {
496 url: {
497 configurable: false, enumerable: false, writable: false,
498 value: normalizeUrl(url)
499 },
500 urls: {
501 configurable: false, enumerable: false, writable: false,
502 value: safeCopyArray(urls, normalizeUrl)
503 }
504 });
505 }
506 );
507
508 return createObject(configuration, {
509 iceServers: {
510 configurable: false, enumerable: false, writable: false,
511 value: iceServers
512 }
513 });
514 }
515
516 function checkUrl(peerconnection, url)
517 {
518 checkRequest("WEBRTC", url, blocked =>
519 {
520 if (blocked)
521 {
522 // Calling .close() throws if already closed.
523 try
524 {
525 closeRTCPeerConnection(peerconnection);
526 }
527 catch (e) {}
528 }
529 });
530 }
531
532 function checkConfiguration(peerconnection, configuration)
533 {
534 if (configuration && configuration.iceServers)
535 {
536 for (let i = 0; i < configuration.iceServers.length; i++)
537 {
538 let iceServer = configuration.iceServers[i];
539 if (iceServer)
540 {
541 if (iceServer.url)
542 checkUrl(peerconnection, iceServer.url);
543
544 if (iceServer.urls)
545 {
546 for (let j = 0; j < iceServer.urls.length; j++)
547 checkUrl(peerconnection, iceServer.urls[j]);
548 }
549 }
550 }
551 }
552 }
553
554 // Chrome unstable (tested with 59) has already implemented
555 // setConfiguration, so we need to wrap that if it exists too.
556 // https://www.chromestatus.com/feature/5596193748942848
557 if (RealRTCPeerConnection.prototype.setConfiguration)
558 {
559 let realSetConfiguration = Function.prototype.call.bind(
560 RealRTCPeerConnection.prototype.setConfiguration
561 );
562
563 RealRTCPeerConnection.prototype.setConfiguration = function(configuration)
564 {
565 configuration = protectConfiguration(configuration);
566
567 // Call the real method first, so that validates the configuration for
568 // us. Also we might as well since checkRequest is asynchronous anyway.
569 realSetConfiguration(this, configuration);
570 checkConfiguration(this, configuration);
571 };
572 }
573
574 function WrappedRTCPeerConnection(...args)
575 {
576 if (!(this instanceof WrappedRTCPeerConnection))
577 return WrappedRTCPeerConnection();
578
579 let configuration = protectConfiguration(args[0]);
580 // Since the old webkitRTCPeerConnection constructor takes an optional
581 // second argument we need to take care to pass that through. Necessary
582 // for older versions of Chrome such as 49.
583 let peerconnection = new RealRTCPeerConnection(configuration, args[1]);
584 checkConfiguration(peerconnection, configuration);
585 return peerconnection;
586 }
587
588 WrappedRTCPeerConnection.prototype = RealRTCPeerConnection.prototype;
589
590 let boundWrappedRTCPeerConnection = WrappedRTCPeerConnection.bind();
591 copyProperties(RealRTCPeerConnection, boundWrappedRTCPeerConnection,
592 ["caller", "generateCertificate", "name", "prototype"]);
593 RealRTCPeerConnection.prototype.constructor = boundWrappedRTCPeerConnection;
594
595 if ("RTCPeerConnection" in window)
596 window.RTCPeerConnection = boundWrappedRTCPeerConnection;
597 if ("webkitRTCPeerConnection" in window)
598 window.webkitRTCPeerConnection = boundWrappedRTCPeerConnection;
599 }, randomEventName);
600 }
601
602 function ElemHide() 336 function ElemHide()
603 { 337 {
604 this.shadow = this.createShadowTree(); 338 this.shadow = this.createShadowTree();
605 this.style = null; 339 this.style = null;
606 this.tracer = null; 340 this.tracer = null;
607 341
608 this.elemHideEmulation = new ElemHideEmulation( 342 this.elemHideEmulation = new ElemHideEmulation(
609 window, 343 window,
610 callback => 344 callback =>
611 { 345 {
(...skipping 22 matching lines...) Expand all
634 if (/\.(?:google|blogger)\.com$/.test(document.domain)) 368 if (/\.(?:google|blogger)\.com$/.test(document.domain))
635 return null; 369 return null;
636 370
637 // Finally since some users have both AdBlock and Adblock Plus installed we 371 // Finally since some users have both AdBlock and Adblock Plus installed we
638 // have to consider how the two extensions interact. For example we want to 372 // have to consider how the two extensions interact. For example we want to
639 // avoid creating the shadowRoot twice. 373 // avoid creating the shadowRoot twice.
640 let shadow = document.documentElement.shadowRoot || 374 let shadow = document.documentElement.shadowRoot ||
641 document.documentElement.createShadowRoot(); 375 document.documentElement.createShadowRoot();
642 shadow.appendChild(document.createElement("shadow")); 376 shadow.appendChild(document.createElement("shadow"));
643 377
644 // Stop the website from messing with our shadow root (#4191, #4298).
645 if ("shadowRoot" in Element.prototype)
646 {
647 runInPageContext(() =>
648 {
649 let ourShadowRoot = document.documentElement.shadowRoot;
650 if (!ourShadowRoot)
651 return;
652 let desc = Object.getOwnPropertyDescriptor(Element.prototype,
653 "shadowRoot");
654 let shadowRoot = Function.prototype.call.bind(desc.get);
655
656 Object.defineProperty(Element.prototype, "shadowRoot", {
657 configurable: true, enumerable: true, get()
658 {
659 let thisShadow = shadowRoot(this);
660 return thisShadow == ourShadowRoot ? null : thisShadow;
661 }
662 });
663 }, null);
664 }
665
666 return shadow; 378 return shadow;
667 }, 379 },
668 380
669 addSelectors(selectors, filters) 381 addSelectors(selectors, filters)
670 { 382 {
671 if (selectors.length == 0) 383 if (selectors.length == 0)
672 return; 384 return;
673 385
674 if (!this.style) 386 if (!this.style)
675 { 387 {
(...skipping 66 matching lines...) Expand 10 before | Expand all | Expand 10 after
742 454
743 this.addSelectors(response.selectors); 455 this.addSelectors(response.selectors);
744 this.elemHideEmulation.apply(); 456 this.elemHideEmulation.apply();
745 }); 457 });
746 } 458 }
747 }; 459 };
748 460
749 if (document instanceof HTMLDocument) 461 if (document instanceof HTMLDocument)
750 { 462 {
751 checkSitekey(); 463 checkSitekey();
752 wrapRequestAPIs();
753 464
754 elemhide = new ElemHide(); 465 elemhide = new ElemHide();
755 elemhide.apply(); 466 elemhide.apply();
756 467
757 document.addEventListener("error", event => 468 document.addEventListener("error", event =>
758 { 469 {
759 checkCollapse(event.target); 470 checkCollapse(event.target);
760 }, true); 471 }, true);
761 472
762 document.addEventListener("load", event => 473 document.addEventListener("load", event =>
763 { 474 {
764 let element = event.target; 475 let element = event.target;
765 if (/^i?frame$/.test(element.localName)) 476 if (/^i?frame$/.test(element.localName))
766 checkCollapse(element); 477 checkCollapse(element);
767 }, true); 478 }, true);
768 } 479 }
OLDNEW
« no previous file with comments | « no previous file | inject.preload.js » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld