Make `userResourcesLocation` able to support multiple URLs

The URLs must be space-separated.

Reminders:
- The additional resources will be updated at the same time
  the built-in resource file is updated
- Purging the cache of 'uBlock filters' will also purge the
  cache of the built-in resource file -- and hence force a
  reload of the user's custom resources if any

Related issues:
- https://github.com/gorhill/uBlock/issues/3307
- https://github.com/uBlockOrigin/uAssets/issues/5184#issuecomment-475875189

Addtionally:
- Opportunitically promisified assets.fetchText()
- Fixed https://github.com/gorhill/uBlock/issues/3586
This commit is contained in:
Raymond Hill 2019-04-20 17:16:49 -04:00
parent d9fe40f1ce
commit a52b07ff6e
No known key found for this signature in database
GPG Key ID: 25E1490B761470C2
5 changed files with 80 additions and 69 deletions

View File

@ -11,3 +11,6 @@ body {
text-align: left;
width: 100%;
}
.CodeMirror-wrap pre {
word-break: break-all;
}

View File

@ -49,7 +49,7 @@ uBlockDashboard.patchCodeMirrorEditor(cmEditor);
/******************************************************************************/
let hashFromAdvancedSettings = function(raw) {
return raw.trim().replace(/\s+/g, '|');
return raw.trim().replace(/[ \t]+/g, ' ').replace(/\s*[\n\r]+\s*/g, '\n');
};
/******************************************************************************/

View File

