OLD | NEW |
| (Empty) |
1 /* | |
2 * Copyright 2014 The Chromium Authors. All rights reserved. | |
3 * Use of this source code is governed by a BSD-style license that can be | |
4 * found in the LICENSE file. | |
5 */ | |
6 | |
7 /** | |
8 * @constructor | |
9 * @implements {WebInspector.TimelineModeView} | |
10 * @implements {WebInspector.FlameChartDelegate} | |
11 * @extends {WebInspector.VBox} | |
12 * @param {!WebInspector.TimelineModeViewDelegate} delegate | |
13 * @param {!WebInspector.TracingModel} tracingModel | |
14 * @param {!WebInspector.TimelineModel} modelForMinimumBoundary | |
15 */ | |
16 WebInspector.TimelineTracingView = function(delegate, tracingModel, modelForMini
mumBoundary) | |
17 { | |
18 WebInspector.VBox.call(this); | |
19 this._delegate = delegate; | |
20 this._tracingModel = tracingModel; | |
21 this.element.classList.add("timeline-flamechart"); | |
22 this.registerRequiredCSS("flameChart.css"); | |
23 this._dataProvider = new WebInspector.TraceViewFlameChartDataProvider(this._
tracingModel, modelForMinimumBoundary); | |
24 this._mainView = new WebInspector.FlameChart(this._dataProvider, this, true)
; | |
25 this._mainView.show(this.element); | |
26 this._mainView.addEventListener(WebInspector.FlameChart.Events.EntrySelected
, this._onEntrySelected, this); | |
27 } | |
28 | |
29 WebInspector.TimelineTracingView.prototype = { | |
30 /** | |
31 * @param {number} windowStartTime | |
32 * @param {number} windowEndTime | |
33 */ | |
34 requestWindowTimes: function(windowStartTime, windowEndTime) | |
35 { | |
36 this._delegate.requestWindowTimes(windowStartTime, windowEndTime); | |
37 }, | |
38 | |
39 wasShown: function() | |
40 { | |
41 this._mainView.scheduleUpdate(); | |
42 }, | |
43 | |
44 /** | |
45 * @return {!WebInspector.View} | |
46 */ | |
47 view: function() | |
48 { | |
49 return this; | |
50 }, | |
51 | |
52 dispose: function() | |
53 { | |
54 }, | |
55 | |
56 reset: function() | |
57 { | |
58 this._dataProvider.reset(); | |
59 this._mainView.setWindowTimes(0, Infinity); | |
60 }, | |
61 | |
62 /** | |
63 * @param {?RegExp} textFilter | |
64 */ | |
65 refreshRecords: function(textFilter) | |
66 { | |
67 this._dataProvider.reset(); | |
68 this._mainView.scheduleUpdate(); | |
69 }, | |
70 | |
71 /** | |
72 * @param {!WebInspector.TimelineModel.Record} record | |
73 */ | |
74 addRecord: function(record) {}, | |
75 | |
76 /** | |
77 * @param {?WebInspector.TimelineModel.Record} record | |
78 * @param {string=} regex | |
79 * @param {boolean=} selectRecord | |
80 */ | |
81 highlightSearchResult: function(record, regex, selectRecord) {}, | |
82 | |
83 /** | |
84 * @param {number} startTime | |
85 * @param {number} endTime | |
86 */ | |
87 setWindowTimes: function(startTime, endTime) | |
88 { | |
89 this._mainView.setWindowTimes(startTime, endTime); | |
90 }, | |
91 | |
92 /** | |
93 * @param {number} width | |
94 */ | |
95 setSidebarSize: function(width) {}, | |
96 | |
97 /** | |
98 * @param {?WebInspector.TimelineSelection} selection | |
99 */ | |
100 setSelection: function(selection) {}, | |
101 | |
102 /** | |
103 * @param {!WebInspector.Event} event | |
104 */ | |
105 _onEntrySelected: function(event) | |
106 { | |
107 var index = /** @type {number} */ (event.data); | |
108 var record = this._dataProvider._recordAt(index); | |
109 if (!record || this._dataProvider._isHeaderRecord(record)) { | |
110 this._delegate.showInDetails("", document.createTextNode("")); | |
111 return; | |
112 } | |
113 var contentHelper = new WebInspector.TimelineDetailsContentHelper(null,
null, false); | |
114 contentHelper.appendTextRow(WebInspector.UIString("Name"), record.name); | |
115 contentHelper.appendTextRow(WebInspector.UIString("Category"), record.ca
tegory); | |
116 contentHelper.appendTextRow(WebInspector.UIString("Start"), Number.milli
sToString(record.startTime - this._tracingModel.minimumRecordTime(), true)); | |
117 contentHelper.appendTextRow(WebInspector.UIString("Duration"), Number.mi
llisToString(record.duration, true)); | |
118 if (!Object.isEmpty(record.args)) | |
119 contentHelper.appendElementRow(WebInspector.UIString("Arguments"), t
his._formatArguments(record.args)); | |
120 | |
121 function reveal() | |
122 { | |
123 WebInspector.Revealer.reveal(new WebInspector.DeferredTracingLayerTr
ee(record.thread.target(), record.args["snapshot"]["active_tree"]["root_layer"],
record.args["snapshot"]["device_viewport_size"])); | |
124 } | |
125 /** | |
126 * @param {!Node=} node | |
127 * @this {WebInspector.TimelineTracingView} | |
128 */ | |
129 function appendPreviewAndShowDetails(node) | |
130 { | |
131 if (node) | |
132 contentHelper.appendElementRow("Preview", node); | |
133 this._delegate.showInDetails(WebInspector.UIString("Selected Event")
, contentHelper.element); | |
134 } | |
135 var recordTypes = WebInspector.TracingTimelineModel.RecordType; | |
136 switch (record.name) { | |
137 case recordTypes.PictureSnapshot: | |
138 WebInspector.TracingTimelineUIUtils.buildPicturePreviewContent(this.
_tracingModel.target(), record.args["snapshot"]["skp64"], appendPreviewAndShowDe
tails.bind(this)); | |
139 break; | |
140 case recordTypes.LayerTreeHostImplSnapshot: | |
141 var link = document.createElement("span"); | |
142 link.classList.add("revealable-link"); | |
143 link.textContent = "show"; | |
144 link.addEventListener("click", reveal, false); | |
145 contentHelper.appendElementRow(WebInspector.UIString("Layer tree"),
link); | |
146 // Fall-through intended. | |
147 default: | |
148 this._delegate.showInDetails(WebInspector.UIString("Selected Event")
, contentHelper.element); | |
149 } | |
150 }, | |
151 | |
152 /** | |
153 * @param {!Object} args | |
154 * @return {!Element} | |
155 */ | |
156 _formatArguments: function(args) | |
157 { | |
158 var table = document.createElement("table"); | |
159 for (var name in args) { | |
160 var row = table.createChild("tr"); | |
161 row.createChild("td", "timeline-details-row-title").textContent = na
me + ":"; | |
162 var valueContainer = row.createChild("td", "timeline-details-row-dat
a"); | |
163 var value = args[name]; | |
164 if (typeof value === "object" && value) { | |
165 var localObject = new WebInspector.LocalJSONObject(value); | |
166 var propertiesSection = new WebInspector.ObjectPropertiesSection
(localObject, localObject.description); | |
167 valueContainer.appendChild(propertiesSection.element); | |
168 } else { | |
169 valueContainer.textContent = String(value); | |
170 } | |
171 } | |
172 return table; | |
173 }, | |
174 | |
175 __proto__: WebInspector.VBox.prototype | |
176 }; | |
177 | |
178 /** | |
179 * @constructor | |
180 * @implements {WebInspector.FlameChartDataProvider} | |
181 * @param {!WebInspector.TracingModel} model | |
182 * @param {!WebInspector.TimelineModel} timelineModelForMinimumBoundary | |
183 */ | |
184 WebInspector.TraceViewFlameChartDataProvider = function(model, timelineModelForM
inimumBoundary) | |
185 { | |
186 WebInspector.FlameChartDataProvider.call(this); | |
187 this._model = model; | |
188 this._timelineModelForMinimumBoundary = timelineModelForMinimumBoundary; | |
189 this._font = "12px " + WebInspector.fontFamily(); | |
190 this._palette = new WebInspector.TraceViewPalette(); | |
191 var dummyEventPayload = { | |
192 cat: "dummy", | |
193 pid: 0, | |
194 tid: 0, | |
195 ts: 0, | |
196 ph: "dummy", | |
197 name: "dummy", | |
198 args: {}, | |
199 dur: 0, | |
200 id: 0, | |
201 s: "" | |
202 } | |
203 this._processHeaderRecord = new WebInspector.TracingModel.Event(dummyEventPa
yload, 0, null); | |
204 this._threadHeaderRecord = new WebInspector.TracingModel.Event(dummyEventPay
load, 0, null); | |
205 } | |
206 | |
207 WebInspector.TraceViewFlameChartDataProvider.prototype = { | |
208 /** | |
209 * @return {number} | |
210 */ | |
211 barHeight: function() | |
212 { | |
213 return 20; | |
214 }, | |
215 | |
216 /** | |
217 * @return {number} | |
218 */ | |
219 textBaseline: function() | |
220 { | |
221 return 6; | |
222 }, | |
223 | |
224 /** | |
225 * @return {number} | |
226 */ | |
227 textPadding: function() | |
228 { | |
229 return 5; | |
230 }, | |
231 | |
232 /** | |
233 * @param {number} entryIndex | |
234 * @return {string} | |
235 */ | |
236 entryFont: function(entryIndex) | |
237 { | |
238 return this._font; | |
239 }, | |
240 | |
241 /** | |
242 * @param {number} entryIndex | |
243 * @return {?string} | |
244 */ | |
245 entryTitle: function(entryIndex) | |
246 { | |
247 var record = this._records[entryIndex]; | |
248 if (this._isHeaderRecord(record)) | |
249 return this._headerTitles[entryIndex] | |
250 return record.name; | |
251 }, | |
252 | |
253 /** | |
254 * @param {number} startTime | |
255 * @param {number} endTime | |
256 * @return {?Array.<number>} | |
257 */ | |
258 dividerOffsets: function(startTime, endTime) | |
259 { | |
260 return null; | |
261 }, | |
262 | |
263 reset: function() | |
264 { | |
265 this._timelineData = null; | |
266 /** @type {!Array.<!WebInspector.TracingModel.Event>} */ | |
267 this._records = []; | |
268 }, | |
269 | |
270 /** | |
271 * @return {!WebInspector.FlameChart.TimelineData} | |
272 */ | |
273 timelineData: function() | |
274 { | |
275 if (this._timelineData) | |
276 return this._timelineData; | |
277 | |
278 /** | |
279 * @type {?WebInspector.FlameChart.TimelineData} | |
280 */ | |
281 this._timelineData = { | |
282 entryLevels: [], | |
283 entryTotalTimes: [], | |
284 entryStartTimes: [] | |
285 }; | |
286 | |
287 this._currentLevel = 0; | |
288 this._headerTitles = {}; | |
289 this._minimumBoundary = this._timelineModelForMinimumBoundary.minimumRec
ordTime(); | |
290 this._timeSpan = Math.max(this._model.maximumRecordTime() - this._minimu
mBoundary, 1000); | |
291 var processes = this._model.sortedProcesses(); | |
292 for (var processIndex = 0; processIndex < processes.length; ++processInd
ex) { | |
293 var process = processes[processIndex]; | |
294 this._appendHeaderRecord(process.name(), this._processHeaderRecord); | |
295 var objectNames = process.sortedObjectNames(); | |
296 for (var objectNameIndex = 0; objectNameIndex < objectNames.length;
++objectNameIndex) { | |
297 this._appendHeaderRecord(WebInspector.UIString("Object %s", obje
ctNames[objectNameIndex]), this._threadHeaderRecord); | |
298 var objects = process.objectsByName(objectNames[objectNameIndex]
); | |
299 for (var objectIndex = 0; objectIndex < objects.length; ++object
Index) | |
300 this._appendRecord(objects[objectIndex], 0); | |
301 ++this._currentLevel; | |
302 } | |
303 var threads = process.sortedThreads(); | |
304 for (var threadIndex = 0; threadIndex < threads.length; ++threadInde
x) { | |
305 this._appendHeaderRecord(threads[threadIndex].name(), this._thre
adHeaderRecord); | |
306 var events = threads[threadIndex].events(); | |
307 for (var eventIndex = 0; eventIndex < events.length; ++eventInde
x) { | |
308 var event = events[eventIndex]; | |
309 if (event.duration) | |
310 this._appendRecord(event, event.level); | |
311 } | |
312 this._currentLevel += threads[threadIndex].maxStackDepth(); | |
313 } | |
314 ++this._currentLevel; | |
315 } | |
316 return this._timelineData; | |
317 }, | |
318 | |
319 /** | |
320 * @return {number} | |
321 */ | |
322 minimumBoundary: function() | |
323 { | |
324 return this._minimumBoundary; | |
325 }, | |
326 | |
327 /** | |
328 * @return {number} | |
329 */ | |
330 totalTime: function() | |
331 { | |
332 return this._timeSpan; | |
333 }, | |
334 | |
335 /** | |
336 * @return {number} | |
337 */ | |
338 maxStackDepth: function() | |
339 { | |
340 return this._currentLevel; | |
341 }, | |
342 | |
343 /** | |
344 * @param {number} entryIndex | |
345 * @return {?Array.<!{title: string, text: string}>} | |
346 */ | |
347 prepareHighlightedEntryInfo: function(entryIndex) | |
348 { | |
349 return null; | |
350 }, | |
351 | |
352 /** | |
353 * @param {number} entryIndex | |
354 * @return {boolean} | |
355 */ | |
356 canJumpToEntry: function(entryIndex) | |
357 { | |
358 var record = this._records[entryIndex]; | |
359 return record.phase === WebInspector.TracingModel.Phase.SnapshotObject; | |
360 }, | |
361 | |
362 /** | |
363 * @param {number} entryIndex | |
364 * @return {string} | |
365 */ | |
366 entryColor: function(entryIndex) | |
367 { | |
368 var record = this._records[entryIndex]; | |
369 if (record.phase === WebInspector.TracingModel.Phase.SnapshotObject) | |
370 return "rgb(20, 150, 20)"; | |
371 if (record === this._processHeaderRecord) | |
372 return "#555"; | |
373 if (record === this._threadHeaderRecord) | |
374 return "#777"; | |
375 return this._palette.colorForString(record.name); | |
376 }, | |
377 | |
378 /** | |
379 * @param {number} entryIndex | |
380 * @param {!CanvasRenderingContext2D} context | |
381 * @param {?string} text | |
382 * @param {number} barX | |
383 * @param {number} barY | |
384 * @param {number} barWidth | |
385 * @param {number} barHeight | |
386 * @param {function(number):number} timeToPosition | |
387 * @return {boolean} | |
388 */ | |
389 decorateEntry: function(entryIndex, context, text, barX, barY, barWidth, bar
Height, timeToPosition) | |
390 { | |
391 return false; | |
392 }, | |
393 | |
394 /** | |
395 * @param {number} entryIndex | |
396 * @return {boolean} | |
397 */ | |
398 forceDecoration: function(entryIndex) | |
399 { | |
400 return false; | |
401 }, | |
402 | |
403 /** | |
404 * @param {number} entryIndex | |
405 * @return {?{startTime: number, endTime: number}} | |
406 */ | |
407 highlightTimeRange: function(entryIndex) | |
408 { | |
409 var record = this._records[entryIndex]; | |
410 if (!record || this._isHeaderRecord(record)) | |
411 return null; | |
412 return { | |
413 startTime: record.startTime, | |
414 endTime: record.endTime | |
415 } | |
416 }, | |
417 | |
418 /** | |
419 * @return {number} | |
420 */ | |
421 paddingLeft: function() | |
422 { | |
423 return 0; | |
424 }, | |
425 | |
426 /** | |
427 * @param {number} entryIndex | |
428 * @return {string} | |
429 */ | |
430 textColor: function(entryIndex) | |
431 { | |
432 return "white"; | |
433 }, | |
434 | |
435 /** | |
436 * @param {string} title | |
437 * @param {!WebInspector.TracingModel.Event} record | |
438 */ | |
439 _appendHeaderRecord: function(title, record) | |
440 { | |
441 var index = this._records.length; | |
442 this._records.push(record); | |
443 this._timelineData.entryLevels[index] = this._currentLevel++; | |
444 this._timelineData.entryTotalTimes[index] = this.totalTime(); | |
445 this._timelineData.entryStartTimes[index] = this._minimumBoundary; | |
446 this._headerTitles[index] = title; | |
447 }, | |
448 | |
449 /** | |
450 * @param {!WebInspector.TracingModel.Event} record | |
451 * @param {number} level | |
452 */ | |
453 _appendRecord: function(record, level) | |
454 { | |
455 var index = this._records.length; | |
456 this._records.push(record); | |
457 this._timelineData.entryLevels[index] = this._currentLevel + level; | |
458 this._timelineData.entryTotalTimes[index] = record.phase === WebInspecto
r.TracingModel.Phase.SnapshotObject ? NaN : record.duration || 0; | |
459 this._timelineData.entryStartTimes[index] = record.startTime; | |
460 }, | |
461 | |
462 /** | |
463 * @param {!WebInspector.TracingModel.Event} record | |
464 */ | |
465 _isHeaderRecord: function(record) | |
466 { | |
467 return record === this._threadHeaderRecord || record === this._processHe
aderRecord; | |
468 }, | |
469 | |
470 /** | |
471 * @param {number} index | |
472 * @return {!WebInspector.TracingModel.Event|undefined} | |
473 */ | |
474 _recordAt: function(index) | |
475 { | |
476 return this._records[index]; | |
477 } | |
478 } | |
479 | |
480 // The below logic is shamelessly stolen from https://code.google.com/p/trace-vi
ewer/source/browse/trunk/trace_viewer/tracing/color_scheme.js | |
481 | |
482 /** | |
483 * @constructor | |
484 */ | |
485 WebInspector.TraceViewPalette = function() | |
486 { | |
487 this._palette = WebInspector.TraceViewPalette._paletteBase.map(WebInspector.
TraceViewPalette._rgbToString); | |
488 } | |
489 | |
490 WebInspector.TraceViewPalette._paletteBase = [ | |
491 [138, 113, 152], | |
492 [175, 112, 133], | |
493 [127, 135, 225], | |
494 [93, 81, 137], | |
495 [116, 143, 119], | |
496 [178, 214, 122], | |
497 [87, 109, 147], | |
498 [119, 155, 95], | |
499 [114, 180, 160], | |
500 [132, 85, 103], | |
501 [157, 210, 150], | |
502 [148, 94, 86], | |
503 [164, 108, 138], | |
504 [139, 191, 150], | |
505 [110, 99, 145], | |
506 [80, 129, 109], | |
507 [125, 140, 149], | |
508 [93, 124, 132], | |
509 [140, 85, 140], | |
510 [104, 163, 162], | |
511 [132, 141, 178], | |
512 [131, 105, 147], | |
513 [135, 183, 98], | |
514 [152, 134, 177], | |
515 [141, 188, 141], | |
516 [133, 160, 210], | |
517 [126, 186, 148], | |
518 [112, 198, 205], | |
519 [180, 122, 195], | |
520 [203, 144, 152] | |
521 ]; | |
522 | |
523 /** | |
524 * @param {string} string | |
525 * @return {number} | |
526 */ | |
527 WebInspector.TraceViewPalette._stringHash = function(string) | |
528 { | |
529 var hash = 0; | |
530 for (var i = 0; i < string.length; ++i) | |
531 hash = (hash + 37 * hash + 11 * string.charCodeAt(i)) % 0xFFFFFFFF; | |
532 return hash; | |
533 } | |
534 | |
535 /** | |
536 * @param {!Array.<number>} rgb | |
537 * @return {string} | |
538 */ | |
539 WebInspector.TraceViewPalette._rgbToString = function(rgb) | |
540 { | |
541 return "rgb(" + rgb.join(",") + ")"; | |
542 } | |
543 | |
544 WebInspector.TraceViewPalette.prototype = { | |
545 /** | |
546 * @param {string} string | |
547 * @return {string} | |
548 */ | |
549 colorForString: function(string) | |
550 { | |
551 var hash = WebInspector.TraceViewPalette._stringHash(string); | |
552 return this._palette[hash % this._palette.length]; | |
553 } | |
554 }; | |
OLD | NEW |