From aab38120893a2af6eb3e44dda05939d1e6f6a793 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Fri, 3 Jul 2020 08:43:40 -0400 Subject: [PATCH] Ignore `!#include` directives within inactive `!#if`/`!#endif` blocks Related issue: - https://github.com/uBlockOrigin/uBlock-issues/issues/1113 --- src/js/assets.js | 51 +++++++++++-------- src/js/storage.js | 126 +++++++++++++++++++++++++--------------------- 2 files changed, 99 insertions(+), 78 deletions(-) diff --git a/src/js/assets.js b/src/js/assets.js index efbb93535..175535e1a 100644 --- a/src/js/assets.js +++ b/src/js/assets.js @@ -244,6 +244,7 @@ api.fetchFilterList = async function(mainlistURL) { const sublistURLs = new Set(); + // https://github.com/uBlockOrigin/uBlock-issues/issues/1113 const processIncludeDirectives = function(results) { const out = []; const reInclude = /^!#include +(\S+)/gm; @@ -254,28 +255,36 @@ api.fetchFilterList = async function(mainlistURL) { } if ( result instanceof Object === false ) { continue; } const content = result.content; - let lastIndex = 0; - for (;;) { - if ( rootDirectoryURL === undefined ) { break; } - const match = reInclude.exec(content); - if ( match === null ) { break; } - if ( toParsedURL(match[1]) !== undefined ) { continue; } - if ( match[1].indexOf('..') !== -1 ) { continue; } - // Compute nested list path relative to parent list path - const pos = result.url.lastIndexOf('/'); - if ( pos === -1 ) { continue; } - const subURL = result.url.slice(0, pos + 1) + match[1]; - if ( sublistURLs.has(subURL) ) { continue; } - sublistURLs.add(subURL); - out.push( - content.slice(lastIndex, match.index), - `! >>>>>>>> ${subURL}`, - api.fetchText(subURL), - `! <<<<<<<< ${subURL}` - ); - lastIndex = reInclude.lastIndex; + const slices = µBlock.processDirectives.split(content); + for ( let i = 0, n = slices.length - 1; i < n; i++ ) { + const slice = content.slice(slices[i+0], slices[i+1]); + if ( (i & 1) !== 0 ) { + out.push(slice); + continue; + } + let lastIndex = 0; + for (;;) { + if ( rootDirectoryURL === undefined ) { break; } + const match = reInclude.exec(slice); + if ( match === null ) { break; } + if ( toParsedURL(match[1]) !== undefined ) { continue; } + if ( match[1].indexOf('..') !== -1 ) { continue; } + // Compute nested list path relative to parent list path + const pos = result.url.lastIndexOf('/'); + if ( pos === -1 ) { continue; } + const subURL = result.url.slice(0, pos + 1) + match[1]; + if ( sublistURLs.has(subURL) ) { continue; } + sublistURLs.add(subURL); + out.push( + slice.slice(lastIndex, match.index), + `! >>>>>>>> ${subURL}`, + api.fetchText(subURL), + `! <<<<<<<< ${subURL}` + ); + lastIndex = reInclude.lastIndex; + } + out.push(lastIndex === 0 ? slice : slice.slice(lastIndex)); } - out.push(lastIndex === 0 ? content : content.slice(lastIndex)); } return out; }; diff --git a/src/js/storage.js b/src/js/storage.js index cbaedcb88..b65914c9e 100644 --- a/src/js/storage.js +++ b/src/js/storage.js @@ -799,7 +799,7 @@ self.addEventListener('hiddenSettingsChanged', ( ) => { // https://adblockplus.org/en/filters const staticNetFilteringEngine = this.staticNetFilteringEngine; const staticExtFilteringEngine = this.staticExtFilteringEngine; - const lineIter = new this.LineIterator(this.processDirectives(rawText)); + const lineIter = new this.LineIterator(this.processDirectives.prune(rawText)); const parser = new vAPI.StaticFilteringParser(); parser.setMaxTokenLength(this.urlTokenizer.MAX_TOKEN_LENGTH); @@ -854,69 +854,81 @@ self.addEventListener('hiddenSettingsChanged', ( ) => { // https://github.com/AdguardTeam/AdguardBrowserExtension/issues/917 -µBlock.processDirectives = function(content) { - const reIf = /^!#(if|endif)\b([^\n]*)/gm; - const stack = []; - const shouldDiscard = ( ) => stack.some(v => v); - const parts = []; - let beg = 0, discard = false; +µBlock.processDirectives = { + // This method returns an array of indices, corresponding to position in + // the content string which should alternatively be parsed and discarded. + split: function(content) { + const reIf = /^!#(if|endif)\b([^\n]*)(?:[\n\r]+|$)/gm; + const stack = []; + const shouldDiscard = ( ) => stack.some(v => v); + const parts = [ 0 ]; + let discard = false; - while ( beg < content.length ) { - const match = reIf.exec(content); - if ( match === null ) { break; } + for (;;) { + const match = reIf.exec(content); + if ( match === null ) { break; } - switch ( match[1] ) { - case 'if': - let expr = match[2].trim(); - const target = expr.charCodeAt(0) === 0x21 /* '!' */; - if ( target ) { expr = expr.slice(1); } - const token = this.processDirectives.tokens.get(expr); - const startDiscard = - token === 'false' && - target === false || - token !== undefined && - vAPI.webextFlavor.soup.has(token) === target; - if ( discard === false && startDiscard ) { - parts.push(content.slice(beg, match.index)); - discard = true; + switch ( match[1] ) { + case 'if': + let expr = match[2].trim(); + const target = expr.charCodeAt(0) === 0x21 /* '!' */; + if ( target ) { expr = expr.slice(1); } + const token = this.tokens.get(expr); + const startDiscard = + token === 'false' && + target === false || + token !== undefined && + vAPI.webextFlavor.soup.has(token) === target; + if ( discard === false && startDiscard ) { + parts.push(match.index); + discard = true; + } + stack.push(startDiscard); + break; + + case 'endif': + stack.pop(); + const stopDiscard = shouldDiscard() === false; + if ( discard && stopDiscard ) { + parts.push(match.index + match[0].length); + discard = false; + } + break; + + default: + break; } - stack.push(startDiscard); - break; - - case 'endif': - stack.pop(); - const stopDiscard = shouldDiscard() === false; - if ( discard && stopDiscard ) { - beg = match.index + match[0].length + 1; - discard = false; - } - break; - - default: - break; } - } - if ( stack.length === 0 && parts.length !== 0 ) { - parts.push(content.slice(beg)); - content = parts.join('\n'); - } - return content.trim(); + parts.push(content.length); + return parts; + }, + + prune: function(content) { + const parts = this.split(content); + const out = []; + for ( let i = 0, n = parts.length - 1; i < n; i += 2 ) { + const beg = parts[i+0]; + const end = parts[i+1]; + out.push(content.slice(beg, end)); + } + return out.join('\n'); + }, + + tokens: new Map([ + [ 'ext_ublock', 'ublock' ], + [ 'env_chromium', 'chromium' ], + [ 'env_edge', 'edge' ], + [ 'env_firefox', 'firefox' ], + [ 'env_legacy', 'legacy' ], + [ 'env_mobile', 'mobile' ], + [ 'env_safari', 'safari' ], + [ 'cap_html_filtering', 'html_filtering' ], + [ 'cap_user_stylesheet', 'user_stylesheet' ], + [ 'false', 'false' ], + ]), }; -µBlock.processDirectives.tokens = new Map([ - [ 'ext_ublock', 'ublock' ], - [ 'env_chromium', 'chromium' ], - [ 'env_edge', 'edge' ], - [ 'env_firefox', 'firefox' ], - [ 'env_legacy', 'legacy' ], - [ 'env_mobile', 'mobile' ], - [ 'env_safari', 'safari' ], - [ 'cap_html_filtering', 'html_filtering' ], - [ 'cap_user_stylesheet', 'user_stylesheet' ], - [ 'false', 'false' ], -]); - /******************************************************************************/ µBlock.loadRedirectResources = async function() {