1
0
mirror of https://github.com/gorhill/uBlock.git synced 2024-07-20 12:17:31 +02:00
This commit is contained in:
AlexVallat 2015-03-02 19:08:05 +00:00
commit e222ace6ef
14 changed files with 278 additions and 194 deletions

View File

@ -2,7 +2,7 @@
"manifest_version": 2, "manifest_version": 2,
"name": "µBlock", "name": "µBlock",
"version": "0.8.9.1", "version": "0.8.9.2",
"default_locale": "en", "default_locale": "en",
"description": "__MSG_extShortDesc__", "description": "__MSG_extShortDesc__",

View File

@ -53,7 +53,6 @@ const contentObserver = {
MAIN_FRAME: Ci.nsIContentPolicy.TYPE_DOCUMENT, MAIN_FRAME: Ci.nsIContentPolicy.TYPE_DOCUMENT,
contentBaseURI: 'chrome://' + hostName + '/content/js/', contentBaseURI: 'chrome://' + hostName + '/content/js/',
cpMessageName: hostName + ':shouldLoad', cpMessageName: hostName + ':shouldLoad',
ignoredPopups: new WeakMap(), ignoredPopups: new WeakMap(),
uniqueSandboxId: 1, uniqueSandboxId: 1,

View File

@ -549,7 +549,7 @@ vAPI.tabs.get = function(tabId, callback) {
} }
} }
// for internal use // For internal use
if ( typeof callback !== 'function' ) { if ( typeof callback !== 'function' ) {
return tab; return tab;
} }
@ -565,12 +565,14 @@ vAPI.tabs.get = function(tabId, callback) {
var browser = getBrowserForTab(tab); var browser = getBrowserForTab(tab);
var tabBrowser = getTabBrowser(win); var tabBrowser = getTabBrowser(win);
var tabIndex = vAPI.fennec var tabIndex, tabTitle;
? tabBrowser.tabs.indexOf(tab) if ( vAPI.fennec ) {
: tabBrowser.browsers.indexOf(browser); tabIndex = tabBrowser.tabs.indexOf(tab);
var tabTitle = vAPI.fennec tabTitle = browser.contentTitle;
? browser.contentTitle } else {
: tab.label; tabIndex = tabBrowser.browsers.indexOf(browser);
tabTitle = tab.label;
}
callback({ callback({
id: tabId, id: tabId,
@ -1022,14 +1024,7 @@ var httpObserver = {
12: 'object', 12: 'object',
14: 'font' 14: 'font'
}, },
lastRequest: { lastRequest: [{}, {}],
url: null,
type: null,
tabId: null,
frameId: null,
parentFrameId: null,
openerURL: null
},
get componentRegistrar() { get componentRegistrar() {
return Components.manager.QueryInterface(Ci.nsIComponentRegistrar); return Components.manager.QueryInterface(Ci.nsIComponentRegistrar);
@ -1134,13 +1129,13 @@ var httpObserver = {
return true; return true;
} }
if ( result.redirectUrl ) { /*if ( result.redirectUrl ) {
channel.redirectionLimit = 1; channel.redirectionLimit = 1;
channel.redirectTo( channel.redirectTo(
Services.io.newURI(result.redirectUrl, null, null) Services.io.newURI(result.redirectUrl, null, null)
); );
return true; return true;
} }*/
return false; return false;
}, },
@ -1159,13 +1154,6 @@ var httpObserver = {
} }
try { try {
/*[
type,
tabId,
sourceTabId - given if it was a popup,
frameId,
parentFrameId
]*/
channelData = channel.getProperty(this.REQDATAKEY); channelData = channel.getProperty(this.REQDATAKEY);
} catch (ex) { } catch (ex) {
return; return;
@ -1175,7 +1163,7 @@ var httpObserver = {
return; return;
} }
if ( (1 << channelData[0] & this.VALID_CSP_TARGETS) === 0 ) { if ( (1 << channelData[4] & this.VALID_CSP_TARGETS) === 0 ) {
return; return;
} }
@ -1189,9 +1177,9 @@ var httpObserver = {
result = vAPI.net.onHeadersReceived.callback({ result = vAPI.net.onHeadersReceived.callback({
hostname: URI.asciiHost, hostname: URI.asciiHost,
parentFrameId: channelData[4], parentFrameId: channelData[1],
responseHeaders: result ? [{name: topic, value: result}] : [], responseHeaders: result ? [{name: topic, value: result}] : [],
tabId: channelData[1], tabId: channelData[3],
url: URI.asciiSpec url: URI.asciiSpec
}); });
@ -1208,13 +1196,36 @@ var httpObserver = {
// http-on-opening-request // 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 ) { if ( lastRequest.url === null ) {
this.handleRequest(channel, URI, { lastRequest.type = channel.loadInfo && channel.loadInfo.contentPolicyType || 1;
result = this.handleRequest(channel, URI, {
tabId: vAPI.noTabId, 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; return;
} }
@ -1222,34 +1233,11 @@ var httpObserver = {
// the URL will be the same, so it could fall into an infinite loop // the URL will be the same, so it could fall into an infinite loop
lastRequest.url = null; lastRequest.url = null;
var sourceTabId = null; if ( this.handleRequest(channel, URI, lastRequest) ) {
return;
// 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 ( lastRequest.type === this.MAIN_FRAME && lastRequest.frameId === 0 ) { if ( vAPI.fennec && lastRequest.type === this.MAIN_FRAME && lastRequest.frameId === 0 ) {
vAPI.tabs.onNavigation({ vAPI.tabs.onNavigation({
frameId: 0, frameId: 0,
tabId: lastRequest.tabId, 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 request is not handled we may use the data in on-modify-request
if ( channel instanceof Ci.nsIWritablePropertyBag ) { if ( channel instanceof Ci.nsIWritablePropertyBag ) {
channel.setProperty( channel.setProperty(this.REQDATAKEY, [
this.REQDATAKEY, lastRequest.frameId,
[ lastRequest.parentFrameId,
lastRequest.type, lastRequest.sourceTabId,
lastRequest.tabId, lastRequest.tabId,
sourceTabId, lastRequest.type
lastRequest.frameId, ]);
lastRequest.parentFrameId
]
);
} }
}, },
@ -1282,12 +1263,6 @@ var httpObserver = {
// If error thrown, the redirect will fail // If error thrown, the redirect will fail
try { try {
// skip internal redirects?
/*if ( flags & 4 ) {
console.log('internal redirect skipped');
return;
}*/
var URI = newChannel.URI; var URI = newChannel.URI;
if ( !URI.schemeIs('http') && !URI.schemeIs('https') ) { if ( !URI.schemeIs('http') && !URI.schemeIs('https') ) {
@ -1298,21 +1273,18 @@ var httpObserver = {
return; 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); 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; result = this.ABORT;
return; return;
} }
var details = { var details = {
type: channelData[0], frameId: channelData[0],
tabId: channelData[1], parentFrameId: channelData[1],
frameId: channelData[3], tabId: channelData[3],
parentFrameId: channelData[4] type: channelData[4]
}; };
if ( this.handleRequest(newChannel, URI, details) ) { if ( this.handleRequest(newChannel, URI, details) ) {
@ -1347,13 +1319,46 @@ vAPI.net.registerListeners = function() {
var shouldLoadListenerMessageName = location.host + ':shouldLoad'; var shouldLoadListenerMessageName = location.host + ':shouldLoad';
var shouldLoadListener = function(e) { var shouldLoadListener = function(e) {
var details = e.data; 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; var lastRequest = httpObserver.lastRequest;
lastRequest.url = details.url; lastRequest[1] = lastRequest[0];
lastRequest.type = details.type; lastRequest[0] = {
lastRequest.tabId = vAPI.tabs.getTabId(e.target); frameId: details.frameId,
lastRequest.frameId = details.frameId; parentFrameId: details.parentFrameId,
lastRequest.parentFrameId = details.parentFrameId; sourceTabId: sourceTabId,
lastRequest.openerURL = details.openerURL; tabId: tabId,
type: details.type,
url: details.url
};
}; };
vAPI.messaging.globalMessageManager.addMessageListener( vAPI.messaging.globalMessageManager.addMessageListener(
@ -1813,7 +1818,7 @@ vAPI.contextMenu.create = function(details, callback) {
if ( gContextMenu.inFrame ) { if ( gContextMenu.inFrame ) {
details.tagName = 'iframe'; details.tagName = 'iframe';
// Probably won't work with e01s // Probably won't work with e10s
details.frameUrl = gContextMenu.focusedWindow.location.href; details.frameUrl = gContextMenu.focusedWindow.location.href;
} else if ( gContextMenu.onImage ) { } else if ( gContextMenu.onImage ) {
details.tagName = 'img'; details.tagName = 'img';

View File

@ -27,6 +27,13 @@
if(vAPI.vapiClientInjected) { if(vAPI.vapiClientInjected) {
return; return;
} }
var safari;
if(typeof self.safari === "undefined") {
safari = self.top.safari;
}
else {
safari = self.safari;
}
vAPI.vapiClientInjected = true; vAPI.vapiClientInjected = true;
vAPI.safari = true; vAPI.safari = true;
vAPI.sessionId = String.fromCharCode(Date.now() % 25 + 97) + vAPI.sessionId = String.fromCharCode(Date.now() % 25 + 97) +
@ -71,9 +78,6 @@
listeners: {}, listeners: {},
requestId: 1, requestId: 1,
setup: function() { setup: function() {
if(typeof safari === "undefined") {
return;
}
this.connector = function(msg) { this.connector = function(msg) {
// messages from the background script are sent to every frame, // messages from the background script are sent to every frame,
// so we need to check the vAPI.sessionId to accept only // so we need to check the vAPI.sessionId to accept only
@ -107,9 +111,6 @@
channelName: channelName, channelName: channelName,
listener: typeof callback === 'function' ? callback : null, listener: typeof callback === 'function' ? callback : null,
send: function(message, callback) { send: function(message, callback) {
if(typeof safari === "undefined") {
return;
}
if(!vAPI.messaging.connector) { if(!vAPI.messaging.connector) {
vAPI.messaging.setup(); vAPI.messaging.setup();
} }
@ -236,7 +237,6 @@ return e.detail.url === false;\
wo = open,\ wo = open,\
xo = XMLHttpRequest.prototype.open,\ xo = XMLHttpRequest.prototype.open,\
img = Image;\ img = Image;\
_noOP = function(){};\
Image = function() {\ Image = function() {\
var x = new img();\ var x = new img();\
Object.defineProperty(x, 'src', {\ Object.defineProperty(x, 'src', {\
@ -252,9 +252,9 @@ return x;\
open = function(u) {\ open = function(u) {\
return block(u, 'popup') ? null : wo.apply(this, arguments);\ return block(u, 'popup') ? null : wo.apply(this, arguments);\
};\ };\
XMLHttpRequest.prototype.open = function(m, u, s) {\ XMLHttpRequest.prototype.open = function(m, u) {\
if(block(u, 'xmlhttprequest')) return {send: _noOP};\ if(block(u, 'xmlhttprequest')) {throw 'InvalidAccessError'; return;}\
else return xo.apply(this, arguments);\ else {xo.apply(this, arguments); return;}\
};"; };";
if(frameId === 0) { if(frameId === 0) {
tmpScript += "\ tmpScript += "\

View File

@ -85,7 +85,7 @@ return {
// read-only // read-only
systemSettings: { systemSettings: {
compiledMagic: 'iolkecdtfsiy', compiledMagic: 'fkaywfqahncj',
selfieMagic: 'spqmeuaftfra' selfieMagic: 'spqmeuaftfra'
}, },

View File

@ -93,25 +93,23 @@ var messager = vAPI.messaging.channel('contentscript-end.js');
return; return;
} }
//var timer = window.performance || Date;
//var tStart = timer.now();
var queriedSelectors = {}; var queriedSelectors = {};
var injectedSelectors = {}; var injectedSelectors = {};
var classSelectors = null; var lowGenericSelectors = [];
var idSelectors = null;
var highGenerics = null; var highGenerics = null;
var contextNodes = [document]; var contextNodes = [document];
var nullArray = { push: function(){} }; var nullArray = { push: function(){} };
var retrieveGenericSelectors = function() { var retrieveGenericSelectors = function() {
var selectors = classSelectors !== null ? Object.keys(classSelectors) : []; if ( lowGenericSelectors.length !== 0 || highGenerics === null ) {
if ( idSelectors !== null ) { //console.log('µBlock> ABP cosmetic filters: retrieving CSS rules using %d selectors', lowGenericSelectors.length);
selectors = selectors.concat(idSelectors);
}
if ( selectors.length > 0 || highGenerics === null ) {
//console.log('µBlock> ABP cosmetic filters: retrieving CSS rules using %d selectors', selectors.length);
messager.send({ messager.send({
what: 'retrieveGenericCosmeticSelectors', what: 'retrieveGenericCosmeticSelectors',
pageURL: window.location.href, pageURL: window.location.href,
selectors: selectors, selectors: lowGenericSelectors,
highGenerics: highGenerics === null highGenerics: highGenerics === null
}, },
retrieveHandler retrieveHandler
@ -122,8 +120,7 @@ var messager = vAPI.messaging.channel('contentscript-end.js');
} else { } else {
otherRetrieveHandler(null); otherRetrieveHandler(null);
} }
idSelectors = null; lowGenericSelectors = [];
classSelectors = null;
}; };
// https://github.com/gorhill/uBlock/issues/452 // https://github.com/gorhill/uBlock/issues/452
@ -139,7 +136,7 @@ var messager = vAPI.messaging.channel('contentscript-end.js');
var selectors = vAPI.hideCosmeticFilters; var selectors = vAPI.hideCosmeticFilters;
if ( typeof selectors === 'object' ) { if ( typeof selectors === 'object' ) {
injectedSelectors = selectors; injectedSelectors = selectors;
hideElements(Object.keys(selectors).join(',')); //hideElements(Object.keys(selectors));
} }
// Add exception filters into injected filters collection, in order // Add exception filters into injected filters collection, in order
// to force them to be seen as "already injected". // 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 otherRetrieveHandler = function(selectors) {
//var tStart = timer.now();
//console.debug('µBlock> contextNodes = %o', contextNodes); //console.debug('µBlock> contextNodes = %o', contextNodes);
if ( selectors && selectors.highGenerics ) { if ( selectors && selectors.highGenerics ) {
highGenerics = selectors.highGenerics; highGenerics = selectors.highGenerics;
@ -196,6 +194,7 @@ var messager = vAPI.messaging.channel('contentscript-end.js');
addStyleTag(hideSelectors); addStyleTag(hideSelectors);
} }
contextNodes.length = 0; contextNodes.length = 0;
//console.debug('%f: uBlock: CSS injection time', timer.now() - tStart);
}; };
var retrieveHandler = firstRetrieveHandler; var retrieveHandler = firstRetrieveHandler;
@ -206,10 +205,10 @@ var messager = vAPI.messaging.channel('contentscript-end.js');
// - Injecting a style tag // - Injecting a style tag
var addStyleTag = function(selectors) { var addStyleTag = function(selectors) {
hideElements(selectors); //hideElements(selectors);
var style = document.createElement('style'); var style = document.createElement('style');
// The linefeed before the style block is very important: do no remove! // 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; var parent = document.body || document.documentElement;
if ( parent ) { if ( parent ) {
parent.appendChild(style); 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. // requests to process high-high generics into as few requests as possible.
// The gain is *significant* on bloated pages. // The gain is *significant* on bloated pages.
var processHighHighGenericsMisses = 8;
var processHighHighGenericsTimer = null; var processHighHighGenericsTimer = null;
var processHighHighGenerics = function() { var processHighHighGenerics = function() {
processHighHighGenericsTimer = null; processHighHighGenericsTimer = null;
if ( injectedSelectors.hasOwnProperty('{{highHighGenerics}}') ) { return; } if ( injectedSelectors.hasOwnProperty('{{highHighGenerics}}') ) {
if ( document.querySelector(highGenerics.hideHigh) === null ) { return; } 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; injectedSelectors['{{highHighGenerics}}'] = true;
// We need to filter out possible exception cosmetic filters from // We need to filter out possible exception cosmetic filters from
// high-high generics selectors. // high-high generics selectors.
@ -388,11 +401,8 @@ var messager = vAPI.messaging.channel('contentscript-end.js');
if ( !nodes || !nodes.length ) { if ( !nodes || !nodes.length ) {
return; return;
} }
if ( idSelectors === null ) {
idSelectors = [];
}
var qq = queriedSelectors; var qq = queriedSelectors;
var ii = idSelectors; var ll = lowGenericSelectors;
var node, v; var node, v;
var i = nodes.length; var i = nodes.length;
while ( i-- ) { while ( i-- ) {
@ -404,8 +414,8 @@ var messager = vAPI.messaging.channel('contentscript-end.js');
v = v.trim(); v = v.trim();
if ( v === '' ) { continue; } if ( v === '' ) { continue; }
v = '#' + v; v = '#' + v;
if ( qq[v] ) { continue; } if ( qq.hasOwnProperty(v) ) { continue; }
ii.push(v); ll.push(v);
qq[v] = true; qq[v] = true;
} }
}; };
@ -417,11 +427,8 @@ var messager = vAPI.messaging.channel('contentscript-end.js');
if ( !nodes || !nodes.length ) { if ( !nodes || !nodes.length ) {
return; return;
} }
if ( classSelectors === null ) {
classSelectors = {};
}
var qq = queriedSelectors; var qq = queriedSelectors;
var cc = classSelectors; var ll = lowGenericSelectors;
var node, v, vv, j; var node, v, vv, j;
var i = nodes.length; var i = nodes.length;
while ( i-- ) { while ( i-- ) {
@ -433,8 +440,8 @@ var messager = vAPI.messaging.channel('contentscript-end.js');
v = vv[j]; v = vv[j];
if ( typeof v !== 'string' ) { continue; } if ( typeof v !== 'string' ) { continue; }
v = '.' + v; v = '.' + v;
if ( qq[v] ) { continue; } if ( qq.hasOwnProperty(v) ) { continue; }
cc[v] = true; ll.push(v);
qq[v] = true; qq[v] = true;
} }
} }
@ -446,6 +453,8 @@ var messager = vAPI.messaging.channel('contentscript-end.js');
classesFromNodeList(document.querySelectorAll('[class]')); classesFromNodeList(document.querySelectorAll('[class]'));
retrieveGenericSelectors(); retrieveGenericSelectors();
//console.debug('%f: uBlock: survey time', timer.now() - tStart);
// Below this point is the code which takes care to observe changes in // 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 // the page and to add if needed relevant CSS rules as a result of the
// changes. // changes.
@ -528,7 +537,6 @@ var messager = vAPI.messaging.channel('contentscript-end.js');
(function() { (function() {
// https://github.com/gorhill/uBlock/issues/683 // https://github.com/gorhill/uBlock/issues/683
// Instead of a closure we use a map to remember the element to collapse // Instead of a closure we use a map to remember the element to collapse
// or hide.
var filterRequestId = 1; var filterRequestId = 1;
var filterRequests = {}; var filterRequests = {};

View File

@ -1199,7 +1199,7 @@ FilterContainer.prototype.retrieveDomainSelectors = function(request) {
/******************************************************************************/ /******************************************************************************/
FilterContainer.prototype.getFilterCount = function() { FilterContainer.prototype.getFilterCount = function() {
return this.acceptedCount; return this.acceptedCount - this.duplicateCount;
}; };
/******************************************************************************/ /******************************************************************************/

View File

@ -59,6 +59,8 @@ var renderURL = function(url, filter) {
.replace(/\?/g, '\\?') .replace(/\?/g, '\\?')
.replace('||', '') .replace('||', '')
.replace(/\^/g, '.') .replace(/\^/g, '.')
.replace(/^\|/g, '^')
.replace(/\|$/g, '$')
.replace(/\*/g, '.*') .replace(/\*/g, '.*')
; ;
} }

View File

@ -575,7 +575,7 @@ PageStore.prototype.disposeFrameStores = function() {
/******************************************************************************/ /******************************************************************************/
PageStore.prototype.getFrame = function(frameId) { PageStore.prototype.getFrame = function(frameId) {
return this.frames[frameId]; return this.frames[frameId] || null;
}; };
/******************************************************************************/ /******************************************************************************/

View File

@ -158,11 +158,11 @@ var onSystemSettingsReady = function(fetched) {
mustSaveSystemSettings = true; mustSaveSystemSettings = true;
} }
if ( fetched.selfieMagic !== µb.systemSettings.selfieMagic ) { if ( fetched.selfieMagic !== µb.systemSettings.selfieMagic ) {
fetched.selfie = null;
µb.destroySelfie();
mustSaveSystemSettings = true; mustSaveSystemSettings = true;
} }
if ( mustSaveSystemSettings ) { if ( mustSaveSystemSettings ) {
fetched.selfie = null;
µb.destroySelfie();
vAPI.storage.set(µb.systemSettings, µb.noopFunc); vAPI.storage.set(µb.systemSettings, µb.noopFunc);
} }
}; };

View File

@ -19,8 +19,8 @@
Home: https://github.com/gorhill/uBlock Home: https://github.com/gorhill/uBlock
*/ */
/* jshint bitwise: false, esnext: true */ /* jshint bitwise: false, esnext: true, boss: true */
/* global µBlock */ /* global punycode, µBlock */
// Older Safari throws an exception for const when it's used with 'use strict'. // Older Safari throws an exception for const when it's used with 'use strict'.
// 'use strict'; // 'use strict';
@ -88,9 +88,7 @@ const AllowAnyTypeAnyParty = AllowAction | AnyType | AnyParty;
const AllowAnyType = AllowAction | AnyType; const AllowAnyType = AllowAction | AnyType;
const AllowAnyParty = AllowAction | AnyParty; const AllowAnyParty = AllowAction | AnyParty;
var reHostnameRule = /^[0-9a-z][0-9a-z.-]+[0-9a-z]$/; 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 reURLPostHostnameAnchors = /[\/?#]/; var reURLPostHostnameAnchors = /[\/?#]/;
// ABP filters: https://adblockplus.org/en/filters // ABP filters: https://adblockplus.org/en/filters
@ -168,6 +166,12 @@ var isFirstParty = function(firstPartyDomain, hostname) {
return c === '.' || c === ''; return c === '.' || c === '';
}; };
var strToRegex = function(prefix, s) {
var reStr = s.replace(/([.+?^=!:${}()|\[\]\/\\])/g, '\\$1')
.replace(/\*/g, '.*');
return new RegExp(prefix + reStr);
};
/******************************************************************************* /*******************************************************************************
Filters family tree: 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. // With many wildcards, a regex is best.
// Ref: regex escaper taken from: // Ref: regex escaper taken from:
@ -881,10 +930,13 @@ FilterSingleWildcardRightAnchoredHostname.fromSelfie = function(s) {
var FilterManyWildcards = function(s, tokenBeg) { var FilterManyWildcards = function(s, tokenBeg) {
this.s = s; this.s = s;
this.tokenBeg = tokenBeg; this.tokenBeg = tokenBeg;
this.re = new RegExp('^' + s.replace(/([.+?^=!:${}()|\[\]\/\\])/g, '\\$1').replace(/\*/g, '.*')); this.re = null;
}; };
FilterManyWildcards.prototype.match = function(url, tokenBeg) { FilterManyWildcards.prototype.match = function(url, tokenBeg) {
if ( this.re === null ) {
this.re = strToRegex('^', this.s);
}
return this.re.test(url.slice(tokenBeg - this.tokenBeg)); return this.re.test(url.slice(tokenBeg - this.tokenBeg));
}; };
@ -912,13 +964,18 @@ FilterManyWildcards.fromSelfie = function(s) {
var FilterManyWildcardsHostname = function(s, tokenBeg, hostname) { var FilterManyWildcardsHostname = function(s, tokenBeg, hostname) {
this.s = s; this.s = s;
this.tokenBeg = tokenBeg; this.tokenBeg = tokenBeg;
this.re = new RegExp('^' + s.replace(/([.+?^=!:${}()|\[\]\/\\])/g, '\\$1').replace(/\*/g, '.*')); this.re = null;
this.hostname = hostname; this.hostname = hostname;
}; };
FilterManyWildcardsHostname.prototype.match = function(url, tokenBeg) { FilterManyWildcardsHostname.prototype.match = function(url, tokenBeg) {
return pageHostnameRegister.slice(-this.hostname.length) === this.hostname && if ( pageHostnameRegister.slice(-this.hostname.length) !== this.hostname ) {
this.re.test(url.slice(tokenBeg - this.tokenBeg)); 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'; FilterManyWildcardsHostname.fid = FilterManyWildcardsHostname.prototype.fid = '*+h';
@ -1316,6 +1373,9 @@ var getFilterClass = function(details) {
var s = details.f; var s = details.f;
var wcOffset = s.indexOf('*'); var wcOffset = s.indexOf('*');
if ( wcOffset !== -1 ) { if ( wcOffset !== -1 ) {
if ( details.hostnameAnchored ) {
return FilterGenericHnAnchored;
}
if ( s.indexOf('*', wcOffset + 1) !== -1 ) { if ( s.indexOf('*', wcOffset + 1) !== -1 ) {
return details.anchor === 0 ? FilterManyWildcards : null; 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" // Trim leading/trailing char "c"
var trimChar = function(s, 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() { FilterParser.prototype.makeToken = function() {
if ( this.isRegex ) { if ( this.isRegex ) {
this.token = '*'; this.token = '*';
@ -1698,7 +1761,8 @@ FilterParser.prototype.makeToken = function() {
var matches; 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); matches = findHostnameToken(this.f);
if ( !matches || matches[0].length === 0 ) { if ( !matches || matches[0].length === 0 ) {
return; return;
@ -1799,7 +1863,8 @@ FilterContainer.prototype.factories = {
'*+h': FilterManyWildcardsHostname, '*+h': FilterManyWildcardsHostname,
'//': FilterRegex, '//': FilterRegex,
'//h': FilterRegexHostname, '//h': FilterRegexHostname,
'{h}': FilterHostnameDict '{h}': FilterHostnameDict,
'||_': FilterGenericHnAnchored
}; };
/******************************************************************************/ /******************************************************************************/

View File

@ -288,7 +288,7 @@
if ( µb.remoteBlacklists.hasOwnProperty(path) ) { if ( µb.remoteBlacklists.hasOwnProperty(path) ) {
var entry = µb.remoteBlacklists[path]; var entry = µb.remoteBlacklists[path];
entry.entryCount = snfe.acceptedCount + cfe.acceptedCount - acceptedCount; 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);
} }
}; };

View File

@ -47,7 +47,7 @@ vAPI.tabs.onNavigation = function(details) {
// The hostname of the bound document must always be present in the // 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 // mini-matrix. That's the best place I could find for the fix, all other
// options had bad side-effects or complications. // 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` // is supported as I suspect we are going to start to see `ws`, `wss`
// as well soon. // as well soon.
if ( pageStore && details.url.lastIndexOf('http', 0) === 0 ) { if ( pageStore && details.url.lastIndexOf('http', 0) === 0 ) {

View File

@ -100,8 +100,13 @@ var onBeforeRequest = function(details) {
//console.debug('traffic.js > onBeforeRequest(): ALLOW "%s" (%o) because "%s"', details.url, details, result); //console.debug('traffic.js > onBeforeRequest(): ALLOW "%s" (%o) because "%s"', details.url, details, result);
// https://github.com/gorhill/uBlock/issues/114 // https://github.com/gorhill/uBlock/issues/114
if ( isFrame && details.frameId > 0 ) { frameId = details.frameId;
pageStore.setFrame(details.frameId, requestURL); 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 // https://code.google.com/p/chromium/issues/detail?id=387198