// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// start.js sends a "start" message to set this.
window.benchmarkConfiguration = {};
// The callback (e.g. report writer) is set via AddBenchmarckCallback.
window.benchmarkCallback;
// Url to load before loading target page.
var kWaitUrl = "http://wprwprwpr/web-page-replay-generate-200";
// Constant StatCounter Names
var kTcpReadBytes = "tcp.read_bytes";
var kTcpWriteBytes = "tcp.write_bytes";
var kRequestCount = "HttpNetworkTransaction.Count";
var kConnectCount = "tcp.connect";
function CHECK(expr, comment) {
if (!expr) {
console.log(comment);
alert(comment);
}
}
function Result() {
var me_ = this;
this.url = "";
this.firstPaintTime = 0;
this.readBytesKB = 0;
this.writeBytesKB = 0;
this.numRequests = 0;
this.numConnects = 0;
this.timing = {}; // window.performance.timing
this.getTotalTime = function() {
var totalTime = 0
if (me_.timing.navigationStart && me_.timing.loadEventEnd) {
totalTime = me_.timing.loadEventEnd - me_.timing.navigationStart;
}
CHECK(totalTime >= 0);
return totalTime;
}
}
// Collect all the results for a session (i.e. different pages).
function ResultsCollection() {
var results_ = [];
var pages_ = [];
var pageResults_ = {};
this.addResult = function(result) {
results_.push(result);
var url = result.url;
if (!(url in pageResults_)) {
pages_.push(url);
pageResults_[url] = [];
}
pageResults_[url].push(result);
}
this.getPages = function() {
return pages_;
}
this.getResults = function() {
return results_;
}
this.getTotalTimes = function() {
return results_.map(function (t) { return t.getTotalTime(); });
}
}
// Load a url in the default tab and record the time.
function PageLoader(url, resultReadyCallback) {
var me_ = this;
var url_ = url;
var resultReadyCallback_ = resultReadyCallback;
// If it record mode, wait a little longer for lazy loaded resources.
var postLoadGraceMs_ = window.isRecordMode ? 5000 : 0;
var loadInterval_ = window.loadInterval;
var checkInterval_ = window.checkInterval;
var timeout_ = window.timeout;
var maxLoadChecks_ = window.maxLoadChecks;
var preloadFunc_;
var timeoutId_;
var isFinished_;
var result_;
var initialReadBytes_;
var initialWriteBytes_;
var initialRequestCount_;
var initialConnectCount_;
this.result = function() { return result_; };
this.run = function() {
timeoutId_ = null;
isFinished_ = false;
result_ = null;
initialReadBytes_ = chrome.benchmarking.counter(kTcpReadBytes);
initialWriteBytes_ = chrome.benchmarking.counter(kTcpWriteBytes);
initialRequestCount_ = chrome.benchmarking.counter(kRequestCount);
initialConnectCount_ = chrome.benchmarking.counter(kConnectCount);
if (me_.preloadFunc_) {
me_.preloadFunc_(me_.load_);
} else {
me_.load_();
}
};
this.setClearAll = function() {
me_.preloadFunc_ = me_.clearAll_;
};
this.setClearConnections = function() {
me_.preloadFunc_ = me_.clearConnections_;
};
this.clearAll_ = function(callback) {
chrome.tabs.getSelected(null, function(tab) {
chrome.tabs.update(tab.id, {"url": kWaitUrl}, function() {
chrome.benchmarking.clearHostResolverCache();
chrome.benchmarking.clearPredictorCache();
chrome.benchmarking.closeConnections();
var dataToRemove = {
"appcache": true,
"cache": true,
"cookies": true,
"downloads": true,
"fileSystems": true,
"formData": true,
"history": true,
"indexedDB": true,
"localStorage": true,
"passwords": true,
"pluginData": true,
"webSQL": true
};
// Add any items new to the API.
for (var prop in chrome.browsingData) {
var dataName = prop.replace("remove", "");
if (dataName && dataName != prop) {
dataName = dataName.charAt(0).toLowerCase() +
dataName.substr(1);
if (!dataToRemove.hasOwnProperty(dataName)) {
console.log("New browsingData API item: " + dataName);
dataToRemove[dataName] = true;
}
}
}
chrome.browsingData.remove({}, dataToRemove, callback);
});
});
};
this.clearConnections_ = function(callback) {
chrome.benchmarking.closeConnections();
callback();
};
this.load_ = function() {
console.log("LOAD started: " + url_);
setTimeout(function() {
chrome.extension.onRequest.addListener(me_.finishLoad_);
timeoutId_ = setTimeout(function() {
me_.finishLoad_({"loadTimes": null, "timing": null});
}, timeout_);
chrome.tabs.getSelected(null, function(tab) {
chrome.tabs.update(tab.id, {"url": url_});
});
}, loadInterval_);
};
this.finishLoad_ = function(msg) {
if (!isFinished_) {
isFinished_ = true;
clearTimeout(timeoutId_);
chrome.extension.onRequest.removeListener(me_.finishLoad_);
me_.saveResult_(msg.loadTimes, msg.timing);
}
};
this.saveResult_ = function(loadTimes, timing) {
result_ = new Result()
result_.url = url_;
if (!loadTimes || !timing) {
console.log("LOAD INCOMPLETE: " + url_);
} else {
console.log("LOAD complete: " + url_);
result_.timing = timing;
var baseTime = timing.navigationStart;
CHECK(baseTime);
result_.firstPaintTime = Math.max(0,
Math.round((1000.0 * loadTimes.firstPaintTime) - baseTime));
}
result_.readBytesKB = (chrome.benchmarking.counter(kTcpReadBytes) -
initialReadBytes_) / 1024;
result_.writeBytesKB = (chrome.benchmarking.counter(kTcpWriteBytes) -
initialWriteBytes_) / 1024;
result_.numRequests = (chrome.benchmarking.counter(kRequestCount) -
initialRequestCount_);
result_.numConnects = (chrome.benchmarking.counter(kConnectCount) -
initialConnectCount_);
setTimeout(function() { resultReadyCallback_(me_); }, postLoadGraceMs_);
};
}
// Load page sets and prepare performance results.
function SessionLoader(resultsReadyCallback) {
var me_ = this;
var resultsReadyCallback_ = resultsReadyCallback;
var pageSets_ = benchmarkConfiguration.pageSets;
var iterations_ = window.iterations;
var retries_ = window.retries;
var pageLoaders_ = [];
var resultsCollection_ = new ResultsCollection();
var loaderIndex_ = 0;
var retryIndex_ = 0;
var iterationIndex_ = 0;
this.run = function() {
me_.createLoaders_();
me_.loadPage_();
}
this.getResultsCollection = function() {
return resultsCollection_;
}
this.createLoaders_ = function() {
// Each url becomes one benchmark.
for (var i = 0; i < pageSets_.length; i++) {
for (var j = 0; j < pageSets_[i].length; j++) {
// Remove extra space at the beginning or end of a url.
var url = pageSets_[i][j].trim();
// Alert about and ignore blank page which does not get loaded.
if (url == "about:blank") {
alert("blank page loaded!");
} else if (!url.match(/https?:\/\//)) {
// Alert about url that is not in scheme http:// or https://.
alert("Skipping url without http:// or https://: " + url);
} else {
var loader = new PageLoader(url, me_.handleResult_)
if (j == 0) {
// Clear all browser data for the first page in a sub list.
loader.setClearAll();
} else {
// Otherwise, only clear the connections.
loader.setClearConnections();
}
pageLoaders_.push(loader);
}
}
}
}
this.loadPage_ = function() {
console.log("LOAD url " + (loaderIndex_ + 1) + " of " +
pageLoaders_.length +
", iteration " + (iterationIndex_ + 1) + " of " +
iterations_);
pageLoaders_[loaderIndex_].run();
}
this.handleResult_ = function(loader) {
var result = loader.result();
resultsCollection_.addResult(result);
var totalTime = result.getTotalTime();
if (!totalTime && retryIndex_ < retries_) {
retryIndex_++;
console.log("LOAD retry, " + retryIndex_);
} else {
retryIndex_ = 0;
console.log("RESULTS url " + (loaderIndex_ + 1) + " of " +
pageLoaders_.length +
", iteration " + (iterationIndex_ + 1) + " of " +
iterations_ + ": " + totalTime);
loaderIndex_++;
if (loaderIndex_ >= pageLoaders_.length) {
iterationIndex_++;
if (iterationIndex_ < iterations_) {
loaderIndex_ = 0;
} else {
resultsReadyCallback_(me_);
return;
}
}
}
me_.loadPage_();
}
}
function AddBenchmarkCallback(callback) {
window.benchmarkCallback = callback;
}
function Run() {
window.checkInterval = 500;
window.loadInterval = 1000;
window.timeout = 20000; // max ms before killing page.
window.retries = 0;
window.isRecordMode = benchmarkConfiguration.isRecordMode;
if (window.isRecordMode) {
window.iterations = 1;
window.timeout = 40000;
window.retries = 2;
} else {
window.iterations = benchmarkConfiguration["iterations"] || 3;
}
var sessionLoader = new SessionLoader(benchmarkCallback);
console.log("pageSets: " + JSON.stringify(benchmarkConfiguration.pageSets));
sessionLoader.run();
}
chrome.runtime.onConnect.addListener(function(port) {
port.onMessage.addListener(function(data) {
if (data.message == "start") {
window.benchmarkConfiguration = data.benchmark;
Run()
}
});
});