| OLD | NEW |
| 1 <style> | 1 <style> |
| 2 #options { | 2 #options { |
| 3 position: absolute; | 3 position: absolute; |
| 4 background-color: #FFFFCC; | 4 background-color: #FFFFCC; |
| 5 display: none; | 5 display: none; |
| 6 font-family: "Courier New"; | 6 font-family: "Courier New"; |
| 7 font-size: 9pt; | 7 font-size: 9pt; |
| 8 padding: 5px; | 8 padding: 5px; |
| 9 border: 1px solid #CCCC88; | 9 border: 1px solid #CCCC88; |
| 10 z-index: 3; | 10 z-index: 3; |
| 11 } | 11 } |
| 12 </style> | 12 </style> |
| 13 | 13 |
| 14 <script> | 14 <script src="background.js"> |
| 15 // Round a number to the 1's place. | |
| 16 function formatNumber(str) { | |
| 17 str += ''; | |
| 18 if (str == '0') { | |
| 19 return 'N/A '; | |
| 20 } | |
| 21 var x = str.split('.'); | |
| 22 var x1 = x[0]; | |
| 23 var x2 = x.length > 1 ? '.' + x[1] : ''; | |
| 24 var regex = /(\d+)(\d{3})/; | |
| 25 while (regex.test(x1)) { | |
| 26 x1 = x1.replace(regex, '$1' + ',' + '$2'); | |
| 27 } | |
| 28 return x1; | |
| 29 } | |
| 30 | |
| 31 // Configuration and results are stored globally. | |
| 32 window.iterations = 10; | |
| 33 window.interval = 200; | |
| 34 window.clearConnections = true; | |
| 35 window.clearCache = true; | |
| 36 window.enableSpdy = false; | |
| 37 window.results = {}; | |
| 38 window.results.data = new Array(); | |
| 39 window.testUrl = "http://www.google.com/"; | |
| 40 window.windowId = 0; | |
| 41 | |
| 42 // Constant StatCounter Names | |
| 43 var kTCPReadBytes = "tcp.read_bytes"; | |
| 44 var kTCPWriteBytes = "tcp.write_bytes"; | |
| 45 var kRequestCount = "HttpNetworkTransaction.Count"; | |
| 46 var kConnectCount = "tcp.connect"; | |
| 47 var kSpdySessionCount = "spdy.sessions"; | |
| 48 | |
| 49 // The list of currently running benchmarks | |
| 50 var benchmarks = new Array(); | |
| 51 var benchmarkIndex = 0; | |
| 52 var benchmarkWindow = 0; | |
| 53 | |
| 54 function addBenchmark(benchmark) { | |
| 55 benchmarks.push(benchmark); | |
| 56 benchmarkIndex = 0; // Reset the counter when adding benchmarks. | |
| 57 } | |
| 58 | |
| 59 // Array Remove - By John Resig (MIT Licensed) | |
| 60 Array.prototype.remove = function(from, to) { | |
| 61 var rest = this.slice((to || from) + 1 || this.length); | |
| 62 this.length = from < 0 ? this.length + from : from; | |
| 63 return this.push.apply(this, rest); | |
| 64 }; | |
| 65 | |
| 66 function removeBenchmark(benchmark) { | |
| 67 var index; | |
| 68 for (var index = 0; index < benchmarks.length; ++index) { | |
| 69 if (benchmarks[index] == benchmark) { | |
| 70 break; | |
| 71 } | |
| 72 } | |
| 73 benchmarks.remove(index); | |
| 74 | |
| 75 // Preserve index ordering when removing from the list. | |
| 76 if (index <= benchmarkIndex) { | |
| 77 benchmarkIndex--; // Note: it is okay to drop to -1 here. | |
| 78 } | |
| 79 } | |
| 80 | |
| 81 function benchmarkStillRunning() { | |
| 82 for (var index = 0; index < benchmarks.length; ++index) { | |
| 83 if (benchmarks[index].isRunning()) { | |
| 84 return true; | |
| 85 } | |
| 86 } | |
| 87 return false; | |
| 88 } | |
| 89 | |
| 90 function findBenchmark(url) { | |
| 91 for (var index = 0; index < benchmarks.length; ++index) { | |
| 92 // One common redirection case: if the url ends without a slash and refers | |
| 93 // to a directory, it actually would be redirected to the correct one | |
| 94 // (with a slash). In this case, the url returned by the JS and the one | |
| 95 // stored locally do not match. | |
| 96 if ((benchmarks[index].url() == url) || | |
| 97 (benchmarks[index].url() + '/' == url)) { | |
| 98 return benchmarks[index]; | |
| 99 } | |
| 100 } | |
| 101 return undefined; | |
| 102 } | |
| 103 | |
| 104 function nextBenchmark() { | |
| 105 benchmarkIndex = (benchmarkIndex + 1) % benchmarks.length; | |
| 106 return benchmarks[benchmarkIndex]; | |
| 107 } | |
| 108 | |
| 109 function show_options() { | |
| 110 chrome.tabs.getSelected(null, function(tab) { | |
| 111 if (window.testUrl == "") { | |
| 112 window.testUrl = tab.url; | |
| 113 } | |
| 114 var tabs = chrome.extension.getExtensionTabs(); | |
| 115 if (tabs && tabs.length) { | |
| 116 // To avoid "Uncaught TypeError: Object Window has no method | |
| 117 // 'setUrl' ". Sometimes tabs are not the desired extension tabs. | |
| 118 if (tabs[0].$suburl != undefined) { | |
| 119 tabs[0].setUrl(testUrl); | |
| 120 } | |
| 121 var optionsUrl = chrome.extension.getURL("options.html"); | |
| 122 chrome.tabs.getAllInWindow(null, function(all) { | |
| 123 for (var i = 0; i < all.length; i++) { | |
| 124 if (all[i].url == optionsUrl) { | |
| 125 chrome.tabs.update(all[i].id, {selected: true}); | |
| 126 return; | |
| 127 } | |
| 128 } | |
| 129 }); | |
| 130 } else { | |
| 131 chrome.tabs.create({"url":"options.html"}); | |
| 132 } | |
| 133 }); | |
| 134 } | |
| 135 | |
| 136 chrome.browserAction.onClicked.addListener(show_options); | |
| 137 | |
| 138 function Benchmark() { | |
| 139 var runCount_ = 0; | |
| 140 var count_; | |
| 141 var totalTime_; | |
| 142 var me_ = this; | |
| 143 var current_; | |
| 144 var initialRequestCount_; | |
| 145 var initialReadBytes_; | |
| 146 var initialWriteBytes_; | |
| 147 | |
| 148 // Start a test run | |
| 149 this.start = function(url) { | |
| 150 // Check if a run is already in progress. | |
| 151 if (me_.isRunning()) { | |
| 152 return; | |
| 153 } | |
| 154 | |
| 155 console.log("Benchmark testing url: " + url); | |
| 156 | |
| 157 // Add this benchmark to the list of benchmarks running. | |
| 158 addBenchmark(this); | |
| 159 | |
| 160 runCount_ = window.iterations; | |
| 161 count_ = 0; | |
| 162 totalTime_ = 0; | |
| 163 | |
| 164 current_ = {}; | |
| 165 current_.url = url; | |
| 166 current_.timestamp = new Date(); | |
| 167 current_.viaSpdy = false; | |
| 168 current_.startLoadResults = new Array(); // times to start | |
| 169 current_.commitLoadResults = new Array(); // times to commit | |
| 170 current_.docLoadResults = new Array(); // times to docload | |
| 171 current_.paintResults = new Array(); // times to paint | |
| 172 current_.totalResults = new Array(); // times to complete load | |
| 173 current_.KbytesRead = new Array(); | |
| 174 current_.KbytesWritten = new Array(); | |
| 175 current_.readbpsResults = new Array(); | |
| 176 current_.writebpsResults = new Array(); | |
| 177 current_.totalTime = 0; | |
| 178 current_.iterations = 0; | |
| 179 current_.requests = new Array(); | |
| 180 current_.connects = new Array(); | |
| 181 current_.spdySessions = new Array(); | |
| 182 current_.domNum = 0; | |
| 183 current_.maxDepth = 0; | |
| 184 current_.minDepth = 0; | |
| 185 current_.avgDepth = 0; | |
| 186 } | |
| 187 | |
| 188 // Is the benchmark currently in progress. | |
| 189 this.isRunning = function() { | |
| 190 return runCount_ > 0; | |
| 191 } | |
| 192 | |
| 193 // The url which this benchmark is running. | |
| 194 this.url = function() { return current_.url; } | |
| 195 | |
| 196 // Called when the test run completes. | |
| 197 this.finish = function() { | |
| 198 removeBenchmark(this); | |
| 199 | |
| 200 // If we're the last benchmark, close the window. | |
| 201 if (benchmarks.length == 0) { | |
| 202 chrome.tabs.remove(benchmarkWindow.id); | |
| 203 benchmarkWindow = 0; | |
| 204 show_options(); | |
| 205 } | |
| 206 } | |
| 207 | |
| 208 // Update the UI after a test run. | |
| 209 this.displayResults = function() { | |
| 210 var score = 0; | |
| 211 if (count_ > 0) { | |
| 212 score = totalTime_ / count_; | |
| 213 var text = score.toFixed(1) + "ms avg"; | |
| 214 chrome.browserAction.setTitle({"title": text}); | |
| 215 } | |
| 216 if (runCount_) { | |
| 217 chrome.browserAction.setBadgeText({"text": "" + runCount_}); | |
| 218 chrome.browserAction.setBadgeBackgroundColor({"color": [255, 0, 0, 255]}); | |
| 219 } else { | |
| 220 chrome.browserAction.setBadgeText({"text": "" + score.toFixed()}); | |
| 221 chrome.browserAction.setBadgeBackgroundColor({"color": [0, 255, 0, 255]}); | |
| 222 } | |
| 223 | |
| 224 // Reload the page after each run to show immediate results. | |
| 225 var tabs = chrome.extension.getExtensionTabs(); | |
| 226 if (tabs && tabs.length) { | |
| 227 tabs[0].location.reload(true); | |
| 228 } | |
| 229 } | |
| 230 | |
| 231 // Called before starting a page load. | |
| 232 this.pageStart = function() { | |
| 233 initialReadBytes_ = chrome.benchmarking.counter(kTCPReadBytes); | |
| 234 initialWriteBytes_ = chrome.benchmarking.counter(kTCPWriteBytes); | |
| 235 initialRequestCount_ = chrome.benchmarking.counter(kRequestCount); | |
| 236 initialConnectCount_ = chrome.benchmarking.counter(kConnectCount); | |
| 237 initialSpdySessionCount_ = chrome.benchmarking.counter(kSpdySessionCount); | |
| 238 } | |
| 239 | |
| 240 this.openNextPage = function() { | |
| 241 var benchmark = nextBenchmark(); | |
| 242 benchmark.pageStart(); | |
| 243 chrome.tabs.create({"url": benchmark.url(),"selected": true}, | |
| 244 function(tab) { | |
| 245 benchmarkWindow = tab; | |
| 246 // script.js only executes on tested pages | |
| 247 // not the ones opened by the user. | |
| 248 chrome.tabs.executeScript(tab.id, {file: "script.js"}); | |
| 249 }); | |
| 250 } | |
| 251 | |
| 252 this.prepareToOpenPage = function() { | |
| 253 // After the previous page is closed, this function will apply | |
| 254 // any settings needed to prepare for opening a new page. | |
| 255 // Note: the previous page must be closed, otherwie, the cache | |
| 256 // clearing and connection clearing may not be thorough. | |
| 257 | |
| 258 if (window.clearCache) { | |
| 259 chrome.benchmarking.clearCache(); | |
| 260 } | |
| 261 | |
| 262 if (window.clearConnections) { | |
| 263 chrome.benchmarking.closeConnections(); | |
| 264 } | |
| 265 | |
| 266 if (window.enableSpdy) { | |
| 267 chrome.benchmarking.enableSpdy(true); | |
| 268 } else { | |
| 269 chrome.benchmarking.enableSpdy(false); | |
| 270 } | |
| 271 | |
| 272 // Go back to the browser so that tasks can run. | |
| 273 setTimeout(me_.openNextPage, window.interval); | |
| 274 } | |
| 275 | |
| 276 this.closePage = function() { | |
| 277 chrome.tabs.remove(benchmarkWindow.id, function() { | |
| 278 me_.prepareToOpenPage(); | |
| 279 }); | |
| 280 } | |
| 281 | |
| 282 // Run a single page in the benchmark | |
| 283 this.runPage = function() { | |
| 284 if (benchmarkWindow) { | |
| 285 // To avoid the error "Error during tabs.remove: No tab with id xx" | |
| 286 // while debugging, due to user manually closing the benchmark tab. | |
| 287 chrome.tabs.getAllInWindow(null, function(all) { | |
| 288 for (var i = 0; i < all.length; i++) { | |
| 289 if (all[i].id == benchmarkWindow.id) { | |
| 290 me_.closePage(); | |
| 291 return; | |
| 292 }; | |
| 293 }; | |
| 294 me_.prepareToOpenPage(); | |
| 295 }); | |
| 296 } else { | |
| 297 me_.prepareToOpenPage(); | |
| 298 } | |
| 299 } | |
| 300 | |
| 301 // Called when a page finishes loading. | |
| 302 this.pageFinished = function(load_times, domNum, depths) { | |
| 303 | |
| 304 // Make sure the content can be fetched via spdy if it is enabled. | |
| 305 if (window.enableSpdy && !load_times.wasFetchedViaSpdy) { | |
| 306 alert("Can not fetch current url via spdy.\n" + | |
| 307 "Ending current test."); | |
| 308 me_.finish(); | |
| 309 // Move on to next benchmarked pages. | |
| 310 if (benchmarks.length > 0) { | |
| 311 if (window.clearConnections) { | |
| 312 chrome.benchmarking.closeConnections(); | |
| 313 } | |
| 314 setTimeout(me_.runPage, 100); | |
| 315 } | |
| 316 return; | |
| 317 } | |
| 318 | |
| 319 // If last fetch was via spdy, current fetch should use spdy too. Same | |
| 320 // for vise versa. | |
| 321 if (current_.iterations > 0 && | |
| 322 current_.viaSpdy != load_times.wasFetchedViaSpdy) { | |
| 323 alert("Error: viaSpdy for current fetch is different from last fetch!\n" + | |
| 324 "Ending current test."); | |
| 325 // Current data set is invalid: remove from the result array. | |
| 326 var currIndex; | |
| 327 currIndex = window.results.data.indexOf(current_, 0); | |
| 328 window.results.data.splice(currIndex, 1); | |
| 329 me_.displayResults(); | |
| 330 me_.finish(); | |
| 331 if (benchmarks.length > 0) { | |
| 332 if (window.clearConnections) { | |
| 333 chrome.benchmarking.closeConnections(); | |
| 334 } | |
| 335 setTimeout(me_.runPage, 100); | |
| 336 } | |
| 337 return; | |
| 338 } | |
| 339 | |
| 340 var requested = load_times.requestTime; | |
| 341 var started = load_times.startLoadTime; | |
| 342 var startLoadTime = | |
| 343 Math.round((load_times.startLoadTime - requested) * 1000.0); | |
| 344 var commitLoadTime = | |
| 345 Math.round((load_times.commitLoadTime - started) * 1000.0); | |
| 346 var docLoadTime = | |
| 347 Math.round((load_times.finishDocumentLoadTime - started) * 1000.0); | |
| 348 var paintTime = | |
| 349 Math.round((load_times.firstPaintTime - started) * 1000.0); | |
| 350 var totalTime = | |
| 351 Math.round((load_times.finishLoadTime - started) * 1000.0); | |
| 352 var firstPaintAfterLoadTime = | |
| 353 Math.round((load_times.firstPaintAfterLoadTime - started) * 1000.0); | |
| 354 | |
| 355 if (paintTime < 0) { | |
| 356 // If the user navigates away from the test while it is running, | |
| 357 // paint may not occur. Also, some lightweight pages, such as the | |
| 358 // google home page, never trigger a paint measurement via the chrome | |
| 359 // page load timer. | |
| 360 // In this case, the time-to-first paint is effectively the same as the | |
| 361 // time to onLoad(). | |
| 362 paintTime = totalTime; | |
| 363 } | |
| 364 | |
| 365 // For our toolbar counters | |
| 366 totalTime_ += totalTime; | |
| 367 count_++; | |
| 368 | |
| 369 // Get the index of current benchmarked page in the result array. | |
| 370 var currIndex; | |
| 371 currIndex = window.results.data.indexOf(current_, 0); | |
| 372 | |
| 373 // Record the result | |
| 374 current_.viaSpdy = load_times.wasFetchedViaSpdy; | |
| 375 current_.iterations++; | |
| 376 current_.startLoadResults.push(startLoadTime); | |
| 377 current_.commitLoadResults.push(commitLoadTime); | |
| 378 current_.docLoadResults.push(docLoadTime); | |
| 379 current_.paintResults.push(paintTime); | |
| 380 current_.totalResults.push(totalTime); | |
| 381 var bytesRead = chrome.benchmarking.counter(kTCPReadBytes) - | |
| 382 initialReadBytes_; | |
| 383 var bytesWrite = chrome.benchmarking.counter(kTCPWriteBytes) - | |
| 384 initialWriteBytes_; | |
| 385 current_.KbytesRead.push(bytesRead / 1024); | |
| 386 current_.KbytesWritten.push(bytesWrite / 1024); | |
| 387 current_.readbpsResults.push(bytesRead * 8 / totalTime); | |
| 388 current_.writebpsResults.push(bytesWrite * 8 / totalTime); | |
| 389 current_.requests.push(chrome.benchmarking.counter(kRequestCount) - | |
| 390 initialRequestCount_); | |
| 391 current_.connects.push(chrome.benchmarking.counter(kConnectCount) - | |
| 392 initialConnectCount_); | |
| 393 current_.spdySessions.push(chrome.benchmarking.counter(kSpdySessionCount) - | |
| 394 initialSpdySessionCount_); | |
| 395 current_.totalTime += totalTime; | |
| 396 current_.domNum = domNum; | |
| 397 current_.maxDepth = depths[0]; | |
| 398 current_.minDepth = depths[1]; | |
| 399 current_.avgDepth = depths[2]; | |
| 400 | |
| 401 // Insert or update the result data after each run. | |
| 402 if (currIndex == -1) { | |
| 403 window.results.data.push(current_); | |
| 404 } else { | |
| 405 window.results.data[currIndex] = current_; | |
| 406 } | |
| 407 | |
| 408 if (--runCount_ == 0) { | |
| 409 me_.finish(); | |
| 410 } | |
| 411 | |
| 412 // If there are more tests, schedule them | |
| 413 if (runCount_ > 0 || benchmarks.length > 0) { | |
| 414 if (window.clearConnections) { | |
| 415 chrome.benchmarking.closeConnections(); | |
| 416 } | |
| 417 setTimeout(me_.runPage, 100); | |
| 418 } | |
| 419 | |
| 420 // Update the UI | |
| 421 me_.displayResults(); | |
| 422 } | |
| 423 } | |
| 424 | |
| 425 chrome.extension.onConnect.addListener(function(port) { | |
| 426 port.onMessage.addListener(function(data) { | |
| 427 if (data.message == "load") { | |
| 428 var benchmark = findBenchmark(data.url); | |
| 429 if (benchmark == undefined && benchmarkStillRunning()) { | |
| 430 alert("Error: Loaded url(" + data.url + ") is not the same as what " + | |
| 431 "you set in url box. This could happen if the request is " + | |
| 432 "redirected. Please use the redirected url for testing."); | |
| 433 // Stop the test here. | |
| 434 benchmarks = []; | |
| 435 } | |
| 436 if (benchmark != undefined && benchmark.isRunning()) { | |
| 437 benchmark.pageFinished(data.values, data.domNum, data.domDepths); | |
| 438 } | |
| 439 } | |
| 440 }); | |
| 441 }); | |
| 442 | |
| 443 function run() { | |
| 444 if (window.clearCache) { | |
| 445 // Show a warning if we will try to clear the cache between runs | |
| 446 // but will also be reusing the same WebKit instance (i.e. Chrome | |
| 447 // is in single-process mode) because the WebKit cache might not get | |
| 448 // completely cleared between runs. | |
| 449 if (chrome.benchmarking.isSingleProcess()) { | |
| 450 alert("Warning: the WebKit cache may not be cleared correctly " + | |
| 451 "between runs because Chrome is running in single-process mode."); | |
| 452 } | |
| 453 } | |
| 454 benchmarks = []; | |
| 455 var urls = testUrl.split(","); | |
| 456 for (var i = 0; i < urls.length; i++) { | |
| 457 | |
| 458 // Remove extra space at the beginning or end of a url. | |
| 459 urls[i] = removeSpace(urls[i]); | |
| 460 | |
| 461 // Alert about and ignore blank page which does not get loaded. | |
| 462 if (urls[i] == "about:blank") { | |
| 463 alert("blank page loaded!"); | |
| 464 } else if (!checkScheme(urls[i])) { | |
| 465 // Alert about url that is not in scheme http:// or https://. | |
| 466 alert(urls[i] + " does not start with http:// or https://."); | |
| 467 } else { | |
| 468 var benchmark = new Benchmark(); | |
| 469 benchmark.start(urls[i]); // XXXMB - move to constructor | |
| 470 } | |
| 471 } | |
| 472 benchmarks[0].runPage(); | |
| 473 } | |
| 474 | |
| 475 // Remove extra whitespace in the beginning or end of a url string. | |
| 476 function removeSpace(url) { | |
| 477 var tempUrl = url; | |
| 478 while (tempUrl.charAt(tempUrl.length-1) == " ") { | |
| 479 tempUrl = tempUrl.substring(0, tempUrl.length-1); | |
| 480 }; | |
| 481 while (tempUrl.charAt(0) == " ") { | |
| 482 tempUrl = tempUrl.substring(1, tempUrl.length); | |
| 483 }; | |
| 484 return tempUrl; | |
| 485 } | |
| 486 | |
| 487 // Check whether a Url starts with http:// or https://. | |
| 488 function checkScheme(url) { | |
| 489 var httpStr = "http://"; | |
| 490 var httpsStr = "https://"; | |
| 491 var urlSubStr1 = url.substring(0, httpStr.length); | |
| 492 var urlSubStr2 = url.substring(0, httpsStr.length); | |
| 493 | |
| 494 if ( (urlSubStr1 == httpStr) || (urlSubStr2 == httpsStr) ) { | |
| 495 return true; | |
| 496 } | |
| 497 return false; | |
| 498 } | |
| 499 | |
| 500 // Run at startup | |
| 501 chrome.windows.getCurrent(function(currentWindow) { | |
| 502 window.windowId = currentWindow.id; | |
| 503 }); | |
| 504 </script> | 15 </script> |
| OLD | NEW |