// 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() } }); });