Home | History | Annotate | Download | only in src
      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  */
     11 base.requireStylesheet('profiling_view');
     12 base.require('timeline_view');
     13 base.require('tracing_controller');
     14 base.exportTo('tracing', function() {
     15   /**
     16    * ProfilingView
     17    * @constructor
     18    * @extends {HTMLDivElement}
     19    */
     20   var ProfilingView = tracing.ui.define('div');
     21 
     22   ProfilingView.prototype = {
     23     __proto__: HTMLDivElement.prototype,
     24 
     25     traceEvents_: [],
     26     systemTraceEvents_: [],
     27 
     28     decorate: function() {
     29       this.classList.add('profiling-view');
     30 
     31       // make the <list>/add/save/record element
     32       this.recordBn_ = document.createElement('button');
     33       this.recordBn_.className = 'record';
     34       this.recordBn_.textContent = 'Record';
     35       this.recordBn_.addEventListener('click',
     36                                       this.onSelectCategories_.bind(this));
     37 
     38       this.saveBn_ = document.createElement('button');
     39       this.saveBn_.textContent = 'Save';
     40       this.saveBn_.addEventListener('click', this.onSave_.bind(this));
     41 
     42       this.loadBn_ = document.createElement('button');
     43       this.loadBn_.textContent = 'Load';
     44       this.loadBn_.addEventListener('click', this.onLoad_.bind(this));
     45 
     46       this.systemTracingBn_ = document.createElement('input');
     47       this.systemTracingBn_.type = 'checkbox';
     48       this.systemTracingBn_.checked = false;
     49 
     50       this.continuousTracingBn_ = document.createElement('input');
     51       this.continuousTracingBn_.type = 'checkbox';
     52       this.continuousTracingBn_.checked = true;
     53 
     54       this.systemTracingLabelEl_ = document.createElement('label');
     55       this.systemTracingLabelEl_.textContent = 'System events';
     56       this.systemTracingLabelEl_.appendChild(this.systemTracingBn_);
     57       this.systemTracingLabelEl_.style.display = 'none';
     58       this.systemTracingLabelEl_.style.marginLeft = '16px';
     59 
     60       this.continuousTracingLabelEl_ = document.createElement('label');
     61       this.continuousTracingLabelEl_.textContent = 'Continuous tracing';
     62       this.continuousTracingLabelEl_.appendChild(this.continuousTracingBn_);
     63       this.continuousTracingLabelEl_.style.marginLeft = '16px';
     64 
     65       this.timelineView_ = new tracing.TimelineView();
     66       this.timelineView_.leftControls.appendChild(this.recordBn_);
     67       this.timelineView_.leftControls.appendChild(this.saveBn_);
     68       this.timelineView_.leftControls.appendChild(this.loadBn_);
     69       this.timelineView_.leftControls.appendChild(this.systemTracingLabelEl_);
     70       this.timelineView_.leftControls.appendChild(
     71           this.continuousTracingLabelEl_);
     72 
     73       this.appendChild(this.timelineView_);
     74 
     75       document.addEventListener('keypress', this.onKeypress_.bind(this));
     76 
     77       this.onCategoriesCollectedBoundToThis_ =
     78         this.onCategoriesCollected_.bind(this);
     79       this.onTraceEndedBoundToThis_ = this.onTraceEnded_.bind(this);
     80 
     81       this.refresh_();
     82     },
     83 
     84     didSetTracingController_: function(value, oldValue) {
     85       if (oldValue)
     86         throw new Error('Can only set tracing controller once.');
     87 
     88       if (this.tracingController_.supportsSystemTracing) {
     89         this.systemTracingLabelEl_.style.display = 'block';
     90         this.systemTracingBn_.checked = true;
     91       } else {
     92         this.systemTracingLabelEl_.style.display = 'none';
     93       }
     94 
     95       this.refresh_();
     96     },
     97 
     98     refresh_: function() {
     99       if (!this.tracingController_)
    100         return;
    101 
    102       var traceEvents = this.tracingController_.traceEvents;
    103       var hasEvents = traceEvents && traceEvents.length;
    104 
    105       this.saveBn_.disabled = !hasEvents;
    106 
    107       if (!hasEvents) return;
    108 
    109       var traces = [traceEvents];
    110       if (this.tracingController_.systemTraceEvents.length)
    111         traces.push(this.tracingController_.systemTraceEvents);
    112 
    113       var m = new tracing.Model();
    114       m.importTraces(traces, true);
    115       this.timelineView_.model = m;
    116     },
    117 
    118     onKeypress_: function(event) {
    119       if (event.keyCode === 114 &&  // r
    120           !this.tracingController_.isTracingEnabled &&
    121           document.activeElement.nodeName !== 'INPUT') {
    122         this.onSelectCategories_();
    123       }
    124     },
    125 
    126     get timelineView() {
    127       return this.timelineView_;
    128     },
    129 
    130     ///////////////////////////////////////////////////////////////////////////
    131 
    132     onSelectCategories_: function() {
    133       var tc = this.tracingController_;
    134       tc.collectCategories();
    135       tc.addEventListener('categoriesCollected',
    136                           this.onCategoriesCollectedBoundToThis_);
    137     },
    138 
    139     onCategoriesCollected_: function(event) {
    140       var tc = this.tracingController_;
    141 
    142       var buttonEl = document.createElement('button');
    143       buttonEl.innerText = 'Record';
    144       buttonEl.className = 'record-categories';
    145       buttonEl.onclick = this.onRecord_.bind(this);
    146 
    147       var categories = event.categories;
    148       var categories_length = categories.length;
    149       // Do not allow categories with ,'s in their name.
    150       for (var i = 0; i < categories_length; ++i) {
    151         var split = categories[i].split(',');
    152         categories[i] = split.shift();
    153         if (split.length > 0)
    154           categories = categories.concat(split);
    155       }
    156 
    157       var dlg = new tracing.CategoryFilterDialog();
    158       dlg.categories = categories;
    159       dlg.settings = this.timelineView_.settings;
    160       dlg.settings_key = 'record_categories';
    161       dlg.appendChild(buttonEl);
    162       dlg.visible = true;
    163       this.categorySelectionDialog_ = dlg;
    164 
    165       buttonEl.focus();
    166 
    167       setTimeout(function() {
    168         tc.removeEventListener('categoriesCollected',
    169                                this.onCategoriesCollectedBoundToThis_);
    170       }, 0);
    171     },
    172 
    173     onRecord_: function() {
    174       var tc = this.tracingController_;
    175       this.categorySelectionDialog_.visible = false;
    176 
    177       var categories = this.categorySelectionDialog_.unselectedCategories();
    178       var categories_length = categories.length;
    179 
    180       var negated_categories = [];
    181       for (var i = 0; i < categories_length; ++i) {
    182         // Skip any category with a , as it will cause issues when we negate.
    183         // Both sides should have been added as separate categories, these can
    184         // only come from settings.
    185         if (categories[i].match(/,/))
    186           continue;
    187         negated_categories.push('-' + categories[i]);
    188       }
    189       categories = negated_categories.join(',');
    190 
    191       tc.beginTracing(this.systemTracingBn_.checked,
    192                       this.continuousTracingBn_.checked,
    193                       categories);
    194 
    195       tc.addEventListener('traceEnded', this.onTraceEndedBoundToThis_);
    196     },
    197 
    198     onTraceEnded_: function() {
    199       var tc = this.tracingController_;
    200       this.refresh_();
    201       setTimeout(function() {
    202         tc.removeEventListener('traceEnded', this.onTraceEndedBoundToThis_);
    203       }, 0);
    204     },
    205 
    206     ///////////////////////////////////////////////////////////////////////////
    207 
    208     onSave_: function() {
    209       this.overlayEl_ = new tracing.ui.Overlay();
    210       this.overlayEl_.className = 'profiling-overlay';
    211 
    212       var labelEl = document.createElement('div');
    213       labelEl.className = 'label';
    214       labelEl.textContent = 'Saving...';
    215       this.overlayEl_.appendChild(labelEl);
    216       this.overlayEl_.visible = true;
    217 
    218       var that = this;
    219       var tc = this.tracingController_;
    220       function response() {
    221         that.overlayEl_.visible = false;
    222         that.overlayEl_ = undefined;
    223         setTimeout(function() {
    224           tc.removeEventListener('saveTraceFileComplete', response);
    225           tc.removeEventListener('saveTraceFileCanceled', response);
    226         }, 0);
    227       }
    228       tc.addEventListener('saveTraceFileComplete', response);
    229       tc.addEventListener('saveTraceFileCanceled', response);
    230       tc.beginSaveTraceFile();
    231     },
    232 
    233     ///////////////////////////////////////////////////////////////////////////
    234 
    235     onLoad_: function() {
    236       this.overlayEl_ = new tracing.ui.Overlay();
    237       this.overlayEl_.className = 'profiling-overlay';
    238 
    239       var labelEl = document.createElement('div');
    240       labelEl.className = 'label';
    241       labelEl.textContent = 'Loading...';
    242       this.overlayEl_.appendChild(labelEl);
    243       this.overlayEl_.visible = true;
    244 
    245       var that = this;
    246       var tc = this.tracingController_;
    247       this.tracingController_.beginLoadTraceFile();
    248       function response(e) {
    249         that.overlayEl_.visible = false;
    250         that.overlayEl_ = undefined;
    251         if (e.type == 'loadTraceFileComplete')
    252           that.refresh_();
    253         setTimeout(function() {
    254           tc.removeEventListener('loadTraceFileComplete', response);
    255           tc.removeEventListener('loadTraceFileCanceled', response);
    256         }, 0);
    257       }
    258 
    259       tc.addEventListener('loadTraceFileComplete', response);
    260       tc.addEventListener('loadTraceFileCanceled', response);
    261     }
    262   };
    263 
    264   base.defineProperty(ProfilingView, 'tracingController', base.PropertyKind.JS,
    265       ProfilingView.prototype.didSetTracingController_);
    266 
    267   return {
    268     ProfilingView: ProfilingView
    269   };
    270 });
    271