Remove `assets` dependency from redirect engine

Related issue:
- https://github.com/uBlockOrigin/uBlock-issues/issues/1664

This change allows to add the redirect engine into the
nodejs package. The purpose of the redirect engine is to
resolve a redirect token into a path to a local resource,
to be used by the caller as wished.
This commit is contained in:
Raymond Hill 2021-08-02 09:23:48 -04:00
parent 3879835324
commit f8daea085b
No known key found for this signature in database
GPG Key ID: 25E1490B761470C2
6 changed files with 66 additions and 42 deletions

View File

@ -23,8 +23,6 @@
/******************************************************************************/
import io from './assets.js';
import {
LineIterator,
orphanizeString,
@ -204,6 +202,14 @@ const mimeFromName = function(name) {
}
};
// vAPI.warSecret() is optional, it could be absent in some environments,
// i.e. nodejs for example. Probably the best approach is to have the
// "web_accessible_resources secret" added outside by the client of this
// module, but for now I just want to remove an obstacle to modularization.
const warSecret = typeof vAPI === 'object' && vAPI !== null
? vAPI.warSecret
: ( ) => '';
/******************************************************************************/
/******************************************************************************/
@ -230,14 +236,20 @@ const RedirectEntry = class {
fctxt instanceof Object &&
fctxt.type !== 'xmlhttprequest'
) {
let url = `${this.warURL}?secret=${vAPI.warSecret()}`;
const params = [];
const secret = warSecret();
if ( secret !== '' ) { params.push(`secret=${secret}`); }
if ( this.params !== undefined ) {
for ( const name of this.params ) {
const value = fctxt[name];
if ( value === undefined ) { continue; }
url += `&${name}=${encodeURIComponent(value)}`;
params.push(`${name}=${encodeURIComponent(value)}`);
}
}
let url = `${this.warURL}`;
if ( params.length !== 0 ) {
url += `?${params.join('&')}`;
}
return url;
}
if ( this.data === undefined ) { return; }
@ -439,18 +451,18 @@ const removeTopCommentBlock = function(text) {
/******************************************************************************/
RedirectEngine.prototype.loadBuiltinResources = function() {
RedirectEngine.prototype.loadBuiltinResources = function(fetcher) {
this.resources = new Map();
this.aliases = new Map();
const fetches = [
io.fetchText(
fetcher(
'/assets/resources/scriptlets.js'
).then(result => {
const content = result.content;
if ( typeof content === 'string' && content.length !== 0 ) {
this.resourcesFromString(content);
}
if ( typeof content !== 'string' ) { return; }
if ( content.length === 0 ) { return; }
this.resourcesFromString(content);
}),
];
@ -459,7 +471,7 @@ RedirectEngine.prototype.loadBuiltinResources = function() {
const entry = RedirectEntry.fromSelfie({
mime: mimeFromName(name),
data,
warURL: vAPI.getURL(`/web_accessible_resources/${name}`),
warURL: `/web_accessible_resources/${name}`,
params: details.params,
});
this.resources.set(name, entry);
@ -506,10 +518,9 @@ RedirectEngine.prototype.loadBuiltinResources = function() {
continue;
}
fetches.push(
io.fetch(
`/web_accessible_resources/${name}?secret=${vAPI.warSecret()}`,
{ responseType: details.data }
).then(
fetcher(`/web_accessible_resources/${name}`, {
responseType: details.data
}).then(
result => process(result)
)
);
@ -545,21 +556,22 @@ RedirectEngine.prototype.getResourceDetails = function() {
/******************************************************************************/
const resourcesSelfieVersion = 5;
const RESOURCES_SELFIE_VERSION = 6;
const RESOURCES_SELFIE_NAME = 'compiled/redirectEngine/resources';
RedirectEngine.prototype.selfieFromResources = function() {
io.put(
'compiled/redirectEngine/resources',
RedirectEngine.prototype.selfieFromResources = function(storage) {
storage.put(
RESOURCES_SELFIE_NAME,
JSON.stringify({
version: resourcesSelfieVersion,
version: RESOURCES_SELFIE_VERSION,
aliases: Array.from(this.aliases),
resources: Array.from(this.resources),
})
);
};
RedirectEngine.prototype.resourcesFromSelfie = async function() {
const result = await io.get('compiled/redirectEngine/resources');
RedirectEngine.prototype.resourcesFromSelfie = async function(storage) {
const result = await storage.get(RESOURCES_SELFIE_NAME);
let selfie;
try {
selfie = JSON.parse(result.content);
@ -567,7 +579,7 @@ RedirectEngine.prototype.resourcesFromSelfie = async function() {
}
if (
selfie instanceof Object === false ||
selfie.version !== resourcesSelfieVersion ||
selfie.version !== RESOURCES_SELFIE_VERSION ||
Array.isArray(selfie.resources) === false
) {
return false;
@ -580,8 +592,8 @@ RedirectEngine.prototype.resourcesFromSelfie = async function() {
return true;
};
RedirectEngine.prototype.invalidateResourcesSelfie = function() {
io.remove('compiled/redirectEngine/resources');
RedirectEngine.prototype.invalidateResourcesSelfie = function(storage) {
storage.remove(RESOURCES_SELFIE_NAME);
};
/******************************************************************************/

View File

@ -140,7 +140,7 @@ const onVersionReady = function(lastVersion) {
// Since built-in resources may have changed since last version, we
// force a reload of all resources.
redirectEngine.invalidateResourcesSelfie();
redirectEngine.invalidateResourcesSelfie(io);
// https://github.com/LiCybora/NanoDefenderFirefox/issues/196
// Toggle on the blocking of CSP reports by default for Firefox.

View File

@ -35,7 +35,6 @@ import {
hostnameFromNetworkURL,
} from './uri-utils.js';
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/import#browser_compatibility
//
// This import would be best done dynamically, but since dynamic imports are
@ -4287,22 +4286,20 @@ FilterContainer.prototype.redirectRequest = function(redirectEngine, fctxt) {
const highest = directives.length - 1;
// More than a single directive means more work.
if ( highest !== 0 ) {
directives.sort(
FilterContainer.compareRedirectRequests.bind(this, redirectEngine)
);
directives.sort((a, b) => compareRedirectRequests(redirectEngine, a, b));
}
// Redirect to highest-ranked directive
const directive = directives[highest];
if ( (directive.bits & AllowAction) === 0 ) {
const { token } =
FilterContainer.parseRedirectRequestValue(directive.modifier);
parseRedirectRequestValue(directive.modifier);
fctxt.redirectURL = redirectEngine.tokenToURL(fctxt, token);
if ( fctxt.redirectURL === undefined ) { return; }
}
return directives;
};
FilterContainer.parseRedirectRequestValue = function(modifier) {
const parseRedirectRequestValue = function(modifier) {
if ( modifier.cache === undefined ) {
modifier.cache =
StaticFilteringParser.parseRedirectValue(modifier.value);
@ -4310,12 +4307,12 @@ FilterContainer.parseRedirectRequestValue = function(modifier) {
return modifier.cache;
};
FilterContainer.compareRedirectRequests = function(redirectEngine, a, b) {
const compareRedirectRequests = function(redirectEngine, a, b) {
const { token: atok, priority: aint, bits: abits } =
FilterContainer.parseRedirectRequestValue(a.modifier);
parseRedirectRequestValue(a.modifier);
if ( redirectEngine.hasToken(atok) === false ) { return -1; }
const { token: btok, priority: bint, bits: bbits } =
FilterContainer.parseRedirectRequestValue(b.modifier);
parseRedirectRequestValue(b.modifier);
if ( redirectEngine.hasToken(btok) === false ) { return 1; }
if ( abits !== bbits ) {
if ( (abits & Important) !== 0 ) { return 1; }

View File

@ -1151,11 +1151,19 @@ self.addEventListener('hiddenSettingsChanged', ( ) => {
µb.loadRedirectResources = async function() {
try {
const success = await redirectEngine.resourcesFromSelfie();
const success = await redirectEngine.resourcesFromSelfie(io);
if ( success === true ) { return true; }
const fetcher = (path, options = undefined) => {
if ( path.startsWith('/web_accessible_resources/') ) {
path += `?secret=${vAPI.warSecret()}`;
return io.fetch(path, options);
}
return io.fetchText(path);
};
const fetchPromises = [
redirectEngine.loadBuiltinResources()
redirectEngine.loadBuiltinResources(fetcher)
];
const userResourcesLocation = this.hiddenSettings.userResourcesLocation;
@ -1182,7 +1190,7 @@ self.addEventListener('hiddenSettingsChanged', ( ) => {
}
redirectEngine.resourcesFromString(content);
redirectEngine.selfieFromResources();
redirectEngine.selfieFromResources(io);
} catch(ex) {
ubolog(ex);
return false;
@ -1617,7 +1625,7 @@ self.addEventListener('hiddenSettingsChanged', ( ) => {
this.hiddenSettings.userResourcesLocation !== 'unset' ||
vAPI.webextFlavor.soup.has('devbuild')
) {
redirectEngine.invalidateResourcesSelfie();
redirectEngine.invalidateResourcesSelfie(io);
}
this.loadFilterLists();
}

View File

@ -61,6 +61,12 @@ const supportsFloc = document.interestCohort instanceof Function;
/******************************************************************************/
const patchLocalRedirectURL = url => url.charCodeAt(0) === 0x2F /* '/' */
? vAPI.getURL(url)
: url;
/******************************************************************************/
// Intercept and filter web requests.
const onBeforeRequest = function(details) {
@ -102,7 +108,7 @@ const onBeforeRequest = function(details) {
// Redirected
if ( fctxt.redirectURL !== undefined ) {
return { redirectUrl: fctxt.redirectURL };
return { redirectUrl: patchLocalRedirectURL(fctxt.redirectURL) };
}
// Not redirected
@ -208,7 +214,7 @@ const onBeforeRootFrameRequest = function(fctxt) {
// Redirected
if ( fctxt.redirectURL !== undefined ) {
return { redirectUrl: fctxt.redirectURL };
return { redirectUrl: patchLocalRedirectURL(fctxt.redirectURL) };
}
// Not blocked
@ -414,7 +420,7 @@ const onBeforeBehindTheSceneRequest = function(fctxt) {
// Redirected
if ( fctxt.redirectURL !== undefined ) {
return { redirectUrl: fctxt.redirectURL };
return { redirectUrl: patchLocalRedirectURL(fctxt.redirectURL) };
}
// Blocked?

View File

@ -25,6 +25,7 @@
import contextMenu from './contextmenu.js';
import cosmeticFilteringEngine from './cosmetic-filtering.js';
import io from './assets.js';
import µb from './background.js';
import { hostnameFromURI } from './uri-utils.js';
import { redirectEngine } from './redirect-engine.js';
@ -423,7 +424,7 @@ const matchBucket = function(url, hostname, bucket, start) {
this.hiddenSettings = hs;
this.saveHiddenSettings();
if ( mustReloadResources ) {
redirectEngine.invalidateResourcesSelfie();
redirectEngine.invalidateResourcesSelfie(io);
this.loadRedirectResources();
}
this.fireDOMEvent('hiddenSettingsChanged');