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