1
0
mirror of https://github.com/gorhill/uBlock.git synced 2024-11-07 03:12:33 +01:00

new content script code: perf work re. high-high generics

Now splitting high-high generics in two subgroups: one group for
simple selectors, another group for complex selectors. Turns out
the great majority of high-high generics are simple selectors, and
simple selectors can be applied incrementally with DOM changes, as
opposed to complex selectors. This brings in a significant perf.
improvement in the processing of high-high generics (previously,
all high-high generic selectors were processed as one big complex
selector).
This commit is contained in:
gorhill 2016-06-28 22:01:15 -04:00
parent b65699aef2
commit e99d993a4c
4 changed files with 117 additions and 78 deletions

View File

@ -1,7 +1,7 @@
/******************************************************************************* /*******************************************************************************
uBlock - a browser extension to block requests. uBlock Origin - a browser extension to block requests.
Copyright (C) 2014-2015 Raymond Hill Copyright (C) 2014-2016 Raymond Hill
This program is free software: you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by
@ -19,7 +19,6 @@
Home: https://github.com/gorhill/uBlock Home: https://github.com/gorhill/uBlock
*/ */
/* global vAPI */
/* exported µBlock */ /* exported µBlock */
'use strict'; 'use strict';
@ -92,8 +91,8 @@ return {
// read-only // read-only
systemSettings: { systemSettings: {
compiledMagic: 'nytangedtvcz', compiledMagic: 'splsmclwnvoj',
selfieMagic: 'emzolxctioww' selfieMagic: 'splsmclwnvoj'
}, },
restoreBackupSettings: { restoreBackupSettings: {

View File

@ -38,6 +38,18 @@ if ( typeof vAPI !== 'object' || vAPI.contentscriptInjected ) {
vAPI.contentscriptInjected = true; vAPI.contentscriptInjected = true;
vAPI.matchesProp = (function() {
var docElem = document.documentElement;
if ( typeof docElem.matches !== 'function' ) {
if ( typeof docElem.mozMatchesSelector === 'function' ) {
return 'mozMatchesSelector';
} else if ( typeof docElem.webkitMatchesSelector === 'function' ) {
return 'webkitMatchesSelector';
}
}
return 'matches';
})();
/******************************************************************************/ /******************************************************************************/
/******************************************************************************/ /******************************************************************************/
/******************************************************************************/ /******************************************************************************/
@ -53,7 +65,7 @@ vAPI.domFilterer = {
enabled: true, enabled: true,
hiddenId: String.fromCharCode(Date.now() % 26 + 97) + Math.floor(Math.random() * 982451653 + 982451653).toString(36), hiddenId: String.fromCharCode(Date.now() % 26 + 97) + Math.floor(Math.random() * 982451653 + 982451653).toString(36),
hiddenNodeCount: 0, hiddenNodeCount: 0,
matchesProp: 'matches', matchesProp: vAPI.matchesProp,
newDeclarativeSelectors: [], newDeclarativeSelectors: [],
shadowId: String.fromCharCode(Date.now() % 26 + 97) + Math.floor(Math.random() * 982451653 + 982451653).toString(36), shadowId: String.fromCharCode(Date.now() % 26 + 97) + Math.floor(Math.random() * 982451653 + 982451653).toString(36),
styleTags: [], styleTags: [],
@ -410,14 +422,6 @@ vAPI.domFilterer = {
var df = vAPI.domFilterer; var df = vAPI.domFilterer;
df.cssNotHiddenId = ':not([' + df.hiddenId + '])'; df.cssNotHiddenId = ':not([' + df.hiddenId + '])';
df.xpathNotHiddenId = '[not(@' + df.hiddenId + ')]'; df.xpathNotHiddenId = '[not(@' + df.hiddenId + ')]';
var docElem = document.documentElement;
if ( typeof docElem.matches !== 'function' ) {
if ( typeof docElem.mozMatchesSelector === 'function' ) {
df.matchesProp = 'mozMatchesSelector';
} else if ( typeof docElem.webkitMatchesSelector === 'function' ) {
df.matchesProp = 'webkitMatchesSelector';
}
}
// Complex selectors, due to their nature may need to be "de-committed". A // Complex selectors, due to their nature may need to be "de-committed". A
// Set() is used to implement this functionality. For browser with no // Set() is used to implement this functionality. For browser with no
@ -835,12 +839,11 @@ if ( !vAPI.contentscriptInjected ) {
domFilterer.checkStyleTags(false); domFilterer.checkStyleTags(false);
domFilterer.commit(); domFilterer.commit();
var contextNodes = [ document.documentElement ]; var contextNodes = [ document.documentElement ],
var messaging = vAPI.messaging; messaging = vAPI.messaging,
var highGenerics = null; highGenerics = null,
var highHighGenericsInjected = false; lowGenericSelectors = [],
var lowGenericSelectors = []; queriedSelectors = Object.create(null);
var queriedSelectors = Object.create(null);
var responseHandler = function(response) { var responseHandler = function(response) {
// https://github.com/gorhill/uMatrix/issues/144 // https://github.com/gorhill/uMatrix/issues/144
@ -859,6 +862,7 @@ if ( !vAPI.contentscriptInjected ) {
highGenerics = result.highGenerics; highGenerics = result.highGenerics;
} }
} }
if ( highGenerics ) { if ( highGenerics ) {
if ( highGenerics.hideLowCount ) { if ( highGenerics.hideLowCount ) {
processHighLowGenerics(highGenerics.hideLow); processHighLowGenerics(highGenerics.hideLow);
@ -866,10 +870,11 @@ if ( !vAPI.contentscriptInjected ) {
if ( highGenerics.hideMediumCount ) { if ( highGenerics.hideMediumCount ) {
processHighMediumGenerics(highGenerics.hideMedium); processHighMediumGenerics(highGenerics.hideMedium);
} }
if ( highGenerics.hideHighCount ) { if ( highGenerics.hideHighSimpleCount || highGenerics.hideHighComplexCount ) {
processHighHighGenericsAsync(); processHighHighGenerics();
} }
} }
domFilterer.commit(contextNodes); domFilterer.commit(contextNodes);
contextNodes = []; contextNodes = [];
}; };
@ -886,10 +891,10 @@ if ( !vAPI.contentscriptInjected ) {
}, },
responseHandler responseHandler
); );
lowGenericSelectors = [];
} else { } else {
responseHandler(null); responseHandler(null);
} }
lowGenericSelectors = [];
}; };
// Extract and return the staged nodes which (may) match the selectors. // Extract and return the staged nodes which (may) match the selectors.
@ -1009,42 +1014,50 @@ if ( !vAPI.contentscriptInjected ) {
} }
}; };
// High-high generics are very costly to process, so we will coalesce var highHighSimpleGenericsCost = 0,
// requests to process high-high generics into as few requests as possible. highHighSimpleGenericsInjected = false,
// The gain is significant on bloated pages. highHighComplexGenericsCost = 0,
highHighComplexGenericsInjected = false;
var processHighHighGenericsMisses = 8;
var processHighHighGenericsTimer = null;
var processHighHighGenerics = function() { var processHighHighGenerics = function() {
processHighHighGenericsTimer = null; var tstart;
if ( highGenerics.hideHigh === '' ) { // Simple selectors.
return; if (
highHighSimpleGenericsInjected === false &&
highHighSimpleGenericsCost < 50 &&
highGenerics.hideHighSimpleCount !== 0
) {
tstart = window.performance.now();
var matchesProp = vAPI.matchesProp,
nodes = contextNodes,
i = nodes.length, node;
while ( i-- ) {
node = nodes[i];
if (
node[matchesProp](highGenerics.hideHighSimple) ||
node.querySelector(highGenerics.hideHighSimple) !== null
) {
highHighSimpleGenericsInjected = true;
domFilterer.addSelectors(highGenerics.hideHighSimple.split(',\n'));
break;
} }
if ( highHighGenericsInjected ) {
return;
} }
// When there are too many misses for these highly generic CSS rules, highHighSimpleGenericsCost += window.performance.now() - tstart;
// we will just give up on looking whether they need to be injected.
if ( document.querySelector(highGenerics.hideHigh) === null ) {
processHighHighGenericsMisses -= 1;
if ( processHighHighGenericsMisses === 0 ) {
highHighGenericsInjected = true;
} }
return; // Complex selectors.
} if (
highHighGenericsInjected = true; highHighComplexGenericsInjected === false &&
// We need to filter out possible exception cosmetic filters from highHighComplexGenericsCost < 50 &&
// high-high generics selectors. highGenerics.hideHighComplexCount !== 0
domFilterer.addSelectors(highGenerics.hideHigh.split(',\n')); ) {
tstart = window.performance.now();
if ( document.querySelector(highGenerics.hideHighComplex) !== null ) {
highHighComplexGenericsInjected = true;
domFilterer.addSelectors(highGenerics.hideHighComplex.split(',\n'));
domFilterer.commit(); domFilterer.commit();
};
var processHighHighGenericsAsync = function() {
if ( processHighHighGenericsTimer !== null ) {
clearTimeout(processHighHighGenericsTimer);
} }
processHighHighGenericsTimer = vAPI.setTimeout(processHighHighGenerics, 300); highHighComplexGenericsCost += window.performance.now() - tstart;
}
}; };
// Extract all classes/ids: these will be passed to the cosmetic filtering // Extract all classes/ids: these will be passed to the cosmetic filtering
@ -1225,9 +1238,6 @@ if ( !vAPI.contentscriptInjected ) {
if ( removedNodeListsTimer !== null ) { if ( removedNodeListsTimer !== null ) {
clearTimeout(removedNodeListsTimer); clearTimeout(removedNodeListsTimer);
} }
if ( processHighHighGenericsTimer !== null ) {
clearTimeout(processHighHighGenericsTimer);
}
}); });
})(); })();

