// Copyright (c) 2012 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. 'use strict'; /** * @fileoverview ProfilingView glues the View control to * TracingController. */ base.requireStylesheet('about_tracing.profiling_view'); base.require('about_tracing.tracing_controller'); base.require('tracing.timeline_view'); base.require('tracing.record_selection_dialog'); base.require('ui'); base.require('ui.info_bar'); base.require('ui.overlay'); /* * Here is where we bring in modules that are used in about:tracing UI only. */ base.require('tracing.importer'); base.require('cc'); base.require('tcmalloc'); base.exportTo('about_tracing', function() { /** * ProfilingView * @constructor * @extends {HTMLDivElement} */ var ProfilingView = ui.define('div'); ProfilingView.prototype = { __proto__: HTMLDivElement.prototype, decorate: function() { this.classList.add('profiling-view'); // make the <list>/add/save/record element this.recordBn_ = document.createElement('button'); this.recordBn_.className = 'record'; this.recordBn_.textContent = 'Record'; this.recordBn_.addEventListener('click', this.onSelectCategories_.bind(this)); this.saveBn_ = document.createElement('button'); this.saveBn_.className = 'save'; this.saveBn_.textContent = 'Save'; this.saveBn_.addEventListener('click', this.onSave_.bind(this)); this.loadBn_ = document.createElement('button'); this.loadBn_.textContent = 'Load'; this.loadBn_.addEventListener('click', this.onLoad_.bind(this)); this.infoBar_ = new ui.InfoBar(); this.infoBar_.visible = false; this.appendChild(this.infoBar_); this.timelineView_ = new tracing.TimelineView(); this.timelineView_.leftControls.appendChild(this.recordBn_); this.timelineView_.leftControls.appendChild(this.saveBn_); this.timelineView_.leftControls.appendChild(this.loadBn_); this.appendChild(this.timelineView_); this.onKeypress_ = this.onKeypress_.bind(this); document.addEventListener('keypress', this.onKeypress_); this.onCategoriesCollected_ = this.onCategoriesCollected_.bind(this); this.onTraceEnded_ = this.onTraceEnded_.bind(this); this.dropHandler_ = this.dropHandler_.bind(this); this.ignoreHandler_ = this.ignoreHandler_.bind(this); document.addEventListener('dragstart', this.ignoreHandler_, false); document.addEventListener('dragend', this.ignoreHandler_, false); document.addEventListener('dragenter', this.ignoreHandler_, false); document.addEventListener('dragleave', this.ignoreHandler_, false); document.addEventListener('dragover', this.ignoreHandler_, false); document.addEventListener('drop', this.dropHandler_, false); this.selectingCategories = false; this.addEventListener('tracingControllerChange', this.refresh_.bind(this), true); }, // Detach all document event listeners. Without this the tests can get // confused as the element may still be listening when the next test runs. detach_: function() { document.removeEventListener('keypress', this.onKeypress_); document.removeEventListener('dragstart', this.ignoreHandler_); document.removeEventListener('dragend', this.ignoreHandler_); document.removeEventListener('dragenter', this.ignoreHandler_); document.removeEventListener('dragleave', this.ignoreHandler_); document.removeEventListener('dragover', this.ignoreHandler_); document.removeEventListener('drop', this.dropHandler_); }, refresh_: function() { if (!this.tracingController) return; this.saveBn_.disabled = true; if (!this.tracingController.traceEventData) { this.infoBar_.visible = false; return; } this.saveBn_.disabled = false; var traces = [this.tracingController.traceEventData]; if (this.tracingController.systemTraceEvents) traces.push(this.tracingController.systemTraceEvents); var m = new tracing.TraceModel(); try { m.importTraces(traces, true); } catch (e) { this.timelineView_.model = undefined; this.infoBar_.message = 'There was an error while importing the traceData: ' + base.normalizeException(e).message; this.infoBar_.visible = true; return; } this.infoBar_.visible = false; this.timelineView_.model = m; }, onKeypress_: function(event) { if (event.keyCode === 114 && // r !this.tracingController.isTracingEnabled && !this.selectingCategories && document.activeElement.nodeName !== 'INPUT') { this.onSelectCategories_(); } }, get selectingCategories() { return this.selectingCategories_; }, set selectingCategories(val) { this.selectingCategories_ = val; }, get timelineView() { return this.timelineView_; }, get tracingController() { return this.tracingController_; }, set tracingController(newValue) { if (this.tracingController_) throw new Error('Can only set tracing controller once.'); base.setPropertyAndDispatchChange(this, 'tracingController', newValue); }, /////////////////////////////////////////////////////////////////////////// onSelectCategories_: function() { this.selectingCategories = true; var tc = this.tracingController; tc.collectCategories(); tc.addEventListener('categoriesCollected', this.onCategoriesCollected_); }, onCategoriesCollected_: function(event) { var tc = this.tracingController; var categories = event.categories; var categories_length = categories.length; // Do not allow categories with ,'s in their name. for (var i = 0; i < categories_length; ++i) { var split = categories[i].split(','); categories[i] = split.shift(); if (split.length > 0) categories = categories.concat(split); } var dlg = new tracing.RecordSelectionDialog(); dlg.categories = categories; dlg.settings = this.timelineView_.settings; dlg.settings_key = 'record_categories'; dlg.recordCallback = this.onRecord_.bind(this); dlg.showSystemTracing = this.tracingController.supportsSystemTracing; dlg.visible = true; dlg.addEventListener('visibleChange', function(ev) { if (!dlg.visible) this.selectingCategories = false; }.bind(this)); this.recordSelectionDialog_ = dlg; setTimeout(function() { tc.removeEventListener('categoriesCollected', this.onCategoriesCollected_); }, 0); }, onRecord_: function() { this.selectingCategories = false; var tc = this.tracingController; var categories = this.recordSelectionDialog_.categoryFilter(); console.log('Recording: ' + categories); this.timelineView_.viewTitle = '-_-'; tc.beginTracing(this.recordSelectionDialog_.isSystemTracingEnabled(), this.recordSelectionDialog_.isContinuousTracingEnabled(), this.recordSelectionDialog_.isSamplingEnabled(), categories); tc.addEventListener('traceEnded', this.onTraceEnded_); }, onTraceEnded_: function() { var tc = this.tracingController; this.timelineView_.viewTitle = '^_^'; this.refresh_(); setTimeout(function() { tc.removeEventListener('traceEnded', this.onTraceEnded_); }, 0); }, /////////////////////////////////////////////////////////////////////////// onSave_: function() { this.overlayEl_ = new ui.Overlay(); this.overlayEl_.className = 'profiling-overlay'; var labelEl = document.createElement('div'); labelEl.className = 'label'; labelEl.textContent = 'Saving...'; this.overlayEl_.appendChild(labelEl); this.overlayEl_.visible = true; var that = this; var tc = this.tracingController; function response() { that.overlayEl_.visible = false; that.overlayEl_ = undefined; setTimeout(function() { tc.removeEventListener('saveTraceFileComplete', response); tc.removeEventListener('saveTraceFileCanceled', response); }, 0); } tc.addEventListener('saveTraceFileComplete', response); tc.addEventListener('saveTraceFileCanceled', response); tc.beginSaveTraceFile(); }, /////////////////////////////////////////////////////////////////////////// onLoad_: function() { this.overlayEl_ = new ui.Overlay(); this.overlayEl_.className = 'profiling-overlay'; var labelEl = document.createElement('div'); labelEl.className = 'label'; labelEl.textContent = 'Loading...'; this.overlayEl_.appendChild(labelEl); this.overlayEl_.visible = true; var that = this; var tc = this.tracingController; this.tracingController.beginLoadTraceFile(); function response(e) { that.overlayEl_.visible = false; that.overlayEl_ = undefined; if (e.type === 'loadTraceFileComplete') { var nameParts = e.filename.split(/\//); if (nameParts.length > 0) that.timelineView_.viewTitle = nameParts[nameParts.length - 1]; else that.timelineView_.viewTitle = '^_^'; that.refresh_(); } setTimeout(function() { tc.removeEventListener('loadTraceFileComplete', response); tc.removeEventListener('loadTraceFileCanceled', response); }, 0); } tc.addEventListener('loadTraceFileComplete', response); tc.addEventListener('loadTraceFileCanceled', response); }, /////////////////////////////////////////////////////////////////////////// ignoreHandler_: function(e) { e.preventDefault(); return false; }, dropHandler_: function(e) { e.stopPropagation(); e.preventDefault(); var that = this; var files = e.dataTransfer.files; var files_len = files.length; for (var i = 0; i < files_len; ++i) { var reader = new FileReader(); var filename = files[i].name; reader.onload = function(data) { try { that.tracingController.onLoadTraceFileComplete(data.target.result, filename); that.timelineView_.viewTitle = filename; that.refresh_(); } catch (e) { console.log('Unable to import the provided trace file.', e.message); } }; reader.readAsText(files[i]); } return false; } }; return { ProfilingView: ProfilingView }; });