From a4b4bc98ffc267496d5dc47d9c4477de38bcd0c7 Mon Sep 17 00:00:00 2001 From: gorhill Date: Wed, 25 Mar 2015 19:28:22 -0400 Subject: [PATCH] this fixes #1013 --- platform/chromium/manifest.json | 5 +- src/_locales/en/messages.json | 20 +++ src/document-blocked.html | 49 ++++++ src/img/Caution_sign_used_on_roads_pn.svg | 176 ++++++++++++++++++++++ src/js/background.js | 2 +- src/js/document-blocked.js | 73 +++++++++ src/js/messaging.js | 39 ++++- src/js/pagestore.js | 10 +- src/js/static-net-filtering.js | 95 ++++++++---- src/js/traffic.js | 88 +++++++++-- 10 files changed, 512 insertions(+), 45 deletions(-) create mode 100644 src/document-blocked.html create mode 100644 src/img/Caution_sign_used_on_roads_pn.svg create mode 100644 src/js/document-blocked.js 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 @@ + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 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; + +/******************************************************************************/ + })(); /******************************************************************************/