From f2ff0edfaf4fdc6e02a8a0e65db73a104c2b1f9c Mon Sep 17 00:00:00 2001 From: gorhill Date: Fri, 27 Mar 2015 13:00:55 -0400 Subject: [PATCH] this fixes #1013, #1062 (draft) --- platform/chromium/vapi-background.js | 46 ++++- platform/firefox/vapi-background.js | 31 ++- src/_locales/en/messages.json | 84 +------- src/background.html | 1 + src/css/common.css | 11 +- src/css/popup.css | 29 ++- src/document-blocked.html | 18 +- src/js/background.js | 3 +- src/js/hnswitches.js | 283 +++++++++++++++++++++++++++ src/js/messaging.js | 20 +- src/js/popup.js | 22 +++ src/js/start.js | 5 +- src/js/static-net-filtering.js | 3 +- src/js/storage.js | 9 +- src/js/tab.js | 17 +- src/js/traffic.js | 26 +-- src/js/ublock.js | 8 + src/popup.html | 4 + 18 files changed, 496 insertions(+), 124 deletions(-) create mode 100644 src/js/hnswitches.js diff --git a/platform/chromium/vapi-background.js b/platform/chromium/vapi-background.js index f77bd65a0..5b9f09dea 100644 --- a/platform/chromium/vapi-background.js +++ b/platform/chromium/vapi-background.js @@ -55,6 +55,8 @@ vAPI.app.restart = function() { /******************************************************************************/ +// chrome.storage.local.get(null, function(bin){ console.debug('%o', bin); }); + vAPI.storage = chrome.storage.local; /******************************************************************************/ @@ -74,6 +76,7 @@ vAPI.noTabId = '-1'; vAPI.tabs.registerListeners = function() { var onNavigationClient = this.onNavigation || noopFunc; var onPopupClient = this.onPopup || noopFunc; + var onUpdatedClient = this.onUpdated || noopFunc; // https://developer.chrome.com/extensions/webNavigation // [onCreatedNavigationTarget ->] @@ -160,6 +163,13 @@ vAPI.tabs.registerListeners = function() { popupCandidateTest(details); }; + var onUpdated = function(tabId, changeInfo, tab) { + if ( changeInfo.url && popupCandidateTest({ tabId: tabId, url: changeInfo.url }) ) { + return; + } + onUpdatedClient(tabId, changeInfo, tab); + }; + var onCommitted = function(details) { if ( details.frameId !== 0 ) { return; @@ -175,10 +185,7 @@ vAPI.tabs.registerListeners = function() { chrome.webNavigation.onCreatedNavigationTarget.addListener(onCreatedNavigationTarget); chrome.webNavigation.onBeforeNavigate.addListener(onBeforeNavigate); chrome.webNavigation.onCommitted.addListener(onCommitted); - - if ( typeof this.onUpdated === 'function' ) { - chrome.tabs.onUpdated.addListener(this.onUpdated); - } + chrome.tabs.onUpdated.addListener(onUpdated); if ( typeof this.onClosed === 'function' ) { chrome.tabs.onRemoved.addListener(this.onClosed); @@ -308,6 +315,37 @@ vAPI.tabs.open = function(details) { /******************************************************************************/ +// Replace the URL of a tab. Noop if the tab does not exist. + +vAPI.tabs.replace = function(tabId, url) { + var targetURL = url; + if ( typeof targetURL !== 'string' || targetURL === '' ) { + return; + } + + // extension pages + if ( /^[\w-]{2,}:/.test(targetURL) !== true ) { + targetURL = vAPI.getURL(targetURL); + } + + if ( typeof tabId !== 'number' ) { + tabId = parseInt(tabId, 10); + if ( isNaN(tabId) ) { + return; + } + } + + chrome.tabs.update(tabId, { url: targetURL }, function() { + // this prevent console error + if ( chrome.runtime.lastError ) { + return; + } + + }); +}; + +/******************************************************************************/ + vAPI.tabs.remove = function(tabId) { var onTabRemoved = function() { if ( vAPI.lastError() ) { diff --git a/platform/firefox/vapi-background.js b/platform/firefox/vapi-background.js index 7779a48e9..70a8aaa9a 100644 --- a/platform/firefox/vapi-background.js +++ b/platform/firefox/vapi-background.js @@ -710,6 +710,24 @@ vAPI.tabs.open = function(details) { /******************************************************************************/ +// Replace the URL of a tab. Noop if the tab does not exist. + +vAPI.tabs.replace = function(tabId, url) { + var targetURL = url; + + // extension pages + if ( /^[\w-]{2,}:/.test(targetURL) !== true ) { + targetURL = vAPI.getURL(targetURL); + } + + var tab = this.getTabsForIds(tabId); + if ( tab ) { + getBrowserForTab(tab).loadURI(targetURL); + } +}; + +/******************************************************************************/ + vAPI.tabs._remove = function(tab, tabBrowser) { if ( vAPI.fennec ) { tabBrowser.closeTab(tab); @@ -1138,18 +1156,13 @@ var httpObserver = { return true; } - if ( result.redirectUrl ) { - if ( type === 'main_frame' ) { - channel.cancel(this.ABORT); - vAPI.tabs.open({ tabId: details.tabId, url: result.redirectUrl }); - return true; - } - /*channel.redirectionLimit = 1; + /*if ( result.redirectUrl ) { + channel.redirectionLimit = 1; channel.redirectTo( Services.io.newURI(result.redirectUrl, null, null) ); - return true;*/ - } + return true; + }*/ return false; }, diff --git a/src/_locales/en/messages.json b/src/_locales/en/messages.json index 812397de1..b30af5680 100644 --- a/src/_locales/en/messages.json +++ b/src/_locales/en/messages.json @@ -75,85 +75,13 @@ "message":"Go to request log", "description":"English: Go to request log" }, - "popupSiteInlineScriptEnabled":{ - "message":"Inline script<\/code> tags are allowed<\/b> on this site", - "description":"" + "popupTipDoBlockAllPopups":{ + "message":"Block all popups for this site", + "description":"English: Block all popups for this site" }, - "popupSiteInlineScriptDisabled":{ - "message":"Inline script<\/code> tags are blocked<\/b> on this site", - "description":"" - }, - "popupSite1pScriptEnabled":{ - "message":"1st-party scripts are allowed<\/b> on this site", - "description":"" - }, - "popupSite1pScriptDisabled":{ - "message":"1st-party scripts are blocked<\/b> on this site", - "description":"" - }, - "popupSite3pScriptEnabled":{ - "message":"3rd-party scripts are allowed<\/b> on this site", - "description":"" - }, - "popupSite3pScriptDisabled":{ - "message":"3rd-party scripts are blocked<\/b> on this site", - "description":"" - }, - "popupSite1pFrameEnabled":{ - "message":"1st-party frames are allowed<\/b> on this site", - "description":"" - }, - "popupSite1pFrameDisabled":{ - "message":"1st-party frames are blocked<\/b> on this site", - "description":"" - }, - "popupSite3pFrameEnabled":{ - "message":"3rd-party frames are allowed<\/b> on this site", - "description":"" - }, - "popupSite3pFrameDisabled":{ - "message":"3rd-party frames are blocked<\/b> on this site", - "description":"" - }, - "popupDefaultInlineScriptEnabled":{ - "message":"Inline script tags are allowed<\/b> everywhere by default", - "description":"" - }, - "popupDefaultInlineScriptDisabled":{ - "message":"Inline script tags are blocked<\/b> everywhere by default", - "description":"" - }, - "popupDefault1pScriptEnabled":{ - "message":"1st-party scripts are allowed<\/b> everywhere by default", - "description":"" - }, - "popupDefault1pScriptDisabled":{ - "message":"1st-party scripts are blocked<\/b> everywhere by default", - "description":"" - }, - "popupDefault3pScriptEnabled":{ - "message":"3rd-party scripts are allowed<\/b> everywhere by default", - "description":"" - }, - "popupDefault3pScriptDisabled":{ - "message":"3rd-party scripts are blocked<\/b> everywhere by default", - "description":"" - }, - "popupDefault1pFrameEnabled":{ - "message":"1st-party frames are allowed<\/b> everywhere by default", - "description":"" - }, - "popupDefault1pFrameDisabled":{ - "message":"1st-party frames are blocked<\/b> everywhere by default", - "description":"" - }, - "popupDefault3pFrameEnabled":{ - "message":"3rd-party frames are allowed<\/b> everywhere by default", - "description":"" - }, - "popupDefault3pFrameDisabled":{ - "message":"3rd-party frames are blocked<\/b> everywhere by default", - "description":"" + "popupTipDontBlockDoc":{ + "message":"Disable strict blocking for this site", + "description":"English: Disable strict blocking for this site" }, "popupAnyRulePrompt":{ "message":"all", diff --git a/src/background.html b/src/background.html index 80faa1108..07eb25d40 100644 --- a/src/background.html +++ b/src/background.html @@ -19,6 +19,7 @@ + diff --git a/src/css/common.css b/src/css/common.css index 1ea1dd0c0..fec2c9bc5 100644 --- a/src/css/common.css +++ b/src/css/common.css @@ -25,6 +25,7 @@ body:not(.advancedUser) [data-tip]:after { box-shadow: 1px 1px 3px gray; color: black; content: attr(data-tip); + display: none; font: 12px sans-serif; left: 0; line-height: 130%; @@ -38,7 +39,8 @@ body:not(.advancedUser) [data-tip]:after { pointer-events: none; opacity: 0; } -[data-tip]:hover:after { +body [data-tip]:hover:after { + display: initial; opacity: 1 !important; -webkit-transition: opacity 0.2s 0.4s; transition: opacity 0.2s 0.4s; @@ -60,6 +62,13 @@ body[dir=rtl] [data-tip][data-tip-anchor="top"]:hover:after { right: -500%; } +body [data-tip][data-tip-anchor="topcenter"]:hover:after { + bottom: 140%; + left: -225%; + right: -225%; + top: auto; + } + .hiddenFileInput { visibility: hidden; width: 0; diff --git a/src/css/popup.css b/src/css/popup.css index eeff8e3de..36d04fcd2 100644 --- a/src/css/popup.css +++ b/src/css/popup.css @@ -80,7 +80,7 @@ body[dir="rtl"] #panes > div:nth-of-type(2) { } #panes > div:nth-of-type(1) { min-width: 150px; - padding: 4px 1px; + padding: 0; } p { text-align: center; @@ -131,6 +131,33 @@ body.off #switch .fa { .tool:hover { color: #444; } +#extraTools { + background-color: #eee; + border: 0; + color: #aaa; + font-weight: normal; + margin: 1em 0 0 0; + padding: 4px 0 2px 0; + text-align: center; + } +#extraTools > span { + cursor: pointer; + font-size: 18px; + margin: 0 0.5em; + position: relative; + } +#extraTools > span.on > span { + color: #e00; + font-size: 20px; + left: 0; + position: absolute; + text-align: center; + top: 0; + width: 100%; + } +#extraTools > span.on > span:after { + content: '\2715'; + } body.advancedUser h2 { cursor: pointer; diff --git a/src/document-blocked.html b/src/document-blocked.html index 7bed5943a..c29baae15 100644 --- a/src/document-blocked.html +++ b/src/document-blocked.html @@ -2,6 +2,7 @@ + +-->

