Home | History | Annotate | Download | only in about_tracing
      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('about_tracing.profiling_view');
     12 base.require('about_tracing.tracing_controller');
     13 base.require('tracing.timeline_view');
     14 base.require('tracing.record_selection_dialog');
     15 base.require('ui');
     16 base.require('ui.info_bar');
     17 base.require('ui.overlay');
     18 
     19 /*
     20  * Here is where we bring in modules that are used in about:tracing UI only.
     21  */
     22 base.require('tracing.importer');
     23 base.require('cc');
     24 base.require('tcmalloc');
     25 
     26 base.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