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

Unified Diff: include.preload.js

Issue 29347034: Issue 1727 - Prevent circumvention via WebSocket (Closed)
Patch Set: Created June 26, 2016, 11:55 a.m.
Use n/p to move between diff chunks; N/P to move between comments.
Jump to:
View side-by-side diff with in-line comments
Download patch
« background.js ('K') | « background.js ('k') | no next file » | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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({
« background.js ('K') | « background.js ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld