uBlock/platform/chromium/websocket.js

208 lines
6.5 KiB
JavaScript
Raw Normal View History

2016-04-21 18:26:08 +02:00
/*******************************************************************************
uBlock Origin - a browser extension to block requests.
Copyright (C) 2014-2106 The uBlock Origin authors
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see {http://www.gnu.org/licenses/}.
Home: https://github.com/gorhill/uBlock
*/
// Purpose of this script is to workaround Chromium issue 129353:
// https://bugs.chromium.org/p/chromium/issues/detail?id=129353
// https://github.com/gorhill/uBlock/issues/956
// https://github.com/gorhill/uBlock/issues/1497
// WebSocket reference: https://html.spec.whatwg.org/multipage/comms.html
// A WeakMap is used to hide the real WebSocket instance from caller's view, in
// order to ensure that the wrapper can't be bypassed.
// The script removes its own tag from the DOM.
(function() {
'use strict';
2016-04-22 17:45:20 +02:00
var Wrapped = window.WebSocket;
2016-04-21 18:26:08 +02:00
var toWrapped = new WeakMap();
2016-04-22 17:45:20 +02:00
var onResponseReceived = function(wrapper, ok) {
this.onload = this.onerror = null;
var bag = toWrapped.get(wrapper);
if ( !ok ) {
if ( bag.properties.onerror ) {
bag.properties.onerror(new window.ErrorEvent('error'));
}
2016-04-21 18:26:08 +02:00
return;
}
2016-04-22 17:45:20 +02:00
var wrapped = new Wrapped(bag.args.url, bag.args.protocols);
for ( var prop in bag.properties ) {
wrapped[prop] = bag.properties[prop];
2016-04-21 18:26:08 +02:00
}
2016-04-22 17:45:20 +02:00
toWrapped.set(wrapper, wrapped);
2016-04-21 18:26:08 +02:00
};
2016-04-26 15:22:05 +02:00
var noopfn = function() {};
2016-04-22 17:45:20 +02:00
var fallthruGet = function(wrapper, prop, value) {
var wrapped = toWrapped.get(wrapper);
2016-04-21 18:26:08 +02:00
if ( !wrapped ) {
2016-04-22 17:45:20 +02:00
return value;
2016-04-21 18:26:08 +02:00
}
2016-04-22 17:45:20 +02:00
if ( wrapped instanceof Wrapped ) {
return wrapped[prop];
2016-04-21 18:26:08 +02:00
}
2016-04-22 17:45:20 +02:00
return wrapped.properties.hasOwnProperty(prop) ?
wrapped.properties[prop] :
value;
2016-04-21 18:26:08 +02:00
};
2016-04-22 17:45:20 +02:00
var fallthruSet = function(wrapper, prop, value) {
if ( value instanceof Function ) {
value = value.bind(wrapper);
2016-04-21 18:26:08 +02:00
}
2016-04-22 17:45:20 +02:00
var wrapped = toWrapped.get(wrapper);
2016-04-21 18:26:08 +02:00
if ( !wrapped ) {
return;
}
2016-04-22 17:45:20 +02:00
if ( wrapped instanceof Wrapped ) {
wrapped[prop] = value;
} else {
wrapped.properties[prop] = value;
2016-04-21 18:26:08 +02:00
}
};
var WebSocket = function(url, protocols) {
2016-04-26 15:22:05 +02:00
if (
window.location.protocol === 'https:' &&
url.lastIndexOf('ws:', 0) === 0
) {
2016-04-22 17:45:20 +02:00
var ws = new Wrapped(url, protocols);
if ( ws ) {
ws.close();
}
2016-04-21 18:26:08 +02:00
}
2016-04-22 17:45:20 +02:00
Object.defineProperties(this, {
'binaryType': {
get: function() {
return fallthruGet(this, 'binaryType', '');
},
set: function(value) {
fallthruSet(this, 'binaryType', value);
}
},
'bufferedAmount': {
get: function() {
return fallthruGet(this, 'bufferedAmount', 0);
},
set: noopfn
},
'extensions': {
get: function() {
return fallthruGet(this, 'extensions', '');
},
set: noopfn
},
'onclose': {
get: function() {
return fallthruGet(this, 'onclose', null);
},
set: function(value) {
fallthruSet(this, 'onclose', value);
}
},
'onerror': {
get: function() {
return fallthruGet(this, 'onerror', null);
},
set: function(value) {
fallthruSet(this, 'onerror', value);
}
},
'onmessage': {
get: function() {
return fallthruGet(this, 'onmessage', null);
},
set: function(value) {
fallthruSet(this, 'onmessage', value);
}
},
'onopen': {
get: function() {
return fallthruGet(this, 'onopen', null);
},
set: function(value) {
fallthruSet(this, 'onopen', value);
}
},
'protocol': {
get: function() {
return fallthruGet(this, 'protocol', '');
},
set: noopfn
},
'readyState': {
get: function() {
return fallthruGet(this, 'readyState', 0);
},
set: noopfn
},
'url': {
get: function() {
return fallthruGet(this, 'url', '');
},
set: noopfn
}
});
toWrapped.set(this, {
args: { url: url, protocols: protocols },
properties: {}
});
2016-04-21 18:26:08 +02:00
var img = new Image();
img.src =
window.location.origin
+ '?url=' + encodeURIComponent(url)
+ '&ubofix=f41665f3028c7fd10eecf573336216d3';
2016-04-22 17:45:20 +02:00
img.onload = onResponseReceived.bind(img, this, true);
img.onerror = onResponseReceived.bind(img, this, false);
2016-04-21 18:26:08 +02:00
};
2016-04-22 17:45:20 +02:00
WebSocket.prototype.CONNECTING = 0;
WebSocket.prototype.OPEN = 1;
WebSocket.prototype.CLOSING = 2;
WebSocket.prototype.CLOSED = 3;
2016-04-21 18:26:08 +02:00
WebSocket.prototype.close = function(code, reason) {
var wrapped = toWrapped.get(this);
2016-04-22 17:45:20 +02:00
if ( wrapped instanceof Wrapped ) {
wrapped.close(code, reason);
2016-04-21 18:26:08 +02:00
}
};
WebSocket.prototype.send = function(data) {
var wrapped = toWrapped.get(this);
2016-04-22 17:45:20 +02:00
if ( wrapped instanceof Wrapped ) {
wrapped.send(data);
2016-04-21 18:26:08 +02:00
}
};
window.WebSocket = WebSocket;
2016-04-26 15:22:05 +02:00
var me = document.currentScript;
if ( me && me.parentNode !== null ) {
2016-04-21 18:26:08 +02:00
me.parentNode.removeChild(me);
}
})();