mirror of
https://github.com/gorhill/uBlock.git
synced 2024-09-18 17:02:27 +02:00
Restore ability to redirect xhr to image resources
The ability to redirect xmlhttprequest to binary
resources was lost when redirectable/injectable
resources became immutable in commit
152cea2dfe
.
This commit restores the ability to redirect a
xmlhttprequest to a binary resource by making
it possible to derive a data: URI from the
content of binary resources such as images.
Addtionally a redirect to a data: URI can be
forced by prefixing the resource token with `%`.
This is a non-official feature at this point,
i.e. it could be removed at any time.
This commit is contained in:
parent
a1b99954e1
commit
7ac7b027f4
175
src/js/assets.js
175
src/js/assets.js
@ -62,6 +62,90 @@ const fireNotification = function(topic, details) {
|
|||||||
|
|
||||||
/******************************************************************************/
|
/******************************************************************************/
|
||||||
|
|
||||||
|
api.fetch = function(url, options = {}) {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
// Start of executor
|
||||||
|
|
||||||
|
const timeoutAfter = µBlock.hiddenSettings.assetFetchTimeout * 1000 || 30000;
|
||||||
|
const xhr = new XMLHttpRequest();
|
||||||
|
let contentLoaded = 0;
|
||||||
|
let timeoutTimer;
|
||||||
|
|
||||||
|
const cleanup = function() {
|
||||||
|
xhr.removeEventListener('load', onLoadEvent);
|
||||||
|
xhr.removeEventListener('error', onErrorEvent);
|
||||||
|
xhr.removeEventListener('abort', onErrorEvent);
|
||||||
|
xhr.removeEventListener('progress', onProgressEvent);
|
||||||
|
if ( timeoutTimer !== undefined ) {
|
||||||
|
clearTimeout(timeoutTimer);
|
||||||
|
timeoutTimer = undefined;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// https://github.com/gorhill/uMatrix/issues/15
|
||||||
|
const onLoadEvent = function() {
|
||||||
|
cleanup();
|
||||||
|
// xhr for local files gives status 0, but actually succeeds
|
||||||
|
const details = {
|
||||||
|
url,
|
||||||
|
content: '',
|
||||||
|
statusCode: this.status || 200,
|
||||||
|
statusText: this.statusText || ''
|
||||||
|
};
|
||||||
|
if ( details.statusCode < 200 || details.statusCode >= 300 ) {
|
||||||
|
return reject(details);
|
||||||
|
}
|
||||||
|
details.content = this.response;
|
||||||
|
resolve(details);
|
||||||
|
};
|
||||||
|
|
||||||
|
const onErrorEvent = function() {
|
||||||
|
cleanup();
|
||||||
|
µBlock.logger.writeOne({
|
||||||
|
realm: 'message',
|
||||||
|
type: 'error',
|
||||||
|
text: errorCantConnectTo.replace('{{msg}}', url)
|
||||||
|
});
|
||||||
|
reject({ url, content: '' });
|
||||||
|
};
|
||||||
|
|
||||||
|
const onTimeout = function() {
|
||||||
|
xhr.abort();
|
||||||
|
};
|
||||||
|
|
||||||
|
// https://github.com/gorhill/uBlock/issues/2526
|
||||||
|
// - Timeout only when there is no progress.
|
||||||
|
const onProgressEvent = function(ev) {
|
||||||
|
if ( ev.loaded === contentLoaded ) { return; }
|
||||||
|
contentLoaded = ev.loaded;
|
||||||
|
if ( timeoutTimer !== undefined ) {
|
||||||
|
clearTimeout(timeoutTimer);
|
||||||
|
}
|
||||||
|
timeoutTimer = vAPI.setTimeout(onTimeout, timeoutAfter);
|
||||||
|
};
|
||||||
|
|
||||||
|
// Be ready for thrown exceptions:
|
||||||
|
// I am pretty sure it used to work, but now using a URL such as
|
||||||
|
// `file:///` on Chromium 40 results in an exception being thrown.
|
||||||
|
try {
|
||||||
|
xhr.open('get', url, true);
|
||||||
|
xhr.addEventListener('load', onLoadEvent);
|
||||||
|
xhr.addEventListener('error', onErrorEvent);
|
||||||
|
xhr.addEventListener('abort', onErrorEvent);
|
||||||
|
xhr.addEventListener('progress', onProgressEvent);
|
||||||
|
xhr.responseType = options.responseType || 'text';
|
||||||
|
xhr.send();
|
||||||
|
timeoutTimer = vAPI.setTimeout(onTimeout, timeoutAfter);
|
||||||
|
} catch (e) {
|
||||||
|
onErrorEvent.call(xhr);
|
||||||
|
}
|
||||||
|
|
||||||
|
// End of executor
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
/******************************************************************************/
|
||||||
|
|
||||||
api.fetchText = function(url, onLoad, onError) {
|
api.fetchText = function(url, onLoad, onError) {
|
||||||
const isExternal = reIsExternalPath.test(url);
|
const isExternal = reIsExternalPath.test(url);
|
||||||
let actualUrl = isExternal ? url : vAPI.getURL(url);
|
let actualUrl = isExternal ? url : vAPI.getURL(url);
|
||||||
@ -91,109 +175,36 @@ api.fetchText = function(url, onLoad, onError) {
|
|||||||
onError = onLoad;
|
onError = onLoad;
|
||||||
}
|
}
|
||||||
|
|
||||||
return new Promise(resolve => {
|
|
||||||
// Start of executor
|
|
||||||
|
|
||||||
const timeoutAfter = µBlock.hiddenSettings.assetFetchTimeout * 1000 || 30000;
|
|
||||||
const xhr = new XMLHttpRequest();
|
|
||||||
let contentLoaded = 0;
|
|
||||||
let timeoutTimer;
|
|
||||||
|
|
||||||
const cleanup = function() {
|
|
||||||
xhr.removeEventListener('load', onLoadEvent);
|
|
||||||
xhr.removeEventListener('error', onErrorEvent);
|
|
||||||
xhr.removeEventListener('abort', onErrorEvent);
|
|
||||||
xhr.removeEventListener('progress', onProgressEvent);
|
|
||||||
if ( timeoutTimer !== undefined ) {
|
|
||||||
clearTimeout(timeoutTimer);
|
|
||||||
timeoutTimer = undefined;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const onResolve = function(details) {
|
const onResolve = function(details) {
|
||||||
if ( onLoad instanceof Function ) {
|
if ( onLoad instanceof Function ) {
|
||||||
return onLoad(details);
|
return onLoad(details);
|
||||||
}
|
}
|
||||||
resolve(details);
|
return details;
|
||||||
};
|
};
|
||||||
|
|
||||||
const onReject = function(details) {
|
const onReject = function(details) {
|
||||||
|
details.content = '';
|
||||||
if ( onError instanceof Function ) {
|
if ( onError instanceof Function ) {
|
||||||
return onError(details);
|
return onError(details);
|
||||||
}
|
}
|
||||||
resolve(details);
|
return details;
|
||||||
};
|
};
|
||||||
|
|
||||||
// https://github.com/gorhill/uMatrix/issues/15
|
return api.fetch(url).then(details => {
|
||||||
const onLoadEvent = function() {
|
|
||||||
cleanup();
|
|
||||||
// xhr for local files gives status 0, but actually succeeds
|
|
||||||
const details = {
|
|
||||||
url,
|
|
||||||
content: '',
|
|
||||||
statusCode: this.status || 200,
|
|
||||||
statusText: this.statusText || ''
|
|
||||||
};
|
|
||||||
if ( details.statusCode < 200 || details.statusCode >= 300 ) {
|
|
||||||
return onReject(details);
|
|
||||||
}
|
|
||||||
// consider an empty result to be an error
|
// consider an empty result to be an error
|
||||||
if ( stringIsNotEmpty(this.responseText) === false ) {
|
if ( stringIsNotEmpty(details.content) === false ) {
|
||||||
return onReject(details);
|
return onReject(details);
|
||||||
}
|
}
|
||||||
// we never download anything else than plain text: discard if response
|
// we never download anything else than plain text: discard if response
|
||||||
// appears to be a HTML document: could happen when server serves
|
// appears to be a HTML document: could happen when server serves
|
||||||
// some kind of error page I suppose
|
// some kind of error page I suppose
|
||||||
const text = this.responseText.trim();
|
const text = details.content.trim();
|
||||||
if ( text.startsWith('<') && text.endsWith('>') ) {
|
if ( text.startsWith('<') && text.endsWith('>') ) {
|
||||||
return onReject(details);
|
return onReject(details);
|
||||||
}
|
}
|
||||||
details.content = this.responseText;
|
return onResolve(details);
|
||||||
onResolve(details);
|
}).catch(details => {
|
||||||
};
|
return onReject(details);
|
||||||
|
|
||||||
const onErrorEvent = function() {
|
|
||||||
cleanup();
|
|
||||||
µBlock.logger.writeOne({
|
|
||||||
realm: 'message',
|
|
||||||
type: 'error',
|
|
||||||
text: errorCantConnectTo.replace('{{msg}}', actualUrl)
|
|
||||||
});
|
|
||||||
onReject({ url, content: '' });
|
|
||||||
};
|
|
||||||
|
|
||||||
const onTimeout = function() {
|
|
||||||
xhr.abort();
|
|
||||||
};
|
|
||||||
|
|
||||||
// https://github.com/gorhill/uBlock/issues/2526
|
|
||||||
// - Timeout only when there is no progress.
|
|
||||||
const onProgressEvent = function(ev) {
|
|
||||||
if ( ev.loaded === contentLoaded ) { return; }
|
|
||||||
contentLoaded = ev.loaded;
|
|
||||||
if ( timeoutTimer !== undefined ) {
|
|
||||||
clearTimeout(timeoutTimer);
|
|
||||||
}
|
|
||||||
timeoutTimer = vAPI.setTimeout(onTimeout, timeoutAfter);
|
|
||||||
};
|
|
||||||
|
|
||||||
// Be ready for thrown exceptions:
|
|
||||||
// I am pretty sure it used to work, but now using a URL such as
|
|
||||||
// `file:///` on Chromium 40 results in an exception being thrown.
|
|
||||||
try {
|
|
||||||
xhr.open('get', actualUrl, true);
|
|
||||||
xhr.addEventListener('load', onLoadEvent);
|
|
||||||
xhr.addEventListener('error', onErrorEvent);
|
|
||||||
xhr.addEventListener('abort', onErrorEvent);
|
|
||||||
xhr.addEventListener('progress', onProgressEvent);
|
|
||||||
xhr.responseType = 'text';
|
|
||||||
xhr.send();
|
|
||||||
timeoutTimer = vAPI.setTimeout(onTimeout, timeoutAfter);
|
|
||||||
} catch (e) {
|
|
||||||
onErrorEvent.call(xhr);
|
|
||||||
}
|
|
||||||
|
|
||||||
// End of executor
|
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -28,133 +28,130 @@
|
|||||||
/******************************************************************************/
|
/******************************************************************************/
|
||||||
/******************************************************************************/
|
/******************************************************************************/
|
||||||
|
|
||||||
|
// The resources referenced below are found in ./web_accessible_resources/
|
||||||
|
//
|
||||||
|
// The content of the resources which declare a `data` property will be loaded
|
||||||
|
// in memory, and converted to a suitable internal format depending on the
|
||||||
|
// type of the loaded data. The `data` property allows for manual injection
|
||||||
|
// through `+js(...)`, or for redirection to a data: URI when a redirection
|
||||||
|
// to a web accessible resource is not desirable.
|
||||||
|
|
||||||
const redirectableResources = new Map([
|
const redirectableResources = new Map([
|
||||||
[ '1x1.gif', {
|
[ '1x1.gif', {
|
||||||
alias: '1x1-transparent.gif',
|
alias: '1x1-transparent.gif',
|
||||||
inject: false
|
data: 'blob',
|
||||||
} ],
|
} ],
|
||||||
[ '2x2.png', {
|
[ '2x2.png', {
|
||||||
alias: '2x2-transparent.png',
|
alias: '2x2-transparent.png',
|
||||||
inject: false
|
data: 'blob',
|
||||||
} ],
|
} ],
|
||||||
[ '3x2.png', {
|
[ '3x2.png', {
|
||||||
alias: '3x2-transparent.png',
|
alias: '3x2-transparent.png',
|
||||||
inject: false
|
data: 'blob',
|
||||||
} ],
|
} ],
|
||||||
[ '32x32.png', {
|
[ '32x32.png', {
|
||||||
alias: '32x32-transparent.png',
|
alias: '32x32-transparent.png',
|
||||||
inject: false
|
data: 'blob',
|
||||||
} ],
|
} ],
|
||||||
[ 'addthis_widget.js', {
|
[ 'addthis_widget.js', {
|
||||||
alias: 'addthis.com/addthis_widget.js',
|
alias: 'addthis.com/addthis_widget.js',
|
||||||
inject: false
|
|
||||||
} ],
|
} ],
|
||||||
[ 'ampproject_v0.js', {
|
[ 'ampproject_v0.js', {
|
||||||
alias: 'ampproject.org/v0.js',
|
alias: 'ampproject.org/v0.js',
|
||||||
inject: false
|
|
||||||
} ],
|
} ],
|
||||||
[ 'chartbeat.js', {
|
[ 'chartbeat.js', {
|
||||||
alias: 'static.chartbeat.com/chartbeat.js',
|
alias: 'static.chartbeat.com/chartbeat.js',
|
||||||
inject: false
|
|
||||||
} ],
|
} ],
|
||||||
[ 'amazon_ads.js', {
|
[ 'amazon_ads.js', {
|
||||||
alias: 'amazon-adsystem.com/aax2/amzn_ads.js',
|
alias: 'amazon-adsystem.com/aax2/amzn_ads.js',
|
||||||
inject: false
|
|
||||||
} ],
|
} ],
|
||||||
[ 'disqus_embed.js', {
|
[ 'disqus_embed.js', {
|
||||||
alias: 'disqus.com/embed.js',
|
alias: 'disqus.com/embed.js',
|
||||||
inject: false
|
|
||||||
} ],
|
} ],
|
||||||
[ 'disqus_forums_embed.js', {
|
[ 'disqus_forums_embed.js', {
|
||||||
alias: 'disqus.com/forums/*/embed.js',
|
alias: 'disqus.com/forums/*/embed.js',
|
||||||
inject: false
|
|
||||||
} ],
|
} ],
|
||||||
[ 'doubleclick_instream_ad_status.js', {
|
[ 'doubleclick_instream_ad_status.js', {
|
||||||
alias: 'doubleclick.net/instream/ad_status.js',
|
alias: 'doubleclick.net/instream/ad_status.js',
|
||||||
inject: false
|
|
||||||
} ],
|
} ],
|
||||||
[ 'google-analytics_analytics.js', {
|
[ 'google-analytics_analytics.js', {
|
||||||
alias: 'google-analytics.com/analytics.js',
|
alias: 'google-analytics.com/analytics.js',
|
||||||
inject: false
|
|
||||||
} ],
|
} ],
|
||||||
[ 'google-analytics_cx_api.js', {
|
[ 'google-analytics_cx_api.js', {
|
||||||
alias: 'google-analytics.com/cx/api.js',
|
alias: 'google-analytics.com/cx/api.js',
|
||||||
inject: false
|
|
||||||
} ],
|
} ],
|
||||||
[ 'google-analytics_ga.js', {
|
[ 'google-analytics_ga.js', {
|
||||||
alias: 'google-analytics.com/ga.js',
|
alias: 'google-analytics.com/ga.js',
|
||||||
inject: false
|
|
||||||
} ],
|
} ],
|
||||||
[ 'google-analytics_inpage_linkid.js', {
|
[ 'google-analytics_inpage_linkid.js', {
|
||||||
alias: 'google-analytics.com/inpage_linkid.js',
|
alias: 'google-analytics.com/inpage_linkid.js',
|
||||||
inject: false
|
|
||||||
} ],
|
} ],
|
||||||
[ 'googlesyndication_adsbygoogle.js', {
|
[ 'googlesyndication_adsbygoogle.js', {
|
||||||
alias: 'googlesyndication.com/adsbygoogle.js',
|
alias: 'googlesyndication.com/adsbygoogle.js',
|
||||||
inject: false
|
|
||||||
} ],
|
} ],
|
||||||
[ 'googletagmanager_gtm.js', {
|
[ 'googletagmanager_gtm.js', {
|
||||||
alias: 'googletagmanager.com/gtm.js',
|
alias: 'googletagmanager.com/gtm.js',
|
||||||
inject: false
|
|
||||||
} ],
|
} ],
|
||||||
[ 'googletagservices_gpt.js', {
|
[ 'googletagservices_gpt.js', {
|
||||||
alias: 'googletagservices.com/gpt.js',
|
alias: 'googletagservices.com/gpt.js',
|
||||||
inject: false
|
|
||||||
} ],
|
} ],
|
||||||
[ 'hd-main.js', {
|
[ 'hd-main.js', {
|
||||||
inject: false
|
|
||||||
} ],
|
} ],
|
||||||
[ 'ligatus_angular-tag.js', {
|
[ 'ligatus_angular-tag.js', {
|
||||||
alias: 'ligatus.com/*/angular-tag.js',
|
alias: 'ligatus.com/*/angular-tag.js',
|
||||||
inject: false
|
|
||||||
} ],
|
} ],
|
||||||
[ 'monkeybroker.js', {
|
[ 'monkeybroker.js', {
|
||||||
alias: 'd3pkae9owd2lcf.cloudfront.net/mb105.js',
|
alias: 'd3pkae9owd2lcf.cloudfront.net/mb105.js',
|
||||||
inject: false
|
|
||||||
} ],
|
} ],
|
||||||
[ 'noeval.js', {
|
[ 'noeval.js', {
|
||||||
|
data: 'text',
|
||||||
} ],
|
} ],
|
||||||
[ 'noeval-silent.js', {
|
[ 'noeval-silent.js', {
|
||||||
alias: 'silent-noeval.js',
|
alias: 'silent-noeval.js',
|
||||||
|
data: 'text',
|
||||||
} ],
|
} ],
|
||||||
[ 'nobab.js', {
|
[ 'nobab.js', {
|
||||||
alias: 'bab-defuser.js',
|
alias: 'bab-defuser.js',
|
||||||
|
data: 'text',
|
||||||
} ],
|
} ],
|
||||||
[ 'nofab.js', {
|
[ 'nofab.js', {
|
||||||
alias: 'fuckadblock.js-3.2.0',
|
alias: 'fuckadblock.js-3.2.0',
|
||||||
|
data: 'text',
|
||||||
} ],
|
} ],
|
||||||
[ 'noop-0.1s.mp3', {
|
[ 'noop-0.1s.mp3', {
|
||||||
alias: 'noopmp3-0.1s',
|
alias: 'noopmp3-0.1s',
|
||||||
inject: false
|
data: 'blob',
|
||||||
} ],
|
} ],
|
||||||
[ 'noop-1s.mp4', {
|
[ 'noop-1s.mp4', {
|
||||||
alias: 'noopmp4-1s',
|
alias: 'noopmp4-1s',
|
||||||
inject: false
|
data: 'blob',
|
||||||
} ],
|
} ],
|
||||||
[ 'noop.html', {
|
[ 'noop.html', {
|
||||||
alias: 'noopframe',
|
alias: 'noopframe',
|
||||||
inject: false
|
|
||||||
} ],
|
} ],
|
||||||
[ 'noop.js', {
|
[ 'noop.js', {
|
||||||
alias: 'noopjs',
|
alias: 'noopjs',
|
||||||
|
data: 'text',
|
||||||
} ],
|
} ],
|
||||||
[ 'noop.txt', {
|
[ 'noop.txt', {
|
||||||
alias: 'nooptext',
|
alias: 'nooptext',
|
||||||
|
data: 'text',
|
||||||
} ],
|
} ],
|
||||||
[ 'outbrain-widget.js', {
|
[ 'outbrain-widget.js', {
|
||||||
alias: 'widgets.outbrain.com/outbrain.js',
|
alias: 'widgets.outbrain.com/outbrain.js',
|
||||||
inject: false
|
|
||||||
} ],
|
} ],
|
||||||
[ 'popads.js', {
|
[ 'popads.js', {
|
||||||
alias: 'popads.net.js',
|
alias: 'popads.net.js',
|
||||||
|
data: 'text',
|
||||||
} ],
|
} ],
|
||||||
[ 'popads-dummy.js', {
|
[ 'popads-dummy.js', {
|
||||||
|
data: 'text',
|
||||||
} ],
|
} ],
|
||||||
[ 'scorecardresearch_beacon.js', {
|
[ 'scorecardresearch_beacon.js', {
|
||||||
alias: 'scorecardresearch.com/beacon.js',
|
alias: 'scorecardresearch.com/beacon.js',
|
||||||
inject: false
|
|
||||||
} ],
|
} ],
|
||||||
[ 'window.open-defuser.js', {
|
[ 'window.open-defuser.js', {
|
||||||
|
data: 'text',
|
||||||
} ],
|
} ],
|
||||||
]);
|
]);
|
||||||
|
|
||||||
@ -194,9 +191,10 @@ const RedirectEntry = function() {
|
|||||||
// - https://stackoverflow.com/a/8056313
|
// - https://stackoverflow.com/a/8056313
|
||||||
// - https://bugzilla.mozilla.org/show_bug.cgi?id=998076
|
// - https://bugzilla.mozilla.org/show_bug.cgi?id=998076
|
||||||
|
|
||||||
RedirectEntry.prototype.toURL = function(fctxt) {
|
RedirectEntry.prototype.toURL = function(fctxt, asDataURI = false) {
|
||||||
if (
|
if (
|
||||||
this.warURL !== undefined &&
|
this.warURL !== undefined &&
|
||||||
|
asDataURI !== true &&
|
||||||
fctxt instanceof Object &&
|
fctxt instanceof Object &&
|
||||||
fctxt.type !== 'xmlhttprequest'
|
fctxt.type !== 'xmlhttprequest'
|
||||||
) {
|
) {
|
||||||
@ -204,11 +202,7 @@ RedirectEntry.prototype.toURL = function(fctxt) {
|
|||||||
}
|
}
|
||||||
if ( this.data === undefined ) { return; }
|
if ( this.data === undefined ) { return; }
|
||||||
if ( this.data.startsWith('data:') === false ) {
|
if ( this.data.startsWith('data:') === false ) {
|
||||||
if ( this.mime.indexOf(';') === -1 ) {
|
this.data = `data:${this.mime};base64,${btoa(this.data)}`;
|
||||||
this.data = 'data:' + this.mime + ';base64,' + btoa(this.data);
|
|
||||||
} else {
|
|
||||||
this.data = 'data:' + this.mime + ',' + this.data;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return this.data;
|
return this.data;
|
||||||
};
|
};
|
||||||
@ -304,9 +298,10 @@ RedirectEngine.prototype.lookup = function(fctxt) {
|
|||||||
if ( this.ruleSources.has(src) ) {
|
if ( this.ruleSources.has(src) ) {
|
||||||
for ( let i = 0; i < n; i++ ) {
|
for ( let i = 0; i < n; i++ ) {
|
||||||
const entries = this.rules.get(`${src} ${desAll[i]} ${type}`);
|
const entries = this.rules.get(`${src} ${desAll[i]} ${type}`);
|
||||||
if ( entries && this.lookupToken(entries, reqURL) ) {
|
if ( entries === undefined ) { continue; }
|
||||||
return this.resourceNameRegister;
|
const rule = this.lookupRule(entries, reqURL);
|
||||||
}
|
if ( rule === undefined ) { continue; }
|
||||||
|
return rule;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
src = this.toBroaderHostname(src);
|
src = this.toBroaderHostname(src);
|
||||||
@ -314,16 +309,13 @@ RedirectEngine.prototype.lookup = function(fctxt) {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
RedirectEngine.prototype.lookupToken = function(entries, reqURL) {
|
RedirectEngine.prototype.lookupRule = function(entries, reqURL) {
|
||||||
let j = entries.length;
|
for ( const entry of entries ) {
|
||||||
while ( j-- ) {
|
|
||||||
let entry = entries[j];
|
|
||||||
if ( entry.pat instanceof RegExp === false ) {
|
if ( entry.pat instanceof RegExp === false ) {
|
||||||
entry.pat = new RegExp(entry.pat, 'i');
|
entry.pat = new RegExp(entry.pat, 'i');
|
||||||
}
|
}
|
||||||
if ( entry.pat.test(reqURL) ) {
|
if ( entry.pat.test(reqURL) ) {
|
||||||
this.resourceNameRegister = entry.tok;
|
return entry;
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -331,24 +323,21 @@ RedirectEngine.prototype.lookupToken = function(entries, reqURL) {
|
|||||||
/******************************************************************************/
|
/******************************************************************************/
|
||||||
|
|
||||||
RedirectEngine.prototype.toURL = function(fctxt) {
|
RedirectEngine.prototype.toURL = function(fctxt) {
|
||||||
let token = this.lookup(fctxt);
|
const rule = this.lookup(fctxt);
|
||||||
if ( token === undefined ) { return; }
|
if ( rule === undefined ) { return; }
|
||||||
|
let token = this.resourceNameRegister = rule.tok;
|
||||||
|
const asDataURI = token.charCodeAt(0) === 0x25 /* '%' */;
|
||||||
|
if ( asDataURI ) {
|
||||||
|
token = token.slice(1);
|
||||||
|
}
|
||||||
const entry = this.resources.get(this.aliases.get(token) || token);
|
const entry = this.resources.get(this.aliases.get(token) || token);
|
||||||
if ( entry !== undefined ) {
|
if ( entry !== undefined ) {
|
||||||
return entry.toURL(fctxt);
|
return entry.toURL(fctxt, asDataURI);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
/******************************************************************************/
|
/******************************************************************************/
|
||||||
|
|
||||||
RedirectEngine.prototype.matches = function(context) {
|
|
||||||
const token = this.lookup(context);
|
|
||||||
return token !== undefined &&
|
|
||||||
this.resources.has(this.aliases.get(token) || token);
|
|
||||||
};
|
|
||||||
|
|
||||||
/******************************************************************************/
|
|
||||||
|
|
||||||
RedirectEngine.prototype.addRule = function(src, des, type, pattern, redirect) {
|
RedirectEngine.prototype.addRule = function(src, des, type, pattern, redirect) {
|
||||||
this.ruleSources.add(src);
|
this.ruleSources.add(src);
|
||||||
this.ruleDestinations.add(des);
|
this.ruleDestinations.add(des);
|
||||||
@ -551,18 +540,6 @@ RedirectEngine.prototype.fromSelfie = function(path) {
|
|||||||
|
|
||||||
/******************************************************************************/
|
/******************************************************************************/
|
||||||
|
|
||||||
RedirectEngine.prototype.resourceURIFromName = function(name, mime) {
|
|
||||||
const entry = this.resources.get(this.aliases.get(name) || name);
|
|
||||||
if (
|
|
||||||
(entry !== undefined) &&
|
|
||||||
(mime === undefined || entry.mime.startsWith(mime))
|
|
||||||
) {
|
|
||||||
return entry.toURL();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
/******************************************************************************/
|
|
||||||
|
|
||||||
RedirectEngine.prototype.resourceContentFromName = function(name, mime) {
|
RedirectEngine.prototype.resourceContentFromName = function(name, mime) {
|
||||||
const entry = this.resources.get(this.aliases.get(name) || name);
|
const entry = this.resources.get(this.aliases.get(name) || name);
|
||||||
if ( entry === undefined ) { return; }
|
if ( entry === undefined ) { return; }
|
||||||
@ -674,57 +651,74 @@ const removeTopCommentBlock = function(text) {
|
|||||||
RedirectEngine.prototype.loadBuiltinResources = function() {
|
RedirectEngine.prototype.loadBuiltinResources = function() {
|
||||||
this.resources = new Map();
|
this.resources = new Map();
|
||||||
this.aliases = new Map();
|
this.aliases = new Map();
|
||||||
const fetches = [
|
|
||||||
µBlock.assets.fetchText('/assets/resources/scriptlets.js'),
|
|
||||||
];
|
|
||||||
|
|
||||||
// TODO: remove once usage of uBO 1.20.4 is widespread.
|
// TODO: remove once usage of uBO 1.20.4 is widespread.
|
||||||
µBlock.assets.remove('ublock-resources');
|
µBlock.assets.remove('ublock-resources');
|
||||||
|
|
||||||
for ( const [ name, details ] of redirectableResources ) {
|
const fetches = [
|
||||||
if ( details.inject !== false ) {
|
µBlock.assets.fetchText(
|
||||||
fetches.push(
|
'/assets/resources/scriptlets.js'
|
||||||
µBlock.assets.fetchText(
|
).then(result => {
|
||||||
`/web_accessible_resources/${name}${vAPI.warSecret()}`
|
const content = result.content;
|
||||||
)
|
if ( typeof content === 'string' && content.length !== 0 ) {
|
||||||
);
|
this.resourcesFromString(content);
|
||||||
continue;
|
}
|
||||||
}
|
}),
|
||||||
|
];
|
||||||
|
|
||||||
|
const store = (name, data = undefined) => {
|
||||||
|
const details = redirectableResources.get(name);
|
||||||
const entry = RedirectEntry.fromSelfie({
|
const entry = RedirectEntry.fromSelfie({
|
||||||
mime: mimeFromName(name),
|
mime: mimeFromName(name),
|
||||||
|
data,
|
||||||
warURL: vAPI.getURL(`/web_accessible_resources/${name}`),
|
warURL: vAPI.getURL(`/web_accessible_resources/${name}`),
|
||||||
});
|
});
|
||||||
this.resources.set(name, entry);
|
this.resources.set(name, entry);
|
||||||
if ( details.alias !== undefined ) {
|
if ( details.alias !== undefined ) {
|
||||||
this.aliases.set(details.alias, name);
|
this.aliases.set(details.alias, name);
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const processBlob = (name, blob) => {
|
||||||
|
return new Promise(resolve => {
|
||||||
|
const reader = new FileReader();
|
||||||
|
reader.onload = ( ) => {
|
||||||
|
store(name, reader.result);
|
||||||
|
resolve();
|
||||||
|
};
|
||||||
|
reader.readAsDataURL(blob);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const processText = (name, text) => {
|
||||||
|
store(name, removeTopCommentBlock(text));
|
||||||
|
};
|
||||||
|
|
||||||
|
const process = result => {
|
||||||
|
const match = /^\/web_accessible_resources\/([^?]+)/.exec(result.url);
|
||||||
|
if ( match === null ) { return; }
|
||||||
|
const name = match[1];
|
||||||
|
return result.content instanceof Blob
|
||||||
|
? processBlob(name, result.content)
|
||||||
|
: processText(name, result.content);
|
||||||
|
};
|
||||||
|
|
||||||
|
for ( const [ name, details ] of redirectableResources ) {
|
||||||
|
if ( typeof details.data !== 'string' ) {
|
||||||
|
store(name);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
fetches.push(
|
||||||
|
µBlock.assets.fetch(
|
||||||
|
`/web_accessible_resources/${name}${vAPI.warSecret()}`,
|
||||||
|
{ responseType: details.data }
|
||||||
|
).then(
|
||||||
|
result => process(result)
|
||||||
|
)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return Promise.all(fetches).then(results => {
|
return Promise.all(fetches);
|
||||||
// Built-in redirectable resources
|
|
||||||
for ( let i = 1; i < results.length; i++ ) {
|
|
||||||
const result = results[i];
|
|
||||||
const match = /^\/web_accessible_resources\/([^?]+)/.exec(result.url);
|
|
||||||
if ( match === null ) { continue; }
|
|
||||||
const name = match[1];
|
|
||||||
const content = removeTopCommentBlock(result.content);
|
|
||||||
const details = redirectableResources.get(name);
|
|
||||||
const entry = RedirectEntry.fromSelfie({
|
|
||||||
mime: mimeFromName(name),
|
|
||||||
data: content,
|
|
||||||
warURL: vAPI.getURL(`/web_accessible_resources/${name}`),
|
|
||||||
});
|
|
||||||
this.resources.set(name, entry);
|
|
||||||
if ( details.alias !== undefined ) {
|
|
||||||
this.aliases.set(details.alias, name);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Additional resources
|
|
||||||
const content = results[0].content;
|
|
||||||
if ( typeof content === 'string' && content.length !== 0 ) {
|
|
||||||
this.resourcesFromString(content);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/******************************************************************************/
|
/******************************************************************************/
|
||||||
|
Loading…
Reference in New Issue
Block a user