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 |