| Index: include.preload.js |
| diff --git a/include.preload.js b/include.preload.js |
| index 949d039663f9130843b4d8d0a3ddfcb434522066..5e14da62ebfe5916af30c5c2881d8cc01ed620b0 100644 |
| --- a/include.preload.js |
| +++ b/include.preload.js |
| @@ -351,7 +351,7 @@ function reinjectStyleSheetWhenRemoved(document, style) |
| function protectStyleSheet(document, style) |
| { |
| - var id = Math.random().toString(36).substr(2) |
| + var id = Math.random().toString(36).substr(2); |
| style.id = id; |
| var code = [ |
| @@ -394,6 +394,93 @@ function protectStyleSheet(document, style) |
| document.documentElement.removeChild(script); |
| } |
| +// Neither Chrome[1] nor Safari allow us to intercept WebSockets, and therefore |
| +// some ad networks are misusing them as a way to serve adverts and circumvent |
| +// us. As a workaround we wrap WebSocket, closing connections that would have |
| +// otherwise been blocked. |
| +// [1] - https://bugs.chromium.org/p/chromium/issues/detail?id=129353 |
| +function wrapWebSocket() |
| +{ |
| + if (typeof WebSocket == "undefined") |
| + return; |
| + |
| + var eventName = "abpws-" + Math.random().toString().substr(2); |
| + |
| + document.addEventListener(eventName, function(event) |
| + { |
| + ext.backgroundPage.sendMessage({ |
| + type: "websocket-request", |
| + url: event.detail.url |
| + }, function (block) |
| + { |
| + document.dispatchEvent( |
| + new CustomEvent(eventName + "-" + event.detail.url, {detail: block}) |
| + ); |
| + }); |
| + }); |
| + |
| + function wrapper(eventName) |
| + { |
| + var originalWebSocket = WebSocket; |
| + var readyStates = { |
| + CLOSED: {value: 3, enumerable: true}, |
| + CLOSING: {value: 2, enumerable: true}, |
| + OPEN: {value: 1, enumerable: true}, |
| + CONNECTING: {value: 0, enumerable: true} |
| + }; |
| + |
| + WebSocket = function(url, protocol) |
| + { |
| + var websocket = new originalWebSocket(url, protocol); |
| + var properties = Object.create(null); |
| + |
| + function getSet(key) |
| + { |
| + return {get: function() { return websocket[key]; }, |
| + set: function(value) { return websocket[key] = value; }, |
| + enumerable: true}; |
| + } |
| + function funcWrap(key) |
| + { |
| + return {value: function() { websocket[key].apply(websocket, arguments); }, |
| + enumerable: true}; |
| + } |
| + |
| + var key; |
| + for (key of ["close", "send", "addEventListener", "removeEventListener"]) |
|
Sebastian Noack
2016/06/28 16:17:51
Is this script processed with jsHydra? It seems no
kzar
2016/06/29 13:40:30
Acknowledged.
|
| + properties[key] = funcWrap(key); |
| + for (key of ["url", "protocol", "readyState", "extensions", "bufferedAmount", |
| + "binaryType", "onopen", "onclose", "onerror", "onmessage"]) |
| + properties[key] = getSet(key); |
| + |
| + Object.defineProperties(this, properties); |
| + |
| + var incomingEventName = eventName + "-" + url; |
| + function listener(event) |
| + { |
| + if (event.detail) |
| + websocket.close(); |
|
Sebastian Noack
2016/06/28 16:17:51
As Lain pointed out, this approach allows WebSocke
kzar
2016/06/28 16:32:40
I actually tested this with WireShark and I found
Sebastian Noack
2016/06/28 17:04:58
I have two concerns here:
1. It might be a potent
kzar
2016/06/29 13:40:31
OK, done.
|
| + |
| + document.removeEventListener(incomingEventName, listener); |
| + } |
| + document.addEventListener(incomingEventName, listener); |
| + |
| + document.dispatchEvent(new CustomEvent(eventName, { |
| + detail: {url: url, protocol: protocol} |
| + })); |
| + }; |
| + WebSocket.prototype = Object.create(window.EventTarget.prototype, readyStates); |
| + Object.defineProperties(WebSocket, readyStates); |
| + |
| + var script = document.currentScript; |
| + script.parentNode.removeChild(script); |
| + } |
| + |
| + var script = document.createElement("script"); |
| + script.textContent = "(" + wrapper.toString() + ")(\"" + eventName + "\");"; |
|
Sebastian Noack
2016/06/28 16:17:51
Ideally, we inject only one script. Note that we a
kzar
2016/06/29 13:40:30
Done.
|
| + document.documentElement.appendChild(script); |
| +} |
| + |
| function init(document) |
| { |
| var shadow = null; |
| @@ -401,6 +488,8 @@ function init(document) |
| var observer = null; |
| var tracer = null; |
| + wrapWebSocket(); |
| + |
| function getPropertyFilters(callback) |
| { |
| ext.backgroundPage.sendMessage({ |