• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1// Copyright (c) 2012 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 ProfilingView glues the View control to
9 * TracingController.
10 */
11base.requireStylesheet('about_tracing.profiling_view');
12base.require('about_tracing.tracing_controller');
13base.require('tracing.timeline_view');
14base.require('tracing.record_selection_dialog');
15base.require('ui');
16base.require('ui.info_bar');
17base.require('ui.overlay');
18
19/*
20 * Here is where we bring in modules that are used in about:tracing UI only.
21 */
22base.require('tracing.importer');
23base.require('cc');
24base.require('tcmalloc');
25
26base.exportTo('about_tracing', function() {
27  /**
28   * ProfilingView
29   * @constructor
30   * @extends {HTMLDivElement}
31   */
32  var ProfilingView = ui.define('div');
33
34  ProfilingView.prototype = {
35    __proto__: HTMLDivElement.prototype,
36
37    decorate: function() {
38      this.classList.add('profiling-view');
39
40      // make the <list>/add/save/record element
41      this.recordBn_ = document.createElement('button');
42      this.recordBn_.className = 'record';
43      this.recordBn_.textContent = 'Record';
44      this.recordBn_.addEventListener('click',
45                                      this.onSelectCategories_.bind(this));
46
47      this.saveBn_ = document.createElement('button');
48      this.saveBn_.className = 'save';
49      this.saveBn_.textContent = 'Save';
50      this.saveBn_.addEventListener('click', this.onSave_.bind(this));
51
52      this.loadBn_ = document.createElement('button');
53      this.loadBn_.textContent = 'Load';
54      this.loadBn_.addEventListener('click', this.onLoad_.bind(this));
55
56      this.infoBar_ = new ui.InfoBar();
57      this.infoBar_.visible = false;
58      this.appendChild(this.infoBar_);
59
60      this.timelineView_ = new tracing.TimelineView();
61      this.timelineView_.leftControls.appendChild(this.recordBn_);
62      this.timelineView_.leftControls.appendChild(this.saveBn_);
63      this.timelineView_.leftControls.appendChild(this.loadBn_);
64      this.appendChild(this.timelineView_);
65
66      this.onKeypress_ = this.onKeypress_.bind(this);
67      document.addEventListener('keypress', this.onKeypress_);
68
69      this.onCategoriesCollected_ = this.onCategoriesCollected_.bind(this);
70      this.onTraceEnded_ = this.onTraceEnded_.bind(this);
71
72      this.dropHandler_ = this.dropHandler_.bind(this);
73      this.ignoreHandler_ = this.ignoreHandler_.bind(this);
74      document.addEventListener('dragstart', this.ignoreHandler_, false);
75      document.addEventListener('dragend', this.ignoreHandler_, false);
76      document.addEventListener('dragenter', this.ignoreHandler_, false);
77      document.addEventListener('dragleave', this.ignoreHandler_, false);
78      document.addEventListener('dragover', this.ignoreHandler_, false);
79      document.addEventListener('drop', this.dropHandler_, false);
80
81      this.selectingCategories = false;
82
83      this.addEventListener('tracingControllerChange',
84          this.refresh_.bind(this), true);
85    },
86
87    // Detach all document event listeners. Without this the tests can get
88    // confused as the element may still be listening when the next test runs.
89    detach_: function() {
90      document.removeEventListener('keypress', this.onKeypress_);
91      document.removeEventListener('dragstart', this.ignoreHandler_);
92      document.removeEventListener('dragend', this.ignoreHandler_);
93      document.removeEventListener('dragenter', this.ignoreHandler_);
94      document.removeEventListener('dragleave', this.ignoreHandler_);
95      document.removeEventListener('dragover', this.ignoreHandler_);
96      document.removeEventListener('drop', this.dropHandler_);
97    },
98
99    refresh_: function() {
100      if (!this.tracingController)
101        return;
102
103      this.saveBn_.disabled = true;
104
105      if (!this.tracingController.traceEventData) {
106        this.infoBar_.visible = false;
107        return;
108      }
109      this.saveBn_.disabled = false;
110
111      var traces = [this.tracingController.traceEventData];
112
113      if (this.tracingController.systemTraceEvents)
114        traces.push(this.tracingController.systemTraceEvents);
115
116      var m = new tracing.TraceModel();
117      try {
118        m.importTraces(traces, true);
119      } catch (e) {
120        this.timelineView_.model = undefined;
121        this.infoBar_.message =
122            'There was an error while importing the traceData: ' +
123            base.normalizeException(e).message;
124        this.infoBar_.visible = true;
125        return;
126      }
127      this.infoBar_.visible = false;
128      this.timelineView_.model = m;
129    },
130
131    onKeypress_: function(event) {
132      if (event.keyCode === 114 &&  // r
133          !this.tracingController.isTracingEnabled &&
134          !this.selectingCategories &&
135          document.activeElement.nodeName !== 'INPUT') {
136        this.onSelectCategories_();
137      }
138    },
139
140    get selectingCategories() {
141      return this.selectingCategories_;
142    },
143
144    set selectingCategories(val) {
145      this.selectingCategories_ = val;
146    },
147
148    get timelineView() {
149      return this.timelineView_;
150    },
151
152    get tracingController() {
153      return this.tracingController_;
154    },
155
156    set tracingController(newValue) {
157      if (this.tracingController_)
158        throw new Error('Can only set tracing controller once.');
159      base.setPropertyAndDispatchChange(this, 'tracingController', newValue);
160    },
161
162    ///////////////////////////////////////////////////////////////////////////
163
164    onSelectCategories_: function() {
165      this.selectingCategories = true;
166      var tc = this.tracingController;
167      tc.collectCategories();
168      tc.addEventListener('categoriesCollected', this.onCategoriesCollected_);
169    },
170
171    onCategoriesCollected_: function(event) {
172      var tc = this.tracingController;
173
174      var categories = event.categories;
175      var categories_length = categories.length;
176      // Do not allow categories with ,'s in their name.
177      for (var i = 0; i < categories_length; ++i) {
178        var split = categories[i].split(',');
179        categories[i] = split.shift();
180        if (split.length > 0)
181          categories = categories.concat(split);
182      }
183
184      var dlg = new tracing.RecordSelectionDialog();
185      dlg.categories = categories;
186      dlg.settings = this.timelineView_.settings;
187      dlg.settings_key = 'record_categories';
188      dlg.recordCallback = this.onRecord_.bind(this);
189      dlg.showSystemTracing = this.tracingController.supportsSystemTracing;
190      dlg.visible = true;
191      dlg.addEventListener('visibleChange', function(ev) {
192        if (!dlg.visible)
193          this.selectingCategories = false;
194      }.bind(this));
195      this.recordSelectionDialog_ = dlg;
196
197      setTimeout(function() {
198        tc.removeEventListener('categoriesCollected',
199                               this.onCategoriesCollected_);
200      }, 0);
201    },
202
203    onRecord_: function() {
204      this.selectingCategories = false;
205
206      var tc = this.tracingController;
207
208      var categories = this.recordSelectionDialog_.categoryFilter();
209      console.log('Recording: ' + categories);
210
211      this.timelineView_.viewTitle = '-_-';
212      tc.beginTracing(this.recordSelectionDialog_.isSystemTracingEnabled(),
213                      this.recordSelectionDialog_.isContinuousTracingEnabled(),
214                      this.recordSelectionDialog_.isSamplingEnabled(),
215                      categories);
216
217      tc.addEventListener('traceEnded', this.onTraceEnded_);
218    },
219
220    onTraceEnded_: function() {
221      var tc = this.tracingController;
222      this.timelineView_.viewTitle = '^_^';
223      this.refresh_();
224      setTimeout(function() {
225        tc.removeEventListener('traceEnded', this.onTraceEnded_);
226      }, 0);
227    },
228
229    ///////////////////////////////////////////////////////////////////////////
230
231    onSave_: function() {
232      this.overlayEl_ = new ui.Overlay();
233      this.overlayEl_.className = 'profiling-overlay';
234
235      var labelEl = document.createElement('div');
236      labelEl.className = 'label';
237      labelEl.textContent = 'Saving...';
238      this.overlayEl_.appendChild(labelEl);
239      this.overlayEl_.visible = true;
240
241      var that = this;
242      var tc = this.tracingController;
243      function response() {
244        that.overlayEl_.visible = false;
245        that.overlayEl_ = undefined;
246        setTimeout(function() {
247          tc.removeEventListener('saveTraceFileComplete', response);
248          tc.removeEventListener('saveTraceFileCanceled', response);
249        }, 0);
250      }
251      tc.addEventListener('saveTraceFileComplete', response);
252      tc.addEventListener('saveTraceFileCanceled', response);
253      tc.beginSaveTraceFile();
254    },
255
256    ///////////////////////////////////////////////////////////////////////////
257
258    onLoad_: function() {
259      this.overlayEl_ = new ui.Overlay();
260      this.overlayEl_.className = 'profiling-overlay';
261
262      var labelEl = document.createElement('div');
263      labelEl.className = 'label';
264      labelEl.textContent = 'Loading...';
265      this.overlayEl_.appendChild(labelEl);
266      this.overlayEl_.visible = true;
267
268      var that = this;
269      var tc = this.tracingController;
270      this.tracingController.beginLoadTraceFile();
271      function response(e) {
272        that.overlayEl_.visible = false;
273        that.overlayEl_ = undefined;
274        if (e.type === 'loadTraceFileComplete') {
275          var nameParts = e.filename.split(/\//);
276          if (nameParts.length > 0)
277            that.timelineView_.viewTitle = nameParts[nameParts.length - 1];
278          else
279            that.timelineView_.viewTitle = '^_^';
280          that.refresh_();
281        }
282
283        setTimeout(function() {
284          tc.removeEventListener('loadTraceFileComplete', response);
285          tc.removeEventListener('loadTraceFileCanceled', response);
286        }, 0);
287      }
288
289      tc.addEventListener('loadTraceFileComplete', response);
290      tc.addEventListener('loadTraceFileCanceled', response);
291    },
292
293    ///////////////////////////////////////////////////////////////////////////
294
295    ignoreHandler_: function(e) {
296      e.preventDefault();
297      return false;
298    },
299
300    dropHandler_: function(e) {
301      e.stopPropagation();
302      e.preventDefault();
303
304      var that = this;
305      var files = e.dataTransfer.files;
306      var files_len = files.length;
307      for (var i = 0; i < files_len; ++i) {
308        var reader = new FileReader();
309        var filename = files[i].name;
310        reader.onload = function(data) {
311          try {
312            that.tracingController.onLoadTraceFileComplete(data.target.result,
313                                                           filename);
314            that.timelineView_.viewTitle = filename;
315            that.refresh_();
316          } catch (e) {
317            console.log('Unable to import the provided trace file.', e.message);
318          }
319        };
320        reader.readAsText(files[i]);
321      }
322      return false;
323    }
324  };
325
326  return {
327    ProfilingView: ProfilingView
328  };
329});
330