/******************************************************************************* ηMatrix - a browser extension to black/white list requests. Copyright (C) 2014-2019 The uMatrix/uBlock Origin authors Copyright (C) 2019-2020 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/}. Home: https://libregit.org/heckyel/ematrix uMatrix Home: https://github.com/gorhill/uMatrix */ // For non background pages 'use strict'; (function (self) { if (self.vAPI === undefined) { self.vAPI = {}; } let vAPI = self.vAPI; vAPI.setTimeout = vAPI.setTimeout || function (callback, delay, extra) { return setTimeout(function (a) { callback(a); }, delay, extra); }; vAPI.sessionId = String.fromCharCode(Date.now() % 25 + 97) + Math.random().toString(36).slice(2); vAPI.shutdown = (function () { let jobs = []; let add = function (job) { jobs.push(job); }; let exec = function () { //console.debug('Shutting down...'); let job; while ((job = jobs.pop())) { job(); } }; return { add: add, exec: exec }; })(); vAPI.messaging = { listeners: new Set(), pending: new Map(), requestId: 1, connected: false, messageListenerCallback: null, toggleListenerCallback: null, start: 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 (let 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) { let details = JSON.parse(msg); if (!details) { return; } if (details.broadcast) { this.sendToListeners(details.msg); return; } if (details.requestId) { let listener = this.pending.get(details.requestId); if (listener !== undefined) { this.pending.delete(details.requestId); listener(details.msg); return; } } }, builtinListener: function (msg) { if (typeof msg.cmd === 'string' && msg.cmd === 'injectScript') { let 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('ematrix:background', message); }, toggleListener: function ({type, persisted}) { if (type === 'pagehide' && !persisted) { vAPI.shutdown.exec(); this.shutdown(); return; } if (type === 'pagehide') { this.disconnect(); } else { this.connect(); } }, sendToListeners: function (msg) { for (let 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.start() // 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). // Needs more investigating // if ( window !== window.top ) { // vAPI.shutdown.add(function() { // vAPI = null; // }); // } })(this);