diff --git a/platform/chromium/manifest.json b/platform/chromium/manifest.json index 57ff1ab15..51bf2e590 100644 --- a/platform/chromium/manifest.json +++ b/platform/chromium/manifest.json @@ -2,7 +2,7 @@ "manifest_version": 2, "name": "µBlock", - "version": "0.8.9.1", + "version": "0.8.9.2", "default_locale": "en", "description": "__MSG_extShortDesc__", diff --git a/platform/firefox/frameModule.js b/platform/firefox/frameModule.js index 363f76faf..3a16c70a7 100644 --- a/platform/firefox/frameModule.js +++ b/platform/firefox/frameModule.js @@ -53,7 +53,6 @@ const contentObserver = { MAIN_FRAME: Ci.nsIContentPolicy.TYPE_DOCUMENT, contentBaseURI: 'chrome://' + hostName + '/content/js/', cpMessageName: hostName + ':shouldLoad', - ignoredPopups: new WeakMap(), uniqueSandboxId: 1, diff --git a/platform/firefox/vapi-background.js b/platform/firefox/vapi-background.js index 8c6509d22..730ed3c1c 100644 --- a/platform/firefox/vapi-background.js +++ b/platform/firefox/vapi-background.js @@ -549,7 +549,7 @@ vAPI.tabs.get = function(tabId, callback) { } } - // for internal use + // For internal use if ( typeof callback !== 'function' ) { return tab; } @@ -565,12 +565,14 @@ vAPI.tabs.get = function(tabId, callback) { var browser = getBrowserForTab(tab); var tabBrowser = getTabBrowser(win); - var tabIndex = vAPI.fennec - ? tabBrowser.tabs.indexOf(tab) - : tabBrowser.browsers.indexOf(browser); - var tabTitle = vAPI.fennec - ? browser.contentTitle - : tab.label; + var tabIndex, tabTitle; + if ( vAPI.fennec ) { + tabIndex = tabBrowser.tabs.indexOf(tab); + tabTitle = browser.contentTitle; + } else { + tabIndex = tabBrowser.browsers.indexOf(browser); + tabTitle = tab.label; + } callback({ id: tabId, @@ -1022,14 +1024,7 @@ var httpObserver = { 12: 'object', 14: 'font' }, - lastRequest: { - url: null, - type: null, - tabId: null, - frameId: null, - parentFrameId: null, - openerURL: null - }, + lastRequest: [{}, {}], get componentRegistrar() { return Components.manager.QueryInterface(Ci.nsIComponentRegistrar); @@ -1134,13 +1129,13 @@ var httpObserver = { return true; } - if ( result.redirectUrl ) { + /*if ( result.redirectUrl ) { channel.redirectionLimit = 1; channel.redirectTo( Services.io.newURI(result.redirectUrl, null, null) ); return true; - } + }*/ return false; }, @@ -1159,13 +1154,6 @@ var httpObserver = { } try { - /*[ - type, - tabId, - sourceTabId - given if it was a popup, - frameId, - parentFrameId - ]*/ channelData = channel.getProperty(this.REQDATAKEY); } catch (ex) { return; @@ -1175,7 +1163,7 @@ var httpObserver = { return; } - if ( (1 << channelData[0] & this.VALID_CSP_TARGETS) === 0 ) { + if ( (1 << channelData[4] & this.VALID_CSP_TARGETS) === 0 ) { return; } @@ -1189,9 +1177,9 @@ var httpObserver = { result = vAPI.net.onHeadersReceived.callback({ hostname: URI.asciiHost, - parentFrameId: channelData[4], + parentFrameId: channelData[1], responseHeaders: result ? [{name: topic, value: result}] : [], - tabId: channelData[1], + tabId: channelData[3], url: URI.asciiSpec }); @@ -1208,13 +1196,36 @@ var httpObserver = { // http-on-opening-request - var lastRequest = this.lastRequest; + var lastRequest = this.lastRequest[0]; + + if ( lastRequest.url !== URI.spec ) { + if ( this.lastRequest[1].url === URI.spec ) { + lastRequest = this.lastRequest[1]; + } else { + lastRequest.url = null; + } + } if ( lastRequest.url === null ) { - this.handleRequest(channel, URI, { + lastRequest.type = channel.loadInfo && channel.loadInfo.contentPolicyType || 1; + result = this.handleRequest(channel, URI, { tabId: vAPI.noTabId, - type: channel.loadInfo && channel.loadInfo.contentPolicyType || 1 + type: lastRequest.type }); + + if ( result === true ) { + return; + } + + if ( channel instanceof Ci.nsIWritablePropertyBag === false ) { + return; + } + + // Carry data for behind-the-scene redirects + channel.setProperty( + this.REQDATAKEY, + [lastRequest.type, vAPI.noTabId, null, 0, -1] + ); return; } @@ -1222,34 +1233,11 @@ var httpObserver = { // the URL will be the same, so it could fall into an infinite loop lastRequest.url = null; - var sourceTabId = null; - - // Popup candidate (only for main_frame type) - if ( lastRequest.openerURL ) { - for ( var tab of vAPI.tabs.getAll() ) { - var tabURI = tab.linkedBrowser.currentURI; - - // Probably isn't the best method to identify the source tab - if ( tabURI.spec !== lastRequest.openerURL ) { - continue; - } - - sourceTabId = vAPI.tabs.getTabId(tab); - - if ( sourceTabId !== lastRequest.tabId ) { - break; - } - - sourceTabId = null; - } - - if ( this.handlePopup(channel.URI, lastRequest.tabId, sourceTabId) ) { - channel.cancel(this.ABORT); - return; - } + if ( this.handleRequest(channel, URI, lastRequest) ) { + return; } - - if ( lastRequest.type === this.MAIN_FRAME && lastRequest.frameId === 0 ) { + + if ( vAPI.fennec && lastRequest.type === this.MAIN_FRAME && lastRequest.frameId === 0 ) { vAPI.tabs.onNavigation({ frameId: 0, tabId: lastRequest.tabId, @@ -1257,22 +1245,15 @@ var httpObserver = { }); } - if ( this.handleRequest(channel, URI, lastRequest) ) { - return; - } - // If request is not handled we may use the data in on-modify-request if ( channel instanceof Ci.nsIWritablePropertyBag ) { - channel.setProperty( - this.REQDATAKEY, - [ - lastRequest.type, - lastRequest.tabId, - sourceTabId, - lastRequest.frameId, - lastRequest.parentFrameId - ] - ); + channel.setProperty(this.REQDATAKEY, [ + lastRequest.frameId, + lastRequest.parentFrameId, + lastRequest.sourceTabId, + lastRequest.tabId, + lastRequest.type + ]); } }, @@ -1282,12 +1263,6 @@ var httpObserver = { // If error thrown, the redirect will fail try { - // skip internal redirects? - /*if ( flags & 4 ) { - console.log('internal redirect skipped'); - return; - }*/ - var URI = newChannel.URI; if ( !URI.schemeIs('http') && !URI.schemeIs('https') ) { @@ -1298,21 +1273,18 @@ var httpObserver = { return; } - // TODO: what if a behind-the-scene request is being redirected? - // This data is present only for tabbed requests, so if this throws, - // the redirection won't be evaluated and canceled (if necessary) var channelData = oldChannel.getProperty(this.REQDATAKEY); - if ( this.handlePopup(URI, channelData[1], channelData[2]) ) { + if ( this.handlePopup(URI, channelData[3], channelData[2]) ) { result = this.ABORT; return; } var details = { - type: channelData[0], - tabId: channelData[1], - frameId: channelData[3], - parentFrameId: channelData[4] + frameId: channelData[0], + parentFrameId: channelData[1], + tabId: channelData[3], + type: channelData[4] }; if ( this.handleRequest(newChannel, URI, details) ) { @@ -1347,13 +1319,46 @@ vAPI.net.registerListeners = function() { var shouldLoadListenerMessageName = location.host + ':shouldLoad'; var shouldLoadListener = function(e) { var details = e.data; + var tabId = vAPI.tabs.getTabId(e.target); + var sourceTabId = null; + + // Popup candidate + if ( details.openerURL ) { + for ( var tab of vAPI.tabs.getAll() ) { + var URI = tab.linkedBrowser.currentURI; + + // Probably isn't the best method to identify the source tab + if ( URI.spec !== details.openerURL ) { + continue; + } + + sourceTabId = vAPI.tabs.getTabId(tab); + + if ( sourceTabId === tabId ) { + sourceTabId = null; + continue; + } + + URI = Services.io.newURI(details.url, null, null); + + if ( httpObserver.handlePopup(URI, tabId, sourceTabId) ) { + return; + } + + break; + } + } + var lastRequest = httpObserver.lastRequest; - lastRequest.url = details.url; - lastRequest.type = details.type; - lastRequest.tabId = vAPI.tabs.getTabId(e.target); - lastRequest.frameId = details.frameId; - lastRequest.parentFrameId = details.parentFrameId; - lastRequest.openerURL = details.openerURL; + lastRequest[1] = lastRequest[0]; + lastRequest[0] = { + frameId: details.frameId, + parentFrameId: details.parentFrameId, + sourceTabId: sourceTabId, + tabId: tabId, + type: details.type, + url: details.url + }; }; vAPI.messaging.globalMessageManager.addMessageListener( @@ -1813,7 +1818,7 @@ vAPI.contextMenu.create = function(details, callback) { if ( gContextMenu.inFrame ) { details.tagName = 'iframe'; - // Probably won't work with e01s + // Probably won't work with e10s details.frameUrl = gContextMenu.focusedWindow.location.href; } else if ( gContextMenu.onImage ) { details.tagName = 'img'; diff --git a/platform/safari/vapi-client.js b/platform/safari/vapi-client.js index a6d5d1481..59c08de4b 100644 --- a/platform/safari/vapi-client.js +++ b/platform/safari/vapi-client.js @@ -27,6 +27,13 @@ if(vAPI.vapiClientInjected) { return; } + var safari; + if(typeof self.safari === "undefined") { + safari = self.top.safari; + } + else { + safari = self.safari; + } vAPI.vapiClientInjected = true; vAPI.safari = true; vAPI.sessionId = String.fromCharCode(Date.now() % 25 + 97) + @@ -71,9 +78,6 @@ listeners: {}, requestId: 1, setup: function() { - if(typeof safari === "undefined") { - return; - } this.connector = function(msg) { // messages from the background script are sent to every frame, // so we need to check the vAPI.sessionId to accept only @@ -107,9 +111,6 @@ channelName: channelName, listener: typeof callback === 'function' ? callback : null, send: function(message, callback) { - if(typeof safari === "undefined") { - return; - } if(!vAPI.messaging.connector) { vAPI.messaging.setup(); } @@ -236,7 +237,6 @@ return e.detail.url === false;\ wo = open,\ xo = XMLHttpRequest.prototype.open,\ img = Image;\ -_noOP = function(){};\ Image = function() {\ var x = new img();\ Object.defineProperty(x, 'src', {\ @@ -252,9 +252,9 @@ return x;\ open = function(u) {\ return block(u, 'popup') ? null : wo.apply(this, arguments);\ };\ -XMLHttpRequest.prototype.open = function(m, u, s) {\ -if(block(u, 'xmlhttprequest')) return {send: _noOP};\ -else return xo.apply(this, arguments);\ +XMLHttpRequest.prototype.open = function(m, u) {\ +if(block(u, 'xmlhttprequest')) {throw 'InvalidAccessError'; return;}\ +else {xo.apply(this, arguments); return;}\ };"; if(frameId === 0) { tmpScript += "\ diff --git a/src/js/background.js b/src/js/background.js index aa8033fad..d9bb7c91d 100644 --- a/src/js/background.js +++ b/src/js/background.js @@ -85,7 +85,7 @@ return { // read-only systemSettings: { - compiledMagic: 'iolkecdtfsiy', + compiledMagic: 'fkaywfqahncj', selfieMagic: 'spqmeuaftfra' }, diff --git a/src/js/contentscript-end.js b/src/js/contentscript-end.js index a1ecfbc50..5efbeb797 100644 --- a/src/js/contentscript-end.js +++ b/src/js/contentscript-end.js @@ -93,25 +93,23 @@ var messager = vAPI.messaging.channel('contentscript-end.js'); return; } + //var timer = window.performance || Date; + //var tStart = timer.now(); + var queriedSelectors = {}; var injectedSelectors = {}; - var classSelectors = null; - var idSelectors = null; + var lowGenericSelectors = []; var highGenerics = null; var contextNodes = [document]; var nullArray = { push: function(){} }; var retrieveGenericSelectors = function() { - var selectors = classSelectors !== null ? Object.keys(classSelectors) : []; - if ( idSelectors !== null ) { - selectors = selectors.concat(idSelectors); - } - if ( selectors.length > 0 || highGenerics === null ) { - //console.log('µBlock> ABP cosmetic filters: retrieving CSS rules using %d selectors', selectors.length); + if ( lowGenericSelectors.length !== 0 || highGenerics === null ) { + //console.log('µBlock> ABP cosmetic filters: retrieving CSS rules using %d selectors', lowGenericSelectors.length); messager.send({ what: 'retrieveGenericCosmeticSelectors', pageURL: window.location.href, - selectors: selectors, + selectors: lowGenericSelectors, highGenerics: highGenerics === null }, retrieveHandler @@ -122,8 +120,7 @@ var messager = vAPI.messaging.channel('contentscript-end.js'); } else { otherRetrieveHandler(null); } - idSelectors = null; - classSelectors = null; + lowGenericSelectors = []; }; // https://github.com/gorhill/uBlock/issues/452 @@ -139,7 +136,7 @@ var messager = vAPI.messaging.channel('contentscript-end.js'); var selectors = vAPI.hideCosmeticFilters; if ( typeof selectors === 'object' ) { injectedSelectors = selectors; - hideElements(Object.keys(selectors).join(',')); + //hideElements(Object.keys(selectors)); } // Add exception filters into injected filters collection, in order // to force them to be seen as "already injected". @@ -158,6 +155,7 @@ var messager = vAPI.messaging.channel('contentscript-end.js'); }; var otherRetrieveHandler = function(selectors) { + //var tStart = timer.now(); //console.debug('µBlock> contextNodes = %o', contextNodes); if ( selectors && selectors.highGenerics ) { highGenerics = selectors.highGenerics; @@ -196,6 +194,7 @@ var messager = vAPI.messaging.channel('contentscript-end.js'); addStyleTag(hideSelectors); } contextNodes.length = 0; + //console.debug('%f: uBlock: CSS injection time', timer.now() - tStart); }; var retrieveHandler = firstRetrieveHandler; @@ -206,10 +205,10 @@ var messager = vAPI.messaging.channel('contentscript-end.js'); // - Injecting a style tag var addStyleTag = function(selectors) { - hideElements(selectors); + //hideElements(selectors); var style = document.createElement('style'); // The linefeed before the style block is very important: do no remove! - style.appendChild(document.createTextNode(selectors.join(',\n') + '\n{display:none !important;}')); + style.appendChild(document.createTextNode(selectors.toString() + '\n{display:none !important;}')); var parent = document.body || document.documentElement; if ( parent ) { parent.appendChild(style); @@ -349,12 +348,26 @@ var messager = vAPI.messaging.channel('contentscript-end.js'); // requests to process high-high generics into as few requests as possible. // The gain is *significant* on bloated pages. + var processHighHighGenericsMisses = 8; var processHighHighGenericsTimer = null; var processHighHighGenerics = function() { processHighHighGenericsTimer = null; - if ( injectedSelectors.hasOwnProperty('{{highHighGenerics}}') ) { return; } - if ( document.querySelector(highGenerics.hideHigh) === null ) { return; } + if ( injectedSelectors.hasOwnProperty('{{highHighGenerics}}') ) { + return; + } + //var tStart = timer.now(); + if ( document.querySelector(highGenerics.hideHigh) === null ) { + //console.debug('%f: high-high generic test time', timer.now() - tStart); + processHighHighGenericsMisses -= 1; + // Too many misses for these nagging highly generic CSS rules, + // so we will just skip them from now on. + if ( processHighHighGenericsMisses === 0 ) { + injectedSelectors['{{highHighGenerics}}'] = true; + console.debug('high-high generic: apparently not needed...'); + } + return; + } injectedSelectors['{{highHighGenerics}}'] = true; // We need to filter out possible exception cosmetic filters from // high-high generics selectors. @@ -388,11 +401,8 @@ var messager = vAPI.messaging.channel('contentscript-end.js'); if ( !nodes || !nodes.length ) { return; } - if ( idSelectors === null ) { - idSelectors = []; - } var qq = queriedSelectors; - var ii = idSelectors; + var ll = lowGenericSelectors; var node, v; var i = nodes.length; while ( i-- ) { @@ -404,8 +414,8 @@ var messager = vAPI.messaging.channel('contentscript-end.js'); v = v.trim(); if ( v === '' ) { continue; } v = '#' + v; - if ( qq[v] ) { continue; } - ii.push(v); + if ( qq.hasOwnProperty(v) ) { continue; } + ll.push(v); qq[v] = true; } }; @@ -417,11 +427,8 @@ var messager = vAPI.messaging.channel('contentscript-end.js'); if ( !nodes || !nodes.length ) { return; } - if ( classSelectors === null ) { - classSelectors = {}; - } var qq = queriedSelectors; - var cc = classSelectors; + var ll = lowGenericSelectors; var node, v, vv, j; var i = nodes.length; while ( i-- ) { @@ -433,8 +440,8 @@ var messager = vAPI.messaging.channel('contentscript-end.js'); v = vv[j]; if ( typeof v !== 'string' ) { continue; } v = '.' + v; - if ( qq[v] ) { continue; } - cc[v] = true; + if ( qq.hasOwnProperty(v) ) { continue; } + ll.push(v); qq[v] = true; } } @@ -446,6 +453,8 @@ var messager = vAPI.messaging.channel('contentscript-end.js'); classesFromNodeList(document.querySelectorAll('[class]')); retrieveGenericSelectors(); + //console.debug('%f: uBlock: survey time', timer.now() - tStart); + // Below this point is the code which takes care to observe changes in // the page and to add if needed relevant CSS rules as a result of the // changes. @@ -528,7 +537,6 @@ var messager = vAPI.messaging.channel('contentscript-end.js'); (function() { // https://github.com/gorhill/uBlock/issues/683 // Instead of a closure we use a map to remember the element to collapse - // or hide. var filterRequestId = 1; var filterRequests = {}; diff --git a/src/js/cosmetic-filtering.js b/src/js/cosmetic-filtering.js index 1f54e0b79..e4b66eb6f 100644 --- a/src/js/cosmetic-filtering.js +++ b/src/js/cosmetic-filtering.js @@ -1199,7 +1199,7 @@ FilterContainer.prototype.retrieveDomainSelectors = function(request) { /******************************************************************************/ FilterContainer.prototype.getFilterCount = function() { - return this.acceptedCount; + return this.acceptedCount - this.duplicateCount; }; /******************************************************************************/ diff --git a/src/js/devtool-log.js b/src/js/devtool-log.js index e7075521b..29a8ae0df 100644 --- a/src/js/devtool-log.js +++ b/src/js/devtool-log.js @@ -59,6 +59,8 @@ var renderURL = function(url, filter) { .replace(/\?/g, '\\?') .replace('||', '') .replace(/\^/g, '.') + .replace(/^\|/g, '^') + .replace(/\|$/g, '$') .replace(/\*/g, '.*') ; } diff --git a/src/js/pagestore.js b/src/js/pagestore.js index 09dc1934f..a8cb32dbd 100644 --- a/src/js/pagestore.js +++ b/src/js/pagestore.js @@ -575,7 +575,7 @@ PageStore.prototype.disposeFrameStores = function() { /******************************************************************************/ PageStore.prototype.getFrame = function(frameId) { - return this.frames[frameId]; + return this.frames[frameId] || null; }; /******************************************************************************/ diff --git a/src/js/start.js b/src/js/start.js index de71253bb..bca966bb0 100644 --- a/src/js/start.js +++ b/src/js/start.js @@ -158,11 +158,11 @@ var onSystemSettingsReady = function(fetched) { mustSaveSystemSettings = true; } if ( fetched.selfieMagic !== µb.systemSettings.selfieMagic ) { - fetched.selfie = null; - µb.destroySelfie(); mustSaveSystemSettings = true; } if ( mustSaveSystemSettings ) { + fetched.selfie = null; + µb.destroySelfie(); vAPI.storage.set(µb.systemSettings, µb.noopFunc); } }; diff --git a/src/js/static-net-filtering.js b/src/js/static-net-filtering.js index 792d130d4..8faf9dd28 100644 --- a/src/js/static-net-filtering.js +++ b/src/js/static-net-filtering.js @@ -19,8 +19,8 @@ Home: https://github.com/gorhill/uBlock */ -/* jshint bitwise: false, esnext: true */ -/* global µBlock */ +/* jshint bitwise: false, esnext: true, boss: true */ +/* global punycode, µBlock */ // Older Safari throws an exception for const when it's used with 'use strict'. // 'use strict'; @@ -88,9 +88,7 @@ const AllowAnyTypeAnyParty = AllowAction | AnyType | AnyParty; const AllowAnyType = AllowAction | AnyType; const AllowAnyParty = AllowAction | AnyParty; -var reHostnameRule = /^[0-9a-z][0-9a-z.-]+[0-9a-z]$/; -var reHostnameToken = /^[0-9a-z]+/g; -var reGoodToken = /[%0-9a-z]{2,}/g; +var reHostnameRule = /^[0-9a-z][0-9a-z.-]*[0-9a-z]$/; var reURLPostHostnameAnchors = /[\/?#]/; // ABP filters: https://adblockplus.org/en/filters @@ -168,6 +166,12 @@ var isFirstParty = function(firstPartyDomain, hostname) { return c === '.' || c === ''; }; +var strToRegex = function(prefix, s) { + var reStr = s.replace(/([.+?^=!:${}()|\[\]\/\\])/g, '\\$1') + .replace(/\*/g, '.*'); + return new RegExp(prefix + reStr); +}; + /******************************************************************************* Filters family tree: @@ -872,6 +876,51 @@ FilterSingleWildcardRightAnchoredHostname.fromSelfie = function(s) { /******************************************************************************/ +// Generic filter: hostname-anchored: it has that extra test to find out +// whether the start of the match falls within the hostname part of the +// URL. + +var FilterGenericHnAnchored = function(s) { + this.s = s; + this.re = null; +}; + +FilterGenericHnAnchored.prototype.match = function(url) { + if ( this.re === null ) { + this.re = strToRegex('', this.s); + } + // Quick test first + if ( this.re.test(url) === false ) { + return false; + } + // Valid only if begininning of match is within the hostname + // part of the url + var match = this.re.exec(url); + var pos = url.indexOf('://'); + return pos !== -1 && + reURLPostHostnameAnchors.test(url.slice(pos + 3, match.index)) === false; +}; + +FilterGenericHnAnchored.fid = FilterGenericHnAnchored.prototype.fid = '||_'; + +FilterGenericHnAnchored.prototype.toString = function() { + return '||' + this.s; +}; + +FilterGenericHnAnchored.prototype.toSelfie = function() { + return this.s; +}; + +FilterGenericHnAnchored.compile = function(details) { + return details.f; +}; + +FilterGenericHnAnchored.fromSelfie = function(s) { + return new FilterGenericHnAnchored(s); +}; + +/******************************************************************************/ + // With many wildcards, a regex is best. // Ref: regex escaper taken from: @@ -881,10 +930,13 @@ FilterSingleWildcardRightAnchoredHostname.fromSelfie = function(s) { var FilterManyWildcards = function(s, tokenBeg) { this.s = s; this.tokenBeg = tokenBeg; - this.re = new RegExp('^' + s.replace(/([.+?^=!:${}()|\[\]\/\\])/g, '\\$1').replace(/\*/g, '.*')); + this.re = null; }; FilterManyWildcards.prototype.match = function(url, tokenBeg) { + if ( this.re === null ) { + this.re = strToRegex('^', this.s); + } return this.re.test(url.slice(tokenBeg - this.tokenBeg)); }; @@ -912,13 +964,18 @@ FilterManyWildcards.fromSelfie = function(s) { var FilterManyWildcardsHostname = function(s, tokenBeg, hostname) { this.s = s; this.tokenBeg = tokenBeg; - this.re = new RegExp('^' + s.replace(/([.+?^=!:${}()|\[\]\/\\])/g, '\\$1').replace(/\*/g, '.*')); + this.re = null; this.hostname = hostname; }; FilterManyWildcardsHostname.prototype.match = function(url, tokenBeg) { - return pageHostnameRegister.slice(-this.hostname.length) === this.hostname && - this.re.test(url.slice(tokenBeg - this.tokenBeg)); + if ( pageHostnameRegister.slice(-this.hostname.length) !== this.hostname ) { + return false; + } + if ( this.re === null ) { + this.re = strToRegex('^', this.s); + } + return this.re.test(url.slice(tokenBeg - this.tokenBeg)); }; FilterManyWildcardsHostname.fid = FilterManyWildcardsHostname.prototype.fid = '*+h'; @@ -1316,6 +1373,9 @@ var getFilterClass = function(details) { var s = details.f; var wcOffset = s.indexOf('*'); if ( wcOffset !== -1 ) { + if ( details.hostnameAnchored ) { + return FilterGenericHnAnchored; + } if ( s.indexOf('*', wcOffset + 1) !== -1 ) { return details.anchor === 0 ? FilterManyWildcards : null; } @@ -1388,47 +1448,6 @@ var getHostnameBasedFilterClass = function(details) { /******************************************************************************/ -// Given a string, find a good token. Tokens which are too generic, i.e. very -// common with a high probability of ending up as a miss, are not -// good. Avoid if possible. This has a *significant* positive impact on -// performance. -// These "bad tokens" are collated manually. - -var badTokens = { - 'com': true, - 'http': true, - 'https': true, - 'icon': true, - 'images': true, - 'img': true, - 'js': true, - 'net': true, - 'news': true, - 'www': true -}; - -var findFirstGoodToken = function(s) { - reGoodToken.lastIndex = 0; - var matches; - while ( matches = reGoodToken.exec(s) ) { - if ( badTokens[matches[0]] === undefined ) { - return matches; - } - } - // No good token found, just return the first token from left - reGoodToken.lastIndex = 0; - return reGoodToken.exec(s); -}; - -/******************************************************************************/ - -var findHostnameToken = function(s) { - reHostnameToken.lastIndex = 0; - return reHostnameToken.exec(s); -}; - -/******************************************************************************/ - // Trim leading/trailing char "c" var trimChar = function(s, c) { @@ -1690,6 +1709,50 @@ FilterParser.prototype.parse = function(raw) { /******************************************************************************/ +// Given a string, find a good token. Tokens which are too generic, i.e. very +// common with a high probability of ending up as a miss, are not +// good. Avoid if possible. This has a *significant* positive impact on +// performance. +// These "bad tokens" are collated manually. + +var reHostnameToken = /^[0-9a-z]+/g; +var reGoodToken = /[%0-9a-z]{2,}/g; + +var badTokens = { + 'com': true, + 'http': true, + 'https': true, + 'icon': true, + 'images': true, + 'img': true, + 'js': true, + 'net': true, + 'news': true, + 'www': true +}; + +var findFirstGoodToken = function(s) { + reGoodToken.lastIndex = 0; + var matches; + while ( matches = reGoodToken.exec(s) ) { + if ( badTokens.hasOwnProperty(matches[0]) ) { + continue; + } + if ( s.charAt(reGoodToken.lastIndex) === '*' ) { + continue; + } + return matches; + } + // No good token found, just return the first token from left + reGoodToken.lastIndex = 0; + return reGoodToken.exec(s); +}; + +var findHostnameToken = function(s) { + reHostnameToken.lastIndex = 0; + return reHostnameToken.exec(s); +}; + FilterParser.prototype.makeToken = function() { if ( this.isRegex ) { this.token = '*'; @@ -1698,7 +1761,8 @@ FilterParser.prototype.makeToken = function() { var matches; - if ( this.hostnameAnchored ) { + // Hostname-anchored with no wildcard always have a token index of 0. + if ( this.hostnameAnchored && this.f.indexOf('*') === -1 ) { matches = findHostnameToken(this.f); if ( !matches || matches[0].length === 0 ) { return; @@ -1799,7 +1863,8 @@ FilterContainer.prototype.factories = { '*+h': FilterManyWildcardsHostname, '//': FilterRegex, '//h': FilterRegexHostname, - '{h}': FilterHostnameDict + '{h}': FilterHostnameDict, + '||_': FilterGenericHnAnchored }; /******************************************************************************/ diff --git a/src/js/storage.js b/src/js/storage.js index f3d358966..beb3edeed 100644 --- a/src/js/storage.js +++ b/src/js/storage.js @@ -288,7 +288,7 @@ if ( µb.remoteBlacklists.hasOwnProperty(path) ) { var entry = µb.remoteBlacklists[path]; entry.entryCount = snfe.acceptedCount + cfe.acceptedCount - acceptedCount; - entry.entryUsedCount = entry.entryCount - snfe.duplicateCount - cfe.duplicateCount + duplicateCount; + entry.entryUsedCount = entry.entryCount - (snfe.duplicateCount + cfe.duplicateCount - duplicateCount); } }; diff --git a/src/js/tab.js b/src/js/tab.js index dbdb45b18..1c1945561 100644 --- a/src/js/tab.js +++ b/src/js/tab.js @@ -47,7 +47,7 @@ vAPI.tabs.onNavigation = function(details) { // The hostname of the bound document must always be present in the // mini-matrix. That's the best place I could find for the fix, all other // options had bad side-effects or complications. - // TODO: Evantually, we will have to use an API to check whether a scheme + // TODO: Evantually, we will have to use an API to check whether a scheme // is supported as I suspect we are going to start to see `ws`, `wss` // as well soon. if ( pageStore && details.url.lastIndexOf('http', 0) === 0 ) { diff --git a/src/js/traffic.js b/src/js/traffic.js index ed1eb63d7..a17d8e467 100644 --- a/src/js/traffic.js +++ b/src/js/traffic.js @@ -100,8 +100,13 @@ var onBeforeRequest = function(details) { //console.debug('traffic.js > onBeforeRequest(): ALLOW "%s" (%o) because "%s"', details.url, details, result); // https://github.com/gorhill/uBlock/issues/114 - if ( isFrame && details.frameId > 0 ) { - pageStore.setFrame(details.frameId, requestURL); + frameId = details.frameId; + if ( frameId > 0 ) { + if ( isFrame ) { + pageStore.setFrame(frameId, requestURL); + } else if ( pageStore.getFrame(frameId) === null ) { + pageStore.setFrame(frameId, requestURL); + } } // https://code.google.com/p/chromium/issues/detail?id=387198