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