1 // Copyright (c) 2013 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
8 * @fileoverview View visualizes TRACE_EVENT events using the
9 * tracing.Timeline component and adds in selection summary and control buttons.
11 base.requireStylesheet('ui.trace_viewer');
12 base.requireStylesheet('tracing.timeline_view');
13 base.requireTemplate('tracing.timeline_view');
15 base.require('base.utils');
16 base.require('base.settings');
17 base.require('tracing.analysis.analysis_view');
18 base.require('tracing.find_control');
19 base.require('tracing.timeline_track_view');
20 base.require('ui.dom_helpers');
21 base.require('ui.overlay');
22 base.require('ui.drag_handle');
24 base.require('tracing.analysis.cpu_slice_view');
25 base.require('tracing.analysis.thread_time_slice_view');
27 base.exportTo('tracing', function() {
32 * @extends {HTMLUnknownElement}
34 var TimelineView = ui.define('x-timeline-view');
36 TimelineView.prototype = {
37 __proto__: HTMLUnknownElement.prototype,
39 decorate: function() {
40 var node = base.instantiateTemplate('#timeline-view-template');
41 this.appendChild(node);
43 this.titleEl_ = this.querySelector('.title');
44 this.leftControlsEl_ = this.querySelector('#left-controls');
45 this.rightControlsEl_ = this.querySelector('#right-controls');
46 this.timelineContainer_ = this.querySelector('.container');
48 this.findCtl_ = new tracing.FindControl();
49 this.findCtl_.controller = new tracing.FindController();
51 this.showFlowEvents_ = false;
52 this.rightControls.appendChild(ui.createCheckBox(
53 this, 'showFlowEvents',
54 'tracing.TimelineView.showFlowEvents', false,
57 this.rightControls.appendChild(this.createMetadataButton_());
58 this.rightControls.appendChild(this.findCtl_);
59 this.rightControls.appendChild(this.createHelpButton_());
61 this.dragEl_ = new ui.DragHandle();
62 this.appendChild(this.dragEl_);
64 this.analysisEl_ = new tracing.analysis.AnalysisView();
65 this.appendChild(this.analysisEl_);
66 this.addEventListener('requestSelectionChange',
67 this.onRequestSelectionChange_.bind(this));
70 this.onSelectionChanged_ = this.onSelectionChanged_.bind(this);
71 document.addEventListener('keydown', this.onKeyDown_.bind(this), true);
72 document.addEventListener('keypress', this.onKeypress_.bind(this), true);
74 this.dragEl_.target = this.analysisEl_;
77 get showFlowEvents() {
78 return this.showFlowEvents_;
81 set showFlowEvents(showFlowEvents) {
82 this.showFlowEvents_ = showFlowEvents;
85 this.timeline_.viewport.showFlowEvents = showFlowEvents;
88 createHelpButton_: function() {
89 var node = base.instantiateTemplate('#help-btn-template');
90 var showEl = node.querySelector('.view-help-button');
91 var helpTextEl = node.querySelector('.view-help-text');
93 var dlg = new ui.Overlay();
94 dlg.title = 'chrome://tracing Help';
95 dlg.classList.add('view-help-overlay');
96 dlg.appendChild(node);
99 dlg.visible = !dlg.visible;
101 var mod = base.isMac ? 'cmd ' : 'ctrl';
102 var spans = helpTextEl.querySelectorAll('span.mod');
103 for (var i = 0; i < spans.length; i++) {
104 spans[i].textContent = mod;
107 // Stop event so it doesn't trigger new click listener on document.
111 showEl.addEventListener('click', onClick.bind(this));
116 createMetadataButton_: function() {
117 var node = base.instantiateTemplate('#metadata-btn-template');
118 var showEl = node.querySelector('.view-metadata-button');
119 var textEl = node.querySelector('.info-button-text');
121 var dlg = new ui.Overlay();
122 dlg.title = 'Metadata for trace';
123 dlg.classList.add('view-metadata-overlay');
124 dlg.appendChild(node);
126 function onClick(e) {
129 var metadataStrings = [];
131 var model = this.model;
132 for (var data in model.metadata) {
133 var meta = model.metadata[data];
134 var name = JSON.stringify(meta.name);
135 var value = JSON.stringify(meta.value, undefined, ' ');
137 metadataStrings.push(name + ': ' + value);
139 textEl.textContent = metadataStrings.join('\n');
144 showEl.addEventListener('click', onClick.bind(this));
146 function updateVisibility() {
147 showEl.style.display =
148 (this.model && this.model.metadata.length) ? '' : 'none';
150 var updateVisibility_ = updateVisibility.bind(this);
152 this.addEventListener('modelChange', updateVisibility_);
158 return this.leftControlsEl_;
161 get rightControls() {
162 return this.rightControlsEl_;
166 return this.titleEl_.textContent.substring(
167 this.titleEl_.textContent.length - 2);
170 set viewTitle(text) {
171 if (text === undefined) {
172 this.titleEl_.textContent = '';
173 this.titleEl_.hidden = true;
176 this.titleEl_.hidden = false;
177 this.titleEl_.textContent = text;
182 return this.timeline_.model;
187 var modelInstanceChanged = model != this.model;
188 var modelValid = model && !model.bounds.isEmpty;
190 // Remove old timeline if the model has completely changed.
191 if (modelInstanceChanged) {
192 this.timelineContainer_.textContent = '';
193 if (this.timeline_) {
194 this.timeline_.removeEventListener(
195 'selectionChange', this.onSelectionChanged_);
196 this.timeline_.detach();
197 this.timeline_ = undefined;
198 this.findCtl_.controller.timeline = undefined;
202 // Create new timeline if needed.
203 if (modelValid && !this.timeline_) {
204 this.timeline_ = new tracing.TimelineTrackView();
205 this.timeline_.focusElement =
206 this.focusElement_ ? this.focusElement_ : this.parentElement;
207 this.timelineContainer_.appendChild(this.timeline_);
208 this.findCtl_.controller.timeline = this.timeline_;
209 this.timeline_.addEventListener(
210 'selectionChange', this.onSelectionChanged_);
211 this.timeline_.viewport.showFlowEvents = this.showFlowEvents;
212 this.analysisEl_.clearSelectionHistory();
217 this.timeline_.model = model;
218 base.dispatchSimpleEvent(this, 'modelChange');
220 // Do things that are selection specific
221 if (modelInstanceChanged)
222 this.onSelectionChanged_();
226 return this.timeline_;
231 this.settings_ = new base.Settings();
232 return this.settings_;
236 * Sets the element whose focus state will determine whether
237 * to respond to keybaord input.
239 set focusElement(value) {
240 this.focusElement_ = value;
242 this.timeline_.focusElement = value;
246 * @return {Element} The element whose focused state determines
247 * whether to respond to keyboard inputs.
248 * Defaults to the parent element.
251 if (this.focusElement_)
252 return this.focusElement_;
253 return this.parentElement;
257 * @return {boolean} Whether the current timeline is attached to the
260 get isAttachedToDocument_() {
262 while (cur.parentNode)
263 cur = cur.parentNode;
264 return cur == this.ownerDocument;
267 get listenToKeys_() {
268 if (!this.isAttachedToDocument_)
270 if (!this.focusElement_)
272 if (this.focusElement.tabIndex >= 0)
273 return document.activeElement == this.focusElement;
277 onKeyDown_: function(e) {
278 if (!this.listenToKeys_)
281 if (e.keyCode === 27) { // ESC
287 onKeypress_: function(e) {
288 if (!this.listenToKeys_)
291 if (e.keyCode === '/'.charCodeAt(0)) {
292 if (this.findCtl_.hasFocus())
295 this.findCtl_.focus();
297 } else if (e.keyCode === '?'.charCodeAt(0)) {
298 this.querySelector('.view-help-button').click();
303 beginFind: function() {
304 if (this.findInProgress_)
306 this.findInProgress_ = true;
307 var dlg = tracing.FindControl();
308 dlg.controller = new tracing.FindController();
309 dlg.controller.timeline = this.timeline;
311 dlg.addEventListener('close', function() {
312 this.findInProgress_ = false;
314 dlg.addEventListener('findNext', function() {
316 dlg.addEventListener('findPrevious', function() {
320 onSelectionChanged_: function(e) {
321 var oldScrollTop = this.timelineContainer_.scrollTop;
323 var selection = this.timeline_ ?
324 this.timeline_.selectionOfInterest :
325 new tracing.Selection();
326 this.analysisEl_.selection = selection;
327 this.timelineContainer_.scrollTop = oldScrollTop;
330 onRequestSelectionChange_: function(e) {
331 this.timeline_.selection = e.selection;
337 TimelineView: TimelineView