Index: chrome/test/data/scroll/scroll.js |
diff --git a/chrome/test/data/scroll/scroll.js b/chrome/test/data/scroll/scroll.js |
index 49d2d445b4d0a75016caf5b27528f8f49c464036..9a7c3bd98c5a3d125b2eb63ce39a7ce096b1a9ef 100644 |
--- a/chrome/test/data/scroll/scroll.js |
+++ b/chrome/test/data/scroll/scroll.js |
@@ -3,119 +3,182 @@ |
// found in the LICENSE file. |
// Inject this script on any page to measure framerate as the page is scrolled |
-// twice from top to bottom. |
+// from top to bottom. |
// |
-// USAGE: |
-// 1. To start the scrolling, invoke __scroll_test(). |
-// 2. Wait for __scrolling_complete to be true |
-// 3. Read __frame_times array. |
- |
- |
-// Function that sets a callback called when the next frame is rendered. |
-var __set_frame_callback; |
- |
-// Function that scrolls the page. |
-var __scroll_by; |
- |
-// Element that can be scrolled. Must provide scrollTop property. |
-var __scrollable_element; |
- |
-// Amount of scrolling at each frame. |
-var __scroll_delta = 100; |
+// Usage: |
+// 1. Define a callback that takes the results array as a parameter. |
+// 2. To start the test, call window.__scrollTest(callback). |
+// 3a. When the test is complete, the callback will be called. |
+// 3b. If no callback is specified, the results are sent to the console. |
+ |
+(function() { |
+ var getTimeMs = (function() { |
+ if (window.performance) |
+ return (performance.now || |
+ performance.mozNow || |
+ performance.msNow || |
+ performance.oNow || |
+ performance.webkitNow).bind(window.performance); |
+ else |
+ return function() { return new Date().getTime(); }; |
+ })(); |
+ |
+ var requestAnimationFrame = (function() { |
+ return window.requestAnimationFrame || |
+ window.webkitRequestAnimationFrame || |
+ window.mozRequestAnimationFrame || |
+ window.oRequestAnimationFrame || |
+ window.msRequestAnimationFrame || |
+ function(callback) { |
+ window.setTimeout(callback, 1000 / 60); |
+ }; |
+ })().bind(window); |
+ |
+ function GpuBenchmarkingScrollStats() { |
+ this.initialStats_ = this.getRenderingStats_(); |
+ } |
-// Number of scrolls to perform. |
-var __num_scrolls = 2; |
+ GpuBenchmarkingScrollStats.prototype.getResult = function() { |
+ var stats = this.getRenderingStats_(); |
+ for (var key in stats) |
+ stats[key] -= this.initialStats_[key]; |
+ return stats; |
+ }; |
+ |
+ GpuBenchmarkingScrollStats.prototype.getRenderingStats_ = function() { |
+ var stats = chrome.gpuBenchmarking.renderingStats(); |
+ stats.totalTimeInSeconds = getTimeMs() / 1000; |
+ return stats; |
+ }; |
+ |
+ function RafScrollStats(timestamp) { |
+ this.frameTimes_ = [timestamp]; |
+ this.recording_ = true; |
+ requestAnimationFrame(this.processStep_.bind(this)); |
+ } |
-// Current scroll position. |
-var __ypos; |
+ RafScrollStats.prototype.getResult = function() { |
+ this.recording_ = false; |
+ |
+ // Fill in the result object. |
+ var result = {}; |
+ result.numAnimationFrames = this.frameTimes_.length - 1; |
+ result.droppedFrameCount = this.getDroppedFrameCount_(this.frameTimes_); |
+ result.totalTimeInSeconds = (this.frameTimes_[this.frameTimes_.length - 1] - |
+ this.frameTimes_[0]) / 1000; |
+ return result; |
+ }; |
+ |
+ RafScrollStats.prototype.processStep_ = function(timestamp) { |
+ if (!this.recording_) |
+ return; |
+ |
+ this.frameTimes_.push(timestamp); |
+ requestAnimationFrame(this.processStep_.bind(this)); |
+ }; |
+ |
+ RafScrollStats.prototype.getDroppedFrameCount_ = function(frameTimes) { |
+ var droppedFrameCount = 0; |
+ for (var i = 1; i < frameTimes.length; i++) { |
+ var frameTime = frameTimes[i] - frameTimes[i-1]; |
+ if (frameTime > 1000 / 55) |
+ droppedFrameCount++; |
+ } |
+ return droppedFrameCount; |
+ }; |
-// Time of previous scroll callback execution. |
-var __start_time = 0; |
+ // In this class, a "step" is when the page is being scrolled. |
+ // i.e. startScroll -> startStep -> scroll -> processStep -> |
+ // -> startStep -> scroll -> processStep -> endScroll |
+ function ScrollTest(callback, opt_isGmailTest) { |
+ var self = this; |
-// True when all scrolling has completed. |
-var __scrolling_complete = false; |
+ this.TOTAL_ITERATIONS_ = 2; |
+ this.SCROLL_DELTA_ = 100; |
-// Array of frame times for each scroll in __num_scrolls. |
-var __frame_times = [[]]; |
+ this.callback_ = callback; |
+ this.isGmailTest_ = opt_isGmailTest; |
+ this.iteration_ = 0; |
-// Set this to true when scrolling in Gmail. |
-var __is_gmail_test = false; |
+ this.results_ = [] |
+ if (this.isGmailTest_) { |
+ gmonkey.load('2.0', function(api) { |
+ self.start_(api.getScrollableElement()); |
+ }); |
+ } else { |
+ if (document.readyState == 'complete') |
+ this.start_(); |
+ else |
+ window.addEventListener('load', function() { self.start_(); }); |
+ } |
+ } |
-// Initializes the platform-independent frame callback scheduler function. |
-function __init_set_frame_callback() { |
- __set_frame_callback = window.requestAnimationFrame || |
- window.mozRequestAnimationFrame || |
- window.msRequestAnimationFrame || |
- window.oRequestAnimationFrame || |
- window.webkitRequestAnimationFrame; |
-} |
+ ScrollTest.prototype.scroll_ = function(x, y) { |
+ if (this.isGmailTest_) |
+ this.element_.scrollByLines(1); |
+ else |
+ window.scrollBy(x, y); |
+ }; |
+ |
+ ScrollTest.prototype.start_ = function(opt_element) { |
+ // Assign this.element_ here instead of constructor, because the constructor |
+ // ensures this method will be called after the document is loaded. |
+ this.element_ = opt_element || document.body; |
+ requestAnimationFrame(this.startScroll_.bind(this)); |
+ }; |
+ |
+ ScrollTest.prototype.startScroll_ = function(timestamp) { |
+ this.element_.scrollTop = 0; |
+ if (window.chrome && chrome.gpuBenchmarking) |
+ this.scrollStats_ = new GpuBenchmarkingScrollStats(); |
+ else |
+ this.scrollStats_ = new RafScrollStats(timestamp); |
+ this.startStep_(); |
+ }; |
+ |
+ ScrollTest.prototype.startStep_ = function() { |
+ this.scroll_(0, this.SCROLL_DELTA_); |
+ requestAnimationFrame(this.processStep_.bind(this)); |
+ }; |
+ |
+ ScrollTest.prototype.endScroll_ = function() { |
+ this.results_.push(this.scrollStats_.getResult()); |
+ this.iteration_++; |
+ }; |
+ |
+ ScrollTest.prototype.sendResults_ = function() { |
nduca
2012/08/16 02:42:59
you can probably just inline this once you clean u
dtu
2012/08/16 19:12:39
Done.
|
+ if (this.callback_) |
+ this.callback_(this.results_); |
+ else |
+ console.log(this.results_); |
+ }; |
+ |
+ ScrollTest.prototype.processStep_ = function(timestamp) { |
+ // clientHeight is "special" for the body element. |
+ if (this.element_ == document.body) |
+ var clientHeight = window.innerHeight; |
+ else |
+ var clientHeight = this.element_.clientHeight; |
+ |
+ var isScrollComplete = |
+ this.element_.scrollTop + clientHeight >= this.element_.scrollHeight; |
+ if (isScrollComplete) { |
nduca
2012/08/16 02:42:59
you can restructure this to be if (!scrollcomplete
dtu
2012/08/16 19:12:39
Done.
|
+ this.endScroll_(); |
+ |
+ var isTestComplete = this.iteration_ >= this.TOTAL_ITERATIONS_; |
+ if (isTestComplete) |
+ this.sendResults_(); |
+ else |
+ requestAnimationFrame(this.startScroll_.bind(this)); |
+ |
+ return; |
+ } |
+ this.startStep_(); |
+ }; |
-// Initializes the most realistic scrolling method. |
-function __init_scroll_by() { |
- if (__is_gmail_test) { |
- __scroll_by = function(x, y) { |
- __scrollable_element.scrollByLines(1); |
- }; |
- } else if (window.chrome && window.chrome.benchmarking && |
- window.chrome.benchmarking.smoothScrollBy) { |
- __scroll_by = window.chrome.benchmarking.smoothScrollBy; |
- } else { |
- __scroll_by = window.scrollBy; |
- } |
-} |
- |
- |
-// Scrolls page down and reschedules itself until it hits the bottom. |
-// Collects stats along the way. |
-function __do_scroll(now_time) { |
- __scroll_by(0, __scroll_delta); |
- __set_frame_callback(function(now_time) { |
- if (__start_time) { |
- if (__scrollable_element.scrollTop > __ypos) { |
- // Scroll in progress, push a frame. |
- __frame_times[__frame_times.length-1].push(now_time - __start_time); |
- } else { |
- // Scroll complete, either scroll again or finish. |
- if (__frame_times.length < __num_scrolls) { |
- __scrollable_element.scrollTop = 0; |
- __frame_times.push([]); |
- } else { |
- console.log('frame_times', '' + __frame_times); |
- __scrolling_complete = true; |
- return; |
- } |
- } |
- } |
- __ypos = __scrollable_element.scrollTop; |
- __start_time = now_time; |
- __do_scroll(); |
- }); |
-} |
- |
- |
-function __start_scroll(scrollable_element) { |
- __scrollable_element = scrollable_element; |
- __init_scroll_by(); |
- __set_frame_callback(__do_scroll); |
-} |
- |
- |
-// Performs the scroll test. |
-function __scroll_test() { |
- __init_set_frame_callback(); |
- if (__is_gmail_test) { |
- gmonkey.load("2.0", function(api) { |
- __start_scroll(api.getScrollableElement()); |
- }); |
- } else { |
- if (window.performance.timing.loadEventStart) { |
- __start_scroll(document.body); // Page already loaded. |
- } else { |
- window.addEventListener('load', function() { |
- __start_scroll(document.body); // Page hasn't loaded yet, schedule. |
- }); |
- } |
+ window.__scrollTest = function(callback, opt_isGmailTest) { |
nduca
2012/08/16 02:42:59
How about exposing the constructor directly as win
dtu
2012/08/16 19:12:39
Done.
|
+ new ScrollTest(callback, opt_isGmailTest); |
} |
-} |
+})(); |