1 /* 2 * Copyright (C) 2012 Google Inc. All rights reserved. 3 * 4 * Redistribution and use in source and binary forms, with or without 5 * modification, are permitted provided that the following conditions are 6 * met: 7 * 8 * * Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * * Redistributions in binary form must reproduce the above 11 * copyright notice, this list of conditions and the following disclaimer 12 * in the documentation and/or other materials provided with the 13 * distribution. 14 * * Neither the name of Google Inc. nor the names of its 15 * contributors may be used to endorse or promote products derived from 16 * this software without specific prior written permission. 17 * 18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 19 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 20 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 21 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 22 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 23 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 24 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 25 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 28 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 */ 30 31 /** 32 * @constructor 33 * @extends {WebInspector.TargetAwareObject} 34 * @param {!WebInspector.Target} target 35 */ 36 WebInspector.TimelineModel = function(target) 37 { 38 WebInspector.TargetAwareObject.call(this, target); 39 this._filters = []; 40 } 41 42 WebInspector.TimelineModel.RecordType = { 43 Root: "Root", 44 Program: "Program", 45 EventDispatch: "EventDispatch", 46 47 GPUTask: "GPUTask", 48 49 RequestMainThreadFrame: "RequestMainThreadFrame", 50 BeginFrame: "BeginFrame", 51 ActivateLayerTree: "ActivateLayerTree", 52 DrawFrame: "DrawFrame", 53 ScheduleStyleRecalculation: "ScheduleStyleRecalculation", 54 RecalculateStyles: "RecalculateStyles", 55 InvalidateLayout: "InvalidateLayout", 56 Layout: "Layout", 57 UpdateLayerTree: "UpdateLayerTree", 58 PaintSetup: "PaintSetup", 59 Paint: "Paint", 60 Rasterize: "Rasterize", 61 ScrollLayer: "ScrollLayer", 62 DecodeImage: "DecodeImage", 63 ResizeImage: "ResizeImage", 64 CompositeLayers: "CompositeLayers", 65 66 ParseHTML: "ParseHTML", 67 68 TimerInstall: "TimerInstall", 69 TimerRemove: "TimerRemove", 70 TimerFire: "TimerFire", 71 72 XHRReadyStateChange: "XHRReadyStateChange", 73 XHRLoad: "XHRLoad", 74 EvaluateScript: "EvaluateScript", 75 76 MarkLoad: "MarkLoad", 77 MarkDOMContent: "MarkDOMContent", 78 MarkFirstPaint: "MarkFirstPaint", 79 80 TimeStamp: "TimeStamp", 81 ConsoleTime: "ConsoleTime", 82 83 ResourceSendRequest: "ResourceSendRequest", 84 ResourceReceiveResponse: "ResourceReceiveResponse", 85 ResourceReceivedData: "ResourceReceivedData", 86 ResourceFinish: "ResourceFinish", 87 88 FunctionCall: "FunctionCall", 89 GCEvent: "GCEvent", 90 JSFrame: "JSFrame", 91 92 UpdateCounters: "UpdateCounters", 93 94 RequestAnimationFrame: "RequestAnimationFrame", 95 CancelAnimationFrame: "CancelAnimationFrame", 96 FireAnimationFrame: "FireAnimationFrame", 97 98 WebSocketCreate : "WebSocketCreate", 99 WebSocketSendHandshakeRequest : "WebSocketSendHandshakeRequest", 100 WebSocketReceiveHandshakeResponse : "WebSocketReceiveHandshakeResponse", 101 WebSocketDestroy : "WebSocketDestroy", 102 103 EmbedderCallback : "EmbedderCallback", 104 } 105 106 WebInspector.TimelineModel.Events = { 107 RecordAdded: "RecordAdded", 108 RecordsCleared: "RecordsCleared", 109 RecordingStarted: "RecordingStarted", 110 RecordingStopped: "RecordingStopped", 111 RecordingProgress: "RecordingProgress", 112 RecordFilterChanged: "RecordFilterChanged" 113 } 114 115 /** 116 * @param {!Array.<!WebInspector.TimelineModel.Record>} recordsArray 117 * @param {?function(!WebInspector.TimelineModel.Record)|?function(!WebInspector.TimelineModel.Record,number)} preOrderCallback 118 * @param {function(!WebInspector.TimelineModel.Record)|function(!WebInspector.TimelineModel.Record,number)=} postOrderCallback 119 * @return {boolean} 120 */ 121 WebInspector.TimelineModel.forAllRecords = function(recordsArray, preOrderCallback, postOrderCallback) 122 { 123 /** 124 * @param {!Array.<!WebInspector.TimelineModel.Record>} records 125 * @param {number} depth 126 * @return {boolean} 127 */ 128 function processRecords(records, depth) 129 { 130 for (var i = 0; i < records.length; ++i) { 131 var record = records[i]; 132 if (preOrderCallback && preOrderCallback(record, depth)) 133 return true; 134 if (processRecords(record.children(), depth + 1)) 135 return true; 136 if (postOrderCallback && postOrderCallback(record, depth)) 137 return true; 138 } 139 return false; 140 } 141 return processRecords(recordsArray, 0); 142 } 143 144 WebInspector.TimelineModel.prototype = { 145 /** 146 * @param {boolean} captureStacks 147 * @param {boolean} captureMemory 148 * @param {boolean} capturePictures 149 */ 150 startRecording: function(captureStacks, captureMemory, capturePictures) 151 { 152 }, 153 154 stopRecording: function() 155 { 156 }, 157 158 /** 159 * @return {boolean} 160 */ 161 loadedFromFile: function() 162 { 163 return false; 164 }, 165 166 /** 167 * @param {?function(!WebInspector.TimelineModel.Record)|?function(!WebInspector.TimelineModel.Record,number)} preOrderCallback 168 * @param {function(!WebInspector.TimelineModel.Record)|function(!WebInspector.TimelineModel.Record,number)=} postOrderCallback 169 */ 170 forAllRecords: function(preOrderCallback, postOrderCallback) 171 { 172 WebInspector.TimelineModel.forAllRecords(this._records, preOrderCallback, postOrderCallback); 173 }, 174 175 /** 176 * @param {!WebInspector.TimelineModel.Filter} filter 177 */ 178 addFilter: function(filter) 179 { 180 this._filters.push(filter); 181 filter._model = this; 182 }, 183 184 /** 185 * @param {function(!WebInspector.TimelineModel.Record)|function(!WebInspector.TimelineModel.Record,number)} callback 186 */ 187 forAllFilteredRecords: function(callback) 188 { 189 /** 190 * @param {!WebInspector.TimelineModel.Record} record 191 * @param {number} depth 192 * @this {WebInspector.TimelineModel} 193 * @return {boolean} 194 */ 195 function processRecord(record, depth) 196 { 197 var visible = this.isVisible(record); 198 if (visible) { 199 if (callback(record, depth)) 200 return true; 201 } 202 203 for (var i = 0; i < record.children().length; ++i) { 204 if (processRecord.call(this, record.children()[i], visible ? depth + 1 : depth)) 205 return true; 206 } 207 return false; 208 } 209 210 for (var i = 0; i < this._records.length; ++i) 211 processRecord.call(this, this._records[i], 0); 212 }, 213 214 /** 215 * @param {!WebInspector.TimelineModel.Record} record 216 * @return {boolean} 217 */ 218 isVisible: function(record) 219 { 220 for (var i = 0; i < this._filters.length; ++i) { 221 if (!this._filters[i].accept(record)) 222 return false; 223 } 224 return true; 225 }, 226 227 _filterChanged: function() 228 { 229 this.dispatchEventToListeners(WebInspector.TimelineModel.Events.RecordFilterChanged); 230 }, 231 232 /** 233 * @return {!Array.<!WebInspector.TimelineModel.Record>} 234 */ 235 records: function() 236 { 237 return this._records; 238 }, 239 240 /** 241 * @param {!Blob} file 242 * @param {!WebInspector.Progress} progress 243 */ 244 loadFromFile: function(file, progress) 245 { 246 throw new Error("Not implemented"); 247 }, 248 249 /** 250 * @param {string} url 251 * @param {!WebInspector.Progress} progress 252 */ 253 loadFromURL: function(url, progress) 254 { 255 throw new Error("Not implemented"); 256 }, 257 258 saveToFile: function() 259 { 260 throw new Error("Not implemented"); 261 }, 262 263 reset: function() 264 { 265 this._loadedFromFile = false; 266 this._records = []; 267 this._minimumRecordTime = 0; 268 this._maximumRecordTime = 0; 269 /** @type {!Array.<!WebInspector.TimelineModel.Record>} */ 270 this._mainThreadTasks = []; 271 /** @type {!Array.<!WebInspector.TimelineModel.Record>} */ 272 this._gpuThreadTasks = []; 273 /** @type {!Array.<!WebInspector.TimelineModel.Record>} */ 274 this._eventDividerRecords = []; 275 this.dispatchEventToListeners(WebInspector.TimelineModel.Events.RecordsCleared); 276 }, 277 278 /** 279 * @return {number} 280 */ 281 minimumRecordTime: function() 282 { 283 return this._minimumRecordTime; 284 }, 285 286 /** 287 * @return {number} 288 */ 289 maximumRecordTime: function() 290 { 291 return this._maximumRecordTime; 292 }, 293 294 /** 295 * @param {!WebInspector.TimelineModel.Record} record 296 */ 297 _updateBoundaries: function(record) 298 { 299 var startTime = record.startTime(); 300 var endTime = record.endTime(); 301 302 if (!this._minimumRecordTime || startTime < this._minimumRecordTime) 303 this._minimumRecordTime = startTime; 304 if (endTime > this._maximumRecordTime) 305 this._maximumRecordTime = endTime; 306 }, 307 308 /** 309 * @return {!Array.<!WebInspector.TimelineModel.Record>} 310 */ 311 mainThreadTasks: function() 312 { 313 return this._mainThreadTasks; 314 }, 315 316 /** 317 * @return {!Array.<!WebInspector.TimelineModel.Record>} 318 */ 319 gpuThreadTasks: function() 320 { 321 return this._gpuThreadTasks; 322 }, 323 324 /** 325 * @return {!Array.<!WebInspector.TimelineModel.Record>} 326 */ 327 eventDividerRecords: function() 328 { 329 return this._eventDividerRecords; 330 }, 331 332 __proto__: WebInspector.TargetAwareObject.prototype 333 } 334 335 /** 336 * @interface 337 */ 338 WebInspector.TimelineModel.Record = function() 339 { 340 } 341 342 WebInspector.TimelineModel.Record.prototype = { 343 /** 344 * @return {?Array.<!ConsoleAgent.CallFrame>} 345 */ 346 callSiteStackTrace: function() { }, 347 348 /** 349 * @return {?WebInspector.TimelineModel.Record} 350 */ 351 initiator: function() { }, 352 353 /** 354 * @return {!WebInspector.Target} 355 */ 356 target: function() { }, 357 358 /** 359 * @return {number} 360 */ 361 selfTime: function() { }, 362 363 /** 364 * @return {!Array.<!WebInspector.TimelineModel.Record>} 365 */ 366 children: function() { }, 367 368 /** 369 * @return {!WebInspector.TimelineCategory} 370 */ 371 category: function() { }, 372 373 /** 374 * @return {number} 375 */ 376 startTime: function() { }, 377 378 /** 379 * @return {string|undefined} 380 */ 381 thread: function() { }, 382 383 /** 384 * @return {number} 385 */ 386 endTime: function() { }, 387 388 /** 389 * @param {number} endTime 390 */ 391 setEndTime: function(endTime) { }, 392 393 /** 394 * @return {!Object} 395 */ 396 data: function() { }, 397 398 /** 399 * @return {string} 400 */ 401 type: function() { }, 402 403 /** 404 * @return {string} 405 */ 406 frameId: function() { }, 407 408 /** 409 * @return {?Array.<!ConsoleAgent.CallFrame>} 410 */ 411 stackTrace: function() { }, 412 413 /** 414 * @param {string} key 415 * @return {?Object} 416 */ 417 getUserObject: function(key) { }, 418 419 /** 420 * @param {string} key 421 * @param {?Object|undefined} value 422 */ 423 setUserObject: function(key, value) { }, 424 425 /** 426 * @return {!Object.<string, number>} 427 */ 428 aggregatedStats: function() { }, 429 430 /** 431 * @return {?Array.<string>} 432 */ 433 warnings: function() { } 434 } 435 436 /** 437 * @constructor 438 */ 439 WebInspector.TimelineModel.Filter = function() 440 { 441 /** @type {!WebInspector.TimelineModel} */ 442 this._model; 443 } 444 445 WebInspector.TimelineModel.Filter.prototype = { 446 /** 447 * @param {!WebInspector.TimelineModel.Record} record 448 * @return {boolean} 449 */ 450 accept: function(record) 451 { 452 return true; 453 }, 454 455 notifyFilterChanged: function() 456 { 457 this._model._filterChanged(); 458 } 459 } 460 461 /** 462 * @constructor 463 */ 464 WebInspector.TimelineMergingRecordBuffer = function() 465 { 466 this._backgroundRecordsBuffer = []; 467 } 468 469 /** 470 * @constructor 471 */ 472 WebInspector.TimelineMergingRecordBuffer.prototype = { 473 /** 474 * @param {string} thread 475 * @param {!Array.<!WebInspector.TimelineModel.Record>} records 476 * @return {!Array.<!WebInspector.TimelineModel.Record>} 477 */ 478 process: function(thread, records) 479 { 480 if (thread) { 481 this._backgroundRecordsBuffer = this._backgroundRecordsBuffer.concat(records); 482 return []; 483 } 484 /** 485 * @param {!WebInspector.TimelineModel.Record} a 486 * @param {!WebInspector.TimelineModel.Record} b 487 */ 488 function recordTimestampComparator(a, b) 489 { 490 // Never return 0, as the merge function will squash identical entries. 491 return a.startTime() < b.startTime() ? -1 : 1; 492 } 493 var result = this._backgroundRecordsBuffer.mergeOrdered(records, recordTimestampComparator); 494 this._backgroundRecordsBuffer = []; 495 return result; 496 } 497 } 498