mirror of
https://github.com/gorhill/uBlock.git
synced 2024-09-20 21:14:10 +02:00
new content script code: code review, fine tuning perf
This commit is contained in:
parent
2d68c8ee6c
commit
b65699aef2
1 changed files with 189 additions and 116 deletions
|
@ -47,31 +47,34 @@ vAPI.contentscriptInjected = true;
|
|||
vAPI.domFilterer = {
|
||||
allExceptions: Object.create(null),
|
||||
allSelectors: Object.create(null),
|
||||
cosmeticFiltersActivatedTimer: null,
|
||||
cssNotHiddenId: '',
|
||||
disabledId: String.fromCharCode(Date.now() % 26 + 97) + Math.floor(Math.random() * 982451653 + 982451653).toString(36),
|
||||
enabled: true,
|
||||
hiddenId: String.fromCharCode(Date.now() % 26 + 97) + Math.floor(Math.random() * 982451653 + 982451653).toString(36),
|
||||
hiddenNodeCount: 0,
|
||||
matchesProp: 'matches',
|
||||
newSelectors: [],
|
||||
newDeclarativeSelectors: [],
|
||||
shadowId: String.fromCharCode(Date.now() % 26 + 97) + Math.floor(Math.random() * 982451653 + 982451653).toString(36),
|
||||
styleTags: [],
|
||||
xpathNotHiddenId: '',
|
||||
|
||||
complexGroupSelector: null,
|
||||
complexSelectors: [],
|
||||
complexSelectorsMissCount: 0,
|
||||
simpleGroupSelector: null,
|
||||
simpleSelectors: [],
|
||||
|
||||
complexGroupSelector: null,
|
||||
complexSelectors: [],
|
||||
complexSelectorsCost: 0,
|
||||
complexSelectorsNodeSet: null,
|
||||
|
||||
complexHasSelectors: [],
|
||||
complexHasSelectorsMissCount: 0,
|
||||
complexHasSelectorsCost: 0,
|
||||
simpleHasSelectors: [],
|
||||
|
||||
xpathExpression: null,
|
||||
xpathResult: null,
|
||||
xpathSelectors: [],
|
||||
xpathSelectorsMissCount: 0,
|
||||
xpathSelectorsCost: 0,
|
||||
|
||||
addExceptions: function(aa) {
|
||||
for ( var i = 0, n = aa.length; i < n; i++ ) {
|
||||
|
@ -85,7 +88,7 @@ vAPI.domFilterer = {
|
|||
this.simpleHasSelectors.push(entry);
|
||||
} else {
|
||||
this.complexHasSelectors.push(entry);
|
||||
this.complexHasSelectorsMissCount = 0;
|
||||
this.complexHasSelectorsCost = 0;
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -112,9 +115,9 @@ vAPI.domFilterer = {
|
|||
} else {
|
||||
this.complexSelectors.push(s);
|
||||
this.complexGroupSelector = null;
|
||||
this.complexSelectorsMissCount = 0;
|
||||
this.complexSelectorsCost = 0;
|
||||
}
|
||||
this.newSelectors.push(s);
|
||||
this.newDeclarativeSelectors.push(s);
|
||||
},
|
||||
|
||||
addSelectors: function(aa) {
|
||||
|
@ -126,6 +129,7 @@ vAPI.domFilterer = {
|
|||
addXpathSelector: function(s1, s2) {
|
||||
this.xpathSelectors.push(s2.slice(7, -1));
|
||||
this.xpathExpression = null;
|
||||
this.xpathSelectorsCost = 0;
|
||||
},
|
||||
|
||||
checkStyleTags: function(commitIfNeeded) {
|
||||
|
@ -164,35 +168,115 @@ vAPI.domFilterer = {
|
|||
}
|
||||
},
|
||||
|
||||
commit: function(newNodes) {
|
||||
commit: function(stagedNodes) {
|
||||
var beforeHiddenNodeCount = this.hiddenNodeCount;
|
||||
|
||||
if ( newNodes === undefined ) {
|
||||
newNodes = [ document.documentElement ];
|
||||
if ( stagedNodes === undefined ) {
|
||||
stagedNodes = [ document.documentElement ];
|
||||
}
|
||||
|
||||
// Inject new selectors as CSS rules in a style tag.
|
||||
if ( this.newSelectors.length ) {
|
||||
// Inject new declarative selectors.
|
||||
if ( this.newDeclarativeSelectors.length ) {
|
||||
var styleTag = document.createElement('style');
|
||||
styleTag.setAttribute('type', 'text/css');
|
||||
styleTag.textContent =
|
||||
':root ' +
|
||||
this.newSelectors.join(',\n:root ') +
|
||||
this.newDeclarativeSelectors.join(',\n:root ') +
|
||||
'\n{ display: none !important; }';
|
||||
document.head.appendChild(styleTag);
|
||||
this.styleTags.push(styleTag);
|
||||
this.newDeclarativeSelectors.length = 0;
|
||||
}
|
||||
|
||||
var nodes, node, parents, parent, i, j, k, entry;
|
||||
|
||||
// Simple `:has()` selectors.
|
||||
i = this.simpleHasSelectors.length;
|
||||
if ( this.simpleHasSelectors.length ) {
|
||||
this.commitSimpleHasSelectors(stagedNodes);
|
||||
}
|
||||
|
||||
// Complex `:has()` selectors.
|
||||
if ( this.complexHasSelectorsCost < 10 && this.complexHasSelectors.length ) {
|
||||
this.commitComplexHasSelectors();
|
||||
}
|
||||
|
||||
// `:xpath()` selectors.
|
||||
if ( this.xpathSelectorsCost < 10 && this.xpathSelectors.length ) {
|
||||
this.commitXpathSelectors();
|
||||
}
|
||||
|
||||
// Committing declarative selectors is entirely optional, but it helps
|
||||
// harden uBO against sites which try to bypass uBO's injected styles.
|
||||
|
||||
// Simple selectors.
|
||||
if ( this.simpleSelectors.length ) {
|
||||
this.commitSimpleSelectors(stagedNodes);
|
||||
}
|
||||
|
||||
// Complex selectors.
|
||||
if ( this.complexSelectorsCost < 10 && this.complexSelectors.length ) {
|
||||
this.commitComplexSelectors();
|
||||
}
|
||||
|
||||
// If DOM nodes have been affected, lazily notify core process.
|
||||
if (
|
||||
this.hiddenNodeCount !== beforeHiddenNodeCount &&
|
||||
this.cosmeticFiltersActivatedTimer === null
|
||||
) {
|
||||
this.cosmeticFiltersActivatedTimer = vAPI.setTimeout(
|
||||
this.cosmeticFiltersActivated,
|
||||
503
|
||||
);
|
||||
}
|
||||
},
|
||||
|
||||
commitComplexHasSelectors: function() {
|
||||
var tstart = window.performance.now(),
|
||||
entry, nodes, j, node,
|
||||
i = this.complexHasSelectors.length;
|
||||
while ( i-- ) {
|
||||
entry = this.complexHasSelectors[i];
|
||||
nodes = document.querySelectorAll(entry.a + this.cssNotHiddenId);
|
||||
j = nodes.length;
|
||||
while ( j-- ) {
|
||||
node = nodes[j];
|
||||
if ( node.querySelector(entry.b) !== null ) {
|
||||
this.hideNode(node);
|
||||
}
|
||||
}
|
||||
}
|
||||
this.complexHasSelectorsCost = window.performance.now() - tstart;
|
||||
},
|
||||
|
||||
commitComplexSelectors: function() {
|
||||
var tstart = window.performance.now(),
|
||||
newNodeSet = new Set();
|
||||
if ( this.complexGroupSelector === null ) {
|
||||
this.complexGroupSelector = this.complexSelectors.join(',');
|
||||
}
|
||||
var nodes = document.querySelectorAll(this.complexGroupSelector),
|
||||
i = nodes.length, node;
|
||||
while ( i-- ) {
|
||||
node = nodes[i];
|
||||
newNodeSet.add(node);
|
||||
if ( !this.complexSelectorsNodeSet.delete(node) ) {
|
||||
this.hideNode(node);
|
||||
}
|
||||
}
|
||||
var iter = this.complexSelectorsNodeSet.values();
|
||||
while ( (node = iter.next().value) ) {
|
||||
this.unhideNode(node);
|
||||
}
|
||||
this.complexSelectorsNodeSet = newNodeSet;
|
||||
this.complexSelectorsCost = window.performance.now() - tstart;
|
||||
},
|
||||
|
||||
commitSimpleHasSelectors: function(stagedNodes) {
|
||||
var i = this.simpleHasSelectors.length,
|
||||
entry, j, parent, nodes, k, node;
|
||||
while ( i-- ) {
|
||||
entry = this.simpleHasSelectors[i];
|
||||
parents = newNodes;
|
||||
j = parents.length;
|
||||
j = stagedNodes.length;
|
||||
while ( j-- ) {
|
||||
parent = parents[j];
|
||||
parent = stagedNodes[j];
|
||||
if ( parent[this.matchesProp](entry.a) && parent.querySelector(entry.b) !== null ) {
|
||||
this.hideNode(parent);
|
||||
}
|
||||
|
@ -206,28 +290,30 @@ vAPI.domFilterer = {
|
|||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
// Complex `:has()` selectors.
|
||||
if ( this.complexHasSelectorsMissCount < 5 && this.complexHasSelectors.length ) {
|
||||
this.complexHasSelectorsMissCount += 1;
|
||||
i = this.complexHasSelectors.length;
|
||||
commitSimpleSelectors: function(stagedNodes) {
|
||||
if ( this.simpleGroupSelector === null ) {
|
||||
this.simpleGroupSelector =
|
||||
this.simpleSelectors.join(this.cssNotHiddenId + ',') +
|
||||
this.cssNotHiddenId;
|
||||
}
|
||||
var i = stagedNodes.length, stagedNode, nodes, j;
|
||||
while ( i-- ) {
|
||||
entry = this.complexHasSelectors[i];
|
||||
nodes = document.querySelectorAll(entry.a + this.cssNotHiddenId);
|
||||
stagedNode = stagedNodes[i];
|
||||
if ( stagedNode[this.matchesProp](this.simpleGroupSelector) ) {
|
||||
this.hideNode(stagedNode);
|
||||
}
|
||||
nodes = stagedNode.querySelectorAll(this.simpleGroupSelector);
|
||||
j = nodes.length;
|
||||
while ( j-- ) {
|
||||
node = nodes[j];
|
||||
if ( node.querySelector(entry.b) !== null ) {
|
||||
this.hideNode(node);
|
||||
this.complexHasSelectorsMissCount = 0;
|
||||
}
|
||||
}
|
||||
this.hideNode(nodes[j]);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
// `:xpath()` selectors.
|
||||
if ( this.xpathSelectorsMissCount < 5 && this.xpathSelectors.length ) {
|
||||
this.xpathSelectorsMissCount += 1;
|
||||
commitXpathSelectors: function() {
|
||||
var tstart = window.performance.now();
|
||||
if ( this.xpathExpression === null ) {
|
||||
this.xpathExpression = document.createExpression(
|
||||
this.xpathSelectors.join(this.xpathNotHiddenId + '|') + this.xpathNotHiddenId,
|
||||
|
@ -239,64 +325,22 @@ vAPI.domFilterer = {
|
|||
XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE,
|
||||
this.xpathResult
|
||||
);
|
||||
i = this.xpathResult.snapshotLength;
|
||||
var i = this.xpathResult.snapshotLength, node;
|
||||
while ( i-- ) {
|
||||
node = this.xpathResult.snapshotItem(i);
|
||||
if ( node.nodeType === 1 ) {
|
||||
this.hideNode(node);
|
||||
this.xpathSelectorsMissCount = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
this.xpathSelectorsCost = window.performance.now() - tstart;
|
||||
},
|
||||
|
||||
// Simple selectors.
|
||||
if ( this.simpleSelectors.length ) {
|
||||
if ( this.simpleGroupSelector === null ) {
|
||||
this.simpleGroupSelector =
|
||||
this.simpleSelectors.join(this.cssNotHiddenId + ',') +
|
||||
this.cssNotHiddenId;
|
||||
}
|
||||
parents = newNodes;
|
||||
i = parents.length;
|
||||
while ( i-- ) {
|
||||
parent = parents[i];
|
||||
if ( parent[this.matchesProp](this.simpleGroupSelector) ) {
|
||||
this.hideNode(parent);
|
||||
}
|
||||
nodes = parent.querySelectorAll(this.simpleGroupSelector);
|
||||
j = nodes.length;
|
||||
while ( j-- ) {
|
||||
this.hideNode(nodes[j]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Complex selectors.
|
||||
if ( this.complexSelectorsMissCount < 5 && this.complexSelectors.length ) {
|
||||
this.complexSelectorsMissCount += 1;
|
||||
if ( this.complexGroupSelector === null ) {
|
||||
this.complexGroupSelector =
|
||||
this.complexSelectors.join(this.cssNotHiddenId + ',') +
|
||||
this.cssNotHiddenId;
|
||||
}
|
||||
nodes = document.querySelectorAll(this.complexGroupSelector);
|
||||
i = nodes.length;
|
||||
while ( i-- ) {
|
||||
this.hideNode(nodes[i]);
|
||||
this.complexSelectorsMissCount = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// Reset transient state.
|
||||
this.newSelectors.length = 0;
|
||||
|
||||
// If DOM nodes have been affected, notify core process.
|
||||
if ( this.hiddenNodeCount !== beforeHiddenNodeCount ) {
|
||||
cosmeticFiltersActivated: function() {
|
||||
this.cosmeticFiltersActivatedTimer = null;
|
||||
vAPI.messaging.send(
|
||||
'contentscript',
|
||||
{ what: 'cosmeticFiltersActivated' }
|
||||
);
|
||||
}
|
||||
},
|
||||
|
||||
hideNode: (function() {
|
||||
|
@ -343,6 +387,20 @@ vAPI.domFilterer = {
|
|||
|
||||
toggleOn: function() {
|
||||
this.enabled = true;
|
||||
},
|
||||
|
||||
unhideNode: function(node) {
|
||||
this.hiddenNodeCount--;
|
||||
node.removeAttribute(this.hiddenId);
|
||||
var shadow = node.shadowRoot;
|
||||
if ( shadow && shadow.className === this.shadowId ) {
|
||||
if ( shadow.firstElementChild !== null ) {
|
||||
shadow.removeChild(shadow.firstElementChild);
|
||||
}
|
||||
shadow.appendChild(document.createElement('content'));
|
||||
} else {
|
||||
node.style.removeProperty('display');
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -360,6 +418,21 @@ vAPI.domFilterer = {
|
|||
df.matchesProp = 'webkitMatchesSelector';
|
||||
}
|
||||
}
|
||||
|
||||
// Complex selectors, due to their nature may need to be "de-committed". A
|
||||
// Set() is used to implement this functionality. For browser with no
|
||||
// support of Set(), uBO will skip committing complex selectors.
|
||||
if ( typeof window.Set === 'function' ) {
|
||||
df.complexSelectorsNodeSet = new Set();
|
||||
} else {
|
||||
df.complexSelectorsCost = Number.MAX_VALUE;
|
||||
}
|
||||
|
||||
// Theoretically, `:has`- and `:xpath`-based selectors may also need to
|
||||
// be de-committed. But for performance purpose, this is not implemented,
|
||||
// and anyways, the point of these selectors is to be very accurate, so
|
||||
// I do not expect de-committing scenarios to occur with proper use of
|
||||
// these selectors.
|
||||
})();
|
||||
|
||||
/******************************************************************************/
|
||||
|
@ -696,9 +769,15 @@ var domCollapser = (function() {
|
|||
var iframesFromNode = function(node) {
|
||||
if ( node.localName === 'iframe' ) {
|
||||
addIFrame(node);
|
||||
}
|
||||
addIFrames(node.getElementsByTagName('iframe'));
|
||||
process();
|
||||
}
|
||||
if ( node.children.length !== 0 ) {
|
||||
var iframes = node.getElementsByTagName('iframe');
|
||||
if ( iframes.length !== 0 ) {
|
||||
addIFrames(iframes);
|
||||
process();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
return {
|
||||
|
@ -736,7 +815,7 @@ if ( !vAPI.contentscriptInjected ) {
|
|||
|
||||
/******************************************************************************/
|
||||
|
||||
// Cosmetic filters
|
||||
// Cosmetic filtering.
|
||||
|
||||
(function() {
|
||||
if ( vAPI.skipCosmeticFiltering ) {
|
||||
|
@ -770,7 +849,6 @@ if ( !vAPI.contentscriptInjected ) {
|
|||
return;
|
||||
}
|
||||
|
||||
//var tStart = timer.now();
|
||||
var result = response && response.result;
|
||||
|
||||
if ( result ) {
|
||||
|
@ -794,7 +872,6 @@ if ( !vAPI.contentscriptInjected ) {
|
|||
}
|
||||
domFilterer.commit(contextNodes);
|
||||
contextNodes = [];
|
||||
//console.debug('%f: uBlock: CSS injection time', timer.now() - tStart);
|
||||
};
|
||||
|
||||
var retrieveGenericSelectors = function() {
|
||||
|
@ -815,10 +892,6 @@ if ( !vAPI.contentscriptInjected ) {
|
|||
lowGenericSelectors = [];
|
||||
};
|
||||
|
||||
// Ensure elements matching a set of selectors are visually removed
|
||||
// from the page, by:
|
||||
// - Modifying the style property on the elements themselves
|
||||
// - Injecting a style tag
|
||||
// Extract and return the staged nodes which (may) match the selectors.
|
||||
|
||||
var selectNodes = function(selector) {
|
||||
|
@ -1069,7 +1142,7 @@ if ( !vAPI.contentscriptInjected ) {
|
|||
var addedNodesHandler = function() {
|
||||
addedNodeListsTimer = null;
|
||||
if ( addedNodeListsTimerDelay < 100 ) {
|
||||
addedNodeListsTimerDelay *= 2;
|
||||
addedNodeListsTimerDelay += 25;
|
||||
}
|
||||
var iNodeList = addedNodeLists.length,
|
||||
nodeList, iNode, node;
|
||||
|
|
Loading…
Reference in a new issue