From 42938c9b63a42b1c7a42a98c69a736130b32bbd9 Mon Sep 17 00:00:00 2001 From: gorhill Date: Mon, 26 Sep 2016 13:45:55 -0400 Subject: [PATCH 1/5] code review re. #1954: also support implicit entity-based scriptlets --- src/js/cosmetic-filtering.js | 51 +++++++++++++++--------------------- 1 file changed, 21 insertions(+), 30 deletions(-) diff --git a/src/js/cosmetic-filtering.js b/src/js/cosmetic-filtering.js index ee530ee74..84515a465 100644 --- a/src/js/cosmetic-filtering.js +++ b/src/js/cosmetic-filtering.js @@ -1304,41 +1304,31 @@ FilterContainer.prototype.createUserScriptRule = function(hash, hostname, select // 14 -1 FilterContainer.prototype.retrieveUserScripts = function(domain, hostname) { - if ( this.userScriptCount === 0 ) { - return; - } + if ( this.userScriptCount === 0 ) { return; } var reng = µb.redirectEngine; - if ( !reng ) { - return; - } + if ( !reng ) { return; } var out = [], - scripts = Object.create(null), + scripts = new Map(), pos = domain.indexOf('.'), - entity = pos !== -1 ? domain.slice(0, pos) + '.*' : '', - token, content; + entity = pos !== -1 ? domain.slice(0, pos) + '.*' : ''; // Implicit var hn = hostname; for (;;) { - token = hn + '.js'; - if ( - (scripts[token] === undefined) && - (content = reng.resourceContentFromName(token, 'application/javascript')) - ) { - scripts[token] = out.length; - out.push(content); - } + this._lookupUserScript(scripts, hn + '.js', reng, out); if ( hn === domain ) { break; } pos = hn.indexOf('.'); if ( pos === -1 ) { break; } hn = hn.slice(pos + 1); } + if ( entity !== '' ) { + this._lookupUserScript(scripts, entity + '.js', reng, out); + } // Explicit (hash is domain). - var selectors = [], - selector, bucket; + var selectors = [], bucket; if ( (bucket = this.userScripts.get(domain)) ) { bucket.retrieve(hostname, selectors); } @@ -1347,15 +1337,7 @@ FilterContainer.prototype.retrieveUserScripts = function(domain, hostname) { } var i = selectors.length; while ( i-- ) { - selector = selectors[i]; - token = selector.slice(14, -1); - if ( - (scripts[token] === undefined) && - (content = reng.resourceContentFromName(token, 'application/javascript')) - ) { - scripts[token] = out.length; - out.push(content); - } + this._lookupUserScript(scripts, selectors[i].slice(14, -1), reng, out); } if ( out.length === 0 ) { @@ -1364,7 +1346,7 @@ FilterContainer.prototype.retrieveUserScripts = function(domain, hostname) { // Exceptions should be rare, so we check for exception only if there are // scriptlets returned. - var exceptions = [], j; + var exceptions = [], j, token; if ( (bucket = this.userScripts.get('!' + domain)) ) { bucket.retrieve(hostname, exceptions); } @@ -1374,7 +1356,7 @@ FilterContainer.prototype.retrieveUserScripts = function(domain, hostname) { i = exceptions.length; while ( i-- ) { token = exceptions[i].slice(14, -1); - if ( (j = scripts[token]) !== undefined ) { + if ( (j = scripts.get(token)) !== undefined ) { out[j] = '// User script "' + token + '" excepted.\n'; } } @@ -1382,6 +1364,15 @@ FilterContainer.prototype.retrieveUserScripts = function(domain, hostname) { return out.join('\n'); }; +FilterContainer.prototype._lookupUserScript = function(dict, token, reng, out) { + if ( dict.has(token) ) { return; } + var content = reng.resourceContentFromName(token, 'application/javascript'); + if ( content ) { + dict.set(token, out.length); + out.push(content); + } +}; + /******************************************************************************/ FilterContainer.prototype.toSelfie = function() { From 7984c7562cd96bcbd9ccfdcb2a9dc8e38cbad885 Mon Sep 17 00:00:00 2001 From: gorhill Date: Tue, 27 Sep 2016 08:31:12 -0400 Subject: [PATCH 2/5] fix #2033 --- src/css/popup.css | 5 ---- src/js/dynamic-net-filtering.js | 42 +++++++-------------------------- src/js/popup.js | 2 +- src/js/ublock.js | 8 +++++-- 4 files changed, 16 insertions(+), 41 deletions(-) diff --git a/src/css/popup.css b/src/css/popup.css index 3e0001b20..ca61207dd 100644 --- a/src/css/popup.css +++ b/src/css/popup.css @@ -473,11 +473,6 @@ body.portrait #firewallContainer > div > span:nth-of-type(4) { #actionSelector.colorBlind > span:nth-of-type(3) { background-color: rgb(0, 19, 110); } -#firewallContainer span.aRule #actionSelector > span:nth-of-type(1), -#firewallContainer span.nRule #actionSelector > span:nth-of-type(2), -#firewallContainer span.bRule #actionSelector > span:nth-of-type(3) { - visibility: hidden; - } #rulesetTools { background-color: transparent; diff --git a/src/js/dynamic-net-filtering.js b/src/js/dynamic-net-filtering.js index fff159efc..fc89f24cc 100644 --- a/src/js/dynamic-net-filtering.js +++ b/src/js/dynamic-net-filtering.js @@ -1,7 +1,7 @@ /******************************************************************************* - uBlock - a browser extension to block requests. - Copyright (C) 2014 Raymond Hill + uBlock Origin - a browser extension to block requests. + Copyright (C) 2014-2016 Raymond Hill This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -19,15 +19,15 @@ Home: https://github.com/gorhill/uBlock */ -/* global punycode, µBlock */ +/* global punycode */ /* jshint bitwise: false */ +'use strict'; + /******************************************************************************/ µBlock.Firewall = (function() { -'use strict'; - /******************************************************************************/ var magicId = 'chmdgxwtetgu'; @@ -94,6 +94,10 @@ var isIPAddress = function(hostname) { /******************************************************************************/ +// TODO: Rearrange the code so as to avoid calling isIPAddress() from within +// toBroaderHostname(). A hostname will never magically become an IP address +// when broadened -- so no need to test for this condition each call. + var toBroaderHostname = function(hostname) { if ( isIPAddress(hostname) ) { return '*'; @@ -261,38 +265,10 @@ Matrix.prototype.unsetCell = function(srcHostname, desHostname, type) { return true; }; -/******************************************************************************/ - -Matrix.prototype.setCellZ = function(srcHostname, desHostname, type, action) { - this.evaluateCellZY(srcHostname, desHostname, type); - if ( this.r === action ) { - return false; - } - this.setCell(srcHostname, desHostname, type, 0); - this.evaluateCellZY(srcHostname, desHostname, type); - if ( this.r === action ) { - return true; - } - this.setCell(srcHostname, desHostname, type, action); - return true; -}; - -/******************************************************************************/ - -Matrix.prototype.blockCell = function(srcHostname, desHostname, type) { - return this.setCellZ(srcHostname, desHostname, type, 1); -}; - // https://www.youtube.com/watch?v=Csewb_eIStY /******************************************************************************/ -Matrix.prototype.allowCell = function(srcHostname, desHostname, type) { - return this.setCellZ(srcHostname, desHostname, type, 2); -}; - -/******************************************************************************/ - Matrix.prototype.evaluateCell = function(srcHostname, desHostname, type) { var key = srcHostname + ' ' + desHostname; var bitmap = this.rules[key]; diff --git a/src/js/popup.js b/src/js/popup.js index 7f16de3d3..bd1839800 100644 --- a/src/js/popup.js +++ b/src/js/popup.js @@ -239,7 +239,7 @@ var updateFirewallCell = function(scope, des, type, rule) { cell.toggleClass(action + 'Rule', true); } - // Use dark shade visual cue if the filter is specific to the cell. + // Use dark shade visual cue if the rule is specific to the cell. var ownRule = false; var matches = reSrcHostnameFromRule.exec(rule); if ( matches !== null ) { diff --git a/src/js/ublock.js b/src/js/ublock.js index 63775a564..7472577ea 100644 --- a/src/js/ublock.js +++ b/src/js/ublock.js @@ -330,11 +330,15 @@ var matchWhitelistDirective = function(url, hostname, directive) { /******************************************************************************/ +// https://github.com/gorhill/uBlock/issues/2033 +// Always set own rules, trying to be fancy to avoid setting seemingly +// (but not really) redundant rules led to this issue. + µBlock.toggleFirewallRule = function(details) { var requestType = details.requestType; if ( details.action !== 0 ) { - this.sessionFirewall.setCellZ(details.srcHostname, details.desHostname, requestType, details.action); + this.sessionFirewall.setCell(details.srcHostname, details.desHostname, requestType, details.action); } else { this.sessionFirewall.unsetCell(details.srcHostname, details.desHostname, requestType); } @@ -342,7 +346,7 @@ var matchWhitelistDirective = function(url, hostname, directive) { // https://github.com/chrisaljoudi/uBlock/issues/731#issuecomment-73937469 if ( details.persist ) { if ( details.action !== 0 ) { - this.permanentFirewall.setCellZ(details.srcHostname, details.desHostname, requestType, details.action); + this.permanentFirewall.setCell(details.srcHostname, details.desHostname, requestType, details.action); } else { this.permanentFirewall.unsetCell(details.srcHostname, details.desHostname, requestType, details.action); } From c084853d9ad15d57b6c982bb8cf37743f544790a Mon Sep 17 00:00:00 2001 From: gorhill Date: Sat, 1 Oct 2016 12:34:25 -0400 Subject: [PATCH 3/5] fix #1772: ability to preview procedural cosmetic filters --- platform/chromium/vapi-client.js | 31 +- platform/firefox/vapi-client.js | 32 +- src/epicker.html | 34 +- src/js/scriptlets/element-picker.js | 595 +++++++++++++++++++--------- 4 files changed, 456 insertions(+), 236 deletions(-) diff --git a/platform/chromium/vapi-client.js b/platform/chromium/vapi-client.js index 5285e9338..dd954e20c 100644 --- a/platform/chromium/vapi-client.js +++ b/platform/chromium/vapi-client.js @@ -142,25 +142,24 @@ vAPI.setTimeout = vAPI.setTimeout || self.setTimeout.bind(self); /******************************************************************************/ -vAPI.shutdown = (function() { - var jobs = []; - - var add = function(job) { - jobs.push(job); - }; - - var exec = function() { +vAPI.shutdown = { + jobs: [], + add: function(job) { + this.jobs.push(job); + }, + exec: function() { var job; - while ( (job = jobs.pop()) ) { + while ( (job = this.jobs.pop()) ) { job(); } - }; - - return { - add: add, - exec: exec - }; -})(); + }, + remove: function(job) { + var pos; + while ( (pos = this.jobs.indexOf(job)) !== -1 ) { + this.jobs.splice(pos, 1); + } + } +}; /******************************************************************************/ /******************************************************************************/ diff --git a/platform/firefox/vapi-client.js b/platform/firefox/vapi-client.js index 1523989f0..8eb7f83dd 100644 --- a/platform/firefox/vapi-client.js +++ b/platform/firefox/vapi-client.js @@ -123,26 +123,24 @@ vAPI.setTimeout = vAPI.setTimeout || function(callback, delay, extra) { /******************************************************************************/ -vAPI.shutdown = (function() { - var jobs = []; - - var add = function(job) { - jobs.push(job); - }; - - var exec = function() { - //console.debug('Shutting down...'); +vAPI.shutdown = { + jobs: [], + add: function(job) { + this.jobs.push(job); + }, + exec: function() { var job; - while ( (job = jobs.pop()) ) { + while ( (job = this.jobs.pop()) ) { job(); } - }; - - return { - add: add, - exec: exec - }; -})(); + }, + remove: function(job) { + var pos; + while ( (pos = this.jobs.indexOf(job)) !== -1 ) { + this.jobs.splice(pos, 1); + } + } +}; /******************************************************************************/ diff --git a/src/epicker.html b/src/epicker.html index 6f4cdc48a..0e15e0a19 100644 --- a/src/epicker.html +++ b/src/epicker.html @@ -56,12 +56,14 @@ section { border: 0; box-sizing: border-box; display: inline-block; - position: relative; width: 100%; } -section > textarea { +section > div { + position: relative; +} +section > div > textarea { background-color: #fff; - border: 1px solid #ccc; + border: 1px solid #aaa; box-sizing: border-box; font: 11px monospace; height: 6em; @@ -70,15 +72,22 @@ section > textarea { resize: none; width: 100%; } -section > div { +section > div > textarea.invalidFilter { + background-color: #fee; +} +section > div > textarea + div { + background-color: #aaa; + bottom: 0; + color: white; + padding: 2px 4px; + position: absolute; + right: 0; +} +section > div + div { direction: ltr; margin: 2px 0; text-align: right; } -section > div > span:last-of-type { - position: absolute; - right: 0; -} ul { padding: 0; list-style-type: none; @@ -137,8 +146,12 @@ svg > path + path { body.preview svg > path { fill: rgba(0,0,0,0.10); } +body.preview svg > path + path { + stroke: none; +} aside { background-color: #eee; + border: 1px solid #aaa; bottom: 4px; box-sizing: border-box; visibility: hidden; @@ -162,7 +175,10 @@ body.paused > aside:hover {