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 tvcm.requireStylesheet('tvcm.ui.common');
12 tvcm.requireStylesheet('tracing.timeline_view');
13 tvcm.requireTemplate('tracing.timeline_view');
15 tvcm.require('tvcm.utils');
16 tvcm.require('tvcm.settings');
17 tvcm.require('tracing.analysis.analysis_view');
18 tvcm.require('tracing.find_control');
19 tvcm.require('tracing.timeline_track_view');
20 tvcm.require('tvcm.ui.dom_helpers');
21 tvcm.require('tvcm.ui.overlay');
22 tvcm.require('tvcm.ui.drag_handle');
24 tvcm.require('tracing.analysis.cpu_slice_view');
25 tvcm.require('tracing.analysis.thread_time_slice_view');
27 tvcm.exportTo('tracing', function() {
32 * @extends {HTMLUnknownElement}
34 var TimelineView = tvcm.ui.define('x-timeline-view');
36 TimelineView.prototype = {
37 __proto__: HTMLUnknownElement.prototype,
39 decorate: function() {
40 var node = tvcm.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.rightControls.appendChild(this.createMetadataButton_());
52 this.rightControls.appendChild(this.findCtl_);
53 this.rightControls.appendChild(this.createHelpButton_());
55 this.dragEl_ = new tvcm.ui.DragHandle();
56 this.appendChild(this.dragEl_);
58 this.analysisEl_ = new tracing.analysis.AnalysisView();
59 this.appendChild(this.analysisEl_);
60 this.addEventListener('requestSelectionChange',
61 this.onRequestSelectionChange_.bind(this));
64 this.onSelectionChanged_ = this.onSelectionChanged_.bind(this);
65 document.addEventListener('keydown', this.onKeyDown_.bind(this), true);
66 document.addEventListener('keypress', this.onKeypress_.bind(this), true);
68 this.dragEl_.target = this.analysisEl_;
71 createHelpButton_: function() {
72 var node = tvcm.instantiateTemplate('#help-btn-template');
73 var showEl = node.querySelector('.view-help-button');
74 var helpTextEl = node.querySelector('.view-help-text');
76 var dlg = new tvcm.ui.Overlay();
77 dlg.title = 'chrome://tracing Help';
78 dlg.classList.add('view-help-overlay');
79 dlg.appendChild(node);
82 dlg.visible = !dlg.visible;
84 var mod = tvcm.isMac ? 'cmd ' : 'ctrl';
85 var spans = helpTextEl.querySelectorAll('span.mod');
86 for (var i = 0; i < spans.length; i++) {
87 spans[i].textContent = mod;
90 // Stop event so it doesn't trigger new click listener on document.
94 showEl.addEventListener('click', onClick.bind(this));
99 createMetadataButton_: function() {
100 var node = tvcm.instantiateTemplate('#metadata-btn-template');
101 var showEl = node.querySelector('.view-metadata-button');
102 var textEl = node.querySelector('.info-button-text');
104 var dlg = new tvcm.ui.Overlay();
105 dlg.title = 'Metadata for trace';
106 dlg.classList.add('view-metadata-overlay');
107 dlg.appendChild(node);
109 function onClick(e) {
112 var metadataStrings = [];
114 var model = this.model;
115 for (var data in model.metadata) {
116 var meta = model.metadata[data];
117 var name = JSON.stringify(meta.name);
118 var value = JSON.stringify(meta.value, undefined, ' ');
120 metadataStrings.push(name + ': ' + value);
122 textEl.textContent = metadataStrings.join('\n');
127 showEl.addEventListener('click', onClick.bind(this));
129 function updateVisibility() {
130 showEl.style.display =
131 (this.model && this.model.metadata.length) ? '' : 'none';
133 var updateVisibility_ = updateVisibility.bind(this);
135 this.addEventListener('modelChange', updateVisibility_);
141 return this.leftControlsEl_;
144 get rightControls() {
145 return this.rightControlsEl_;
149 return this.titleEl_.textContent.substring(
150 this.titleEl_.textContent.length - 2);
153 set viewTitle(text) {
154 if (text === undefined) {
155 this.titleEl_.textContent = '';
156 this.titleEl_.hidden = true;
159 this.titleEl_.hidden = false;
160 this.titleEl_.textContent = text;
165 return this.timeline_.model;
170 var modelInstanceChanged = model != this.model;
171 var modelValid = model && !model.bounds.isEmpty;
173 // Remove old timeline if the model has completely changed.
174 if (modelInstanceChanged) {
175 this.timelineContainer_.textContent = '';
176 if (this.timeline_) {
177 this.timeline_.removeEventListener(
178 'selectionChange', this.onSelectionChanged_);
179 this.timeline_.detach();
180 this.timeline_ = undefined;
181 this.findCtl_.controller.timeline = undefined;
185 // Create new timeline if needed.
186 if (modelValid && !this.timeline_) {
187 this.timeline_ = new tracing.TimelineTrackView();
188 this.timeline_.focusElement =
189 this.focusElement_ ? this.focusElement_ : this.parentElement;
190 this.timelineContainer_.appendChild(this.timeline_);
191 this.findCtl_.controller.timeline = this.timeline_;
192 this.timeline_.addEventListener(
193 'selectionChange', this.onSelectionChanged_);
194 this.analysisEl_.clearSelectionHistory();
199 this.timeline_.model = model;
200 tvcm.dispatchSimpleEvent(this, 'modelChange');
202 // Do things that are selection specific
203 if (modelInstanceChanged)
204 this.onSelectionChanged_();
208 return this.timeline_;
213 this.settings_ = new tvcm.Settings();
214 return this.settings_;
218 * Sets the element whose focus state will determine whether
219 * to respond to keybaord input.
221 set focusElement(value) {
222 this.focusElement_ = value;
224 this.timeline_.focusElement = value;
228 * @return {Element} The element whose focused state determines
229 * whether to respond to keyboard inputs.
230 * Defaults to the parent element.
233 if (this.focusElement_)
234 return this.focusElement_;
235 return this.parentElement;
239 * @return {boolean} Whether the current timeline is attached to the
242 get isAttachedToDocument_() {
244 while (cur.parentNode)
245 cur = cur.parentNode;
246 return cur == this.ownerDocument;
249 get listenToKeys_() {
250 if (!this.isAttachedToDocument_)
252 if (!this.focusElement_)
254 if (this.focusElement.tabIndex >= 0)
255 return document.activeElement == this.focusElement;
259 onKeyDown_: function(e) {
260 if (!this.listenToKeys_)
263 if (e.keyCode === 27) { // ESC
269 onKeypress_: function(e) {
270 if (!this.listenToKeys_)
273 if (e.keyCode === '/'.charCodeAt(0)) {
274 if (this.findCtl_.hasFocus())
277 this.findCtl_.focus();
279 } else if (e.keyCode === '?'.charCodeAt(0)) {
280 this.querySelector('.view-help-button').click();
285 beginFind: function() {
286 if (this.findInProgress_)
288 this.findInProgress_ = true;
289 var dlg = tracing.FindControl();
290 dlg.controller = new tracing.FindController();
291 dlg.controller.timeline = this.timeline;
293 dlg.addEventListener('close', function() {
294 this.findInProgress_ = false;
296 dlg.addEventListener('findNext', function() {
298 dlg.addEventListener('findPrevious', function() {
302 onSelectionChanged_: function(e) {
303 var oldScrollTop = this.timelineContainer_.scrollTop;
305 var selection = this.timeline_ ?
306 this.timeline_.selectionOfInterest :
307 new tracing.Selection();
308 this.analysisEl_.selection = selection;
309 this.timelineContainer_.scrollTop = oldScrollTop;
312 onRequestSelectionChange_: function(e) {
313 this.timeline_.selection = e.selection;
319 TimelineView: TimelineView