1
0
mirror of https://github.com/gorhill/uBlock.git synced 2024-11-01 16:33:06 +01:00
uBlock/platform/chromium/vapi-background-ext.js

234 lines
7.6 KiB
JavaScript
Raw Normal View History

2017-09-02 12:11:33 +02:00
/*******************************************************************************
uBlock Origin - a browser extension to block requests.
Copyright (C) 2017-present Raymond Hill
2017-09-02 12:11:33 +02:00
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
'use strict';
/******************************************************************************/
// https://github.com/uBlockOrigin/uBlock-issues/issues/1659
// Chromium fails to dispatch onCreatedNavigationTarget() events sometimes,
// so we synthetize these missing events when this happens.
vAPI.Tabs = class extends vAPI.Tabs {
constructor() {
super();
this.tabIds = new Set();
browser.tabs.onCreated.addListener(tab => {
this.onCreatedHandler(tab);
});
}
2021-07-16 20:06:59 +02:00
onCreatedHandler(tab) {
if ( typeof tab.openerTabId === 'number' ) { return; }
if ( tab.index !== 0 ) { return; }
if ( tab.url !== '' ) { return; }
this.tabIds.add(tab.id);
}
2021-07-16 20:06:59 +02:00
onCreatedNavigationTargetHandler(details) {
this.tabIds.delete(details.tabId);
super.onCreatedNavigationTargetHandler(details);
}
2021-07-16 20:06:59 +02:00
onCommittedHandler(details) {
2021-07-16 20:06:59 +02:00
if ( details.frameId === 0 ) {
this.synthesizeNavigationTargetEvent(details);
}
super.onCommittedHandler(details);
}
2021-07-16 20:06:59 +02:00
onRemovedHandler(tabId, details) {
this.tabIds.delete(tabId);
super.onRemovedHandler(tabId, details);
}
2021-07-16 20:06:59 +02:00
synthesizeNavigationTargetEvent(details) {
if ( this.tabIds.has(details.tabId) === false ) { return; }
this.tabIds.delete(details.tabId);
if (
Array.isArray(details.transitionQualifiers) === false ||
details.transitionQualifiers.includes('client_redirect') === false
) {
return;
}
this.onCreatedNavigationTargetHandler({
tabId: details.tabId,
sourceTabId: details.tabId,
sourceFrameId: 0,
url: details.url,
});
}
};
/******************************************************************************/
{
const extToTypeMap = new Map([
2017-09-02 12:11:33 +02:00
['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']
]);
const headerValue = (headers, name) => {
let i = headers.length;
2017-09-02 12:11:33 +02:00
while ( i-- ) {
if ( headers[i].name.toLowerCase() === name ) {
return headers[i].value.trim();
}
}
return '';
};
const parsedURL = new URL('https://www.example.org/');
// Extend base class to normalize as per platform.
2017-09-02 12:11:33 +02:00
vAPI.Net = class extends vAPI.Net {
constructor() {
super();
this.suspendedTabIds = new Set();
2017-09-02 12:11:33 +02:00
}
2021-07-16 20:06:59 +02:00
normalizeDetails(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;
}
2017-09-02 12:11:33 +02:00
let type = details.type;
2017-09-02 12:11:33 +02:00
if ( type === 'imageset' ) {
2017-09-02 12:11:33 +02:00
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;
2017-09-02 12:11:33 +02:00
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;
}
}
2017-09-02 12:11:33 +02:00
}
2021-07-16 20:06:59 +02:00
// https://www.reddit.com/r/uBlockOrigin/comments/9vcrk3/
// 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.
denormalizeTypes(types) {
if ( types.length === 0 ) {
return Array.from(this.validTypes);
}
const out = new Set();
for ( const type of types ) {
if ( this.validTypes.has(type) ) {
out.add(type);
}
2017-09-02 12:11:33 +02:00
}
if ( out.has('other') === false ) {
for ( const type of extToTypeMap.values() ) {
if ( out.has(type) ) {
out.add('other');
break;
}
}
2017-09-02 12:11:33 +02:00
}
return Array.from(out);
2017-09-02 12:11:33 +02:00
}
2021-07-16 20:06:59 +02:00
suspendOneRequest(details) {
this.suspendedTabIds.add(details.tabId);
return { cancel: true };
}
2021-07-16 20:06:59 +02:00
unsuspendAllRequests() {
for ( const tabId of this.suspendedTabIds ) {
vAPI.tabs.reload(tabId);
}
this.suspendedTabIds.clear();
}
};
}
/******************************************************************************/
// https://github.com/uBlockOrigin/uBlock-issues/issues/548
// Use `X-DNS-Prefetch-Control` to workaround Chromium's disregard of the
// setting "Predict network actions to improve page load performance".
vAPI.prefetching = (( ) => {
let listening = false;
const onHeadersReceived = function(details) {
details.responseHeaders.push({
name: 'X-DNS-Prefetch-Control',
value: 'off'
});
return { responseHeaders: details.responseHeaders };
};
return state => {
const wr = chrome.webRequest;
if ( state && listening ) {
wr.onHeadersReceived.removeListener(onHeadersReceived);
listening = false;
} else if ( !state && !listening ) {
wr.onHeadersReceived.addListener(
onHeadersReceived,
{
urls: [ 'http://*/*', 'https://*/*' ],
types: [ 'main_frame', 'sub_frame' ]
},
[ 'blocking', 'responseHeaders' ]
);
listening = true;
}
};
})();
/******************************************************************************/