- add sources.
[platform/framework/web/crosswalk.git] / src / chrome / test / data / scroll / scroll.js
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.
4
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.
13
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   })();
25
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);
34            };
35   })().bind(window);
36
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
41    */
42   function SmoothScrollDownGesture(opt_element, opt_isGmailTest) {
43     this.element_ = opt_element || document.body;
44     this.isGmailTest_ = opt_isGmailTest;
45   };
46
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;
56     }
57
58     if (this.isGmailTest_) {
59       this.element_.scrollByLines(1);
60       requestAnimationFrame(callback);
61       return;
62     }
63
64     var SCROLL_DELTA = 100;
65     this.element_.scrollTop += SCROLL_DELTA;
66     requestAnimationFrame(callback);
67   }
68
69   /**
70    * Tracks rendering performance using the gpuBenchmarking.renderingStats API.
71    * @constructor
72    */
73   function GpuBenchmarkingRenderingStats() {
74   }
75
76   GpuBenchmarkingRenderingStats.prototype.start = function() {
77     this.initialStats_ = this.getRenderingStats_();
78   }
79   GpuBenchmarkingRenderingStats.prototype.stop = function() {
80     this.finalStats_ = this.getRenderingStats_();
81   }
82
83   GpuBenchmarkingRenderingStats.prototype.getResult = function() {
84     if (!this.initialStats_)
85       throw new Error("Start not called.");
86
87     if (!this.finalStats_)
88       throw new Error("Stop was not called.");
89
90     var stats = this.finalStats_;
91     for (var key in stats)
92       stats[key] -= this.initialStats_[key];
93     return stats;
94   };
95
96   GpuBenchmarkingRenderingStats.prototype.getRenderingStats_ = function() {
97     var stats = chrome.gpuBenchmarking.renderingStats();
98     stats.totalTimeInSeconds = getTimeMs() / 1000;
99     return stats;
100   };
101
102   /**
103    * Tracks rendering performance using requestAnimationFrame.
104    * @constructor
105    */
106   function RafRenderingStats() {
107     this.recording_ = false;
108     this.frameTimes_ = [];
109   }
110
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));
116   }
117
118   RafRenderingStats.prototype.stop = function() {
119     this.recording_ = false;
120   }
121
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;
129   };
130
131   RafRenderingStats.prototype.recordFrameTime_ = function(timestamp) {
132     if (!this.recording_)
133       return;
134
135     this.frameTimes_.push(timestamp);
136     requestAnimationFrame(this.recordFrameTime_.bind(this));
137   };
138
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++;
145     }
146     return droppedFrameCount;
147   };
148
149   // This class scrolls a page from the top to the bottom a given number of
150   // times.
151   //
152   // Each full transit of the page is called a "pass."
153   //
154   // The page is scrolled down by a set of scroll gestures. These gestures
155   // correspond to a reading gesture on that platform.
156   //
157   // i.e. for TOTAL_ITERATIONS_ = 2, we do
158   // start_ -> startPass_ -> ...scrolling... -> onGestureComplete_ ->
159   //        -> startPass_ -> .. scrolling... -> onGestureComplete_ -> callback_
160   //
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;
165
166     this.TOTAL_ITERATIONS_ = 2;
167
168     this.callback_ = opt_callback;
169     this.isGmailTest_ = opt_isGmailTest;
170     this.iteration_ = 0;
171
172     this.results_ = []
173
174     if (this.isGmailTest_) {
175       gmonkey.load('2.0', function(api) {
176           self.start_(api.getScrollableElement());
177       });
178     } else {
179       if (document.readyState == 'complete')
180         this.start_();
181       else
182         window.addEventListener('load', function() { self.start_(); });
183     }
184   }
185
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));
191   };
192
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();
200
201     this.gesture_ = new SmoothScrollDownGesture(this.element_,
202                                                 this.isGmailTest_);
203     this.gesture_.start(this.onGestureComplete_.bind(this));
204   };
205
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;
213
214     var isPassComplete =
215         this.element_.scrollTop + clientHeight >= this.element_.scrollHeight;
216
217     if (!isPassComplete) {
218       this.gesture_.start(this.onGestureComplete_.bind(this));
219       return;
220     }
221
222     this.endPass_();
223
224     var isTestComplete = this.iteration_ >= this.TOTAL_ITERATIONS_;
225     if (!isTestComplete) {
226       this.startPass_();
227       return;
228     }
229
230     // Send results.
231     if (this.callback_)
232       this.callback_(this.results_);
233     else
234       console.log(this.results_);
235   };
236
237   ScrollTest.prototype.endPass_ = function() {
238     this.renderingStats_.stop();
239     this.results_.push(this.renderingStats_.getResult());
240     this.iteration_++;
241   };
242
243
244   window.__ScrollTest = ScrollTest;
245 })();