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