| OLD | NEW |
| 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 Loading... |
| 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 Loading... |
| 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 Loading... |
| 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 } |
| OLD | NEW |