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

new content script code: code review, fine tuning perf

This commit is contained in:
gorhill 2016-06-28 09:06:14 -04:00
parent 6c513629bf
commit 2d68c8ee6c

View File

@ -42,6 +42,8 @@ vAPI.contentscriptInjected = true;
/******************************************************************************/ /******************************************************************************/
/******************************************************************************/ /******************************************************************************/
// The DOM filterer is the heart of uBO's cosmetic filtering.
vAPI.domFilterer = { vAPI.domFilterer = {
allExceptions: Object.create(null), allExceptions: Object.create(null),
allSelectors: Object.create(null), allSelectors: Object.create(null),
@ -49,6 +51,7 @@ vAPI.domFilterer = {
disabledId: String.fromCharCode(Date.now() % 26 + 97) + Math.floor(Math.random() * 982451653 + 982451653).toString(36), disabledId: String.fromCharCode(Date.now() % 26 + 97) + Math.floor(Math.random() * 982451653 + 982451653).toString(36),
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,
matchesProp: 'matches', matchesProp: 'matches',
newSelectors: [], newSelectors: [],
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),
@ -57,15 +60,18 @@ vAPI.domFilterer = {
complexGroupSelector: null, complexGroupSelector: null,
complexSelectors: [], complexSelectors: [],
complexSelectorsMissCount: 0,
simpleGroupSelector: null, simpleGroupSelector: null,
simpleSelectors: [], simpleSelectors: [],
complexHasSelectors: [], complexHasSelectors: [],
complexHasSelectorsMissCount: 0,
simpleHasSelectors: [], simpleHasSelectors: [],
xpathSelectors: [],
xpathExpression: null, xpathExpression: null,
xpathResult: null, xpathResult: null,
xpathSelectors: [],
xpathSelectorsMissCount: 0,
addExceptions: function(aa) { addExceptions: function(aa) {
for ( var i = 0, n = aa.length; i < n; i++ ) { for ( var i = 0, n = aa.length; i < n; i++ ) {
@ -79,6 +85,7 @@ vAPI.domFilterer = {
this.simpleHasSelectors.push(entry); this.simpleHasSelectors.push(entry);
} else { } else {
this.complexHasSelectors.push(entry); this.complexHasSelectors.push(entry);
this.complexHasSelectorsMissCount = 0;
} }
}, },
@ -105,6 +112,7 @@ vAPI.domFilterer = {
} else { } else {
this.complexSelectors.push(s); this.complexSelectors.push(s);
this.complexGroupSelector = null; this.complexGroupSelector = null;
this.complexSelectorsMissCount = 0;
} }
this.newSelectors.push(s); this.newSelectors.push(s);
}, },
@ -157,11 +165,13 @@ vAPI.domFilterer = {
}, },
commit: function(newNodes) { commit: function(newNodes) {
var beforeHiddenNodeCount = this.hiddenNodeCount;
if ( newNodes === undefined ) { if ( newNodes === undefined ) {
newNodes = [ document.documentElement ]; newNodes = [ document.documentElement ];
} }
// Inject new selectors as CSS rules in a style tags. // Inject new selectors as CSS rules in a style tag.
if ( this.newSelectors.length ) { if ( this.newSelectors.length ) {
var styleTag = document.createElement('style'); var styleTag = document.createElement('style');
styleTag.setAttribute('type', 'text/css'); styleTag.setAttribute('type', 'text/css');
@ -198,21 +208,26 @@ vAPI.domFilterer = {
} }
// Complex `:has()` selectors. // Complex `:has()` selectors.
i = this.complexHasSelectors.length; if ( this.complexHasSelectorsMissCount < 5 && this.complexHasSelectors.length ) {
while ( i-- ) { this.complexHasSelectorsMissCount += 1;
entry = this.complexHasSelectors[i]; i = this.complexHasSelectors.length;
nodes = document.querySelectorAll(entry.a + this.cssNotHiddenId); while ( i-- ) {
j = nodes.length; entry = this.complexHasSelectors[i];
while ( j-- ) { nodes = document.querySelectorAll(entry.a + this.cssNotHiddenId);
node = nodes[j]; j = nodes.length;
if ( node.querySelector(entry.b) !== null ) { while ( j-- ) {
this.hideNode(node); node = nodes[j];
if ( node.querySelector(entry.b) !== null ) {
this.hideNode(node);
this.complexHasSelectorsMissCount = 0;
}
} }
} }
} }
// `:xpath()` selectors. // `:xpath()` selectors.
if ( this.xpathSelectors.length ) { if ( this.xpathSelectorsMissCount < 5 && this.xpathSelectors.length ) {
this.xpathSelectorsMissCount += 1;
if ( this.xpathExpression === null ) { if ( this.xpathExpression === null ) {
this.xpathExpression = document.createExpression( this.xpathExpression = document.createExpression(
this.xpathSelectors.join(this.xpathNotHiddenId + '|') + this.xpathNotHiddenId, this.xpathSelectors.join(this.xpathNotHiddenId + '|') + this.xpathNotHiddenId,
@ -229,6 +244,7 @@ vAPI.domFilterer = {
node = this.xpathResult.snapshotItem(i); node = this.xpathResult.snapshotItem(i);
if ( node.nodeType === 1 ) { if ( node.nodeType === 1 ) {
this.hideNode(node); this.hideNode(node);
this.xpathSelectorsMissCount = 0;
} }
} }
} }
@ -256,7 +272,8 @@ vAPI.domFilterer = {
} }
// Complex selectors. // Complex selectors.
if ( this.complexSelectors.length ) { if ( this.complexSelectorsMissCount < 5 && this.complexSelectors.length ) {
this.complexSelectorsMissCount += 1;
if ( this.complexGroupSelector === null ) { if ( this.complexGroupSelector === null ) {
this.complexGroupSelector = this.complexGroupSelector =
this.complexSelectors.join(this.cssNotHiddenId + ',') + this.complexSelectors.join(this.cssNotHiddenId + ',') +
@ -266,16 +283,26 @@ vAPI.domFilterer = {
i = nodes.length; i = nodes.length;
while ( i-- ) { while ( i-- ) {
this.hideNode(nodes[i]); this.hideNode(nodes[i]);
this.complexSelectorsMissCount = 0;
} }
} }
// Reset transient state. // Reset transient state.
this.newSelectors.length = 0; this.newSelectors.length = 0;
// If DOM nodes have been affected, notify core process.
if ( this.hiddenNodeCount !== beforeHiddenNodeCount ) {
vAPI.messaging.send(
'contentscript',
{ what: 'cosmeticFiltersActivated' }
);
}
}, },
hideNode: (function() { hideNode: (function() {
if ( document.documentElement.shadowRoot === undefined ) { if ( document.documentElement.shadowRoot === undefined ) {
return function(node) { return function(node) {
this.hiddenNodeCount += 1;
node.setAttribute(this.hiddenId, ''); node.setAttribute(this.hiddenId, '');
if ( this.enabled ) { if ( this.enabled ) {
node.style.setProperty('display', 'none', 'important'); node.style.setProperty('display', 'none', 'important');
@ -283,6 +310,7 @@ vAPI.domFilterer = {
}; };
} }
return function(node) { return function(node) {
this.hiddenNodeCount += 1;
node.setAttribute(this.hiddenId, ''); node.setAttribute(this.hiddenId, '');
var shadow = node.shadowRoot; var shadow = node.shadowRoot;
// https://www.chromestatus.com/features/4668884095336448 // https://www.chromestatus.com/features/4668884095336448
@ -408,9 +436,6 @@ var injectScripts = function(scripts) {
/******************************************************************************/ /******************************************************************************/
var responseHandler = function(details) { var responseHandler = function(details) {
var domFilterer = vAPI.domFilterer;
var styleTagCount = domFilterer.styleTags.length;
if ( details ) { if ( details ) {
if ( if (
(vAPI.skipCosmeticFiltering = details.skipCosmeticFiltering) !== true && (vAPI.skipCosmeticFiltering = details.skipCosmeticFiltering) !== true &&
@ -428,12 +453,6 @@ var responseHandler = function(details) {
// the browser to flush this script from memory. // the browser to flush this script from memory.
} }
// This is just to inform the background process that cosmetic filters were
// actually injected.
if ( domFilterer.styleTags.length !== styleTagCount ) {
vAPI.messaging.send('contentscript', { what: 'cosmeticFiltersActivated' });
}
// https://github.com/chrisaljoudi/uBlock/issues/587 // https://github.com/chrisaljoudi/uBlock/issues/587
// If no filters were found, maybe the script was injected before uBlock's // If no filters were found, maybe the script was injected before uBlock's
// process was fully initialized. When this happens, pages won't be // process was fully initialized. When this happens, pages won't be
@ -1042,17 +1061,16 @@ if ( !vAPI.contentscriptInjected ) {
// Added node lists will be cumulated here before being processed // Added node lists will be cumulated here before being processed
var addedNodeLists = []; var addedNodeLists = [];
var addedNodeListsTimer = null; var addedNodeListsTimer = null;
var addedNodeListsTimerDelay = 10; var addedNodeListsTimerDelay = 25;
var removedNodeListsTimer = null; var removedNodeListsTimer = null;
var removedNodeListsTimerDelay = 25;
var collapser = domCollapser; var collapser = domCollapser;
// The `cosmeticFiltersActivated` message is required: a new element could
// be matching an already injected but otherwise inactive cosmetic filter.
// This means the already injected cosmetic filter become active (has an
// effect on the document), and thus must be logged if needed.
var addedNodesHandler = function() { var addedNodesHandler = function() {
addedNodeListsTimer = null; addedNodeListsTimer = null;
addedNodeListsTimerDelay = Math.min(addedNodeListsTimerDelay*2, 100); if ( addedNodeListsTimerDelay < 100 ) {
addedNodeListsTimerDelay *= 2;
}
var iNodeList = addedNodeLists.length, var iNodeList = addedNodeLists.length,
nodeList, iNode, node; nodeList, iNode, node;
while ( iNodeList-- ) { while ( iNodeList-- ) {
@ -1074,7 +1092,6 @@ if ( !vAPI.contentscriptInjected ) {
if ( contextNodes.length !== 0 ) { if ( contextNodes.length !== 0 ) {
classesAndIdsFromNodeList(selectNodes('[class],[id]')); classesAndIdsFromNodeList(selectNodes('[class],[id]'));
retrieveGenericSelectors(); retrieveGenericSelectors();
messaging.send('contentscript', { what: 'cosmeticFiltersActivated' });
} }
}; };
@ -1082,6 +1099,11 @@ if ( !vAPI.contentscriptInjected ) {
// This will ensure our style elements will stay in the DOM. // This will ensure our style elements will stay in the DOM.
var removedNodesHandler = function() { var removedNodesHandler = function() {
removedNodeListsTimer = null; removedNodeListsTimer = null;
removedNodeListsTimerDelay *= 2;
// Stop watching style tags after a while.
if ( removedNodeListsTimerDelay > 1000 ) {
removedNodeListsTimerDelay = 0;
}
domFilterer.checkStyleTags(true); domFilterer.checkStyleTags(true);
}; };
@ -1107,8 +1129,8 @@ if ( !vAPI.contentscriptInjected ) {
if ( addedNodeLists.length !== 0 && addedNodeListsTimer === null ) { if ( addedNodeLists.length !== 0 && addedNodeListsTimer === null ) {
addedNodeListsTimer = vAPI.setTimeout(addedNodesHandler, addedNodeListsTimerDelay); addedNodeListsTimer = vAPI.setTimeout(addedNodesHandler, addedNodeListsTimerDelay);
} }
if ( removedNodeLists && removedNodeListsTimer === null ) { if ( removedNodeListsTimerDelay !== 0 && removedNodeLists && removedNodeListsTimer === null ) {
removedNodeListsTimer = vAPI.setTimeout(removedNodesHandler, 100); removedNodeListsTimer = vAPI.setTimeout(removedNodesHandler, removedNodeListsTimerDelay);
} }
}; };