@ -67,10 +67,10 @@ api.fetchText = function(url, onLoad, onError) {
let actualUrl = isExternal ? url : vAPI.getURL(url);
// https://github.com/gorhill/uBlock/issues/2592
// Force browser cache to be bypassed, but only for resources which have
// been fetched more than one hour ago.
// Force browser cache to be bypassed, but only for resources which have
// been fetched more than one hour ago.
if ( isExternal ) {
const queryValue = '_=' + Math.floor(Date.now() / 7200000);
const queryValue = `_=${Math.floor(Date.now() / 3600000) % 12}`;
if ( actualUrl.indexOf('?') === -1 ) {
actualUrl += '?';
} else {
@ -83,6 +83,9 @@ api.fetchText = function(url, onLoad, onError) {
onError = onLoad;
}
return new Promise((resolve, reject) => {
// Start of executor
const timeoutAfter = µBlock.hiddenSettings.assetFetchTimeout * 1000 || 30000;
const xhr = new XMLHttpRequest();
let contentLoaded = 0;
@ -99,6 +102,20 @@ api.fetchText = function(url, onLoad, onError) {
}
};
const onResolve = function(details) {
if ( onLoad instanceof Function ) {
onLoad(details);
}
resolve(details);
};
const onReject = function(details) {
if ( onError instanceof Function ) {
onError(details);
}
reject(details);
};
// https://github.com/gorhill/uMatrix/issues/15
const onLoadEvent = function() {
cleanup();
@ -110,21 +127,21 @@ api.fetchText = function(url, onLoad, onError) {
statusText: this.statusText || ''
};
if ( details.statusCode < 200 || details.statusCode >= 300 ) {
return onError.call(null, details);
return onReject(details);
}
// consider an empty result to be an error
if ( stringIsNotEmpty(this.responseText) === false ) {
return onError.call(null, details);
return onReject(details);
}
// we never download anything else than plain text: discard if response
// appears to be a HTML document: could happen when server serves
// some kind of error page I suppose
const text = this.responseText.trim();
if ( text.startsWith('<') && text.endsWith('>') ) {
return onError.call(null, details);
return onReject(details);
}
details.content = this.responseText;
onLoad(details);
onResolve(details);
};
const onErrorEvent = function() {
@ -134,7 +151,7 @@ api.fetchText = function(url, onLoad, onError) {
type: 'error',
text: errorCantConnectTo.replace('{{msg}}', actualUrl)
});
onError({ url, content: '' });
onReject({ url, content: '' });
};
const onTimeout = function() {
@ -167,6 +184,9 @@ api.fetchText = function(url, onLoad, onError) {
} catch (e) {
onErrorEvent.call(xhr);
}
// End of executor
});
};
/******************************************************************************/

View File

@ -988,41 +988,37 @@
/******************************************************************************/
µBlock.loadRedirectResources = function(updatedContent) {
let content = '';
µBlock.loadRedirectResources = function() {
return this.redirectEngine.resourcesFromSelfie().then(success => {
if ( success === true ) { return; }
const fetchPromises = [ this.assets.get('ublock-resources') ];
const userResourcesLocation = this.hiddenSettings.userResourcesLocation;
if ( userResourcesLocation !== 'unset' ) {
for ( const url of userResourcesLocation.split(/\s+/) ) {
fetchPromises.push(this.assets.fetchText(url));
}
}
return Promise.all(fetchPromises);
}).then(results => {
if ( Array.isArray(results) === false ) { return; }
let content = '';
for ( const result of results ) {
if (
result instanceof Object === false ||
typeof result.content !== 'string' ||
result.content === ''
) {
continue;
}
content += '\n\n' + result.content;
}
const onDone = ( ) => {
this.redirectEngine.resourcesFromString(content);
};
const onUserResourcesLoaded = details => {
if ( details.content !== '' ) {
content += '\n\n' + details.content;
}
onDone();
};
const onResourcesLoaded = details => {
if ( details.content !== '' ) {
content = details.content;
}
if ( this.hiddenSettings.userResourcesLocation === 'unset' ) {
return onDone();
}
this.assets.fetchText(
this.hiddenSettings.userResourcesLocation,
onUserResourcesLoaded
);
};
if ( typeof updatedContent === 'string' && updatedContent.length !== 0 ) {
return onResourcesLoaded({ content: updatedContent });
}
this.redirectEngine.resourcesFromSelfie().then(success => {
if ( success !== true ) {
this.assets.get('ublock-resources', onResourcesLoaded);
}
});
};
@ -1353,9 +1349,6 @@
}
} else if ( details.assetKey === 'ublock-resources' ) {
this.redirectEngine.invalidateResourcesSelfie();
if ( cached ) {
this.loadRedirectResources(details.content);
}
}
vAPI.messaging.broadcast({
what: 'assetUpdated',

View File

@ -672,31 +672,26 @@
return datasetPromise;
}
datasetPromise = new Promise(resolve => {
console.info(`Loading benchmark dataset...`);
const url = vAPI.getURL('/assets/requests.json');
µBlock.assets.fetchText(url, details => {
if ( details.error !== undefined ) {
datasetPromise = undefined;
console.info(`Not found: ${url}`);
resolve();
return;
console.info(`Loading benchmark dataset...`);
const url = vAPI.getURL('/assets/requests.json');
datasetPromise = µBlock.assets.fetchText(url).then(details => {
console.info(`Parsing benchmark dataset...`);
const requests = [];
const lineIter = new µBlock.LineIterator(details.content);
while ( lineIter.eot() === false ) {
let request;
try {
request = JSON.parse(lineIter.next());
} catch(ex) {
}
console.info(`Parsing benchmark dataset...`);
const requests = [];
const lineIter = new µBlock.LineIterator(details.content);
while ( lineIter.eot() === false ) {
let request;
try {
request = JSON.parse(lineIter.next());
} catch(ex) {
}
if ( request instanceof Object === false ) { continue; }
if ( !request.frameUrl || !request.url ) { continue; }
requests.push(request);
}
resolve(requests);
});
if ( request instanceof Object === false ) { continue; }
if ( !request.frameUrl || !request.url ) { continue; }
requests.push(request);
}
return requests;
}).catch(details => {
console.info(`Not found: ${details.url}`);
datasetPromise = undefined;
});
return datasetPromise;