sitespeed.io/lib/headless/scripts/collectTimings.js

276 lines
8.0 KiB
JavaScript

/**
* Sitespeed.io - How speedy is your site? (https://www.sitespeed.io)
* Copyright (c) 2014, Peter Hedenskog, Tobias Lidskog
* and other contributors
* Released under the Apache 2.0 License
*/
if (!Date.prototype.toISOString) {
Date.prototype.toISOString = function() {
function pad(n) {
return n < 10 ? '0' + n : n;
}
function ms(n) {
return n < 10 ? '00' + n : n < 100 ? '0' + n : n
}
return this.getFullYear() + '-' +
pad(this.getMonth() + 1) + '-' +
pad(this.getDate()) + 'T' +
pad(this.getHours()) + ':' +
pad(this.getMinutes()) + ':' +
pad(this.getSeconds()) + '.' +
ms(this.getMilliseconds()) + 'Z';
}
}
function createHAR(address, title, startTime, onContentLoad, onLoad, resources) {
var entries = [];
resources.forEach(function(resource) {
var request = resource.request,
startReply = resource.startReply,
endReply = resource.endReply;
if (!request || !startReply || !endReply) {
return;
}
// Exclude Data URI from HAR file because
// they aren't included in specification
if (request.url.match(/(^data:image\/.*)/i)) {
return;
}
entries.push({
startedDateTime: request.time.toISOString(),
time: endReply.time - request.time,
request: {
method: request.method,
url: request.url,
httpVersion: "HTTP/1.1",
cookies: [],
headers: request.headers,
queryString: [],
headersSize: -1,
bodySize: -1
},
response: {
status: endReply.status,
statusText: endReply.statusText,
httpVersion: "HTTP/1.1",
cookies: [],
headers: endReply.headers,
redirectURL: "",
headersSize: -1,
bodySize: startReply.bodySize,
content: {
size: startReply.bodySize,
mimeType: endReply.contentType
}
},
cache: {},
timings: {
blocked: 0,
dns: -1,
connect: -1,
send: 0,
wait: startReply.time - request.time,
receive: endReply.time - startReply.time,
ssl: -1
},
pageref: address
});
});
return {
log: {
version: '1.2',
creator: {
name: "PhantomJS",
version: phantom.version.major + '.' + phantom.version.minor +
'.' + phantom.version.patch
},
pages: [{
startedDateTime: startTime.toISOString(),
id: address,
title: title,
pageTimings: {
onContentLoad: onContentLoad,
onLoad: onLoad
}
}],
entries: entries
}
};
}
/** Classic waitFor example from PhantomJS
*/
function waitFor(testFx, onReady, timeOutMillis) {
var maxtimeOutMillis = timeOutMillis ? timeOutMillis : 60000, //< Default Max Timout is 60s
start = new Date().getTime(),
condition = false,
interval = setInterval(function() {
if ((new Date().getTime() - start < maxtimeOutMillis) && !condition) {
// If not time-out yet and condition not yet fulfilled
condition = (typeof(testFx) === "string" ? eval(testFx) : testFx()); //< defensive code
} else {
if (!condition) {
// If condition still not fulfilled (timeout but condition is 'false')
console.log("'waitFor()' timeout");
phantom.exit(1);
} else {
// Condition fulfilled (timeout and/or condition is 'true')
typeof(onReady) === "string" ? eval(onReady) : onReady(); //< Do what it's supposed to do once the condition is fulfilled
clearInterval(interval); //< Stop this interval
}
}
}, 250); //< repeat check every 250ms
}
var page = require('webpage').create(),
system = require('system'),
address, output, harfilename, w, h, agent, basicauth, auth, headers, waitScript, fs = require('fs');
if (system.args.length < 7 || system.args.length > 10) {
console.log('Usage: phantom.js URL filename harfilename width height user-agent headers basic:auth');
phantom.exit();
} else {
address = system.args[1];
output = system.args[2];
harfilename = system.args[3];
w = system.args[4];
h = system.args[5];
agent = system.args[6];
waitScript = new Function(system.args[7]),
headers = system.args[8];
basicauth = system.args[9];
if (basicauth) {
auth = basicauth.split(':');
page.settings.userName = auth[0];
page.settings.password = auth[1];
}
if (headers) {
page.customHeaders = JSON.parse(headers);
}
page.viewportSize = {
width: w,
height: h
};
if (agent) {
page.settings.userAgent = agent;
}
page.resources = [];
page.onLoadStarted = function() {
page.startTime = new Date();
};
page.onResourceRequested = function(req) {
page.resources[req.id] = {
request: req,
startReply: null,
endReply: null
};
};
page.onResourceReceived = function(res) {
if (res.stage === 'start') {
page.resources[res.id].startReply = res;
}
if (res.stage === 'end') {
page.resources[res.id].endReply = res;
}
};
page.open(address, function(status) {
if (status !== 'success') {
console.log('Unable to load the address!');
} else {
page.endTime = new Date();
page.title = page.evaluate(function() {
return document.title;
});
waitFor(function() {
// Check in the page if a specific element is now visible
return page.evaluate(eval(waitScript));
}, function() {
var timings = page.evaluate(function() {
var t = window.performance.timing;
var marks = '';
try {
marks = window.performance.getEntriesByType('mark');
} catch (Error) {
}
return {
navigation: {
navigationStart: t.navigationStart,
unloadEventStart: t.unloadEventStart,
unloadEventEnd: t.unloadEventEnd,
redirectStart: t.redirectStart,
redirectEnd: t.redirectEnd,
fetchStart: t.fetchStart,
domainLookupStart: t.domainLookupStart,
domainLookupEnd: t.domainLookupEnd,
connectStart: t.connectStart,
connectEnd: t.connectEnd,
secureConnectionStart: t.secureConnectionStart,
requestStart: t.requestStart,
responseStart: t.responseStart,
responseEnd: t.responseEnd,
domLoading: t.domLoading,
domInteractive: t.domInteractive,
domContentLoadedEventStart: t.domContentLoadedEventStart,
domContentLoadedEventEnd: t.domContentLoadedEventEnd,
domComplete: t.domComplete,
loadEventStart: t.loadEventStart,
loadEventEnd: t.loadEventEnd
},
timings: {
domainLookupTime: (t.domainLookupEnd - t.domainLookupStart),
redirectionTime: (t.fetchStart - t.navigationStart),
serverConnectionTime: (t.connectEnd - t.requestStart),
serverResponseTime: (t.responseEnd - t.requestStart),
pageDownloadTime: (t.responseEnd - t.responseStart),
domInteractiveTime: (t.domInteractive - t.navigationStart),
domContentLoadedTime: (t.domContentLoadedEventStart - t.navigationStart),
pageLoadTime: (t.loadEventStart - t.navigationStart),
frontEndTime: (t.loadEventStart - t.responseEnd),
backEndTime: (t.responseStart - t.navigationStart)
},
userTimings: {
marks: marks
}
};
});
timings.url = page.url;
har = createHAR(page.url, page.title, page.startTime, timings.timings.domContentLoadedTime, timings.timings
.pageLoadTime, page.resources);
try {
fs.write(output, JSON.stringify(timings), 'w');
fs.write(harfilename, JSON.stringify(har, undefined, 4), 'w');
} catch (e) {
console.log(e);
}
phantom.exit();
});
}
});
}