diff --git a/src/js/background.js b/src/js/background.js index c96b5d747..541d29040 100644 --- a/src/js/background.js +++ b/src/js/background.js @@ -57,7 +57,6 @@ return { autoUpdate: true, collapseBlocked: true, contextMenuEnabled: true, - dynamicFilteringString: '', dynamicFilteringEnabled: false, experimentalEnabled: false, externalLists: defaultExternalLists, @@ -88,7 +87,7 @@ return { // read-only systemSettings: { - compiledMagic: 'squafjaywuba', + compiledMagic: 'perhodsoahya', selfieMagic: 'spqmeuaftfra' }, diff --git a/src/js/hnswitches.js b/src/js/hnswitches.js new file mode 100644 index 000000000..b90e9a04f --- /dev/null +++ b/src/js/hnswitches.js @@ -0,0 +1,283 @@ +/******************************************************************************* + + uBlock - a Chromium browser extension to black/white list requests. + Copyright (C) 2015 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 + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see {http://www.gnu.org/licenses/}. + + Home: https://github.com/gorhill/uBlock +*/ + +/* global punycode, µBlock */ +/* jshint bitwise: false */ + +/******************************************************************************/ + +µBlock.HnSwitches = (function() { + +'use strict'; + +/******************************************************************************/ + +var HnSwitches = function() { + this.reset(); +}; + +/******************************************************************************/ + +var switchBitOffsets = { + 'dontBlockDoc': 0, + 'doBlockAllPopups': 2 +}; + +var switchStateToNameMap = { + '1': 'true', + '2': 'false' +}; + +var nameToSwitchStateMap = { + 'true': 1, + 'false': 2 +}; + +/******************************************************************************/ + +// For performance purpose, as simple tests as possible +var reHostnameVeryCoarse = /[g-z_-]/; +var reIPv4VeryCoarse = /\.\d+$/; + +// http://tools.ietf.org/html/rfc5952 +// 4.3: "MUST be represented in lowercase" +// Also: http://en.wikipedia.org/wiki/IPv6_address#Literal_IPv6_addresses_in_network_resource_identifiers + +var isIPAddress = function(hostname) { + if ( reHostnameVeryCoarse.test(hostname) ) { + return false; + } + if ( reIPv4VeryCoarse.test(hostname) ) { + return true; + } + return hostname.charAt(0) === '['; +}; + +/******************************************************************************/ + +var toBroaderHostname = function(hostname) { + if ( hostname === '*' ) { + return ''; + } + if ( isIPAddress(hostname) ) { + return '*'; + } + var pos = hostname.indexOf('.'); + if ( pos === -1 ) { + return '*'; + } + return hostname.slice(pos + 1); +}; + +HnSwitches.toBroaderHostname = toBroaderHostname; + +/******************************************************************************/ + +HnSwitches.prototype.reset = function() { + this.switches = {}; +}; + +/******************************************************************************/ + +// If value is undefined, the switch is removed + +HnSwitches.prototype.toggle = function(switchName, hostname, newVal) { + var bitOffset = switchBitOffsets[switchName]; + if ( bitOffset === undefined ) { + return false; + } + if ( newVal === this.evaluate(switchName, hostname) ) { + return false; + } + var bits = this.switches[hostname] || 0; + bits &= ~(3 << bitOffset); + bits |= newVal << bitOffset; + if ( bits === 0 ) { + delete this.switches[hostname]; + } else { + this.switches[hostname] = bits; + } + return true; +}; + +/******************************************************************************/ + +HnSwitches.prototype.toggleZ = function(switchName, hostname, newState) { + var bitOffset = switchBitOffsets[switchName]; + if ( bitOffset === undefined ) { + return false; + } + var state = this.evaluateZ(switchName, hostname); + if ( newState === state ) { + return false; + } + if ( newState === undefined ) { + newState = !state; + } + var bits = this.switches[hostname] || 0; + bits &= ~(3 << bitOffset); + if ( bits === 0 ) { + delete this.switches[hostname]; + } else { + this.switches[hostname] = bits; + } + state = this.evaluateZ(switchName, hostname); + if ( state === newState ) { + return true; + } + this.switches[hostname] = bits | ((newState ? 1 : 2) << bitOffset); + return true; +}; + +/******************************************************************************/ + +// 0 = inherit from broader scope, up to default state +// 1 = non-default state +// 2 = forced default state (to override a broader non-default state) + +HnSwitches.prototype.evaluate = function(switchName, hostname) { + var bits = this.switches[hostname] || 0; + if ( bits === 0 ) { + return 0; + } + var bitOffset = switchBitOffsets[switchName]; + if ( bitOffset === undefined ) { + return 0; + } + return (bits >> bitOffset) & 3; +}; + +/******************************************************************************/ + +HnSwitches.prototype.evaluateZ = function(switchName, hostname) { + var bitOffset = switchBitOffsets[switchName]; + if ( bitOffset === undefined ) { + return false; + } + var bits; + var s = hostname; + for (;;) { + bits = this.switches[s] || 0; + if ( bits !== 0 ) { + bits = bits >> bitOffset & 3; + if ( bits !== 0 ) { + return bits === 1; + } + } + s = toBroaderHostname(s); + if ( s === '' ) { + break; + } + } + return false; +}; + +/******************************************************************************/ + +HnSwitches.prototype.toString = function() { + var out = []; + var switchName, val; + var hostname; + for ( hostname in this.switches ) { + if ( this.switches.hasOwnProperty(hostname) === false ) { + continue; + } + for ( switchName in switchBitOffsets ) { + if ( switchBitOffsets.hasOwnProperty(switchName) === false ) { + continue; + } + val = this.evaluate(switchName, hostname); + if ( val === 0 ) { + continue; + } + out.push(switchName + ': ' + hostname + ' ' + switchStateToNameMap[val]); + } + } + return out.join('\n'); +}; + +/******************************************************************************/ + +HnSwitches.prototype.fromString = function(text) { + var textEnd = text.length; + var lineBeg = 0, lineEnd; + var line, pos; + var fields; + var switchName, hostname, state; + + while ( lineBeg < textEnd ) { + lineEnd = text.indexOf('\n', lineBeg); + if ( lineEnd < 0 ) { + lineEnd = text.indexOf('\r', lineBeg); + if ( lineEnd < 0 ) { + lineEnd = textEnd; + } + } + line = text.slice(lineBeg, lineEnd).trim(); + lineBeg = lineEnd + 1; + + pos = line.indexOf('# '); + if ( pos !== -1 ) { + line = line.slice(0, pos).trim(); + } + if ( line === '' ) { + continue; + } + + fields = line.split(/\s+/); + if ( fields.length !== 3 ) { + continue; + } + + switchName = fields[0]; + pos = switchName.indexOf(':'); + if ( pos === -1 ) { + continue; + } + switchName = switchName.slice(0, pos); + if ( switchBitOffsets.hasOwnProperty(switchName) === false ) { + continue; + } + + hostname = punycode.toASCII(fields[1]); + + state = fields[2]; + if ( nameToSwitchStateMap.hasOwnProperty(state) === false ) { + continue; + } + + this.toggle(switchName, hostname, nameToSwitchStateMap[state]); + } +}; + +/******************************************************************************/ + +return HnSwitches; + +/******************************************************************************/ + +})(); + +/******************************************************************************/ + +µBlock.hnSwitches = new µBlock.HnSwitches(); + +/******************************************************************************/ diff --git a/src/js/messaging.js b/src/js/messaging.js index 313ac9a07..82f8e7e8a 100644 --- a/src/js/messaging.js +++ b/src/js/messaging.js @@ -88,6 +88,10 @@ var onMessage = function(request, sender, callback) { µb.selectFilterLists(request.switches); break; + case 'toggleHostnameSwitch': + µb.toggleHostnameSwitch(request); + break; + case 'userSettings': response = µb.changeUserSettings(request.name, request.value); break; @@ -229,6 +233,8 @@ var getStats = function(tabId, tabTitle) { r.firewallRules = getFirewallRules(pageStore.pageHostname, r.hostnameDict); r.canElementPicker = r.pageHostname.indexOf('.') !== -1; r.canRequestLog = canRequestLog; + r.doBlockAllPopups = µb.hnSwitches.evaluateZ('doBlockAllPopups', r.pageHostname); + r.dontBlockDoc = µb.hnSwitches.evaluateZ('dontBlockDoc', r.pageHostname); } else { r.hostnameDict = {}; r.firewallRules = getFirewallRules(); @@ -1007,6 +1013,8 @@ var backupUserData = function(callback) { userSettings: µb.userSettings, filterLists: µb.remoteBlacklists, netWhitelist: µb.stringFromWhitelist(µb.netWhitelist), + dynamicFilteringString: µb.permanentFirewall.toString(), + hostnameSwitchesString: µb.hnSwitches.toString(), userFilters: details.content }; var now = new Date(); @@ -1033,7 +1041,7 @@ var backupUserData = function(callback) { var restoreUserData = function(request) { var userData = request.userData; - var countdown = 5; + var countdown = 7; var onCountdown = function() { countdown -= 1; if ( countdown === 0 ) { @@ -1047,9 +1055,15 @@ var restoreUserData = function(request) { µBlock.saveLocalSettings(true); µb.XAL.keyvalSetMany(userData.userSettings, onCountdown); µb.XAL.keyvalSetOne('remoteBlacklists', userData.filterLists, onCountdown); - µb.XAL.keyvalSetOne('netWhitelist', userData.netWhitelist, onCountdown); - µb.assets.put('assets/user/filters.txt', userData.userFilters, onCountdown); + µb.XAL.keyvalSetOne('netWhitelist', userData.netWhitelist || '', onCountdown); + // With versions 0.9.2.4-, dynamic rules were saved within the + // `userSettings` object. No longer the case. + var s = userData.dynamicFilteringString || userData.userSettings.dynamicFilteringString || ''; + µb.XAL.keyvalSetOne('dynamicFilteringString', s, onCountdown); + + µb.XAL.keyvalSetOne('hostnameSwitchesString', userData.hostnameSwitchesString || '', onCountdown); + µb.assets.put('assets/user/filters.txt', userData.userFilters, onCountdown); µb.XAL.keyvalSetMany({ lastRestoreFile: request.file || '', lastRestoreTime: Date.now(), diff --git a/src/js/popup.js b/src/js/popup.js index 5dc7aa042..9317f38f5 100644 --- a/src/js/popup.js +++ b/src/js/popup.js @@ -429,6 +429,10 @@ var renderPopup = function() { // This will collate all domains, touched or not renderPrivacyExposure(); + // Extra tools + uDom('#doBlockAllPopups').toggleClass('on', popupData.doBlockAllPopups === true); + uDom('#dontBlockDoc').toggleClass('on', popupData.dontBlockDoc === true); + // https://github.com/gorhill/uBlock/issues/470 // This must be done here, to be sure the popup is resized properly var dfPaneVisible = popupData.dfEnabled && popupData.advancedUserEnabled; @@ -651,6 +655,23 @@ var saveFirewallRules = function() { /******************************************************************************/ +var toggleHostnameSwitch = function() { + var elem = uDom(this); + var switchName = elem.attr('id'); + if ( !switchName ) { + return; + } + elem.toggleClass('on'); + messager.send({ + what: 'toggleHostnameSwitch', + name: switchName, + hostname: popupData.pageHostname, + state: elem.hasClass('on') + }); +}; + +/******************************************************************************/ + // Poll for changes. // // I couldn't find a better way to be notified of changes which can affect @@ -733,6 +754,7 @@ uDom.onLoad(function () { uDom('a[href]').on('click', gotoURL); uDom('h2').on('click', toggleFirewallPane); uDom('#refresh').on('click', reloadTab); + uDom('.hnSwitch').on('click', toggleHostnameSwitch); uDom('#saveRules').on('click', saveFirewallRules); uDom('[data-i18n="popupAnyRulePrompt"]').on('click', toggleMinimize); }); diff --git a/src/js/start.js b/src/js/start.js index 238ee4aea..eda993f58 100644 --- a/src/js/start.js +++ b/src/js/start.js @@ -135,8 +135,9 @@ var onUserSettingsReady = function(fetched) { µb.mirrors.toggle(false /* userSettings.experimentalEnabled */); µb.contextMenu.toggle(userSettings.contextMenuEnabled); - µb.permanentFirewall.fromString(userSettings.dynamicFilteringString); + µb.permanentFirewall.fromString(fetched.dynamicFilteringString); µb.sessionFirewall.assign(µb.permanentFirewall); + µb.hnSwitches.fromString(fetched.hostnameSwitchesString); // Remove obsolete setting delete userSettings.logRequests; @@ -214,6 +215,8 @@ return function() { var fetchableProps = { 'compiledMagic': '', + 'dynamicFilteringString': '', + 'hostnameSwitchesString': '', 'lastRestoreFile': '', 'lastRestoreTime': 0, 'lastBackupFile': '', diff --git a/src/js/static-net-filtering.js b/src/js/static-net-filtering.js index 29c3789f8..ba886976b 100644 --- a/src/js/static-net-filtering.js +++ b/src/js/static-net-filtering.js @@ -63,7 +63,6 @@ var typeNameToTypeValue = { 'xmlhttprequest': 5 << 4, 'sub_frame': 6 << 4, 'other': 7 << 4, - 'main_frame': 12 << 4, 'cosmetic-filtering': 13 << 4, 'inline-script': 14 << 4, 'popup': 15 << 4 @@ -2117,7 +2116,7 @@ FilterContainer.prototype.matchString = function(context) { // Use exact type match for anything beyond `other` // Also, be prepared to support unknown types var type = typeNameToTypeValue[context.requestType] || typeOtherValue; - if ( type > (7 << 4) ) { + if ( type > typeOtherValue ) { return this.matchStringExactType(context, context.requestURL, context.requestType); } diff --git a/src/js/storage.js b/src/js/storage.js index 0e216971b..8854fe9a0 100644 --- a/src/js/storage.js +++ b/src/js/storage.js @@ -70,8 +70,13 @@ /******************************************************************************/ µBlock.savePermanentFirewallRules = function() { - this.userSettings.dynamicFilteringString = this.permanentFirewall.toString(); - this.XAL.keyvalSetOne('dynamicFilteringString', this.userSettings.dynamicFilteringString); + this.XAL.keyvalSetOne('dynamicFilteringString', this.permanentFirewall.toString()); +}; + +/******************************************************************************/ + +µBlock.saveHostnameSwitches = function() { + this.XAL.keyvalSetOne('hostnameSwitchesString', this.hnSwitches.toString()); }; /******************************************************************************/ diff --git a/src/js/tab.js b/src/js/tab.js index f9f5f3b8e..77190e9df 100644 --- a/src/js/tab.js +++ b/src/js/tab.js @@ -111,11 +111,26 @@ vAPI.tabs.onPopup = function(details) { }; var targetURL = details.targetURL; + + // If the page URL is that of our "blocked page" URL, extract the URL of + // the page which was blocked. + if ( targetURL.lastIndexOf(vAPI.getURL('document-blocked.html'), 0) === 0 ) { + var matches = /details=([^&]+)/.exec(targetURL); + if ( matches !== null ) { + targetURL = JSON.parse(atob(matches[1])).url; + } + } + var result = ''; + // Check user switch first + if ( µb.hnSwitches.evaluateZ('doBlockAllPopups', openerHostname) ) { + result = 'ub:doBlockAllPopups true'; + } + // https://github.com/gorhill/uBlock/issues/323 // If popup URL is whitelisted, do not block it - if ( µb.getNetFilteringSwitch(targetURL) ) { + if ( result === '' && µb.getNetFilteringSwitch(targetURL) ) { result = µb.staticNetFilteringEngine.matchStringExactType(context, targetURL, 'popup'); } diff --git a/src/js/traffic.js b/src/js/traffic.js index edf2f2ab1..0e97e30cf 100644 --- a/src/js/traffic.js +++ b/src/js/traffic.js @@ -199,11 +199,18 @@ var onBeforeRootFrameRequest = function(details) { var result = ''; + // Permanently unrestricted? + if ( result === '' && µb.hnSwitches.evaluateZ('dontBlockDoc', requestHostname) ) { + result = 'ua:dontBlockDoc true'; + } + // Temporarily whitelisted? var obsolete = documentWhitelists[requestHostname]; if ( obsolete !== undefined ) { if ( obsolete > Date.now() ) { - result = 'da:*' + ' ' + requestHostname + ' doc allow'; + if ( result === '' ) { + result = 'ta:*' + ' ' + requestHostname + ' doc allow'; + } } else { delete documentWhitelists[requestHostname]; } @@ -211,15 +218,7 @@ var onBeforeRootFrameRequest = function(details) { // Filtering if ( result === '' && µb.getNetFilteringSwitch(requestURL) ) { - if ( µb.userSettings.advancedUserEnabled ) { - var df = µb.sessionFirewall.evaluateCellZY(requestHostname, requestHostname, '*'); - if ( df.mustBlockOrAllow() ) { - result = df.toFilterString(); - } - } - if ( result === '' ) { - result = µb.staticNetFilteringEngine.matchString(context); - } + result = µb.staticNetFilteringEngine.matchString(context); } // Log @@ -236,9 +235,12 @@ var onBeforeRootFrameRequest = function(details) { // Blocked var query = btoa(JSON.stringify({ url: requestURL, - why: result + '$document' + why: result })); - return { redirectUrl: vAPI.getURL('document-blocked.html?details=') + query }; + + vAPI.tabs.replace(details.tabId, vAPI.getURL('document-blocked.html?details=') + query); + + return { cancel: true }; }; /******************************************************************************/ diff --git a/src/js/ublock.js b/src/js/ublock.js index 0cc7e2882..95d2f3b9f 100644 --- a/src/js/ublock.js +++ b/src/js/ublock.js @@ -318,4 +318,12 @@ var matchWhitelistDirective = function(url, hostname, directive) { /******************************************************************************/ +µBlock.toggleHostnameSwitch = function(details) { + if ( this.hnSwitches.toggleZ(details.name, details.hostname, details.state) ) { + this.saveHostnameSwitches(); + } +}; + +/******************************************************************************/ + })(); diff --git a/src/popup.html b/src/popup.html index 8022a5efe..13d11db36 100644 --- a/src/popup.html +++ b/src/popup.html @@ -26,6 +26,10 @@

?

 

 

+
+ + +