Disable view source for Developer Tools.
[chromium-blink-merge.git] / chrome / test / data / scroll / scroll.js
blobd974ca4294c5d2b490b8880bdca6166f4d3eaf80
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 // Inject this script on any page to measure framerate as the page is scrolled
6 // from top to bottom.
7 //
8 // Usage:
9 // 1. Define a callback that takes the results array as a parameter.
10 // 2. To start the test, call new __ScrollTest(callback).
11 // 3a. When the test is complete, the callback will be called.
12 // 3b. If no callback is specified, the results are sent to the console.
14 (function() {
15 var getTimeMs = (function() {
16 if (window.performance)
17 return (performance.now ||
18 performance.mozNow ||
19 performance.msNow ||
20 performance.oNow ||
21 performance.webkitNow).bind(window.performance);
22 else
23 return function() { return new Date().getTime(); };
24 })();
26 var requestAnimationFrame = (function() {
27 return window.requestAnimationFrame ||
28 window.webkitRequestAnimationFrame ||
29 window.mozRequestAnimationFrame ||
30 window.oRequestAnimationFrame ||
31 window.msRequestAnimationFrame ||
32 function(callback) {
33 window.setTimeout(callback, 1000 / 60);
35 })().bind(window);
37 /**
38 * Scrolls a given element down a certain amount to emulate user scrolling.
39 * Uses smooth scrolling capabilities provided by the platform, if available.
40 * @constructor
42 function SmoothScrollDownGesture(opt_element, opt_isGmailTest) {
43 this.element_ = opt_element || document.body;
44 this.isGmailTest_ = opt_isGmailTest;
47 SmoothScrollDownGesture.prototype.start = function(callback) {
48 this.callback_ = callback;
49 if (chrome &&
50 chrome.gpuBenchmarking &&
51 chrome.gpuBenchmarking.beginSmoothScrollDown) {
52 chrome.gpuBenchmarking.beginSmoothScrollDown(true, function() {
53 callback();
54 });
55 return;
58 if (this.isGmailTest_) {
59 this.element_.scrollByLines(1);
60 requestAnimationFrame(callback);
61 return;
64 var SCROLL_DELTA = 100;
65 this.element_.scrollTop += SCROLL_DELTA;
66 requestAnimationFrame(callback);
69 /**
70 * Tracks rendering performance using the gpuBenchmarking.renderingStats API.
71 * @constructor
73 function GpuBenchmarkingRenderingStats() {
76 GpuBenchmarkingRenderingStats.prototype.start = function() {
77 this.initialStats_ = this.getRenderingStats_();
79 GpuBenchmarkingRenderingStats.prototype.stop = function() {
80 this.finalStats_ = this.getRenderingStats_();
83 GpuBenchmarkingRenderingStats.prototype.getResult = function() {
84 if (!this.initialStats_)
85 throw new Error("Start not called.");
87 if (!this.finalStats_)
88 throw new Error("Stop was not called.");
90 var stats = this.finalStats_;
91 for (var key in stats)
92 stats[key] -= this.initialStats_[key];
93 return stats;
96 GpuBenchmarkingRenderingStats.prototype.getRenderingStats_ = function() {
97 var stats = chrome.gpuBenchmarking.renderingStats();
98 stats.totalTimeInSeconds = getTimeMs() / 1000;
99 return stats;
103 * Tracks rendering performance using requestAnimationFrame.
104 * @constructor
106 function RafRenderingStats() {
107 this.recording_ = false;
108 this.frameTimes_ = [];
111 RafRenderingStats.prototype.start = function() {
112 if (this.recording_)
113 throw new Error("Already started.");
114 this.recording_ = true;
115 requestAnimationFrame(this.recordFrameTime_.bind(this));
118 RafRenderingStats.prototype.stop = function() {
119 this.recording_ = false;
122 RafRenderingStats.prototype.getResult = function() {
123 var result = {};
124 result.numAnimationFrames = this.frameTimes_.length - 1;
125 result.droppedFrameCount = this.getDroppedFrameCount_(this.frameTimes_);
126 result.totalTimeInSeconds = (this.frameTimes_[this.frameTimes_.length - 1] -
127 this.frameTimes_[0]) / 1000;
128 return result;
131 RafRenderingStats.prototype.recordFrameTime_ = function(timestamp) {
132 if (!this.recording_)
133 return;
135 this.frameTimes_.push(timestamp);
136 requestAnimationFrame(this.recordFrameTime_.bind(this));
139 RafRenderingStats.prototype.getDroppedFrameCount_ = function(frameTimes) {
140 var droppedFrameCount = 0;
141 for (var i = 1; i < frameTimes.length; i++) {
142 var frameTime = frameTimes[i] - frameTimes[i-1];
143 if (frameTime > 1000 / 55)
144 droppedFrameCount++;
146 return droppedFrameCount;
149 // This class scrolls a page from the top to the bottom a given number of
150 // times.
152 // Each full transit of the page is called a "pass."
154 // The page is scrolled down by a set of scroll gestures. These gestures
155 // correspond to a reading gesture on that platform.
157 // i.e. for TOTAL_ITERATIONS_ = 2, we do
158 // start_ -> startPass_ -> ...scrolling... -> onGestureComplete_ ->
159 // -> startPass_ -> .. scrolling... -> onGestureComplete_ -> callback_
161 // TODO(nduca): This test starts in its constructor. That is strange. We
162 // should change it to start explicitly.
163 function ScrollTest(opt_callback, opt_isGmailTest) {
164 var self = this;
166 this.TOTAL_ITERATIONS_ = 2;
168 this.callback_ = opt_callback;
169 this.isGmailTest_ = opt_isGmailTest;
170 this.iteration_ = 0;
172 this.results_ = []
174 if (this.isGmailTest_) {
175 gmonkey.load('2.0', function(api) {
176 self.start_(api.getScrollableElement());
178 } else {
179 if (document.readyState == 'complete')
180 this.start_();
181 else
182 window.addEventListener('load', function() { self.start_(); });
186 ScrollTest.prototype.start_ = function(opt_element) {
187 // Assign this.element_ here instead of constructor, because the constructor
188 // ensures this method will be called after the document is loaded.
189 this.element_ = opt_element || document.body;
190 requestAnimationFrame(this.startPass_.bind(this));
193 ScrollTest.prototype.startPass_ = function() {
194 this.element_.scrollTop = 0;
195 if (window.chrome && chrome.gpuBenchmarking)
196 this.renderingStats_ = new GpuBenchmarkingRenderingStats();
197 else
198 this.renderingStats_ = new RafRenderingStats();
199 this.renderingStats_.start();
201 this.gesture_ = new SmoothScrollDownGesture(this.element_,
202 this.isGmailTest_);
203 this.gesture_.start(this.onGestureComplete_.bind(this));
206 ScrollTest.prototype.onGestureComplete_ = function(timestamp) {
207 // clientHeight is "special" for the body element.
208 var clientHeight;
209 if (this.element_ == document.body)
210 clientHeight = window.innerHeight;
211 else
212 clientHeight = this.element_.clientHeight;
214 var isPassComplete =
215 this.element_.scrollTop + clientHeight >= this.element_.scrollHeight;
217 if (!isPassComplete) {
218 this.gesture_.start(this.onGestureComplete_.bind(this));
219 return;
222 this.endPass_();
224 var isTestComplete = this.iteration_ >= this.TOTAL_ITERATIONS_;
225 if (!isTestComplete) {
226 this.startPass_();
227 return;
230 // Send results.
231 if (this.callback_)
232 this.callback_(this.results_);
233 else
234 console.log(this.results_);
237 ScrollTest.prototype.endPass_ = function() {
238 this.renderingStats_.stop();
239 this.results_.push(this.renderingStats_.getResult());
240 this.iteration_++;
244 window.__ScrollTest = ScrollTest;
245 })();