Browse Source
Add ability to block early at launch; adapt to changes in Chromium 72+
Add ability to block early at launch; adapt to changes in Chromium 72+
Related issues: - "Requests bypass uMatrix on Firefox start" <https://github.com/uBlockOrigin/uMatrix-issues/issues/69> Using same approach as with uBO: https://github.com/gorhill/uBloc/commit/41548be6be35 `suspendTabsUntilReady` advanced setting added to "More" pane, useful only for Chromium -- the blocking of early network requests is enforced unconditionally on Firefox (because it supports returning Promises from webRequest handlers). - "Cookies leaking temporarily" <https://github.com/uBlockOrigin/uMatrix-issues/issues/74> Changes in the webRequest API in Chromium 72+ caused uMatrix to fail to process `Cookie` and `Referer` headers on that platform.pull/2/head
Raymond Hill
6 years ago
No known key found for this signature in database
GPG Key ID: 25E1490B761470C2
13 changed files with 623 additions and 460 deletions
-
234platform/chromium/polyfill.js
-
133platform/chromium/vapi-background.js
-
12platform/chromium/vapi-common.js
-
194platform/chromium/vapi-webrequest.js
-
30platform/firefox/polyfill.js
-
185platform/firefox/vapi-webrequest.js
-
9src/background.html
-
35src/js/background.js
-
4src/js/raw-settings.js
-
69src/js/start.js
-
97src/js/storage.js
-
78src/js/traffic.js
-
3tools/make-firefox.sh
@ -1,234 +0,0 @@ |
|||
/******************************************************************************* |
|||
|
|||
uMatrix - a browser extension to block requests. |
|||
Copyright (C) 2017 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/uMatrix
|
|||
|
|||
This file has been originally imported from: |
|||
https://github.com/gorhill/uBlock/tree/master/platform/chromium
|
|||
|
|||
*/ |
|||
|
|||
// For background page or non-background pages
|
|||
|
|||
/* exported objectAssign */ |
|||
|
|||
'use strict'; |
|||
|
|||
/******************************************************************************/ |
|||
/******************************************************************************/ |
|||
|
|||
// https://github.com/gorhill/uBlock/issues/1067
|
|||
// https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/String/startsWith
|
|||
// Firefox 17/Chromium 41 supports `startsWith`.
|
|||
|
|||
if ( String.prototype.startsWith instanceof Function === false ) { |
|||
String.prototype.startsWith = function(needle, pos) { |
|||
if ( typeof pos !== 'number' ) { |
|||
pos = 0; |
|||
} |
|||
return this.lastIndexOf(needle, pos) === pos; |
|||
}; |
|||
} |
|||
|
|||
/******************************************************************************/ |
|||
|
|||
// https://github.com/gorhill/uBlock/issues/1067
|
|||
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/endsWith
|
|||
// Firefox 17/Chromium 41 supports `endsWith`.
|
|||
|
|||
if ( String.prototype.endsWith instanceof Function === false ) { |
|||
String.prototype.endsWith = function(needle, pos) { |
|||
if ( typeof pos !== 'number' ) { |
|||
pos = this.length; |
|||
} |
|||
pos -= needle.length; |
|||
return this.indexOf(needle, pos) === pos; |
|||
}; |
|||
} |
|||
|
|||
/******************************************************************************/ |
|||
|
|||
// As per MDN, Object.assign appeared first in Chromium 45.
|
|||
// https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Object/assign#Browser_compatibility
|
|||
|
|||
var objectAssign = Object.assign || function(target, source) { |
|||
var keys = Object.keys(source); |
|||
for ( var i = 0, n = keys.length, key; i < n; i++ ) { |
|||
key = keys[i]; |
|||
target[key] = source[key]; |
|||
} |
|||
return target; |
|||
}; |
|||
|
|||
/******************************************************************************/ |
|||
|
|||
// https://github.com/gorhill/uBlock/issues/1070
|
|||
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Set#Browser_compatibility
|
|||
// This polyfill is designed to fulfill *only* what uBlock Origin needs -- this
|
|||
// is not an accurate API of the real Set() type.
|
|||
|
|||
if ( self.Set instanceof Function === false ) { |
|||
self.Set = function(iter) { |
|||
this.clear(); |
|||
if ( Array.isArray(iter) ) { |
|||
for ( var i = 0, n = iter.length; i < n; i++ ) { |
|||
this.add(iter[i]); |
|||
} |
|||
return; |
|||
} |
|||
}; |
|||
|
|||
self.Set.polyfill = true; |
|||
|
|||
self.Set.prototype.clear = function() { |
|||
this._set = Object.create(null); |
|||
this.size = 0; |
|||
// Iterator stuff
|
|||
this._values = undefined; |
|||
this._i = undefined; |
|||
this.value = undefined; |
|||
this.done = true; |
|||
}; |
|||
|
|||
self.Set.prototype.add = function(k) { |
|||
if ( this._set[k] === undefined ) { |
|||
this._set[k] = true; |
|||
this.size += 1; |
|||
} |
|||
return this; |
|||
}; |
|||
|
|||
self.Set.prototype.delete = function(k) { |
|||
if ( this._set[k] !== undefined ) { |
|||
delete this._set[k]; |
|||
this.size -= 1; |
|||
return true; |
|||
} |
|||
return false; |
|||
}; |
|||
|
|||
self.Set.prototype.has = function(k) { |
|||
return this._set[k] !== undefined; |
|||
}; |
|||
|
|||
self.Set.prototype.next = function() { |
|||
if ( this._i < this.size ) { |
|||
this.value = this._values[this._i++]; |
|||
} else { |
|||
this._values = undefined; |
|||
this.value = undefined; |
|||
this.done = true; |
|||
} |
|||
return this; |
|||
}; |
|||
|
|||
self.Set.prototype.values = function() { |
|||
this._values = Object.keys(this._set); |
|||
this._i = 0; |
|||
this.value = undefined; |
|||
this.done = false; |
|||
return this; |
|||
}; |
|||
} |
|||
|
|||
/******************************************************************************/ |
|||
|
|||
// https://github.com/gorhill/uBlock/issues/1070
|
|||
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Set#Browser_compatibility
|
|||
// This polyfill is designed to fulfill *only* what uBlock Origin needs -- this
|
|||
// is not an accurate API of the real Map() type.
|
|||
|
|||
if ( self.Map instanceof Function === false ) { |
|||
self.Map = function(iter) { |
|||
this.clear(); |
|||
if ( Array.isArray(iter) ) { |
|||
for ( var i = 0, n = iter.length, entry; i < n; i++ ) { |
|||
entry = iter[i]; |
|||
this.set(entry[0], entry[1]); |
|||
} |
|||
return; |
|||
} |
|||
}; |
|||
|
|||
self.Map.polyfill = true; |
|||
|
|||
self.Map.prototype.clear = function() { |
|||
this._map = Object.create(null); |
|||
this.size = 0; |
|||
// Iterator stuff
|
|||
this._keys = undefined; |
|||
this._i = undefined; |
|||
this.value = undefined; |
|||
this.done = true; |
|||
}; |
|||
|
|||
self.Map.prototype.delete = function(k) { |
|||
if ( this._map[k] !== undefined ) { |
|||
delete this._map[k]; |
|||
this.size -= 1; |
|||
return true; |
|||
} |
|||
return false; |
|||
}; |
|||
|
|||
self.Map.prototype.entries = function() { |
|||
this._keys = Object.keys(this._map); |
|||
this._i = 0; |
|||
this.value = [ undefined, undefined ]; |
|||
this.done = false; |
|||
return this; |
|||
}; |
|||
|
|||
self.Map.prototype.get = function(k) { |
|||
return this._map[k]; |
|||
}; |
|||
|
|||
self.Map.prototype.has = function(k) { |
|||
return this._map[k] !== undefined; |
|||
}; |
|||
|
|||
self.Map.prototype.next = function() { |
|||
if ( this._i < this.size ) { |
|||
var key = this._keys[this._i++]; |
|||
this.value[0] = key; |
|||
this.value[1] = this._map[key]; |
|||
} else { |
|||
this._keys = undefined; |
|||
this.value = undefined; |
|||
this.done = true; |
|||
} |
|||
return this; |
|||
}; |
|||
|
|||
self.Map.prototype.set = function(k, v) { |
|||
if ( v !== undefined ) { |
|||
if ( this._map[k] === undefined ) { |
|||
this.size += 1; |
|||
} |
|||
this._map[k] = v; |
|||
} else { |
|||
if ( this._map[k] !== undefined ) { |
|||
this.size -= 1; |
|||
} |
|||
delete this._map[k]; |
|||
} |
|||
return this; |
|||
}; |
|||
} |
|||
|
|||
/******************************************************************************/ |
@ -0,0 +1,194 @@ |
|||
/******************************************************************************* |
|||
|
|||
uMatrix - a browser extension to block requests. |
|||
Copyright (C) 2017-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/uMatrix
|
|||
*/ |
|||
|
|||
// For background page
|
|||
|
|||
'use strict'; |
|||
|
|||
/******************************************************************************/ |
|||
|
|||
(function() { |
|||
const extToTypeMap = new Map([ |
|||
['eot','font'],['otf','font'],['svg','font'],['ttf','font'],['woff','font'],['woff2','font'], |
|||
['mp3','media'],['mp4','media'],['webm','media'], |
|||
['gif','image'],['ico','image'],['jpeg','image'],['jpg','image'],['png','image'],['webp','image'] |
|||
]); |
|||
|
|||
// https://www.reddit.com/r/uBlockOrigin/comments/9vcrk3/bug_in_ubo_1173_betas_when_saving_files_hosted_on/
|
|||
// Some types can be mapped from 'other', thus include 'other' if and
|
|||
// only if the caller is interested in at least one of those types.
|
|||
const denormalizeTypes = function(aa) { |
|||
if ( aa.length === 0 ) { |
|||
return Array.from(vAPI.net.validTypes); |
|||
} |
|||
const out = new Set(); |
|||
let i = aa.length; |
|||
while ( i-- ) { |
|||
const type = aa[i]; |
|||
if ( vAPI.net.validTypes.has(type) ) { |
|||
out.add(type); |
|||
} |
|||
} |
|||
if ( out.has('other') === false ) { |
|||
for ( const type of extToTypeMap.values() ) { |
|||
if ( out.has(type) ) { |
|||
out.add('other'); |
|||
break; |
|||
} |
|||
} |
|||
} |
|||
return Array.from(out); |
|||
}; |
|||
|
|||
const headerValue = function(headers, name) { |
|||
let i = headers.length; |
|||
while ( i-- ) { |
|||
if ( headers[i].name.toLowerCase() === name ) { |
|||
return headers[i].value.trim(); |
|||
} |
|||
} |
|||
return ''; |
|||
}; |
|||
|
|||
const parsedURL = new URL('https://www.example.org/'); |
|||
|
|||
vAPI.net.normalizeDetails = function(details) { |
|||
// Chromium 63+ supports the `initiator` property, which contains
|
|||
// the URL of the origin from which the network request was made.
|
|||
if ( |
|||
typeof details.initiator === 'string' && |
|||
details.initiator !== 'null' |
|||
) { |
|||
details.documentUrl = details.initiator; |
|||
} |
|||
|
|||
let type = details.type; |
|||
|
|||
// https://github.com/gorhill/uBlock/issues/1493
|
|||
// Chromium 49+/WebExtensions support a new request type: `ping`,
|
|||
// which is fired as a result of using `navigator.sendBeacon`.
|
|||
if ( type === 'ping' ) { |
|||
details.type = 'beacon'; |
|||
return; |
|||
} |
|||
|
|||
if ( type === 'imageset' ) { |
|||
details.type = 'image'; |
|||
return; |
|||
} |
|||
|
|||
// The rest of the function code is to normalize type
|
|||
if ( type !== 'other' ) { return; } |
|||
|
|||
// Try to map known "extension" part of URL to request type.
|
|||
parsedURL.href = details.url; |
|||
const path = parsedURL.pathname, |
|||
pos = path.indexOf('.', path.length - 6); |
|||
if ( pos !== -1 && (type = extToTypeMap.get(path.slice(pos + 1))) ) { |
|||
details.type = type; |
|||
return; |
|||
} |
|||
|
|||
// Try to extract type from response headers if present.
|
|||
if ( details.responseHeaders ) { |
|||
type = headerValue(details.responseHeaders, 'content-type'); |
|||
if ( type.startsWith('font/') ) { |
|||
details.type = 'font'; |
|||
return; |
|||
} |
|||
if ( type.startsWith('image/') ) { |
|||
details.type = 'image'; |
|||
return; |
|||
} |
|||
if ( type.startsWith('audio/') || type.startsWith('video/') ) { |
|||
details.type = 'media'; |
|||
return; |
|||
} |
|||
} |
|||
}; |
|||
|
|||
vAPI.net.denormalizeFilters = function(filters) { |
|||
const urls = filters.urls || [ '<all_urls>' ]; |
|||
let types = filters.types; |
|||
if ( Array.isArray(types) ) { |
|||
types = denormalizeTypes(types); |
|||
} |
|||
if ( |
|||
(vAPI.net.validTypes.has('websocket')) && |
|||
(types === undefined || types.indexOf('websocket') !== -1) && |
|||
(urls.indexOf('<all_urls>') === -1) |
|||
) { |
|||
if ( urls.indexOf('ws://*/*') === -1 ) { |
|||
urls.push('ws://*/*'); |
|||
} |
|||
if ( urls.indexOf('wss://*/*') === -1 ) { |
|||
urls.push('wss://*/*'); |
|||
} |
|||
} |
|||
return { types, urls }; |
|||
}; |
|||
})(); |
|||
|
|||
/******************************************************************************/ |
|||
|
|||
// https://github.com/gorhill/uBlock/issues/2067
|
|||
// Experimental: Block everything until uBO is fully ready.
|
|||
|
|||
vAPI.net.onBeforeReady = (function() { |
|||
let pendings; |
|||
|
|||
const handler = function(details) { |
|||
if ( pendings === undefined ) { return; } |
|||
if ( details.tabId < 0 ) { return; } |
|||
|
|||
pendings.add(details.tabId); |
|||
|
|||
//console.log(`Aborting tab ${details.tabId}: ${details.type} ${details.url}`);
|
|||
|
|||
return { cancel: true }; |
|||
}; |
|||
|
|||
return { |
|||
experimental: true, |
|||
start: function() { |
|||
pendings = new Set(); |
|||
browser.webRequest.onBeforeRequest.addListener( |
|||
handler, |
|||
{ urls: [ 'http://*/*', 'https://*/*' ] }, |
|||
[ 'blocking' ] |
|||
); |
|||
}, |
|||
// https://github.com/gorhill/uBlock/issues/2067
|
|||
// Force-reload tabs for which network requests were blocked
|
|||
// during launch. This can happen only if tabs were "suspended".
|
|||
stop: function() { |
|||
if ( pendings === undefined ) { return; } |
|||
browser.webRequest.onBeforeRequest.removeListener(handler); |
|||
for ( const tabId of pendings ) { |
|||
//console.log(`Reloading tab ${tabId}`);
|
|||
vAPI.tabs.reload(tabId); |
|||
} |
|||
pendings = undefined; |
|||
}, |
|||
}; |
|||
})(); |
|||
|
|||
/******************************************************************************/ |
@ -1,30 +0,0 @@ |
|||
/******************************************************************************* |
|||
|
|||
uBlock Origin - a browser extension to block requests. |
|||
Copyright (C) 2016 The uBlock Origin authors |
|||
|
|||
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 background page or non-background pages
|
|||
|
|||
'use strict'; |
|||
|
|||
/******************************************************************************/ |
|||
|
|||
var objectAssign = Object.assign; |
|||
|
|||
/******************************************************************************/ |
@ -0,0 +1,185 @@ |
|||
/******************************************************************************* |
|||
|
|||
uMatrix - a browser extension to block requests. |
|||
Copyright (C) 2017-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/uMatrix
|
|||
*/ |
|||
|
|||
// For background page
|
|||
|
|||
'use strict'; |
|||
|
|||
/******************************************************************************/ |
|||
|
|||
(function() { |
|||
|
|||
// https://github.com/gorhill/uBlock/issues/2950
|
|||
// Firefox 56 does not normalize URLs to ASCII, uBO must do this itself.
|
|||
// https://bugzilla.mozilla.org/show_bug.cgi?id=945240
|
|||
const evalMustPunycode = function() { |
|||
return vAPI.webextFlavor.soup.has('firefox') && |
|||
vAPI.webextFlavor.major < 57; |
|||
}; |
|||
|
|||
let mustPunycode = evalMustPunycode(); |
|||
|
|||
// The real actual webextFlavor value may not be set in stone, so listen
|
|||
// for possible future changes.
|
|||
window.addEventListener('webextFlavor', ( ) => { |
|||
mustPunycode = evalMustPunycode(); |
|||
}, { once: true }); |
|||
|
|||
const denormalizeTypes = function(aa) { |
|||
if ( aa.length === 0 ) { |
|||
return Array.from(vAPI.net.validTypes); |
|||
} |
|||
const out = new Set(); |
|||
let i = aa.length; |
|||
while ( i-- ) { |
|||
let type = aa[i]; |
|||
if ( vAPI.net.validTypes.has(type) ) { |
|||
out.add(type); |
|||
} |
|||
if ( type === 'image' && vAPI.net.validTypes.has('imageset') ) { |
|||
out.add('imageset'); |
|||
} |
|||
if ( type === 'sub_frame' ) { |
|||
out.add('object'); |
|||
} |
|||
} |
|||
return Array.from(out); |
|||
}; |
|||
|
|||
const punycode = self.punycode; |
|||
const reAsciiHostname = /^https?:\/\/[0-9a-z_.:@-]+[/?#]/; |
|||
const parsedURL = new URL('about:blank'); |
|||
|
|||
vAPI.net.normalizeDetails = function(details) { |
|||
if ( mustPunycode && !reAsciiHostname.test(details.url) ) { |
|||
parsedURL.href = details.url; |
|||
details.url = details.url.replace( |
|||
parsedURL.hostname, |
|||
punycode.toASCII(parsedURL.hostname) |
|||
); |
|||
} |
|||
|
|||
const type = details.type; |
|||
|
|||
// https://github.com/gorhill/uBlock/issues/1493
|
|||
// Chromium 49+/WebExtensions support a new request type: `ping`,
|
|||
// which is fired as a result of using `navigator.sendBeacon`.
|
|||
if ( type === 'ping' ) { |
|||
details.type = 'beacon'; |
|||
return; |
|||
} |
|||
|
|||
if ( type === 'imageset' ) { |
|||
details.type = 'image'; |
|||
return; |
|||
} |
|||
|
|||
// https://github.com/uBlockOrigin/uBlock-issues/issues/345
|
|||
// Re-categorize an embedded object as a `sub_frame` if its
|
|||
// content type is that of a HTML document.
|
|||
if ( type === 'object' && Array.isArray(details.responseHeaders) ) { |
|||
for ( const header of details.responseHeaders ) { |
|||
if ( header.name.toLowerCase() === 'content-type' ) { |
|||
if ( header.value.startsWith('text/html') ) { |
|||
details.type = 'sub_frame'; |
|||
} |
|||
break; |
|||
} |
|||
} |
|||
} |
|||
}; |
|||
|
|||
vAPI.net.denormalizeFilters = function(filters) { |
|||
const urls = filters.urls || [ '<all_urls>' ]; |
|||
let types = filters.types; |
|||
if ( Array.isArray(types) ) { |
|||
types = denormalizeTypes(types); |
|||
} |
|||
if ( |
|||
(vAPI.net.validTypes.has('websocket')) && |
|||
(types === undefined || types.indexOf('websocket') !== -1) && |
|||
(urls.indexOf('<all_urls>') === -1) |
|||
) { |
|||
if ( urls.indexOf('ws://*/*') === -1 ) { |
|||
urls.push('ws://*/*'); |
|||
} |
|||
if ( urls.indexOf('wss://*/*') === -1 ) { |
|||
urls.push('wss://*/*'); |
|||
} |
|||
} |
|||
return { types, urls }; |
|||
}; |
|||
})(); |
|||
|
|||
/******************************************************************************/ |
|||
|
|||
// Related issues:
|
|||
// - https://github.com/gorhill/uBlock/issues/1327
|
|||
// - https://github.com/uBlockOrigin/uBlock-issues/issues/128
|
|||
// - https://bugzilla.mozilla.org/show_bug.cgi?id=1503721
|
|||
|
|||
vAPI.net.onBeforeReady = (function() { |
|||
let pendings; |
|||
|
|||
const handler = function(details) { |
|||
if ( pendings === undefined ) { return; } |
|||
if ( details.tabId < 0 ) { return; } |
|||
|
|||
//console.log(`Deferring tab ${details.tabId}: ${details.type} ${details.url}`);
|
|||
|
|||
const pending = { |
|||
details: Object.assign({}, details), |
|||
resolve: undefined, |
|||
promise: undefined |
|||
}; |
|||
|
|||
pending.promise = new Promise(function(resolve) { |
|||
pending.resolve = resolve; |
|||
}); |
|||
|
|||
pendings.push(pending); |
|||
|
|||
return pending.promise; |
|||
}; |
|||
|
|||
return { |
|||
start: function() { |
|||
pendings = []; |
|||
browser.webRequest.onBeforeRequest.addListener( |
|||
handler, |
|||
{ urls: [ 'http://*/*', 'https://*/*' ] }, |
|||
[ 'blocking' ] |
|||
); |
|||
}, |
|||
stop: function(resolver) { |
|||
if ( pendings === undefined ) { return; } |
|||
for ( const pending of pendings ) { |
|||
const details = pending.details; |
|||
vAPI.net.normalizeDetails(details); |
|||
//console.log(`Processing tab ${details.tabId}: ${details.type} ${details.url}`);
|
|||
pending.resolve(resolver(details)); |
|||
} |
|||
pendings = undefined; |
|||
}, |
|||
}; |
|||
})(); |
|||
|
|||
/******************************************************************************/ |
Write
Preview
Loading…
Cancel
Save
Reference in new issue