View File

@ -672,10 +672,15 @@ FilterContainer.prototype.reset = function() {
this.highMediumGenericHide = {}; this.highMediumGenericHide = {};
this.highMediumGenericHideCount = 0; this.highMediumGenericHideCount = 0;
// everything else // high-high-simple selectors
this.highHighGenericHideArray = []; this.highHighSimpleGenericHideArray = [];
this.highHighGenericHide = ''; this.highHighSimpleGenericHide = '';
this.highHighGenericHideCount = 0; this.highHighSimpleGenericHideCount = 0;
// high-high-complex selectors
this.highHighComplexGenericHideArray = [];
this.highHighComplexGenericHide = '';
this.highHighComplexGenericHideCount = 0;
// generic exception filters // generic exception filters
this.genericDonthide = []; this.genericDonthide = [];
@ -888,8 +893,13 @@ FilterContainer.prototype.compileGenericSelector = function(parsed, out) {
return; return;
} }
// All else // All else: high-high generics.
out.push('c\vhhg0\v' + selector); // Distinguish simple vs complex selectors.
if ( selector.indexOf(' ') === -1 ) {
out.push('c\vhhsg0\v' + selector);
} else {
out.push('c\vhhcg0\v' + selector);
}
}; };
FilterContainer.prototype.reClassOrIdSelector = /^[#.][\w-]+$/; FilterContainer.prototype.reClassOrIdSelector = /^[#.][\w-]+$/;
@ -1048,9 +1058,15 @@ FilterContainer.prototype.fromCompiledContent = function(text, lineBeg, skip) {
continue; continue;
} }
if ( fields[0] === 'hhg0' ) { if ( fields[0] === 'hhsg0' ) {
this.highHighGenericHideArray.push(fields[1]); this.highHighSimpleGenericHideArray.push(fields[1]);
this.highHighGenericHideCount += 1; this.highHighSimpleGenericHideCount += 1;
continue;
}
if ( fields[0] === 'hhcg0' ) {
this.highHighComplexGenericHideArray.push(fields[1]);
this.highHighComplexGenericHideCount += 1;
continue; continue;
} }
@ -1197,11 +1213,17 @@ FilterContainer.prototype.retrieveScriptTags = function(domain, hostname) {
FilterContainer.prototype.freeze = function() { FilterContainer.prototype.freeze = function() {
this.duplicateBuster = {}; this.duplicateBuster = {};
if ( this.highHighGenericHide !== '' ) { if ( this.highHighSimpleGenericHide !== '' ) {
this.highHighGenericHideArray.unshift(this.highHighGenericHide); this.highHighSimpleGenericHideArray.unshift(this.highHighSimpleGenericHide);
} }
this.highHighGenericHide = this.highHighGenericHideArray.join(',\n'); this.highHighSimpleGenericHide = this.highHighSimpleGenericHideArray.join(',\n');
this.highHighGenericHideArray = []; this.highHighSimpleGenericHideArray = [];
if ( this.highHighComplexGenericHide !== '' ) {
this.highHighComplexGenericHideArray.unshift(this.highHighComplexGenericHide);
}
this.highHighComplexGenericHide = this.highHighComplexGenericHideArray.join(',\n');
this.highHighComplexGenericHideArray = [];
this.parser.reset(); this.parser.reset();
this.frozen = true; this.frozen = true;
@ -1245,8 +1267,10 @@ FilterContainer.prototype.toSelfie = function() {
highLowGenericHideCount: this.highLowGenericHideCount, highLowGenericHideCount: this.highLowGenericHideCount,
highMediumGenericHide: this.highMediumGenericHide, highMediumGenericHide: this.highMediumGenericHide,
highMediumGenericHideCount: this.highMediumGenericHideCount, highMediumGenericHideCount: this.highMediumGenericHideCount,
highHighGenericHide: this.highHighGenericHide, highHighSimpleGenericHide: this.highHighSimpleGenericHide,
highHighGenericHideCount: this.highHighGenericHideCount, highHighSimpleGenericHideCount: this.highHighSimpleGenericHideCount,
highHighComplexGenericHide: this.highHighComplexGenericHide,
highHighComplexGenericHideCount: this.highHighComplexGenericHideCount,
genericDonthide: this.genericDonthide, genericDonthide: this.genericDonthide,
scriptTagFilters: this.scriptTagFilters, scriptTagFilters: this.scriptTagFilters,
scriptTagFilterCount: this.scriptTagFilterCount, scriptTagFilterCount: this.scriptTagFilterCount,
@ -1309,8 +1333,10 @@ FilterContainer.prototype.fromSelfie = function(selfie) {
this.highLowGenericHideCount = selfie.highLowGenericHideCount; this.highLowGenericHideCount = selfie.highLowGenericHideCount;
this.highMediumGenericHide = selfie.highMediumGenericHide; this.highMediumGenericHide = selfie.highMediumGenericHide;
this.highMediumGenericHideCount = selfie.highMediumGenericHideCount; this.highMediumGenericHideCount = selfie.highMediumGenericHideCount;
this.highHighGenericHide = selfie.highHighGenericHide; this.highHighSimpleGenericHide = selfie.highHighSimpleGenericHide;
this.highHighGenericHideCount = selfie.highHighGenericHideCount; this.highHighSimpleGenericHideCount = selfie.highHighSimpleGenericHideCount;
this.highHighComplexGenericHide = selfie.highHighComplexGenericHide;
this.highHighComplexGenericHideCount = selfie.highHighComplexGenericHideCount;
this.genericDonthide = selfie.genericDonthide; this.genericDonthide = selfie.genericDonthide;
this.scriptTagFilters = selfie.scriptTagFilters; this.scriptTagFilters = selfie.scriptTagFilters;
this.scriptTagFilterCount = selfie.scriptTagFilterCount; this.scriptTagFilterCount = selfie.scriptTagFilterCount;
@ -1441,8 +1467,10 @@ FilterContainer.prototype.retrieveGenericSelectors = function(request) {
hideLowCount: this.highLowGenericHideCount, hideLowCount: this.highLowGenericHideCount,
hideMedium: this.highMediumGenericHide, hideMedium: this.highMediumGenericHide,
hideMediumCount: this.highMediumGenericHideCount, hideMediumCount: this.highMediumGenericHideCount,
hideHigh: this.highHighGenericHide, hideHighSimple: this.highHighSimpleGenericHide,
hideHighCount: this.highHighGenericHideCount hideHighSimpleCount: this.highHighSimpleGenericHideCount,
hideHighComplex: this.highHighComplexGenericHide,
hideHighComplexCount: this.highHighComplexGenericHideCount
}; };
} }

View File

@ -125,8 +125,10 @@ var fromCosmeticFilter = function(details) {
reStr.push('c', 'hlg0', reEscape(filter)); reStr.push('c', 'hlg0', reEscape(filter));
} else if ( reHighMedium.test(filter) ) { // [href^="..."] } else if ( reHighMedium.test(filter) ) { // [href^="..."]
reStr.push('c', 'hmg0', '[^"]{8}', '[a-z]*' + reEscape(filter)); reStr.push('c', 'hmg0', '[^"]{8}', '[a-z]*' + reEscape(filter));
} else { // all else } else if ( filter.indexOf(' ') === -1 ) { // high-high-simple selector
reStr.push('c', 'hhg0', reEscape(filter)); reStr.push('c', 'hhsg0', reEscape(filter));
} else { // high-high-complex selector
reStr.push('c', 'hhcg0', reEscape(filter));
} }
candidates[details.rawFilter] = new RegExp(reStr.join('\\v') + '(?:\\n|$)'); candidates[details.rawFilter] = new RegExp(reStr.join('\\v') + '(?:\\n|$)');