| OLD | NEW |
| (Empty) |
| 1 // Copyright 2014 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 * @constructor | |
| 7 * @param {!WebInspector.TracingManager} tracingManager | |
| 8 * @param {!WebInspector.TracingModel} tracingModel | |
| 9 * @param {!WebInspector.TimelineModel.Filter} recordFilter | |
| 10 * @extends {WebInspector.TimelineModel} | |
| 11 */ | |
| 12 WebInspector.TracingTimelineModel = function(tracingManager, tracingModel, recor
dFilter) | |
| 13 { | |
| 14 WebInspector.TimelineModel.call(this); | |
| 15 | |
| 16 this._tracingManager = tracingManager; | |
| 17 this._tracingModel = tracingModel; | |
| 18 this._recordFilter = recordFilter; | |
| 19 this._tracingManager.addEventListener(WebInspector.TracingManager.Events.Tra
cingStarted, this._onTracingStarted, this); | |
| 20 this._tracingManager.addEventListener(WebInspector.TracingManager.Events.Eve
ntsCollected, this._onEventsCollected, this); | |
| 21 this._tracingManager.addEventListener(WebInspector.TracingManager.Events.Tra
cingComplete, this._onTracingComplete, this); | |
| 22 this.reset(); | |
| 23 } | |
| 24 | |
| 25 WebInspector.TracingTimelineModel.RecordType = { | |
| 26 Program: "Program", | |
| 27 EventDispatch: "EventDispatch", | |
| 28 | |
| 29 GPUTask: "GPUTask", | |
| 30 | |
| 31 RequestMainThreadFrame: "RequestMainThreadFrame", | |
| 32 BeginFrame: "BeginFrame", | |
| 33 BeginMainThreadFrame: "BeginMainThreadFrame", | |
| 34 ActivateLayerTree: "ActivateLayerTree", | |
| 35 DrawFrame: "DrawFrame", | |
| 36 ScheduleStyleRecalculation: "ScheduleStyleRecalculation", | |
| 37 RecalculateStyles: "RecalculateStyles", | |
| 38 InvalidateLayout: "InvalidateLayout", | |
| 39 Layout: "Layout", | |
| 40 UpdateLayer: "UpdateLayer", | |
| 41 UpdateLayerTree: "UpdateLayerTree", | |
| 42 PaintSetup: "PaintSetup", | |
| 43 Paint: "Paint", | |
| 44 PaintImage: "PaintImage", | |
| 45 Rasterize: "Rasterize", | |
| 46 RasterTask: "RasterTask", | |
| 47 ScrollLayer: "ScrollLayer", | |
| 48 CompositeLayers: "CompositeLayers", | |
| 49 | |
| 50 StyleRecalcInvalidationTracking: "StyleRecalcInvalidationTracking", | |
| 51 LayoutInvalidationTracking: "LayoutInvalidationTracking", | |
| 52 LayerInvalidationTracking: "LayerInvalidationTracking", | |
| 53 PaintInvalidationTracking: "PaintInvalidationTracking", | |
| 54 | |
| 55 ParseHTML: "ParseHTML", | |
| 56 | |
| 57 TimerInstall: "TimerInstall", | |
| 58 TimerRemove: "TimerRemove", | |
| 59 TimerFire: "TimerFire", | |
| 60 | |
| 61 XHRReadyStateChange: "XHRReadyStateChange", | |
| 62 XHRLoad: "XHRLoad", | |
| 63 EvaluateScript: "EvaluateScript", | |
| 64 | |
| 65 MarkLoad: "MarkLoad", | |
| 66 MarkDOMContent: "MarkDOMContent", | |
| 67 MarkFirstPaint: "MarkFirstPaint", | |
| 68 | |
| 69 TimeStamp: "TimeStamp", | |
| 70 ConsoleTime: "ConsoleTime", | |
| 71 | |
| 72 ResourceSendRequest: "ResourceSendRequest", | |
| 73 ResourceReceiveResponse: "ResourceReceiveResponse", | |
| 74 ResourceReceivedData: "ResourceReceivedData", | |
| 75 ResourceFinish: "ResourceFinish", | |
| 76 | |
| 77 FunctionCall: "FunctionCall", | |
| 78 GCEvent: "GCEvent", | |
| 79 JSFrame: "JSFrame", | |
| 80 JSSample: "JSSample", | |
| 81 | |
| 82 UpdateCounters: "UpdateCounters", | |
| 83 | |
| 84 RequestAnimationFrame: "RequestAnimationFrame", | |
| 85 CancelAnimationFrame: "CancelAnimationFrame", | |
| 86 FireAnimationFrame: "FireAnimationFrame", | |
| 87 | |
| 88 WebSocketCreate : "WebSocketCreate", | |
| 89 WebSocketSendHandshakeRequest : "WebSocketSendHandshakeRequest", | |
| 90 WebSocketReceiveHandshakeResponse : "WebSocketReceiveHandshakeResponse", | |
| 91 WebSocketDestroy : "WebSocketDestroy", | |
| 92 | |
| 93 EmbedderCallback : "EmbedderCallback", | |
| 94 | |
| 95 CallStack: "CallStack", | |
| 96 SetLayerTreeId: "SetLayerTreeId", | |
| 97 TracingStartedInPage: "TracingStartedInPage", | |
| 98 TracingSessionIdForWorker: "TracingSessionIdForWorker", | |
| 99 | |
| 100 DecodeImage: "Decode Image", | |
| 101 ResizeImage: "Resize Image", | |
| 102 DrawLazyPixelRef: "Draw LazyPixelRef", | |
| 103 DecodeLazyPixelRef: "Decode LazyPixelRef", | |
| 104 | |
| 105 LazyPixelRef: "LazyPixelRef", | |
| 106 LayerTreeHostImplSnapshot: "cc::LayerTreeHostImpl", | |
| 107 PictureSnapshot: "cc::Picture", | |
| 108 | |
| 109 // CpuProfile is a virtual event created on frontend to support | |
| 110 // serialization of CPU Profiles within tracing timeline data. | |
| 111 CpuProfile: "CpuProfile" | |
| 112 }; | |
| 113 | |
| 114 /** | |
| 115 * @constructor | |
| 116 * @param {string} name | |
| 117 */ | |
| 118 WebInspector.TracingTimelineModel.VirtualThread = function(name) | |
| 119 { | |
| 120 this.name = name; | |
| 121 /** @type {!Array.<!WebInspector.TracingModel.Event>} */ | |
| 122 this.events = []; | |
| 123 /** @type {!Array.<!Array.<!WebInspector.TracingModel.Event>>} */ | |
| 124 this.asyncEvents = []; | |
| 125 } | |
| 126 | |
| 127 WebInspector.TracingTimelineModel.prototype = { | |
| 128 /** | |
| 129 * @param {boolean} captureCauses | |
| 130 * @param {boolean} enableJSSampling | |
| 131 * @param {boolean} captureMemory | |
| 132 * @param {boolean} capturePictures | |
| 133 */ | |
| 134 startRecording: function(captureCauses, enableJSSampling, captureMemory, cap
turePictures) | |
| 135 { | |
| 136 function disabledByDefault(category) | |
| 137 { | |
| 138 return "disabled-by-default-" + category; | |
| 139 } | |
| 140 var categoriesArray = [ | |
| 141 "-*", | |
| 142 disabledByDefault("devtools.timeline"), | |
| 143 disabledByDefault("devtools.timeline.frame"), | |
| 144 WebInspector.TracingModel.ConsoleEventCategory | |
| 145 ]; | |
| 146 if (captureCauses || enableJSSampling) | |
| 147 categoriesArray.push(disabledByDefault("devtools.timeline.stack")); | |
| 148 if (enableJSSampling) | |
| 149 this._startCpuProfilingOnAllTargets(); | |
| 150 if (captureCauses && Runtime.experiments.isEnabled("timelineInvalidation
Tracking")) | |
| 151 categoriesArray.push(disabledByDefault("devtools.timeline.invalidati
onTracking")); | |
| 152 if (capturePictures) { | |
| 153 categoriesArray = categoriesArray.concat([ | |
| 154 disabledByDefault("devtools.timeline.layers"), | |
| 155 disabledByDefault("devtools.timeline.picture"), | |
| 156 disabledByDefault("blink.graphics_context_annotations")]); | |
| 157 } | |
| 158 var categories = categoriesArray.join(","); | |
| 159 this._startRecordingWithCategories(categories); | |
| 160 }, | |
| 161 | |
| 162 stopRecording: function() | |
| 163 { | |
| 164 this._stopCallbackBarrier = new CallbackBarrier(); | |
| 165 this._stopProfilingOnAllTargets(); | |
| 166 this._tracingManager.stop(); | |
| 167 }, | |
| 168 | |
| 169 /** | |
| 170 * @param {!Array.<!WebInspector.TracingManager.EventPayload>} events | |
| 171 */ | |
| 172 setEventsForTest: function(events) | |
| 173 { | |
| 174 this._startCollectingTraceEvents(false); | |
| 175 this._tracingModel.addEvents(events); | |
| 176 this._onTracingComplete(); | |
| 177 }, | |
| 178 | |
| 179 _startCpuProfilingOnAllTargets: function() | |
| 180 { | |
| 181 this._profilingTargets = WebInspector.targetManager.targets(); | |
| 182 for (var i = 0; i < this._profilingTargets.length; ++i) { | |
| 183 var target = this._profilingTargets[i]; | |
| 184 this._configureCpuProfilerSamplingInterval(target); | |
| 185 target.profilerAgent().start(); | |
| 186 } | |
| 187 }, | |
| 188 | |
| 189 _stopProfilingOnAllTargets: function() | |
| 190 { | |
| 191 if (!this._profilingTargets) | |
| 192 return; | |
| 193 for (var i = 0; i < this._profilingTargets.length; ++i) { | |
| 194 var target = this._profilingTargets[i]; | |
| 195 target.profilerAgent().stop(this._stopCallbackBarrier.createCallback
(this._didStopRecordingJSSamples.bind(this, target))); | |
| 196 } | |
| 197 this._profilingTargets = null; | |
| 198 }, | |
| 199 | |
| 200 /** | |
| 201 * @param {!WebInspector.Target} target | |
| 202 */ | |
| 203 _configureCpuProfilerSamplingInterval: function(target) | |
| 204 { | |
| 205 var intervalUs = WebInspector.settings.highResolutionCpuProfiling.get()
? 100 : 1000; | |
| 206 target.profilerAgent().setSamplingInterval(intervalUs, didChangeInterval
); | |
| 207 | |
| 208 function didChangeInterval(error) | |
| 209 { | |
| 210 if (error) | |
| 211 WebInspector.console.error(error); | |
| 212 } | |
| 213 }, | |
| 214 | |
| 215 /** | |
| 216 * @param {string} categories | |
| 217 */ | |
| 218 _startRecordingWithCategories: function(categories) | |
| 219 { | |
| 220 this._tracingManager.start(categories, ""); | |
| 221 }, | |
| 222 | |
| 223 _onTracingStarted: function() | |
| 224 { | |
| 225 this._startCollectingTraceEvents(false); | |
| 226 }, | |
| 227 | |
| 228 /** | |
| 229 * @param {boolean} fromFile | |
| 230 */ | |
| 231 _startCollectingTraceEvents: function(fromFile) | |
| 232 { | |
| 233 this.reset(); | |
| 234 this._tracingModel.reset(); | |
| 235 this.dispatchEventToListeners(WebInspector.TimelineModel.Events.Recordin
gStarted, { fromFile: fromFile }); | |
| 236 }, | |
| 237 | |
| 238 /** | |
| 239 * @param {!WebInspector.Event} event | |
| 240 */ | |
| 241 _onEventsCollected: function(event) | |
| 242 { | |
| 243 var traceEvents = /** @type {!Array.<!WebInspector.TracingManager.EventP
ayload>} */ (event.data); | |
| 244 this._tracingModel.addEvents(traceEvents); | |
| 245 }, | |
| 246 | |
| 247 _onTracingComplete: function() | |
| 248 { | |
| 249 if (this._stopCallbackBarrier) { | |
| 250 this._stopCallbackBarrier.callWhenDone(this._didStopRecordingTraceEv
ents.bind(this)); | |
| 251 this._stopCallbackBarrier = null; | |
| 252 } else { | |
| 253 this._didStopRecordingTraceEvents(); | |
| 254 } | |
| 255 }, | |
| 256 | |
| 257 /** | |
| 258 * @param {!WebInspector.Target} target | |
| 259 * @param {?Protocol.Error} error | |
| 260 * @param {?ProfilerAgent.CPUProfile} cpuProfile | |
| 261 */ | |
| 262 _didStopRecordingJSSamples: function(target, error, cpuProfile) | |
| 263 { | |
| 264 if (error) | |
| 265 WebInspector.console.error(error); | |
| 266 if (!this._cpuProfiles) | |
| 267 this._cpuProfiles = {}; | |
| 268 this._cpuProfiles[target.id()] = cpuProfile; | |
| 269 }, | |
| 270 | |
| 271 _didStopRecordingTraceEvents: function() | |
| 272 { | |
| 273 this._tracingModel.tracingComplete(); | |
| 274 | |
| 275 var events = this._tracingModel.devtoolsPageMetadataEvents(); | |
| 276 var workerMetadataEvents = this._tracingModel.devtoolsWorkerMetadataEven
ts(); | |
| 277 | |
| 278 this._resetProcessingState(); | |
| 279 for (var i = 0, length = events.length; i < length; i++) { | |
| 280 var event = events[i]; | |
| 281 var process = event.thread.process(); | |
| 282 var startTime = event.startTime; | |
| 283 | |
| 284 var endTime = Infinity; | |
| 285 if (i + 1 < length) | |
| 286 endTime = events[i + 1].startTime; | |
| 287 | |
| 288 var threads = process.sortedThreads(); | |
| 289 for (var j = 0; j < threads.length; j++) { | |
| 290 var thread = threads[j]; | |
| 291 if (thread.name() === "WebCore: Worker" && workerMetadataEvents.
every(function(e) { return e.args["data"]["workerThreadId"] !== thread.id(); })) | |
| 292 continue; | |
| 293 this._processThreadEvents(startTime, endTime, event.thread, thre
ad); | |
| 294 } | |
| 295 } | |
| 296 this._resetProcessingState(); | |
| 297 | |
| 298 this._inspectedTargetEvents.sort(WebInspector.TracingModel.Event.compare
StartTime); | |
| 299 | |
| 300 this._cpuProfiles = null; | |
| 301 | |
| 302 this._buildTimelineRecords(); | |
| 303 this.dispatchEventToListeners(WebInspector.TimelineModel.Events.Recordin
gStopped); | |
| 304 }, | |
| 305 | |
| 306 /** | |
| 307 * @param {!ProfilerAgent.CPUProfile} cpuProfile | |
| 308 */ | |
| 309 _injectCpuProfileEvent: function(cpuProfile) | |
| 310 { | |
| 311 var metaEvent = this._tracingModel.devtoolsPageMetadataEvents().peekLast
(); | |
| 312 if (!metaEvent) | |
| 313 return; | |
| 314 var cpuProfileEvent = /** @type {!WebInspector.TracingManager.EventPaylo
ad} */ ({ | |
| 315 cat: WebInspector.TracingModel.DevToolsMetadataEventCategory, | |
| 316 ph: WebInspector.TracingModel.Phase.Instant, | |
| 317 ts: this._tracingModel.maximumRecordTime() * 1000, | |
| 318 pid: metaEvent.thread.process().id(), | |
| 319 tid: metaEvent.thread.id(), | |
| 320 name: WebInspector.TracingTimelineModel.RecordType.CpuProfile, | |
| 321 args: { data: { cpuProfile: cpuProfile } } | |
| 322 }); | |
| 323 this._tracingModel.addEvents([cpuProfileEvent]); | |
| 324 }, | |
| 325 | |
| 326 /** | |
| 327 * @return {number} | |
| 328 */ | |
| 329 minimumRecordTime: function() | |
| 330 { | |
| 331 return this._tracingModel.minimumRecordTime(); | |
| 332 }, | |
| 333 | |
| 334 /** | |
| 335 * @return {number} | |
| 336 */ | |
| 337 maximumRecordTime: function() | |
| 338 { | |
| 339 return this._tracingModel.maximumRecordTime(); | |
| 340 }, | |
| 341 | |
| 342 /** | |
| 343 * @return {!Array.<!WebInspector.TracingModel.Event>} | |
| 344 */ | |
| 345 inspectedTargetEvents: function() | |
| 346 { | |
| 347 return this._inspectedTargetEvents; | |
| 348 }, | |
| 349 | |
| 350 /** | |
| 351 * @return {!Array.<!WebInspector.TracingModel.Event>} | |
| 352 */ | |
| 353 mainThreadEvents: function() | |
| 354 { | |
| 355 return this._mainThreadEvents; | |
| 356 }, | |
| 357 | |
| 358 /** | |
| 359 * @param {!Array.<!WebInspector.TracingModel.Event>} events | |
| 360 */ | |
| 361 _setMainThreadEvents: function(events) | |
| 362 { | |
| 363 this._mainThreadEvents = events; | |
| 364 }, | |
| 365 | |
| 366 /** | |
| 367 * @return {!Array.<!Array.<!WebInspector.TracingModel.Event>>} | |
| 368 */ | |
| 369 mainThreadAsyncEvents: function() | |
| 370 { | |
| 371 return this._mainThreadAsyncEvents; | |
| 372 }, | |
| 373 | |
| 374 /** | |
| 375 * @return {!Array.<!WebInspector.TracingTimelineModel.VirtualThread>} | |
| 376 */ | |
| 377 virtualThreads: function() | |
| 378 { | |
| 379 return this._virtualThreads; | |
| 380 }, | |
| 381 | |
| 382 /** | |
| 383 * @param {!WebInspector.ChunkedFileReader} fileReader | |
| 384 * @param {!WebInspector.Progress} progress | |
| 385 * @return {!WebInspector.OutputStream} | |
| 386 */ | |
| 387 createLoader: function(fileReader, progress) | |
| 388 { | |
| 389 return new WebInspector.TracingModelLoader(this, fileReader, progress); | |
| 390 }, | |
| 391 | |
| 392 /** | |
| 393 * @param {!WebInspector.OutputStream} stream | |
| 394 */ | |
| 395 writeToStream: function(stream) | |
| 396 { | |
| 397 var saver = new WebInspector.TracingTimelineSaver(stream); | |
| 398 this._tracingModel.writeToStream(stream, saver); | |
| 399 }, | |
| 400 | |
| 401 reset: function() | |
| 402 { | |
| 403 this._virtualThreads = []; | |
| 404 this._mainThreadEvents = []; | |
| 405 this._mainThreadAsyncEvents = []; | |
| 406 this._inspectedTargetEvents = []; | |
| 407 WebInspector.TimelineModel.prototype.reset.call(this); | |
| 408 }, | |
| 409 | |
| 410 _buildTimelineRecords: function() | |
| 411 { | |
| 412 var topLevelRecords = this._buildTimelineRecordsForThread(this.mainThrea
dEvents()); | |
| 413 | |
| 414 /** | |
| 415 * @param {!WebInspector.TracingTimelineModel.TraceEventRecord} a | |
| 416 * @param {!WebInspector.TracingTimelineModel.TraceEventRecord} b | |
| 417 * @return {number} | |
| 418 */ | |
| 419 function compareRecordStartTime(a, b) | |
| 420 { | |
| 421 // Never return 0 as otherwise equal records would be merged. | |
| 422 return (a.startTime() <= b.startTime()) ? -1 : +1; | |
| 423 } | |
| 424 | |
| 425 /** | |
| 426 * @param {!WebInspector.TracingTimelineModel.VirtualThread} virtualThre
ad | |
| 427 * @this {!WebInspector.TracingTimelineModel} | |
| 428 */ | |
| 429 function processVirtualThreadEvents(virtualThread) | |
| 430 { | |
| 431 var threadRecords = this._buildTimelineRecordsForThread(virtualThrea
d.events); | |
| 432 topLevelRecords = topLevelRecords.mergeOrdered(threadRecords, compar
eRecordStartTime); | |
| 433 } | |
| 434 this.virtualThreads().forEach(processVirtualThreadEvents.bind(this)); | |
| 435 | |
| 436 | |
| 437 for (var i = 0; i < topLevelRecords.length; i++) { | |
| 438 var record = topLevelRecords[i]; | |
| 439 if (record.type() === WebInspector.TracingTimelineModel.RecordType.P
rogram) | |
| 440 this._mainThreadTasks.push(record); | |
| 441 if (record.type() === WebInspector.TracingTimelineModel.RecordType.G
PUTask) | |
| 442 this._gpuThreadTasks.push(record); | |
| 443 } | |
| 444 this._records = topLevelRecords; | |
| 445 }, | |
| 446 | |
| 447 /** | |
| 448 * @param {!Array.<!WebInspector.TracingModel.Event>} threadEvents | |
| 449 * @return {!Array.<!WebInspector.TracingTimelineModel.TraceEventRecord>} | |
| 450 */ | |
| 451 _buildTimelineRecordsForThread: function(threadEvents) | |
| 452 { | |
| 453 var recordStack = []; | |
| 454 var topLevelRecords = []; | |
| 455 | |
| 456 for (var i = 0, size = threadEvents.length; i < size; ++i) { | |
| 457 var event = threadEvents[i]; | |
| 458 for (var top = recordStack.peekLast(); top && top._event.endTime <=
event.startTime; top = recordStack.peekLast()) { | |
| 459 recordStack.pop(); | |
| 460 if (!recordStack.length) | |
| 461 topLevelRecords.push(top); | |
| 462 } | |
| 463 if (event.phase === WebInspector.TracingModel.Phase.AsyncEnd || even
t.phase === WebInspector.TracingModel.Phase.NestableAsyncEnd) | |
| 464 continue; | |
| 465 var parentRecord = recordStack.peekLast(); | |
| 466 // Maintain the back-end logic of old timeline, skip console.time()
/ console.timeEnd() that are not properly nested. | |
| 467 if (WebInspector.TracingModel.isAsyncBeginPhase(event.phase) && pare
ntRecord && event.endTime > parentRecord._event.endTime) | |
| 468 continue; | |
| 469 var record = new WebInspector.TracingTimelineModel.TraceEventRecord(
this, event); | |
| 470 if (WebInspector.TimelineUIUtils.isMarkerEvent(event)) | |
| 471 this._eventDividerRecords.push(record); | |
| 472 if (!this._recordFilter.accept(record)) | |
| 473 continue; | |
| 474 if (parentRecord) | |
| 475 parentRecord._addChild(record); | |
| 476 if (event.endTime) | |
| 477 recordStack.push(record); | |
| 478 } | |
| 479 | |
| 480 if (recordStack.length) | |
| 481 topLevelRecords.push(recordStack[0]); | |
| 482 | |
| 483 return topLevelRecords; | |
| 484 }, | |
| 485 | |
| 486 _resetProcessingState: function() | |
| 487 { | |
| 488 this._sendRequestEvents = {}; | |
| 489 this._timerEvents = {}; | |
| 490 this._requestAnimationFrameEvents = {}; | |
| 491 this._invalidationTracker = new WebInspector.InvalidationTracker(); | |
| 492 this._layoutInvalidate = {}; | |
| 493 this._lastScheduleStyleRecalculation = {}; | |
| 494 this._webSocketCreateEvents = {}; | |
| 495 this._paintImageEventByPixelRefId = {}; | |
| 496 this._lastPaintForLayer = {}; | |
| 497 this._lastRecalculateStylesEvent = null; | |
| 498 this._currentScriptEvent = null; | |
| 499 this._eventStack = []; | |
| 500 }, | |
| 501 | |
| 502 /** | |
| 503 * @param {number} startTime | |
| 504 * @param {?number} endTime | |
| 505 * @param {!WebInspector.TracingModel.Thread} mainThread | |
| 506 * @param {!WebInspector.TracingModel.Thread} thread | |
| 507 */ | |
| 508 _processThreadEvents: function(startTime, endTime, mainThread, thread) | |
| 509 { | |
| 510 var events = thread.events(); | |
| 511 var length = events.length; | |
| 512 var i = events.lowerBound(startTime, function (time, event) { return tim
e - event.startTime }); | |
| 513 | |
| 514 var threadEvents; | |
| 515 var virtualThread = null; | |
| 516 if (thread === mainThread) { | |
| 517 threadEvents = this._mainThreadEvents; | |
| 518 this._mainThreadAsyncEvents = this._mainThreadAsyncEvents.concat(thr
ead.asyncEvents()); | |
| 519 } else { | |
| 520 virtualThread = new WebInspector.TracingTimelineModel.VirtualThread(
thread.name()); | |
| 521 threadEvents = virtualThread.events; | |
| 522 virtualThread.asyncEvents = virtualThread.asyncEvents.concat(thread.
asyncEvents()); | |
| 523 this._virtualThreads.push(virtualThread); | |
| 524 } | |
| 525 | |
| 526 this._eventStack = []; | |
| 527 for (; i < length; i++) { | |
| 528 var event = events[i]; | |
| 529 if (endTime && event.startTime >= endTime) | |
| 530 break; | |
| 531 this._processEvent(event); | |
| 532 threadEvents.push(event); | |
| 533 this._inspectedTargetEvents.push(event); | |
| 534 } | |
| 535 | |
| 536 if (this._cpuProfiles && thread.target()) { | |
| 537 var cpuProfile = this._cpuProfiles[thread.target().id()]; | |
| 538 if (cpuProfile) { | |
| 539 var jsSamples = WebInspector.TimelineJSProfileProcessor.generate
TracingEventsFromCpuProfile(cpuProfile, thread); | |
| 540 var mergedEvents = threadEvents.mergeOrdered(jsSamples, WebInspe
ctor.TracingModel.Event.orderedCompareStartTime); | |
| 541 var jsFrameEvents = WebInspector.TimelineJSProfileProcessor.gene
rateJSFrameEvents(mergedEvents); | |
| 542 mergedEvents = jsFrameEvents.mergeOrdered(mergedEvents, WebInspe
ctor.TracingModel.Event.orderedCompareStartTime); | |
| 543 if (virtualThread) | |
| 544 virtualThread.events = mergedEvents; | |
| 545 else | |
| 546 this._mainThreadEvents = mergedEvents; | |
| 547 this._inspectedTargetEvents = this._inspectedTargetEvents.concat
(jsSamples, jsFrameEvents); | |
| 548 } | |
| 549 } | |
| 550 }, | |
| 551 | |
| 552 /** | |
| 553 * @param {!WebInspector.TracingModel.Event} event | |
| 554 */ | |
| 555 _processEvent: function(event) | |
| 556 { | |
| 557 var recordTypes = WebInspector.TracingTimelineModel.RecordType; | |
| 558 | |
| 559 var eventStack = this._eventStack; | |
| 560 while (eventStack.length && eventStack.peekLast().endTime < event.startT
ime) | |
| 561 eventStack.pop(); | |
| 562 var duration = event.duration; | |
| 563 if (duration) { | |
| 564 if (eventStack.length) { | |
| 565 var parent = eventStack.peekLast(); | |
| 566 parent.selfTime -= duration; | |
| 567 } | |
| 568 event.selfTime = duration; | |
| 569 eventStack.push(event); | |
| 570 } | |
| 571 | |
| 572 if (this._currentScriptEvent && event.startTime > this._currentScriptEve
nt.endTime) | |
| 573 this._currentScriptEvent = null; | |
| 574 | |
| 575 switch (event.name) { | |
| 576 case recordTypes.CallStack: | |
| 577 var lastMainThreadEvent = this.mainThreadEvents().peekLast(); | |
| 578 if (lastMainThreadEvent && event.args["stack"] && event.args["stack"
].length) | |
| 579 lastMainThreadEvent.stackTrace = event.args["stack"]; | |
| 580 break; | |
| 581 | |
| 582 case recordTypes.CpuProfile: | |
| 583 this._cpuProfile = event.args["data"]["cpuProfile"]; | |
| 584 break; | |
| 585 | |
| 586 case recordTypes.ResourceSendRequest: | |
| 587 this._sendRequestEvents[event.args["data"]["requestId"]] = event; | |
| 588 event.imageURL = event.args["data"]["url"]; | |
| 589 break; | |
| 590 | |
| 591 case recordTypes.ResourceReceiveResponse: | |
| 592 case recordTypes.ResourceReceivedData: | |
| 593 case recordTypes.ResourceFinish: | |
| 594 event.initiator = this._sendRequestEvents[event.args["data"]["reques
tId"]]; | |
| 595 if (event.initiator) | |
| 596 event.imageURL = event.initiator.imageURL; | |
| 597 break; | |
| 598 | |
| 599 case recordTypes.TimerInstall: | |
| 600 this._timerEvents[event.args["data"]["timerId"]] = event; | |
| 601 break; | |
| 602 | |
| 603 case recordTypes.TimerFire: | |
| 604 event.initiator = this._timerEvents[event.args["data"]["timerId"]]; | |
| 605 break; | |
| 606 | |
| 607 case recordTypes.RequestAnimationFrame: | |
| 608 this._requestAnimationFrameEvents[event.args["data"]["id"]] = event; | |
| 609 break; | |
| 610 | |
| 611 case recordTypes.FireAnimationFrame: | |
| 612 event.initiator = this._requestAnimationFrameEvents[event.args["data
"]["id"]]; | |
| 613 break; | |
| 614 | |
| 615 case recordTypes.ScheduleStyleRecalculation: | |
| 616 this._lastScheduleStyleRecalculation[event.args["frame"]] = event; | |
| 617 break; | |
| 618 | |
| 619 case recordTypes.RecalculateStyles: | |
| 620 this._invalidationTracker.didRecalcStyle(event); | |
| 621 event.initiator = this._lastScheduleStyleRecalculation[event.args["f
rame"]]; | |
| 622 this._lastRecalculateStylesEvent = event; | |
| 623 break; | |
| 624 | |
| 625 case recordTypes.StyleRecalcInvalidationTracking: | |
| 626 case recordTypes.LayoutInvalidationTracking: | |
| 627 case recordTypes.LayerInvalidationTracking: | |
| 628 case recordTypes.PaintInvalidationTracking: | |
| 629 this._invalidationTracker.addInvalidation(event); | |
| 630 break; | |
| 631 | |
| 632 case recordTypes.InvalidateLayout: | |
| 633 // Consider style recalculation as a reason for layout invalidation, | |
| 634 // but only if we had no earlier layout invalidation records. | |
| 635 var layoutInitator = event; | |
| 636 var frameId = event.args["frame"]; | |
| 637 if (!this._layoutInvalidate[frameId] && this._lastRecalculateStylesE
vent && this._lastRecalculateStylesEvent.endTime > event.startTime) | |
| 638 layoutInitator = this._lastRecalculateStylesEvent.initiator; | |
| 639 this._layoutInvalidate[frameId] = layoutInitator; | |
| 640 break; | |
| 641 | |
| 642 case recordTypes.Layout: | |
| 643 this._invalidationTracker.didLayout(event); | |
| 644 var frameId = event.args["beginData"]["frame"]; | |
| 645 event.initiator = this._layoutInvalidate[frameId]; | |
| 646 // In case we have no closing Layout event, endData is not available
. | |
| 647 if (event.args["endData"]) { | |
| 648 event.backendNodeId = event.args["endData"]["rootNode"]; | |
| 649 event.highlightQuad = event.args["endData"]["root"]; | |
| 650 } | |
| 651 this._layoutInvalidate[frameId] = null; | |
| 652 if (this._currentScriptEvent) | |
| 653 event.warning = WebInspector.UIString("Forced synchronous layout
is a possible performance bottleneck."); | |
| 654 break; | |
| 655 | |
| 656 case recordTypes.WebSocketCreate: | |
| 657 this._webSocketCreateEvents[event.args["data"]["identifier"]] = even
t; | |
| 658 break; | |
| 659 | |
| 660 case recordTypes.WebSocketSendHandshakeRequest: | |
| 661 case recordTypes.WebSocketReceiveHandshakeResponse: | |
| 662 case recordTypes.WebSocketDestroy: | |
| 663 event.initiator = this._webSocketCreateEvents[event.args["data"]["id
entifier"]]; | |
| 664 break; | |
| 665 | |
| 666 case recordTypes.EvaluateScript: | |
| 667 case recordTypes.FunctionCall: | |
| 668 if (!this._currentScriptEvent) | |
| 669 this._currentScriptEvent = event; | |
| 670 break; | |
| 671 | |
| 672 case recordTypes.SetLayerTreeId: | |
| 673 this._inspectedTargetLayerTreeId = event.args["layerTreeId"]; | |
| 674 break; | |
| 675 | |
| 676 case recordTypes.Paint: | |
| 677 this._invalidationTracker.didPaint(event); | |
| 678 event.highlightQuad = event.args["data"]["clip"]; | |
| 679 event.backendNodeId = event.args["data"]["nodeId"]; | |
| 680 var layerUpdateEvent = this._findAncestorEvent(recordTypes.UpdateLay
er); | |
| 681 if (!layerUpdateEvent || layerUpdateEvent.args["layerTreeId"] !== th
is._inspectedTargetLayerTreeId) | |
| 682 break; | |
| 683 // Only keep layer paint events, skip paints for subframes that get
painted to the same layer as parent. | |
| 684 if (!event.args["data"]["layerId"]) | |
| 685 break; | |
| 686 this._lastPaintForLayer[layerUpdateEvent.args["layerId"]] = event; | |
| 687 break; | |
| 688 | |
| 689 case recordTypes.PictureSnapshot: | |
| 690 var layerUpdateEvent = this._findAncestorEvent(recordTypes.UpdateLay
er); | |
| 691 if (!layerUpdateEvent || layerUpdateEvent.args["layerTreeId"] !== th
is._inspectedTargetLayerTreeId) | |
| 692 break; | |
| 693 var paintEvent = this._lastPaintForLayer[layerUpdateEvent.args["laye
rId"]]; | |
| 694 if (paintEvent) | |
| 695 paintEvent.picture = event; | |
| 696 break; | |
| 697 | |
| 698 case recordTypes.ScrollLayer: | |
| 699 event.backendNodeId = event.args["data"]["nodeId"]; | |
| 700 break; | |
| 701 | |
| 702 case recordTypes.PaintImage: | |
| 703 event.backendNodeId = event.args["data"]["nodeId"]; | |
| 704 event.imageURL = event.args["data"]["url"]; | |
| 705 break; | |
| 706 | |
| 707 case recordTypes.DecodeImage: | |
| 708 case recordTypes.ResizeImage: | |
| 709 var paintImageEvent = this._findAncestorEvent(recordTypes.PaintImage
); | |
| 710 if (!paintImageEvent) { | |
| 711 var decodeLazyPixelRefEvent = this._findAncestorEvent(recordType
s.DecodeLazyPixelRef); | |
| 712 paintImageEvent = decodeLazyPixelRefEvent && this._paintImageEve
ntByPixelRefId[decodeLazyPixelRefEvent.args["LazyPixelRef"]]; | |
| 713 } | |
| 714 if (!paintImageEvent) | |
| 715 break; | |
| 716 event.backendNodeId = paintImageEvent.backendNodeId; | |
| 717 event.imageURL = paintImageEvent.imageURL; | |
| 718 break; | |
| 719 | |
| 720 case recordTypes.DrawLazyPixelRef: | |
| 721 var paintImageEvent = this._findAncestorEvent(recordTypes.PaintImage
); | |
| 722 if (!paintImageEvent) | |
| 723 break; | |
| 724 this._paintImageEventByPixelRefId[event.args["LazyPixelRef"]] = pain
tImageEvent; | |
| 725 event.backendNodeId = paintImageEvent.backendNodeId; | |
| 726 event.imageURL = paintImageEvent.imageURL; | |
| 727 break; | |
| 728 } | |
| 729 }, | |
| 730 | |
| 731 /** | |
| 732 * @param {string} name | |
| 733 * @return {?WebInspector.TracingModel.Event} | |
| 734 */ | |
| 735 _findAncestorEvent: function(name) | |
| 736 { | |
| 737 for (var i = this._eventStack.length - 1; i >= 0; --i) { | |
| 738 var event = this._eventStack[i]; | |
| 739 if (event.name === name) | |
| 740 return event; | |
| 741 } | |
| 742 return null; | |
| 743 }, | |
| 744 | |
| 745 __proto__: WebInspector.TimelineModel.prototype | |
| 746 } | |
| 747 | |
| 748 /** | |
| 749 * @interface | |
| 750 */ | |
| 751 WebInspector.TracingTimelineModel.Filter = function() { } | |
| 752 | |
| 753 WebInspector.TracingTimelineModel.Filter.prototype = { | |
| 754 /** | |
| 755 * @param {!WebInspector.TracingModel.Event} event | |
| 756 * @return {boolean} | |
| 757 */ | |
| 758 accept: function(event) { } | |
| 759 } | |
| 760 | |
| 761 /** | |
| 762 * @constructor | |
| 763 * @implements {WebInspector.TracingTimelineModel.Filter} | |
| 764 * @param {!Array.<string>} eventNames | |
| 765 */ | |
| 766 WebInspector.TracingTimelineModel.EventNameFilter = function(eventNames) | |
| 767 { | |
| 768 this._eventNames = eventNames.keySet(); | |
| 769 } | |
| 770 | |
| 771 WebInspector.TracingTimelineModel.EventNameFilter.prototype = { | |
| 772 /** | |
| 773 * @param {!WebInspector.TracingModel.Event} event | |
| 774 * @return {boolean} | |
| 775 */ | |
| 776 accept: function(event) | |
| 777 { | |
| 778 throw new Error("Not implemented."); | |
| 779 } | |
| 780 } | |
| 781 | |
| 782 /** | |
| 783 * @constructor | |
| 784 * @extends {WebInspector.TracingTimelineModel.EventNameFilter} | |
| 785 * @param {!Array.<string>} includeNames | |
| 786 */ | |
| 787 WebInspector.TracingTimelineModel.InclusiveEventNameFilter = function(includeNam
es) | |
| 788 { | |
| 789 WebInspector.TracingTimelineModel.EventNameFilter.call(this, includeNames) | |
| 790 } | |
| 791 | |
| 792 WebInspector.TracingTimelineModel.InclusiveEventNameFilter.prototype = { | |
| 793 /** | |
| 794 * @override | |
| 795 * @param {!WebInspector.TracingModel.Event} event | |
| 796 * @return {boolean} | |
| 797 */ | |
| 798 accept: function(event) | |
| 799 { | |
| 800 return event.category === WebInspector.TracingModel.ConsoleEventCategory
|| !!this._eventNames[event.name]; | |
| 801 }, | |
| 802 __proto__: WebInspector.TracingTimelineModel.EventNameFilter.prototype | |
| 803 } | |
| 804 | |
| 805 /** | |
| 806 * @constructor | |
| 807 * @extends {WebInspector.TracingTimelineModel.EventNameFilter} | |
| 808 * @param {!Array.<string>} excludeNames | |
| 809 */ | |
| 810 WebInspector.TracingTimelineModel.ExclusiveEventNameFilter = function(excludeNam
es) | |
| 811 { | |
| 812 WebInspector.TracingTimelineModel.EventNameFilter.call(this, excludeNames) | |
| 813 } | |
| 814 | |
| 815 WebInspector.TracingTimelineModel.ExclusiveEventNameFilter.prototype = { | |
| 816 /** | |
| 817 * @override | |
| 818 * @param {!WebInspector.TracingModel.Event} event | |
| 819 * @return {boolean} | |
| 820 */ | |
| 821 accept: function(event) | |
| 822 { | |
| 823 return !this._eventNames[event.name]; | |
| 824 }, | |
| 825 __proto__: WebInspector.TracingTimelineModel.EventNameFilter.prototype | |
| 826 } | |
| 827 | |
| 828 /** | |
| 829 * @constructor | |
| 830 * @implements {WebInspector.TimelineModel.Record} | |
| 831 * @param {!WebInspector.TimelineModel} model | |
| 832 * @param {!WebInspector.TracingModel.Event} traceEvent | |
| 833 */ | |
| 834 WebInspector.TracingTimelineModel.TraceEventRecord = function(model, traceEvent) | |
| 835 { | |
| 836 this._model = model; | |
| 837 this._event = traceEvent; | |
| 838 traceEvent._timelineRecord = this; | |
| 839 this._children = []; | |
| 840 } | |
| 841 | |
| 842 WebInspector.TracingTimelineModel.TraceEventRecord.prototype = { | |
| 843 /** | |
| 844 * @return {?Array.<!ConsoleAgent.CallFrame>} | |
| 845 */ | |
| 846 callSiteStackTrace: function() | |
| 847 { | |
| 848 var initiator = this._event.initiator; | |
| 849 return initiator ? initiator.stackTrace : null; | |
| 850 }, | |
| 851 | |
| 852 /** | |
| 853 * @return {?WebInspector.TimelineModel.Record} | |
| 854 */ | |
| 855 initiator: function() | |
| 856 { | |
| 857 var initiator = this._event.initiator; | |
| 858 return initiator ? initiator._timelineRecord : null; | |
| 859 }, | |
| 860 | |
| 861 /** | |
| 862 * @return {?WebInspector.Target} | |
| 863 */ | |
| 864 target: function() | |
| 865 { | |
| 866 return this._event.thread.target(); | |
| 867 }, | |
| 868 | |
| 869 /** | |
| 870 * @return {number} | |
| 871 */ | |
| 872 selfTime: function() | |
| 873 { | |
| 874 return this._event.selfTime; | |
| 875 }, | |
| 876 | |
| 877 /** | |
| 878 * @return {!Array.<!WebInspector.TimelineModel.Record>} | |
| 879 */ | |
| 880 children: function() | |
| 881 { | |
| 882 return this._children; | |
| 883 }, | |
| 884 | |
| 885 /** | |
| 886 * @return {number} | |
| 887 */ | |
| 888 startTime: function() | |
| 889 { | |
| 890 return this._event.startTime; | |
| 891 }, | |
| 892 | |
| 893 /** | |
| 894 * @return {string} | |
| 895 */ | |
| 896 thread: function() | |
| 897 { | |
| 898 if (this._event.thread.name() === "CrRendererMain") | |
| 899 return WebInspector.TimelineModel.MainThreadName; | |
| 900 return this._event.thread.name(); | |
| 901 }, | |
| 902 | |
| 903 /** | |
| 904 * @return {number} | |
| 905 */ | |
| 906 endTime: function() | |
| 907 { | |
| 908 return this._endTime || this._event.endTime || this._event.startTime; | |
| 909 }, | |
| 910 | |
| 911 /** | |
| 912 * @param {number} endTime | |
| 913 */ | |
| 914 setEndTime: function(endTime) | |
| 915 { | |
| 916 this._endTime = endTime; | |
| 917 }, | |
| 918 | |
| 919 /** | |
| 920 * @return {!Object} | |
| 921 */ | |
| 922 data: function() | |
| 923 { | |
| 924 return this._event.args["data"]; | |
| 925 }, | |
| 926 | |
| 927 /** | |
| 928 * @return {string} | |
| 929 */ | |
| 930 type: function() | |
| 931 { | |
| 932 if (this._event.category === WebInspector.TracingModel.ConsoleEventCateg
ory) | |
| 933 return WebInspector.TracingTimelineModel.RecordType.ConsoleTime; | |
| 934 return this._event.name; | |
| 935 }, | |
| 936 | |
| 937 /** | |
| 938 * @return {string} | |
| 939 */ | |
| 940 frameId: function() | |
| 941 { | |
| 942 switch (this._event.name) { | |
| 943 case WebInspector.TracingTimelineModel.RecordType.ScheduleStyleRecalcula
tion: | |
| 944 case WebInspector.TracingTimelineModel.RecordType.RecalculateStyles: | |
| 945 case WebInspector.TracingTimelineModel.RecordType.InvalidateLayout: | |
| 946 return this._event.args["frameId"]; | |
| 947 case WebInspector.TracingTimelineModel.RecordType.Layout: | |
| 948 return this._event.args["beginData"]["frameId"]; | |
| 949 default: | |
| 950 var data = this._event.args["data"]; | |
| 951 return (data && data["frame"]) || ""; | |
| 952 } | |
| 953 }, | |
| 954 | |
| 955 /** | |
| 956 * @return {?Array.<!ConsoleAgent.CallFrame>} | |
| 957 */ | |
| 958 stackTrace: function() | |
| 959 { | |
| 960 return this._event.stackTrace; | |
| 961 }, | |
| 962 | |
| 963 /** | |
| 964 * @param {string} key | |
| 965 * @return {?Object} | |
| 966 */ | |
| 967 getUserObject: function(key) | |
| 968 { | |
| 969 if (key === "TimelineUIUtils::preview-element") | |
| 970 return this._event.previewElement; | |
| 971 throw new Error("Unexpected key: " + key); | |
| 972 }, | |
| 973 | |
| 974 /** | |
| 975 * @param {string} key | |
| 976 * @param {?Object|undefined} value | |
| 977 */ | |
| 978 setUserObject: function(key, value) | |
| 979 { | |
| 980 if (key !== "TimelineUIUtils::preview-element") | |
| 981 throw new Error("Unexpected key: " + key); | |
| 982 this._event.previewElement = /** @type {?Element} */ (value); | |
| 983 }, | |
| 984 | |
| 985 /** | |
| 986 * @return {?Array.<string>} | |
| 987 */ | |
| 988 warnings: function() | |
| 989 { | |
| 990 if (this._event.warning) | |
| 991 return [this._event.warning]; | |
| 992 return null; | |
| 993 }, | |
| 994 | |
| 995 /** | |
| 996 * @return {!WebInspector.TracingModel.Event} | |
| 997 */ | |
| 998 traceEvent: function() | |
| 999 { | |
| 1000 return this._event; | |
| 1001 }, | |
| 1002 | |
| 1003 /** | |
| 1004 * @param {!WebInspector.TracingTimelineModel.TraceEventRecord} child | |
| 1005 */ | |
| 1006 _addChild: function(child) | |
| 1007 { | |
| 1008 this._children.push(child); | |
| 1009 child.parent = this; | |
| 1010 }, | |
| 1011 | |
| 1012 /** | |
| 1013 * @return {!WebInspector.TimelineModel} | |
| 1014 */ | |
| 1015 timelineModel: function() | |
| 1016 { | |
| 1017 return this._model; | |
| 1018 } | |
| 1019 } | |
| 1020 | |
| 1021 | |
| 1022 | |
| 1023 /** | |
| 1024 * @constructor | |
| 1025 * @implements {WebInspector.OutputStream} | |
| 1026 * @param {!WebInspector.TracingTimelineModel} model | |
| 1027 * @param {!{cancel: function()}} reader | |
| 1028 * @param {!WebInspector.Progress} progress | |
| 1029 */ | |
| 1030 WebInspector.TracingModelLoader = function(model, reader, progress) | |
| 1031 { | |
| 1032 this._model = model; | |
| 1033 this._reader = reader; | |
| 1034 this._progress = progress; | |
| 1035 this._buffer = ""; | |
| 1036 this._firstChunk = true; | |
| 1037 this._loader = new WebInspector.TracingModel.Loader(model._tracingModel); | |
| 1038 } | |
| 1039 | |
| 1040 WebInspector.TracingModelLoader.prototype = { | |
| 1041 /** | |
| 1042 * @param {string} chunk | |
| 1043 */ | |
| 1044 write: function(chunk) | |
| 1045 { | |
| 1046 var data = this._buffer + chunk; | |
| 1047 var lastIndex = 0; | |
| 1048 var index; | |
| 1049 do { | |
| 1050 index = lastIndex; | |
| 1051 lastIndex = WebInspector.TextUtils.findBalancedCurlyBrackets(data, i
ndex); | |
| 1052 } while (lastIndex !== -1) | |
| 1053 | |
| 1054 var json = data.slice(0, index) + "]"; | |
| 1055 this._buffer = data.slice(index); | |
| 1056 | |
| 1057 if (!index) | |
| 1058 return; | |
| 1059 | |
| 1060 if (this._firstChunk) { | |
| 1061 this._model._startCollectingTraceEvents(true); | |
| 1062 } else { | |
| 1063 var commaIndex = json.indexOf(","); | |
| 1064 if (commaIndex !== -1) | |
| 1065 json = json.slice(commaIndex + 1); | |
| 1066 json = "[" + json; | |
| 1067 } | |
| 1068 | |
| 1069 var items; | |
| 1070 try { | |
| 1071 items = /** @type {!Array.<!WebInspector.TracingManager.EventPayload
>} */ (JSON.parse(json)); | |
| 1072 } catch (e) { | |
| 1073 this._reportErrorAndCancelLoading("Malformed timeline data: " + e); | |
| 1074 return; | |
| 1075 } | |
| 1076 | |
| 1077 if (this._firstChunk) { | |
| 1078 this._firstChunk = false; | |
| 1079 if (this._looksLikeAppVersion(items[0])) { | |
| 1080 this._reportErrorAndCancelLoading("Old Timeline format is not su
pported."); | |
| 1081 return; | |
| 1082 } | |
| 1083 } | |
| 1084 | |
| 1085 try { | |
| 1086 this._loader.loadNextChunk(items); | |
| 1087 } catch(e) { | |
| 1088 this._reportErrorAndCancelLoading("Malformed timeline data: " + e); | |
| 1089 return; | |
| 1090 } | |
| 1091 }, | |
| 1092 | |
| 1093 _reportErrorAndCancelLoading: function(messsage) | |
| 1094 { | |
| 1095 WebInspector.console.error(messsage); | |
| 1096 this._model._onTracingComplete(); | |
| 1097 this._model.reset(); | |
| 1098 this._reader.cancel(); | |
| 1099 this._progress.done(); | |
| 1100 }, | |
| 1101 | |
| 1102 _looksLikeAppVersion: function(item) | |
| 1103 { | |
| 1104 return typeof item === "string" && item.indexOf("Chrome") !== -1; | |
| 1105 }, | |
| 1106 | |
| 1107 close: function() | |
| 1108 { | |
| 1109 this._loader.finish(); | |
| 1110 this._model._onTracingComplete(); | |
| 1111 } | |
| 1112 } | |
| 1113 | |
| 1114 /** | |
| 1115 * @constructor | |
| 1116 * @param {!WebInspector.OutputStream} stream | |
| 1117 * @implements {WebInspector.OutputStreamDelegate} | |
| 1118 */ | |
| 1119 WebInspector.TracingTimelineSaver = function(stream) | |
| 1120 { | |
| 1121 this._stream = stream; | |
| 1122 } | |
| 1123 | |
| 1124 WebInspector.TracingTimelineSaver.prototype = { | |
| 1125 onTransferStarted: function() | |
| 1126 { | |
| 1127 this._stream.write("["); | |
| 1128 }, | |
| 1129 | |
| 1130 onTransferFinished: function() | |
| 1131 { | |
| 1132 this._stream.write("]"); | |
| 1133 }, | |
| 1134 | |
| 1135 /** | |
| 1136 * @param {!WebInspector.ChunkedReader} reader | |
| 1137 */ | |
| 1138 onChunkTransferred: function(reader) { }, | |
| 1139 | |
| 1140 /** | |
| 1141 * @param {!WebInspector.ChunkedReader} reader | |
| 1142 * @param {!Event} event | |
| 1143 */ | |
| 1144 onError: function(reader, event) { }, | |
| 1145 } | |
| 1146 | |
| 1147 /** | |
| 1148 * @constructor | |
| 1149 * @param {!WebInspector.TracingModel.Event} event | |
| 1150 */ | |
| 1151 WebInspector.InvalidationTrackingEvent = function(event) | |
| 1152 { | |
| 1153 this.type = event.name; | |
| 1154 this.frameId = event.args["data"]["frame"]; | |
| 1155 this.nodeId = event.args["data"]["nodeId"]; | |
| 1156 this.nodeName = event.args["data"]["nodeName"]; | |
| 1157 this.paintId = event.args["data"]["paintId"]; | |
| 1158 this.reason = event.args["data"]["reason"]; | |
| 1159 this.stackTrace = event.args["data"]["stackTrace"]; | |
| 1160 } | |
| 1161 | |
| 1162 /** | |
| 1163 * @constructor | |
| 1164 */ | |
| 1165 WebInspector.InvalidationTracker = function() | |
| 1166 { | |
| 1167 this._initializePerFrameState(); | |
| 1168 } | |
| 1169 | |
| 1170 WebInspector.InvalidationTracker.prototype = { | |
| 1171 /** | |
| 1172 * @param {!WebInspector.TracingModel.Event} event | |
| 1173 */ | |
| 1174 addInvalidation: function(event) | |
| 1175 { | |
| 1176 var invalidation = new WebInspector.InvalidationTrackingEvent(event); | |
| 1177 | |
| 1178 this._startNewFrameIfNeeded(); | |
| 1179 if (!invalidation.nodeId && !invalidation.paintId) { | |
| 1180 console.error("Invalidation lacks node information."); | |
| 1181 console.error(invalidation); | |
| 1182 } | |
| 1183 | |
| 1184 // Record the paintIds for style recalc or layout invalidations. | |
| 1185 // FIXME: This O(n^2) loop could be optimized with a map. | |
| 1186 var recordTypes = WebInspector.TracingTimelineModel.RecordType; | |
| 1187 if (invalidation.type == recordTypes.PaintInvalidationTracking) | |
| 1188 this._invalidationEvents.forEach(updatePaintId); | |
| 1189 else | |
| 1190 this._invalidationEvents.push(invalidation); | |
| 1191 | |
| 1192 function updatePaintId(invalidationToUpdate) | |
| 1193 { | |
| 1194 if (invalidationToUpdate.nodeId !== invalidation.nodeId) | |
| 1195 return; | |
| 1196 if (invalidationToUpdate.type === recordTypes.StyleRecalcInvalidatio
nTracking | |
| 1197 || invalidationToUpdate.type === recordTypes.LayoutInvalidat
ionTracking) { | |
| 1198 invalidationToUpdate.paintId = invalidation.paintId; | |
| 1199 } | |
| 1200 } | |
| 1201 }, | |
| 1202 | |
| 1203 /** | |
| 1204 * @param {!WebInspector.TracingModel.Event} styleRecalcEvent | |
| 1205 */ | |
| 1206 didRecalcStyle: function(styleRecalcEvent) | |
| 1207 { | |
| 1208 var recalcFrameId = styleRecalcEvent.args["frame"]; | |
| 1209 var index = this._lastStyleRecalcEventIndex; | |
| 1210 var invalidationCount = this._invalidationEvents.length; | |
| 1211 for (; index < invalidationCount; index++) { | |
| 1212 var invalidation = this._invalidationEvents[index]; | |
| 1213 if (invalidation.type !== WebInspector.TracingTimelineModel.RecordTy
pe.StyleRecalcInvalidationTracking) | |
| 1214 continue; | |
| 1215 if (invalidation.frameId === recalcFrameId) | |
| 1216 this._addInvalidationTrackingEvent(styleRecalcEvent, invalidatio
n); | |
| 1217 } | |
| 1218 | |
| 1219 this._lastStyleRecalcEventIndex = invalidationCount; | |
| 1220 }, | |
| 1221 | |
| 1222 /** | |
| 1223 * @param {!WebInspector.TracingModel.Event} layoutEvent | |
| 1224 */ | |
| 1225 didLayout: function(layoutEvent) | |
| 1226 { | |
| 1227 var layoutFrameId = layoutEvent.args["beginData"]["frame"]; | |
| 1228 var index = this._lastLayoutEventIndex; | |
| 1229 var invalidationCount = this._invalidationEvents.length; | |
| 1230 for (; index < invalidationCount; index++) { | |
| 1231 var invalidation = this._invalidationEvents[index]; | |
| 1232 if (invalidation.type !== WebInspector.TracingTimelineModel.RecordTy
pe.LayoutInvalidationTracking) | |
| 1233 continue; | |
| 1234 if (invalidation.frameId === layoutFrameId) | |
| 1235 this._addInvalidationTrackingEvent(layoutEvent, invalidation); | |
| 1236 } | |
| 1237 | |
| 1238 this._lastLayoutEventIndex = invalidationCount; | |
| 1239 }, | |
| 1240 | |
| 1241 /** | |
| 1242 * @param {!WebInspector.TracingModel.Event} paintEvent | |
| 1243 */ | |
| 1244 didPaint: function(paintEvent) | |
| 1245 { | |
| 1246 this._didPaint = true; | |
| 1247 | |
| 1248 // If a paint doesn't have a corresponding graphics layer id, it paints | |
| 1249 // into its parent so add an effectivePaintId to these events. | |
| 1250 var layerId = paintEvent.args["data"]["layerId"]; | |
| 1251 if (layerId) | |
| 1252 this._lastPaintWithLayer = paintEvent; | |
| 1253 if (!this._lastPaintWithLayer) { | |
| 1254 console.error("Failed to find the paint container for a paint event.
"); | |
| 1255 return; | |
| 1256 } | |
| 1257 | |
| 1258 var effectivePaintId = this._lastPaintWithLayer.args["data"]["nodeId"]; | |
| 1259 var frameId = paintEvent.args["data"]["frame"]; | |
| 1260 this._invalidationEvents.forEach(recordInvalidationForPaint.bind(this)); | |
| 1261 | |
| 1262 /** | |
| 1263 * @param {!WebInspector.InvalidationTrackingEvent} invalidation | |
| 1264 * @this {WebInspector.InvalidationTracker} | |
| 1265 */ | |
| 1266 function recordInvalidationForPaint(invalidation) | |
| 1267 { | |
| 1268 if (invalidation.paintId === effectivePaintId && invalidation.frameI
d === frameId) | |
| 1269 this._addInvalidationTrackingEvent(paintEvent, invalidation); | |
| 1270 } | |
| 1271 }, | |
| 1272 | |
| 1273 /** | |
| 1274 * @param {!WebInspector.TracingModel.Event} event | |
| 1275 * @param {!WebInspector.InvalidationTrackingEvent} invalidation | |
| 1276 */ | |
| 1277 _addInvalidationTrackingEvent: function(event, invalidation) | |
| 1278 { | |
| 1279 if (!event.invalidationTrackingEvents) | |
| 1280 event.invalidationTrackingEvents = [ invalidation ]; | |
| 1281 else | |
| 1282 event.invalidationTrackingEvents.push(invalidation); | |
| 1283 }, | |
| 1284 | |
| 1285 _startNewFrameIfNeeded: function() | |
| 1286 { | |
| 1287 if (!this._didPaint) | |
| 1288 return; | |
| 1289 | |
| 1290 this._initializePerFrameState(); | |
| 1291 }, | |
| 1292 | |
| 1293 _initializePerFrameState: function() | |
| 1294 { | |
| 1295 /** @type {!Array.<!WebInspector.InvalidationTrackingEvent>} */ | |
| 1296 this._invalidationEvents = []; | |
| 1297 this._lastStyleRecalcEventIndex = 0; | |
| 1298 this._lastLayoutEventIndex = 0; | |
| 1299 this._lastPaintWithLayer = undefined; | |
| 1300 this._didPaint = false; | |
| 1301 } | |
| 1302 } | |
| OLD | NEW |