diff --git a/platform/chromium/manifest.json b/platform/chromium/manifest.json
index 9f44f4e92..610fd0319 100644
--- a/platform/chromium/manifest.json
+++ b/platform/chromium/manifest.json
@@ -63,5 +63,8 @@
"http://*/*",
"https://*/*"
],
- "short_name": "uBlock"
+ "short_name": "uBlock",
+ "web_accessible_resources": [
+ "document-blocked.html"
+ ]
}
diff --git a/src/_locales/en/messages.json b/src/_locales/en/messages.json
index 2cd88b577..812397de1 100644
--- a/src/_locales/en/messages.json
+++ b/src/_locales/en/messages.json
@@ -567,6 +567,26 @@
"message": "off",
"description": "Firefox-specific: appears as 'uBlock (off)'"
},
+ "docblockedPrompt1": {
+ "message": "uBlock has prevented the following page from loading:",
+ "description": "English: uBlock has prevented the following page from loading:"
+ },
+ "docblockedPrompt2": {
+ "message": "Because of the following filter",
+ "description": "English: Because of the following filter"
+ },
+ "docblockedBack": {
+ "message": "Go back",
+ "description": "English: Go back"
+ },
+ "docblockedClose": {
+ "message": "Close this window",
+ "description": "English: Close this window"
+ },
+ "docblockedProceed": {
+ "message": "Proceed at your own risk",
+ "description": "English: Proceed at your own risk"
+ },
"dummy":{
"message":"This entry must be the last one",
"description":"so we dont need to deal with comma for last entry"
diff --git a/src/document-blocked.html b/src/document-blocked.html
new file mode 100644
index 000000000..da5a2ddca
--- /dev/null
+++ b/src/document-blocked.html
@@ -0,0 +1,49 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/img/Caution_sign_used_on_roads_pn.svg b/src/img/Caution_sign_used_on_roads_pn.svg
new file mode 100644
index 000000000..9eed3944b
--- /dev/null
+++ b/src/img/Caution_sign_used_on_roads_pn.svg
@@ -0,0 +1,176 @@
+
+
diff --git a/src/js/background.js b/src/js/background.js
index 36b446686..c96b5d747 100644
--- a/src/js/background.js
+++ b/src/js/background.js
@@ -88,7 +88,7 @@ return {
// read-only
systemSettings: {
- compiledMagic: 'intdliecolot',
+ compiledMagic: 'squafjaywuba',
selfieMagic: 'spqmeuaftfra'
},
diff --git a/src/js/document-blocked.js b/src/js/document-blocked.js
new file mode 100644
index 000000000..f7919287a
--- /dev/null
+++ b/src/js/document-blocked.js
@@ -0,0 +1,73 @@
+/*******************************************************************************
+
+ uBlock - a browser extension to block 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 uDom */
+
+/******************************************************************************/
+
+(function() {
+
+'use strict';
+
+/******************************************************************************/
+
+var messager = vAPI.messaging.channel('document-blocked.js');
+
+var matches = /details=([^&]+)/.exec(window.location.search);
+if ( matches === null ) {
+ return;
+}
+var details = JSON.parse(atob(matches[1]));
+
+/******************************************************************************/
+
+var yolo = function(ev) {
+ var onReady = function() {
+ window.location.replace(details.url);
+ };
+
+ messager.send({
+ what: 'temporarilyWhitelistDocument',
+ url: details.url
+ }, onReady);
+
+ ev.preventDefault();
+};
+
+/******************************************************************************/
+
+uDom('.what').text(details.url);
+uDom('#why').text(details.why.slice(3));
+
+if ( window.history.length > 1 ) {
+ uDom('#back').on('click', function() { window.history.back(); });
+} else {
+ uDom('#back').css('display', 'none');
+}
+
+uDom('#bye').on('click', function() { window.close(); });
+
+uDom('#yolo').attr('href', details.url)
+ .on('click', yolo);
+
+})();
+
+/******************************************************************************/
diff --git a/src/js/messaging.js b/src/js/messaging.js
index 4bbece661..e2349b749 100644
--- a/src/js/messaging.js
+++ b/src/js/messaging.js
@@ -1204,6 +1204,43 @@ vAPI.messaging.listen('subscriber.js', onMessage);
})();
-// https://www.youtube.com/watch?v=3_WcygKJP1k
+/******************************************************************************/
+/******************************************************************************/
+
+// document-blocked.js
+
+(function() {
+
+'use strict';
+
+/******************************************************************************/
+
+var onMessage = function(request, sender, callback) {
+ // Async
+ switch ( request.what ) {
+ default:
+ break;
+ }
+
+ // Sync
+ var response;
+
+ switch ( request.what ) {
+ case 'temporarilyWhitelistDocument':
+ µBlock.webRequest.temporarilyWhitelistDocument(request.url);
+ break;
+
+ default:
+ return vAPI.messaging.UNHANDLED;
+ }
+
+ callback(response);
+};
+
+vAPI.messaging.listen('document-blocked.js', onMessage);
+
+/******************************************************************************/
+
+})();
/******************************************************************************/
diff --git a/src/js/pagestore.js b/src/js/pagestore.js
index c01fd96a7..69d2be91d 100644
--- a/src/js/pagestore.js
+++ b/src/js/pagestore.js
@@ -515,6 +515,12 @@ PageStore.prototype.reuse = function(rawURL, pageURL, context) {
// return this;
//}
+ // If the hostname changes, we can't merely just update the context.
+ var pageHostname = µb.URI.hostnameFromURI(pageURL);
+ if ( pageHostname !== this.pageHostname ) {
+ context = '';
+ }
+
// If URL changes without a page reload (more and more common), then we
// need to keep all that we collected for reuse. In particular, not
// doing so was causing a problem in `videos.foxnews.com`: clicking a
@@ -523,10 +529,6 @@ PageStore.prototype.reuse = function(rawURL, pageURL, context) {
if ( context === 'tabUpdated' ) {
this.rawURL = rawURL;
this.pageURL = pageURL;
- this.pageHostname = µb.URI.hostnameFromURI(pageURL);
- this.pageDomain = µb.URI.domainFromHostname(this.pageHostname) || this.pageHostname;
- this.rootHostname = this.pageHostname;
- this.rootDomain = this.pageDomain;
// As part of https://github.com/gorhill/uBlock/issues/405
// URL changed, force a re-evaluation of filtering switch
diff --git a/src/js/static-net-filtering.js b/src/js/static-net-filtering.js
index f9e6488a6..dbcce5fc1 100644
--- a/src/js/static-net-filtering.js
+++ b/src/js/static-net-filtering.js
@@ -54,15 +54,16 @@ var AnyParty = 0 << 2;
var FirstParty = 1 << 2;
var ThirdParty = 2 << 2;
-var AnyType = 1 << 4;
+var AnyType = 0 << 4;
var typeNameToTypeValue = {
- 'stylesheet': 2 << 4,
- 'image': 3 << 4,
- 'object': 4 << 4,
- 'script': 5 << 4,
- 'xmlhttprequest': 6 << 4,
- 'sub_frame': 7 << 4,
- 'other': 8 << 4,
+ 'stylesheet': 1 << 4,
+ 'image': 2 << 4,
+ 'object': 3 << 4,
+ 'script': 4 << 4,
+ '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
@@ -71,13 +72,9 @@ var typeOtherValue = typeNameToTypeValue.other;
// All network request types to bitmap
// bring origin to 0 (from 4 -- see typeNameToTypeValue)
-// add 2 = number of left shift to use
// left-shift 1 by the above-calculated value
-// subtract 4 to set all type bits, *except* for 2 lsb
-
-// https://github.com/gorhill/uBlock/issues/723
-// The 2 lsb *must* be zeroed
-var allNetRequestTypesBitmap = (1 << (typeOtherValue >>> 4) + 2) - 4;
+// subtract 1 to set all type bits
+var allNetRequestTypesBitmap = (1 << (typeOtherValue >>> 4)) - 1;
var BlockAnyTypeAnyParty = BlockAction | AnyType | AnyParty;
var BlockAnyType = BlockAction | AnyType;
@@ -100,6 +97,8 @@ var reURLPostHostnameAnchors = /[\/?#]/;
var pageHostnameRegister = '';
var requestHostnameRegister = '';
+var filterRegister = null;
+var categoryRegister = '';
/******************************************************************************/
@@ -1192,6 +1191,7 @@ FilterParser.prototype.toNormalizedType = {
'xmlhttprequest': 'xmlhttprequest',
'subdocument': 'sub_frame',
'other': 'other',
+ 'document': 'main_frame',
'elemhide': 'cosmetic-filtering',
'inline-script': 'inline-script',
'popup': 'popup'
@@ -1227,10 +1227,10 @@ FilterParser.prototype.reset = function() {
// Be ready to handle multiple negated types
FilterParser.prototype.parseOptType = function(raw, not) {
- var type = typeNameToTypeValue[this.toNormalizedType[raw]];
+ var typeBit = 1 << ((typeNameToTypeValue[this.toNormalizedType[raw]] >>> 4) - 1);
if ( !not ) {
- this.types |= 1 << (type >>> 4);
+ this.types |= typeBit;
return;
}
@@ -1239,7 +1239,7 @@ FilterParser.prototype.parseOptType = function(raw, not) {
this.types = allNetRequestTypesBitmap;
}
- this.types &= ~(1 << (type >>> 4));
+ this.types &= ~typeBit;
};
/******************************************************************************/
@@ -1283,10 +1283,22 @@ FilterParser.prototype.parseOptions = function(s) {
this.parseOptParty(not);
continue;
}
- if ( opt === 'elemhide' && this.action === AllowAction ) {
- this.parseOptType('elemhide', false);
- this.action = BlockAction;
- continue;
+ if ( opt === 'elemhide' ) {
+ if ( this.action !== AllowAction ) {
+ this.parseOptType('elemhide', false);
+ this.action = BlockAction;
+ continue;
+ }
+ this.unsupported = true;
+ break;
+ }
+ if ( opt === 'document' ) {
+ if ( this.action === BlockAction ) {
+ this.parseOptType('document', false);
+ continue;
+ }
+ this.unsupported = true;
+ break;
}
if ( this.toNormalizedType.hasOwnProperty(opt) ) {
this.parseOptType(opt, not);
@@ -1743,9 +1755,20 @@ FilterContainer.prototype.compileHostnameOnlyFilter = function(parsed, out) {
party = parsed.firstParty ? FirstParty : ThirdParty;
}
var keyShard = parsed.action | parsed.important | party;
- var type = parsed.types >>> 1 || 1; // bit 0 is unused; also, default to AnyType
+
+ var type = parsed.types;
+ if ( type === 0 ) {
+ out.push(
+ 'n\v' +
+ this.makeCategoryKey(keyShard) + '\v' +
+ '.\v' +
+ parsed.f
+ );
+ return true;
+ }
+
var bitOffset = 1;
- while ( type !== 0 ) {
+ do {
if ( type & 1 ) {
out.push(
'n\v' +
@@ -1756,7 +1779,7 @@ FilterContainer.prototype.compileHostnameOnlyFilter = function(parsed, out) {
}
bitOffset += 1;
type >>>= 1;
- }
+ } while ( type !== 0 );
return true;
};
@@ -1845,9 +1868,19 @@ FilterContainer.prototype.compileFilter = function(parsed, out) {
FilterContainer.prototype.compileToAtomicFilter = function(filterClass, parsed, party, out, hostname) {
var bits = parsed.action | parsed.important | party;
- var type = parsed.types >>> 1 || 1; // bit 0 is unused; also, default to AnyType
+ var type = parsed.types;
+ if ( type === 0 ) {
+ out.push(
+ 'n\v' +
+ this.makeCategoryKey(bits) + '\v' +
+ parsed.token + '\v' +
+ filterClass.fid + '\v' +
+ filterClass.compile(parsed, hostname)
+ );
+ return;
+ }
var bitOffset = 1;
- while ( type !== 0 ) {
+ do {
if ( type & 1 ) {
out.push(
'n\v' +
@@ -1859,7 +1892,7 @@ FilterContainer.prototype.compileToAtomicFilter = function(filterClass, parsed,
}
bitOffset += 1;
type >>>= 1;
- }
+ } while ( type !== 0 );
};
/******************************************************************************/
@@ -2008,7 +2041,11 @@ FilterContainer.prototype.matchStringExactType = function(context, requestURL, r
var party = isFirstParty(context.pageDomain, requestHostnameRegister) ? FirstParty : ThirdParty;
// Be prepared to support unknown types
- var type = typeNameToTypeValue[requestType] || typeOtherValue;
+ var type = typeNameToTypeValue[requestType] || 0;
+ if ( type === 0 ) {
+ return '';
+ }
+
var categories = this.categories;
var bf = false, bucket;
@@ -2069,7 +2106,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 > 8 << 4 ) {
+ if ( type > (7 << 4) ) {
return this.matchStringExactType(context, context.requestURL, context.requestType);
}
diff --git a/src/js/traffic.js b/src/js/traffic.js
index 501492fe8..edf2f2ab1 100644
--- a/src/js/traffic.js
+++ b/src/js/traffic.js
@@ -31,6 +31,8 @@
/******************************************************************************/
+var exports = {};
+
// https://github.com/gorhill/uBlock/issues/1001
// This is to be used as last-resort fallback in case a tab is found to not
// be bound while network requests are fired for the tab.
@@ -38,6 +40,9 @@
var mostRecentRootDocURLTimestamp = 0;
var mostRecentRootDocURL = '';
+
+var documentWhitelists = Object.create(null);
+
/******************************************************************************/
// Intercept and filter web requests.
@@ -165,26 +170,75 @@ var onBeforeRequest = function(details) {
// Do not use redirection, we need to block outright to be sure the request
// will not be made. There can be no such guarantee with redirection.
- return { 'cancel': true };
+ return { cancel: true };
};
/******************************************************************************/
var onBeforeRootFrameRequest = function(details) {
+ mostRecentRootDocURL = requestURL;
+ mostRecentRootDocURLTimestamp = Date.now();
+
// Special handling for root document.
// https://github.com/gorhill/uBlock/issues/1001
// This must be executed regardless of whether the request is
// behind-the-scene
+ var µb = µBlock;
var requestURL = details.url;
- var pageStore = µBlock.bindTabToPageStats(details.tabId, requestURL, 'beforeRequest');
- if ( pageStore !== null ) {
- pageStore.requestURL = requestURL;
- pageStore.requestHostname = pageStore.pageHostname;
- pageStore.requestType = 'main_frame';
- pageStore.logRequest(pageStore, '');
+ var requestHostname = details.hostname;
+ var requestDomain = µb.URI.domainFromHostname(requestHostname);
+ var context = {
+ rootHostname: requestHostname,
+ rootDomain: requestDomain,
+ pageHostname: requestHostname,
+ pageDomain: requestDomain,
+ requestURL: requestURL,
+ requestHostname: requestHostname,
+ requestType: 'main_frame'
+ };
+
+ var result = '';
+
+ // Temporarily whitelisted?
+ var obsolete = documentWhitelists[requestHostname];
+ if ( obsolete !== undefined ) {
+ if ( obsolete > Date.now() ) {
+ result = 'da:*' + ' ' + requestHostname + ' doc allow';
+ } else {
+ delete documentWhitelists[requestHostname];
+ }
}
- mostRecentRootDocURL = requestURL;
- mostRecentRootDocURLTimestamp = Date.now();
+
+ // 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);
+ }
+ }
+
+ // Log
+ var pageStore = µb.bindTabToPageStats(details.tabId, requestURL, 'beforeRequest');
+ if ( pageStore ) {
+ pageStore.logRequest(context, result);
+ }
+
+ // Not blocked
+ if ( µb.isAllowResult(result) ) {
+ return;
+ }
+
+ // Blocked
+ var query = btoa(JSON.stringify({
+ url: requestURL,
+ why: result + '$document'
+ }));
+ return { redirectUrl: vAPI.getURL('document-blocked.html?details=') + query };
};
/******************************************************************************/
@@ -348,6 +402,22 @@ vAPI.net.registerListeners();
/******************************************************************************/
+exports.temporarilyWhitelistDocument = function(url) {
+ var µb = µBlock;
+ var hostname = µb.URI.hostnameFromURI(url);
+ if ( hostname === '' ) {
+ return;
+ }
+
+ documentWhitelists[hostname] = Date.now() + 60 * 1000;
+};
+
+/******************************************************************************/
+
+return exports;
+
+/******************************************************************************/
+
})();
/******************************************************************************/