diff --git a/src/css/popup.css b/src/css/popup.css index 8c69029a6..4f1524a54 100644 --- a/src/css/popup.css +++ b/src/css/popup.css @@ -165,14 +165,14 @@ body.dirty #refresh:hover { color: black; } -#dynamicFilteringContainer { +#firewallContainer { border: 0; font-size: 12px; margin: 0; padding: 0; text-align: right; } -#dynamicFilteringContainer > div { +#firewallContainer > div { background-color: #e6e6e6; border: 0; border-bottom: 1px solid white; @@ -180,10 +180,10 @@ body.dirty #refresh:hover { margin: 0; padding: 0; } -#dynamicFilteringContainer > div:hover { +#firewallContainer > div:hover { background-color: #f0f0f0; } -#dynamicFilteringContainer > div > span { +#firewallContainer > div > span { background-color: transparent; border: none; box-sizing: border-box; @@ -196,58 +196,58 @@ body.dirty #refresh:hover { position: relative; vertical-align: middle; } -#dynamicFilteringContainer > div:nth-of-type(1) > span { +#firewallContainer > div:nth-of-type(1) > span { height: 18px; line-height: 18px; } -#dynamicFilteringContainer > div > span:nth-of-type(1) { +#firewallContainer > div > span:nth-of-type(1) { border-right: 1px solid white; padding-right: 2px; text-overflow: ellipsis; width: 70%; } -#dynamicFilteringContainer > div > span:nth-of-type(2) { +#firewallContainer > div > span:nth-of-type(2) { cursor: pointer; width: 15%; } -#dynamicFilteringContainer > div > span:nth-of-type(3) { +#firewallContainer > div > span:nth-of-type(3) { border-left: 1px solid white; color: #444; cursor: pointer; text-align: center; width: 15%; } -#dynamicFilteringContainer > div.isDomain > span:nth-of-type(1) { +#firewallContainer > div.isDomain > span:nth-of-type(1) { font-weight: bold; } -#dynamicFilteringContainer > div.allowed > span:nth-of-type(1) { +#firewallContainer > div.allowed > span:nth-of-type(1) { background-color: rgba(0, 160, 0, 0.1); } -#dynamicFilteringContainer > div.blocked > span:nth-of-type(1) { +#firewallContainer > div.blocked > span:nth-of-type(1) { background-color: rgba(192, 0, 0, 0.1); } -#dynamicFilteringContainer > div.allowed.blocked > span:nth-of-type(1) { +#firewallContainer > div.allowed.blocked > span:nth-of-type(1) { background-color: rgba(192, 160, 0, 0.1); } -#dynamicFilteringContainer > div > span.aRule { +#firewallContainer > div > span.aRule { background-color: rgba(0, 160, 0, 0.3); } -#dynamicFilteringContainer > div > span.bRule { +#firewallContainer > div > span.bRule { background-color: rgba(192, 0, 0, 0.3); } -#dynamicFilteringContainer > div > span.nRule { +#firewallContainer > div > span.nRule { background-color: rgba(96, 96, 96, 0.3); } -#dynamicFilteringContainer > div > span.ownRule { +#firewallContainer > div > span.ownRule { color: white; } -#dynamicFilteringContainer > div > span.aRule.ownRule { +#firewallContainer > div > span.aRule.ownRule { background-color: rgba(0, 160, 0, 1); } -#dynamicFilteringContainer > div > span.bRule.ownRule { +#firewallContainer > div > span.bRule.ownRule { background-color: rgba(192, 0, 0, 1); } -#dynamicFilteringContainer > div > span.nRule.ownRule { +#firewallContainer > div > span.nRule.ownRule { background-color: rgba(108, 108, 108, 1); } @@ -276,23 +276,28 @@ body.dirty #refresh:hover { #actionSelector > span:nth-of-type(3) { background-color: rgb(192, 0, 0); } -#dynamicFilteringContainer span.aRule #actionSelector > span:nth-of-type(1), -#dynamicFilteringContainer span.nRule #actionSelector > span:nth-of-type(2), -#dynamicFilteringContainer span.bRule #actionSelector > span:nth-of-type(3) { +#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; } -#offOverlay { - background-color: #fff; - bottom: 0; +#saveRules { + background-color: #ffe; + border: 1px solid #eec; + border-radius: 4px; + color: #888; + cursor: pointer; display: none; - left: 0; - opacity: 0.4; - pointer-events: none; - position: absolute; - right: 0; - top: 0; + font-size: 30px; + line-height: 40px; + padding: 0.1em 0.4em; + position: fixed; + text-align: center; } -body.off #offOverlay { +#firewallContainer.dirty ~ #saveRules { display: block; } +#firewallContainer.dirty ~ #saveRules:hover { + color: black; + } diff --git a/src/js/background.js b/src/js/background.js index e0b082d48..208fc5951 100644 --- a/src/js/background.js +++ b/src/js/background.js @@ -58,7 +58,6 @@ return { collapseBlocked: true, contextMenuEnabled: true, dynamicFilteringString: '', - dynamicFilteringSelfie: '', dynamicFilteringEnabled: false, experimentalEnabled: false, externalLists: defaultExternalLists, diff --git a/src/js/dynamic-net-filtering.js b/src/js/dynamic-net-filtering.js index 7a7c411be..0da5f4755 100644 --- a/src/js/dynamic-net-filtering.js +++ b/src/js/dynamic-net-filtering.js @@ -24,7 +24,7 @@ /******************************************************************************/ -µBlock.dynamicNetFilteringEngine = (function() { +µBlock.Firewall = (function() { /******************************************************************************/ @@ -119,6 +119,97 @@ Matrix.prototype.reset = function() { /******************************************************************************/ +Matrix.prototype.assign = function(other) { + var thisRules = this.rules; + var otherRules = other.rules; + var k; + + // Remove rules not in other + for ( k in thisRules ) { + if ( thisRules.hasOwnProperty(k) === false ) { + continue; + } + if ( otherRules.hasOwnProperty(k) === false ) { + delete thisRules[k]; + } + } + + // Add/change rules in other + for ( k in otherRules ) { + if ( otherRules.hasOwnProperty(k) === false ) { + continue; + } + thisRules[k] = otherRules[k]; + } +}; + +/******************************************************************************/ + +Matrix.prototype.copyRules = function(other, srcHostname, desHostnames) { + var thisRules = this.rules; + var otherRules = other.rules; + + // Specific types + thisRules['* *'] = otherRules['* *']; + var ruleKey = srcHostname + ' *'; + thisRules[ruleKey] = otherRules[ruleKey]; + + // Specific destinations + for ( var desHostname in desHostnames ) { + if ( desHostnames.hasOwnProperty(desHostname) === false ) { + continue; + } + ruleKey = '* ' + desHostname; + thisRules[ruleKey] = otherRules[ruleKey]; + ruleKey = srcHostname + ' ' + desHostname ; + thisRules[ruleKey] = otherRules[ruleKey]; + } + + return true; +}; + +/******************************************************************************/ + +// - * * type +// - from * type +// - * to * +// - from to * + +Matrix.prototype.hasSameRules = function(other, srcHostname, desHostnames) { + var thisRules = this.rules; + var otherRules = other.rules; + var ruleKey; + + // Specific types + ruleKey = '* *'; + if ( thisRules[ruleKey] !== otherRules[ruleKey] ) { + return false; + } + ruleKey = srcHostname + ' *'; + if ( thisRules[ruleKey] !== otherRules[ruleKey] ) { + return false; + } + + // Specific destinations + for ( var desHostname in desHostnames ) { + if ( desHostnames.hasOwnProperty(desHostname) === false ) { + continue; + } + ruleKey = '* ' + desHostname; + if ( thisRules[ruleKey] !== otherRules[ruleKey] ) { + return false; + } + ruleKey = srcHostname + ' ' + desHostname ; + if ( thisRules[ruleKey] !== otherRules[ruleKey] ) { + return false; + } + } + + return true; +}; + +/******************************************************************************/ + Matrix.prototype.setCell = function(srcHostname, desHostname, type, state) { var bitOffset = typeBitOffsets[type]; var k = srcHostname + ' ' + desHostname; @@ -501,7 +592,7 @@ Matrix.prototype.fromSelfie = function(selfie) { /******************************************************************************/ -return new Matrix(); +return Matrix; /******************************************************************************/ @@ -510,3 +601,8 @@ return new Matrix(); })(); /******************************************************************************/ + +µBlock.sessionFirewall = new µBlock.Firewall(); +µBlock.permanentFirewall = new µBlock.Firewall(); + +/******************************************************************************/ diff --git a/src/js/messaging.js b/src/js/messaging.js index f3baa58cc..af8a3bd6a 100644 --- a/src/js/messaging.js +++ b/src/js/messaging.js @@ -142,9 +142,9 @@ var getHostnameDict = function(hostnameToCountMap) { /******************************************************************************/ -var getDynamicFilterRules = function(srcHostname, desHostnames) { +var getFirewallRules = function(srcHostname, desHostnames) { var r = {}; - var dFiltering = µb.dynamicNetFilteringEngine; + var dFiltering = µb.sessionFirewall; r['/ * *'] = dFiltering.evaluateCellZY('*', '*', '*').toFilterString(); r['/ * image'] = dFiltering.evaluateCellZY('*', '*', 'image').toFilterString(); r['/ * 3p'] = dFiltering.evaluateCellZY('*', '*', '3p').toFilterString(); @@ -200,12 +200,19 @@ var getStats = function(tabId) { r.netFilteringSwitch = pageStore.getNetFilteringSwitch(); r.hostnameDict = getHostnameDict(pageStore.hostnameToCountMap); r.contentLastModified = pageStore.contentLastModified; - r.dynamicFilterRules = getDynamicFilterRules(pageStore.pageHostname, r.hostnameDict); + r.firewallRules = getFirewallRules(pageStore.pageHostname, r.hostnameDict); r.canElementPicker = r.pageHostname.indexOf('.') !== -1; r.canRequestLog = canRequestLog; } else { r.hostnameDict = {}; - r.dynamicFilterRules = getDynamicFilterRules(); + r.firewallRules = getFirewallRules(); + } + if ( r.pageHostname ) { + r.matrixIsDirty = !µb.sessionFirewall.hasSameRules( + µb.permanentFirewall, + r.pageHostname, + r.hostnameDict + ); } return r; }; @@ -275,8 +282,17 @@ var onMessage = function(request, sender, callback) { response = lastModified !== request.contentLastModified; break; - case 'toggleDynamicFilter': - µb.toggleDynamicFilter(request); + case 'saveRules': + µb.permanentFirewall.copyRules( + µb.sessionFirewall, + request.srcHostname, + request.desHostnames + ); + µb.savePermanentFirewallRules(); + break; + + case 'toggleFirewallRule': + µb.toggleFirewallRule(request); response = getStats(request.tabId); break; @@ -723,13 +739,13 @@ var onMessage = function(request, sender, callback) { switch ( request.what ) { case 'getDynamicRules': - response = µb.dynamicNetFilteringEngine.toString(); + response = µb.permanentFirewall.toString(); break; case 'setDynamicRules': - µb.dynamicNetFilteringEngine.fromString(request.rawRules); - µb.saveDynamicRules(); - response = µb.dynamicNetFilteringEngine.toString(); + µb.permanentFirewall.fromString(request.rawRules); + µb.savePermanentFirewallRules(); + response = µb.permanentFirewall.toString(); break; default: diff --git a/src/js/pagestore.js b/src/js/pagestore.js index 926a975a3..e1104dac9 100644 --- a/src/js/pagestore.js +++ b/src/js/pagestore.js @@ -604,7 +604,7 @@ PageStore.prototype.getNetFilteringSwitch = function() { PageStore.prototype.getSpecificCosmeticFilteringSwitch = function() { return this.getNetFilteringSwitch() && (µb.userSettings.advancedUserEnabled && - µb.dynamicNetFilteringEngine.mustAllowCellZY(this.rootHostname, this.rootHostname, '*')) === false; + µb.sessionFirewall.mustAllowCellZY(this.rootHostname, this.rootHostname, '*')) === false; }; /******************************************************************************/ @@ -613,7 +613,7 @@ PageStore.prototype.getGenericCosmeticFilteringSwitch = function() { return this.getNetFilteringSwitch() && this.skipCosmeticFiltering === false && (µb.userSettings.advancedUserEnabled && - µb.dynamicNetFilteringEngine.mustAllowCellZY(this.rootHostname, this.rootHostname, '*')) === false; + µb.sessionFirewall.mustAllowCellZY(this.rootHostname, this.rootHostname, '*')) === false; }; /******************************************************************************/ @@ -648,7 +648,7 @@ PageStore.prototype.filterRequest = function(context) { // We evaluate dynamic filtering first, and hopefully we can skip // evaluation of static filtering. if ( µb.userSettings.advancedUserEnabled ) { - var df = µb.dynamicNetFilteringEngine.evaluateCellZY(context.rootHostname, context.requestHostname, context.requestType); + var df = µb.sessionFirewall.evaluateCellZY(context.rootHostname, context.requestHostname, context.requestType); if ( df.mustBlockOrAllow() ) { result = df.toFilterString(); } @@ -689,8 +689,7 @@ PageStore.prototype.filterRequestNoCache = function(context) { // We evaluate dynamic filtering first, and hopefully we can skip // evaluation of static filtering. if ( µb.userSettings.advancedUserEnabled ) { - var df = µb.dynamicNetFilteringEngine.clearRegisters(); - df.evaluateCellZY(context.rootHostname, context.requestHostname, context.requestType); + var df = µb.sessionFirewall.evaluateCellZY(context.rootHostname, context.requestHostname, context.requestType); if ( df.mustBlockOrAllow() ) { result = df.toFilterString(); } diff --git a/src/js/popup.js b/src/js/popup.js index c37ada143..94d8c29dd 100644 --- a/src/js/popup.js +++ b/src/js/popup.js @@ -99,7 +99,7 @@ var hashFromPopupData = function(reset) { } var hasher = []; - var rules = popupData.dynamicFilterRules; + var rules = popupData.firewallRules; var rule; for ( var key in rules ) { if ( rules.hasOwnProperty(key) === false ) { @@ -141,7 +141,7 @@ var rulekeyCompare = function(a, b) { /******************************************************************************/ -var addDynamicFilterRow = function(des) { +var addFirewallRow = function(des) { var row = rowsToRecycle.pop(); if ( row.length === 0 ) { row = uDom('#templates > div:nth-of-type(1)').clone(); @@ -155,7 +155,7 @@ var addDynamicFilterRow = function(des) { row.toggleClass('isDomain', des === hnDetails.domain); row.toggleClass('allowed', hnDetails.allowCount !== 0); row.toggleClass('blocked', hnDetails.blockCount !== 0); - row.appendTo('#dynamicFilteringContainer'); + row.appendTo('#firewallContainer'); // Hacky? I couldn't figure a CSS recipe for this problem. // I do not want the left pane -- optional and hidden by defaut -- to @@ -171,8 +171,8 @@ var addDynamicFilterRow = function(des) { /******************************************************************************/ -var updateDynamicFilterCell = function(scope, des, type, rule) { - var selector = '#dynamicFilteringContainer span[data-src="' + scope + '"][data-des="' + des + '"][data-type="' + type + '"]'; +var updateFirewallCell = function(scope, des, type, rule) { + var selector = '#firewallContainer span[data-src="' + scope + '"][data-des="' + des + '"][data-type="' + type + '"]'; var cell = uDom(selector); // This should not happen @@ -230,48 +230,61 @@ var updateDynamicFilterCell = function(scope, des, type, rule) { /******************************************************************************/ -var updateAllDynamicFilters = function() { - var rules = popupData.dynamicFilterRules; +var updateAllFirewallCells = function() { + var rules = popupData.firewallRules; for ( var key in rules ) { if ( rules.hasOwnProperty(key) === false ) { continue; } - updateDynamicFilterCell( + updateFirewallCell( key.charAt(0), key.slice(2, key.indexOf(' ', 2)), key.slice(key.lastIndexOf(' ') + 1), rules[key] ); } + + uDom('#firewallContainer').toggleClass( + 'dirty', + popupData.matrixIsDirty === true + ); }; /******************************************************************************/ -var buildAllDynamicFilters = function() { +var buildAllFirewallRows = function() { // Do this before removing the rows if ( dfHotspots === null ) { - dfHotspots = uDom('#actionSelector').on('click', 'span', setDynamicFilterHandler); + dfHotspots = uDom('#actionSelector').on('click', 'span', setFirewallRuleHandler); } dfHotspots.detach(); // Remove and reuse all rows: the order may have changed, we can't just // reuse them in-place. - rowsToRecycle = uDom('#dynamicFilteringContainer > div:nth-of-type(7) ~ div').detach(); + rowsToRecycle = uDom('#firewallContainer > div:nth-of-type(7) ~ div').detach(); var n = allHostnameRows.length; for ( var i = 0; i < n; i++ ) { - addDynamicFilterRow(allHostnameRows[i]); + addFirewallRow(allHostnameRows[i]); } if ( dfPaneBuilt !== true ) { - uDom('#dynamicFilteringContainer') - .on('click', 'span[data-src]', unsetDynamicFilterHandler) + uDom('#firewallContainer') + .on('click', 'span[data-src]', unsetFirewallRuleHandler) .on('mouseenter', '[data-src]', mouseenterCellHandler) .on('mouseleave', '[data-src]', mouseleaveCellHandler); dfPaneBuilt = true; } - updateAllDynamicFilters(); + // The padlock must be manually positioned, because its position depends + // on whether there is a vertical scrollbar. + var pane = document.getElementById('firewallContainer'); + var rect = pane.getBoundingClientRect(); + var padlock = document.getElementById('saveRules'); + padlock.style.setProperty('left', (rect.left + 4) + 'px'); + padlock.style.setProperty('top', (rect.top + 4) + 'px'); + + updateAllFirewallCells(); }; /******************************************************************************/ @@ -284,7 +297,7 @@ var renderPrivacyExposure = function() { // Sort hostnames. First-party hostnames must always appear at the top // of the list. var desHostnameDone = {}; - var keys = Object.keys(popupData.dynamicFilterRules) + var keys = Object.keys(popupData.firewallRules) .sort(rulekeyCompare); var key, des, hnDetails; for ( var i = 0; i < keys.length; i++ ) { @@ -384,7 +397,7 @@ var renderPopup = function() { // Build dynamic filtering pane only if in use if ( dfPaneVisible ) { - buildAllDynamicFilters(); + buildAllFirewallRows(); } }; @@ -442,7 +455,7 @@ var gotoURL = function(ev) { /******************************************************************************/ -var toggleDynamicFiltering = function() { +var toggleFirewallPane = function() { if ( popupData.advancedUserEnabled === false ) { return; } @@ -457,7 +470,7 @@ var toggleDynamicFiltering = function() { // Dynamic filtering pane may not have been built yet uDom('#panes').toggleClass('dfEnabled', popupData.dfEnabled); if ( popupData.dfEnabled && dfPaneBuilt === false ) { - buildAllDynamicFilters(); + buildAllFirewallRows(); } }; @@ -475,32 +488,32 @@ var mouseleaveCellHandler = function() { /******************************************************************************/ -var setDynamicFilter = function(src, des, type, action) { +var setFirewallRule = function(src, des, type, action) { // This can happen on pages where uBlock does not work if ( typeof popupData.pageHostname !== 'string' || popupData.pageHostname === '' ) { return; } - var onDynamicFilterChanged = function(response) { + var onFirewallRuleChanged = function(response) { cachePopupData(response); - updateAllDynamicFilters(); + updateAllFirewallCells(); hashFromPopupData(); }; messager.send({ - what: 'toggleDynamicFilter', + what: 'toggleFirewallRule', tabId: popupData.tabId, pageHostname: popupData.pageHostname, srcHostname: src, desHostname: des, requestType: type, action: action - }, onDynamicFilterChanged); + }, onFirewallRuleChanged); }; /******************************************************************************/ -var unsetDynamicFilterHandler = function() { +var unsetFirewallRuleHandler = function() { var cell = uDom(this); - setDynamicFilter( + setFirewallRule( cell.attr('data-src') === '/' ? '*' : popupData.pageHostname, cell.attr('data-des'), cell.attr('data-type'), @@ -511,7 +524,7 @@ var unsetDynamicFilterHandler = function() { /******************************************************************************/ -var setDynamicFilterHandler = function() { +var setFirewallRuleHandler = function() { var hotspot = uDom(this); var cell = hotspot.ancestors('[data-src]'); if ( cell.length === 0 ) { @@ -526,7 +539,7 @@ var setDynamicFilterHandler = function() { } else { action = 1; } - setDynamicFilter( + setFirewallRule( cell.attr('data-src') === '/' ? '*' : popupData.pageHostname, cell.attr('data-des'), cell.attr('data-type'), @@ -553,6 +566,16 @@ var reloadTab = function() { /******************************************************************************/ +var saveRules = function() { + messager.send({ what: 'saveRules', + 'srcHostname': popupData.pageHostname, + 'desHostnames': popupData.hostnameDict + }); + uDom('#firewallContainer').removeClass('dirty'); +}; + +/******************************************************************************/ + // Poll for changes. // // I couldn't find a better way to be notified of changes which can affect @@ -625,8 +648,9 @@ uDom.onLoad(function() { uDom('#switch').on('click', toggleNetFilteringSwitch); uDom('#gotoPick').on('click', gotoPick); uDom('a[href]').on('click', gotoURL); - uDom('h2').on('click', toggleDynamicFiltering); + uDom('h2').on('click', toggleFirewallPane); uDom('#refresh').on('click', reloadTab); + uDom('#saveRules').on('click', saveRules); }); /******************************************************************************/ diff --git a/src/js/storage.js b/src/js/storage.js index ff6c267e4..39c4e7c2f 100644 --- a/src/js/storage.js +++ b/src/js/storage.js @@ -84,8 +84,8 @@ /******************************************************************************/ -µBlock.saveDynamicRules = function() { - this.userSettings.dynamicFilteringString = this.dynamicNetFilteringEngine.toString(); +µBlock.savePermanentFirewallRules = function() { + this.userSettings.dynamicFilteringString = this.permanentFirewall.toString(); this.XAL.keyvalSetOne('dynamicFilteringString', this.userSettings.dynamicFilteringString); }; @@ -715,25 +715,12 @@ µb.mirrors.toggle(false /* userSettings.experimentalEnabled */); µb.contextMenu.toggle(userSettings.contextMenuEnabled); - µb.dynamicNetFilteringEngine.fromString(userSettings.dynamicFilteringString); + µb.permanentFirewall.fromString(userSettings.dynamicFilteringString); + µb.sessionFirewall.assign(µb.permanentFirewall); // Remove obsolete setting delete userSettings.logRequests; µb.XAL.keyvalRemoveOne('logRequests'); - - if ( typeof userSettings.dynamicFilteringSelfie === 'string' ) { - if ( userSettings.dynamicFilteringString === '' && userSettings.dynamicFilteringSelfie !== '' ) { - µb.dynamicNetFilteringEngine.fromObsoleteSelfie(userSettings.dynamicFilteringSelfie); - userSettings.dynamicFilteringString = µb.dynamicNetFilteringEngine.toString(); - µb.XAL.keyvalSetOne('dynamicFilteringString', userSettings.dynamicFilteringString); - - // Auto-enable advanced user if there were dynamic rules - userSettings.advancedUserEnabled = userSettings.dynamicFilteringString !== ''; - µb.XAL.keyvalSetOne('advancedUserEnabled', userSettings.advancedUserEnabled); - } - delete userSettings.dynamicFilteringSelfie; - µb.XAL.keyvalRemoveOne('dynamicFilteringSelfie'); - } }; this.loadUserSettings(onUserSettingsReady); diff --git a/src/js/ublock.js b/src/js/ublock.js index c2753649a..4ad8c78ab 100644 --- a/src/js/ublock.js +++ b/src/js/ublock.js @@ -280,19 +280,13 @@ var matchWhitelistDirective = function(url, hostname, directive) { /******************************************************************************/ -µBlock.toggleDynamicFilter = function(details) { +µBlock.toggleFirewallRule = function(details) { var changed = false; if ( details.action !== 0 ) { - changed = this.dynamicNetFilteringEngine.setCellZ(details.srcHostname, details.desHostname, details.requestType, details.action); + this.sessionFirewall.setCellZ(details.srcHostname, details.desHostname, details.requestType, details.action); } else { - changed = this.dynamicNetFilteringEngine.unsetCell(details.srcHostname, details.desHostname, details.requestType); + this.sessionFirewall.unsetCell(details.srcHostname, details.desHostname, details.requestType); } - if ( !changed ) { - return; - } - - this.userSettings.dynamicFilteringString = this.dynamicNetFilteringEngine.toString(); - this.XAL.keyvalSetOne('dynamicFilteringString', this.userSettings.dynamicFilteringString); // https://github.com/gorhill/uBlock/issues/420 this.cosmeticFilteringEngine.removeFromSelectorCache(details.srcHostname, 'net'); diff --git a/src/popup.html b/src/popup.html index 1b8026b0e..13692cec1 100644 --- a/src/popup.html +++ b/src/popup.html @@ -27,7 +27,7 @@