2b1683631305b9bf09ef3d24eaf0290be4ec498f
[platform/framework/web/crosswalk.git] / src / third_party / trace-viewer / trace_viewer / tracing / timeline_view.js
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.
4
5 'use strict';
6
7 /**
8  * @fileoverview View visualizes TRACE_EVENT events using the
9  * tracing.Timeline component and adds in selection summary and control buttons.
10  */
11 tvcm.requireStylesheet('tvcm.ui.common');
12 tvcm.requireStylesheet('tracing.timeline_view');
13 tvcm.requireTemplate('tracing.timeline_view');
14
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');
23
24 tvcm.require('tracing.analysis.cpu_slice_view');
25 tvcm.require('tracing.analysis.thread_time_slice_view');
26 tvcm.require('tracing.thread_times_side_panel');
27 tvcm.require('tracing.timeline_view_side_panel');
28
29 tvcm.exportTo('tracing', function() {
30
31   /**
32    * View
33    * @constructor
34    * @extends {HTMLUnknownElement}
35    */
36   var TimelineView = tvcm.ui.define('x-timeline-view');
37
38   TimelineView.prototype = {
39     __proto__: HTMLUnknownElement.prototype,
40
41     decorate: function() {
42       var node = tvcm.instantiateTemplate('#timeline-view-template');
43       this.appendChild(node);
44
45       this.titleEl_ = this.querySelector('.title');
46       this.leftControlsEl_ = this.querySelector('#left-controls');
47       this.rightControlsEl_ = this.querySelector('#right-controls');
48       this.timelineViewSidePanelContainer_ = this.querySelector(
49           'x-timeline-view-side-panel-container');
50       this.trackViewContainer_ = this.querySelector('track-view-container');
51
52       tvcm.ui.decorate(this.timelineViewSidePanelContainer_,
53                        tracing.TimelineViewSidePanelContainer);
54
55       this.findCtl_ = new tracing.FindControl();
56       this.findCtl_.controller = new tracing.FindController();
57
58       this.rightControls.appendChild(this.createMetadataButton_());
59       this.rightControls.appendChild(this.findCtl_);
60       this.rightControls.appendChild(this.createHelpButton_());
61
62       this.dragEl_ = this.querySelector('x-drag-handle');
63       tvcm.ui.decorate(this.dragEl_, tvcm.ui.DragHandle);
64
65       this.analysisEl_ = this.querySelector('#analysis');
66       tvcm.ui.decorate(this.analysisEl_, tracing.analysis.AnalysisView);
67
68       this.appendChild(this.analysisEl_);
69       this.addEventListener('requestSelectionChange',
70                             this.onRequestSelectionChange_.bind(this));
71
72       // Bookkeeping.
73       this.onSelectionChanged_ = this.onSelectionChanged_.bind(this);
74       document.addEventListener('keydown', this.onKeyDown_.bind(this), true);
75       document.addEventListener('keypress', this.onKeypress_.bind(this), true);
76
77       this.dragEl_.target = this.analysisEl_;
78     },
79
80     createHelpButton_: function() {
81       var node = tvcm.instantiateTemplate('#help-btn-template');
82       var showEl = node.querySelector('.view-help-button');
83       var helpTextEl = node.querySelector('.view-help-text');
84
85       var dlg = new tvcm.ui.Overlay();
86       dlg.title = 'chrome://tracing Help';
87       dlg.classList.add('view-help-overlay');
88       dlg.appendChild(node);
89
90       function onClick(e) {
91         dlg.visible = !dlg.visible;
92
93         var mod = tvcm.isMac ? 'cmd ' : 'ctrl';
94         var spans = helpTextEl.querySelectorAll('span.mod');
95         for (var i = 0; i < spans.length; i++) {
96           spans[i].textContent = mod;
97         }
98
99         // Stop event so it doesn't trigger new click listener on document.
100         e.stopPropagation();
101         return false;
102       }
103       showEl.addEventListener('click', onClick.bind(this));
104
105       return showEl;
106     },
107
108     createMetadataButton_: function() {
109       var node = tvcm.instantiateTemplate('#metadata-btn-template');
110       var showEl = node.querySelector('.view-metadata-button');
111       var textEl = node.querySelector('.info-button-text');
112
113       var dlg = new tvcm.ui.Overlay();
114       dlg.title = 'Metadata for trace';
115       dlg.classList.add('view-metadata-overlay');
116       dlg.appendChild(node);
117
118       function onClick(e) {
119         dlg.visible = true;
120
121         var metadataStrings = [];
122
123         var model = this.model;
124         for (var data in model.metadata) {
125           var meta = model.metadata[data];
126           var name = JSON.stringify(meta.name);
127           var value = JSON.stringify(meta.value, undefined, ' ');
128
129           metadataStrings.push(name + ': ' + value);
130         }
131         textEl.textContent = metadataStrings.join('\n');
132
133         e.stopPropagation();
134         return false;
135       }
136       showEl.addEventListener('click', onClick.bind(this));
137
138       function updateVisibility() {
139         showEl.style.display =
140             (this.model && this.model.metadata.length) ? '' : 'none';
141       }
142       var updateVisibility_ = updateVisibility.bind(this);
143       updateVisibility_();
144       this.addEventListener('modelChange', updateVisibility_);
145
146       return showEl;
147     },
148
149     get leftControls() {
150       return this.leftControlsEl_;
151     },
152
153     get rightControls() {
154       return this.rightControlsEl_;
155     },
156
157     get viewTitle() {
158       return this.titleEl_.textContent.substring(
159           this.titleEl_.textContent.length - 2);
160     },
161
162     set viewTitle(text) {
163       if (text === undefined) {
164         this.titleEl_.textContent = '';
165         this.titleEl_.hidden = true;
166         return;
167       }
168       this.titleEl_.hidden = false;
169       this.titleEl_.textContent = text;
170     },
171
172     get model() {
173       if (this.trackView_)
174         return this.trackView_.model;
175       return undefined;
176     },
177
178     set model(model) {
179       var modelInstanceChanged = model != this.model;
180       var modelValid = model && !model.bounds.isEmpty;
181
182       // Remove old trackView if the model has completely changed.
183       if (modelInstanceChanged) {
184         this.trackViewContainer_.textContent = '';
185         if (this.trackView_) {
186           this.trackView_.removeEventListener(
187               'selectionChange', this.onSelectionChanged_);
188           this.trackView_.detach();
189           this.trackView_ = undefined;
190           this.findCtl_.controller.trackView = undefined;
191         }
192         this.timelineViewSidePanelContainer_.model = undefined;
193       }
194
195       // Create new trackView if needed.
196       if (modelValid && !this.trackView_) {
197         this.trackView_ = new tracing.TimelineTrackView();
198         this.trackView_.focusElement =
199             this.focusElement_ ? this.focusElement_ : this.parentElement;
200         this.trackViewContainer_.appendChild(this.trackView_);
201         this.findCtl_.controller.timeline = this.trackView_;
202         this.trackView_.addEventListener(
203             'selectionChange', this.onSelectionChanged_);
204         this.analysisEl_.clearSelectionHistory();
205       }
206
207       // Set the model.
208       if (modelValid) {
209         this.trackView_.model = model;
210         this.timelineViewSidePanelContainer_.model = model;
211       }
212       tvcm.dispatchSimpleEvent(this, 'modelChange');
213
214       // Do things that are selection specific
215       if (modelInstanceChanged)
216         this.onSelectionChanged_();
217     },
218
219     get timeline() {
220       return this.trackView_;
221     },
222
223     get settings() {
224       if (!this.settings_)
225         this.settings_ = new tvcm.Settings();
226       return this.settings_;
227     },
228
229     /**
230      * Sets the element whose focus state will determine whether
231      * to respond to keybaord input.
232      */
233     set focusElement(value) {
234       this.focusElement_ = value;
235       if (this.trackView_)
236         this.trackView_.focusElement = value;
237     },
238
239     /**
240      * @return {Element} The element whose focused state determines
241      * whether to respond to keyboard inputs.
242      * Defaults to the parent element.
243      */
244     get focusElement() {
245       if (this.focusElement_)
246         return this.focusElement_;
247       return this.parentElement;
248     },
249
250     /**
251      * @return {boolean} Whether the current view is attached to the
252      * document.
253      */
254     get isAttachedToDocument_() {
255       var cur = this;
256       while (cur.parentNode)
257         cur = cur.parentNode;
258       return cur == this.ownerDocument;
259     },
260
261     get listenToKeys_() {
262       if (!this.isAttachedToDocument_)
263         return;
264       if (!this.focusElement_)
265         return true;
266       if (this.focusElement.tabIndex >= 0)
267         return document.activeElement == this.focusElement;
268       return true;
269     },
270
271     onKeyDown_: function(e) {
272       if (!this.listenToKeys_)
273         return;
274
275       if (e.keyCode === 27) { // ESC
276         this.focus();
277         e.preventDefault();
278       }
279     },
280
281     onKeypress_: function(e) {
282       if (!this.listenToKeys_)
283         return;
284
285       if (e.keyCode === '/'.charCodeAt(0)) {
286         if (this.findCtl_.hasFocus())
287           this.focus();
288         else
289           this.findCtl_.focus();
290         e.preventDefault();
291       } else if (e.keyCode === '?'.charCodeAt(0)) {
292         this.querySelector('.view-help-button').click();
293         e.preventDefault();
294       }
295     },
296
297     beginFind: function() {
298       if (this.findInProgress_)
299         return;
300       this.findInProgress_ = true;
301       var dlg = tracing.FindControl();
302       dlg.controller = new tracing.FindController();
303       dlg.controller.trackView = this.trackView;
304       dlg.visible = true;
305       dlg.addEventListener('close', function() {
306         this.findInProgress_ = false;
307       }.bind(this));
308       dlg.addEventListener('findNext', function() {
309       });
310       dlg.addEventListener('findPrevious', function() {
311       });
312     },
313
314     onSelectionChanged_: function(e) {
315       var oldScrollTop = this.trackViewContainer_.scrollTop;
316
317       var selection = this.trackView_ ?
318           this.trackView_.selectionOfInterest :
319           new tracing.Selection();
320       this.analysisEl_.selection = selection;
321       this.trackViewContainer_.scrollTop = oldScrollTop;
322     },
323
324     onRequestSelectionChange_: function(e) {
325       this.trackView_.selection = e.selection;
326       e.stopPropagation();
327     }
328   };
329
330   return {
331     TimelineView: TimelineView
332   };
333 });