performance work for pseudo-user styles code

This commit is contained in:
gorhill 2017-03-12 10:22:46 -04:00
parent bed2e99e66
commit 7a023077b3
No known key found for this signature in database
GPG Key ID: 25E1490B761470C2
1 changed files with 50 additions and 30 deletions

View File

@ -229,55 +229,75 @@ var platformHideNode = vAPI.hideNode,
}
var uid,
timerId,
timer,
observer,
changedNodes = [];
var observerOptions = {
changedNodes = [],
observerOptions = {
attributes: true,
attributeFilter: [ 'style' ]
};
var overrideInlineStyle = function(node) {
var style = window.getComputedStyle(node),
display = style.getPropertyValue('display'),
attr = node.getAttribute('style') || '';
if ( node[uid] === undefined ) {
node[uid] = node.hasAttribute('style') && attr;
// https://jsperf.com/clientheight-and-clientwidth-vs-getcomputedstyle
// Avoid getComputedStyle(), detecting whether a node is visible can be
// achieved with clientWidth/clientHeight.
// https://gist.github.com/paulirish/5d52fb081b3570c81e3a
// Do not interleave read-from/write-to the DOM. Write-to DOM
// operations would cause the first read-from to be expensive, and
// interleaving means that potentially all single read-from operation
// would be expensive rather than just the 1st one.
// Benchmarking toggling off/on cosmetic filtering confirms quite an
// improvement when:
// - batching as much as possible handling of all nodes;
// - avoiding to interleave read-from/write-to operations.
// However, toggling off/on cosmetic filtering repeatedly is not
// a real use case, but this shows this will help performance
// on sites which try to use inline styles to bypass blockers.
var batchProcess = function() {
timer.clear();
var cNodes = changedNodes, i = cNodes.length,
vNodes = [], j = 0,
node;
while ( i-- ) {
node = cNodes[i];
if ( node[uid] !== undefined && node.clientHeight && node.clientWidth ) {
vNodes[j++] = node;
}
}
if ( display !== '' && display !== 'none' ) {
if ( attr !== '' ) { attr += '; '; }
cNodes.length = 0;
while ( j-- ) {
node = vNodes[j];
var attr = node.getAttribute('style');
if ( !attr ) {
attr = '';
} else {
attr += '; ';
}
node.setAttribute('style', attr + 'display: none !important;');
}
};
var timerHandler = function() {
timerId = undefined;
var nodes = changedNodes,
i = nodes.length, node;
while ( i-- ) {
node = nodes[i];
if ( node[uid] !== undefined ) {
overrideInlineStyle(node);
}
}
nodes.length = 0;
};
var observerHandler = function(mutations) {
var i = mutations.length;
var i = mutations.length,
cNodes = changedNodes,
j = cNodes.length;
while ( i-- ) {
changedNodes.push(mutations[i].target);
}
if ( timerId === undefined ) {
timerId = vAPI.setTimeout(timerHandler, 1);
cNodes[j++] = mutations[i].target;
}
timer.start();
};
platformHideNode = function(node) {
if ( uid === undefined ) {
uid = vAPI.randomToken();
timer = new vAPI.SafeAnimationFrame(batchProcess);
}
overrideInlineStyle(node);
if ( node[uid] === undefined ) {
node[uid] = node.hasAttribute('style') && (node.getAttribute('style') || '');
}
// Performance: batch-process nodes to hide.
var cNodes = changedNodes;
cNodes[cNodes.length] = node;
timer.start();
if ( observer === undefined ) {
observer = new MutationObserver(observerHandler);
}