summaryrefslogtreecommitdiff
path: root/data/extensions/uBlock0@raymondhill.net/js/vapi-client.js
diff options
context:
space:
mode:
Diffstat (limited to 'data/extensions/uBlock0@raymondhill.net/js/vapi-client.js')
-rw-r--r--data/extensions/uBlock0@raymondhill.net/js/vapi-client.js248
1 files changed, 248 insertions, 0 deletions
diff --git a/data/extensions/uBlock0@raymondhill.net/js/vapi-client.js b/data/extensions/uBlock0@raymondhill.net/js/vapi-client.js
new file mode 100644
index 0000000..afa939d
--- /dev/null
+++ b/data/extensions/uBlock0@raymondhill.net/js/vapi-client.js
@@ -0,0 +1,248 @@
+/*******************************************************************************
+
+ uBlock Origin - a comprehensive, efficient content blocker
+ Copyright (C) 2014-2015 The uBlock Origin authors
+ Copyright (C) 2014-present Raymond Hill
+
+ 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
+*/
+
+// For non-background page
+
+/******************************************************************************/
+
+// https://github.com/chrisaljoudi/uBlock/issues/456
+// Skip if already injected.
+
+// >>>>>>>> start of HUGE-IF-BLOCK
+if (
+ typeof vAPI === 'object' &&
+ vAPI.randomToken instanceof Function === false
+) {
+
+/******************************************************************************/
+/******************************************************************************/
+
+vAPI.randomToken = function() {
+ const n = Math.random();
+ return String.fromCharCode(n * 25 + 97) +
+ Math.floor(
+ (0.25 + n * 0.75) * Number.MAX_SAFE_INTEGER
+ ).toString(36).slice(-8);
+};
+
+vAPI.sessionId = vAPI.randomToken();
+vAPI.setTimeout = vAPI.setTimeout || self.setTimeout.bind(self);
+
+/******************************************************************************/
+
+vAPI.shutdown = {
+ jobs: [],
+ add: function(job) {
+ this.jobs.push(job);
+ },
+ exec: function() {
+ // Shutdown asynchronously, to ensure shutdown jobs are called from
+ // the top context.
+ self.requestIdleCallback(( ) => {
+ const jobs = this.jobs.slice();
+ this.jobs.length = 0;
+ while ( jobs.length !== 0 ) {
+ (jobs.pop())();
+ }
+ });
+ },
+ remove: function(job) {
+ let pos;
+ while ( (pos = this.jobs.indexOf(job)) !== -1 ) {
+ this.jobs.splice(pos, 1);
+ }
+ }
+};
+
+/******************************************************************************/
+
+vAPI.messaging = {
+ port: null,
+ portTimer: null,
+ portTimerDelay: 10000,
+ msgIdGenerator: 1,
+ pending: new Map(),
+ waitStartTime: 0,
+ shuttingDown: false,
+
+ shutdown: function() {
+ this.shuttingDown = true;
+ this.destroyPort();
+ },
+
+ // https://github.com/uBlockOrigin/uBlock-issues/issues/403
+ // Spurious disconnection can happen, so do not consider such events
+ // as world-ending, i.e. stay around. Except for embedded frames.
+
+ disconnectListener: function() {
+ void browser.runtime.lastError;
+ this.port = null;
+ if ( window !== window.top ) {
+ vAPI.shutdown.exec();
+ } else {
+ this.destroyPort();
+ }
+ },
+ disconnectListenerBound: null,
+
+ // 2020-09-01:
+ // In Firefox, `details instanceof Object` resolves to `false` despite
+ // `details` being a valid object. Consequently, falling back to use
+ // `typeof details`.
+ // This is an issue which surfaced when the element picker code was
+ // revisited to isolate the picker dialog DOM from the page DOM.
+ messageListener: function(details) {
+ if ( typeof details !== 'object' || details === null ) { return; }
+ if ( details.msgId === undefined ) { return; }
+ const resolver = this.pending.get(details.msgId);
+ if ( resolver === undefined ) { return; }
+ this.pending.delete(details.msgId);
+ resolver(details.msg);
+ },
+ messageListenerBound: null,
+
+ canDestroyPort: function() {
+ return this.pending.size === 0;
+ },
+
+ portPoller: function() {
+ this.portTimer = null;
+ if ( this.port !== null && this.canDestroyPort() ) {
+ return this.destroyPort();
+ }
+ this.portTimer = vAPI.setTimeout(this.portPollerBound, this.portTimerDelay);
+ this.portTimerDelay = Math.min(this.portTimerDelay * 2, 60 * 60 * 1000);
+ },
+ portPollerBound: null,
+
+ destroyPort: function() {
+ if ( this.portTimer !== null ) {
+ clearTimeout(this.portTimer);
+ this.portTimer = null;
+ }
+ const port = this.port;
+ if ( port !== null ) {
+ port.disconnect();
+ port.onMessage.removeListener(this.messageListenerBound);
+ port.onDisconnect.removeListener(this.disconnectListenerBound);
+ this.port = null;
+ }
+ // service pending callbacks
+ if ( this.pending.size !== 0 ) {
+ const pending = this.pending;
+ this.pending = new Map();
+ for ( const resolver of pending.values() ) {
+ resolver();
+ }
+ }
+ },
+
+ createPort: function() {
+ if ( this.shuttingDown ) { return null; }
+ if ( this.messageListenerBound === null ) {
+ this.messageListenerBound = this.messageListener.bind(this);
+ this.disconnectListenerBound = this.disconnectListener.bind(this);
+ this.portPollerBound = this.portPoller.bind(this);
+ }
+ try {
+ this.port = browser.runtime.connect({name: vAPI.sessionId}) || null;
+ } catch {
+ this.port = null;
+ }
+ // Not having a valid port at this point means the main process is
+ // not available: no point keeping the content scripts alive.
+ if ( this.port === null ) {
+ vAPI.shutdown.exec();
+ return null;
+ }
+ this.port.onMessage.addListener(this.messageListenerBound);
+ this.port.onDisconnect.addListener(this.disconnectListenerBound);
+ this.portTimerDelay = 10000;
+ if ( this.portTimer === null ) {
+ this.portTimer = vAPI.setTimeout(
+ this.portPollerBound,
+ this.portTimerDelay
+ );
+ }
+ return this.port;
+ },
+
+ getPort: function() {
+ return this.port !== null ? this.port : this.createPort();
+ },
+
+ send: function(channel, msg) {
+ // Too large a gap between the last request and the last response means
+ // the main process is no longer reachable: memory leaks and bad
+ // performance become a risk -- especially for long-lived, dynamic
+ // pages. Guard against this.
+ if ( this.pending.size > 64 ) {
+ if ( (Date.now() - this.waitStartTime) > 60000 ) {
+ vAPI.shutdown.exec();
+ }
+ }
+ const port = this.getPort();
+ if ( port === null ) {
+ return Promise.resolve();
+ }
+ if ( this.pending.size === 0 ) {
+ this.waitStartTime = Date.now();
+ }
+ const msgId = this.msgIdGenerator++;
+ const promise = new Promise(resolve => {
+ this.pending.set(msgId, resolve);
+ });
+ port.postMessage({ channel, msgId, msg });
+ return promise;
+ },
+};
+
+vAPI.shutdown.add(( ) => {
+ vAPI.messaging.shutdown();
+ window.vAPI = undefined;
+});
+
+/******************************************************************************/
+/******************************************************************************/
+
+}
+// <<<<<<<< end of HUGE-IF-BLOCK
+
+
+
+
+
+
+
+
+/*******************************************************************************
+
+ DO NOT:
+ - Remove the following code
+ - Add code beyond the following code
+ Reason:
+ - https://github.com/gorhill/uBlock/pull/3721
+ - uBO never uses the return value from injected content scripts
+
+**/
+
+void 0;