From 8dcc99437112fb792cfa099e00059805e62fb0d7 Mon Sep 17 00:00:00 2001 From: gorhill Date: Sat, 24 Jan 2015 12:06:22 -0500 Subject: [PATCH] addressing all small details --- src/js/background.js | 10 +++++ src/js/pagestore.js | 58 ++++++++++++++++++++++++----- src/js/popup.js | 17 ++++++++- src/js/storage.js | 13 +++++-- src/js/tab.js | 11 ++++-- src/js/traffic.js | 89 +++++++++++++++++++++++++++++++------------- 6 files changed, 155 insertions(+), 43 deletions(-) diff --git a/src/js/background.js b/src/js/background.js index 00d0d12ff..f36078d95 100644 --- a/src/js/background.js +++ b/src/js/background.js @@ -71,6 +71,16 @@ return { netExceptionList: {}, // TODO: remove once all users are up to date netWhitelist: {}, netWhitelistModifyTime: 0, + netWhitelistDefault: [ + 'about-scheme', + 'behind-the-scene', + 'chrome-extension-scheme', + 'chrome-scheme', + 'data-scheme', + 'file-scheme', + 'opera-scheme', + '' + ].join('\n').trim(), localSettings: { blockedRequestCount: 0, diff --git a/src/js/pagestore.js b/src/js/pagestore.js index 0a2b9fb66..e9ffc89dd 100644 --- a/src/js/pagestore.js +++ b/src/js/pagestore.js @@ -616,8 +616,11 @@ PageStore.prototype.toggleNetFilteringSwitch = function(url, scope, state) { /******************************************************************************/ PageStore.prototype.filterRequest = function(context) { + if ( this.getNetFilteringSwitch() === false ) { - this.cacheResult(context, ''); + if ( collapsibleRequestTypes.indexOf(context.requestType) !== -1 ) { + this.netFilteringCache.add(context, ''); + } return ''; } @@ -648,16 +651,53 @@ PageStore.prototype.filterRequest = function(context) { } //console.debug('cache MISS: PageStore.filterRequest("%s")', context.requestURL); - this.cacheResult(context, result); + if ( collapsibleRequestTypes.indexOf(context.requestType) !== -1 ) { + this.netFilteringCache.add(context, result); + } // console.debug('[%s, %s] = "%s"', context.requestHostname, context.requestType, result); return result; }; +// Cache only what is worth it if logging is disabled +// http://jsperf.com/string-indexof-vs-object +var collapsibleRequestTypes = 'image sub_frame object'; + /******************************************************************************/ -PageStore.prototype.cacheResult = function(context, result) { +PageStore.prototype.filterRequestNoCache = function(context) { + + if ( this.getNetFilteringSwitch() === false ) { + return ''; + } + + var result = ''; + + // Given that: + // - Dynamic filtering override static filtering + // - Evaluating dynamic filtering is much faster than static filtering + // 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); + if ( df.mustBlockOrAllow() ) { + result = df.toFilterString(); + } + } + + // Static filtering never override dynamic filtering + if ( result === '' ) { + result = µb.staticNetFilteringEngine.matchString(context); + } + + return result; +}; + +/******************************************************************************/ + +PageStore.prototype.logRequest = function(context, result) { var requestHostname = context.requestHostname; if ( this.hostnameToCountMap.hasOwnProperty(requestHostname) === false ) { this.hostnameToCountMap[requestHostname] = 0; @@ -666,18 +706,16 @@ PageStore.prototype.cacheResult = function(context, result) { var c = result.charAt(1); if ( c === '' || c === 'a' ) { this.hostnameToCountMap[requestHostname] += 0x00010000; + this.perLoadAllowedRequestCount++; + µb.localSettings.allowedRequestCount++; } else /* if ( c === 'b' ) */ { this.hostnameToCountMap[requestHostname] += 0x00000001; + this.perLoadBlockedRequestCount++; + µb.localSettings.blockedRequestCount++; } - if ( collapsibleRequestTypes.indexOf(context.requestType) !== -1 ) { - this.netFilteringCache.add(context, result); - } + this.logBuffer.writeOne(context, result); }; -// Cache only what is worth it if logging is disabled -// http://jsperf.com/string-indexof-vs-object -var collapsibleRequestTypes = 'image sub_frame object'; - /******************************************************************************/ PageStore.prototype.toMirrorURL = function(requestURL) { diff --git a/src/js/popup.js b/src/js/popup.js index 04d9a3019..58003ff16 100644 --- a/src/js/popup.js +++ b/src/js/popup.js @@ -89,6 +89,12 @@ var cachePopupData = function(data) { /******************************************************************************/ var hashFromPopupData = function(reset) { + // It makes no sense to offer to refresh the behind-the-scene scope + if ( popupData.pageHostname === 'behind-the-scene' ) { + uDom('body').toggleClass('dirty', false); + return; + } + var hasher = []; var rules = popupData.dynamicFilterRules; var rule; @@ -299,7 +305,13 @@ var renderPopup = function() { uDom('#appname').text(popupData.appName); uDom('#version').text(popupData.appVersion); uDom('body').toggleClass('advancedUser', popupData.advancedUserEnabled); - uDom('#switch').toggleClass('off', popupData.pageURL === '' || !popupData.netFilteringSwitch); + + uDom('#switch').toggleClass( + 'off', + (popupData.pageURL === '') || + (!popupData.netFilteringSwitch) || + (popupData.pageHostname === 'behind-the-scene' && !popupData.advancedUserEnabled) + ); // If you think the `=== true` is pointless, you are mistaken uDom('#gotoLog').toggleClass('enabled', popupData.canRequestLog === true) @@ -359,6 +371,9 @@ var toggleNetFilteringSwitch = function(ev) { if ( !popupData || !popupData.pageURL ) { return; } + if ( popupData.pageHostname === 'behind-the-scene' && !popupData.advancedUserEnabled ) { + return; + } messager.send({ what: 'toggleNetFiltering', url: popupData.pageURL, diff --git a/src/js/storage.js b/src/js/storage.js index 2a4d77c29..49e8a02ea 100644 --- a/src/js/storage.js +++ b/src/js/storage.js @@ -124,7 +124,7 @@ }; var bin = { - 'netWhitelist': 'behind-the-scene', + 'netWhitelist': this.netWhitelistDefault, 'netExceptionList': '' }; vAPI.storage.get(bin, onWhitelistLoaded); @@ -641,10 +641,17 @@ // To bring older versions up to date var onVersionReady = function(bin) { var lastVersion = bin.version || '0.0.0.0'; - // Whitelist behind-the-scene scope by default + + // Whitelist some key scopes by default if ( lastVersion.localeCompare('0.8.6.0') < 0 ) { - µb.toggleNetFilteringSwitch('http://behind-the-scene/', 'site', false); + µb.netWhitelist = µb.whitelistFromString( + µb.stringFromWhitelist(µb.netWhitelist) + + '\n' + + µb.netWhitelistDefault + ); + µb.saveWhitelist(); } + vAPI.storage.set({ version: vAPI.app.version }); onAllReady(); }; diff --git a/src/js/tab.js b/src/js/tab.js index d729e3770..f0e6cde14 100644 --- a/src/js/tab.js +++ b/src/js/tab.js @@ -88,7 +88,7 @@ vAPI.tabs.onPopup = function(details) { requestHostname: µb.URI.hostnameFromURI(requestURL), requestType: 'popup' }; - pageStore.logBuffer.writeOne(context, result); + pageStore.logRequest(context, result); } // Not blocked @@ -131,11 +131,13 @@ vAPI.tabs.registerListeners(); return uri.normalizedURI(); } + var url = 'http://' + scheme + '-scheme/'; + if ( uri.hostname !== '' ) { - return 'http://' + scheme + '-' + uri.hostname + uri.path; + url += uri.hostname + '/'; } - return 'http://' + scheme + '-scheme/'; + return url; }; /******************************************************************************/ @@ -169,7 +171,8 @@ vAPI.tabs.registerListeners(); return pageStore; } - if ( context === 'afterNavigate' ) { + // Rebind according to context + if ( pageURL !== pageStore.pageURL ) { pageStore.reuse(pageURL, context); } diff --git a/src/js/traffic.js b/src/js/traffic.js index 7e61e3b5b..7d2af1766 100644 --- a/src/js/traffic.js +++ b/src/js/traffic.js @@ -34,10 +34,15 @@ // Intercept and filter web requests. var onBeforeRequest = function(details) { - //console.debug('onBeforeRequest()> "%s": %o', details.url, details); + //console.debug('traffic.js > onBeforeRequest(): "%s": %o', details.url, details); - // Do not block behind the scene requests. var tabId = details.tabId; + + // Special treatment: behind-the-scene requests + if ( vAPI.isNoTabId(tabId) ) { + return onBeforeBehindTheSceneRequest(details); + } + var µb = µBlock; var requestURL = details.url; var requestType = details.type; @@ -51,7 +56,7 @@ var onBeforeRequest = function(details) { pageStore.requestURL = requestURL; pageStore.requestHostname = pageStore.pageHostname; pageStore.requestType = 'main_frame'; - pageStore.logBuffer.writeOne(pageStore, ''); + pageStore.logRequest(pageStore, ''); } return; } @@ -83,10 +88,7 @@ var onBeforeRequest = function(details) { // Not blocked if ( µb.isAllowResult(result) ) { - //console.debug('onBeforeRequest()> ALLOW "%s" (%o) because "%s"', details.url, details, result); - - pageStore.perLoadAllowedRequestCount++; - µb.localSettings.allowedRequestCount++; + //console.debug('traffic.js > onBeforeRequest(): ALLOW "%s" (%o) because "%s"', details.url, details, result); // https://github.com/gorhill/uBlock/issues/114 if ( frameId > 0 && frameStore === undefined ) { @@ -99,23 +101,21 @@ var onBeforeRequest = function(details) { // Disabling local mirroring for the time being //var redirectURL = pageStore.toMirrorURL(requestURL); //if ( redirectURL !== '' ) { - // pageStore.logBuffer.writeOne(requestContext, 'ma:'); - //console.debug('"%s" redirected to "%s..."', requestURL.slice(0, 50), redirectURL.slice(0, 50)); + // pageStore.logRequest(requestContext, 'ma:'); + //console.debug('traffic.js > "%s" redirected to "%s..."', requestURL.slice(0, 50), redirectURL.slice(0, 50)); // return { redirectUrl: redirectURL }; //} - pageStore.logBuffer.writeOne(requestContext, result); + pageStore.logRequest(requestContext, result); return; } // Blocked - //console.debug('onBeforeRequest()> BLOCK "%s" (%o) because "%s"', details.url, details, result); + //console.debug('traffic.js > onBeforeRequest(): BLOCK "%s" (%o) because "%s"', details.url, details, result); - pageStore.logBuffer.writeOne(requestContext, result); + pageStore.logRequest(requestContext, result); - pageStore.perLoadBlockedRequestCount++; - µb.localSettings.blockedRequestCount++; µb.updateBadgeAsync(tabId); // https://github.com/gorhill/uBlock/issues/18 @@ -127,6 +127,46 @@ var onBeforeRequest = function(details) { /******************************************************************************/ +// Intercept and filter behind-the-scene requests. + +var onBeforeBehindTheSceneRequest = function(details) { + //console.debug('traffic.js > onBeforeBehindTheSceneRequest(): "%s": %o', details.url, details); + + var µb = µBlock; + var pageStore = µb.pageStoreFromTabId(vAPI.noTabId); + if ( !pageStore ) { + return; + } + + pageStore.requestURL = details.url; + pageStore.requestHostname = details.hostname; + pageStore.requestType = details.type; + + // Blocking behind-the-scene requests can break a lot of stuff: prevent + // browser updates, prevent extension updates, prevent extensions from + // working properly, etc. + // So we filter if and only if the "advanced user" mode is selected + var result = ''; + if ( µb.userSettings.advancedUserEnabled ) { + result = pageStore.filterRequestNoCache(pageStore); + } + + pageStore.logRequest(pageStore, result); + + // Not blocked + if ( µb.isAllowResult(result) ) { + //console.debug('traffic.js > onBeforeBehindTheSceneRequest(): ALLOW "%s" (%o) because "%s"', details.url, details, result); + return; + } + + // Blocked + //console.debug('traffic.js > onBeforeBehindTheSceneRequest(): BLOCK "%s" (%o) because "%s"', details.url, details, result); + + return { 'cancel': true }; +}; + +/******************************************************************************/ + // Intercept root frame requests. This is where we identify and block popups. var onBeforeSendHeaders = function(details) { @@ -134,11 +174,11 @@ var onBeforeSendHeaders = function(details) { // the tab id could be -1, despite the request not really being a // behind-the-scene request. If true, the test below would prevent // the popup blocker from working. Need to check this. - //console.debug('onBeforeSendHeaders()> "%s" (%o) because "%s"', details.url, details, result); + //console.debug('traffic.js > onBeforeSendHeaders(): "%s" (%o) because "%s"', details.url, details, result); // Do not block behind the scene requests. var tabId = details.tabId; - if ( tabId < 0 ) { + if ( vAPI.isNoTabId(tabId) ) { return; } @@ -193,7 +233,7 @@ var onBeforeSendHeaders = function(details) { }; pageDetails.rootHostname = pageDetails.pageHostname; pageDetails.rootDomain = pageDetails.pageDomain; - //console.debug('Referrer="%s"', referrer); + //console.debug('traffic.js > Referrer="%s"', referrer); var result = µb.staticNetFilteringEngine.matchStringExactType(pageDetails, requestURL, 'popup'); // Not blocked? @@ -215,7 +255,7 @@ var onBeforeSendHeaders = function(details) { var onHeadersReceived = function(details) { // Do not interfere with behind-the-scene requests. var tabId = details.tabId; - if ( tabId < 0 ) { + if ( vAPI.isNoTabId(tabId) ) { return; } @@ -265,17 +305,16 @@ var onHeadersReceived = function(details) { context.requestType = 'inline-script'; var result = pageStore.filterRequest(context); - if ( µb.isBlockResult(result) === false ) { + + pageStore.logRequest(context, result); + + // Don't block + if ( µb.isAllowResult(result) ) { return; } - // Blocked - pageStore.perLoadBlockedRequestCount++; - µb.localSettings.blockedRequestCount++; µb.updateBadgeAsync(tabId); - pageStore.logBuffer.writeOne(context, result); - details.responseHeaders.push({ 'name': 'Content-Security-Policy', 'value': "script-src 'unsafe-eval' *" @@ -368,7 +407,7 @@ vAPI.net.onHeadersReceived = { vAPI.net.registerListeners(); -//console.log('µBlock> Beginning to intercept net requests at %s', (new Date()).toISOString()); +//console.log('traffic.js > Beginning to intercept net requests at %s', (new Date()).toISOString()); /******************************************************************************/