Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(38)

Side by Side Diff: third_party/WebKit/Source/devtools/front_end/timeline_model/TimelineModel.js

Issue 2466123002: DevTools: reformat front-end code to match chromium style. (Closed)
Patch Set: all done Created 4 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 /* 1 /*
2 * Copyright (C) 2012 Google Inc. All rights reserved. 2 * Copyright (C) 2012 Google Inc. All rights reserved.
3 * 3 *
4 * Redistribution and use in source and binary forms, with or without 4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are 5 * modification, are permitted provided that the following conditions are
6 * met: 6 * met:
7 * 7 *
8 * * Redistributions of source code must retain the above copyright 8 * * Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer. 9 * notice, this list of conditions and the following disclaimer.
10 * * Redistributions in binary form must reproduce the above 10 * * Redistributions in binary form must reproduce the above
11 * copyright notice, this list of conditions and the following disclaimer 11 * copyright notice, this list of conditions and the following disclaimer
12 * in the documentation and/or other materials provided with the 12 * in the documentation and/or other materials provided with the
13 * distribution. 13 * distribution.
14 * * Neither the name of Google Inc. nor the names of its 14 * * Neither the name of Google Inc. nor the names of its
15 * contributors may be used to endorse or promote products derived from 15 * contributors may be used to endorse or promote products derived from
16 * this software without specific prior written permission. 16 * this software without specific prior written permission.
17 * 17 *
18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 19 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 20 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 21 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 22 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 23 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 24 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 25 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 */ 29 */
30
31 /** 30 /**
32 * @constructor 31 * @unrestricted
33 * @param {!WebInspector.TimelineModel.Filter} eventFilter
34 */ 32 */
35 WebInspector.TimelineModel = function(eventFilter) 33 WebInspector.TimelineModel = class {
36 { 34 /**
35 * @param {!WebInspector.TimelineModel.Filter} eventFilter
36 */
37 constructor(eventFilter) {
37 this._eventFilter = eventFilter; 38 this._eventFilter = eventFilter;
38 this.reset(); 39 this.reset();
40 }
41
42 /**
43 * @param {!Array.<!WebInspector.TracingModel.Event>} events
44 * @param {function(!WebInspector.TracingModel.Event)} onStartEvent
45 * @param {function(!WebInspector.TracingModel.Event)} onEndEvent
46 * @param {function(!WebInspector.TracingModel.Event,?WebInspector.TracingMode l.Event)|undefined=} onInstantEvent
47 * @param {number=} startTime
48 * @param {number=} endTime
49 */
50 static forEachEvent(events, onStartEvent, onEndEvent, onInstantEvent, startTim e, endTime) {
51 startTime = startTime || 0;
52 endTime = endTime || Infinity;
53 var stack = [];
54 for (var i = 0; i < events.length; ++i) {
55 var e = events[i];
56 if ((e.endTime || e.startTime) < startTime)
57 continue;
58 if (e.startTime >= endTime)
59 break;
60 if (WebInspector.TracingModel.isAsyncPhase(e.phase) || WebInspector.Tracin gModel.isFlowPhase(e.phase))
61 continue;
62 while (stack.length && stack.peekLast().endTime <= e.startTime)
63 onEndEvent(stack.pop());
64 if (e.duration) {
65 onStartEvent(e);
66 stack.push(e);
67 } else {
68 onInstantEvent && onInstantEvent(e, stack.peekLast() || null);
69 }
70 }
71 while (stack.length)
72 onEndEvent(stack.pop());
73 }
74
75 /**
76 * @return {!WebInspector.TimelineModel.RecordType}
77 */
78 static _eventType(event) {
79 if (event.hasCategory(WebInspector.TimelineModel.Category.Console))
80 return WebInspector.TimelineModel.RecordType.ConsoleTime;
81 if (event.hasCategory(WebInspector.TimelineModel.Category.UserTiming))
82 return WebInspector.TimelineModel.RecordType.UserTiming;
83 if (event.hasCategory(WebInspector.TimelineModel.Category.LatencyInfo))
84 return WebInspector.TimelineModel.RecordType.LatencyInfo;
85 return /** @type !WebInspector.TimelineModel.RecordType */ (event.name);
86 }
87
88 /**
89 * @param {!Array<!WebInspector.TimelineModel.Filter>} filters
90 * @param {!WebInspector.TracingModel.Event} event
91 * @return {boolean}
92 */
93 static isVisible(filters, event) {
94 for (var i = 0; i < filters.length; ++i) {
95 if (!filters[i].accept(event))
96 return false;
97 }
98 return true;
99 }
100
101 /**
102 * @param {!WebInspector.TracingModel.Event} event
103 * @return {boolean}
104 */
105 static isMarkerEvent(event) {
106 var recordTypes = WebInspector.TimelineModel.RecordType;
107 switch (event.name) {
108 case recordTypes.TimeStamp:
109 case recordTypes.MarkFirstPaint:
110 return true;
111 case recordTypes.MarkDOMContent:
112 case recordTypes.MarkLoad:
113 return event.args['data']['isMainFrame'];
114 default:
115 return false;
116 }
117 }
118
119 /**
120 * @deprecated Test use only!
121 * @param {?function(!WebInspector.TimelineModel.Record)|?function(!WebInspect or.TimelineModel.Record,number)} preOrderCallback
122 * @param {function(!WebInspector.TimelineModel.Record)|function(!WebInspector .TimelineModel.Record,number)=} postOrderCallback
123 * @return {boolean}
124 */
125 forAllRecords(preOrderCallback, postOrderCallback) {
126 /**
127 * @param {!Array.<!WebInspector.TimelineModel.Record>} records
128 * @param {number} depth
129 * @return {boolean}
130 */
131 function processRecords(records, depth) {
132 for (var i = 0; i < records.length; ++i) {
133 var record = records[i];
134 if (preOrderCallback && preOrderCallback(record, depth))
135 return true;
136 if (processRecords(record.children(), depth + 1))
137 return true;
138 if (postOrderCallback && postOrderCallback(record, depth))
139 return true;
140 }
141 return false;
142 }
143 return processRecords(this._records, 0);
144 }
145
146 /**
147 * @param {!Array<!WebInspector.TimelineModel.Filter>} filters
148 * @param {function(!WebInspector.TimelineModel.Record)|function(!WebInspector .TimelineModel.Record,number)} callback
149 */
150 forAllFilteredRecords(filters, callback) {
151 /**
152 * @param {!WebInspector.TimelineModel.Record} record
153 * @param {number} depth
154 * @this {WebInspector.TimelineModel}
155 * @return {boolean}
156 */
157 function processRecord(record, depth) {
158 var visible = WebInspector.TimelineModel.isVisible(filters, record.traceEv ent());
159 if (visible && callback(record, depth))
160 return true;
161
162 for (var i = 0; i < record.children().length; ++i) {
163 if (processRecord.call(this, record.children()[i], visible ? depth + 1 : depth))
164 return true;
165 }
166 return false;
167 }
168
169 for (var i = 0; i < this._records.length; ++i)
170 processRecord.call(this, this._records[i], 0);
171 }
172
173 /**
174 * @return {!Array.<!WebInspector.TimelineModel.Record>}
175 */
176 records() {
177 return this._records;
178 }
179
180 /**
181 * @return {!Array<!WebInspector.CPUProfileDataModel>}
182 */
183 cpuProfiles() {
184 return this._cpuProfiles;
185 }
186
187 /**
188 * @return {?string}
189 */
190 sessionId() {
191 return this._sessionId;
192 }
193
194 /**
195 * @param {!WebInspector.TracingModel.Event} event
196 * @return {?WebInspector.Target}
197 */
198 targetByEvent(event) {
199 // FIXME: Consider returning null for loaded traces.
200 var workerId = this._workerIdByThread.get(event.thread);
201 var mainTarget = WebInspector.targetManager.mainTarget();
202 return workerId ? mainTarget.subTargetsManager.targetForId(workerId) : mainT arget;
203 }
204
205 /**
206 * @param {!WebInspector.TracingModel} tracingModel
207 * @param {boolean=} produceTraceStartedInPage
208 */
209 setEvents(tracingModel, produceTraceStartedInPage) {
210 this.reset();
211 this._resetProcessingState();
212
213 this._minimumRecordTime = tracingModel.minimumRecordTime();
214 this._maximumRecordTime = tracingModel.maximumRecordTime();
215
216 var metadataEvents = this._processMetadataEvents(tracingModel, !!produceTrac eStartedInPage);
217 if (Runtime.experiments.isEnabled('timelineShowAllProcesses')) {
218 var lastPageMetaEvent = metadataEvents.page.peekLast();
219 for (var process of tracingModel.sortedProcesses()) {
220 for (var thread of process.sortedThreads())
221 this._processThreadEvents(tracingModel, 0, Infinity, thread, thread == = lastPageMetaEvent.thread);
222 }
223 } else {
224 var startTime = 0;
225 for (var i = 0, length = metadataEvents.page.length; i < length; i++) {
226 var metaEvent = metadataEvents.page[i];
227 var process = metaEvent.thread.process();
228 var endTime = i + 1 < length ? metadataEvents.page[i + 1].startTime : In finity;
229 this._currentPage = metaEvent.args['data'] && metaEvent.args['data']['pa ge'];
230 for (var thread of process.sortedThreads()) {
231 if (thread.name() === WebInspector.TimelineModel.WorkerThreadName) {
232 var workerMetaEvent = metadataEvents.workers.find(e => e.args['data' ]['workerThreadId'] === thread.id());
233 if (!workerMetaEvent)
234 continue;
235 var workerId = workerMetaEvent.args['data']['workerId'];
236 if (workerId)
237 this._workerIdByThread.set(thread, workerId);
238 }
239 this._processThreadEvents(tracingModel, startTime, endTime, thread, th read === metaEvent.thread);
240 }
241 startTime = endTime;
242 }
243 }
244 this._inspectedTargetEvents.sort(WebInspector.TracingModel.Event.compareStar tTime);
245
246 this._processBrowserEvents(tracingModel);
247 this._buildTimelineRecords();
248 this._buildGPUEvents(tracingModel);
249 this._insertFirstPaintEvent();
250 this._resetProcessingState();
251 }
252
253 /**
254 * @param {!WebInspector.TracingModel} tracingModel
255 * @param {boolean} produceTraceStartedInPage
256 * @return {!WebInspector.TimelineModel.MetadataEvents}
257 */
258 _processMetadataEvents(tracingModel, produceTraceStartedInPage) {
259 var metadataEvents = tracingModel.devToolsMetadataEvents();
260
261 var pageDevToolsMetadataEvents = [];
262 var workersDevToolsMetadataEvents = [];
263 for (var event of metadataEvents) {
264 if (event.name === WebInspector.TimelineModel.DevToolsMetadataEvent.Tracin gStartedInPage) {
265 pageDevToolsMetadataEvents.push(event);
266 } else if (event.name === WebInspector.TimelineModel.DevToolsMetadataEvent .TracingSessionIdForWorker) {
267 workersDevToolsMetadataEvents.push(event);
268 } else if (event.name === WebInspector.TimelineModel.DevToolsMetadataEvent .TracingStartedInBrowser) {
269 console.assert(!this._mainFrameNodeId, 'Multiple sessions in trace');
270 this._mainFrameNodeId = event.args['frameTreeNodeId'];
271 }
272 }
273 if (!pageDevToolsMetadataEvents.length) {
274 // The trace is probably coming not from DevTools. Make a mock Metadata ev ent.
275 var pageMetaEvent = produceTraceStartedInPage ? this._makeMockPageMetadata Event(tracingModel) : null;
276 if (!pageMetaEvent) {
277 console.error(WebInspector.TimelineModel.DevToolsMetadataEvent.TracingSt artedInPage + ' event not found.');
278 return {page: [], workers: []};
279 }
280 pageDevToolsMetadataEvents.push(pageMetaEvent);
281 }
282 var sessionId =
283 pageDevToolsMetadataEvents[0].args['sessionId'] || pageDevToolsMetadataE vents[0].args['data']['sessionId'];
284 this._sessionId = sessionId;
285
286 var mismatchingIds = new Set();
287 /**
288 * @param {!WebInspector.TracingModel.Event} event
289 * @return {boolean}
290 */
291 function checkSessionId(event) {
292 var args = event.args;
293 // FIXME: put sessionId into args["data"] for TracingStartedInPage event.
294 if (args['data'])
295 args = args['data'];
296 var id = args['sessionId'];
297 if (id === sessionId)
298 return true;
299 mismatchingIds.add(id);
300 return false;
301 }
302 var result = {
303 page: pageDevToolsMetadataEvents.filter(checkSessionId).sort(WebInspector. TracingModel.Event.compareStartTime),
304 workers:
305 workersDevToolsMetadataEvents.filter(checkSessionId).sort(WebInspector .TracingModel.Event.compareStartTime)
306 };
307 if (mismatchingIds.size)
308 WebInspector.console.error(
309 'Timeline recording was started in more than one page simultaneously. Session id mismatch: ' +
310 this._sessionId + ' and ' + mismatchingIds.valuesArray() + '.');
311 return result;
312 }
313
314 /**
315 * @param {!WebInspector.TracingModel} tracingModel
316 * @return {?WebInspector.TracingModel.Event}
317 */
318 _makeMockPageMetadataEvent(tracingModel) {
319 var rendererMainThreadName = WebInspector.TimelineModel.RendererMainThreadNa me;
320 // FIXME: pick up the first renderer process for now.
321 var process = tracingModel.sortedProcesses().filter(function(p) {
322 return p.threadByName(rendererMainThreadName);
323 })[0];
324 var thread = process && process.threadByName(rendererMainThreadName);
325 if (!thread)
326 return null;
327 var pageMetaEvent = new WebInspector.TracingModel.Event(
328 WebInspector.TracingModel.DevToolsMetadataEventCategory,
329 WebInspector.TimelineModel.DevToolsMetadataEvent.TracingStartedInPage, W ebInspector.TracingModel.Phase.Metadata,
330 tracingModel.minimumRecordTime(), thread);
331 pageMetaEvent.addArgs({'data': {'sessionId': 'mockSessionId'}});
332 return pageMetaEvent;
333 }
334
335 _insertFirstPaintEvent() {
336 if (!this._firstCompositeLayers)
337 return;
338
339 // First Paint is actually a DrawFrame that happened after first CompositeLa yers following last CommitLoadEvent.
340 var recordTypes = WebInspector.TimelineModel.RecordType;
341 var i = this._inspectedTargetEvents.lowerBound(
342 this._firstCompositeLayers, WebInspector.TracingModel.Event.compareStart Time);
343 for (; i < this._inspectedTargetEvents.length && this._inspectedTargetEvents [i].name !== recordTypes.DrawFrame;
344 ++i) {
345 }
346 if (i >= this._inspectedTargetEvents.length)
347 return;
348 var drawFrameEvent = this._inspectedTargetEvents[i];
349 var firstPaintEvent = new WebInspector.TracingModel.Event(
350 drawFrameEvent.categoriesString, recordTypes.MarkFirstPaint, WebInspecto r.TracingModel.Phase.Instant,
351 drawFrameEvent.startTime, drawFrameEvent.thread);
352 this._mainThreadEvents.splice(
353 this._mainThreadEvents.lowerBound(firstPaintEvent, WebInspector.TracingM odel.Event.compareStartTime), 0,
354 firstPaintEvent);
355 var firstPaintRecord = new WebInspector.TimelineModel.Record(firstPaintEvent );
356 this._eventDividerRecords.splice(
357 this._eventDividerRecords.lowerBound(firstPaintRecord, WebInspector.Time lineModel.Record._compareStartTime), 0,
358 firstPaintRecord);
359 }
360
361 /**
362 * @param {!WebInspector.TracingModel} tracingModel
363 */
364 _processBrowserEvents(tracingModel) {
365 var browserMain = WebInspector.TracingModel.browserMainThread(tracingModel);
366 if (!browserMain)
367 return;
368
369 // Disregard regular events, we don't need them yet, but still process to ge t proper metadata.
370 browserMain.events().forEach(this._processBrowserEvent, this);
371 /** @type {!Map<!WebInspector.TimelineModel.AsyncEventGroup, !Array<!WebInsp ector.TracingModel.AsyncEvent>>} */
372 var asyncEventsByGroup = new Map();
373 this._processAsyncEvents(asyncEventsByGroup, browserMain.asyncEvents());
374 this._mergeAsyncEvents(this._mainThreadAsyncEventsByGroup, asyncEventsByGrou p);
375 }
376
377 _buildTimelineRecords() {
378 var topLevelRecords = this._buildTimelineRecordsForThread(this.mainThreadEve nts());
379 for (var i = 0; i < topLevelRecords.length; i++) {
380 var record = topLevelRecords[i];
381 if (WebInspector.TracingModel.isTopLevelEvent(record.traceEvent()))
382 this._mainThreadTasks.push(record);
383 }
384
385 /**
386 * @param {!WebInspector.TimelineModel.VirtualThread} virtualThread
387 * @this {!WebInspector.TimelineModel}
388 */
389 function processVirtualThreadEvents(virtualThread) {
390 var threadRecords = this._buildTimelineRecordsForThread(virtualThread.even ts);
391 topLevelRecords =
392 topLevelRecords.mergeOrdered(threadRecords, WebInspector.TimelineModel .Record._compareStartTime);
393 }
394 this.virtualThreads().forEach(processVirtualThreadEvents.bind(this));
395 this._records = topLevelRecords;
396 }
397
398 /**
399 * @param {!WebInspector.TracingModel} tracingModel
400 */
401 _buildGPUEvents(tracingModel) {
402 var thread = tracingModel.threadByName('GPU Process', 'CrGpuMain');
403 if (!thread)
404 return;
405 var gpuEventName = WebInspector.TimelineModel.RecordType.GPUTask;
406 this._gpuEvents = thread.events().filter(event => event.name === gpuEventNam e);
407 }
408
409 /**
410 * @param {!Array.<!WebInspector.TracingModel.Event>} threadEvents
411 * @return {!Array.<!WebInspector.TimelineModel.Record>}
412 */
413 _buildTimelineRecordsForThread(threadEvents) {
414 var recordStack = [];
415 var topLevelRecords = [];
416
417 for (var i = 0, size = threadEvents.length; i < size; ++i) {
418 var event = threadEvents[i];
419 for (var top = recordStack.peekLast(); top && top._event.endTime <= event. startTime; top = recordStack.peekLast())
420 recordStack.pop();
421 if (event.phase === WebInspector.TracingModel.Phase.AsyncEnd ||
422 event.phase === WebInspector.TracingModel.Phase.NestableAsyncEnd)
423 continue;
424 var parentRecord = recordStack.peekLast();
425 // Maintain the back-end logic of old timeline, skip console.time() / cons ole.timeEnd() that are not properly nested.
426 if (WebInspector.TracingModel.isAsyncBeginPhase(event.phase) && parentReco rd &&
427 event.endTime > parentRecord._event.endTime)
428 continue;
429 var record = new WebInspector.TimelineModel.Record(event);
430 if (WebInspector.TimelineModel.isMarkerEvent(event))
431 this._eventDividerRecords.push(record);
432 if (!this._eventFilter.accept(event) && !WebInspector.TracingModel.isTopLe velEvent(event))
433 continue;
434 if (parentRecord)
435 parentRecord._addChild(record);
436 else
437 topLevelRecords.push(record);
438 if (event.endTime)
439 recordStack.push(record);
440 }
441
442 return topLevelRecords;
443 }
444
445 _resetProcessingState() {
446 this._asyncEventTracker = new WebInspector.TimelineAsyncEventTracker();
447 this._invalidationTracker = new WebInspector.InvalidationTracker();
448 this._layoutInvalidate = {};
449 this._lastScheduleStyleRecalculation = {};
450 this._paintImageEventByPixelRefId = {};
451 this._lastPaintForLayer = {};
452 this._lastRecalculateStylesEvent = null;
453 this._currentScriptEvent = null;
454 this._eventStack = [];
455 this._hadCommitLoad = false;
456 this._firstCompositeLayers = null;
457 /** @type {!Set<string>} */
458 this._knownInputEvents = new Set();
459 this._currentPage = null;
460 }
461
462 /**
463 * @param {!WebInspector.TracingModel} tracingModel
464 * @param {!WebInspector.TracingModel.Thread} thread
465 * @return {?WebInspector.CPUProfileDataModel}
466 */
467 _extractCpuProfile(tracingModel, thread) {
468 var events = thread.events();
469 var cpuProfile;
470
471 // Check for legacy CpuProfile event format first.
472 var cpuProfileEvent = events.peekLast();
473 if (cpuProfileEvent && cpuProfileEvent.name === WebInspector.TimelineModel.R ecordType.CpuProfile) {
474 var eventData = cpuProfileEvent.args['data'];
475 cpuProfile = /** @type {?ProfilerAgent.Profile} */ (eventData && eventData ['cpuProfile']);
476 }
477
478 if (!cpuProfile) {
479 cpuProfileEvent = events.find(e => e.name === WebInspector.TimelineModel.R ecordType.Profile);
480 if (!cpuProfileEvent)
481 return null;
482 var profileGroup = tracingModel.profileGroup(cpuProfileEvent.id);
483 if (!profileGroup) {
484 WebInspector.console.error('Invalid CPU profile format.');
485 return null;
486 }
487 cpuProfile = /** @type {!ProfilerAgent.Profile} */ (
488 {startTime: cpuProfileEvent.args['data']['startTime'], endTime: 0, nod es: [], samples: [], timeDeltas: []});
489 for (var profileEvent of profileGroup.children) {
490 var eventData = profileEvent.args['data'];
491 if ('startTime' in eventData)
492 cpuProfile.startTime = eventData['startTime'];
493 if ('endTime' in eventData)
494 cpuProfile.endTime = eventData['endTime'];
495 var nodesAndSamples = eventData['cpuProfile'] || {};
496 cpuProfile.nodes.pushAll(nodesAndSamples['nodes'] || []);
497 cpuProfile.samples.pushAll(nodesAndSamples['samples'] || []);
498 cpuProfile.timeDeltas.pushAll(eventData['timeDeltas'] || []);
499 if (cpuProfile.samples.length !== cpuProfile.timeDeltas.length) {
500 WebInspector.console.error('Failed to parse CPU profile.');
501 return null;
502 }
503 }
504 if (!cpuProfile.endTime)
505 cpuProfile.endTime = cpuProfile.timeDeltas.reduce((x, y) => x + y, cpuPr ofile.startTime);
506 }
507
508 try {
509 var jsProfileModel = new WebInspector.CPUProfileDataModel(cpuProfile);
510 this._cpuProfiles.push(jsProfileModel);
511 return jsProfileModel;
512 } catch (e) {
513 WebInspector.console.error('Failed to parse CPU profile.');
514 }
515 return null;
516 }
517
518 /**
519 * @param {!WebInspector.TracingModel} tracingModel
520 * @param {!WebInspector.TracingModel.Thread} thread
521 * @return {!Array<!WebInspector.TracingModel.Event>}
522 */
523 _injectJSFrameEvents(tracingModel, thread) {
524 var jsProfileModel = this._extractCpuProfile(tracingModel, thread);
525 var events = thread.events();
526 var jsSamples = jsProfileModel ?
527 WebInspector.TimelineJSProfileProcessor.generateTracingEventsFromCpuProf ile(jsProfileModel, thread) :
528 null;
529 if (jsSamples && jsSamples.length)
530 events = events.mergeOrdered(jsSamples, WebInspector.TracingModel.Event.or deredCompareStartTime);
531 if (jsSamples || events.some(e => e.name === WebInspector.TimelineModel.Reco rdType.JSSample)) {
532 var jsFrameEvents = WebInspector.TimelineJSProfileProcessor.generateJSFram eEvents(events);
533 if (jsFrameEvents && jsFrameEvents.length)
534 events = jsFrameEvents.mergeOrdered(events, WebInspector.TracingModel.Ev ent.orderedCompareStartTime);
535 }
536 return events;
537 }
538
539 /**
540 * @param {!WebInspector.TracingModel} tracingModel
541 * @param {number} startTime
542 * @param {number} endTime
543 * @param {!WebInspector.TracingModel.Thread} thread
544 * @param {boolean} isMainThread
545 */
546 _processThreadEvents(tracingModel, startTime, endTime, thread, isMainThread) {
547 var events = this._injectJSFrameEvents(tracingModel, thread);
548 var asyncEvents = thread.asyncEvents();
549
550 var threadEvents;
551 var threadAsyncEventsByGroup;
552 if (isMainThread) {
553 threadEvents = this._mainThreadEvents;
554 threadAsyncEventsByGroup = this._mainThreadAsyncEventsByGroup;
555 } else {
556 var virtualThread = new WebInspector.TimelineModel.VirtualThread(thread.na me());
557 this._virtualThreads.push(virtualThread);
558 threadEvents = virtualThread.events;
559 threadAsyncEventsByGroup = virtualThread.asyncEventsByGroup;
560 }
561
562 this._eventStack = [];
563 var i = events.lowerBound(startTime, (time, event) => time - event.startTime );
564 var length = events.length;
565 for (; i < length; i++) {
566 var event = events[i];
567 if (endTime && event.startTime >= endTime)
568 break;
569 if (!this._processEvent(event))
570 continue;
571 threadEvents.push(event);
572 this._inspectedTargetEvents.push(event);
573 }
574 this._processAsyncEvents(threadAsyncEventsByGroup, asyncEvents, startTime, e ndTime);
575 // Pretend the compositor's async events are on the main thread.
576 if (thread.name() === 'Compositor') {
577 this._mergeAsyncEvents(this._mainThreadAsyncEventsByGroup, threadAsyncEven tsByGroup);
578 threadAsyncEventsByGroup.clear();
579 }
580 }
581
582 /**
583 * @param {!Map<!WebInspector.TimelineModel.AsyncEventGroup, !Array<!WebInspec tor.TracingModel.AsyncEvent>>} asyncEventsByGroup
584 * @param {!Array<!WebInspector.TracingModel.AsyncEvent>} asyncEvents
585 * @param {number=} startTime
586 * @param {number=} endTime
587 */
588 _processAsyncEvents(asyncEventsByGroup, asyncEvents, startTime, endTime) {
589 var i = startTime ? asyncEvents.lowerBound(startTime, function(time, asyncEv ent) {
590 return time - asyncEvent.startTime;
591 }) : 0;
592 for (; i < asyncEvents.length; ++i) {
593 var asyncEvent = asyncEvents[i];
594 if (endTime && asyncEvent.startTime >= endTime)
595 break;
596 var asyncGroup = this._processAsyncEvent(asyncEvent);
597 if (!asyncGroup)
598 continue;
599 var groupAsyncEvents = asyncEventsByGroup.get(asyncGroup);
600 if (!groupAsyncEvents) {
601 groupAsyncEvents = [];
602 asyncEventsByGroup.set(asyncGroup, groupAsyncEvents);
603 }
604 groupAsyncEvents.push(asyncEvent);
605 }
606 }
607
608 /**
609 * @param {!WebInspector.TracingModel.Event} event
610 * @return {boolean}
611 */
612 _processEvent(event) {
613 var eventStack = this._eventStack;
614 while (eventStack.length && eventStack.peekLast().endTime <= event.startTime )
615 eventStack.pop();
616
617 var recordTypes = WebInspector.TimelineModel.RecordType;
618
619 if (this._currentScriptEvent && event.startTime > this._currentScriptEvent.e ndTime)
620 this._currentScriptEvent = null;
621
622 var eventData = event.args['data'] || event.args['beginData'] || {};
623 if (eventData['stackTrace'])
624 event.stackTrace = eventData['stackTrace'];
625 if (event.stackTrace && event.name !== recordTypes.JSSample) {
626 // TraceEvents come with 1-based line & column numbers. The frontend code
627 // requires 0-based ones. Adjust the values.
628 for (var i = 0; i < event.stackTrace.length; ++i) {
629 --event.stackTrace[i].lineNumber;
630 --event.stackTrace[i].columnNumber;
631 }
632 }
633
634 if (eventStack.length && eventStack.peekLast().name === recordTypes.EventDis patch)
635 eventStack.peekLast().hasChildren = true;
636 this._asyncEventTracker.processEvent(event);
637 if (event.initiator && event.initiator.url)
638 event.url = event.initiator.url;
639 switch (event.name) {
640 case recordTypes.ResourceSendRequest:
641 case recordTypes.WebSocketCreate:
642 event.url = eventData['url'];
643 event.initiator = eventStack.peekLast() || null;
644 break;
645
646 case recordTypes.ScheduleStyleRecalculation:
647 this._lastScheduleStyleRecalculation[eventData['frame']] = event;
648 break;
649
650 case recordTypes.UpdateLayoutTree:
651 case recordTypes.RecalculateStyles:
652 this._invalidationTracker.didRecalcStyle(event);
653 if (event.args['beginData'])
654 event.initiator = this._lastScheduleStyleRecalculation[event.args['beg inData']['frame']];
655 this._lastRecalculateStylesEvent = event;
656 if (this._currentScriptEvent)
657 event.warning = WebInspector.TimelineModel.WarningType.ForcedStyle;
658 break;
659
660 case recordTypes.ScheduleStyleInvalidationTracking:
661 case recordTypes.StyleRecalcInvalidationTracking:
662 case recordTypes.StyleInvalidatorInvalidationTracking:
663 case recordTypes.LayoutInvalidationTracking:
664 case recordTypes.LayerInvalidationTracking:
665 case recordTypes.PaintInvalidationTracking:
666 case recordTypes.ScrollInvalidationTracking:
667 this._invalidationTracker.addInvalidation(new WebInspector.InvalidationT rackingEvent(event));
668 break;
669
670 case recordTypes.InvalidateLayout:
671 // Consider style recalculation as a reason for layout invalidation,
672 // but only if we had no earlier layout invalidation records.
673 var layoutInitator = event;
674 var frameId = eventData['frame'];
675 if (!this._layoutInvalidate[frameId] && this._lastRecalculateStylesEvent &&
676 this._lastRecalculateStylesEvent.endTime > event.startTime)
677 layoutInitator = this._lastRecalculateStylesEvent.initiator;
678 this._layoutInvalidate[frameId] = layoutInitator;
679 break;
680
681 case recordTypes.Layout:
682 this._invalidationTracker.didLayout(event);
683 var frameId = event.args['beginData']['frame'];
684 event.initiator = this._layoutInvalidate[frameId];
685 // In case we have no closing Layout event, endData is not available.
686 if (event.args['endData']) {
687 event.backendNodeId = event.args['endData']['rootNode'];
688 event.highlightQuad = event.args['endData']['root'];
689 }
690 this._layoutInvalidate[frameId] = null;
691 if (this._currentScriptEvent)
692 event.warning = WebInspector.TimelineModel.WarningType.ForcedLayout;
693 break;
694
695 case recordTypes.FunctionCall:
696 // Compatibility with old format.
697 if (typeof eventData['scriptName'] === 'string')
698 eventData['url'] = eventData['scriptName'];
699 if (typeof eventData['scriptLine'] === 'number')
700 eventData['lineNumber'] = eventData['scriptLine'];
701 // Fallthrough.
702 case recordTypes.EvaluateScript:
703 case recordTypes.CompileScript:
704 if (typeof eventData['lineNumber'] === 'number')
705 --eventData['lineNumber'];
706 if (typeof eventData['columnNumber'] === 'number')
707 --eventData['columnNumber'];
708 // Fallthrough intended.
709 case recordTypes.RunMicrotasks:
710 // Microtasks technically are not necessarily scripts, but for purpose o f
711 // forced sync style recalc or layout detection they are.
712 if (!this._currentScriptEvent)
713 this._currentScriptEvent = event;
714 break;
715
716 case recordTypes.SetLayerTreeId:
717 this._inspectedTargetLayerTreeId = event.args['layerTreeId'] || event.ar gs['data']['layerTreeId'];
718 break;
719
720 case recordTypes.Paint:
721 this._invalidationTracker.didPaint(event);
722 event.highlightQuad = eventData['clip'];
723 event.backendNodeId = eventData['nodeId'];
724 // Only keep layer paint events, skip paints for subframes that get pain ted to the same layer as parent.
725 if (!eventData['layerId'])
726 break;
727 var layerId = eventData['layerId'];
728 this._lastPaintForLayer[layerId] = event;
729 break;
730
731 case recordTypes.DisplayItemListSnapshot:
732 case recordTypes.PictureSnapshot:
733 var layerUpdateEvent = this._findAncestorEvent(recordTypes.UpdateLayer);
734 if (!layerUpdateEvent || layerUpdateEvent.args['layerTreeId'] !== this._ inspectedTargetLayerTreeId)
735 break;
736 var paintEvent = this._lastPaintForLayer[layerUpdateEvent.args['layerId' ]];
737 if (paintEvent)
738 paintEvent.picture = event;
739 break;
740
741 case recordTypes.ScrollLayer:
742 event.backendNodeId = eventData['nodeId'];
743 break;
744
745 case recordTypes.PaintImage:
746 event.backendNodeId = eventData['nodeId'];
747 event.url = eventData['url'];
748 break;
749
750 case recordTypes.DecodeImage:
751 case recordTypes.ResizeImage:
752 var paintImageEvent = this._findAncestorEvent(recordTypes.PaintImage);
753 if (!paintImageEvent) {
754 var decodeLazyPixelRefEvent = this._findAncestorEvent(recordTypes.Deco deLazyPixelRef);
755 paintImageEvent = decodeLazyPixelRefEvent &&
756 this._paintImageEventByPixelRefId[decodeLazyPixelRefEvent.args['La zyPixelRef']];
757 }
758 if (!paintImageEvent)
759 break;
760 event.backendNodeId = paintImageEvent.backendNodeId;
761 event.url = paintImageEvent.url;
762 break;
763
764 case recordTypes.DrawLazyPixelRef:
765 var paintImageEvent = this._findAncestorEvent(recordTypes.PaintImage);
766 if (!paintImageEvent)
767 break;
768 this._paintImageEventByPixelRefId[event.args['LazyPixelRef']] = paintIma geEvent;
769 event.backendNodeId = paintImageEvent.backendNodeId;
770 event.url = paintImageEvent.url;
771 break;
772
773 case recordTypes.MarkDOMContent:
774 case recordTypes.MarkLoad:
775 var page = eventData['page'];
776 if (page && page !== this._currentPage)
777 return false;
778 break;
779
780 case recordTypes.CommitLoad:
781 var page = eventData['page'];
782 if (page && page !== this._currentPage)
783 return false;
784 if (!eventData['isMainFrame'])
785 break;
786 this._hadCommitLoad = true;
787 this._firstCompositeLayers = null;
788 break;
789
790 case recordTypes.CompositeLayers:
791 if (!this._firstCompositeLayers && this._hadCommitLoad)
792 this._firstCompositeLayers = event;
793 break;
794
795 case recordTypes.FireIdleCallback:
796 if (event.duration > eventData['allottedMilliseconds']) {
797 event.warning = WebInspector.TimelineModel.WarningType.IdleDeadlineExc eeded;
798 }
799 break;
800 }
801 if (WebInspector.TracingModel.isAsyncPhase(event.phase))
802 return true;
803 var duration = event.duration;
804 if (!duration)
805 return true;
806 if (eventStack.length) {
807 var parent = eventStack.peekLast();
808 parent.selfTime -= duration;
809 if (parent.selfTime < 0) {
810 var epsilon = 1e-3;
811 if (parent.selfTime < -epsilon)
812 console.error(
813 'Children are longer than parent at ' + event.startTime + ' (' +
814 (event.startTime - this.minimumRecordTime()).toFixed(3) + ') by ' + parent.selfTime.toFixed(3));
815 parent.selfTime = 0;
816 }
817 }
818 event.selfTime = duration;
819 eventStack.push(event);
820 return true;
821 }
822
823 /**
824 * @param {!WebInspector.TracingModel.Event} event
825 */
826 _processBrowserEvent(event) {
827 if (event.name !== WebInspector.TimelineModel.RecordType.LatencyInfoFlow)
828 return;
829 var frameId = event.args['frameTreeNodeId'];
830 if (typeof frameId === 'number' && frameId === this._mainFrameNodeId)
831 this._knownInputEvents.add(event.bind_id);
832 }
833
834 /**
835 * @param {!WebInspector.TracingModel.AsyncEvent} asyncEvent
836 * @return {?WebInspector.TimelineModel.AsyncEventGroup}
837 */
838 _processAsyncEvent(asyncEvent) {
839 var groups = WebInspector.TimelineModel.AsyncEventGroup;
840 if (asyncEvent.hasCategory(WebInspector.TimelineModel.Category.Console))
841 return groups.console;
842 if (asyncEvent.hasCategory(WebInspector.TimelineModel.Category.UserTiming))
843 return groups.userTiming;
844 if (asyncEvent.name === WebInspector.TimelineModel.RecordType.Animation)
845 return groups.animation;
846 if (asyncEvent.hasCategory(WebInspector.TimelineModel.Category.LatencyInfo) ||
847 asyncEvent.name === WebInspector.TimelineModel.RecordType.ImplSideFling) {
848 var lastStep = asyncEvent.steps.peekLast();
849 // FIXME: fix event termination on the back-end instead.
850 if (lastStep.phase !== WebInspector.TracingModel.Phase.AsyncEnd)
851 return null;
852 var data = lastStep.args['data'];
853 asyncEvent.causedFrame = !!(data && data['INPUT_EVENT_LATENCY_RENDERER_SWA P_COMPONENT']);
854 if (asyncEvent.hasCategory(WebInspector.TimelineModel.Category.LatencyInfo )) {
855 if (!this._knownInputEvents.has(lastStep.id))
856 return null;
857 if (asyncEvent.name === WebInspector.TimelineModel.RecordType.InputLaten cyMouseMove && !asyncEvent.causedFrame)
858 return null;
859 var rendererMain = data['INPUT_EVENT_LATENCY_RENDERER_MAIN_COMPONENT'];
860 if (rendererMain) {
861 var time = rendererMain['time'] / 1000;
862 asyncEvent.steps[0].timeWaitingForMainThread = time - asyncEvent.steps [0].startTime;
863 }
864 }
865 return groups.input;
866 }
867 return null;
868 }
869
870 /**
871 * @param {string} name
872 * @return {?WebInspector.TracingModel.Event}
873 */
874 _findAncestorEvent(name) {
875 for (var i = this._eventStack.length - 1; i >= 0; --i) {
876 var event = this._eventStack[i];
877 if (event.name === name)
878 return event;
879 }
880 return null;
881 }
882
883 /**
884 * @param {!Map<!WebInspector.TimelineModel.AsyncEventGroup, !Array<!WebInspec tor.TracingModel.AsyncEvent>>} target
885 * @param {!Map<!WebInspector.TimelineModel.AsyncEventGroup, !Array<!WebInspec tor.TracingModel.AsyncEvent>>} source
886 */
887 _mergeAsyncEvents(target, source) {
888 for (var group of source.keys()) {
889 var events = target.get(group) || [];
890 events = events.mergeOrdered(source.get(group) || [], WebInspector.Tracing Model.Event.compareStartAndEndTime);
891 target.set(group, events);
892 }
893 }
894
895 reset() {
896 this._virtualThreads = [];
897 /** @type {!Array<!WebInspector.TracingModel.Event>} */
898 this._mainThreadEvents = [];
899 /** @type {!Map<!WebInspector.TimelineModel.AsyncEventGroup, !Array<!WebInsp ector.TracingModel.AsyncEvent>>} */
900 this._mainThreadAsyncEventsByGroup = new Map();
901 /** @type {!Array<!WebInspector.TracingModel.Event>} */
902 this._inspectedTargetEvents = [];
903 /** @type {!Array<!WebInspector.TimelineModel.Record>} */
904 this._records = [];
905 /** @type {!Array<!WebInspector.TimelineModel.Record>} */
906 this._mainThreadTasks = [];
907 /** @type {!Array<!WebInspector.TracingModel.Event>} */
908 this._gpuEvents = [];
909 /** @type {!Array<!WebInspector.TimelineModel.Record>} */
910 this._eventDividerRecords = [];
911 /** @type {?string} */
912 this._sessionId = null;
913 /** @type {?number} */
914 this._mainFrameNodeId = null;
915 /** @type {!Array<!WebInspector.CPUProfileDataModel>} */
916 this._cpuProfiles = [];
917 /** @type {!WeakMap<!WebInspector.TracingModel.Thread, string>} */
918 this._workerIdByThread = new WeakMap();
919 this._minimumRecordTime = 0;
920 this._maximumRecordTime = 0;
921 }
922
923 /**
924 * @return {number}
925 */
926 minimumRecordTime() {
927 return this._minimumRecordTime;
928 }
929
930 /**
931 * @return {number}
932 */
933 maximumRecordTime() {
934 return this._maximumRecordTime;
935 }
936
937 /**
938 * @return {!Array.<!WebInspector.TracingModel.Event>}
939 */
940 inspectedTargetEvents() {
941 return this._inspectedTargetEvents;
942 }
943
944 /**
945 * @return {!Array.<!WebInspector.TracingModel.Event>}
946 */
947 mainThreadEvents() {
948 return this._mainThreadEvents;
949 }
950
951 /**
952 * @param {!Array.<!WebInspector.TracingModel.Event>} events
953 */
954 _setMainThreadEvents(events) {
955 this._mainThreadEvents = events;
956 }
957
958 /**
959 * @return {!Map<!WebInspector.TimelineModel.AsyncEventGroup, !Array.<!WebInsp ector.TracingModel.AsyncEvent>>}
960 */
961 mainThreadAsyncEvents() {
962 return this._mainThreadAsyncEventsByGroup;
963 }
964
965 /**
966 * @return {!Array.<!WebInspector.TimelineModel.VirtualThread>}
967 */
968 virtualThreads() {
969 return this._virtualThreads;
970 }
971
972 /**
973 * @return {boolean}
974 */
975 isEmpty() {
976 return this.minimumRecordTime() === 0 && this.maximumRecordTime() === 0;
977 }
978
979 /**
980 * @return {!Array.<!WebInspector.TimelineModel.Record>}
981 */
982 mainThreadTasks() {
983 return this._mainThreadTasks;
984 }
985
986 /**
987 * @return {!Array<!WebInspector.TracingModel.Event>}
988 */
989 gpuEvents() {
990 return this._gpuEvents;
991 }
992
993 /**
994 * @return {!Array.<!WebInspector.TimelineModel.Record>}
995 */
996 eventDividerRecords() {
997 return this._eventDividerRecords;
998 }
999
1000 /**
1001 * @return {!Array<!WebInspector.TimelineModel.NetworkRequest>}
1002 */
1003 networkRequests() {
1004 /** @type {!Map<string,!WebInspector.TimelineModel.NetworkRequest>} */
1005 var requests = new Map();
1006 /** @type {!Array<!WebInspector.TimelineModel.NetworkRequest>} */
1007 var requestsList = [];
1008 /** @type {!Array<!WebInspector.TimelineModel.NetworkRequest>} */
1009 var zeroStartRequestsList = [];
1010 var types = WebInspector.TimelineModel.RecordType;
1011 var resourceTypes = new Set(
1012 [types.ResourceSendRequest, types.ResourceReceiveResponse, types.Resourc eReceivedData, types.ResourceFinish]);
1013 var events = this.mainThreadEvents();
1014 for (var i = 0; i < events.length; ++i) {
1015 var e = events[i];
1016 if (!resourceTypes.has(e.name))
1017 continue;
1018 var id = e.args['data']['requestId'];
1019 var request = requests.get(id);
1020 if (request) {
1021 request.addEvent(e);
1022 } else {
1023 request = new WebInspector.TimelineModel.NetworkRequest(e);
1024 requests.set(id, request);
1025 if (request.startTime)
1026 requestsList.push(request);
1027 else
1028 zeroStartRequestsList.push(request);
1029 }
1030 }
1031 return zeroStartRequestsList.concat(requestsList);
1032 }
39 }; 1033 };
40 1034
41 /** 1035 /**
42 * @enum {string} 1036 * @enum {string}
43 */ 1037 */
44 WebInspector.TimelineModel.RecordType = { 1038 WebInspector.TimelineModel.RecordType = {
45 Task: "Task", 1039 Task: 'Task',
46 Program: "Program", 1040 Program: 'Program',
47 EventDispatch: "EventDispatch", 1041 EventDispatch: 'EventDispatch',
48 1042
49 GPUTask: "GPUTask", 1043 GPUTask: 'GPUTask',
50 1044
51 Animation: "Animation", 1045 Animation: 'Animation',
52 RequestMainThreadFrame: "RequestMainThreadFrame", 1046 RequestMainThreadFrame: 'RequestMainThreadFrame',
53 BeginFrame: "BeginFrame", 1047 BeginFrame: 'BeginFrame',
54 NeedsBeginFrameChanged: "NeedsBeginFrameChanged", 1048 NeedsBeginFrameChanged: 'NeedsBeginFrameChanged',
55 BeginMainThreadFrame: "BeginMainThreadFrame", 1049 BeginMainThreadFrame: 'BeginMainThreadFrame',
56 ActivateLayerTree: "ActivateLayerTree", 1050 ActivateLayerTree: 'ActivateLayerTree',
57 DrawFrame: "DrawFrame", 1051 DrawFrame: 'DrawFrame',
58 HitTest: "HitTest", 1052 HitTest: 'HitTest',
59 ScheduleStyleRecalculation: "ScheduleStyleRecalculation", 1053 ScheduleStyleRecalculation: 'ScheduleStyleRecalculation',
60 RecalculateStyles: "RecalculateStyles", // For backwards compatibility only, now replaced by UpdateLayoutTree. 1054 RecalculateStyles: 'RecalculateStyles', // For backwards compatibility only, now replaced by UpdateLayoutTree.
61 UpdateLayoutTree: "UpdateLayoutTree", 1055 UpdateLayoutTree: 'UpdateLayoutTree',
62 InvalidateLayout: "InvalidateLayout", 1056 InvalidateLayout: 'InvalidateLayout',
63 Layout: "Layout", 1057 Layout: 'Layout',
64 UpdateLayer: "UpdateLayer", 1058 UpdateLayer: 'UpdateLayer',
65 UpdateLayerTree: "UpdateLayerTree", 1059 UpdateLayerTree: 'UpdateLayerTree',
66 PaintSetup: "PaintSetup", 1060 PaintSetup: 'PaintSetup',
67 Paint: "Paint", 1061 Paint: 'Paint',
68 PaintImage: "PaintImage", 1062 PaintImage: 'PaintImage',
69 Rasterize: "Rasterize", 1063 Rasterize: 'Rasterize',
70 RasterTask: "RasterTask", 1064 RasterTask: 'RasterTask',
71 ScrollLayer: "ScrollLayer", 1065 ScrollLayer: 'ScrollLayer',
72 CompositeLayers: "CompositeLayers", 1066 CompositeLayers: 'CompositeLayers',
73 1067
74 ScheduleStyleInvalidationTracking: "ScheduleStyleInvalidationTracking", 1068 ScheduleStyleInvalidationTracking: 'ScheduleStyleInvalidationTracking',
75 StyleRecalcInvalidationTracking: "StyleRecalcInvalidationTracking", 1069 StyleRecalcInvalidationTracking: 'StyleRecalcInvalidationTracking',
76 StyleInvalidatorInvalidationTracking: "StyleInvalidatorInvalidationTracking" , 1070 StyleInvalidatorInvalidationTracking: 'StyleInvalidatorInvalidationTracking',
77 LayoutInvalidationTracking: "LayoutInvalidationTracking", 1071 LayoutInvalidationTracking: 'LayoutInvalidationTracking',
78 LayerInvalidationTracking: "LayerInvalidationTracking", 1072 LayerInvalidationTracking: 'LayerInvalidationTracking',
79 PaintInvalidationTracking: "PaintInvalidationTracking", 1073 PaintInvalidationTracking: 'PaintInvalidationTracking',
80 ScrollInvalidationTracking: "ScrollInvalidationTracking", 1074 ScrollInvalidationTracking: 'ScrollInvalidationTracking',
81 1075
82 ParseHTML: "ParseHTML", 1076 ParseHTML: 'ParseHTML',
83 ParseAuthorStyleSheet: "ParseAuthorStyleSheet", 1077 ParseAuthorStyleSheet: 'ParseAuthorStyleSheet',
84 1078
85 TimerInstall: "TimerInstall", 1079 TimerInstall: 'TimerInstall',
86 TimerRemove: "TimerRemove", 1080 TimerRemove: 'TimerRemove',
87 TimerFire: "TimerFire", 1081 TimerFire: 'TimerFire',
88 1082
89 XHRReadyStateChange: "XHRReadyStateChange", 1083 XHRReadyStateChange: 'XHRReadyStateChange',
90 XHRLoad: "XHRLoad", 1084 XHRLoad: 'XHRLoad',
91 CompileScript: "v8.compile", 1085 CompileScript: 'v8.compile',
92 EvaluateScript: "EvaluateScript", 1086 EvaluateScript: 'EvaluateScript',
93 1087
94 CommitLoad: "CommitLoad", 1088 CommitLoad: 'CommitLoad',
95 MarkLoad: "MarkLoad", 1089 MarkLoad: 'MarkLoad',
96 MarkDOMContent: "MarkDOMContent", 1090 MarkDOMContent: 'MarkDOMContent',
97 MarkFirstPaint: "MarkFirstPaint", 1091 MarkFirstPaint: 'MarkFirstPaint',
98 1092
99 TimeStamp: "TimeStamp", 1093 TimeStamp: 'TimeStamp',
100 ConsoleTime: "ConsoleTime", 1094 ConsoleTime: 'ConsoleTime',
101 UserTiming: "UserTiming", 1095 UserTiming: 'UserTiming',
102 1096
103 ResourceSendRequest: "ResourceSendRequest", 1097 ResourceSendRequest: 'ResourceSendRequest',
104 ResourceReceiveResponse: "ResourceReceiveResponse", 1098 ResourceReceiveResponse: 'ResourceReceiveResponse',
105 ResourceReceivedData: "ResourceReceivedData", 1099 ResourceReceivedData: 'ResourceReceivedData',
106 ResourceFinish: "ResourceFinish", 1100 ResourceFinish: 'ResourceFinish',
107 1101
108 RunMicrotasks: "RunMicrotasks", 1102 RunMicrotasks: 'RunMicrotasks',
109 FunctionCall: "FunctionCall", 1103 FunctionCall: 'FunctionCall',
110 GCEvent: "GCEvent", // For backwards compatibility only, now replaced by Min orGC/MajorGC. 1104 GCEvent: 'GCEvent', // For backwards compatibility only, now replaced by Mino rGC/MajorGC.
111 MajorGC: "MajorGC", 1105 MajorGC: 'MajorGC',
112 MinorGC: "MinorGC", 1106 MinorGC: 'MinorGC',
113 JSFrame: "JSFrame", 1107 JSFrame: 'JSFrame',
114 JSSample: "JSSample", 1108 JSSample: 'JSSample',
115 // V8Sample events are coming from tracing and contain raw stacks with funct ion addresses. 1109 // V8Sample events are coming from tracing and contain raw stacks with functio n addresses.
116 // After being processed with help of JitCodeAdded and JitCodeMoved events t hey 1110 // After being processed with help of JitCodeAdded and JitCodeMoved events the y
117 // get translated into function infos and stored as stacks in JSSample event s. 1111 // get translated into function infos and stored as stacks in JSSample events.
118 V8Sample: "V8Sample", 1112 V8Sample: 'V8Sample',
119 JitCodeAdded: "JitCodeAdded", 1113 JitCodeAdded: 'JitCodeAdded',
120 JitCodeMoved: "JitCodeMoved", 1114 JitCodeMoved: 'JitCodeMoved',
121 ParseScriptOnBackground: "v8.parseOnBackground", 1115 ParseScriptOnBackground: 'v8.parseOnBackground',
122 1116
123 UpdateCounters: "UpdateCounters", 1117 UpdateCounters: 'UpdateCounters',
124 1118
125 RequestAnimationFrame: "RequestAnimationFrame", 1119 RequestAnimationFrame: 'RequestAnimationFrame',
126 CancelAnimationFrame: "CancelAnimationFrame", 1120 CancelAnimationFrame: 'CancelAnimationFrame',
127 FireAnimationFrame: "FireAnimationFrame", 1121 FireAnimationFrame: 'FireAnimationFrame',
128 1122
129 RequestIdleCallback: "RequestIdleCallback", 1123 RequestIdleCallback: 'RequestIdleCallback',
130 CancelIdleCallback: "CancelIdleCallback", 1124 CancelIdleCallback: 'CancelIdleCallback',
131 FireIdleCallback: "FireIdleCallback", 1125 FireIdleCallback: 'FireIdleCallback',
132 1126
133 WebSocketCreate : "WebSocketCreate", 1127 WebSocketCreate: 'WebSocketCreate',
134 WebSocketSendHandshakeRequest : "WebSocketSendHandshakeRequest", 1128 WebSocketSendHandshakeRequest: 'WebSocketSendHandshakeRequest',
135 WebSocketReceiveHandshakeResponse : "WebSocketReceiveHandshakeResponse", 1129 WebSocketReceiveHandshakeResponse: 'WebSocketReceiveHandshakeResponse',
136 WebSocketDestroy : "WebSocketDestroy", 1130 WebSocketDestroy: 'WebSocketDestroy',
137 1131
138 EmbedderCallback : "EmbedderCallback", 1132 EmbedderCallback: 'EmbedderCallback',
139 1133
140 SetLayerTreeId: "SetLayerTreeId", 1134 SetLayerTreeId: 'SetLayerTreeId',
141 TracingStartedInPage: "TracingStartedInPage", 1135 TracingStartedInPage: 'TracingStartedInPage',
142 TracingSessionIdForWorker: "TracingSessionIdForWorker", 1136 TracingSessionIdForWorker: 'TracingSessionIdForWorker',
143 1137
144 DecodeImage: "Decode Image", 1138 DecodeImage: 'Decode Image',
145 ResizeImage: "Resize Image", 1139 ResizeImage: 'Resize Image',
146 DrawLazyPixelRef: "Draw LazyPixelRef", 1140 DrawLazyPixelRef: 'Draw LazyPixelRef',
147 DecodeLazyPixelRef: "Decode LazyPixelRef", 1141 DecodeLazyPixelRef: 'Decode LazyPixelRef',
148 1142
149 LazyPixelRef: "LazyPixelRef", 1143 LazyPixelRef: 'LazyPixelRef',
150 LayerTreeHostImplSnapshot: "cc::LayerTreeHostImpl", 1144 LayerTreeHostImplSnapshot: 'cc::LayerTreeHostImpl',
151 PictureSnapshot: "cc::Picture", 1145 PictureSnapshot: 'cc::Picture',
152 DisplayItemListSnapshot: "cc::DisplayItemList", 1146 DisplayItemListSnapshot: 'cc::DisplayItemList',
153 LatencyInfo: "LatencyInfo", 1147 LatencyInfo: 'LatencyInfo',
154 LatencyInfoFlow: "LatencyInfo.Flow", 1148 LatencyInfoFlow: 'LatencyInfo.Flow',
155 InputLatencyMouseMove: "InputLatency::MouseMove", 1149 InputLatencyMouseMove: 'InputLatency::MouseMove',
156 InputLatencyMouseWheel: "InputLatency::MouseWheel", 1150 InputLatencyMouseWheel: 'InputLatency::MouseWheel',
157 ImplSideFling: "InputHandlerProxy::HandleGestureFling::started", 1151 ImplSideFling: 'InputHandlerProxy::HandleGestureFling::started',
158 GCIdleLazySweep: "ThreadState::performIdleLazySweep", 1152 GCIdleLazySweep: 'ThreadState::performIdleLazySweep',
159 GCCompleteSweep: "ThreadState::completeSweep", 1153 GCCompleteSweep: 'ThreadState::completeSweep',
160 GCCollectGarbage: "BlinkGCMarking", 1154 GCCollectGarbage: 'BlinkGCMarking',
161 1155
162 // CpuProfile is a virtual event created on frontend to support 1156 // CpuProfile is a virtual event created on frontend to support
163 // serialization of CPU Profiles within tracing timeline data. 1157 // serialization of CPU Profiles within tracing timeline data.
164 CpuProfile: "CpuProfile", 1158 CpuProfile: 'CpuProfile',
165 Profile: "Profile" 1159 Profile: 'Profile'
166 }; 1160 };
167 1161
168 WebInspector.TimelineModel.Category = { 1162 WebInspector.TimelineModel.Category = {
169 Console: "blink.console", 1163 Console: 'blink.console',
170 UserTiming: "blink.user_timing", 1164 UserTiming: 'blink.user_timing',
171 LatencyInfo: "latencyInfo" 1165 LatencyInfo: 'latencyInfo'
172 }; 1166 };
173 1167
174 /** 1168 /**
175 * @enum {string} 1169 * @enum {string}
176 */ 1170 */
177 WebInspector.TimelineModel.WarningType = { 1171 WebInspector.TimelineModel.WarningType = {
178 ForcedStyle: "ForcedStyle", 1172 ForcedStyle: 'ForcedStyle',
179 ForcedLayout: "ForcedLayout", 1173 ForcedLayout: 'ForcedLayout',
180 IdleDeadlineExceeded: "IdleDeadlineExceeded", 1174 IdleDeadlineExceeded: 'IdleDeadlineExceeded',
181 V8Deopt: "V8Deopt" 1175 V8Deopt: 'V8Deopt'
182 }; 1176 };
183 1177
184 WebInspector.TimelineModel.MainThreadName = "main"; 1178 WebInspector.TimelineModel.MainThreadName = 'main';
185 WebInspector.TimelineModel.WorkerThreadName = "DedicatedWorker Thread"; 1179 WebInspector.TimelineModel.WorkerThreadName = 'DedicatedWorker Thread';
186 WebInspector.TimelineModel.RendererMainThreadName = "CrRendererMain"; 1180 WebInspector.TimelineModel.RendererMainThreadName = 'CrRendererMain';
187 1181
188 /** 1182 /**
189 * @enum {symbol} 1183 * @enum {symbol}
190 */ 1184 */
191 WebInspector.TimelineModel.AsyncEventGroup = { 1185 WebInspector.TimelineModel.AsyncEventGroup = {
192 animation: Symbol("animation"), 1186 animation: Symbol('animation'),
193 console: Symbol("console"), 1187 console: Symbol('console'),
194 userTiming: Symbol("userTiming"), 1188 userTiming: Symbol('userTiming'),
195 input: Symbol("input") 1189 input: Symbol('input')
196 }; 1190 };
197 1191
1192
1193 WebInspector.TimelineModel.DevToolsMetadataEvent = {
1194 TracingStartedInBrowser: 'TracingStartedInBrowser',
1195 TracingStartedInPage: 'TracingStartedInPage',
1196 TracingSessionIdForWorker: 'TracingSessionIdForWorker',
1197 };
1198
198 /** 1199 /**
199 * @param {!Array.<!WebInspector.TracingModel.Event>} events 1200 * @unrestricted
200 * @param {function(!WebInspector.TracingModel.Event)} onStartEvent
201 * @param {function(!WebInspector.TracingModel.Event)} onEndEvent
202 * @param {function(!WebInspector.TracingModel.Event,?WebInspector.TracingModel. Event)|undefined=} onInstantEvent
203 * @param {number=} startTime
204 * @param {number=} endTime
205 */ 1201 */
206 WebInspector.TimelineModel.forEachEvent = function(events, onStartEvent, onEndEv ent, onInstantEvent, startTime, endTime) 1202 WebInspector.TimelineModel.VirtualThread = class {
207 { 1203 /**
208 startTime = startTime || 0; 1204 * @param {string} name
209 endTime = endTime || Infinity; 1205 */
210 var stack = []; 1206 constructor(name) {
211 for (var i = 0; i < events.length; ++i) {
212 var e = events[i];
213 if ((e.endTime || e.startTime) < startTime)
214 continue;
215 if (e.startTime >= endTime)
216 break;
217 if (WebInspector.TracingModel.isAsyncPhase(e.phase) || WebInspector.Trac ingModel.isFlowPhase(e.phase))
218 continue;
219 while (stack.length && stack.peekLast().endTime <= e.startTime)
220 onEndEvent(stack.pop());
221 if (e.duration) {
222 onStartEvent(e);
223 stack.push(e);
224 } else {
225 onInstantEvent && onInstantEvent(e, stack.peekLast() || null);
226 }
227 }
228 while (stack.length)
229 onEndEvent(stack.pop());
230 };
231
232 WebInspector.TimelineModel.DevToolsMetadataEvent = {
233 TracingStartedInBrowser: "TracingStartedInBrowser",
234 TracingStartedInPage: "TracingStartedInPage",
235 TracingSessionIdForWorker: "TracingSessionIdForWorker",
236 };
237
238 /**
239 * @constructor
240 * @param {string} name
241 */
242 WebInspector.TimelineModel.VirtualThread = function(name)
243 {
244 this.name = name; 1207 this.name = name;
245 /** @type {!Array<!WebInspector.TracingModel.Event>} */ 1208 /** @type {!Array<!WebInspector.TracingModel.Event>} */
246 this.events = []; 1209 this.events = [];
247 /** @type {!Map<!WebInspector.TimelineModel.AsyncEventGroup, !Array<!WebInsp ector.TracingModel.AsyncEvent>>} */ 1210 /** @type {!Map<!WebInspector.TimelineModel.AsyncEventGroup, !Array<!WebInsp ector.TracingModel.AsyncEvent>>} */
248 this.asyncEventsByGroup = new Map(); 1211 this.asyncEventsByGroup = new Map();
1212 }
1213
1214 /**
1215 * @return {boolean}
1216 */
1217 isWorker() {
1218 return this.name === WebInspector.TimelineModel.WorkerThreadName;
1219 }
249 }; 1220 };
250 1221
251 WebInspector.TimelineModel.VirtualThread.prototype = {
252 /**
253 * @return {boolean}
254 */
255 isWorker: function()
256 {
257 return this.name === WebInspector.TimelineModel.WorkerThreadName;
258 }
259 };
260
261 /** 1222 /**
262 * @constructor 1223 * @unrestricted
263 * @param {!WebInspector.TracingModel.Event} traceEvent
264 */ 1224 */
265 WebInspector.TimelineModel.Record = function(traceEvent) 1225 WebInspector.TimelineModel.Record = class {
266 { 1226 /**
1227 * @param {!WebInspector.TracingModel.Event} traceEvent
1228 */
1229 constructor(traceEvent) {
267 this._event = traceEvent; 1230 this._event = traceEvent;
268 this._children = []; 1231 this._children = [];
269 }; 1232 }
270 1233
271 /** 1234 /**
272 * @param {!WebInspector.TimelineModel.Record} a 1235 * @param {!WebInspector.TimelineModel.Record} a
273 * @param {!WebInspector.TimelineModel.Record} b 1236 * @param {!WebInspector.TimelineModel.Record} b
274 * @return {number} 1237 * @return {number}
275 */ 1238 */
276 WebInspector.TimelineModel.Record._compareStartTime = function(a, b) 1239 static _compareStartTime(a, b) {
277 {
278 // Never return 0 as otherwise equal records would be merged. 1240 // Never return 0 as otherwise equal records would be merged.
279 return a.startTime() <= b.startTime() ? -1 : 1; 1241 return a.startTime() <= b.startTime() ? -1 : 1;
1242 }
1243
1244 /**
1245 * @return {?WebInspector.Target}
1246 */
1247 target() {
1248 var threadName = this._event.thread.name();
1249 // FIXME: correctly specify target
1250 return threadName === WebInspector.TimelineModel.RendererMainThreadName ?
1251 WebInspector.targetManager.targets()[0] || null :
1252 null;
1253 }
1254
1255 /**
1256 * @return {!Array.<!WebInspector.TimelineModel.Record>}
1257 */
1258 children() {
1259 return this._children;
1260 }
1261
1262 /**
1263 * @return {number}
1264 */
1265 startTime() {
1266 return this._event.startTime;
1267 }
1268
1269 /**
1270 * @return {number}
1271 */
1272 endTime() {
1273 return this._event.endTime || this._event.startTime;
1274 }
1275
1276 /**
1277 * @return {string}
1278 */
1279 thread() {
1280 if (this._event.thread.name() === WebInspector.TimelineModel.RendererMainThr eadName)
1281 return WebInspector.TimelineModel.MainThreadName;
1282 return this._event.thread.name();
1283 }
1284
1285 /**
1286 * @return {!WebInspector.TimelineModel.RecordType}
1287 */
1288 type() {
1289 return WebInspector.TimelineModel._eventType(this._event);
1290 }
1291
1292 /**
1293 * @param {string} key
1294 * @return {?Object}
1295 */
1296 getUserObject(key) {
1297 if (key === 'TimelineUIUtils::preview-element')
1298 return this._event.previewElement;
1299 throw new Error('Unexpected key: ' + key);
1300 }
1301
1302 /**
1303 * @param {string} key
1304 * @param {?Object|undefined} value
1305 */
1306 setUserObject(key, value) {
1307 if (key !== 'TimelineUIUtils::preview-element')
1308 throw new Error('Unexpected key: ' + key);
1309 this._event.previewElement = /** @type {?Element} */ (value);
1310 }
1311
1312 /**
1313 * @return {!WebInspector.TracingModel.Event}
1314 */
1315 traceEvent() {
1316 return this._event;
1317 }
1318
1319 /**
1320 * @param {!WebInspector.TimelineModel.Record} child
1321 */
1322 _addChild(child) {
1323 this._children.push(child);
1324 child.parent = this;
1325 }
280 }; 1326 };
281 1327
282 WebInspector.TimelineModel.Record.prototype = {
283 /**
284 * @return {?WebInspector.Target}
285 */
286 target: function()
287 {
288 var threadName = this._event.thread.name();
289 // FIXME: correctly specify target
290 return threadName === WebInspector.TimelineModel.RendererMainThreadName ? WebInspector.targetManager.targets()[0] || null : null;
291 },
292
293 /**
294 * @return {!Array.<!WebInspector.TimelineModel.Record>}
295 */
296 children: function()
297 {
298 return this._children;
299 },
300
301 /**
302 * @return {number}
303 */
304 startTime: function()
305 {
306 return this._event.startTime;
307 },
308
309 /**
310 * @return {number}
311 */
312 endTime: function()
313 {
314 return this._event.endTime || this._event.startTime;
315 },
316
317 /**
318 * @return {string}
319 */
320 thread: function()
321 {
322 if (this._event.thread.name() === WebInspector.TimelineModel.RendererMai nThreadName)
323 return WebInspector.TimelineModel.MainThreadName;
324 return this._event.thread.name();
325 },
326
327 /**
328 * @return {!WebInspector.TimelineModel.RecordType}
329 */
330 type: function()
331 {
332 return WebInspector.TimelineModel._eventType(this._event);
333 },
334
335 /**
336 * @param {string} key
337 * @return {?Object}
338 */
339 getUserObject: function(key)
340 {
341 if (key === "TimelineUIUtils::preview-element")
342 return this._event.previewElement;
343 throw new Error("Unexpected key: " + key);
344 },
345
346 /**
347 * @param {string} key
348 * @param {?Object|undefined} value
349 */
350 setUserObject: function(key, value)
351 {
352 if (key !== "TimelineUIUtils::preview-element")
353 throw new Error("Unexpected key: " + key);
354 this._event.previewElement = /** @type {?Element} */ (value);
355 },
356
357 /**
358 * @return {!WebInspector.TracingModel.Event}
359 */
360 traceEvent: function()
361 {
362 return this._event;
363 },
364
365 /**
366 * @param {!WebInspector.TimelineModel.Record} child
367 */
368 _addChild: function(child)
369 {
370 this._children.push(child);
371 child.parent = this;
372 }
373 };
374 1328
375 /** @typedef {!{page: !Array<!WebInspector.TracingModel.Event>, workers: !Array< !WebInspector.TracingModel.Event>}} */ 1329 /** @typedef {!{page: !Array<!WebInspector.TracingModel.Event>, workers: !Array< !WebInspector.TracingModel.Event>}} */
376 WebInspector.TimelineModel.MetadataEvents; 1330 WebInspector.TimelineModel.MetadataEvents;
377 1331
1332
378 /** 1333 /**
379 * @return {!WebInspector.TimelineModel.RecordType} 1334 * @unrestricted
380 */ 1335 */
381 WebInspector.TimelineModel._eventType = function(event) 1336 WebInspector.TimelineModel.NetworkRequest = class {
382 { 1337 /**
383 if (event.hasCategory(WebInspector.TimelineModel.Category.Console)) 1338 * @param {!WebInspector.TracingModel.Event} event
384 return WebInspector.TimelineModel.RecordType.ConsoleTime; 1339 */
385 if (event.hasCategory(WebInspector.TimelineModel.Category.UserTiming)) 1340 constructor(event) {
386 return WebInspector.TimelineModel.RecordType.UserTiming;
387 if (event.hasCategory(WebInspector.TimelineModel.Category.LatencyInfo))
388 return WebInspector.TimelineModel.RecordType.LatencyInfo;
389 return /** @type !WebInspector.TimelineModel.RecordType */ (event.name);
390 };
391
392 WebInspector.TimelineModel.prototype = {
393 /**
394 * @deprecated Test use only!
395 * @param {?function(!WebInspector.TimelineModel.Record)|?function(!WebInspe ctor.TimelineModel.Record,number)} preOrderCallback
396 * @param {function(!WebInspector.TimelineModel.Record)|function(!WebInspect or.TimelineModel.Record,number)=} postOrderCallback
397 * @return {boolean}
398 */
399 forAllRecords: function(preOrderCallback, postOrderCallback)
400 {
401 /**
402 * @param {!Array.<!WebInspector.TimelineModel.Record>} records
403 * @param {number} depth
404 * @return {boolean}
405 */
406 function processRecords(records, depth)
407 {
408 for (var i = 0; i < records.length; ++i) {
409 var record = records[i];
410 if (preOrderCallback && preOrderCallback(record, depth))
411 return true;
412 if (processRecords(record.children(), depth + 1))
413 return true;
414 if (postOrderCallback && postOrderCallback(record, depth))
415 return true;
416 }
417 return false;
418 }
419 return processRecords(this._records, 0);
420 },
421
422 /**
423 * @param {!Array<!WebInspector.TimelineModel.Filter>} filters
424 * @param {function(!WebInspector.TimelineModel.Record)|function(!WebInspect or.TimelineModel.Record,number)} callback
425 */
426 forAllFilteredRecords: function(filters, callback)
427 {
428 /**
429 * @param {!WebInspector.TimelineModel.Record} record
430 * @param {number} depth
431 * @this {WebInspector.TimelineModel}
432 * @return {boolean}
433 */
434 function processRecord(record, depth)
435 {
436 var visible = WebInspector.TimelineModel.isVisible(filters, record.t raceEvent());
437 if (visible && callback(record, depth))
438 return true;
439
440 for (var i = 0; i < record.children().length; ++i) {
441 if (processRecord.call(this, record.children()[i], visible ? dep th + 1 : depth))
442 return true;
443 }
444 return false;
445 }
446
447 for (var i = 0; i < this._records.length; ++i)
448 processRecord.call(this, this._records[i], 0);
449 },
450
451 /**
452 * @return {!Array.<!WebInspector.TimelineModel.Record>}
453 */
454 records: function()
455 {
456 return this._records;
457 },
458
459 /**
460 * @return {!Array<!WebInspector.CPUProfileDataModel>}
461 */
462 cpuProfiles: function()
463 {
464 return this._cpuProfiles;
465 },
466
467 /**
468 * @return {?string}
469 */
470 sessionId: function()
471 {
472 return this._sessionId;
473 },
474
475 /**
476 * @param {!WebInspector.TracingModel.Event} event
477 * @return {?WebInspector.Target}
478 */
479 targetByEvent: function(event)
480 {
481 // FIXME: Consider returning null for loaded traces.
482 var workerId = this._workerIdByThread.get(event.thread);
483 var mainTarget = WebInspector.targetManager.mainTarget();
484 return workerId ? mainTarget.subTargetsManager.targetForId(workerId) : m ainTarget;
485 },
486
487 /**
488 * @param {!WebInspector.TracingModel} tracingModel
489 * @param {boolean=} produceTraceStartedInPage
490 */
491 setEvents: function(tracingModel, produceTraceStartedInPage)
492 {
493 this.reset();
494 this._resetProcessingState();
495
496 this._minimumRecordTime = tracingModel.minimumRecordTime();
497 this._maximumRecordTime = tracingModel.maximumRecordTime();
498
499 var metadataEvents = this._processMetadataEvents(tracingModel, !!produce TraceStartedInPage);
500 if (Runtime.experiments.isEnabled("timelineShowAllProcesses")) {
501 var lastPageMetaEvent = metadataEvents.page.peekLast();
502 for (var process of tracingModel.sortedProcesses()) {
503 for (var thread of process.sortedThreads())
504 this._processThreadEvents(tracingModel, 0, Infinity, thread, thread === lastPageMetaEvent.thread);
505 }
506 } else {
507 var startTime = 0;
508 for (var i = 0, length = metadataEvents.page.length; i < length; i++ ) {
509 var metaEvent = metadataEvents.page[i];
510 var process = metaEvent.thread.process();
511 var endTime = i + 1 < length ? metadataEvents.page[i + 1].startT ime : Infinity;
512 this._currentPage = metaEvent.args["data"] && metaEvent.args["da ta"]["page"];
513 for (var thread of process.sortedThreads()) {
514 if (thread.name() === WebInspector.TimelineModel.WorkerThrea dName) {
515 var workerMetaEvent = metadataEvents.workers.find(e => e .args["data"]["workerThreadId"] === thread.id());
516 if (!workerMetaEvent)
517 continue;
518 var workerId = workerMetaEvent.args["data"]["workerId"];
519 if (workerId)
520 this._workerIdByThread.set(thread, workerId);
521 }
522 this._processThreadEvents(tracingModel, startTime, endTime, thread, thread === metaEvent.thread);
523 }
524 startTime = endTime;
525 }
526 }
527 this._inspectedTargetEvents.sort(WebInspector.TracingModel.Event.compare StartTime);
528
529 this._processBrowserEvents(tracingModel);
530 this._buildTimelineRecords();
531 this._buildGPUEvents(tracingModel);
532 this._insertFirstPaintEvent();
533 this._resetProcessingState();
534 },
535
536 /**
537 * @param {!WebInspector.TracingModel} tracingModel
538 * @param {boolean} produceTraceStartedInPage
539 * @return {!WebInspector.TimelineModel.MetadataEvents}
540 */
541 _processMetadataEvents: function(tracingModel, produceTraceStartedInPage)
542 {
543 var metadataEvents = tracingModel.devToolsMetadataEvents();
544
545 var pageDevToolsMetadataEvents = [];
546 var workersDevToolsMetadataEvents = [];
547 for (var event of metadataEvents) {
548 if (event.name === WebInspector.TimelineModel.DevToolsMetadataEvent. TracingStartedInPage) {
549 pageDevToolsMetadataEvents.push(event);
550 } else if (event.name === WebInspector.TimelineModel.DevToolsMetadat aEvent.TracingSessionIdForWorker) {
551 workersDevToolsMetadataEvents.push(event);
552 } else if (event.name === WebInspector.TimelineModel.DevToolsMetadat aEvent.TracingStartedInBrowser) {
553 console.assert(!this._mainFrameNodeId, "Multiple sessions in tra ce");
554 this._mainFrameNodeId = event.args["frameTreeNodeId"];
555 }
556 }
557 if (!pageDevToolsMetadataEvents.length) {
558 // The trace is probably coming not from DevTools. Make a mock Metad ata event.
559 var pageMetaEvent = produceTraceStartedInPage ? this._makeMockPageMe tadataEvent(tracingModel) : null;
560 if (!pageMetaEvent) {
561 console.error(WebInspector.TimelineModel.DevToolsMetadataEvent.T racingStartedInPage + " event not found.");
562 return {page: [], workers: []};
563 }
564 pageDevToolsMetadataEvents.push(pageMetaEvent);
565 }
566 var sessionId = pageDevToolsMetadataEvents[0].args["sessionId"] || pageD evToolsMetadataEvents[0].args["data"]["sessionId"];
567 this._sessionId = sessionId;
568
569 var mismatchingIds = new Set();
570 /**
571 * @param {!WebInspector.TracingModel.Event} event
572 * @return {boolean}
573 */
574 function checkSessionId(event)
575 {
576 var args = event.args;
577 // FIXME: put sessionId into args["data"] for TracingStartedInPage e vent.
578 if (args["data"])
579 args = args["data"];
580 var id = args["sessionId"];
581 if (id === sessionId)
582 return true;
583 mismatchingIds.add(id);
584 return false;
585 }
586 var result = {
587 page: pageDevToolsMetadataEvents.filter(checkSessionId).sort(WebInsp ector.TracingModel.Event.compareStartTime),
588 workers: workersDevToolsMetadataEvents.filter(checkSessionId).sort(W ebInspector.TracingModel.Event.compareStartTime)
589 };
590 if (mismatchingIds.size)
591 WebInspector.console.error("Timeline recording was started in more t han one page simultaneously. Session id mismatch: " + this._sessionId + " and " + mismatchingIds.valuesArray() + ".");
592 return result;
593 },
594
595 /**
596 * @param {!WebInspector.TracingModel} tracingModel
597 * @return {?WebInspector.TracingModel.Event}
598 */
599 _makeMockPageMetadataEvent: function(tracingModel)
600 {
601 var rendererMainThreadName = WebInspector.TimelineModel.RendererMainThre adName;
602 // FIXME: pick up the first renderer process for now.
603 var process = tracingModel.sortedProcesses().filter(function(p) { return p.threadByName(rendererMainThreadName); })[0];
604 var thread = process && process.threadByName(rendererMainThreadName);
605 if (!thread)
606 return null;
607 var pageMetaEvent = new WebInspector.TracingModel.Event(
608 WebInspector.TracingModel.DevToolsMetadataEventCategory,
609 WebInspector.TimelineModel.DevToolsMetadataEvent.TracingStartedInPag e,
610 WebInspector.TracingModel.Phase.Metadata,
611 tracingModel.minimumRecordTime(), thread);
612 pageMetaEvent.addArgs({"data": {"sessionId": "mockSessionId"}});
613 return pageMetaEvent;
614 },
615
616 _insertFirstPaintEvent: function()
617 {
618 if (!this._firstCompositeLayers)
619 return;
620
621 // First Paint is actually a DrawFrame that happened after first Composi teLayers following last CommitLoadEvent.
622 var recordTypes = WebInspector.TimelineModel.RecordType;
623 var i = this._inspectedTargetEvents.lowerBound(this._firstCompositeLayer s, WebInspector.TracingModel.Event.compareStartTime);
624 for (; i < this._inspectedTargetEvents.length && this._inspectedTargetEv ents[i].name !== recordTypes.DrawFrame; ++i) { }
625 if (i >= this._inspectedTargetEvents.length)
626 return;
627 var drawFrameEvent = this._inspectedTargetEvents[i];
628 var firstPaintEvent = new WebInspector.TracingModel.Event(drawFrameEvent .categoriesString, recordTypes.MarkFirstPaint, WebInspector.TracingModel.Phase.I nstant, drawFrameEvent.startTime, drawFrameEvent.thread);
629 this._mainThreadEvents.splice(this._mainThreadEvents.lowerBound(firstPai ntEvent, WebInspector.TracingModel.Event.compareStartTime), 0, firstPaintEvent);
630 var firstPaintRecord = new WebInspector.TimelineModel.Record(firstPaintE vent);
631 this._eventDividerRecords.splice(this._eventDividerRecords.lowerBound(fi rstPaintRecord, WebInspector.TimelineModel.Record._compareStartTime), 0, firstPa intRecord);
632 },
633
634 /**
635 * @param {!WebInspector.TracingModel} tracingModel
636 */
637 _processBrowserEvents: function(tracingModel)
638 {
639 var browserMain = WebInspector.TracingModel.browserMainThread(tracingMod el);
640 if (!browserMain)
641 return;
642
643 // Disregard regular events, we don't need them yet, but still process t o get proper metadata.
644 browserMain.events().forEach(this._processBrowserEvent, this);
645 /** @type {!Map<!WebInspector.TimelineModel.AsyncEventGroup, !Array<!Web Inspector.TracingModel.AsyncEvent>>} */
646 var asyncEventsByGroup = new Map();
647 this._processAsyncEvents(asyncEventsByGroup, browserMain.asyncEvents());
648 this._mergeAsyncEvents(this._mainThreadAsyncEventsByGroup, asyncEventsBy Group);
649 },
650
651 _buildTimelineRecords: function()
652 {
653 var topLevelRecords = this._buildTimelineRecordsForThread(this.mainThrea dEvents());
654 for (var i = 0; i < topLevelRecords.length; i++) {
655 var record = topLevelRecords[i];
656 if (WebInspector.TracingModel.isTopLevelEvent(record.traceEvent()))
657 this._mainThreadTasks.push(record);
658 }
659
660 /**
661 * @param {!WebInspector.TimelineModel.VirtualThread} virtualThread
662 * @this {!WebInspector.TimelineModel}
663 */
664 function processVirtualThreadEvents(virtualThread)
665 {
666 var threadRecords = this._buildTimelineRecordsForThread(virtualThrea d.events);
667 topLevelRecords = topLevelRecords.mergeOrdered(threadRecords, WebIns pector.TimelineModel.Record._compareStartTime);
668 }
669 this.virtualThreads().forEach(processVirtualThreadEvents.bind(this));
670 this._records = topLevelRecords;
671 },
672
673 /**
674 * @param {!WebInspector.TracingModel} tracingModel
675 */
676 _buildGPUEvents: function(tracingModel)
677 {
678 var thread = tracingModel.threadByName("GPU Process", "CrGpuMain");
679 if (!thread)
680 return;
681 var gpuEventName = WebInspector.TimelineModel.RecordType.GPUTask;
682 this._gpuEvents = thread.events().filter(event => event.name === gpuEven tName);
683 },
684
685 /**
686 * @param {!Array.<!WebInspector.TracingModel.Event>} threadEvents
687 * @return {!Array.<!WebInspector.TimelineModel.Record>}
688 */
689 _buildTimelineRecordsForThread: function(threadEvents)
690 {
691 var recordStack = [];
692 var topLevelRecords = [];
693
694 for (var i = 0, size = threadEvents.length; i < size; ++i) {
695 var event = threadEvents[i];
696 for (var top = recordStack.peekLast(); top && top._event.endTime <= event.startTime; top = recordStack.peekLast())
697 recordStack.pop();
698 if (event.phase === WebInspector.TracingModel.Phase.AsyncEnd || even t.phase === WebInspector.TracingModel.Phase.NestableAsyncEnd)
699 continue;
700 var parentRecord = recordStack.peekLast();
701 // Maintain the back-end logic of old timeline, skip console.time() / console.timeEnd() that are not properly nested.
702 if (WebInspector.TracingModel.isAsyncBeginPhase(event.phase) && pare ntRecord && event.endTime > parentRecord._event.endTime)
703 continue;
704 var record = new WebInspector.TimelineModel.Record(event);
705 if (WebInspector.TimelineModel.isMarkerEvent(event))
706 this._eventDividerRecords.push(record);
707 if (!this._eventFilter.accept(event) && !WebInspector.TracingModel.i sTopLevelEvent(event))
708 continue;
709 if (parentRecord)
710 parentRecord._addChild(record);
711 else
712 topLevelRecords.push(record);
713 if (event.endTime)
714 recordStack.push(record);
715 }
716
717 return topLevelRecords;
718 },
719
720 _resetProcessingState: function()
721 {
722 this._asyncEventTracker = new WebInspector.TimelineAsyncEventTracker();
723 this._invalidationTracker = new WebInspector.InvalidationTracker();
724 this._layoutInvalidate = {};
725 this._lastScheduleStyleRecalculation = {};
726 this._paintImageEventByPixelRefId = {};
727 this._lastPaintForLayer = {};
728 this._lastRecalculateStylesEvent = null;
729 this._currentScriptEvent = null;
730 this._eventStack = [];
731 this._hadCommitLoad = false;
732 this._firstCompositeLayers = null;
733 /** @type {!Set<string>} */
734 this._knownInputEvents = new Set();
735 this._currentPage = null;
736 },
737
738 /**
739 * @param {!WebInspector.TracingModel} tracingModel
740 * @param {!WebInspector.TracingModel.Thread} thread
741 * @return {?WebInspector.CPUProfileDataModel}
742 */
743 _extractCpuProfile: function(tracingModel, thread)
744 {
745 var events = thread.events();
746 var cpuProfile;
747
748 // Check for legacy CpuProfile event format first.
749 var cpuProfileEvent = events.peekLast();
750 if (cpuProfileEvent && cpuProfileEvent.name === WebInspector.TimelineMod el.RecordType.CpuProfile) {
751 var eventData = cpuProfileEvent.args["data"];
752 cpuProfile = /** @type {?ProfilerAgent.Profile} */ (eventData && eve ntData["cpuProfile"]);
753 }
754
755 if (!cpuProfile) {
756 cpuProfileEvent = events.find(e => e.name === WebInspector.TimelineM odel.RecordType.Profile);
757 if (!cpuProfileEvent)
758 return null;
759 var profileGroup = tracingModel.profileGroup(cpuProfileEvent.id);
760 if (!profileGroup) {
761 WebInspector.console.error("Invalid CPU profile format.");
762 return null;
763 }
764 cpuProfile = /** @type {!ProfilerAgent.Profile} */ ({
765 startTime: cpuProfileEvent.args["data"]["startTime"],
766 endTime: 0,
767 nodes: [],
768 samples: [],
769 timeDeltas: []
770 });
771 for (var profileEvent of profileGroup.children) {
772 var eventData = profileEvent.args["data"];
773 if ("startTime" in eventData)
774 cpuProfile.startTime = eventData["startTime"];
775 if ("endTime" in eventData)
776 cpuProfile.endTime = eventData["endTime"];
777 var nodesAndSamples = eventData["cpuProfile"] || {};
778 cpuProfile.nodes.pushAll(nodesAndSamples["nodes"] || []);
779 cpuProfile.samples.pushAll(nodesAndSamples["samples"] || []);
780 cpuProfile.timeDeltas.pushAll(eventData["timeDeltas"] || []);
781 if (cpuProfile.samples.length !== cpuProfile.timeDeltas.length) {
782 WebInspector.console.error("Failed to parse CPU profile.");
783 return null;
784 }
785 }
786 if (!cpuProfile.endTime)
787 cpuProfile.endTime = cpuProfile.timeDeltas.reduce((x, y) => x + y, cpuProfile.startTime);
788 }
789
790 try {
791 var jsProfileModel = new WebInspector.CPUProfileDataModel(cpuProfile );
792 this._cpuProfiles.push(jsProfileModel);
793 return jsProfileModel;
794 } catch (e) {
795 WebInspector.console.error("Failed to parse CPU profile.");
796 }
797 return null;
798 },
799
800 /**
801 * @param {!WebInspector.TracingModel} tracingModel
802 * @param {!WebInspector.TracingModel.Thread} thread
803 * @return {!Array<!WebInspector.TracingModel.Event>}
804 */
805 _injectJSFrameEvents: function(tracingModel, thread)
806 {
807 var jsProfileModel = this._extractCpuProfile(tracingModel, thread);
808 var events = thread.events();
809 var jsSamples = jsProfileModel ? WebInspector.TimelineJSProfileProcessor .generateTracingEventsFromCpuProfile(jsProfileModel, thread) : null;
810 if (jsSamples && jsSamples.length)
811 events = events.mergeOrdered(jsSamples, WebInspector.TracingModel.Ev ent.orderedCompareStartTime);
812 if (jsSamples || events.some(e => e.name === WebInspector.TimelineModel. RecordType.JSSample)) {
813 var jsFrameEvents = WebInspector.TimelineJSProfileProcessor.generate JSFrameEvents(events);
814 if (jsFrameEvents && jsFrameEvents.length)
815 events = jsFrameEvents.mergeOrdered(events, WebInspector.Tracing Model.Event.orderedCompareStartTime);
816 }
817 return events;
818 },
819
820 /**
821 * @param {!WebInspector.TracingModel} tracingModel
822 * @param {number} startTime
823 * @param {number} endTime
824 * @param {!WebInspector.TracingModel.Thread} thread
825 * @param {boolean} isMainThread
826 */
827 _processThreadEvents: function(tracingModel, startTime, endTime, thread, isM ainThread)
828 {
829 var events = this._injectJSFrameEvents(tracingModel, thread);
830 var asyncEvents = thread.asyncEvents();
831
832 var threadEvents;
833 var threadAsyncEventsByGroup;
834 if (isMainThread) {
835 threadEvents = this._mainThreadEvents;
836 threadAsyncEventsByGroup = this._mainThreadAsyncEventsByGroup;
837 } else {
838 var virtualThread = new WebInspector.TimelineModel.VirtualThread(thr ead.name());
839 this._virtualThreads.push(virtualThread);
840 threadEvents = virtualThread.events;
841 threadAsyncEventsByGroup = virtualThread.asyncEventsByGroup;
842 }
843
844 this._eventStack = [];
845 var i = events.lowerBound(startTime, (time, event) => time - event.start Time);
846 var length = events.length;
847 for (; i < length; i++) {
848 var event = events[i];
849 if (endTime && event.startTime >= endTime)
850 break;
851 if (!this._processEvent(event))
852 continue;
853 threadEvents.push(event);
854 this._inspectedTargetEvents.push(event);
855 }
856 this._processAsyncEvents(threadAsyncEventsByGroup, asyncEvents, startTim e, endTime);
857 // Pretend the compositor's async events are on the main thread.
858 if (thread.name() === "Compositor") {
859 this._mergeAsyncEvents(this._mainThreadAsyncEventsByGroup, threadAsy ncEventsByGroup);
860 threadAsyncEventsByGroup.clear();
861 }
862 },
863
864 /**
865 * @param {!Map<!WebInspector.TimelineModel.AsyncEventGroup, !Array<!WebInsp ector.TracingModel.AsyncEvent>>} asyncEventsByGroup
866 * @param {!Array<!WebInspector.TracingModel.AsyncEvent>} asyncEvents
867 * @param {number=} startTime
868 * @param {number=} endTime
869 */
870 _processAsyncEvents: function(asyncEventsByGroup, asyncEvents, startTime, en dTime)
871 {
872 var i = startTime ? asyncEvents.lowerBound(startTime, function(time, asy ncEvent) { return time - asyncEvent.startTime; }) : 0;
873 for (; i < asyncEvents.length; ++i) {
874 var asyncEvent = asyncEvents[i];
875 if (endTime && asyncEvent.startTime >= endTime)
876 break;
877 var asyncGroup = this._processAsyncEvent(asyncEvent);
878 if (!asyncGroup)
879 continue;
880 var groupAsyncEvents = asyncEventsByGroup.get(asyncGroup);
881 if (!groupAsyncEvents) {
882 groupAsyncEvents = [];
883 asyncEventsByGroup.set(asyncGroup, groupAsyncEvents);
884 }
885 groupAsyncEvents.push(asyncEvent);
886 }
887 },
888
889 /**
890 * @param {!WebInspector.TracingModel.Event} event
891 * @return {boolean}
892 */
893 _processEvent: function(event)
894 {
895 var eventStack = this._eventStack;
896 while (eventStack.length && eventStack.peekLast().endTime <= event.start Time)
897 eventStack.pop();
898
899 var recordTypes = WebInspector.TimelineModel.RecordType;
900
901 if (this._currentScriptEvent && event.startTime > this._currentScriptEve nt.endTime)
902 this._currentScriptEvent = null;
903
904 var eventData = event.args["data"] || event.args["beginData"] || {};
905 if (eventData["stackTrace"])
906 event.stackTrace = eventData["stackTrace"];
907 if (event.stackTrace && event.name !== recordTypes.JSSample) {
908 // TraceEvents come with 1-based line & column numbers. The frontend code
909 // requires 0-based ones. Adjust the values.
910 for (var i = 0; i < event.stackTrace.length; ++i) {
911 --event.stackTrace[i].lineNumber;
912 --event.stackTrace[i].columnNumber;
913 }
914 }
915
916 if (eventStack.length && eventStack.peekLast().name === recordTypes.Even tDispatch)
917 eventStack.peekLast().hasChildren = true;
918 this._asyncEventTracker.processEvent(event);
919 if (event.initiator && event.initiator.url)
920 event.url = event.initiator.url;
921 switch (event.name) {
922 case recordTypes.ResourceSendRequest:
923 case recordTypes.WebSocketCreate:
924 event.url = eventData["url"];
925 event.initiator = eventStack.peekLast() || null;
926 break;
927
928 case recordTypes.ScheduleStyleRecalculation:
929 this._lastScheduleStyleRecalculation[eventData["frame"]] = event;
930 break;
931
932 case recordTypes.UpdateLayoutTree:
933 case recordTypes.RecalculateStyles:
934 this._invalidationTracker.didRecalcStyle(event);
935 if (event.args["beginData"])
936 event.initiator = this._lastScheduleStyleRecalculation[event.arg s["beginData"]["frame"]];
937 this._lastRecalculateStylesEvent = event;
938 if (this._currentScriptEvent)
939 event.warning = WebInspector.TimelineModel.WarningType.ForcedSty le;
940 break;
941
942 case recordTypes.ScheduleStyleInvalidationTracking:
943 case recordTypes.StyleRecalcInvalidationTracking:
944 case recordTypes.StyleInvalidatorInvalidationTracking:
945 case recordTypes.LayoutInvalidationTracking:
946 case recordTypes.LayerInvalidationTracking:
947 case recordTypes.PaintInvalidationTracking:
948 case recordTypes.ScrollInvalidationTracking:
949 this._invalidationTracker.addInvalidation(new WebInspector.Invalidat ionTrackingEvent(event));
950 break;
951
952 case recordTypes.InvalidateLayout:
953 // Consider style recalculation as a reason for layout invalidation,
954 // but only if we had no earlier layout invalidation records.
955 var layoutInitator = event;
956 var frameId = eventData["frame"];
957 if (!this._layoutInvalidate[frameId] && this._lastRecalculateStylesE vent && this._lastRecalculateStylesEvent.endTime > event.startTime)
958 layoutInitator = this._lastRecalculateStylesEvent.initiator;
959 this._layoutInvalidate[frameId] = layoutInitator;
960 break;
961
962 case recordTypes.Layout:
963 this._invalidationTracker.didLayout(event);
964 var frameId = event.args["beginData"]["frame"];
965 event.initiator = this._layoutInvalidate[frameId];
966 // In case we have no closing Layout event, endData is not available .
967 if (event.args["endData"]) {
968 event.backendNodeId = event.args["endData"]["rootNode"];
969 event.highlightQuad = event.args["endData"]["root"];
970 }
971 this._layoutInvalidate[frameId] = null;
972 if (this._currentScriptEvent)
973 event.warning = WebInspector.TimelineModel.WarningType.ForcedLay out;
974 break;
975
976 case recordTypes.FunctionCall:
977 // Compatibility with old format.
978 if (typeof eventData["scriptName"] === "string")
979 eventData["url"] = eventData["scriptName"];
980 if (typeof eventData["scriptLine"] === "number")
981 eventData["lineNumber"] = eventData["scriptLine"];
982 // Fallthrough.
983 case recordTypes.EvaluateScript:
984 case recordTypes.CompileScript:
985 if (typeof eventData["lineNumber"] === "number")
986 --eventData["lineNumber"];
987 if (typeof eventData["columnNumber"] === "number")
988 --eventData["columnNumber"];
989 // Fallthrough intended.
990 case recordTypes.RunMicrotasks:
991 // Microtasks technically are not necessarily scripts, but for purpo se of
992 // forced sync style recalc or layout detection they are.
993 if (!this._currentScriptEvent)
994 this._currentScriptEvent = event;
995 break;
996
997 case recordTypes.SetLayerTreeId:
998 this._inspectedTargetLayerTreeId = event.args["layerTreeId"] || even t.args["data"]["layerTreeId"];
999 break;
1000
1001 case recordTypes.Paint:
1002 this._invalidationTracker.didPaint(event);
1003 event.highlightQuad = eventData["clip"];
1004 event.backendNodeId = eventData["nodeId"];
1005 // Only keep layer paint events, skip paints for subframes that get painted to the same layer as parent.
1006 if (!eventData["layerId"])
1007 break;
1008 var layerId = eventData["layerId"];
1009 this._lastPaintForLayer[layerId] = event;
1010 break;
1011
1012 case recordTypes.DisplayItemListSnapshot:
1013 case recordTypes.PictureSnapshot:
1014 var layerUpdateEvent = this._findAncestorEvent(recordTypes.UpdateLay er);
1015 if (!layerUpdateEvent || layerUpdateEvent.args["layerTreeId"] !== th is._inspectedTargetLayerTreeId)
1016 break;
1017 var paintEvent = this._lastPaintForLayer[layerUpdateEvent.args["laye rId"]];
1018 if (paintEvent)
1019 paintEvent.picture = event;
1020 break;
1021
1022 case recordTypes.ScrollLayer:
1023 event.backendNodeId = eventData["nodeId"];
1024 break;
1025
1026 case recordTypes.PaintImage:
1027 event.backendNodeId = eventData["nodeId"];
1028 event.url = eventData["url"];
1029 break;
1030
1031 case recordTypes.DecodeImage:
1032 case recordTypes.ResizeImage:
1033 var paintImageEvent = this._findAncestorEvent(recordTypes.PaintImage );
1034 if (!paintImageEvent) {
1035 var decodeLazyPixelRefEvent = this._findAncestorEvent(recordType s.DecodeLazyPixelRef);
1036 paintImageEvent = decodeLazyPixelRefEvent && this._paintImageEve ntByPixelRefId[decodeLazyPixelRefEvent.args["LazyPixelRef"]];
1037 }
1038 if (!paintImageEvent)
1039 break;
1040 event.backendNodeId = paintImageEvent.backendNodeId;
1041 event.url = paintImageEvent.url;
1042 break;
1043
1044 case recordTypes.DrawLazyPixelRef:
1045 var paintImageEvent = this._findAncestorEvent(recordTypes.PaintImage );
1046 if (!paintImageEvent)
1047 break;
1048 this._paintImageEventByPixelRefId[event.args["LazyPixelRef"]] = pain tImageEvent;
1049 event.backendNodeId = paintImageEvent.backendNodeId;
1050 event.url = paintImageEvent.url;
1051 break;
1052
1053 case recordTypes.MarkDOMContent:
1054 case recordTypes.MarkLoad:
1055 var page = eventData["page"];
1056 if (page && page !== this._currentPage)
1057 return false;
1058 break;
1059
1060 case recordTypes.CommitLoad:
1061 var page = eventData["page"];
1062 if (page && page !== this._currentPage)
1063 return false;
1064 if (!eventData["isMainFrame"])
1065 break;
1066 this._hadCommitLoad = true;
1067 this._firstCompositeLayers = null;
1068 break;
1069
1070 case recordTypes.CompositeLayers:
1071 if (!this._firstCompositeLayers && this._hadCommitLoad)
1072 this._firstCompositeLayers = event;
1073 break;
1074
1075 case recordTypes.FireIdleCallback:
1076 if (event.duration > eventData["allottedMilliseconds"]) {
1077 event.warning = WebInspector.TimelineModel.WarningType.IdleDeadl ineExceeded;
1078 }
1079 break;
1080 }
1081 if (WebInspector.TracingModel.isAsyncPhase(event.phase))
1082 return true;
1083 var duration = event.duration;
1084 if (!duration)
1085 return true;
1086 if (eventStack.length) {
1087 var parent = eventStack.peekLast();
1088 parent.selfTime -= duration;
1089 if (parent.selfTime < 0) {
1090 var epsilon = 1e-3;
1091 if (parent.selfTime < -epsilon)
1092 console.error("Children are longer than parent at " + event. startTime + " (" + (event.startTime - this.minimumRecordTime()).toFixed(3) + ") by " + parent.selfTime.toFixed(3));
1093 parent.selfTime = 0;
1094 }
1095 }
1096 event.selfTime = duration;
1097 eventStack.push(event);
1098 return true;
1099 },
1100
1101 /**
1102 * @param {!WebInspector.TracingModel.Event} event
1103 */
1104 _processBrowserEvent: function(event)
1105 {
1106 if (event.name !== WebInspector.TimelineModel.RecordType.LatencyInfoFlow )
1107 return;
1108 var frameId = event.args["frameTreeNodeId"];
1109 if (typeof frameId === "number" && frameId === this._mainFrameNodeId)
1110 this._knownInputEvents.add(event.bind_id);
1111 },
1112
1113 /**
1114 * @param {!WebInspector.TracingModel.AsyncEvent} asyncEvent
1115 * @return {?WebInspector.TimelineModel.AsyncEventGroup}
1116 */
1117 _processAsyncEvent: function(asyncEvent)
1118 {
1119 var groups = WebInspector.TimelineModel.AsyncEventGroup;
1120 if (asyncEvent.hasCategory(WebInspector.TimelineModel.Category.Console))
1121 return groups.console;
1122 if (asyncEvent.hasCategory(WebInspector.TimelineModel.Category.UserTimin g))
1123 return groups.userTiming;
1124 if (asyncEvent.name === WebInspector.TimelineModel.RecordType.Animation)
1125 return groups.animation;
1126 if (asyncEvent.hasCategory(WebInspector.TimelineModel.Category.LatencyIn fo) || asyncEvent.name === WebInspector.TimelineModel.RecordType.ImplSideFling) {
1127 var lastStep = asyncEvent.steps.peekLast();
1128 // FIXME: fix event termination on the back-end instead.
1129 if (lastStep.phase !== WebInspector.TracingModel.Phase.AsyncEnd)
1130 return null;
1131 var data = lastStep.args["data"];
1132 asyncEvent.causedFrame = !!(data && data["INPUT_EVENT_LATENCY_RENDER ER_SWAP_COMPONENT"]);
1133 if (asyncEvent.hasCategory(WebInspector.TimelineModel.Category.Laten cyInfo)) {
1134 if (!this._knownInputEvents.has(lastStep.id))
1135 return null;
1136 if (asyncEvent.name === WebInspector.TimelineModel.RecordType.In putLatencyMouseMove && !asyncEvent.causedFrame)
1137 return null;
1138 var rendererMain = data["INPUT_EVENT_LATENCY_RENDERER_MAIN_COMPO NENT"];
1139 if (rendererMain) {
1140 var time = rendererMain["time"] / 1000;
1141 asyncEvent.steps[0].timeWaitingForMainThread = time - asyncE vent.steps[0].startTime;
1142 }
1143 }
1144 return groups.input;
1145 }
1146 return null;
1147 },
1148
1149 /**
1150 * @param {string} name
1151 * @return {?WebInspector.TracingModel.Event}
1152 */
1153 _findAncestorEvent: function(name)
1154 {
1155 for (var i = this._eventStack.length - 1; i >= 0; --i) {
1156 var event = this._eventStack[i];
1157 if (event.name === name)
1158 return event;
1159 }
1160 return null;
1161 },
1162
1163 /**
1164 * @param {!Map<!WebInspector.TimelineModel.AsyncEventGroup, !Array<!WebInsp ector.TracingModel.AsyncEvent>>} target
1165 * @param {!Map<!WebInspector.TimelineModel.AsyncEventGroup, !Array<!WebInsp ector.TracingModel.AsyncEvent>>} source
1166 */
1167 _mergeAsyncEvents: function(target, source)
1168 {
1169 for (var group of source.keys()) {
1170 var events = target.get(group) || [];
1171 events = events.mergeOrdered(source.get(group) || [], WebInspector.T racingModel.Event.compareStartAndEndTime);
1172 target.set(group, events);
1173 }
1174 },
1175
1176 reset: function()
1177 {
1178 this._virtualThreads = [];
1179 /** @type {!Array<!WebInspector.TracingModel.Event>} */
1180 this._mainThreadEvents = [];
1181 /** @type {!Map<!WebInspector.TimelineModel.AsyncEventGroup, !Array<!Web Inspector.TracingModel.AsyncEvent>>} */
1182 this._mainThreadAsyncEventsByGroup = new Map();
1183 /** @type {!Array<!WebInspector.TracingModel.Event>} */
1184 this._inspectedTargetEvents = [];
1185 /** @type {!Array<!WebInspector.TimelineModel.Record>} */
1186 this._records = [];
1187 /** @type {!Array<!WebInspector.TimelineModel.Record>} */
1188 this._mainThreadTasks = [];
1189 /** @type {!Array<!WebInspector.TracingModel.Event>} */
1190 this._gpuEvents = [];
1191 /** @type {!Array<!WebInspector.TimelineModel.Record>} */
1192 this._eventDividerRecords = [];
1193 /** @type {?string} */
1194 this._sessionId = null;
1195 /** @type {?number} */
1196 this._mainFrameNodeId = null;
1197 /** @type {!Array<!WebInspector.CPUProfileDataModel>} */
1198 this._cpuProfiles = [];
1199 /** @type {!WeakMap<!WebInspector.TracingModel.Thread, string>} */
1200 this._workerIdByThread = new WeakMap();
1201 this._minimumRecordTime = 0;
1202 this._maximumRecordTime = 0;
1203 },
1204
1205 /**
1206 * @return {number}
1207 */
1208 minimumRecordTime: function()
1209 {
1210 return this._minimumRecordTime;
1211 },
1212
1213 /**
1214 * @return {number}
1215 */
1216 maximumRecordTime: function()
1217 {
1218 return this._maximumRecordTime;
1219 },
1220
1221 /**
1222 * @return {!Array.<!WebInspector.TracingModel.Event>}
1223 */
1224 inspectedTargetEvents: function()
1225 {
1226 return this._inspectedTargetEvents;
1227 },
1228
1229 /**
1230 * @return {!Array.<!WebInspector.TracingModel.Event>}
1231 */
1232 mainThreadEvents: function()
1233 {
1234 return this._mainThreadEvents;
1235 },
1236
1237 /**
1238 * @param {!Array.<!WebInspector.TracingModel.Event>} events
1239 */
1240 _setMainThreadEvents: function(events)
1241 {
1242 this._mainThreadEvents = events;
1243 },
1244
1245 /**
1246 * @return {!Map<!WebInspector.TimelineModel.AsyncEventGroup, !Array.<!WebIn spector.TracingModel.AsyncEvent>>}
1247 */
1248 mainThreadAsyncEvents: function()
1249 {
1250 return this._mainThreadAsyncEventsByGroup;
1251 },
1252
1253 /**
1254 * @return {!Array.<!WebInspector.TimelineModel.VirtualThread>}
1255 */
1256 virtualThreads: function()
1257 {
1258 return this._virtualThreads;
1259 },
1260
1261 /**
1262 * @return {boolean}
1263 */
1264 isEmpty: function()
1265 {
1266 return this.minimumRecordTime() === 0 && this.maximumRecordTime() === 0;
1267 },
1268
1269 /**
1270 * @return {!Array.<!WebInspector.TimelineModel.Record>}
1271 */
1272 mainThreadTasks: function()
1273 {
1274 return this._mainThreadTasks;
1275 },
1276
1277 /**
1278 * @return {!Array<!WebInspector.TracingModel.Event>}
1279 */
1280 gpuEvents: function()
1281 {
1282 return this._gpuEvents;
1283 },
1284
1285 /**
1286 * @return {!Array.<!WebInspector.TimelineModel.Record>}
1287 */
1288 eventDividerRecords: function()
1289 {
1290 return this._eventDividerRecords;
1291 },
1292
1293 /**
1294 * @return {!Array<!WebInspector.TimelineModel.NetworkRequest>}
1295 */
1296 networkRequests: function()
1297 {
1298 /** @type {!Map<string,!WebInspector.TimelineModel.NetworkRequest>} */
1299 var requests = new Map();
1300 /** @type {!Array<!WebInspector.TimelineModel.NetworkRequest>} */
1301 var requestsList = [];
1302 /** @type {!Array<!WebInspector.TimelineModel.NetworkRequest>} */
1303 var zeroStartRequestsList = [];
1304 var types = WebInspector.TimelineModel.RecordType;
1305 var resourceTypes = new Set([
1306 types.ResourceSendRequest,
1307 types.ResourceReceiveResponse,
1308 types.ResourceReceivedData,
1309 types.ResourceFinish
1310 ]);
1311 var events = this.mainThreadEvents();
1312 for (var i = 0; i < events.length; ++i) {
1313 var e = events[i];
1314 if (!resourceTypes.has(e.name))
1315 continue;
1316 var id = e.args["data"]["requestId"];
1317 var request = requests.get(id);
1318 if (request) {
1319 request.addEvent(e);
1320 } else {
1321 request = new WebInspector.TimelineModel.NetworkRequest(e);
1322 requests.set(id, request);
1323 if (request.startTime)
1324 requestsList.push(request);
1325 else
1326 zeroStartRequestsList.push(request);
1327 }
1328 }
1329 return zeroStartRequestsList.concat(requestsList);
1330 },
1331 };
1332
1333 /**
1334 * @param {!Array<!WebInspector.TimelineModel.Filter>} filters
1335 * @param {!WebInspector.TracingModel.Event} event
1336 * @return {boolean}
1337 */
1338 WebInspector.TimelineModel.isVisible = function(filters, event)
1339 {
1340 for (var i = 0; i < filters.length; ++i) {
1341 if (!filters[i].accept(event))
1342 return false;
1343 }
1344 return true;
1345 };
1346
1347 /**
1348 * @param {!WebInspector.TracingModel.Event} event
1349 * @return {boolean}
1350 */
1351 WebInspector.TimelineModel.isMarkerEvent = function(event)
1352 {
1353 var recordTypes = WebInspector.TimelineModel.RecordType;
1354 switch (event.name) {
1355 case recordTypes.TimeStamp:
1356 case recordTypes.MarkFirstPaint:
1357 return true;
1358 case recordTypes.MarkDOMContent:
1359 case recordTypes.MarkLoad:
1360 return event.args["data"]["isMainFrame"];
1361 default:
1362 return false;
1363 }
1364 };
1365
1366 /**
1367 * @constructor
1368 * @param {!WebInspector.TracingModel.Event} event
1369 */
1370 WebInspector.TimelineModel.NetworkRequest = function(event)
1371 {
1372 this.startTime = event.name === WebInspector.TimelineModel.RecordType.Resour ceSendRequest ? event.startTime : 0; 1341 this.startTime = event.name === WebInspector.TimelineModel.RecordType.Resour ceSendRequest ? event.startTime : 0;
1373 this.endTime = Infinity; 1342 this.endTime = Infinity;
1374 /** @type {!Array<!WebInspector.TracingModel.Event>} */ 1343 /** @type {!Array<!WebInspector.TracingModel.Event>} */
1375 this.children = []; 1344 this.children = [];
1376 this.addEvent(event); 1345 this.addEvent(event);
1346 }
1347
1348 /**
1349 * @param {!WebInspector.TracingModel.Event} event
1350 */
1351 addEvent(event) {
1352 this.children.push(event);
1353 var recordType = WebInspector.TimelineModel.RecordType;
1354 this.startTime = Math.min(this.startTime, event.startTime);
1355 var eventData = event.args['data'];
1356 if (eventData['mimeType'])
1357 this.mimeType = eventData['mimeType'];
1358 if ('priority' in eventData)
1359 this.priority = eventData['priority'];
1360 if (event.name === recordType.ResourceFinish)
1361 this.endTime = event.startTime;
1362 if (!this.responseTime &&
1363 (event.name === recordType.ResourceReceiveResponse || event.name === rec ordType.ResourceReceivedData))
1364 this.responseTime = event.startTime;
1365 if (!this.url)
1366 this.url = eventData['url'];
1367 if (!this.requestMethod)
1368 this.requestMethod = eventData['requestMethod'];
1369 }
1377 }; 1370 };
1378 1371
1379 WebInspector.TimelineModel.NetworkRequest.prototype = { 1372 /**
1380 /** 1373 * @unrestricted
1381 * @param {!WebInspector.TracingModel.Event} event 1374 */
1382 */ 1375 WebInspector.TimelineModel.Filter = class {
1383 addEvent: function(event) 1376 /**
1384 { 1377 * @param {!WebInspector.TracingModel.Event} event
1385 this.children.push(event); 1378 * @return {boolean}
1386 var recordType = WebInspector.TimelineModel.RecordType; 1379 */
1387 this.startTime = Math.min(this.startTime, event.startTime); 1380 accept(event) {
1388 var eventData = event.args["data"]; 1381 return true;
1389 if (eventData["mimeType"]) 1382 }
1390 this.mimeType = eventData["mimeType"];
1391 if ("priority" in eventData)
1392 this.priority = eventData["priority"];
1393 if (event.name === recordType.ResourceFinish)
1394 this.endTime = event.startTime;
1395 if (!this.responseTime && (event.name === recordType.ResourceReceiveResp onse || event.name === recordType.ResourceReceivedData))
1396 this.responseTime = event.startTime;
1397 if (!this.url)
1398 this.url = eventData["url"];
1399 if (!this.requestMethod)
1400 this.requestMethod = eventData["requestMethod"];
1401 }
1402 }; 1383 };
1403 1384
1404 /** 1385 /**
1405 * @constructor 1386 * @unrestricted
1406 */ 1387 */
1407 WebInspector.TimelineModel.Filter = function() 1388 WebInspector.TimelineVisibleEventsFilter = class extends WebInspector.TimelineMo del.Filter {
1408 { 1389 /**
1390 * @param {!Array.<string>} visibleTypes
1391 */
1392 constructor(visibleTypes) {
1393 super();
1394 this._visibleTypes = new Set(visibleTypes);
1395 }
1396
1397 /**
1398 * @override
1399 * @param {!WebInspector.TracingModel.Event} event
1400 * @return {boolean}
1401 */
1402 accept(event) {
1403 return this._visibleTypes.has(WebInspector.TimelineModel._eventType(event));
1404 }
1409 }; 1405 };
1410 1406
1411 WebInspector.TimelineModel.Filter.prototype = { 1407 /**
1412 /** 1408 * @unrestricted
1413 * @param {!WebInspector.TracingModel.Event} event 1409 */
1414 * @return {boolean} 1410 WebInspector.ExclusiveNameFilter = class extends WebInspector.TimelineModel.Filt er {
1415 */ 1411 /**
1416 accept: function(event) 1412 * @param {!Array<string>} excludeNames
1417 { 1413 */
1418 return true; 1414 constructor(excludeNames) {
1419 } 1415 super();
1416 this._excludeNames = new Set(excludeNames);
1417 }
1418
1419 /**
1420 * @override
1421 * @param {!WebInspector.TracingModel.Event} event
1422 * @return {boolean}
1423 */
1424 accept(event) {
1425 return !this._excludeNames.has(event.name);
1426 }
1420 }; 1427 };
1421 1428
1422 /** 1429 /**
1423 * @constructor 1430 * @unrestricted
1424 * @extends {WebInspector.TimelineModel.Filter}
1425 * @param {!Array.<string>} visibleTypes
1426 */ 1431 */
1427 WebInspector.TimelineVisibleEventsFilter = function(visibleTypes) 1432 WebInspector.ExcludeTopLevelFilter = class extends WebInspector.TimelineModel.Fi lter {
1428 { 1433 constructor() {
1429 WebInspector.TimelineModel.Filter.call(this); 1434 super();
1430 this._visibleTypes = new Set(visibleTypes); 1435 }
1436
1437 /**
1438 * @override
1439 * @param {!WebInspector.TracingModel.Event} event
1440 * @return {boolean}
1441 */
1442 accept(event) {
1443 return !WebInspector.TracingModel.isTopLevelEvent(event);
1444 }
1431 }; 1445 };
1432 1446
1433 WebInspector.TimelineVisibleEventsFilter.prototype = {
1434 /**
1435 * @override
1436 * @param {!WebInspector.TracingModel.Event} event
1437 * @return {boolean}
1438 */
1439 accept: function(event)
1440 {
1441 return this._visibleTypes.has(WebInspector.TimelineModel._eventType(even t));
1442 },
1443
1444 __proto__: WebInspector.TimelineModel.Filter.prototype
1445 };
1446
1447 /** 1447 /**
1448 * @constructor 1448 * @unrestricted
1449 * @extends {WebInspector.TimelineModel.Filter}
1450 * @param {!Array<string>} excludeNames
1451 */ 1449 */
1452 WebInspector.ExclusiveNameFilter = function(excludeNames) 1450 WebInspector.InvalidationTrackingEvent = class {
1453 { 1451 /**
1454 WebInspector.TimelineModel.Filter.call(this); 1452 * @param {!WebInspector.TracingModel.Event} event
1455 this._excludeNames = new Set(excludeNames); 1453 */
1456 }; 1454 constructor(event) {
1457
1458 WebInspector.ExclusiveNameFilter.prototype = {
1459 /**
1460 * @override
1461 * @param {!WebInspector.TracingModel.Event} event
1462 * @return {boolean}
1463 */
1464 accept: function(event)
1465 {
1466 return !this._excludeNames.has(event.name);
1467 },
1468
1469 __proto__: WebInspector.TimelineModel.Filter.prototype
1470 };
1471
1472 /**
1473 * @constructor
1474 * @extends {WebInspector.TimelineModel.Filter}
1475 */
1476 WebInspector.ExcludeTopLevelFilter = function()
1477 {
1478 WebInspector.TimelineModel.Filter.call(this);
1479 };
1480
1481 WebInspector.ExcludeTopLevelFilter.prototype = {
1482 /**
1483 * @override
1484 * @param {!WebInspector.TracingModel.Event} event
1485 * @return {boolean}
1486 */
1487 accept: function(event)
1488 {
1489 return !WebInspector.TracingModel.isTopLevelEvent(event);
1490 },
1491
1492 __proto__: WebInspector.TimelineModel.Filter.prototype
1493 };
1494
1495 /**
1496 * @constructor
1497 * @param {!WebInspector.TracingModel.Event} event
1498 */
1499 WebInspector.InvalidationTrackingEvent = function(event)
1500 {
1501 /** @type {string} */ 1455 /** @type {string} */
1502 this.type = event.name; 1456 this.type = event.name;
1503 /** @type {number} */ 1457 /** @type {number} */
1504 this.startTime = event.startTime; 1458 this.startTime = event.startTime;
1505 /** @type {!WebInspector.TracingModel.Event} */ 1459 /** @type {!WebInspector.TracingModel.Event} */
1506 this._tracingEvent = event; 1460 this._tracingEvent = event;
1507 1461
1508 var eventData = event.args["data"]; 1462 var eventData = event.args['data'];
1509 1463
1510 /** @type {number} */ 1464 /** @type {number} */
1511 this.frame = eventData["frame"]; 1465 this.frame = eventData['frame'];
1512 /** @type {?number} */ 1466 /** @type {?number} */
1513 this.nodeId = eventData["nodeId"]; 1467 this.nodeId = eventData['nodeId'];
1514 /** @type {?string} */ 1468 /** @type {?string} */
1515 this.nodeName = eventData["nodeName"]; 1469 this.nodeName = eventData['nodeName'];
1516 /** @type {?number} */ 1470 /** @type {?number} */
1517 this.paintId = eventData["paintId"]; 1471 this.paintId = eventData['paintId'];
1518 /** @type {?number} */ 1472 /** @type {?number} */
1519 this.invalidationSet = eventData["invalidationSet"]; 1473 this.invalidationSet = eventData['invalidationSet'];
1520 /** @type {?string} */ 1474 /** @type {?string} */
1521 this.invalidatedSelectorId = eventData["invalidatedSelectorId"]; 1475 this.invalidatedSelectorId = eventData['invalidatedSelectorId'];
1522 /** @type {?string} */ 1476 /** @type {?string} */
1523 this.changedId = eventData["changedId"]; 1477 this.changedId = eventData['changedId'];
1524 /** @type {?string} */ 1478 /** @type {?string} */
1525 this.changedClass = eventData["changedClass"]; 1479 this.changedClass = eventData['changedClass'];
1526 /** @type {?string} */ 1480 /** @type {?string} */
1527 this.changedAttribute = eventData["changedAttribute"]; 1481 this.changedAttribute = eventData['changedAttribute'];
1528 /** @type {?string} */ 1482 /** @type {?string} */
1529 this.changedPseudo = eventData["changedPseudo"]; 1483 this.changedPseudo = eventData['changedPseudo'];
1530 /** @type {?string} */ 1484 /** @type {?string} */
1531 this.selectorPart = eventData["selectorPart"]; 1485 this.selectorPart = eventData['selectorPart'];
1532 /** @type {?string} */ 1486 /** @type {?string} */
1533 this.extraData = eventData["extraData"]; 1487 this.extraData = eventData['extraData'];
1534 /** @type {?Array.<!Object.<string, number>>} */ 1488 /** @type {?Array.<!Object.<string, number>>} */
1535 this.invalidationList = eventData["invalidationList"]; 1489 this.invalidationList = eventData['invalidationList'];
1536 /** @type {!WebInspector.InvalidationCause} */ 1490 /** @type {!WebInspector.InvalidationCause} */
1537 this.cause = {reason: eventData["reason"], stackTrace: eventData["stackTrace "]}; 1491 this.cause = {reason: eventData['reason'], stackTrace: eventData['stackTrace ']};
1538 1492
1539 // FIXME: Move this to TimelineUIUtils.js. 1493 // FIXME: Move this to TimelineUIUtils.js.
1540 if (!this.cause.reason && this.cause.stackTrace && this.type === WebInspecto r.TimelineModel.RecordType.LayoutInvalidationTracking) 1494 if (!this.cause.reason && this.cause.stackTrace &&
1541 this.cause.reason = "Layout forced"; 1495 this.type === WebInspector.TimelineModel.RecordType.LayoutInvalidationTr acking)
1496 this.cause.reason = 'Layout forced';
1497 }
1542 }; 1498 };
1543 1499
1544 /** @typedef {{reason: string, stackTrace: ?Array<!RuntimeAgent.CallFrame>}} */ 1500 /** @typedef {{reason: string, stackTrace: ?Array<!RuntimeAgent.CallFrame>}} */
1545 WebInspector.InvalidationCause; 1501 WebInspector.InvalidationCause;
1546 1502
1547 /** 1503 /**
1548 * @constructor 1504 * @unrestricted
1549 */ 1505 */
1550 WebInspector.InvalidationTracker = function() 1506 WebInspector.InvalidationTracker = class {
1551 { 1507 constructor() {
1552 this._initializePerFrameState(); 1508 this._initializePerFrameState();
1509 }
1510
1511 /**
1512 * @param {!WebInspector.InvalidationTrackingEvent} invalidation
1513 */
1514 addInvalidation(invalidation) {
1515 this._startNewFrameIfNeeded();
1516
1517 if (!invalidation.nodeId && !invalidation.paintId) {
1518 console.error('Invalidation lacks node information.');
1519 console.error(invalidation);
1520 return;
1521 }
1522
1523 // PaintInvalidationTracking events provide a paintId and a nodeId which
1524 // we can use to update the paintId for all other invalidation tracking
1525 // events.
1526 var recordTypes = WebInspector.TimelineModel.RecordType;
1527 if (invalidation.type === recordTypes.PaintInvalidationTracking && invalidat ion.nodeId) {
1528 var invalidations = this._invalidationsByNodeId[invalidation.nodeId] || [] ;
1529 for (var i = 0; i < invalidations.length; ++i)
1530 invalidations[i].paintId = invalidation.paintId;
1531
1532 // PaintInvalidationTracking is only used for updating paintIds.
1533 return;
1534 }
1535
1536 // Suppress StyleInvalidator StyleRecalcInvalidationTracking invalidations b ecause they
1537 // will be handled by StyleInvalidatorInvalidationTracking.
1538 // FIXME: Investigate if we can remove StyleInvalidator invalidations entire ly.
1539 if (invalidation.type === recordTypes.StyleRecalcInvalidationTracking &&
1540 invalidation.cause.reason === 'StyleInvalidator')
1541 return;
1542
1543 // Style invalidation events can occur before and during recalc style. didRe calcStyle
1544 // handles style invalidations that occur before the recalc style event but we need to
1545 // handle style recalc invalidations during recalc style here.
1546 var styleRecalcInvalidation =
1547 (invalidation.type === recordTypes.ScheduleStyleInvalidationTracking ||
1548 invalidation.type === recordTypes.StyleInvalidatorInvalidationTracking ||
1549 invalidation.type === recordTypes.StyleRecalcInvalidationTracking);
1550 if (styleRecalcInvalidation) {
1551 var duringRecalcStyle = invalidation.startTime && this._lastRecalcStyle &&
1552 invalidation.startTime >= this._lastRecalcStyle.startTime &&
1553 invalidation.startTime <= this._lastRecalcStyle.endTime;
1554 if (duringRecalcStyle)
1555 this._associateWithLastRecalcStyleEvent(invalidation);
1556 }
1557
1558 // Record the invalidation so later events can look it up.
1559 if (this._invalidations[invalidation.type])
1560 this._invalidations[invalidation.type].push(invalidation);
1561 else
1562 this._invalidations[invalidation.type] = [invalidation];
1563 if (invalidation.nodeId) {
1564 if (this._invalidationsByNodeId[invalidation.nodeId])
1565 this._invalidationsByNodeId[invalidation.nodeId].push(invalidation);
1566 else
1567 this._invalidationsByNodeId[invalidation.nodeId] = [invalidation];
1568 }
1569 }
1570
1571 /**
1572 * @param {!WebInspector.TracingModel.Event} recalcStyleEvent
1573 */
1574 didRecalcStyle(recalcStyleEvent) {
1575 this._lastRecalcStyle = recalcStyleEvent;
1576 var types = [
1577 WebInspector.TimelineModel.RecordType.ScheduleStyleInvalidationTracking,
1578 WebInspector.TimelineModel.RecordType.StyleInvalidatorInvalidationTracking ,
1579 WebInspector.TimelineModel.RecordType.StyleRecalcInvalidationTracking
1580 ];
1581 for (var invalidation of this._invalidationsOfTypes(types))
1582 this._associateWithLastRecalcStyleEvent(invalidation);
1583 }
1584
1585 /**
1586 * @param {!WebInspector.InvalidationTrackingEvent} invalidation
1587 */
1588 _associateWithLastRecalcStyleEvent(invalidation) {
1589 if (invalidation.linkedRecalcStyleEvent)
1590 return;
1591
1592 var recordTypes = WebInspector.TimelineModel.RecordType;
1593 var recalcStyleFrameId = this._lastRecalcStyle.args['beginData']['frame'];
1594 if (invalidation.type === recordTypes.StyleInvalidatorInvalidationTracking) {
1595 // Instead of calling _addInvalidationToEvent directly, we create syntheti c
1596 // StyleRecalcInvalidationTracking events which will be added in _addInval idationToEvent.
1597 this._addSyntheticStyleRecalcInvalidations(this._lastRecalcStyle, recalcSt yleFrameId, invalidation);
1598 } else if (invalidation.type === recordTypes.ScheduleStyleInvalidationTracki ng) {
1599 // ScheduleStyleInvalidationTracking events are only used for adding infor mation to
1600 // StyleInvalidatorInvalidationTracking events. See: _addSyntheticStyleRec alcInvalidations.
1601 } else {
1602 this._addInvalidationToEvent(this._lastRecalcStyle, recalcStyleFrameId, in validation);
1603 }
1604
1605 invalidation.linkedRecalcStyleEvent = true;
1606 }
1607
1608 /**
1609 * @param {!WebInspector.TracingModel.Event} event
1610 * @param {number} frameId
1611 * @param {!WebInspector.InvalidationTrackingEvent} styleInvalidatorInvalidati on
1612 */
1613 _addSyntheticStyleRecalcInvalidations(event, frameId, styleInvalidatorInvalida tion) {
1614 if (!styleInvalidatorInvalidation.invalidationList) {
1615 this._addSyntheticStyleRecalcInvalidation(
1616 styleInvalidatorInvalidation._tracingEvent, styleInvalidatorInvalidati on);
1617 return;
1618 }
1619 if (!styleInvalidatorInvalidation.nodeId) {
1620 console.error('Invalidation lacks node information.');
1621 console.error(invalidation);
1622 return;
1623 }
1624 for (var i = 0; i < styleInvalidatorInvalidation.invalidationList.length; i+ +) {
1625 var setId = styleInvalidatorInvalidation.invalidationList[i]['id'];
1626 var lastScheduleStyleRecalculation;
1627 var nodeInvalidations = this._invalidationsByNodeId[styleInvalidatorInvali dation.nodeId] || [];
1628 for (var j = 0; j < nodeInvalidations.length; j++) {
1629 var invalidation = nodeInvalidations[j];
1630 if (invalidation.frame !== frameId || invalidation.invalidationSet !== s etId ||
1631 invalidation.type !== WebInspector.TimelineModel.RecordType.Schedule StyleInvalidationTracking)
1632 continue;
1633 lastScheduleStyleRecalculation = invalidation;
1634 }
1635 if (!lastScheduleStyleRecalculation) {
1636 console.error('Failed to lookup the event that scheduled a style invalid ator invalidation.');
1637 continue;
1638 }
1639 this._addSyntheticStyleRecalcInvalidation(
1640 lastScheduleStyleRecalculation._tracingEvent, styleInvalidatorInvalida tion);
1641 }
1642 }
1643
1644 /**
1645 * @param {!WebInspector.TracingModel.Event} baseEvent
1646 * @param {!WebInspector.InvalidationTrackingEvent} styleInvalidatorInvalidati on
1647 */
1648 _addSyntheticStyleRecalcInvalidation(baseEvent, styleInvalidatorInvalidation) {
1649 var invalidation = new WebInspector.InvalidationTrackingEvent(baseEvent);
1650 invalidation.type = WebInspector.TimelineModel.RecordType.StyleRecalcInvalid ationTracking;
1651 invalidation.synthetic = true;
1652 if (styleInvalidatorInvalidation.cause.reason)
1653 invalidation.cause.reason = styleInvalidatorInvalidation.cause.reason;
1654 if (styleInvalidatorInvalidation.selectorPart)
1655 invalidation.selectorPart = styleInvalidatorInvalidation.selectorPart;
1656
1657 this.addInvalidation(invalidation);
1658 if (!invalidation.linkedRecalcStyleEvent)
1659 this._associateWithLastRecalcStyleEvent(invalidation);
1660 }
1661
1662 /**
1663 * @param {!WebInspector.TracingModel.Event} layoutEvent
1664 */
1665 didLayout(layoutEvent) {
1666 var layoutFrameId = layoutEvent.args['beginData']['frame'];
1667 for (var invalidation of this._invalidationsOfTypes(
1668 [WebInspector.TimelineModel.RecordType.LayoutInvalidationTracking]) ) {
1669 if (invalidation.linkedLayoutEvent)
1670 continue;
1671 this._addInvalidationToEvent(layoutEvent, layoutFrameId, invalidation);
1672 invalidation.linkedLayoutEvent = true;
1673 }
1674 }
1675
1676 /**
1677 * @param {!WebInspector.TracingModel.Event} paintEvent
1678 */
1679 didPaint(paintEvent) {
1680 this._didPaint = true;
1681
1682 // If a paint doesn't have a corresponding graphics layer id, it paints
1683 // into its parent so add an effectivePaintId to these events.
1684 var layerId = paintEvent.args['data']['layerId'];
1685 if (layerId)
1686 this._lastPaintWithLayer = paintEvent;
1687 // Quietly discard top-level paints without layerId, as these are likely
1688 // to come from overlay.
1689 if (!this._lastPaintWithLayer)
1690 return;
1691
1692 var effectivePaintId = this._lastPaintWithLayer.args['data']['nodeId'];
1693 var paintFrameId = paintEvent.args['data']['frame'];
1694 var types = [
1695 WebInspector.TimelineModel.RecordType.StyleRecalcInvalidationTracking,
1696 WebInspector.TimelineModel.RecordType.LayoutInvalidationTracking,
1697 WebInspector.TimelineModel.RecordType.PaintInvalidationTracking,
1698 WebInspector.TimelineModel.RecordType.ScrollInvalidationTracking
1699 ];
1700 for (var invalidation of this._invalidationsOfTypes(types)) {
1701 if (invalidation.paintId === effectivePaintId)
1702 this._addInvalidationToEvent(paintEvent, paintFrameId, invalidation);
1703 }
1704 }
1705
1706 /**
1707 * @param {!WebInspector.TracingModel.Event} event
1708 * @param {number} eventFrameId
1709 * @param {!WebInspector.InvalidationTrackingEvent} invalidation
1710 */
1711 _addInvalidationToEvent(event, eventFrameId, invalidation) {
1712 if (eventFrameId !== invalidation.frame)
1713 return;
1714 if (!event.invalidationTrackingEvents)
1715 event.invalidationTrackingEvents = [invalidation];
1716 else
1717 event.invalidationTrackingEvents.push(invalidation);
1718 }
1719
1720 /**
1721 * @param {!Array.<string>=} types
1722 * @return {!Iterator.<!WebInspector.InvalidationTrackingEvent>}
1723 */
1724 _invalidationsOfTypes(types) {
1725 var invalidations = this._invalidations;
1726 if (!types)
1727 types = Object.keys(invalidations);
1728 function* generator() {
1729 for (var i = 0; i < types.length; ++i) {
1730 var invalidationList = invalidations[types[i]] || [];
1731 for (var j = 0; j < invalidationList.length; ++j)
1732 yield invalidationList[j];
1733 }
1734 }
1735 return generator();
1736 }
1737
1738 _startNewFrameIfNeeded() {
1739 if (!this._didPaint)
1740 return;
1741
1742 this._initializePerFrameState();
1743 }
1744
1745 _initializePerFrameState() {
1746 /** @type {!Object.<string, !Array.<!WebInspector.InvalidationTrackingEvent> >} */
1747 this._invalidations = {};
1748 /** @type {!Object.<number, !Array.<!WebInspector.InvalidationTrackingEvent> >} */
1749 this._invalidationsByNodeId = {};
1750
1751 this._lastRecalcStyle = undefined;
1752 this._lastPaintWithLayer = undefined;
1753 this._didPaint = false;
1754 }
1553 }; 1755 };
1554 1756
1555 WebInspector.InvalidationTracker.prototype = {
1556 /**
1557 * @param {!WebInspector.InvalidationTrackingEvent} invalidation
1558 */
1559 addInvalidation: function(invalidation)
1560 {
1561 this._startNewFrameIfNeeded();
1562
1563 if (!invalidation.nodeId && !invalidation.paintId) {
1564 console.error("Invalidation lacks node information.");
1565 console.error(invalidation);
1566 return;
1567 }
1568
1569 // PaintInvalidationTracking events provide a paintId and a nodeId which
1570 // we can use to update the paintId for all other invalidation tracking
1571 // events.
1572 var recordTypes = WebInspector.TimelineModel.RecordType;
1573 if (invalidation.type === recordTypes.PaintInvalidationTracking && inval idation.nodeId) {
1574 var invalidations = this._invalidationsByNodeId[invalidation.nodeId] || [];
1575 for (var i = 0; i < invalidations.length; ++i)
1576 invalidations[i].paintId = invalidation.paintId;
1577
1578 // PaintInvalidationTracking is only used for updating paintIds.
1579 return;
1580 }
1581
1582 // Suppress StyleInvalidator StyleRecalcInvalidationTracking invalidatio ns because they
1583 // will be handled by StyleInvalidatorInvalidationTracking.
1584 // FIXME: Investigate if we can remove StyleInvalidator invalidations en tirely.
1585 if (invalidation.type === recordTypes.StyleRecalcInvalidationTracking && invalidation.cause.reason === "StyleInvalidator")
1586 return;
1587
1588 // Style invalidation events can occur before and during recalc style. d idRecalcStyle
1589 // handles style invalidations that occur before the recalc style event but we need to
1590 // handle style recalc invalidations during recalc style here.
1591 var styleRecalcInvalidation = (invalidation.type === recordTypes.Schedul eStyleInvalidationTracking
1592 || invalidation.type === recordTypes.StyleInvalidatorInvalidationTra cking
1593 || invalidation.type === recordTypes.StyleRecalcInvalidationTracking );
1594 if (styleRecalcInvalidation) {
1595 var duringRecalcStyle = invalidation.startTime && this._lastRecalcSt yle
1596 && invalidation.startTime >= this._lastRecalcStyle.startTime
1597 && invalidation.startTime <= this._lastRecalcStyle.endTime;
1598 if (duringRecalcStyle)
1599 this._associateWithLastRecalcStyleEvent(invalidation);
1600 }
1601
1602 // Record the invalidation so later events can look it up.
1603 if (this._invalidations[invalidation.type])
1604 this._invalidations[invalidation.type].push(invalidation);
1605 else
1606 this._invalidations[invalidation.type] = [ invalidation ];
1607 if (invalidation.nodeId) {
1608 if (this._invalidationsByNodeId[invalidation.nodeId])
1609 this._invalidationsByNodeId[invalidation.nodeId].push(invalidati on);
1610 else
1611 this._invalidationsByNodeId[invalidation.nodeId] = [ invalidatio n ];
1612 }
1613 },
1614
1615 /**
1616 * @param {!WebInspector.TracingModel.Event} recalcStyleEvent
1617 */
1618 didRecalcStyle: function(recalcStyleEvent)
1619 {
1620 this._lastRecalcStyle = recalcStyleEvent;
1621 var types = [WebInspector.TimelineModel.RecordType.ScheduleStyleInvalida tionTracking,
1622 WebInspector.TimelineModel.RecordType.StyleInvalidatorInvalidati onTracking,
1623 WebInspector.TimelineModel.RecordType.StyleRecalcInvalidationTra cking];
1624 for (var invalidation of this._invalidationsOfTypes(types))
1625 this._associateWithLastRecalcStyleEvent(invalidation);
1626 },
1627
1628 /**
1629 * @param {!WebInspector.InvalidationTrackingEvent} invalidation
1630 */
1631 _associateWithLastRecalcStyleEvent: function(invalidation)
1632 {
1633 if (invalidation.linkedRecalcStyleEvent)
1634 return;
1635
1636 var recordTypes = WebInspector.TimelineModel.RecordType;
1637 var recalcStyleFrameId = this._lastRecalcStyle.args["beginData"]["frame" ];
1638 if (invalidation.type === recordTypes.StyleInvalidatorInvalidationTracki ng) {
1639 // Instead of calling _addInvalidationToEvent directly, we create sy nthetic
1640 // StyleRecalcInvalidationTracking events which will be added in _ad dInvalidationToEvent.
1641 this._addSyntheticStyleRecalcInvalidations(this._lastRecalcStyle, re calcStyleFrameId, invalidation);
1642 } else if (invalidation.type === recordTypes.ScheduleStyleInvalidationTr acking) {
1643 // ScheduleStyleInvalidationTracking events are only used for adding information to
1644 // StyleInvalidatorInvalidationTracking events. See: _addSyntheticSt yleRecalcInvalidations.
1645 } else {
1646 this._addInvalidationToEvent(this._lastRecalcStyle, recalcStyleFrame Id, invalidation);
1647 }
1648
1649 invalidation.linkedRecalcStyleEvent = true;
1650 },
1651
1652 /**
1653 * @param {!WebInspector.TracingModel.Event} event
1654 * @param {number} frameId
1655 * @param {!WebInspector.InvalidationTrackingEvent} styleInvalidatorInvalida tion
1656 */
1657 _addSyntheticStyleRecalcInvalidations: function(event, frameId, styleInvalid atorInvalidation)
1658 {
1659 if (!styleInvalidatorInvalidation.invalidationList) {
1660 this._addSyntheticStyleRecalcInvalidation(styleInvalidatorInvalidati on._tracingEvent, styleInvalidatorInvalidation);
1661 return;
1662 }
1663 if (!styleInvalidatorInvalidation.nodeId) {
1664 console.error("Invalidation lacks node information.");
1665 console.error(invalidation);
1666 return;
1667 }
1668 for (var i = 0; i < styleInvalidatorInvalidation.invalidationList.length ; i++) {
1669 var setId = styleInvalidatorInvalidation.invalidationList[i]["id"];
1670 var lastScheduleStyleRecalculation;
1671 var nodeInvalidations = this._invalidationsByNodeId[styleInvalidator Invalidation.nodeId] || [];
1672 for (var j = 0; j < nodeInvalidations.length; j++) {
1673 var invalidation = nodeInvalidations[j];
1674 if (invalidation.frame !== frameId || invalidation.invalidationS et !== setId || invalidation.type !== WebInspector.TimelineModel.RecordType.Sche duleStyleInvalidationTracking)
1675 continue;
1676 lastScheduleStyleRecalculation = invalidation;
1677 }
1678 if (!lastScheduleStyleRecalculation) {
1679 console.error("Failed to lookup the event that scheduled a style invalidator invalidation.");
1680 continue;
1681 }
1682 this._addSyntheticStyleRecalcInvalidation(lastScheduleStyleRecalcula tion._tracingEvent, styleInvalidatorInvalidation);
1683 }
1684 },
1685
1686 /**
1687 * @param {!WebInspector.TracingModel.Event} baseEvent
1688 * @param {!WebInspector.InvalidationTrackingEvent} styleInvalidatorInvalida tion
1689 */
1690 _addSyntheticStyleRecalcInvalidation: function(baseEvent, styleInvalidatorIn validation)
1691 {
1692 var invalidation = new WebInspector.InvalidationTrackingEvent(baseEvent) ;
1693 invalidation.type = WebInspector.TimelineModel.RecordType.StyleRecalcInv alidationTracking;
1694 invalidation.synthetic = true;
1695 if (styleInvalidatorInvalidation.cause.reason)
1696 invalidation.cause.reason = styleInvalidatorInvalidation.cause.reaso n;
1697 if (styleInvalidatorInvalidation.selectorPart)
1698 invalidation.selectorPart = styleInvalidatorInvalidation.selectorPar t;
1699
1700 this.addInvalidation(invalidation);
1701 if (!invalidation.linkedRecalcStyleEvent)
1702 this._associateWithLastRecalcStyleEvent(invalidation);
1703 },
1704
1705 /**
1706 * @param {!WebInspector.TracingModel.Event} layoutEvent
1707 */
1708 didLayout: function(layoutEvent)
1709 {
1710 var layoutFrameId = layoutEvent.args["beginData"]["frame"];
1711 for (var invalidation of this._invalidationsOfTypes([WebInspector.Timeli neModel.RecordType.LayoutInvalidationTracking])) {
1712 if (invalidation.linkedLayoutEvent)
1713 continue;
1714 this._addInvalidationToEvent(layoutEvent, layoutFrameId, invalidatio n);
1715 invalidation.linkedLayoutEvent = true;
1716 }
1717 },
1718
1719 /**
1720 * @param {!WebInspector.TracingModel.Event} paintEvent
1721 */
1722 didPaint: function(paintEvent)
1723 {
1724 this._didPaint = true;
1725
1726 // If a paint doesn't have a corresponding graphics layer id, it paints
1727 // into its parent so add an effectivePaintId to these events.
1728 var layerId = paintEvent.args["data"]["layerId"];
1729 if (layerId)
1730 this._lastPaintWithLayer = paintEvent;
1731 // Quietly discard top-level paints without layerId, as these are likely
1732 // to come from overlay.
1733 if (!this._lastPaintWithLayer)
1734 return;
1735
1736 var effectivePaintId = this._lastPaintWithLayer.args["data"]["nodeId"];
1737 var paintFrameId = paintEvent.args["data"]["frame"];
1738 var types = [WebInspector.TimelineModel.RecordType.StyleRecalcInvalidati onTracking,
1739 WebInspector.TimelineModel.RecordType.LayoutInvalidationTracking,
1740 WebInspector.TimelineModel.RecordType.PaintInvalidationTracking,
1741 WebInspector.TimelineModel.RecordType.ScrollInvalidationTracking];
1742 for (var invalidation of this._invalidationsOfTypes(types)) {
1743 if (invalidation.paintId === effectivePaintId)
1744 this._addInvalidationToEvent(paintEvent, paintFrameId, invalidat ion);
1745 }
1746 },
1747
1748 /**
1749 * @param {!WebInspector.TracingModel.Event} event
1750 * @param {number} eventFrameId
1751 * @param {!WebInspector.InvalidationTrackingEvent} invalidation
1752 */
1753 _addInvalidationToEvent: function(event, eventFrameId, invalidation)
1754 {
1755 if (eventFrameId !== invalidation.frame)
1756 return;
1757 if (!event.invalidationTrackingEvents)
1758 event.invalidationTrackingEvents = [ invalidation ];
1759 else
1760 event.invalidationTrackingEvents.push(invalidation);
1761 },
1762
1763 /**
1764 * @param {!Array.<string>=} types
1765 * @return {!Iterator.<!WebInspector.InvalidationTrackingEvent>}
1766 */
1767 _invalidationsOfTypes: function(types)
1768 {
1769 var invalidations = this._invalidations;
1770 if (!types)
1771 types = Object.keys(invalidations);
1772 function* generator()
1773 {
1774 for (var i = 0; i < types.length; ++i) {
1775 var invalidationList = invalidations[types[i]] || [];
1776 for (var j = 0; j < invalidationList.length; ++j)
1777 yield invalidationList[j];
1778 }
1779 }
1780 return generator();
1781 },
1782
1783 _startNewFrameIfNeeded: function()
1784 {
1785 if (!this._didPaint)
1786 return;
1787
1788 this._initializePerFrameState();
1789 },
1790
1791 _initializePerFrameState: function()
1792 {
1793 /** @type {!Object.<string, !Array.<!WebInspector.InvalidationTrackingEv ent>>} */
1794 this._invalidations = {};
1795 /** @type {!Object.<number, !Array.<!WebInspector.InvalidationTrackingEv ent>>} */
1796 this._invalidationsByNodeId = {};
1797
1798 this._lastRecalcStyle = undefined;
1799 this._lastPaintWithLayer = undefined;
1800 this._didPaint = false;
1801 }
1802 };
1803
1804 /** 1757 /**
1805 * @constructor 1758 * @unrestricted
1806 */ 1759 */
1807 WebInspector.TimelineAsyncEventTracker = function() 1760 WebInspector.TimelineAsyncEventTracker = class {
1808 { 1761 constructor() {
1809 WebInspector.TimelineAsyncEventTracker._initialize(); 1762 WebInspector.TimelineAsyncEventTracker._initialize();
1810 /** @type {!Map<!WebInspector.TimelineModel.RecordType, !Map<string, !WebIns pector.TracingModel.Event>>} */ 1763 /** @type {!Map<!WebInspector.TimelineModel.RecordType, !Map<string, !WebIns pector.TracingModel.Event>>} */
1811 this._initiatorByType = new Map(); 1764 this._initiatorByType = new Map();
1812 for (var initiator of WebInspector.TimelineAsyncEventTracker._asyncEvents.ke ys()) 1765 for (var initiator of WebInspector.TimelineAsyncEventTracker._asyncEvents.ke ys())
1813 this._initiatorByType.set(initiator, new Map()); 1766 this._initiatorByType.set(initiator, new Map());
1814 }; 1767 }
1815 1768
1816 WebInspector.TimelineAsyncEventTracker._initialize = function() 1769 static _initialize() {
1817 {
1818 if (WebInspector.TimelineAsyncEventTracker._asyncEvents) 1770 if (WebInspector.TimelineAsyncEventTracker._asyncEvents)
1819 return; 1771 return;
1820 var events = new Map(); 1772 var events = new Map();
1821 var type = WebInspector.TimelineModel.RecordType; 1773 var type = WebInspector.TimelineModel.RecordType;
1822 1774
1823 events.set(type.TimerInstall, {causes: [type.TimerFire], joinBy: "timerId"}) ; 1775 events.set(type.TimerInstall, {causes: [type.TimerFire], joinBy: 'timerId'}) ;
1824 events.set(type.ResourceSendRequest, {causes: [type.ResourceReceiveResponse, type.ResourceReceivedData, type.ResourceFinish], joinBy: "requestId"}); 1776 events.set(
1825 events.set(type.RequestAnimationFrame, {causes: [type.FireAnimationFrame], j oinBy: "id"}); 1777 type.ResourceSendRequest,
1826 events.set(type.RequestIdleCallback, {causes: [type.FireIdleCallback], joinB y: "id"}); 1778 {causes: [type.ResourceReceiveResponse, type.ResourceReceivedData, type. ResourceFinish], joinBy: 'requestId'});
1827 events.set(type.WebSocketCreate, {causes: [type.WebSocketSendHandshakeReques t, type.WebSocketReceiveHandshakeResponse, type.WebSocketDestroy], joinBy: "iden tifier"}); 1779 events.set(type.RequestAnimationFrame, {causes: [type.FireAnimationFrame], j oinBy: 'id'});
1780 events.set(type.RequestIdleCallback, {causes: [type.FireIdleCallback], joinB y: 'id'});
1781 events.set(type.WebSocketCreate, {
1782 causes: [type.WebSocketSendHandshakeRequest, type.WebSocketReceiveHandshak eResponse, type.WebSocketDestroy],
1783 joinBy: 'identifier'
1784 });
1828 1785
1829 WebInspector.TimelineAsyncEventTracker._asyncEvents = events; 1786 WebInspector.TimelineAsyncEventTracker._asyncEvents = events;
1830 /** @type {!Map<!WebInspector.TimelineModel.RecordType, !WebInspector.Timeli neModel.RecordType>} */ 1787 /** @type {!Map<!WebInspector.TimelineModel.RecordType, !WebInspector.Timeli neModel.RecordType>} */
1831 WebInspector.TimelineAsyncEventTracker._typeToInitiator = new Map(); 1788 WebInspector.TimelineAsyncEventTracker._typeToInitiator = new Map();
1832 for (var entry of events) { 1789 for (var entry of events) {
1833 var types = entry[1].causes; 1790 var types = entry[1].causes;
1834 for (type of types) 1791 for (type of types)
1835 WebInspector.TimelineAsyncEventTracker._typeToInitiator.set(type, en try[0]); 1792 WebInspector.TimelineAsyncEventTracker._typeToInitiator.set(type, entry[ 0]);
1836 } 1793 }
1794 }
1795
1796 /**
1797 * @param {!WebInspector.TracingModel.Event} event
1798 */
1799 processEvent(event) {
1800 var initiatorType = WebInspector.TimelineAsyncEventTracker._typeToInitiator. get(
1801 /** @type {!WebInspector.TimelineModel.RecordType} */ (event.name));
1802 var isInitiator = !initiatorType;
1803 if (!initiatorType)
1804 initiatorType = /** @type {!WebInspector.TimelineModel.RecordType} */ (eve nt.name);
1805 var initiatorInfo = WebInspector.TimelineAsyncEventTracker._asyncEvents.get( initiatorType);
1806 if (!initiatorInfo)
1807 return;
1808 var id = event.args['data'][initiatorInfo.joinBy];
1809 if (!id)
1810 return;
1811 /** @type {!Map<string, !WebInspector.TracingModel.Event>|undefined} */
1812 var initiatorMap = this._initiatorByType.get(initiatorType);
1813 if (isInitiator)
1814 initiatorMap.set(id, event);
1815 else
1816 event.initiator = initiatorMap.get(id) || null;
1817 }
1837 }; 1818 };
1838 1819
1839 WebInspector.TimelineAsyncEventTracker.prototype = { 1820
1840 /**
1841 * @param {!WebInspector.TracingModel.Event} event
1842 */
1843 processEvent: function(event)
1844 {
1845 var initiatorType = WebInspector.TimelineAsyncEventTracker._typeToInitia tor.get(/** @type {!WebInspector.TimelineModel.RecordType} */ (event.name));
1846 var isInitiator = !initiatorType;
1847 if (!initiatorType)
1848 initiatorType = /** @type {!WebInspector.TimelineModel.RecordType} * / (event.name);
1849 var initiatorInfo = WebInspector.TimelineAsyncEventTracker._asyncEvents. get(initiatorType);
1850 if (!initiatorInfo)
1851 return;
1852 var id = event.args["data"][initiatorInfo.joinBy];
1853 if (!id)
1854 return;
1855 /** @type {!Map<string, !WebInspector.TracingModel.Event>|undefined} */
1856 var initiatorMap = this._initiatorByType.get(initiatorType);
1857 if (isInitiator)
1858 initiatorMap.set(id, event);
1859 else
1860 event.initiator = initiatorMap.get(id) || null;
1861 }
1862 };
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698