| OLD | NEW |
| (Empty) |
| 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 }; | |
| OLD | NEW |