Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
thewickedkarma
GitHub Repository: thewickedkarma/blackeye-im
Path: blob/master/sites/bitcoin/js/vapi-client.js
777 views
1
/*******************************************************************************
2
3
uBlock Origin - a browser extension to block requests.
4
Copyright (C) 2014-2018 The uBlock Origin authors
5
6
This program is free software: you can redistribute it and/or modify
7
it under the terms of the GNU General Public License as published by
8
the Free Software Foundation, either version 3 of the License, or
9
(at your option) any later version.
10
11
This program is distributed in the hope that it will be useful,
12
but WITHOUT ANY WARRANTY; without even the implied warranty of
13
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14
GNU General Public License for more details.
15
16
You should have received a copy of the GNU General Public License
17
along with this program. If not, see {http://www.gnu.org/licenses/}.
18
19
Home: https://github.com/gorhill/uBlock
20
*/
21
22
// For non-background page
23
24
'use strict';
25
26
/******************************************************************************/
27
28
// https://github.com/chrisaljoudi/uBlock/issues/456
29
// Skip if already injected.
30
31
// >>>>>>>> start of HUGE-IF-BLOCK
32
if ( typeof vAPI === 'object' && !vAPI.clientScript ) {
33
34
/******************************************************************************/
35
/******************************************************************************/
36
37
vAPI.clientScript = true;
38
39
vAPI.randomToken = function() {
40
return String.fromCharCode(Date.now() % 26 + 97) +
41
Math.floor(Math.random() * 982451653 + 982451653).toString(36);
42
};
43
44
vAPI.sessionId = vAPI.randomToken();
45
vAPI.chrome = true;
46
vAPI.setTimeout = vAPI.setTimeout || self.setTimeout.bind(self);
47
48
/******************************************************************************/
49
50
vAPI.shutdown = {
51
jobs: [],
52
add: function(job) {
53
this.jobs.push(job);
54
},
55
exec: function() {
56
var job;
57
while ( (job = this.jobs.pop()) ) {
58
job();
59
}
60
},
61
remove: function(job) {
62
var pos;
63
while ( (pos = this.jobs.indexOf(job)) !== -1 ) {
64
this.jobs.splice(pos, 1);
65
}
66
}
67
};
68
69
/******************************************************************************/
70
71
vAPI.messaging = {
72
port: null,
73
portTimer: null,
74
portTimerDelay: 10000,
75
channels: new Map(),
76
connections: new Map(),
77
pending: new Map(),
78
auxProcessId: 1,
79
shuttingDown: false,
80
81
Connection: function(handler, details) {
82
var messaging = vAPI.messaging;
83
this.messaging = messaging;
84
this.handler = handler;
85
this.id = details.id;
86
this.to = details.to;
87
this.toToken = details.toToken;
88
this.from = details.from;
89
this.fromToken = details.fromToken;
90
this.checkBound = this.check.bind(this);
91
this.checkTimer = undefined;
92
// On Firefox it appears ports are not automatically disconnected when
93
// navigating to another page.
94
if ( messaging.Connection.pagehide !== undefined ) { return; }
95
messaging.Connection.pagehide = function() {
96
for ( var connection of this.connections.values() ) {
97
connection.disconnect();
98
connection.handler(connection.toDetails('connectionBroken'));
99
}
100
}.bind(messaging);
101
window.addEventListener('pagehide', messaging.Connection.pagehide);
102
},
103
104
shutdown: function() {
105
this.shuttingDown = true;
106
this.destroyPort();
107
},
108
109
disconnectListener: function() {
110
this.port = null;
111
vAPI.shutdown.exec();
112
},
113
disconnectListenerBound: null,
114
115
messageListener: function(details) {
116
if ( !details ) { return; }
117
118
// Sent to all channels
119
if ( details.broadcast ) {
120
for ( var channelName of this.channels.keys() ) {
121
this.sendToChannelListeners(channelName, details.msg);
122
}
123
return;
124
}
125
126
// Response to specific message previously sent
127
var listener;
128
if ( details.auxProcessId ) {
129
listener = this.pending.get(details.auxProcessId);
130
if ( listener !== undefined ) {
131
this.pending.delete(details.auxProcessId);
132
listener(details.msg);
133
return;
134
}
135
}
136
137
if ( details.channelName !== 'vapi' ) { return; }
138
139
// Internal handler
140
var connection;
141
142
switch ( details.msg.what ) {
143
case 'connectionAccepted':
144
case 'connectionBroken':
145
case 'connectionCheck':
146
case 'connectionMessage':
147
case 'connectionRefused':
148
connection = this.connections.get(details.msg.id);
149
if ( connection === undefined ) { return; }
150
connection.receive(details.msg);
151
break;
152
case 'connectionRequested':
153
var listeners = this.channels.get(details.msg.to);
154
if ( listeners === undefined ) { return; }
155
var port = this.getPort();
156
if ( port === null ) { return; }
157
for ( listener of listeners ) {
158
if ( listener(details.msg) !== true ) { continue; }
159
details.msg.what = 'connectionAccepted';
160
details.msg.toToken = port.name;
161
connection = new this.Connection(listener, details.msg);
162
this.connections.set(connection.id, connection);
163
break;
164
}
165
if ( details.msg.what !== 'connectionAccepted' ) {
166
details.msg.what = 'connectionRefused';
167
}
168
port.postMessage(details);
169
break;
170
default:
171
break;
172
}
173
},
174
messageListenerCallback: null,
175
176
portPoller: function() {
177
this.portTimer = null;
178
if (
179
this.port !== null &&
180
this.channels.size === 0 &&
181
this.connections.size === 0 &&
182
this.pending.size === 0
183
) {
184
return this.destroyPort();
185
}
186
this.portTimer = vAPI.setTimeout(this.portPollerBound, this.portTimerDelay);
187
this.portTimerDelay = Math.min(this.portTimerDelay * 2, 60 * 60 * 1000);
188
},
189
portPollerBound: null,
190
191
destroyPort: function() {
192
if ( this.portTimer !== null ) {
193
clearTimeout(this.portTimer);
194
this.portTimer = null;
195
}
196
var port = this.port;
197
if ( port !== null ) {
198
port.disconnect();
199
port.onMessage.removeListener(this.messageListenerCallback);
200
port.onDisconnect.removeListener(this.disconnectListenerBound);
201
this.port = null;
202
}
203
this.channels.clear();
204
if ( this.connections.size !== 0 ) {
205
for ( var connection of this.connections.values() ) {
206
connection.receive({ what: 'connectionBroken' });
207
}
208
this.connections.clear();
209
}
210
// service pending callbacks
211
if ( this.pending.size !== 0 ) {
212
var pending = this.pending;
213
this.pending = new Map();
214
for ( var callback of pending.values() ) {
215
if ( typeof callback === 'function' ) {
216
callback(null);
217
}
218
}
219
}
220
},
221
222
createPort: function() {
223
if ( this.shuttingDown ) { return null; }
224
if ( this.messageListenerCallback === null ) {
225
this.messageListenerCallback = this.messageListener.bind(this);
226
this.disconnectListenerBound = this.disconnectListener.bind(this);
227
this.portPollerBound = this.portPoller.bind(this);
228
}
229
try {
230
this.port = chrome.runtime.connect({name: vAPI.sessionId}) || null;
231
} catch (ex) {
232
this.port = null;
233
}
234
if ( this.port !== null ) {
235
this.port.onMessage.addListener(this.messageListenerCallback);
236
this.port.onDisconnect.addListener(this.disconnectListenerBound);
237
this.portTimerDelay = 10000;
238
if ( this.portTimer === null ) {
239
this.portTimer = vAPI.setTimeout(
240
this.portPollerBound,
241
this.portTimerDelay
242
);
243
}
244
}
245
return this.port;
246
},
247
248
getPort: function() {
249
return this.port !== null ? this.port : this.createPort();
250
},
251
252
send: function(channelName, message, callback) {
253
// Too large a gap between the last request and the last response means
254
// the main process is no longer reachable: memory leaks and bad
255
// performance become a risk -- especially for long-lived, dynamic
256
// pages. Guard against this.
257
if ( this.pending.size > 25 ) {
258
vAPI.shutdown.exec();
259
}
260
var port = this.getPort();
261
if ( port === null ) {
262
if ( typeof callback === 'function' ) { callback(); }
263
return;
264
}
265
var auxProcessId;
266
if ( callback ) {
267
auxProcessId = this.auxProcessId++;
268
this.pending.set(auxProcessId, callback);
269
}
270
port.postMessage({
271
channelName: channelName,
272
auxProcessId: auxProcessId,
273
msg: message
274
});
275
},
276
277
connectTo: function(from, to, handler) {
278
var port = this.getPort();
279
if ( port === null ) { return; }
280
var connection = new this.Connection(handler, {
281
id: from + '-' + to + '-' + vAPI.sessionId,
282
to: to,
283
from: from,
284
fromToken: port.name
285
});
286
this.connections.set(connection.id, connection);
287
port.postMessage({
288
channelName: 'vapi',
289
msg: {
290
what: 'connectionRequested',
291
id: connection.id,
292
from: from,
293
fromToken: port.name,
294
to: to
295
}
296
});
297
return connection.id;
298
},
299
300
disconnectFrom: function(connectionId) {
301
var connection = this.connections.get(connectionId);
302
if ( connection === undefined ) { return; }
303
connection.disconnect();
304
},
305
306
sendTo: function(connectionId, payload) {
307
var connection = this.connections.get(connectionId);
308
if ( connection === undefined ) { return; }
309
connection.send(payload);
310
},
311
312
addChannelListener: function(channelName, listener) {
313
var listeners = this.channels.get(channelName);
314
if ( listeners === undefined ) {
315
this.channels.set(channelName, [ listener ]);
316
} else if ( listeners.indexOf(listener) === -1 ) {
317
listeners.push(listener);
318
}
319
this.getPort();
320
},
321
322
removeChannelListener: function(channelName, listener) {
323
var listeners = this.channels.get(channelName);
324
if ( listeners === undefined ) { return; }
325
var pos = listeners.indexOf(listener);
326
if ( pos === -1 ) { return; }
327
listeners.splice(pos, 1);
328
if ( listeners.length === 0 ) {
329
this.channels.delete(channelName);
330
}
331
},
332
333
removeAllChannelListeners: function(channelName) {
334
this.channels.delete(channelName);
335
},
336
337
sendToChannelListeners: function(channelName, msg) {
338
var listeners = this.channels.get(channelName);
339
if ( listeners === undefined ) { return; }
340
listeners = listeners.slice(0);
341
var response;
342
for ( var listener of listeners ) {
343
response = listener(msg);
344
if ( response !== undefined ) { break; }
345
}
346
return response;
347
}
348
};
349
350
/******************************************************************************/
351
352
vAPI.messaging.Connection.prototype = {
353
toDetails: function(what, payload) {
354
return {
355
what: what,
356
id: this.id,
357
from: this.from,
358
fromToken: this.fromToken,
359
to: this.to,
360
toToken: this.toToken,
361
payload: payload
362
};
363
},
364
disconnect: function() {
365
if ( this.checkTimer !== undefined ) {
366
clearTimeout(this.checkTimer);
367
this.checkTimer = undefined;
368
}
369
this.messaging.connections.delete(this.id);
370
var port = this.messaging.getPort();
371
if ( port === null ) { return; }
372
port.postMessage({
373
channelName: 'vapi',
374
msg: this.toDetails('connectionBroken')
375
});
376
},
377
checkAsync: function() {
378
if ( this.checkTimer !== undefined ) {
379
clearTimeout(this.checkTimer);
380
}
381
this.checkTimer = vAPI.setTimeout(this.checkBound, 499);
382
},
383
check: function() {
384
this.checkTimer = undefined;
385
if ( this.messaging.connections.has(this.id) === false ) { return; }
386
var port = this.messaging.getPort();
387
if ( port === null ) { return; }
388
port.postMessage({
389
channelName: 'vapi',
390
msg: this.toDetails('connectionCheck')
391
});
392
this.checkAsync();
393
},
394
receive: function(details) {
395
switch ( details.what ) {
396
case 'connectionAccepted':
397
this.toToken = details.toToken;
398
this.handler(details);
399
this.checkAsync();
400
break;
401
case 'connectionBroken':
402
this.messaging.connections.delete(this.id);
403
this.handler(details);
404
break;
405
case 'connectionMessage':
406
this.handler(details);
407
this.checkAsync();
408
break;
409
case 'connectionCheck':
410
var port = this.messaging.getPort();
411
if ( port === null ) { return; }
412
if ( this.messaging.connections.has(this.id) ) {
413
this.checkAsync();
414
} else {
415
details.what = 'connectionBroken';
416
port.postMessage({ channelName: 'vapi', msg: details });
417
}
418
break;
419
case 'connectionRefused':
420
this.messaging.connections.delete(this.id);
421
this.handler(details);
422
break;
423
}
424
},
425
send: function(payload) {
426
var port = this.messaging.getPort();
427
if ( port === null ) { return; }
428
port.postMessage({
429
channelName: 'vapi',
430
msg: this.toDetails('connectionMessage', payload)
431
});
432
}
433
};
434
435
/******************************************************************************/
436
437
vAPI.shutdown.add(function() {
438
vAPI.messaging.shutdown();
439
window.vAPI = undefined;
440
});
441
442
// https://www.youtube.com/watch?v=rT5zCHn0tsg
443
// https://www.youtube.com/watch?v=E-jS4e3zacI
444
445
/******************************************************************************/
446
/******************************************************************************/
447
448
}
449
// <<<<<<<< end of HUGE-IF-BLOCK
450
451
452
453
454
455
456
457
458
/*******************************************************************************
459
460
DO NOT:
461
- Remove the following code
462
- Add code beyond the following code
463
Reason:
464
- https://github.com/gorhill/uBlock/pull/3721
465
- uBO never uses the return value from injected content scripts
466
467
**/
468
469
void 0;
470
471