| <!DOCTYPE html> |
| <!-- |
| Copyright (c) 2013 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. |
| --> |
| <link rel="import" href="/core/filter.html"> |
| <link rel="import" href="/base/utils.html"> |
| <link rel="import" href="/base/ui/overlay.html"> |
| <link rel="import" href="/base/ui/dom_helpers.html"> |
| <link rel="import" href="/base/ui/info_bar_group.html"> |
| |
| <template id="record-selection-dialog-template"> |
| <style> |
| .categories-column-view { |
| display: -webkit-flex; |
| -webkit-flex-direction: column; |
| font-family: sans-serif; |
| max-width: 640px; |
| min-height: 0; |
| min-width: 0; |
| opacity: 1; |
| transition: max-height 1s ease, max-width 1s ease, opacity 1s ease; |
| will-change: opacity; |
| } |
| |
| .categories-column-view-hidden { |
| max-height: 0; |
| max-width: 0; |
| opacity: 0; |
| overflow: hidden; |
| } |
| |
| .categories-selection { |
| display: -webkit-flex; |
| -webkit-flex-direction: row; |
| } |
| |
| .category-presets { |
| padding: 4px; |
| } |
| |
| .category-description { |
| color: #aaa; |
| font-size: small; |
| max-height: 1em; |
| opacity: 1; |
| padding-left: 4px; |
| padding-right: 4px; |
| text-align: right; |
| transition: max-height 1s ease, opacity 1s ease; |
| will-change: opacity; |
| } |
| |
| .category-description-hidden { |
| max-height: 0; |
| opacity: 0; |
| } |
| |
| .default-enabled-categories, |
| .default-disabled-categories { |
| -webkit-flex: 1 1 auto; |
| display: -webkit-flex; |
| -webkit-flex-direction: column; |
| padding: 4px; |
| width: 300px; |
| } |
| |
| .default-enabled-categories > div, |
| .default-disabled-categories > div { |
| padding: 4px; |
| } |
| |
| .tracing-modes { |
| -webkit-flex: 1 0 auto; |
| display: -webkit-flex; |
| -webkit-flex-direction: reverse; |
| padding: 4px; |
| border-bottom: 2px solid #ddd; |
| border-top: 2px solid #ddd; |
| } |
| |
| .default-disabled-categories { |
| border-left: 2px solid #ddd; |
| } |
| |
| .warning-default-disabled-categories { |
| display: inline-block; |
| font-weight: bold; |
| text-align: center; |
| color: #BD2E2E; |
| width: 2.0ex; |
| height: 2.0ex; |
| border-radius: 2.0ex; |
| border: 1px solid #BD2E2E; |
| } |
| |
| .categories { |
| font-size: 80%; |
| padding: 10px; |
| -webkit-flex: 1 1 auto; |
| } |
| |
| .group-selectors { |
| font-size: 80%; |
| border-bottom: 1px solid #ddd; |
| padding-bottom: 6px; |
| -webkit-flex: 0 0 auto; |
| } |
| |
| .group-selectors button { |
| padding: 1px; |
| } |
| </style> |
| |
| <div class="record-selection-dialog"> |
| <tr-b-ui-info-bar-group></tr-b-ui-info-bar-group> |
| <div class="category-presets"> |
| </div> |
| <div class="category-description"></div> |
| <div class="categories-column-view"> |
| <div class="tracing-modes"></div> |
| <div class="categories-selection"> |
| <div class="default-enabled-categories"> |
| <div>Record Categories</div> |
| <div class="group-selectors"> |
| Select |
| <button class="all-btn">All</button> |
| <button class="none-btn">None</button> |
| </div> |
| <div class="categories"></div> |
| </div> |
| <div class="default-disabled-categories"> |
| <div>Disabled by Default Categories |
| <a class="warning-default-disabled-categories">!</a> |
| </div> |
| <div class="group-selectors"> |
| Select |
| <button class="all-btn">All</button> |
| <button class="none-btn">None</button> |
| </div> |
| <div class="categories"></div> |
| </div> |
| </div> |
| </div> |
| </div> |
| </template> |
| |
| <script> |
| 'use strict'; |
| |
| /** |
| * @fileoverview RecordSelectionDialog presents the available categories |
| * to be enabled/disabled during tr.c. |
| */ |
| tr.exportTo('tr.e.about_tracing', function() { |
| var THIS_DOC = document.currentScript.ownerDocument; |
| var RecordSelectionDialog = tr.b.ui.define('div'); |
| |
| var DEFAULT_PRESETS = [ |
| {title: 'Web developer', |
| categoryFilter: ['blink', 'cc', 'netlog', 'renderer.scheduler', |
| 'toplevel', 'v8']}, |
| {title: 'Input latency', |
| categoryFilter: ['benchmark', 'input', 'evdev', 'renderer.scheduler', |
| 'toplevel']}, |
| {title: 'Rendering', |
| categoryFilter: ['blink', 'cc', 'gpu', 'toplevel']}, |
| {title: 'Javascript and rendering', |
| categoryFilter: ['blink', 'cc', 'gpu', 'renderer.scheduler', 'v8', |
| 'toplevel']}, |
| {title: 'Frame Viewer', |
| categoryFilter: ['blink', 'cc', 'gpu', 'renderer.scheduler', 'v8', |
| 'toplevel', |
| 'disabled-by-default-cc.debug', |
| 'disabled-by-default-cc.debug.picture']}, |
| {title: 'Manually select settings', |
| categoryFilter: []} |
| ]; |
| var RECORDING_MODES = [ |
| {'label': 'Record until full', |
| 'value': 'record-until-full'}, |
| {'label': 'Record continuously', |
| 'value': 'record-continuously'}, |
| {'label': 'Record as much as possible', |
| 'value': 'record-as-much-as-possible'}]; |
| var DEFAULT_RECORD_MODE = 'record-until-full'; |
| var DEFAULT_CONTINUOUS_TRACING = true; |
| var DEFAULT_SYSTEM_TRACING = false; |
| var DEFAULT_SAMPLING_TRACING = false; |
| |
| RecordSelectionDialog.prototype = { |
| __proto__: tr.b.ui.Overlay.prototype, |
| |
| decorate: function() { |
| tr.b.ui.Overlay.prototype.decorate.call(this); |
| this.title = 'Record a new trace...'; |
| |
| this.classList.add('record-dialog-overlay'); |
| |
| var node = tr.b.instantiateTemplate('#record-selection-dialog-template', |
| THIS_DOC); |
| this.appendChild(node); |
| |
| this.recordButtonEl_ = document.createElement('button'); |
| this.recordButtonEl_.textContent = 'Record'; |
| this.recordButtonEl_.addEventListener( |
| 'click', |
| this.onRecordButtonClicked_.bind(this)); |
| this.recordButtonEl_.style.fontSize = '110%'; |
| this.buttons.appendChild(this.recordButtonEl_); |
| |
| this.categoriesView_ = this.querySelector('.categories-column-view'); |
| this.presetsEl_ = this.querySelector('.category-presets'); |
| this.presetsEl_.appendChild(tr.b.ui.createOptionGroup( |
| this, 'currentlyChosenPreset', |
| 'about_tracing.record_selection_dialog_preset', |
| DEFAULT_PRESETS[0].categoryFilter, |
| DEFAULT_PRESETS.map(function(p) { |
| return { label: p.title, value: p.categoryFilter }; |
| }))); |
| |
| this.tracingRecordModeSltr_ = tr.b.ui.createSelector( |
| this, 'tracingRecordMode', |
| 'recordSelectionDialog.tracingRecordMode', |
| DEFAULT_RECORD_MODE, RECORDING_MODES); |
| |
| this.systemTracingBn_ = tr.b.ui.createCheckBox( |
| undefined, undefined, |
| 'recordSelectionDialog.useSystemTracing', true, |
| 'System tracing'); |
| this.samplingTracingBn_ = tr.b.ui.createCheckBox( |
| undefined, undefined, |
| 'recordSelectionDialog.useSampling', false, |
| 'State sampling'); |
| this.tracingModesContainerEl_ = this.querySelector('.tracing-modes'); |
| this.tracingModesContainerEl_.appendChild(this.tracingRecordModeSltr_); |
| this.tracingModesContainerEl_.appendChild(this.systemTracingBn_); |
| this.tracingModesContainerEl_.appendChild(this.samplingTracingBn_); |
| |
| |
| this.enabledCategoriesContainerEl_ = |
| this.querySelector('.default-enabled-categories .categories'); |
| |
| this.disabledCategoriesContainerEl_ = |
| this.querySelector('.default-disabled-categories .categories'); |
| |
| this.createGroupSelectButtons_( |
| this.querySelector('.default-enabled-categories')); |
| this.createGroupSelectButtons_( |
| this.querySelector('.default-disabled-categories')); |
| this.createDefaultDisabledWarningDialog_( |
| this.querySelector('.warning-default-disabled-categories')); |
| this.editCategoriesOpened_ = false; |
| |
| // TODO(chrishenry): When used with tr.b.ui.Overlay (such as in |
| // chrome://tracing, this does not yet look quite right due to |
| // the 10px overlay content padding (but it's good enough). |
| this.infoBarGroup_ = this.querySelector('tr-b-ui-info-bar-group'); |
| |
| this.addEventListener('visibleChange', this.onVisibleChange_.bind(this)); |
| }, |
| |
| set supportsSystemTracing(s) { |
| if (s) { |
| this.systemTracingBn_.style.display = undefined; |
| } else { |
| this.systemTracingBn_.style.display = 'none'; |
| this.useSystemTracing = false; |
| } |
| }, |
| |
| get tracingRecordMode() { |
| if (this.usingPreset_()) |
| return DEFAULT_RECORD_MODE; |
| return this.tracingRecordModeSltr_.selectedValue; |
| }, |
| set tracingRecordMode(value) { |
| this.tracingRecordMode_ = value; |
| }, |
| |
| get useSystemTracing() { |
| if (this.usingPreset_()) |
| return DEFAULT_SYSTEM_TRACING; |
| return this.systemTracingBn_.checked; |
| }, |
| set useSystemTracing(value) { |
| this.systemTracingBn_.checked = !!value; |
| }, |
| |
| get useSampling() { |
| if (this.usingPreset_()) |
| return DEFAULT_SAMPLING_TRACING; |
| return this.samplingTracingBn_.checked; |
| }, |
| set useSampling(value) { |
| this.samplingTracingBn_.checked = !!value; |
| }, |
| |
| set categories(c) { |
| this.categories_ = c; |
| |
| for (var i = 0; i < this.categories_.length; i++) { |
| var split = this.categories_[i].split(','); |
| this.categories_[i] = split.shift(); |
| if (split.length > 0) |
| this.categories_ = this.categories_.concat(split); |
| } |
| }, |
| |
| set settings_key(k) { |
| this.settings_key_ = k; |
| }, |
| |
| set settings(s) { |
| throw new Error('Dont use this!'); |
| }, |
| |
| usingPreset_: function() { |
| return this.currentlyChosenPreset_.length > 0 || |
| this.isPresetSelected_; |
| }, |
| |
| get currentlyChosenPreset() { |
| return this.currentlyChosenPreset_; |
| }, |
| |
| set currentlyChosenPreset(preset) { |
| if (!(preset instanceof Array)) |
| throw new Error('RecordSelectionDialog.currentlyChosenPreset:' + |
| ' preset must be an array.'); |
| this.currentlyChosenPreset_ = preset; |
| this.isPresetSelected_ = false; |
| |
| if (this.currentlyChosenPreset_.length) { |
| this.isPresetSelected_ = true; |
| this.changeEditCategoriesState_(false); |
| } else { |
| this.updateCategoryColumnView_(true); |
| this.changeEditCategoriesState_(true); |
| } |
| this.updateManualSelectionView_(); |
| this.updatePresetDescription_(); |
| }, |
| |
| updateManualSelectionView_: function() { |
| var classList = this.categoriesView_.classList; |
| if (!this.usingPreset_()) { |
| classList.remove('categories-column-view-hidden'); |
| } else { |
| if (this.editCategoriesOpened_) |
| classList.remove('categories-column-view-hidden'); |
| else |
| classList.add('categories-column-view-hidden'); |
| } |
| }, |
| |
| updateCategoryColumnView_: function(shouldReadFromSettings) { |
| var categorySet = this.querySelectorAll('.categories'); |
| for (var i = 0; i < categorySet.length; ++i) { |
| var categoryGroup = categorySet[i].children; |
| for (var j = 0; j < categoryGroup.length; ++j) { |
| var categoryEl = categoryGroup[j].children[0]; |
| categoryEl.checked = shouldReadFromSettings ? |
| tr.b.Settings.get(categoryEl.value, false, this.settings_key_) : |
| false; |
| } |
| } |
| }, |
| |
| onClickEditCategories: function() { |
| if (!this.usingPreset_()) |
| return; |
| |
| if (!this.editCategoriesOpened_) { |
| this.updateCategoryColumnView_(false); |
| for (var i = 0; i < this.currentlyChosenPreset_.length; ++i) { |
| var categoryEl = document.getElementById( |
| this.currentlyChosenPreset_[i]); |
| if (!categoryEl) |
| continue; |
| categoryEl.checked = true; |
| } |
| } |
| |
| this.changeEditCategoriesState_(!this.editCategoriesOpened_); |
| this.updateManualSelectionView_(); |
| this.recordButtonEl_.focus(); |
| }, |
| |
| changeEditCategoriesState_: function(editCategoriesState) { |
| var presetOptionsGroup = this.querySelector('.labeled-option-group'); |
| if (!presetOptionsGroup) |
| return; |
| |
| this.editCategoriesOpened_ = editCategoriesState; |
| if (this.editCategoriesOpened_) |
| presetOptionsGroup.classList.add('categories-expanded'); |
| else |
| presetOptionsGroup.classList.remove('categories-expanded'); |
| }, |
| |
| updatePresetDescription_: function() { |
| var description = this.querySelector('.category-description'); |
| if (this.usingPreset_()) { |
| description.innerText = this.currentlyChosenPreset_; |
| description.classList.remove('category-description-hidden'); |
| } else { |
| description.innerText = ''; |
| if (!description.classList.contains('category-description-hidden')) |
| description.classList.add('category-description-hidden'); |
| } |
| }, |
| |
| categoryFilter: function() { |
| if (this.usingPreset_()) { |
| var categories = []; |
| var allCategories = this.allCategories_(); |
| for (var category in allCategories) { |
| var disabled = category.indexOf('disabled-by-default-') == 0; |
| if (this.currentlyChosenPreset_.indexOf(category) >= 0) { |
| if (disabled) |
| categories.push(category); |
| } else { |
| if (!disabled) |
| categories.push('-' + category); |
| } |
| } |
| return categories.join(','); |
| } |
| |
| var categories = this.unselectedCategories_(); |
| var categories_length = categories.length; |
| var negated_categories = []; |
| for (var i = 0; i < categories_length; ++i) { |
| // Skip any category with a , as it will cause issues when we negate. |
| // Both sides should have been added as separate categories, these can |
| // only come from settings. |
| if (categories[i].match(/,/)) |
| continue; |
| negated_categories.push('-' + categories[i]); |
| } |
| categories = negated_categories.join(','); |
| |
| var disabledCategories = this.enabledDisabledByDefaultCategories_(); |
| disabledCategories = disabledCategories.join(','); |
| |
| var results = []; |
| if (categories !== '') |
| results.push(categories); |
| if (disabledCategories !== '') |
| results.push(disabledCategories); |
| return results.join(','); |
| }, |
| |
| clickRecordButton: function() { |
| this.recordButtonEl_.click(); |
| }, |
| |
| onRecordButtonClicked_: function() { |
| this.visible = false; |
| tr.b.dispatchSimpleEvent(this, 'recordclick'); |
| return false; |
| }, |
| |
| collectInputs_: function(inputs, isChecked) { |
| var inputs_length = inputs.length; |
| var categories = []; |
| for (var i = 0; i < inputs_length; ++i) { |
| var input = inputs[i]; |
| if (input.checked === isChecked) |
| categories.push(input.value); |
| } |
| return categories; |
| }, |
| |
| unselectedCategories_: function() { |
| var inputs = |
| this.enabledCategoriesContainerEl_.querySelectorAll('input'); |
| return this.collectInputs_(inputs, false); |
| }, |
| |
| enabledDisabledByDefaultCategories_: function() { |
| var inputs = |
| this.disabledCategoriesContainerEl_.querySelectorAll('input'); |
| return this.collectInputs_(inputs, true); |
| }, |
| |
| onVisibleChange_: function() { |
| if (this.visible) |
| this.updateForm_(); |
| }, |
| |
| buildInputs_: function(inputs, checkedDefault, parent) { |
| var inputs_length = inputs.length; |
| for (var i = 0; i < inputs_length; i++) { |
| var category = inputs[i]; |
| |
| var inputEl = document.createElement('input'); |
| inputEl.type = 'checkbox'; |
| inputEl.id = category; |
| inputEl.value = category; |
| |
| inputEl.checked = tr.b.Settings.get( |
| category, checkedDefault, this.settings_key_); |
| inputEl.onclick = this.updateSetting_.bind(this); |
| |
| var labelEl = document.createElement('label'); |
| labelEl.textContent = category.replace('disabled-by-default-', ''); |
| labelEl.setAttribute('for', category); |
| |
| var divEl = document.createElement('div'); |
| divEl.appendChild(inputEl); |
| divEl.appendChild(labelEl); |
| |
| parent.appendChild(divEl); |
| } |
| }, |
| |
| allCategories_: function() { |
| // Dedup the categories. We may have things in settings that are also |
| // returned when we query the category list. |
| var categorySet = {}; |
| var allCategories = |
| this.categories_.concat(tr.b.Settings.keys(this.settings_key_)); |
| var allCategoriesLength = allCategories.length; |
| for (var i = 0; i < allCategoriesLength; ++i) |
| categorySet[allCategories[i]] = true; |
| return categorySet; |
| }, |
| |
| updateForm_: function() { |
| this.enabledCategoriesContainerEl_.innerHTML = ''; // Clear old categories |
| this.disabledCategoriesContainerEl_.innerHTML = ''; |
| |
| this.recordButtonEl_.focus(); |
| |
| var allCategories = this.allCategories_(); |
| var categories = []; |
| var disabledCategories = []; |
| for (var category in allCategories) { |
| if (category.indexOf('disabled-by-default-') == 0) |
| disabledCategories.push(category); |
| else |
| categories.push(category); |
| } |
| disabledCategories = disabledCategories.sort(); |
| categories = categories.sort(); |
| |
| if (this.categories_.length == 0) { |
| this.infoBarGroup_.addMessage( |
| 'No categories found; recording will use default categories.'); |
| } |
| |
| this.buildInputs_(categories, true, this.enabledCategoriesContainerEl_); |
| |
| if (disabledCategories.length > 0) { |
| this.disabledCategoriesContainerEl_.hidden = false; |
| this.buildInputs_(disabledCategories, false, |
| this.disabledCategoriesContainerEl_); |
| } |
| }, |
| |
| updateSetting_: function(e) { |
| var checkbox = e.target; |
| tr.b.Settings.set(checkbox.value, checkbox.checked, this.settings_key_); |
| |
| // Change the current record mode to 'Manually select settings' from |
| // preset mode if and only if currently user is in preset record mode |
| // and user selects/deselects any category in 'Edit Categories' mode. |
| if (this.usingPreset_()) { |
| if (checkbox.checked) { |
| this.currentlyChosenPreset_.push(checkbox.value); |
| } else { |
| var pos = this.currentlyChosenPreset_.lastIndexOf(checkbox.value); |
| this.currentlyChosenPreset_.splice(pos, 1); |
| } |
| var categoryEl = document.getElementById( |
| 'category-preset-Manually-select-settings'); |
| categoryEl.checked = true; |
| var description = this.querySelector('.category-description'); |
| description.innerText = ''; |
| description.classList.add('category-description-hidden'); |
| } |
| }, |
| |
| createGroupSelectButtons_: function(parent) { |
| var flipInputs = function(dir) { |
| var inputs = parent.querySelectorAll('input'); |
| for (var i = 0; i < inputs.length; i++) { |
| if (inputs[i].checked === dir) |
| continue; |
| // click() is used so the settings will be correclty stored. Setting |
| // checked does not trigger the onclick (or onchange) callback. |
| inputs[i].click(); |
| } |
| }; |
| |
| var allBtn = parent.querySelector('.all-btn'); |
| allBtn.onclick = function(evt) { |
| flipInputs(true); |
| evt.preventDefault(); |
| }; |
| |
| var noneBtn = parent.querySelector('.none-btn'); |
| noneBtn.onclick = function(evt) { |
| flipInputs(false); |
| evt.preventDefault(); |
| }; |
| }, |
| |
| setWarningDialogOverlayText_: function(messages) { |
| var contentDiv = document.createElement('div'); |
| |
| for (var i = 0; i < messages.length; ++i) { |
| var messageDiv = document.createElement('div'); |
| messageDiv.textContent = messages[i]; |
| contentDiv.appendChild(messageDiv); |
| } |
| this.warningOverlay_.textContent = ''; |
| this.warningOverlay_.appendChild(contentDiv); |
| }, |
| |
| createDefaultDisabledWarningDialog_: function(warningLink) { |
| function onClickHandler(evt) { |
| this.warningOverlay_ = tr.b.ui.Overlay(); |
| this.warningOverlay_.parentEl_ = this; |
| this.warningOverlay_.title = 'Warning...'; |
| this.warningOverlay_.userCanClose = true; |
| this.warningOverlay_.visible = true; |
| |
| this.setWarningDialogOverlayText_([ |
| 'Enabling the default disabled categories may have', |
| 'performance and memory impact while tr.c.' |
| ]); |
| |
| evt.preventDefault(); |
| } |
| warningLink.onclick = onClickHandler.bind(this); |
| } |
| }; |
| |
| return { |
| RecordSelectionDialog: RecordSelectionDialog |
| }; |
| }); |
| </script> |