- add sources.
[platform/framework/web/crosswalk.git] / src / tools / playback_benchmark / playback.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 /**
6  * @fileoverview Playback agent.
7  */
8
9 var Benchmark = Benchmark || {};
10
11 /**
12  * Playback agent class.
13  * @param {Object} data Test data.
14  * @constructor
15  */
16 Benchmark.Agent = function(data) {
17   this.timeline = data.timeline;
18   this.timelinePosition = 0;
19   this.steps = data.steps;
20   this.stepsPosition = 0;
21   this.randoms = data.randoms;
22   this.randomsPosition = 0;
23   this.ticks = data.ticks;
24   this.ticksPosition = 0;
25   this.delayedScriptElements = {};
26   this.callStackDepth = 0;
27   document.cookie = data.cookie;
28   if (window.innerWidth != data.width || window.innerHeight != data.height) {
29     Benchmark.die('Wrong window size: ' +
30                   window.innerWidth + 'x' + window.innerHeight +
31                   ' instead of ' + data.width + 'x' + data.height);
32   }
33   this.startTime = Benchmark.originals.Date.now();
34 };
35
36 /**
37  * Returns current timeline event.
38  * @return {Object} Event.
39  */
40 Benchmark.Agent.prototype.getCurrentEvent = function() {
41   return this.timeline[this.timelinePosition];
42 };
43
44 /**
45  * Returns next recorded event in timeline. If event is the last event in
46  * timeline, posts test results to driver.
47  * @param {Object} event Event that actually happened, should correspond to
48  * the recorded one (used for debug only).
49  * @return {Object} Recorded event from timeline.
50  */
51 Benchmark.Agent.prototype.getNextEvent = function(event) {
52   var recordedEvent = this.getCurrentEvent();
53   this.ensureEqual(event, recordedEvent);
54   if (event.type == 'random' || event.type == 'ticks') {
55     recordedEvent.count -= 1;
56     if (recordedEvent.count == 0) {
57       this.timelinePosition += 1;
58     }
59   } else {
60     this.timelinePosition += 1;
61   }
62   if (this.timelinePosition == this.steps[this.stepsPosition][1]) {
63     var score = Benchmark.originals.Date.now() - this.startTime;
64     Benchmark.reportScore(score);
65   }
66   return recordedEvent;
67 };
68
69 /**
70  * Checks if two events can be considered equal. Throws exception if events
71  * differ.
72  * @param {Object} event Event that actually happened.
73  * @param {Object} recordedEvent Event taken from timeline.
74  */
75 Benchmark.Agent.prototype.ensureEqual = function(event, recordedEvent) {
76   var equal = false;
77   if (event.type == recordedEvent.type &&
78       event.type in Benchmark.eventPropertiesMap) {
79     equal = true;
80     var properties = Benchmark.eventPropertiesMap[event.type];
81     for (var i = 0; i < properties.length && equal; ++i)
82       if (event[properties[i]] != recordedEvent[properties[i]])
83         equal = false;
84   }
85   if (!equal) {
86     Benchmark.die('unexpected event: ' + JSON.stringify(event) +
87                   ' instead of ' + JSON.stringify(recordedEvent));
88   }
89 };
90
91 /**
92  * Gets next event from timeline and returns its identifier.
93  * @param {Object} event Object with event information.
94  * @return {number} Event identifier.
95  */
96 Benchmark.Agent.prototype.createAsyncEvent = function(event) {
97   return this.getNextEvent(event).id;
98 };
99
100 /**
101  * Stores callback to be invoked according to timeline order.
102  * @param {number} eventId 'Parent' event identifier.
103  * @param {function} callback Callback.
104  */
105 Benchmark.Agent.prototype.fireAsyncEvent = function(eventId, callback) {
106   var event = this.timeline[eventId];
107   if (!event.callbackReference) return;
108   this.timeline[event.callbackReference].callback = callback;
109   this.fireSome();
110 };
111
112 /**
113  * Ensures that things are happening according to recorded timeline.
114  * @param {number} eventId Identifier of cancelled event.
115  */
116 Benchmark.Agent.prototype.cancelAsyncEvent = function(eventId) {
117   this.getNextEvent({type: 'cancel', reference: eventId});
118 };
119
120 /**
121  * Checks if script isn't going to be executed too early and delays script
122  * execution if necessary.
123  * @param {number} scriptId Unique script identifier.
124  * @param {HTMLElement} doc Document element.
125  * @param {boolean} inlined Indicates whether script is a text block in the page
126  * or resides in a separate file.
127  * @param {string} src Script url (if script is not inlined).
128  */
129 Benchmark.Agent.prototype.readyToExecuteScript = function(scriptId, doc,
130                                                           inlined, src) {
131   var event = this.getCurrentEvent();
132   if (event.type == 'willExecuteScript' && event.scriptId == scriptId) {
133     this.timelinePosition += 1;
134     return true;
135   }
136   var element;
137   var elements = doc.getElementsByTagName('script');
138   for (var i = 0, el; (el = elements[i]) && !element; ++i) {
139     if (inlined) {
140       if (el.src) continue;
141       var text = el.textContent;
142       if (scriptId == text.substring(2, text.indexOf("*/")))
143         element = elements[i];
144     } else {
145       if (!el.src) continue;
146       if (el.src.indexOf(src) != -1 || src.indexOf(el.src) != -1) {
147         element = el;
148       }
149     }
150   }
151   if (!element) {
152     Benchmark.die('script element not found', scriptId, src);
153   }
154   for (var el2 = element; el2; el2 = el2.parentElement) {
155     if (el2.onload) {
156       console.log('found', el2);
157     }
158   }
159   this.delayedScriptElements[scriptId] = element;
160   return false;
161 };
162
163 /**
164  * Ensures that things are happening according to recorded timeline.
165  * @param {Object} event Object with event information.
166  */
167 Benchmark.Agent.prototype.didExecuteScript = function(scriptId ) {
168   this.getNextEvent({type: 'didExecuteScript', scriptId: scriptId});
169   this.fireSome();
170 };
171
172 /**
173  * Invokes async events' callbacks according to timeline order.
174  */
175 Benchmark.Agent.prototype.fireSome = function() {
176   while (this.timelinePosition < this.timeline.length) {
177     var event = this.getCurrentEvent();
178     if (event.type == 'willFire') {
179       if(!event.callback) break;
180       this.timelinePosition += 1;
181       this.callStackDepth += 1;
182       event.callback();
183       this.callStackDepth -= 1;
184       this.getNextEvent({type: 'didFire', reference: event.reference});
185     } else if (event.type == 'willExecuteScript') {
186       if (event.scriptId in this.delayedScriptElements) {
187         var element = this.delayedScriptElements[event.scriptId];
188         var parent = element.parentElement;
189         var cloneElement = element.cloneNode();
190         delete this.delayedScriptElements[event.scriptId];
191         parent.replaceChild(cloneElement, element);
192       }
193       break;
194     } else if (this.callStackDepth > 0) {
195       break;
196     } else {
197       Benchmark.die('unexpected event in fireSome:' + JSON.stringify(event));
198     }
199   }
200 };
201
202 /**
203  * Returns recorded random.
204  * @return {number} Recorded random.
205  */
206 Benchmark.Agent.prototype.random = function() {
207   this.getNextEvent({type: 'random'});
208   return this.randoms[this.randomsPosition++];
209 };
210
211 /**
212  * Returns recorded ticks.
213  * @return {number} Recorded ticks.
214  */
215 Benchmark.Agent.prototype.dateNow = function(event) {
216   this.getNextEvent({type: 'ticks'});
217   return this.ticks[this.ticksPosition++];
218 };
219
220 /**
221  * Event type -> property list mapping used for matching events.
222  * @const
223  */
224 Benchmark.eventPropertiesMap = {
225   'timeout': ['timeout'],
226   'request': ['url'],
227   'addEventListener': ['eventType'],
228   'script load': ['src'],
229   'willExecuteScript': ['scriptId'],
230   'didExecuteScript': ['scriptId'],
231   'willFire': ['reference'],
232   'didFire': ['reference'],
233   'cancel': ['reference'],
234   'random': [],
235   'ticks': []
236 };
237
238 /**
239  * Agent used by native window functions wrappers.
240  */
241 Benchmark.agent = new Benchmark.Agent(Benchmark.data);
242
243 /**
244  * Playback flag.
245  * @const
246  */
247 Benchmark.playback = true;
248
249 Benchmark.reportScore = function(score) {
250   Benchmark.score = score;
251 };
252
253 Benchmark.originals.addEventListenerToWindow.call(
254     window, 'message', function(event) {
255       if (Benchmark.score) {
256         event.source.postMessage(Benchmark.score, event.origin);
257       }
258     }, false);