aboutsummaryrefslogtreecommitdiffstats
path: root/js/vapi-client.js
diff options
context:
space:
mode:
Diffstat (limited to 'js/vapi-client.js')
-rw-r--r--js/vapi-client.js226
1 files changed, 226 insertions, 0 deletions
diff --git a/js/vapi-client.js b/js/vapi-client.js
new file mode 100644
index 0000000..7f9521d
--- /dev/null
+++ b/js/vapi-client.js
@@ -0,0 +1,226 @@
+/*******************************************************************************
+
+ ηMatrix - a browser extension to black/white list requests.
+ Copyright (C) 2014-2019 The uMatrix/uBlock Origin authors
+ Copyright (C) 2019 Alessio Vanni
+
+ 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/}.
+
+ uMatrix Home: https://github.com/gorhill/uMatrix
+*/
+
+/* jshint esnext: true */
+/* global addMessageListener, removeMessageListener, sendAsyncMessage */
+
+// For non background pages
+
+'use strict';
+
+/******************************************************************************/
+
+(function(self) {
+
+/******************************************************************************/
+
+// https://bugs.chromium.org/p/project-zero/issues/detail?id=1225&desc=6#c10
+if ( self.vAPI === undefined || self.vAPI.uMatrix !== true ) {
+ self.vAPI = { uMatrix: true };
+}
+
+var vAPI = self.vAPI;
+vAPI.firefox = true;
+vAPI.sessionId = String.fromCharCode(Date.now() % 25 + 97) +
+ Math.random().toString(36).slice(2);
+
+/******************************************************************************/
+
+vAPI.setTimeout = vAPI.setTimeout || function(callback, delay) {
+ return setTimeout(function() { callback(); }, delay);
+};
+
+/******************************************************************************/
+
+vAPI.shutdown = (function() {
+ var jobs = [];
+
+ var add = function(job) {
+ jobs.push(job);
+ };
+
+ var exec = function() {
+ //console.debug('Shutting down...');
+ var job;
+ while ( (job = jobs.pop()) ) {
+ job();
+ }
+ };
+
+ return {
+ add: add,
+ exec: exec
+ };
+})();
+
+/******************************************************************************/
+
+vAPI.messaging = {
+ listeners: new Set(),
+ pending: new Map(),
+ requestId: 1,
+ connected: false,
+
+ setup: function() {
+ this.addListener(this.builtinListener);
+ if ( this.toggleListenerCallback === null ) {
+ this.toggleListenerCallback = this.toggleListener.bind(this);
+ }
+ window.addEventListener('pagehide', this.toggleListenerCallback, true);
+ window.addEventListener('pageshow', this.toggleListenerCallback, true);
+ },
+
+ shutdown: function() {
+ if ( this.toggleListenerCallback !== null ) {
+ window.removeEventListener('pagehide', this.toggleListenerCallback, true);
+ window.removeEventListener('pageshow', this.toggleListenerCallback, true);
+ }
+ this.removeAllListeners();
+ //service pending callbacks
+ var pending = this.pending;
+ this.pending.clear();
+ for ( var callback of pending.values() ) {
+ if ( typeof callback === 'function' ) {
+ callback(null);
+ }
+ }
+ },
+
+ connect: function() {
+ if ( !this.connected ) {
+ if ( this.messageListenerCallback === null ) {
+ this.messageListenerCallback = this.messageListener.bind(this);
+ }
+ addMessageListener(this.messageListenerCallback);
+ this.connected = true;
+ }
+ },
+
+ disconnect: function() {
+ if ( this.connected ) {
+ removeMessageListener();
+ this.connected = false;
+ }
+ },
+
+ messageListener: function(msg) {
+ var details = JSON.parse(msg);
+ if ( !details ) {
+ return;
+ }
+
+ if ( details.broadcast ) {
+ this.sendToListeners(details.msg);
+ return;
+ }
+
+ if ( details.requestId ) {
+ var listener = this.pending.get(details.requestId);
+ if ( listener !== undefined ) {
+ this.pending.delete(details.requestId);
+ listener(details.msg);
+ return;
+ }
+ }
+ },
+ messageListenerCallback: null,
+
+ builtinListener: function(msg) {
+ if ( typeof msg.cmd === 'string' && msg.cmd === 'injectScript' ) {
+ var details = msg.details;
+ if ( !details.allFrames && window !== window.top ) {
+ return;
+ }
+ self.injectScript(details.file);
+ }
+ },
+
+ send: function(channelName, message, callback) {
+ this.connect()
+
+ message = {
+ channelName: self._sandboxId_ + '|' + channelName,
+ msg: message
+ };
+
+ if ( callback ) {
+ message.requestId = this.requestId++;
+ this.pending.set(message.requestId, callback);
+ }
+
+ sendAsyncMessage('umatrix:background', message);
+ },
+
+ toggleListener: function({type, persisted}) {
+ if ( type === 'pagehide' && !persisted ) {
+ vAPI.shutdown.exec();
+ this.shutdown();
+ return;
+ }
+
+ if ( type === 'pagehide' ) {
+ this.disconnect();
+ } else /* if ( type === 'pageshow' ) */ {
+ this.connect();
+ }
+ },
+ toggleListenerCallback: null,
+
+ sendToListeners: function(msg) {
+ for ( var listener of this.listeners ) {
+ listener(msg);
+ }
+ },
+
+ addListener: function(listener) {
+ this.listeners.add(listener);
+ this.connect()
+ },
+
+ removeListener: function(listener) {
+ this.listeners.delete(listener);
+ },
+
+ removeAllListeners: function() {
+ this.disconnect();
+ this.listeners.clear();;
+ }
+};
+
+vAPI.messaging.setup()
+
+/******************************************************************************/
+
+// No need to have vAPI client linger around after shutdown if
+// we are not a top window (because element picker can still
+// be injected in top window).
+if ( window !== window.top ) {
+ vAPI.shutdown.add(function() {
+ vAPI = null;
+ });
+}
+
+/******************************************************************************/
+
+})(this);
+
+/******************************************************************************/