Path: blob/master/sites/bitcoin/vapi-client.js
777 views
/*******************************************************************************12uBlock Origin - a browser extension to block requests.3Copyright (C) 2014-2018 The uBlock Origin authors45This program is free software: you can redistribute it and/or modify6it under the terms of the GNU General Public License as published by7the Free Software Foundation, either version 3 of the License, or8(at your option) any later version.910This program is distributed in the hope that it will be useful,11but WITHOUT ANY WARRANTY; without even the implied warranty of12MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the13GNU General Public License for more details.1415You should have received a copy of the GNU General Public License16along with this program. If not, see {http://www.gnu.org/licenses/}.1718Home: https://github.com/gorhill/uBlock19*/2021// For non-background page2223'use strict';2425/******************************************************************************/2627// https://github.com/chrisaljoudi/uBlock/issues/45628// Skip if already injected.2930// >>>>>>>> start of HUGE-IF-BLOCK31if ( typeof vAPI === 'object' && !vAPI.clientScript ) {3233/******************************************************************************/34/******************************************************************************/3536vAPI.clientScript = true;3738vAPI.randomToken = function() {39return String.fromCharCode(Date.now() % 26 + 97) +40Math.floor(Math.random() * 982451653 + 982451653).toString(36);41};4243vAPI.sessionId = vAPI.randomToken();44vAPI.chrome = true;45vAPI.setTimeout = vAPI.setTimeout || self.setTimeout.bind(self);4647/******************************************************************************/4849vAPI.shutdown = {50jobs: [],51add: function(job) {52this.jobs.push(job);53},54exec: function() {55var job;56while ( (job = this.jobs.pop()) ) {57job();58}59},60remove: function(job) {61var pos;62while ( (pos = this.jobs.indexOf(job)) !== -1 ) {63this.jobs.splice(pos, 1);64}65}66};6768/******************************************************************************/6970vAPI.messaging = {71port: null,72portTimer: null,73portTimerDelay: 10000,74channels: new Map(),75connections: new Map(),76pending: new Map(),77auxProcessId: 1,78shuttingDown: false,7980Connection: function(handler, details) {81var messaging = vAPI.messaging;82this.messaging = messaging;83this.handler = handler;84this.id = details.id;85this.to = details.to;86this.toToken = details.toToken;87this.from = details.from;88this.fromToken = details.fromToken;89this.checkBound = this.check.bind(this);90this.checkTimer = undefined;91// On Firefox it appears ports are not automatically disconnected when92// navigating to another page.93if ( messaging.Connection.pagehide !== undefined ) { return; }94messaging.Connection.pagehide = function() {95for ( var connection of this.connections.values() ) {96connection.disconnect();97connection.handler(connection.toDetails('connectionBroken'));98}99}.bind(messaging);100window.addEventListener('pagehide', messaging.Connection.pagehide);101},102103shutdown: function() {104this.shuttingDown = true;105this.destroyPort();106},107108disconnectListener: function() {109this.port = null;110vAPI.shutdown.exec();111},112disconnectListenerBound: null,113114messageListener: function(details) {115if ( !details ) { return; }116117// Sent to all channels118if ( details.broadcast ) {119for ( var channelName of this.channels.keys() ) {120this.sendToChannelListeners(channelName, details.msg);121}122return;123}124125// Response to specific message previously sent126var listener;127if ( details.auxProcessId ) {128listener = this.pending.get(details.auxProcessId);129if ( listener !== undefined ) {130this.pending.delete(details.auxProcessId);131listener(details.msg);132return;133}134}135136if ( details.channelName !== 'vapi' ) { return; }137138// Internal handler139var connection;140141switch ( details.msg.what ) {142case 'connectionAccepted':143case 'connectionBroken':144case 'connectionCheck':145case 'connectionMessage':146case 'connectionRefused':147connection = this.connections.get(details.msg.id);148if ( connection === undefined ) { return; }149connection.receive(details.msg);150break;151case 'connectionRequested':152var listeners = this.channels.get(details.msg.to);153if ( listeners === undefined ) { return; }154var port = this.getPort();155if ( port === null ) { return; }156for ( listener of listeners ) {157if ( listener(details.msg) !== true ) { continue; }158details.msg.what = 'connectionAccepted';159details.msg.toToken = port.name;160connection = new this.Connection(listener, details.msg);161this.connections.set(connection.id, connection);162break;163}164if ( details.msg.what !== 'connectionAccepted' ) {165details.msg.what = 'connectionRefused';166}167port.postMessage(details);168break;169default:170break;171}172},173messageListenerCallback: null,174175portPoller: function() {176this.portTimer = null;177if (178this.port !== null &&179this.channels.size === 0 &&180this.connections.size === 0 &&181this.pending.size === 0182) {183return this.destroyPort();184}185this.portTimer = vAPI.setTimeout(this.portPollerBound, this.portTimerDelay);186this.portTimerDelay = Math.min(this.portTimerDelay * 2, 60 * 60 * 1000);187},188portPollerBound: null,189190destroyPort: function() {191if ( this.portTimer !== null ) {192clearTimeout(this.portTimer);193this.portTimer = null;194}195var port = this.port;196if ( port !== null ) {197port.disconnect();198port.onMessage.removeListener(this.messageListenerCallback);199port.onDisconnect.removeListener(this.disconnectListenerBound);200this.port = null;201}202this.channels.clear();203if ( this.connections.size !== 0 ) {204for ( var connection of this.connections.values() ) {205connection.receive({ what: 'connectionBroken' });206}207this.connections.clear();208}209// service pending callbacks210if ( this.pending.size !== 0 ) {211var pending = this.pending;212this.pending = new Map();213for ( var callback of pending.values() ) {214if ( typeof callback === 'function' ) {215callback(null);216}217}218}219},220221createPort: function() {222if ( this.shuttingDown ) { return null; }223if ( this.messageListenerCallback === null ) {224this.messageListenerCallback = this.messageListener.bind(this);225this.disconnectListenerBound = this.disconnectListener.bind(this);226this.portPollerBound = this.portPoller.bind(this);227}228try {229this.port = chrome.runtime.connect({name: vAPI.sessionId}) || null;230} catch (ex) {231this.port = null;232}233if ( this.port !== null ) {234this.port.onMessage.addListener(this.messageListenerCallback);235this.port.onDisconnect.addListener(this.disconnectListenerBound);236this.portTimerDelay = 10000;237if ( this.portTimer === null ) {238this.portTimer = vAPI.setTimeout(239this.portPollerBound,240this.portTimerDelay241);242}243}244return this.port;245},246247getPort: function() {248return this.port !== null ? this.port : this.createPort();249},250251send: function(channelName, message, callback) {252// Too large a gap between the last request and the last response means253// the main process is no longer reachable: memory leaks and bad254// performance become a risk -- especially for long-lived, dynamic255// pages. Guard against this.256if ( this.pending.size > 25 ) {257vAPI.shutdown.exec();258}259var port = this.getPort();260if ( port === null ) {261if ( typeof callback === 'function' ) { callback(); }262return;263}264var auxProcessId;265if ( callback ) {266auxProcessId = this.auxProcessId++;267this.pending.set(auxProcessId, callback);268}269port.postMessage({270channelName: channelName,271auxProcessId: auxProcessId,272msg: message273});274},275276connectTo: function(from, to, handler) {277var port = this.getPort();278if ( port === null ) { return; }279var connection = new this.Connection(handler, {280id: from + '-' + to + '-' + vAPI.sessionId,281to: to,282from: from,283fromToken: port.name284});285this.connections.set(connection.id, connection);286port.postMessage({287channelName: 'vapi',288msg: {289what: 'connectionRequested',290id: connection.id,291from: from,292fromToken: port.name,293to: to294}295});296return connection.id;297},298299disconnectFrom: function(connectionId) {300var connection = this.connections.get(connectionId);301if ( connection === undefined ) { return; }302connection.disconnect();303},304305sendTo: function(connectionId, payload) {306var connection = this.connections.get(connectionId);307if ( connection === undefined ) { return; }308connection.send(payload);309},310311addChannelListener: function(channelName, listener) {312var listeners = this.channels.get(channelName);313if ( listeners === undefined ) {314this.channels.set(channelName, [ listener ]);315} else if ( listeners.indexOf(listener) === -1 ) {316listeners.push(listener);317}318this.getPort();319},320321removeChannelListener: function(channelName, listener) {322var listeners = this.channels.get(channelName);323if ( listeners === undefined ) { return; }324var pos = listeners.indexOf(listener);325if ( pos === -1 ) { return; }326listeners.splice(pos, 1);327if ( listeners.length === 0 ) {328this.channels.delete(channelName);329}330},331332removeAllChannelListeners: function(channelName) {333this.channels.delete(channelName);334},335336sendToChannelListeners: function(channelName, msg) {337var listeners = this.channels.get(channelName);338if ( listeners === undefined ) { return; }339listeners = listeners.slice(0);340var response;341for ( var listener of listeners ) {342response = listener(msg);343if ( response !== undefined ) { break; }344}345return response;346}347};348349/******************************************************************************/350351vAPI.messaging.Connection.prototype = {352toDetails: function(what, payload) {353return {354what: what,355id: this.id,356from: this.from,357fromToken: this.fromToken,358to: this.to,359toToken: this.toToken,360payload: payload361};362},363disconnect: function() {364if ( this.checkTimer !== undefined ) {365clearTimeout(this.checkTimer);366this.checkTimer = undefined;367}368this.messaging.connections.delete(this.id);369var port = this.messaging.getPort();370if ( port === null ) { return; }371port.postMessage({372channelName: 'vapi',373msg: this.toDetails('connectionBroken')374});375},376checkAsync: function() {377if ( this.checkTimer !== undefined ) {378clearTimeout(this.checkTimer);379}380this.checkTimer = vAPI.setTimeout(this.checkBound, 499);381},382check: function() {383this.checkTimer = undefined;384if ( this.messaging.connections.has(this.id) === false ) { return; }385var port = this.messaging.getPort();386if ( port === null ) { return; }387port.postMessage({388channelName: 'vapi',389msg: this.toDetails('connectionCheck')390});391this.checkAsync();392},393receive: function(details) {394switch ( details.what ) {395case 'connectionAccepted':396this.toToken = details.toToken;397this.handler(details);398this.checkAsync();399break;400case 'connectionBroken':401this.messaging.connections.delete(this.id);402this.handler(details);403break;404case 'connectionMessage':405this.handler(details);406this.checkAsync();407break;408case 'connectionCheck':409var port = this.messaging.getPort();410if ( port === null ) { return; }411if ( this.messaging.connections.has(this.id) ) {412this.checkAsync();413} else {414details.what = 'connectionBroken';415port.postMessage({ channelName: 'vapi', msg: details });416}417break;418case 'connectionRefused':419this.messaging.connections.delete(this.id);420this.handler(details);421break;422}423},424send: function(payload) {425var port = this.messaging.getPort();426if ( port === null ) { return; }427port.postMessage({428channelName: 'vapi',429msg: this.toDetails('connectionMessage', payload)430});431}432};433434/******************************************************************************/435436vAPI.shutdown.add(function() {437vAPI.messaging.shutdown();438window.vAPI = undefined;439});440441// https://www.youtube.com/watch?v=rT5zCHn0tsg442// https://www.youtube.com/watch?v=E-jS4e3zacI443444/******************************************************************************/445/******************************************************************************/446447}448// <<<<<<<< end of HUGE-IF-BLOCK449450451452453454455456457/*******************************************************************************458459DO NOT:460- Remove the following code461- Add code beyond the following code462Reason:463- https://github.com/gorhill/uBlock/pull/3721464- uBO never uses the return value from injected content scripts465466**/467468void 0;469470471