- add sources.
[platform/framework/web/crosswalk.git] / src / tools / playback_benchmark / common.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 Classes and functions used during recording and playback.
7  */
8
9 var Benchmark = Benchmark || {};
10
11 Benchmark.functionList = [
12   ['setTimeout', 'setTimeout'],
13   ['clearTimeout', 'clearTimeout'],
14   ['setInterval', 'setInterval'],
15   ['clearInterval', 'clearInterval'],
16   ['XMLHttpRequest', 'XMLHttpRequest'],
17   ['addEventListenerToWindow', 'addEventListener'],
18   ['addEventListenerToNode', 'addEventListener', ['Node', 'prototype']],
19   ['removeEventListenerFromNode', 'removeEventListener', ['Node', 'prototype']],
20   ['addEventListenerToXHR', 'addEventListener',
21    ['XMLHttpRequest', 'prototype']],
22   ['random', 'random', ['Math']],
23   ['Date', 'Date'],
24   ['documentWriteln', 'writeln', ['document']],
25   ['documentWrite', 'write', ['document']]
26 ];
27
28 Benchmark.timeoutMapping = [];
29
30 Benchmark.ignoredListeners = ['mousemove', 'mouseover', 'mouseout'];
31
32 Benchmark.originals = {};
33
34 Benchmark.overrides = {
35   setTimeout: function(callback, timeout) {
36     var event = {type: 'timeout', timeout: timeout};
37     var eventId = Benchmark.agent.createAsyncEvent(event);
38     var timerId = Benchmark.originals.setTimeout.call(this, function() {
39       Benchmark.agent.fireAsyncEvent(eventId, callback);
40     }, Benchmark.playback ? 0 : timeout);
41     Benchmark.timeoutMapping[timerId] = eventId;
42     return timerId;
43   },
44
45   clearTimeout: function(timerId) {
46     var eventId = Benchmark.timeoutMapping[timerId];
47     if (eventId == undefined) return;
48     Benchmark.agent.cancelAsyncEvent(eventId);
49     Benchmark.originals.clearTimeout.call(this, timerId);
50   },
51
52   setInterval: function(callback, timeout) {
53     console.warn('setInterval');
54   },
55
56   clearInterval: function(timerId) {
57     console.warn('clearInterval');
58   },
59
60   XMLHttpRequest: function() {
61     return new Benchmark.XMLHttpRequestWrapper();
62   },
63
64   addEventListener: function(type, listener, useCapture, target, targetType,
65                              originalFunction) {
66     var event = {type: 'addEventListener', target: targetType, eventType: type};
67     var eventId = Benchmark.agent.createAsyncEvent(event);
68     listener.eventId = eventId;
69     listener.wrapper = function(e) {
70       Benchmark.agent.fireAsyncEvent(eventId, function() {
71         listener.call(target, e);
72       });
73     };
74     originalFunction.call(target, type, listener.wrapper, useCapture);
75   },
76
77   addEventListenerToWindow: function(type, listener, useCapture) {
78     if (Benchmark.ignoredListeners.indexOf(type) != -1) return;
79     Benchmark.overrides.addEventListener(
80         type, listener, useCapture, this, 'window',
81         Benchmark.originals.addEventListenerToWindow);
82   },
83
84   addEventListenerToNode: function(type, listener, useCapture) {
85     if (Benchmark.ignoredListeners.indexOf(type) != -1) return;
86     Benchmark.overrides.addEventListener(
87         type, listener, useCapture, this, 'node',
88         Benchmark.originals.addEventListenerToNode);
89   },
90
91   addEventListenerToXHR: function(type, listener, useCapture) {
92     Benchmark.overrides.addEventListener(
93         type, listener, useCapture, this, 'xhr',
94         Benchmark.originals.addEventListenerToXHR);
95   },
96
97   removeEventListener: function(type, listener, useCapture, target,
98                                 originalFunction) {
99     Benchmark.agent.cancelAsyncEvent(listener.eventId);
100     originalFunction.call(target, listener.wrapper, useCapture);
101   },
102
103   removeEventListenerFromWindow: function(type, listener, useCapture) {
104     removeEventListener(type, listener, useCapture, this,
105                         Benchmark.originals.removeEventListenerFromWindow);
106   },
107
108   removeEventListenerFromNode: function(type, listener, useCapture) {
109     removeEventListener(type, listener, useCapture, this,
110         Benchmark.originals.removeEventListenerFromNode);
111   },
112
113   removeEventListenerFromXHR: function(type, listener, useCapture) {
114     removeEventListener(type, listener, useCapture, this,
115         Benchmark.originals.removeEventListenerFromXHR);
116   },
117
118   random: function() {
119     return Benchmark.agent.random();
120   },
121
122   Date: function() {
123     var a = arguments;
124     var D = Benchmark.originals.Date, d;
125     switch(a.length) {
126       case 0: d = new D(Benchmark.agent.dateNow()); break;
127       case 1: d = new D(a[0]); break;
128       case 2: d = new D(a[0], a[1]); break;
129       case 3: d = new D(a[0], a[1], a[2]); break;
130       default: Benchmark.die('window.Date', arguments);
131     }
132     d.getTimezoneOffset = function() { return -240; };
133     return d;
134   },
135
136   dateNow: function() {
137     return Benchmark.agent.dateNow();
138   },
139
140   documentWriteln: function() {
141     console.warn('writeln');
142   },
143
144   documentWrite: function() {
145     console.warn('write');
146   }
147 };
148
149 /**
150  * Replaces window functions specified by Benchmark.functionList with overrides
151  * and optionally saves original functions to Benchmark.originals.
152  * @param {Object} wnd Window object.
153  * @param {boolean} storeOriginals When true, original functions are saved to
154  * Benchmark.originals.
155  */
156 Benchmark.installOverrides = function(wnd, storeOriginals) {
157   // Substitute window functions with overrides.
158   for (var i = 0; i < Benchmark.functionList.length; ++i) {
159     var info = Benchmark.functionList[i], object = wnd;
160     var propertyName = info[1], pathToProperty = info[2];
161     if (pathToProperty)
162       for (var j = 0; j < pathToProperty.length; ++j)
163         object = object[pathToProperty[j]];
164     if (storeOriginals)
165       Benchmark.originals[info[0]] = object[propertyName];
166     object[propertyName] = Benchmark.overrides[info[0]];
167   }
168   wnd.__defineSetter__('onload', function() {
169     console.warn('window.onload setter')}
170   );
171
172   // Substitute window functions of static frames when DOM content is loaded.
173   Benchmark.originals.addEventListenerToWindow.call(wnd, 'DOMContentLoaded',
174                                                     function() {
175     var frames = document.getElementsByTagName('iframe');
176     for (var i = 0, frame; frame = frames[i]; ++i) {
177       Benchmark.installOverrides(frame.contentWindow);
178     }
179   }, true);
180
181   // Substitute window functions of dynamically added frames.
182   Benchmark.originals.addEventListenerToWindow.call(
183       wnd, 'DOMNodeInsertedIntoDocument', function(e) {
184         if (e.target.tagName && e.target.tagName.toLowerCase() != 'iframe')
185           return;
186         if (e.target.contentWindow)
187           Benchmark.installOverrides(e.target.contentWindow);
188       }, true);
189 };
190
191 // Install overrides on top window.
192 Benchmark.installOverrides(window, true);
193
194 /**
195  * window.XMLHttpRequest wrapper. Notifies Benchmark.agent when request is
196  * opened, aborted, and when it's ready state changes to DONE.
197  * @constructor
198  */
199 Benchmark.XMLHttpRequestWrapper = function() {
200   this.request = new Benchmark.originals.XMLHttpRequest();
201   this.wrapperReadyState = 0;
202 };
203
204 // Create XMLHttpRequestWrapper functions and property accessors using original
205 // ones.
206 (function() {
207   var request = new Benchmark.originals.XMLHttpRequest();
208   for (var property in request) {
209     if (property === 'channel') continue; // Quick fix for FF.
210     if (typeof(request[property]) == 'function') {
211       (function(property) {
212         var f = Benchmark.originals.XMLHttpRequest.prototype[property];
213         Benchmark.XMLHttpRequestWrapper.prototype[property] = function() {
214           f.apply(this.request, arguments);
215         };
216       })(property);
217     } else {
218       (function(property) {
219         Benchmark.XMLHttpRequestWrapper.prototype.__defineGetter__(property,
220             function() { return this.request[property]; });
221         Benchmark.XMLHttpRequestWrapper.prototype.__defineSetter__(property,
222             function(value) {
223               this.request[property] = value;
224             });
225
226       })(property);
227     }
228   }
229 })();
230
231 // Define onreadystatechange getter.
232 Benchmark.XMLHttpRequestWrapper.prototype.__defineGetter__('onreadystatechange',
233     function() { return this.clientOnReadyStateChange; });
234
235 // Define onreadystatechange setter.
236 Benchmark.XMLHttpRequestWrapper.prototype.__defineSetter__('onreadystatechange',
237     function(value) { this.clientOnReadyStateChange = value; });
238
239 Benchmark.XMLHttpRequestWrapper.prototype.__defineGetter__('readyState',
240     function() { return this.wrapperReadyState; });
241
242 Benchmark.XMLHttpRequestWrapper.prototype.__defineSetter__('readyState',
243     function() {});
244
245
246 /**
247  * Wrapper for XMLHttpRequest.open.
248  */
249 Benchmark.XMLHttpRequestWrapper.prototype.open = function() {
250   var url = Benchmark.extractURL(arguments[1]);
251   var event = {type: 'request', method: arguments[0], url: url};
252   this.eventId = Benchmark.agent.createAsyncEvent(event);
253
254   var request = this.request;
255   var requestWrapper = this;
256   Benchmark.originals.XMLHttpRequest.prototype.open.apply(request, arguments);
257   request.onreadystatechange = function() {
258     if (this.readyState != 4 || requestWrapper.cancelled) return;
259     var callback = requestWrapper.clientOnReadyStateChange || function() {};
260     Benchmark.agent.fireAsyncEvent(requestWrapper.eventId, function() {
261       requestWrapper.wrapperReadyState = 4;
262       callback.call(request);
263     });
264   }
265 };
266
267 /**
268  * Wrapper for XMLHttpRequest.abort.
269  */
270 Benchmark.XMLHttpRequestWrapper.prototype.abort = function() {
271   this.cancelled = true;
272   Benchmark.originals.XMLHttpRequest.prototype.abort.apply(
273       this.request, arguments);
274   Benchmark.agent.cancelAsyncEvent(this.eventId);
275 };
276
277 /**
278  * Driver url for reporting results.
279  * @const {string}
280  */
281 Benchmark.DRIVER_URL = '/benchmark/';
282
283 /**
284  * Posts request as json to Benchmark.DRIVER_URL.
285  * @param {Object} request Request to post.
286  */
287 Benchmark.post = function(request, async) {
288   if (async === undefined) async = true;
289   var xmlHttpRequest = new Benchmark.originals.XMLHttpRequest();
290   xmlHttpRequest.open("POST", Benchmark.DRIVER_URL, async);
291   xmlHttpRequest.setRequestHeader("Content-type", "application/json");
292   xmlHttpRequest.send(JSON.stringify(request));
293 };
294
295 /**
296  * Extracts url string.
297  * @param {(string|Object)} url Object or string representing url.
298  * @return {string} Extracted url.
299  */
300 Benchmark.extractURL = function(url) {
301   if (typeof(url) == 'string') return url;
302   return url.nI || url.G || '';
303 };
304
305
306 /**
307  * Logs error message to console and throws an exception.
308  * @param {string} message Error message
309  */
310 Benchmark.die = function(message) {
311   // Debugging stuff.
312   var position = top.Benchmark.playback ? top.Benchmark.agent.timelinePosition :
313                  top.Benchmark.agent.timeline.length;
314   message = message + ' at position ' + position;
315   console.error(message);
316   Benchmark.post({error: message});
317   console.log(Benchmark.originals.setTimeout.call(window, function() {}, 9999));
318   try { (0)() } catch(ex) { console.error(ex.stack); }
319   throw message;
320 };