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