aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--platform/chromium/vapi-background.js46
-rw-r--r--platform/firefox/vapi-background.js31
-rw-r--r--src/_locales/en/messages.json84
-rw-r--r--src/background.html1
-rw-r--r--src/css/common.css11
-rw-r--r--src/css/popup.css29
-rw-r--r--src/document-blocked.html18
-rw-r--r--src/js/background.js3
-rw-r--r--src/js/hnswitches.js283
-rw-r--r--src/js/messaging.js20
-rw-r--r--src/js/popup.js22
-rw-r--r--src/js/start.js5
-rw-r--r--src/js/static-net-filtering.js3
-rw-r--r--src/js/storage.js9
-rw-r--r--src/js/tab.js17
-rw-r--r--src/js/traffic.js26
-rw-r--r--src/js/ublock.js8
-rw-r--r--src/popup.html4
18 files changed, 496 insertions, 124 deletions
diff --git a/platform/chromium/vapi-background.js b/platform/chromium/vapi-background.js
index f77bd65..5b9f09d 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 7779a48..70a8aaa 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 812397d..b30af56 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 <code>script<\/code> tags are <b>allowed<\/b> on this site",
- "description":""
- },
- "popupSiteInlineScriptDisabled":{
- "message":"Inline <code>script<\/code> tags are <b>blocked<\/b> on this site",
- "description":""
- },
- "popupSite1pScriptEnabled":{
- "message":"1st-party scripts are <b>allowed<\/b> on this site",
- "description":""
- },
- "popupSite1pScriptDisabled":{
- "message":"1st-party scripts are <b>blocked<\/b> on this site",
- "description":""
- },
- "popupSite3pScriptEnabled":{
- "message":"3rd-party scripts are <b>allowed<\/b> on this site",
- "description":""
- },
- "popupSite3pScriptDisabled":{
- "message":"3rd-party scripts are <b>blocked<\/b> on this site",
- "description":""
- },
- "popupSite1pFrameEnabled":{
- "message":"1st-party frames are <b>allowed<\/b> on this site",
- "description":""
- },
- "popupSite1pFrameDisabled":{
- "message":"1st-party frames are <b>blocked<\/b> on this site",
- "description":""
- },
- "popupSite3pFrameEnabled":{
- "message":"3rd-party frames are <b>allowed<\/b> on this site",
- "description":""
- },
- "popupSite3pFrameDisabled":{
- "message":"3rd-party frames are <b>blocked<\/b> on this site",
- "description":""
+ "popupTipDoBlockAllPopups":{
+ "message":"Block all popups for this site",
+ "description":"English: Block all popups for this site"
},
- "popupDefaultInlineScriptEnabled":{
- "message":"Inline <code>script</code> tags are <b>allowed<\/b> everywhere by default",
- "description":""
- },
- "popupDefaultInlineScriptDisabled":{
- "message":"Inline <code>script</code> tags are <b>blocked<\/b> everywhere by default",
- "description":""
- },
- "popupDefault1pScriptEnabled":{
- "message":"1st-party scripts are <b>allowed<\/b> everywhere by default",
- "description":""
- },
- "popupDefault1pScriptDisabled":{
- "message":"1st-party scripts are <b>blocked<\/b> everywhere by default",
- "description":""
- },
- "popupDefault3pScriptEnabled":{
- "message":"3rd-party scripts are <b>allowed<\/b> everywhere by default",
- "description":""
- },
- "popupDefault3pScriptDisabled":{
- "message":"3rd-party scripts are <b>blocked<\/b> everywhere by default",
- "description":""
- },
- "popupDefault1pFrameEnabled":{
- "message":"1st-party frames are <b>allowed<\/b> everywhere by default",
- "description":""
- },
- "popupDefault1pFrameDisabled":{
- "message":"1st-party frames are <b>blocked<\/b> everywhere by default",
- "description":""
- },
- "popupDefault3pFrameEnabled":{
- "message":"3rd-party frames are <b>allowed<\/b> everywhere by default",
- "description":""
- },
- "popupDefault3pFrameDisabled":{
- "message":"3rd-party frames are <b>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 80faa11..07eb25d 100644
--- a/src/background.html
+++ b/src/background.html
@@ -19,6 +19,7 @@
<script src="js/dynamic-net-filtering.js"></script>
<script src="js/static-net-filtering.js"></script>
<script src="js/cosmetic-filtering.js"></script>
+<script src="js/hnswitches.js"></script>
<script src="js/ublock.js"></script>
<script src="js/messaging.js"></script>
<script src="js/profiler.js"></script>
diff --git a/src/css/common.css b/src/css/common.css
index 1ea1dd0..fec2c9b 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 eeff8e3..36d04fc 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 7bed594..c29baae 100644
--- a/src/document-blocked.html
+++ b/src/document-blocked.html
@@ -2,6 +2,7 @@
<html>
<head>
<title></title>
+<link rel="stylesheet" href="css/common.css" type="text/css">
<style>
body {
font-family: sans-serif;
@@ -33,21 +34,22 @@ button {
padding: 0.25em 0.5em;
font-size: inherit;
}
-img {
- height: 60vh;
- left: 10vw;
- opacity: 0.05;
+#warningSign {
+ margin: 1e, 0;
+ opacity: 1;
pointer-events: none;
- position: fixed;
- bottom: 5vh;
- width: 80vw;
+ width: 100%;
+ }
+#warningSign > span {
+ color: #f2a500;
+ font-size: 180px;
}
</style>
</head>
<body>
<!-- http://commons.wikimedia.org/wiki/File:Caution_sign_used_on_roads_pn.svg
Public domain. I removed the shadow.
---><img src="img/Caution_sign_used_on_roads_pn.svg" />
+--><div id="warningSign"><span class="fa">&#xf071;</span></div>
<div>
<p data-i18n="docblockedPrompt1"></p>
<p class="what code"></p>
diff --git a/src/js/background.js b/src/js/background.js
index c96b5d7..541d290 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 0000000..b90e9a0
--- /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 313ac9a..82f8e7e 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 5dc7aa0..9317f38 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 238ee4a..eda993f 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 29c3789..ba88697 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 0e21697..8854fe9 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 f9f5f3b..77190e9 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 edf2f2a..0e97e30 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 0cc7e28..95d2f3b 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 8022a5e..13d11db 100644
--- a/src/popup.html
+++ b/src/popup.html
@@ -26,6 +26,10 @@
<p class="statValue" id="total-blocked">?</p>
<h2 data-i18n="popupHitDomainCountPrompt">&nbsp;</h2>
<p class="statValue" id="popupHitDomainCount">&nbsp;</p>
+ <div id="extraTools">
+ <span id="doBlockAllPopups" class="hnSwitch fa" data-i18n-tip="popupTipDoBlockAllPopups" data-tip-anchor="topcenter">&#xf0c5;<span></span></span>
+ <span id="dontBlockDoc" class="hnSwitch fa" data-i18n-tip="popupTipDontBlockDoc" data-tip-anchor="topcenter">&#xf071;<span></span></span>
+ </div>
<div id="refresh" class="fa">&#xf021;</div>
</div><!-- DO NOT REMOVE --><div>
<div id="firewallContainer">