From 12baeadac448d8b614399ace31218d511d5a9d41 Mon Sep 17 00:00:00 2001 From: gorhill Date: Sun, 27 Sep 2015 10:13:31 -0400 Subject: code review of new script tag filtering code --- src/js/contentscript-start.js | 23 ++++++--------- src/js/cosmetic-filtering.js | 67 +++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 73 insertions(+), 17 deletions(-) (limited to 'src') diff --git a/src/js/contentscript-start.js b/src/js/contentscript-start.js index 0eaa1d9..3d78a9f 100644 --- a/src/js/contentscript-start.js +++ b/src/js/contentscript-start.js @@ -74,7 +74,6 @@ var localMessager = vAPI.messaging.channel('contentscript-start.js'); var cosmeticFilters = function(details) { var donthideCosmeticFilters = {}; var hideCosmeticFilters = {}; - var scriptTagFilters = []; var donthide = details.cosmeticDonthide; var hide = details.cosmeticHide; var i; @@ -92,14 +91,9 @@ var cosmeticFilters = function(details) { selector = hide[i]; if ( donthideCosmeticFilters[selector] ) { hide.splice(i, 1); - continue; + } else { + hideCosmeticFilters[selector] = true; } - if ( selector.lastIndexOf('script//:', 0) === 0 ) { - scriptTagFilters.push(selector.slice(9)); - hide.splice(i, 1); - continue; - } - hideCosmeticFilters[selector] = true; } } if ( hide.length !== 0 ) { @@ -117,11 +111,6 @@ var cosmeticFilters = function(details) { } vAPI.donthideCosmeticFilters = donthideCosmeticFilters; vAPI.hideCosmeticFilters = hideCosmeticFilters; - - if ( scriptTagFilters.length !== 0 ) { - vAPI.reScriptTagFilters = new RegExp(scriptTagFilters.join('|')); - document.addEventListener('beforescriptexecute', onBeforeScriptExecuteHandler); - } }; var netFilters = function(details) { @@ -140,13 +129,14 @@ var netFilters = function(details) { }; var onBeforeScriptExecuteHandler = function(ev) { - if ( vAPI.reScriptTagFilters.test(ev.target.textContent) ) { + if ( vAPI.reScriptTagRegex.test(ev.target.textContent) ) { ev.preventDefault(); ev.stopPropagation(); } }; var filteringHandler = function(details) { + var value; var styleTagCount = vAPI.styles.length; vAPI.skipCosmeticFiltering = !details || details.skipCosmeticFiltering; @@ -157,6 +147,11 @@ var filteringHandler = function(details) { if ( details.netHide.length !== 0 ) { netFilters(details); } + value = details.scriptTagRegex; + if ( typeof value === 'string' && value.length !== 0 ) { + vAPI.reScriptTagRegex = new RegExp(value); + document.addEventListener('beforescriptexecute', onBeforeScriptExecuteHandler); + } // The port will never be used again at this point, disconnecting allows // the browser to flush this script from memory. } diff --git a/src/js/cosmetic-filtering.js b/src/js/cosmetic-filtering.js index 5d386af..8a0afa8 100644 --- a/src/js/cosmetic-filtering.js +++ b/src/js/cosmetic-filtering.js @@ -237,6 +237,7 @@ var FilterParser = function() { this.invalid = false; this.cosmetic = true; this.reParser = /^\s*([^#]*)(##|#@#)(.+)\s*$/; + this.reScriptSelectorParser = /^script:contains\(\/.+?\/\)$/; }; /******************************************************************************/ @@ -286,8 +287,8 @@ FilterParser.prototype.parse = function(s) { // Script tag filters: pre-process them so that can be used with minimal // overhead in the content script. if ( - this.suffix.lastIndexOf('script:contains(/', 0) === 0 && - this.suffix.slice(-2) === '/)' + this.suffix.charAt(0) === 's' && + this.reScriptSelectorParser.test(this.suffix) ) { // Currently supported only as non-generic selector. if ( this.prefix.length === 0 ) { @@ -568,6 +569,8 @@ FilterContainer.prototype.reset = function() { // hostname, entity-based filters this.hostnameFilters = {}; this.entityFilters = {}; + this.scriptTagFilters = {}; + this.scriptTagFilterCount = 0; }; /******************************************************************************/ @@ -807,6 +810,11 @@ FilterContainer.prototype.fromCompiledContent = function(text, lineBeg, skip) { // h ir twitter.com .promoted-tweet if ( fields[0] === 'h' ) { + // Special filter: script tags. Not a real CSS selector. + if ( fields[3].lastIndexOf('script//:', 0) === 0 ) { + this.createScriptTagFilter(fields[2], fields[3].slice(9)); + continue; + } filter = new FilterHostname(fields[3], fields[2]); bucket = this.hostnameFilters[fields[1]]; if ( bucket === undefined ) { @@ -838,6 +846,11 @@ FilterContainer.prototype.fromCompiledContent = function(text, lineBeg, skip) { // entity selector if ( fields[0] === 'e' ) { + // Special filter: script tags. Not a real CSS selector. + if ( fields[2].lastIndexOf('script//:', 0) === 0 ) { + this.createScriptTagFilter(fields[1], fields[2].slice(9)); + continue; + } bucket = this.entityFilters[fields[1]]; if ( bucket === undefined ) { this.entityFilters[fields[1]] = [fields[2]]; @@ -903,6 +916,49 @@ FilterContainer.prototype.skipCompiledContent = function(text, lineBeg) { /******************************************************************************/ +FilterContainer.prototype.createScriptTagFilter = function(hostname, s) { + if ( this.scriptTagFilters.hasOwnProperty(hostname) ) { + this.scriptTagFilters[hostname] += '|' + s; + } else { + this.scriptTagFilters[hostname] = s; + } + this.scriptTagFilterCount += 1; +}; + +/******************************************************************************/ + +FilterContainer.prototype.retrieveScriptTagRegex = function(domain, hostname) { + if ( this.scriptTagFilterCount === 0 ) { + return; + } + var out = [], hn = hostname, pos; + for (;;) { + if ( this.scriptTagFilters.hasOwnProperty(hn) ) { + out.push(this.scriptTagFilters[hn]); + } + if ( hn === domain ) { + break; + } + pos = hn.indexOf('.'); + if ( pos === -1 ) { + break; + } + hn = hn.slice(pos + 1); + } + pos = domain.indexOf('.'); + if ( pos !== -1 ) { + hn = domain.slice(0, pos); + if ( this.scriptTagFilters.hasOwnProperty(hn) ) { + out.push(this.scriptTagFilters[hn]); + } + } + if ( out.length !== 0 ) { + return out.join('|'); + } +}; + +/******************************************************************************/ + FilterContainer.prototype.freeze = function() { this.duplicateBuster = {}; @@ -956,7 +1012,9 @@ FilterContainer.prototype.toSelfie = function() { highMediumGenericHideCount: this.highMediumGenericHideCount, highHighGenericHide: this.highHighGenericHide, highHighGenericHideCount: this.highHighGenericHideCount, - genericDonthide: this.genericDonthide + genericDonthide: this.genericDonthide, + scriptTagFilters: this.scriptTagFilters, + scriptTagFilterCount: this.scriptTagFilterCount }; }; @@ -1017,6 +1075,8 @@ FilterContainer.prototype.fromSelfie = function(selfie) { this.highHighGenericHide = selfie.highHighGenericHide; this.highHighGenericHideCount = selfie.highHighGenericHideCount; this.genericDonthide = selfie.genericDonthide; + this.scriptTagFilters = selfie.scriptTagFilters; + this.scriptTagFilterCount = selfie.scriptTagFilterCount; this.frozen = true; }; @@ -1197,6 +1257,7 @@ FilterContainer.prototype.retrieveDomainSelectors = function(request) { cosmeticHide: [], cosmeticDonthide: [], netHide: [], + scriptTagRegex: this.retrieveScriptTagRegex(domain, hostname), netCollapse: µb.userSettings.collapseBlocked }; -- cgit v1.1