Roll src/third_party/WebKit d9c6159:8139f33 (svn 201974:201975)
[chromium-blink-merge.git] / chrome / test / data / webui / mock_timer.js
blob37f5f6cfe130bb49606e90a2f4357270bd1d0dcb
1 // Copyright 2013 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 /**
6  * Overrides timeout and interval callbacks to mock timing behavior.
7  * @constructor
8  */
9 function MockTimer() {
10   /**
11    * Default versions of the timing functions.
12    * @type {Object<string, !Function>}
13    * @private
14    */
15   this.originals_ = [];
17   /**
18    * Key to assign on the next creation of a scheduled timer. Each call to
19    * setTimeout or setInterval returns a unique key that can be used for
20    * clearing the timer.
21    * @type {number}
22    * @private
23    */
24   this.nextTimerKey_ = 1;
26   /**
27    * Details for active timers.
28    * @type {Array<{callback: Function,
29    *                delay: number,
30    *                key: number,
31    *                repeats: boolean}>}
32    * @private
33    */
34   this.timers_ =  [];
36   /**
37    * List of scheduled tasks.
38    * @type {Array<{when: number, key: number}>}
39    * @private
40    */
41   this.schedule_ = [];
43   /**
44    * Virtual elapsed time in milliseconds.
45    * @type {number}
46    * @private
47    */
48   this.now_ = 0;
50   /**
51    * Used to control when scheduled callbacks fire.  Calling the 'tick' method
52    * inflates this parameter and triggers callbacks.
53    * @type {number}
54    * @private
55    */
56   this.until_ = 0;
59 MockTimer.prototype = {
60   /**
61    * Replaces built-in functions for scheduled callbacks.
62    */
63   install: function() {
64     this.replace_('setTimeout', this.setTimeout_.bind(this));
65     this.replace_('clearTimeout', this.clearTimeout_.bind(this));
66     this.replace_('setInterval', this.setInterval_.bind(this));
67     this.replace_('clearInterval', this.clearInterval_.bind(this));
68   },
70   /**
71    * Restores default behavior for scheduling callbacks.
72    */
73   uninstall: function() {
74     if (this.originals_) {
75       for (var key in this.originals_) {
76         window[key] = this.originals_[key];
77       }
78     }
79   },
81   /**
82    * Overrides a global function.
83    * @param {string} functionName The name of the function.
84    * @param {!Function} replacementFunction The function override.
85    * @private
86    */
87   replace_: function(functionName, replacementFunction) {
88     this.originals_[functionName] = window[functionName];
89     window[functionName] = replacementFunction;
90   },
92   /**
93    * Creates a virtual timer.
94    * @param {!Function} callback The callback function.
95    * @param {number} delayInMs The virtual delay in milliseconds.
96    * @param {boolean} repeats Indicates if the timer repeats.
97    * @return {number} Idetifier for the timer.
98    * @private
99    */
100   createTimer_: function(callback, delayInMs, repeats) {
101     var key = this.nextTimerKey_++;
102     var task = {
103       callback: callback,
104       delay: delayInMs,
105       key: key,
106       repeats: repeats
107     };
108     this.timers_[key] = task;
109     this.scheduleTask_(task);
110     return key;
111   },
113   /**
114    * Schedules a callback for execution after a virtual time delay. The tasks
115    * are sorted in descending order of time delay such that the next callback
116    * to fire is at the end of the list.
117    * @param {{callback: Function,
118    *          delay: number,
119    *          key: number,
120    *          repeats: boolean}} details The timer details.
121    * @private
122    */
123   scheduleTask_: function(details) {
124     var key = details.key;
125     var when = this.now_ + details.delay;
126     var index = this.schedule_.length;
127     while (index > 0 && this.schedule_[index - 1].when < when) {
128       index--;
129     }
130     this.schedule_.splice(index, 0, {when: when, key: key});
131   },
133   /**
134    * Override of window.setInterval.
135    * @param {!Function} callback The callback function.
136    * @param {number} intervalInMs The repeat interval.
137    * @private
138    */
139   setInterval_: function(callback, intervalInMs) {
140     return this.createTimer_(callback, intervalInMs, true);
141   },
143   /**
144    * Override of window.clearInterval.
145    * @param {number} key The ID of the interval timer returned from
146    *     setInterval.
147    * @private
148    */
149   clearInterval_: function(key) {
150     this.timers_[key] = undefined;
151   },
153   /**
154    * Override of window.setTimeout.
155    * @param {!Function} callback The callback function.
156    * @param {number} delayInMs The scheduled delay.
157    * @private
158    */
159   setTimeout_: function(callback, delayInMs) {
160     return this.createTimer_(callback, delayInMs, false);
161   },
163   /**
164    * Override of window.clearTimeout.
165    * @param {number} key The ID of the schedule timeout callback returned
166    *     from setTimeout.
167    * @private
168    */
169   clearTimeout_: function(key) {
170     this.timers_[key] = undefined;
171   },
173   /**
174    * Simulates passage of time, triggering any scheduled callbacks whose timer
175    * has elapsed.
176    * @param {number} elapsedMs The simulated elapsed time in milliseconds.
177    */
178   tick: function(elapsedMs) {
179     this.until_ += elapsedMs;
180     this.fireElapsedCallbacks_();
181   },
183   /**
184    * Triggers any callbacks that should have fired based in the simulated
185    * timing.
186    * @private
187    */
188   fireElapsedCallbacks_: function() {
189     while (this.schedule_.length > 0) {
190       var when = this.schedule_[this.schedule_.length - 1].when;
191       if (when > this.until_)
192         break;
194       var task = this.schedule_.pop();
195       var details = this.timers_[task.key];
196       if (!details)
197         continue;  // Cancelled task.
199       this.now_ = when;
200       details.callback.apply(window);
201       if (details.repeats)
202         this.scheduleTask_(details);
203       else
204         this.clearTimeout_(details.key);
205     }
206     this.now_ = this.until_;
207   },