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({ |