New rules: eventsScrollBound & DOMaccessesOnScroll
This commit is contained in:
@@ -6,18 +6,19 @@ var jsExecutionTransformer = function() {
|
||||
|
||||
this.transform = function(data) {
|
||||
var javascriptExecutionTree = {};
|
||||
var scrollExecutionTree = {};
|
||||
|
||||
var metrics = {
|
||||
domManipulations: 0,
|
||||
DOMaccesses: 0,
|
||||
queriesWithoutResults: 0,
|
||||
jQueryCalls: 0,
|
||||
jQueryCallsOnEmptyObject: 0
|
||||
|
||||
jQueryCallsOnEmptyObject: 0,
|
||||
DOMaccessesOnScroll: 0
|
||||
};
|
||||
|
||||
debug('Starting JS execution transformation');
|
||||
|
||||
try {
|
||||
|
||||
debug('Starting JS execution transformation');
|
||||
javascriptExecutionTree = JSON.parse(data.toolsResults.phantomas.offenders.javascriptExecutionTree[0]);
|
||||
|
||||
if (javascriptExecutionTree.children) {
|
||||
@@ -57,33 +58,46 @@ var jsExecutionTransformer = function() {
|
||||
break;
|
||||
}
|
||||
|
||||
// Change the list of dom paths into a tree
|
||||
treeRecursiveParser(node, function(node) {
|
||||
|
||||
if (node.data.callDetails && node.data.callDetails.context && node.data.callDetails.context.length > 0) {
|
||||
node.data.callDetails.context.elements = node.data.callDetails.context.elements.map(offendersHelpers.domPathToDomElementObj, offendersHelpers);
|
||||
}
|
||||
// Transform domPaths into objects
|
||||
changeListOfDomPaths(node);
|
||||
|
||||
if (node.data.type === 'appendChild' || node.data.type === 'insertBefore' || node.data.type === 'getComputedStyle') {
|
||||
node.data.callDetails.arguments[0] = offendersHelpers.domPathToDomElementObj(node.data.callDetails.arguments[0]);
|
||||
}
|
||||
|
||||
if (node.data.type === 'insertBefore') {
|
||||
node.data.callDetails.arguments[1] = offendersHelpers.domPathToDomElementObj(node.data.callDetails.arguments[1]);
|
||||
}
|
||||
});
|
||||
// Count the number of DOM accesses, by counting the tree leafs
|
||||
metrics.DOMaccesses += countTreeLeafs(node);
|
||||
});
|
||||
}
|
||||
|
||||
debug('JS execution transformation complete');
|
||||
|
||||
|
||||
debug('Starting scroll execution transformation');
|
||||
scrollExecutionTree = JSON.parse(data.toolsResults.phantomas.offenders.scrollExecutionTree[0]);
|
||||
if (scrollExecutionTree.children) {
|
||||
scrollExecutionTree.children.forEach(function(node) {
|
||||
|
||||
// Mark a event flag
|
||||
if (['documentScroll', 'windowScroll', 'window.onscroll'].indexOf(node.data.type) >= 0) {
|
||||
node.windowPerformance = true;
|
||||
}
|
||||
|
||||
// Transform domPaths into objects
|
||||
changeListOfDomPaths(node);
|
||||
|
||||
// Count the number of DOM accesses, by counting the tree leafs
|
||||
metrics.DOMaccessesOnScroll += countTreeLeafs(node);
|
||||
});
|
||||
}
|
||||
debug('Scroll execution transformation complete');
|
||||
|
||||
} catch(err) {
|
||||
throw err;
|
||||
}
|
||||
|
||||
data.javascriptExecutionTree = javascriptExecutionTree;
|
||||
|
||||
data.toolsResults.jsExecutionTransformer = {
|
||||
metrics: metrics
|
||||
metrics: metrics,
|
||||
offenders: {
|
||||
DOMaccessesOnScroll: scrollExecutionTree
|
||||
}
|
||||
};
|
||||
|
||||
return data;
|
||||
@@ -97,6 +111,39 @@ var jsExecutionTransformer = function() {
|
||||
}
|
||||
fn(node);
|
||||
}
|
||||
|
||||
function changeListOfDomPaths(rootNode) {
|
||||
treeRecursiveParser(rootNode, function(node) {
|
||||
|
||||
if (node.data.callDetails && node.data.callDetails.context && node.data.callDetails.context.length > 0) {
|
||||
node.data.callDetails.context.elements = node.data.callDetails.context.elements.map(offendersHelpers.domPathToDomElementObj, offendersHelpers);
|
||||
}
|
||||
|
||||
if (node.data.type === 'appendChild' || node.data.type === 'insertBefore' || node.data.type === 'getComputedStyle') {
|
||||
node.data.callDetails.arguments[0] = offendersHelpers.domPathToDomElementObj(node.data.callDetails.arguments[0]);
|
||||
}
|
||||
|
||||
if (node.data.type === 'insertBefore') {
|
||||
node.data.callDetails.arguments[1] = offendersHelpers.domPathToDomElementObj(node.data.callDetails.arguments[1]);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Returns the number of leafs (nodes without children)
|
||||
function countTreeLeafs(rootNode) {
|
||||
var count = 0;
|
||||
|
||||
treeRecursiveParser(rootNode, function(node) {
|
||||
if (!node.children &&
|
||||
!node.error &&
|
||||
!node.windowPerformance &&
|
||||
node.data.type !== 'jQuery loaded') {
|
||||
count ++;
|
||||
}
|
||||
});
|
||||
|
||||
return count;
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = new jsExecutionTransformer();
|
||||
@@ -104,10 +104,11 @@ exports.module = function(phantomas) {
|
||||
|
||||
var root = new ContextTreeNode(null, {type: 'main'});
|
||||
var currentContext = root;
|
||||
var depth = 0;
|
||||
|
||||
if (deepAnalysis) {
|
||||
phantomas.log('Entering deep Javascript analysis mode');
|
||||
}
|
||||
var depth = 0;
|
||||
|
||||
// Add a child but don't enter his context
|
||||
function pushContext(data) {
|
||||
@@ -176,6 +177,13 @@ exports.module = function(phantomas) {
|
||||
|
||||
return root;
|
||||
}
|
||||
|
||||
// Empty the tree
|
||||
function resetTree() {
|
||||
root = new ContextTreeNode(null, {type: 'main'});
|
||||
currentContext = root;
|
||||
depth = 0;
|
||||
}
|
||||
|
||||
|
||||
function ContextTreeNode(parent, data) {
|
||||
@@ -196,6 +204,7 @@ exports.module = function(phantomas) {
|
||||
phantomas.leaveContext = leaveContext;
|
||||
phantomas.getContextData = getContextData;
|
||||
phantomas.readFullTree = readFullTree;
|
||||
phantomas.resetTree = resetTree;
|
||||
|
||||
})();
|
||||
|
||||
|
||||
@@ -9,11 +9,11 @@ exports.version = '0.1';
|
||||
exports.module = function(phantomas) {
|
||||
'use strict';
|
||||
|
||||
phantomas.setMetric('javascriptExecutionTree'); // @desc number of duplicated DOM queries
|
||||
phantomas.setMetric('javascriptExecutionTree');
|
||||
|
||||
// save data
|
||||
phantomas.on('report', function() {
|
||||
phantomas.log('Reading execution tree JSON');
|
||||
phantomas.log('JS execution tree: Reading execution tree JSON');
|
||||
|
||||
phantomas.evaluate(function() {(function(phantomas) {
|
||||
var fullTree = phantomas.readFullTree();
|
||||
|
||||
@@ -0,0 +1,75 @@
|
||||
exports.version = '0.1';
|
||||
|
||||
exports.module = function(phantomas) {
|
||||
'use strict';
|
||||
|
||||
phantomas.setMetric('scrollExecutionTree');
|
||||
|
||||
phantomas.on('report', function() {
|
||||
|
||||
phantomas.evaluate(function() {
|
||||
(function(phantomas) {
|
||||
|
||||
var evt = document.createEvent('CustomEvent');
|
||||
evt.initCustomEvent('scroll', false, false, null);
|
||||
|
||||
function triggerScrollEvent() {
|
||||
phantomas.resetTree();
|
||||
|
||||
try {
|
||||
|
||||
// Chrome triggers them in this order:
|
||||
|
||||
// 1. document
|
||||
phantomas.pushContext({
|
||||
type: 'documentScroll'
|
||||
});
|
||||
document.dispatchEvent(evt);
|
||||
|
||||
// 2. window
|
||||
phantomas.pushContext({
|
||||
type: 'windowScroll'
|
||||
});
|
||||
window.dispatchEvent(evt);
|
||||
|
||||
// 3. onscroll()
|
||||
if (window.onscroll) {
|
||||
phantomas.pushContext({
|
||||
type: 'window.onscroll'
|
||||
});
|
||||
window.onscroll();
|
||||
}
|
||||
|
||||
} catch(e) {
|
||||
phantomas.log('ScrollListener error: %s', e);
|
||||
}
|
||||
}
|
||||
|
||||
var firstScrollTime = Date.now();
|
||||
phantomas.log('ScrollListener: triggering a first scroll event...');
|
||||
triggerScrollEvent();
|
||||
|
||||
|
||||
// Ignore the first scroll event and only save the second one,
|
||||
// because we want to detect un-throttled things, throttled ones are ok.
|
||||
var secondScrollTime = Date.now();
|
||||
phantomas.log('ScrollListener: triggering a second scroll event (%dms after the first)...', secondScrollTime - firstScrollTime);
|
||||
triggerScrollEvent();
|
||||
|
||||
|
||||
var fullTree = phantomas.readFullTree();
|
||||
if (fullTree !== null) {
|
||||
phantomas.setMetric('scrollExecutionTree', true, true);
|
||||
phantomas.addOffender('scrollExecutionTree', JSON.stringify(fullTree));
|
||||
phantomas.log('ScrollListener: scrollExecutionTree correctly extracted');
|
||||
} else {
|
||||
phantomas.log('Error: scrollExecutionTree could not be extracted');
|
||||
}
|
||||
|
||||
|
||||
phantomas.log('ScrollListener: end of scroll triggering');
|
||||
|
||||
})(window.__phantomas);
|
||||
});
|
||||
});
|
||||
};
|
||||
Reference in New Issue
Block a user