blob: 9ca6bbc9d4f4377b21c6b6875635b6ae4f1f7eac [file] [log] [blame]
Chris Craikb122baf2015-03-05 13:58:42 -08001<!DOCTYPE html>
2<!--
3Copyright (c) 2013 The Chromium Authors. All rights reserved.
4Use of this source code is governed by a BSD-style license that can be
5found in the LICENSE file.
6-->
7<link rel="import" href="/core/filter.html">
8<link rel="import" href="/base/utils.html">
9<link rel="import" href="/base/ui/overlay.html">
10<link rel="import" href="/base/ui/dom_helpers.html">
11<link rel="import" href="/base/ui/info_bar.html">
12
13<template id="record-selection-dialog-template">
14 <style>
15 .categories-column-view {
16 display: -webkit-flex;
17 -webkit-flex-direction: column;
18 font-family: sans-serif;
19 max-width: 640px;
20 min-height: 0;
21 min-width: 0;
22 opacity: 1;
23 transition: max-height 1s ease, max-width 1s ease, opacity 1s ease;
24 will-change: opacity;
25 }
26
27 .categories-column-view-hidden {
28 max-height: 0;
29 max-width: 0;
30 opacity: 0;
31 overflow: hidden;
32 }
33
34 .categories-selection {
35 display: -webkit-flex;
36 -webkit-flex-direction: row;
37 }
38
39 .category-presets {
40 padding: 4px;
41 }
42
43 .category-description {
44 color: #aaa;
45 font-size: small;
46 max-height: 1em;
47 opacity: 1;
48 padding-left: 4px;
49 padding-right: 4px;
50 text-align: right;
51 transition: max-height 1s ease, opacity 1s ease;
52 will-change: opacity;
53 }
54
55 .category-description-hidden {
56 max-height: 0;
57 opacity: 0;
58 }
59
60 .default-enabled-categories,
61 .default-disabled-categories {
62 -webkit-flex: 1 1 auto;
63 display: -webkit-flex;
64 -webkit-flex-direction: column;
65 padding: 4px;
66 width: 300px;
67 }
68
69 .default-enabled-categories > div,
70 .default-disabled-categories > div {
71 padding: 4px;
72 }
73
74 .tracing-modes {
75 -webkit-flex: 1 0 auto;
76 display: -webkit-flex;
77 -webkit-flex-direction: reverse;
78 padding: 4px;
79 border-bottom: 2px solid #ddd;
80 border-top: 2px solid #ddd;
81 }
82
83 .default-disabled-categories {
84 border-left: 2px solid #ddd;
85 }
86
87 .warning-default-disabled-categories {
88 display: inline-block;
89 font-weight: bold;
90 text-align: center;
91 color: #BD2E2E;
92 width: 2.0ex;
93 height: 2.0ex;
94 border-radius: 2.0ex;
95 border: 1px solid #BD2E2E;
96 }
97
98 .categories {
99 font-size: 80%;
100 padding: 10px;
101 -webkit-flex: 1 1 auto;
102 }
103
104 .group-selectors {
105 font-size: 80%;
106 border-bottom: 1px solid #ddd;
107 padding-bottom: 6px;
108 -webkit-flex: 0 0 auto;
109 }
110
111 .group-selectors button {
112 padding: 1px;
113 }
114 </style>
115
116 <div class="record-selection-dialog">
117 <tv-b-ui-info-bar-group></tv-b-ui-info-bar-group>
118 <div class="category-presets">
119 </div>
120 <div class="category-description"></div>
121 <div class="categories-column-view">
122 <div class="tracing-modes"></div>
123 <div class="categories-selection">
124 <div class="default-enabled-categories">
125 <div>Record&nbsp;Categories</div>
126 <div class="group-selectors">
127 Select
128 <button class="all-btn">All</button>
129 <button class="none-btn">None</button>
130 </div>
131 <div class="categories"></div>
132 </div>
133 <div class="default-disabled-categories">
134 <div>Disabled&nbsp;by&nbsp;Default&nbsp;Categories
135 <a class="warning-default-disabled-categories">!</a>
136 </div>
137 <div class="group-selectors">
138 Select
139 <button class="all-btn">All</button>
140 <button class="none-btn">None</button>
141 </div>
142 <div class="categories"></div>
143 </div>
144 </div>
145 </div>
146 </div>
147</template>
148
149<script>
150'use strict';
151
152/**
153 * @fileoverview RecordSelectionDialog presents the available categories
154 * to be enabled/disabled during tv.c.
155 */
156tv.exportTo('tv.e.about_tracing', function() {
157 var THIS_DOC = document.currentScript.ownerDocument;
158 var RecordSelectionDialog = tv.b.ui.define('div');
159
160 var DEFAULT_PRESETS = [
161 {title: 'Web developer',
162 categoryFilter: ['blink', 'cc', 'netlog', 'renderer.scheduler',
163 'toplevel', 'v8']},
164 {title: 'Input latency',
Chris Craik44c28202015-05-12 17:25:16 -0700165 categoryFilter: ['benchmark', 'input', 'evdev', 'renderer.scheduler',
166 'toplevel']},
Chris Craikb122baf2015-03-05 13:58:42 -0800167 {title: 'Rendering',
168 categoryFilter: ['blink', 'cc', 'gpu', 'toplevel']},
169 {title: 'Javascript and rendering',
170 categoryFilter: ['blink', 'cc', 'gpu', 'renderer.scheduler', 'v8',
171 'toplevel']},
172 {title: 'Frame Viewer',
173 categoryFilter: ['blink', 'cc', 'gpu', 'renderer.scheduler', 'v8',
174 'toplevel',
175 'disabled-by-default-cc.debug',
176 'disabled-by-default-cc.debug.picture']},
177 {title: 'Manually select settings',
178 categoryFilter: []}
179 ];
180 var RECORDING_MODES = [
181 {'label': 'Record until full',
182 'value': 'record-until-full'},
183 {'label': 'Record continuously',
184 'value': 'record-continuously'},
185 {'label': 'Record as much as possible',
186 'value': 'record-as-much-as-possible'}];
Chris Craikbeca7ae2015-04-07 13:29:55 -0700187 var DEFAULT_RECORD_MODE = 'record-until-full';
Chris Craikb122baf2015-03-05 13:58:42 -0800188 var DEFAULT_CONTINUOUS_TRACING = true;
189 var DEFAULT_SYSTEM_TRACING = false;
190 var DEFAULT_SAMPLING_TRACING = false;
191
192 RecordSelectionDialog.prototype = {
193 __proto__: tv.b.ui.Overlay.prototype,
194
195 decorate: function() {
196 tv.b.ui.Overlay.prototype.decorate.call(this);
197 this.title = 'Record a new trace...';
198
199 this.classList.add('record-dialog-overlay');
200
201 var node = tv.b.instantiateTemplate('#record-selection-dialog-template',
202 THIS_DOC);
203 this.appendChild(node);
204
205 this.recordButtonEl_ = document.createElement('button');
206 this.recordButtonEl_.textContent = 'Record';
207 this.recordButtonEl_.addEventListener(
208 'click',
209 this.onRecordButtonClicked_.bind(this));
210 this.recordButtonEl_.style.fontSize = '110%';
211 this.buttons.appendChild(this.recordButtonEl_);
212
213 this.categoriesView_ = this.querySelector('.categories-column-view');
214 this.presetsEl_ = this.querySelector('.category-presets');
215 this.presetsEl_.appendChild(tv.b.ui.createOptionGroup(
216 this, 'currentlyChosenPreset',
217 'about_tracing.record_selection_dialog_preset',
218 DEFAULT_PRESETS[0].categoryFilter,
219 DEFAULT_PRESETS.map(function(p) {
220 return { label: p.title, value: p.categoryFilter };
221 })));
222
223 this.tracingRecordModeSltr_ = tv.b.ui.createSelector(
224 this, 'tracingRecordMode',
225 'recordSelectionDialog.tracingRecordMode',
226 DEFAULT_RECORD_MODE, RECORDING_MODES);
227
228 this.systemTracingBn_ = tv.b.ui.createCheckBox(
229 undefined, undefined,
230 'recordSelectionDialog.useSystemTracing', true,
231 'System tracing');
232 this.samplingTracingBn_ = tv.b.ui.createCheckBox(
233 undefined, undefined,
234 'recordSelectionDialog.useSampling', false,
235 'State sampling');
236 this.tracingModesContainerEl_ = this.querySelector('.tracing-modes');
237 this.tracingModesContainerEl_.appendChild(this.tracingRecordModeSltr_);
238 this.tracingModesContainerEl_.appendChild(this.systemTracingBn_);
239 this.tracingModesContainerEl_.appendChild(this.samplingTracingBn_);
240
241
242 this.enabledCategoriesContainerEl_ =
243 this.querySelector('.default-enabled-categories .categories');
244
245 this.disabledCategoriesContainerEl_ =
246 this.querySelector('.default-disabled-categories .categories');
247
248 this.createGroupSelectButtons_(
249 this.querySelector('.default-enabled-categories'));
250 this.createGroupSelectButtons_(
251 this.querySelector('.default-disabled-categories'));
252 this.createDefaultDisabledWarningDialog_(
253 this.querySelector('.warning-default-disabled-categories'));
254 this.editCategoriesOpened_ = false;
255
256 // TODO(chrishenry): When used with tv.b.ui.Overlay (such as in
257 // chrome://tracing, this does not yet look quite right due to
258 // the 10px overlay content padding (but it's good enough).
259 this.infoBarGroup_ = this.querySelector('tv-b-ui-info-bar-group');
260
261 this.addEventListener('visibleChange', this.onVisibleChange_.bind(this));
262 },
263
264 set supportsSystemTracing(s) {
265 if (s) {
266 this.systemTracingBn_.style.display = undefined;
267 } else {
268 this.systemTracingBn_.style.display = 'none';
269 this.useSystemTracing = false;
270 }
271 },
272
273 get tracingRecordMode() {
274 if (this.usingPreset_())
275 return DEFAULT_RECORD_MODE;
276 return this.tracingRecordModeSltr_.selectedValue;
277 },
278 set tracingRecordMode(value) {
279 this.tracingRecordMode_ = value;
280 },
281
282 get useSystemTracing() {
283 if (this.usingPreset_())
284 return DEFAULT_SYSTEM_TRACING;
285 return this.systemTracingBn_.checked;
286 },
287 set useSystemTracing(value) {
288 this.systemTracingBn_.checked = !!value;
289 },
290
291 get useSampling() {
292 if (this.usingPreset_())
293 return DEFAULT_SAMPLING_TRACING;
294 return this.samplingTracingBn_.checked;
295 },
296 set useSampling(value) {
297 this.samplingTracingBn_.checked = !!value;
298 },
299
300 set categories(c) {
301 this.categories_ = c;
302
303 for (var i = 0; i < this.categories_.length; i++) {
304 var split = this.categories_[i].split(',');
305 this.categories_[i] = split.shift();
306 if (split.length > 0)
307 this.categories_ = this.categories_.concat(split);
308 }
309 },
310
311 set settings_key(k) {
312 this.settings_key_ = k;
313 },
314
315 set settings(s) {
316 throw new Error('Dont use this!');
317 },
318
319 usingPreset_: function() {
320 return this.currentlyChosenPreset_.length > 0 ||
321 this.isPresetSelected_;
322 },
323
324 get currentlyChosenPreset() {
325 return this.currentlyChosenPreset_;
326 },
327
328 set currentlyChosenPreset(preset) {
329 if (!(preset instanceof Array))
330 throw new Error('RecordSelectionDialog.currentlyChosenPreset:' +
331 ' preset must be an array.');
332 this.currentlyChosenPreset_ = preset;
333 this.isPresetSelected_ = false;
334
335 if (this.currentlyChosenPreset_.length) {
336 this.isPresetSelected_ = true;
337 this.changeEditCategoriesState_(false);
338 } else {
339 this.updateCategoryColumnView_(true);
340 this.changeEditCategoriesState_(true);
341 }
342 this.updateManualSelectionView_();
343 this.updatePresetDescription_();
344 },
345
346 updateManualSelectionView_: function() {
347 var classList = this.categoriesView_.classList;
348 if (!this.usingPreset_()) {
349 classList.remove('categories-column-view-hidden');
350 } else {
351 if (this.editCategoriesOpened_)
352 classList.remove('categories-column-view-hidden');
353 else
354 classList.add('categories-column-view-hidden');
355 }
356 },
357
358 updateCategoryColumnView_: function(shouldReadFromSettings) {
359 var categorySet = this.querySelectorAll('.categories');
360 for (var i = 0; i < categorySet.length; ++i) {
361 var categoryGroup = categorySet[i].children;
362 for (var j = 0; j < categoryGroup.length; ++j) {
363 var categoryEl = categoryGroup[j].children[0];
364 categoryEl.checked = shouldReadFromSettings ?
365 tv.b.Settings.get(categoryEl.value, false, this.settings_key_) :
366 false;
367 }
368 }
369 },
370
371 onClickEditCategories: function() {
372 if (!this.usingPreset_())
373 return;
374
375 if (!this.editCategoriesOpened_) {
376 this.updateCategoryColumnView_(false);
377 for (var i = 0; i < this.currentlyChosenPreset_.length; ++i) {
378 var categoryEl = document.getElementById(
379 this.currentlyChosenPreset_[i]);
380 categoryEl.checked = true;
381 }
382 }
383
384 this.changeEditCategoriesState_(!this.editCategoriesOpened_);
385 this.updateManualSelectionView_();
386 this.recordButtonEl_.focus();
387 },
388
389 changeEditCategoriesState_: function(editCategoriesState) {
390 var presetOptionsGroup = this.querySelector('.labeled-option-group');
391 if (!presetOptionsGroup)
392 return;
393
394 this.editCategoriesOpened_ = editCategoriesState;
395 if (this.editCategoriesOpened_)
396 presetOptionsGroup.classList.add('categories-expanded');
397 else
398 presetOptionsGroup.classList.remove('categories-expanded');
399 },
400
401 updatePresetDescription_: function() {
402 var description = this.querySelector('.category-description');
403 if (this.usingPreset_()) {
404 description.innerText = this.currentlyChosenPreset_;
405 description.classList.remove('category-description-hidden');
406 } else {
407 description.innerText = '';
408 if (!description.classList.contains('category-description-hidden'))
409 description.classList.add('category-description-hidden');
410 }
411 },
412
413 categoryFilter: function() {
414 if (this.usingPreset_()) {
415 var categories = [];
416 var allCategories = this.allCategories_();
417 for (var category in allCategories) {
418 var disabled = category.indexOf('disabled-by-default-') == 0;
419 if (this.currentlyChosenPreset_.indexOf(category) >= 0) {
420 if (disabled)
421 categories.push(category);
422 } else {
423 if (!disabled)
424 categories.push('-' + category);
425 }
426 }
427 return categories.join(',');
428 }
429
430 var categories = this.unselectedCategories_();
431 var categories_length = categories.length;
432 var negated_categories = [];
433 for (var i = 0; i < categories_length; ++i) {
434 // Skip any category with a , as it will cause issues when we negate.
435 // Both sides should have been added as separate categories, these can
436 // only come from settings.
437 if (categories[i].match(/,/))
438 continue;
439 negated_categories.push('-' + categories[i]);
440 }
441 categories = negated_categories.join(',');
442
443 var disabledCategories = this.enabledDisabledByDefaultCategories_();
444 disabledCategories = disabledCategories.join(',');
445
446 var results = [];
447 if (categories !== '')
448 results.push(categories);
449 if (disabledCategories !== '')
450 results.push(disabledCategories);
451 return results.join(',');
452 },
453
454 clickRecordButton: function() {
455 this.recordButtonEl_.click();
456 },
457
458 onRecordButtonClicked_: function() {
459 this.visible = false;
460 tv.b.dispatchSimpleEvent(this, 'recordclick');
461 return false;
462 },
463
464 collectInputs_: function(inputs, isChecked) {
465 var inputs_length = inputs.length;
466 var categories = [];
467 for (var i = 0; i < inputs_length; ++i) {
468 var input = inputs[i];
469 if (input.checked === isChecked)
470 categories.push(input.value);
471 }
472 return categories;
473 },
474
475 unselectedCategories_: function() {
476 var inputs =
477 this.enabledCategoriesContainerEl_.querySelectorAll('input');
478 return this.collectInputs_(inputs, false);
479 },
480
481 enabledDisabledByDefaultCategories_: function() {
482 var inputs =
483 this.disabledCategoriesContainerEl_.querySelectorAll('input');
484 return this.collectInputs_(inputs, true);
485 },
486
487 onVisibleChange_: function() {
488 if (this.visible)
489 this.updateForm_();
490 },
491
492 buildInputs_: function(inputs, checkedDefault, parent) {
493 var inputs_length = inputs.length;
494 for (var i = 0; i < inputs_length; i++) {
495 var category = inputs[i];
496
497 var inputEl = document.createElement('input');
498 inputEl.type = 'checkbox';
499 inputEl.id = category;
500 inputEl.value = category;
501
502 inputEl.checked = tv.b.Settings.get(
503 category, checkedDefault, this.settings_key_);
504 inputEl.onclick = this.updateSetting_.bind(this);
505
506 var labelEl = document.createElement('label');
507 labelEl.textContent = category.replace('disabled-by-default-', '');
508 labelEl.setAttribute('for', category);
509
510 var divEl = document.createElement('div');
511 divEl.appendChild(inputEl);
512 divEl.appendChild(labelEl);
513
514 parent.appendChild(divEl);
515 }
516 },
517
518 allCategories_: function() {
519 // Dedup the categories. We may have things in settings that are also
520 // returned when we query the category list.
521 var categorySet = {};
522 var allCategories =
523 this.categories_.concat(tv.b.Settings.keys(this.settings_key_));
524 var allCategoriesLength = allCategories.length;
525 for (var i = 0; i < allCategoriesLength; ++i)
526 categorySet[allCategories[i]] = true;
527 return categorySet;
528 },
529
530 updateForm_: function() {
531 this.enabledCategoriesContainerEl_.innerHTML = ''; // Clear old categories
532 this.disabledCategoriesContainerEl_.innerHTML = '';
533
534 this.recordButtonEl_.focus();
535
536 var allCategories = this.allCategories_();
537 var categories = [];
538 var disabledCategories = [];
539 for (var category in allCategories) {
540 if (category.indexOf('disabled-by-default-') == 0)
541 disabledCategories.push(category);
542 else
543 categories.push(category);
544 }
545 disabledCategories = disabledCategories.sort();
546 categories = categories.sort();
547
548 if (this.categories_.length == 0) {
549 this.infoBarGroup_.addMessage(
550 'No categories found; recording will use default categories.');
551 }
552
553 this.buildInputs_(categories, true, this.enabledCategoriesContainerEl_);
554
555 if (disabledCategories.length > 0) {
556 this.disabledCategoriesContainerEl_.hidden = false;
557 this.buildInputs_(disabledCategories, false,
558 this.disabledCategoriesContainerEl_);
559 }
560 },
561
562 updateSetting_: function(e) {
563 var checkbox = e.target;
564 tv.b.Settings.set(checkbox.value, checkbox.checked, this.settings_key_);
565
566 // Change the current record mode to 'Manually select settings' from
567 // preset mode if and only if currently user is in preset record mode
568 // and user selects/deselects any category in 'Edit Categories' mode.
569 if (this.usingPreset_()) {
570 if (checkbox.checked) {
571 this.currentlyChosenPreset_.push(checkbox.value);
572 } else {
573 var pos = this.currentlyChosenPreset_.lastIndexOf(checkbox.value);
574 this.currentlyChosenPreset_.splice(pos, 1);
575 }
576 var categoryEl = document.getElementById(
577 'category-preset-Manually-select-settings');
578 categoryEl.checked = true;
579 var description = this.querySelector('.category-description');
580 description.innerText = '';
581 description.classList.add('category-description-hidden');
582 }
583 },
584
585 createGroupSelectButtons_: function(parent) {
586 var flipInputs = function(dir) {
587 var inputs = parent.querySelectorAll('input');
588 for (var i = 0; i < inputs.length; i++) {
589 if (inputs[i].checked === dir)
590 continue;
591 // click() is used so the settings will be correclty stored. Setting
592 // checked does not trigger the onclick (or onchange) callback.
593 inputs[i].click();
594 }
595 };
596
597 var allBtn = parent.querySelector('.all-btn');
598 allBtn.onclick = function(evt) {
599 flipInputs(true);
600 evt.preventDefault();
601 };
602
603 var noneBtn = parent.querySelector('.none-btn');
604 noneBtn.onclick = function(evt) {
605 flipInputs(false);
606 evt.preventDefault();
607 };
608 },
609
610 setWarningDialogOverlayText_: function(messages) {
611 var contentDiv = document.createElement('div');
612
613 for (var i = 0; i < messages.length; ++i) {
614 var messageDiv = document.createElement('div');
615 messageDiv.textContent = messages[i];
616 contentDiv.appendChild(messageDiv);
617 }
618 this.warningOverlay_.textContent = '';
619 this.warningOverlay_.appendChild(contentDiv);
620 },
621
622 createDefaultDisabledWarningDialog_: function(warningLink) {
623 function onClickHandler(evt) {
624 this.warningOverlay_ = tv.b.ui.Overlay();
625 this.warningOverlay_.parentEl_ = this;
626 this.warningOverlay_.title = 'Warning...';
627 this.warningOverlay_.userCanClose = true;
628 this.warningOverlay_.visible = true;
629
630 this.setWarningDialogOverlayText_([
631 'Enabling the default disabled categories may have',
632 'performance and memory impact while tv.c.'
633 ]);
634
635 evt.preventDefault();
636 }
637 warningLink.onclick = onClickHandler.bind(this);
638 }
639 };
640
641 return {
642 RecordSelectionDialog: RecordSelectionDialog
643 };
644});
645</script>