Upstream version 5.34.104.0
[platform/framework/web/crosswalk.git] / src / third_party / trace-viewer / src / 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
27 tvcm.exportTo('tracing', function() {
28
29   /**
30    * View
31    * @constructor
32    * @extends {HTMLUnknownElement}
33    */
34   var TimelineView = tvcm.ui.define('x-timeline-view');
35
36   TimelineView.prototype = {
37     __proto__: HTMLUnknownElement.prototype,
38
39     decorate: function() {
40       var node = tvcm.instantiateTemplate('#timeline-view-template');
41       this.appendChild(node);
42
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');
47
48       this.findCtl_ = new tracing.FindControl();
49       this.findCtl_.controller = new tracing.FindController();
50
51       this.rightControls.appendChild(this.createMetadataButton_());
52       this.rightControls.appendChild(this.findCtl_);
53       this.rightControls.appendChild(this.createHelpButton_());
54
55       this.dragEl_ = new tvcm.ui.DragHandle();
56       this.appendChild(this.dragEl_);
57
58       this.analysisEl_ = new tracing.analysis.AnalysisView();
59       this.appendChild(this.analysisEl_);
60       this.addEventListener('requestSelectionChange',
61                             this.onRequestSelectionChange_.bind(this));
62
63       // Bookkeeping.
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);
67
68       this.dragEl_.target = this.analysisEl_;
69     },
70
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');
75
76       var dlg = new tvcm.ui.Overlay();
77       dlg.title = 'chrome://tracing Help';
78       dlg.classList.add('view-help-overlay');
79       dlg.appendChild(node);
80
81       function onClick(e) {
82         dlg.visible = !dlg.visible;
83
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;
88         }
89
90         // Stop event so it doesn't trigger new click listener on document.
91         e.stopPropagation();
92         return false;
93       }
94       showEl.addEventListener('click', onClick.bind(this));
95
96       return showEl;
97     },
98
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');
103
104       var dlg = new tvcm.ui.Overlay();
105       dlg.title = 'Metadata for trace';
106       dlg.classList.add('view-metadata-overlay');
107       dlg.appendChild(node);
108
109       function onClick(e) {
110         dlg.visible = true;
111
112         var metadataStrings = [];
113
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, ' ');
119
120           metadataStrings.push(name + ': ' + value);
121         }
122         textEl.textContent = metadataStrings.join('\n');
123
124         e.stopPropagation();
125         return false;
126       }
127       showEl.addEventListener('click', onClick.bind(this));
128
129       function updateVisibility() {
130         showEl.style.display =
131             (this.model && this.model.metadata.length) ? '' : 'none';
132       }
133       var updateVisibility_ = updateVisibility.bind(this);
134       updateVisibility_();
135       this.addEventListener('modelChange', updateVisibility_);
136
137       return showEl;
138     },
139
140     get leftControls() {
141       return this.leftControlsEl_;
142     },
143
144     get rightControls() {
145       return this.rightControlsEl_;
146     },
147
148     get viewTitle() {
149       return this.titleEl_.textContent.substring(
150           this.titleEl_.textContent.length - 2);
151     },
152
153     set viewTitle(text) {
154       if (text === undefined) {
155         this.titleEl_.textContent = '';
156         this.titleEl_.hidden = true;
157         return;
158       }
159       this.titleEl_.hidden = false;
160       this.titleEl_.textContent = text;
161     },
162
163     get model() {
164       if (this.timeline_)
165         return this.timeline_.model;
166       return undefined;
167     },
168
169     set model(model) {
170       var modelInstanceChanged = model != this.model;
171       var modelValid = model && !model.bounds.isEmpty;
172
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;
182         }
183       }
184
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();
195       }
196
197       // Set the model.
198       if (modelValid)
199         this.timeline_.model = model;
200       tvcm.dispatchSimpleEvent(this, 'modelChange');
201
202       // Do things that are selection specific
203       if (modelInstanceChanged)
204         this.onSelectionChanged_();
205     },
206
207     get timeline() {
208       return this.timeline_;
209     },
210
211     get settings() {
212       if (!this.settings_)
213         this.settings_ = new tvcm.Settings();
214       return this.settings_;
215     },
216
217     /**
218      * Sets the element whose focus state will determine whether
219      * to respond to keybaord input.
220      */
221     set focusElement(value) {
222       this.focusElement_ = value;
223       if (this.timeline_)
224         this.timeline_.focusElement = value;
225     },
226
227     /**
228      * @return {Element} The element whose focused state determines
229      * whether to respond to keyboard inputs.
230      * Defaults to the parent element.
231      */
232     get focusElement() {
233       if (this.focusElement_)
234         return this.focusElement_;
235       return this.parentElement;
236     },
237
238     /**
239      * @return {boolean} Whether the current timeline is attached to the
240      * document.
241      */
242     get isAttachedToDocument_() {
243       var cur = this;
244       while (cur.parentNode)
245         cur = cur.parentNode;
246       return cur == this.ownerDocument;
247     },
248
249     get listenToKeys_() {
250       if (!this.isAttachedToDocument_)
251         return;
252       if (!this.focusElement_)
253         return true;
254       if (this.focusElement.tabIndex >= 0)
255         return document.activeElement == this.focusElement;
256       return true;
257     },
258
259     onKeyDown_: function(e) {
260       if (!this.listenToKeys_)
261         return;
262
263       if (e.keyCode === 27) { // ESC
264         this.focus();
265         e.preventDefault();
266       }
267     },
268
269     onKeypress_: function(e) {
270       if (!this.listenToKeys_)
271         return;
272
273       if (e.keyCode === '/'.charCodeAt(0)) {
274         if (this.findCtl_.hasFocus())
275           this.focus();
276         else
277           this.findCtl_.focus();
278         e.preventDefault();
279       } else if (e.keyCode === '?'.charCodeAt(0)) {
280         this.querySelector('.view-help-button').click();
281         e.preventDefault();
282       }
283     },
284
285     beginFind: function() {
286       if (this.findInProgress_)
287         return;
288       this.findInProgress_ = true;
289       var dlg = tracing.FindControl();
290       dlg.controller = new tracing.FindController();
291       dlg.controller.timeline = this.timeline;
292       dlg.visible = true;
293       dlg.addEventListener('close', function() {
294         this.findInProgress_ = false;
295       }.bind(this));
296       dlg.addEventListener('findNext', function() {
297       });
298       dlg.addEventListener('findPrevious', function() {
299       });
300     },
301
302     onSelectionChanged_: function(e) {
303       var oldScrollTop = this.timelineContainer_.scrollTop;
304
305       var selection = this.timeline_ ?
306           this.timeline_.selectionOfInterest :
307           new tracing.Selection();
308       this.analysisEl_.selection = selection;
309       this.timelineContainer_.scrollTop = oldScrollTop;
310     },
311
312     onRequestSelectionChange_: function(e) {
313       this.timeline_.selection = e.selection;
314       e.stopPropagation();
315     }
316   };
317
318   return {
319     TimelineView: TimelineView
320   };
321 });