From a52b07ff6ef6e6b16785e237178e1cd6f38a1833 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sat, 20 Apr 2019 17:16:49 -0400 Subject: [PATCH] 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 --- src/css/advanced-settings.css | 3 ++ src/js/advanced-settings.js | 2 +- src/js/assets.js | 36 ++++++++++++++----- src/js/storage.js | 65 ++++++++++++++++------------------- src/js/utils.js | 43 ++++++++++------------- 5 files changed, 80 insertions(+), 69 deletions(-) diff --git a/src/css/advanced-settings.css b/src/css/advanced-settings.css index 73036f27b..7c4a8723a 100644 --- a/src/css/advanced-settings.css +++ b/src/css/advanced-settings.css @@ -11,3 +11,6 @@ body { text-align: left; width: 100%; } +.CodeMirror-wrap pre { + word-break: break-all; + } diff --git a/src/js/advanced-settings.js b/src/js/advanced-settings.js index b65ad7a11..1f34f8d3f 100644 --- a/src/js/advanced-settings.js +++ b/src/js/advanced-settings.js @@ -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'); }; /******************************************************************************/ diff --git a/src/js/assets.js b/src/js/assets.js index 9317bba09..e14725dec 100644 --- a/src/js/assets.js +++ b/src/js/assets.js @@ -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 + }); }; /******************************************************************************/ diff --git a/src/js/storage.js b/src/js/storage.js index 266293675..a2ce93e8a 100644 --- a/src/js/storage.js +++ b/src/js/storage.js @@ -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', diff --git a/src/js/utils.js b/src/js/utils.js index ec13c0b76..129b03994 100644 --- a/src/js/utils.js +++ b/src/js/utils.js @@ -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;