blob: e46c2affc370bc884f492ca1fbae1e5f9f327ad4 [file] [log] [blame]
<!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="stylesheet" href="/base/ui/common.css">
<link rel="stylesheet" href="/core/timeline_view.css">
<link rel="import" href="/base/utils.html">
<link rel="import" href="/base/settings.html">
<link rel="import" href="/base/ui/dom_helpers.html">
<link rel="import" href="/base/ui/overlay.html">
<link rel="import" href="/base/ui/dropdown.html">
<link rel="import" href="/base/ui/drag_handle.html">
<link rel="import" href="/core/analysis/analysis_view.html">
<link rel="import" href="/core/favicons.html">
<link rel="import" href="/core/find_control.html">
<link rel="import" href="/core/find_controller.html">
<link rel="import" href="/core/timeline_track_view.html">
<link rel="import" href="/core/selection_controller.html">
<link rel="import" href="/core/scripting_control.html">
<link rel="import" href="/core/scripting_controller.html">
<link rel="import" href="/core/side_panel/side_panel_container.html">
<template id="timeline-view-template">
<div class="control">
<div class="bar">
<div id="left-controls" class="controls"></div>
<div class="title">^_^</div>
<div id="right-controls" class="controls"></div>
</div>
<div id="collapsing-controls" class="controls"></div>
</div>
<middle-container>
<track-view-container></track-view-container>
<tr-c-side-panel-container></tr-c-side-panel-container>
</middle-container>
<x-drag-handle></x-drag-handle>
<tr-c-a-analysis-view id="analysis"></tr-c-a-analysis-view>
</template>
<template id="help-btn-template">
<div class="button view-help-button">?</div>
<div class="view-help-text">
<div class="column left">
<h2>Navigation</h2>
<div class='pair'>
<div class='command'>w/s</div>
<div class='action'>Zoom in/out (+shift: faster)</div>
</div>
<div class='pair'>
<div class='command'>a/d</div>
<div class='action'>Pan left/right (+shift: faster)</div>
</div>
<div class='pair'>
<div class='command'>&rarr;/shift-TAB</div>
<div class='action'>Select previous event</div>
</div>
<div class='pair'>
<div class='command'>&larr;/TAB</div>
<div class='action'>Select next event</div>
</div>
<h2>Mouse Controls</h2>
<div class='pair'>
<div class='command'>click</div>
<div class='action'>Select event</div>
</div>
<div class='pair'>
<div class='command'>alt-mousewheel</div>
<div class='action'>Zoom in/out</div>
</div>
<h3>
<span class='mouse-mode-icon select-mode'></span>
Select mode
</h3>
<div class='pair'>
<div class='command'>drag</div>
<div class='action'>Box select</div>
</div>
<div class='pair'>
<div class='command'>double click</div>
<div class='action'>Select all events with same title</div>
</div>
<h3>
<span class='mouse-mode-icon pan-mode'></span>
Pan mode
</h3>
<div class='pair'>
<div class='command'>drag</div>
<div class='action'>Pan the view</div>
</div>
<h3>
<span class='mouse-mode-icon zoom-mode'></span>
Zoom mode
</h3>
<div class='pair'>
<div class='command'>drag</div>
<div class='action'>Zoom in/out by dragging up/down</div>
</div>
<h3>
<span class='mouse-mode-icon timing-mode'></span>
Timing mode
</h3>
<div class='pair'>
<div class='command'>drag</div>
<div class='action'>Create or move markers</div>
</div>
<div class='pair'>
<div class='command'>double click</div>
<div class='action'>Set marker range to slice</div>
</div>
</div>
<div class="column right">
<h2>General</h2>
<div class='pair'>
<div class='command'>1-4</div>
<div class='action'>Switch mouse mode</div>
</div>
<div class='pair'>
<div class='command'>shift</div>
<div class='action'>Hold for temporary select</div>
</div>
<div class='pair'>
<div class='command'>space</div>
<div class='action'>Hold for temporary pan</div>
</div>
<div class='pair'>
<div class='command'><span class='mod'></span></div>
<div class='action'>Hold for temporary zoom</div>
</div>
<div class='pair'>
<div class='command'>/</div>
<div class='action'>Search</div>
</div>
<div class='pair'>
<div class='command'>enter</div>
<div class='action'>Step through search results</div>
</div>
<div class='pair'>
<div class='command'>f</div>
<div class='action'>Zoom into selection</div>
</div>
<div class='pair'>
<div class='command'>z/0</div>
<div class='action'>Reset zoom and pan</div>
</div>
<div class='pair'>
<div class='command'>g/G</div>
<div class='action'>Toggle 60hz grid</div>
</div>
<div class='pair'>
<div class='command'>v</div>
<div class='action'>Highlight VSync</div>
</div>
<div class='pair'>
<div class='command'>h</div>
<div class='action'>Toggle low/high details</div>
</div>
<div class='pair'>
<div class='command'>m</div>
<div class='action'>Mark current selection</div>
</div>
<div class='pair'>
<div class='command'>`</div>
<div class='action'>Show or hide the scripting console</div>
</div>
<div class='pair'>
<div class='command'>?</div>
<div class='action'>Show help</div>
</div>
</div>
</div>
</template>
<template id="metadata-btn-template">
<div class="button view-metadata-button view-info-button">Metadata</div>
<div class="info-button-text metadata-dialog-text"></div>
</template>
<template id="console-btn-template">
<div class="button view-console-button">&#187;</div>
</template>
<script>
'use strict';
/**
* @fileoverview View visualizes TRACE_EVENT events using the
* tr.c.Timeline component and adds in selection summary and control buttons.
*/
tr.exportTo('tr.c', function() {
var THIS_DOC = document.currentScript.ownerDocument;
/**
* View
* @constructor
* @extends {HTMLUnknownElement}
*/
var TimelineView = tr.b.ui.define('x-timeline-view');
TimelineView.prototype = {
__proto__: HTMLUnknownElement.prototype,
decorate: function() {
var node = tr.b.instantiateTemplate('#timeline-view-template', THIS_DOC);
this.appendChild(node);
this.titleEl_ = this.querySelector('.title');
this.leftControlsEl_ = this.querySelector('#left-controls');
this.rightControlsEl_ = this.querySelector('#right-controls');
this.collapsingControlsEl_ = this.querySelector('#collapsing-controls');
this.sidePanelContainer_ = this.querySelector(
'tr-c-side-panel-container');
this.trackViewContainer_ = this.querySelector('track-view-container');
this.selectionController_ = new tr.c.SelectionController(this);
this.selectionController_.historyEnabled = true;
this.findCtl_ = new TracingFindControl();
this.findCtl_.controller = new tr.c.FindController(
this.selectionController_);
this.findCtl_.addEventListener('keydown', function(e) {
if (e.keyCode === 27) { // ESC
this.focus();
e.preventDefault();
}
}.bind(this));
this.scriptingCtl_ = new TracingScriptingControl();
this.scriptingCtl_.controller = new tr.c.ScriptingController(
this.selectionController_);
this.sidePanelContainer_.selectionController = this.selectionController_;
if (window.tr.e && window.tr.e.rail && window.tr.e.rail.RAILScore) {
this.railScoreSpan_ = document.createElement(
'tr-e-rail-rail-score-span');
this.rightControls.appendChild(this.railScoreSpan_);
} else {
this.railScoreSpan_ = undefined;
}
this.optionsDropdown_ = document.createElement('tr-b-ui-dropdown');
this.optionsDropdown_.iconElement.textContent = 'View Options';
this.showFlowEvents_ = false;
this.optionsDropdown_.appendChild(tr.b.ui.createCheckBox(
this, 'showFlowEvents',
'tr.c.TimelineView.showFlowEvents', false,
'Flow events'));
this.highlightVSync_ = false;
this.highlightVSyncCheckbox_ = tr.b.ui.createCheckBox(
this, 'highlightVSync',
'tr.c.TimelineView.highlightVSync', false,
'Highlight VSync');
this.optionsDropdown_.appendChild(this.highlightVSyncCheckbox_);
this.rightControls.appendChild(this.createMetadataButton_());
this.rightControls.appendChild(this.optionsDropdown_);
this.rightControls.appendChild(this.findCtl_);
this.rightControls.appendChild(this.createConsoleButton_());
this.rightControls.appendChild(this.createHelpButton_());
this.collapsingControls.appendChild(this.scriptingCtl_);
this.dragEl_ = this.querySelector('x-drag-handle');
tr.b.ui.decorate(this.dragEl_, tr.b.ui.DragHandle);
this.analysisEl_ = this.querySelector('#analysis');
this.analysisEl_.selectionController = this.selectionController_;
this.addEventListener(
'requestSelectionChange',
function(e) {
var sc = this.selectionController_;
sc.changeSelectionFromRequestSelectionChangeEvent(e.selection);
}.bind(this));
// Bookkeeping.
this.onViewportChanged_ = this.onViewportChanged_.bind(this);
document.addEventListener('keydown', this.onKeyDown_.bind(this), true);
document.addEventListener('keypress', this.onKeypress_.bind(this), true);
this.dragEl_.target = this.analysisEl_;
},
updateDocumentFavicon: function() {
var hue;
if (!this.model)
hue = 'blue';
else
hue = this.model.faviconHue;
var faviconData = tr.c.FaviconsByHue[hue];
if (faviconData === undefined)
faviconData = tr.c.FaviconsByHue['blue'];
// Find link if its there
var link = document.head.querySelector('link[rel="shortcut icon"]');
if (!link) {
link = document.createElement('link');
link.rel = 'shortcut icon';
document.head.appendChild(link);
}
link.href = faviconData;
},
get showFlowEvents() {
return this.showFlowEvents_;
},
set showFlowEvents(showFlowEvents) {
this.showFlowEvents_ = showFlowEvents;
if (!this.trackView_)
return;
this.trackView_.viewport.showFlowEvents = showFlowEvents;
},
get highlightVSync() {
return this.highlightVSync_;
},
set highlightVSync(highlightVSync) {
this.highlightVSync_ = highlightVSync;
if (!this.trackView_)
return;
this.trackView_.viewport.highlightVSync = highlightVSync;
},
createHelpButton_: function() {
var node = tr.b.instantiateTemplate('#help-btn-template', THIS_DOC);
var showEl = node.querySelector('.view-help-button');
var helpTextEl = node.querySelector('.view-help-text');
var dlg = new tr.b.ui.Overlay();
dlg.title = 'chrome://tracing Help';
dlg.classList.add('view-help-overlay');
dlg.appendChild(node);
function onClick(e) {
dlg.visible = !dlg.visible;
var mod = tr.isMac ? 'cmd ' : 'ctrl';
var spans = helpTextEl.querySelectorAll('span.mod');
for (var i = 0; i < spans.length; i++) {
spans[i].textContent = mod;
}
// Stop event so it doesn't trigger new click listener on document.
e.stopPropagation();
return false;
}
showEl.addEventListener('click', onClick.bind(this));
return showEl;
},
createConsoleButton_: function() {
var node = tr.b.instantiateTemplate('#console-btn-template', THIS_DOC);
var toggleEl = node.querySelector('.view-console-button');
function onClick(e) {
this.scriptingCtl_.toggleVisibility();
e.stopPropagation();
return false;
}
toggleEl.addEventListener('click', onClick.bind(this));
return toggleEl;
},
createMetadataButton_: function() {
var node = tr.b.instantiateTemplate('#metadata-btn-template', THIS_DOC);
var showEl = node.querySelector('.view-metadata-button');
var textEl = node.querySelector('.info-button-text');
var dlg = new tr.b.ui.Overlay();
dlg.title = 'Metadata for trace';
dlg.classList.add('view-metadata-overlay');
dlg.appendChild(node);
function onClick(e) {
dlg.visible = true;
var metadataStrings = [];
var model = this.model;
for (var data in model.metadata) {
var meta = model.metadata[data];
var name = JSON.stringify(meta.name);
var value = JSON.stringify(meta.value, undefined, ' ');
metadataStrings.push(name + ': ' + value);
}
textEl.textContent = metadataStrings.join('\n');
e.stopPropagation();
return false;
}
showEl.addEventListener('click', onClick.bind(this));
function updateVisibility() {
showEl.style.display =
(this.model && this.model.metadata.length) ? '' : 'none';
}
var updateVisibility_ = updateVisibility.bind(this);
updateVisibility_();
this.addEventListener('modelChange', updateVisibility_);
return showEl;
},
get leftControls() {
return this.leftControlsEl_;
},
get rightControls() {
return this.rightControlsEl_;
},
get collapsingControls() {
return this.collapsingControlsEl_;
},
get viewTitle() {
return this.titleEl_.textContent.substring(
this.titleEl_.textContent.length - 2);
},
set viewTitle(text) {
if (text === undefined) {
this.titleEl_.textContent = '';
this.titleEl_.hidden = true;
return;
}
this.titleEl_.hidden = false;
this.titleEl_.textContent = text;
},
get model() {
if (this.trackView_)
return this.trackView_.model;
return undefined;
},
set model(model) {
var modelInstanceChanged = model != this.model;
var modelValid = model && !model.bounds.isEmpty;
// Remove old trackView if the model has completely changed.
if (modelInstanceChanged) {
if (this.railScoreSpan_)
this.railScoreSpan_.railScore = undefined;
this.trackViewContainer_.textContent = '';
if (this.trackView_) {
this.trackView_.viewport.removeEventListener(
'change', this.onViewportChanged_);
this.trackView_.selectionController = undefined;
this.trackView_.detach();
this.trackView_ = undefined;
}
this.selectionController_.modelWillChange();
}
// Create new trackView if needed.
if (modelValid && !this.trackView_) {
this.trackView_ = new tr.c.TimelineTrackView(this);
this.trackView.selectionController = this.selectionController_;
this.trackView_.focusElement =
this.focusElement_ ? this.focusElement_ : this.parentElement;
this.trackViewContainer_.appendChild(this.trackView_);
this.trackView_.viewport.addEventListener(
'change', this.onViewportChanged_);
}
// Set the model.
if (modelValid) {
this.trackView_.model = model;
this.trackView_.viewport.showFlowEvents = this.showFlowEvents;
this.trackView_.viewport.highlightVSync = this.highlightVSync;
if (this.railScoreSpan_) {
var railScore = tr.e.rail.RAILScore.fromModel(model);
this.railScoreSpan_.railScore = railScore;
}
}
// Do things that are selection specific
if (modelInstanceChanged) {
tr.b.dispatchSimpleEvent(this, 'modelChange');
this.selectionController_.modelDidChange();
this.onViewportChanged_();
}
},
get selectionController() {
return this.selectionController_;
},
get trackView() {
return this.trackView_;
},
get settings() {
if (!this.settings_)
this.settings_ = new tr.b.Settings();
return this.settings_;
},
/**
* Sets the element whose focus state will determine whether
* to respond to keybaord input.
*/
set focusElement(value) {
this.focusElement_ = value;
if (this.trackView_)
this.trackView_.focusElement = value;
},
/**
* @return {Element} The element whose focused state determines
* whether to respond to keyboard inputs.
* Defaults to the parent element.
*/
get focusElement() {
if (this.focusElement_)
return this.focusElement_;
return this.parentElement;
},
get listenToKeys_() {
if (!tr.b.ui.isElementAttachedToDocument(this))
return;
if (document.activeElement.type === 'textarea')
return false;
if (this.sidePanelContainer_.activePanel &&
this.sidePanelContainer_.activePanel.listeningToKeys)
return false;
if (!this.focusElement_)
return true;
if (this.focusElement.tabIndex >= 0)
return document.activeElement == this.focusElement;
return true;
},
onKeyDown_: function(e) {
if (!this.listenToKeys_)
return;
},
onKeypress_: function(e) {
if (!this.listenToKeys_)
return;
// Shortcuts that *can* steal focus from the console and the filter text
// box.
switch (e.keyCode) {
case '`'.charCodeAt(0):
this.scriptingCtl_.toggleVisibility();
if (!this.scriptingCtl_.hasFocus)
this.focus();
e.preventDefault();
break;
}
if (this.scriptingCtl_.hasFocus)
return;
// Shortcuts that *can* steal focus from the filter text box.
switch (e.keyCode) {
case '/'.charCodeAt(0):
if (this.findCtl_.hasFocus)
this.focus();
else
this.findCtl_.focus();
e.preventDefault();
break;
case '?'.charCodeAt(0):
this.querySelector('.view-help-button').click();
e.preventDefault();
break;
}
if (this.findCtl_.hasFocus)
return;
// Shortcuts that *can't* steal focus from the filter text box or the
// console.
switch (e.keyCode) {
case 'v'.charCodeAt(0):
this.toggleHighlightVSync_();
e.preventDefault();
break;
}
},
onViewportChanged_: function(e) {
var spc = this.sidePanelContainer_;
if (!this.trackView_) {
spc.rangeOfInterest.reset();
return;
}
var vr = this.trackView_.viewport.interestRange.asRangeObject();
if (!spc.rangeOfInterest.equals(vr))
spc.rangeOfInterest = vr;
},
toggleHighlightVSync_: function() {
this.highlightVSyncCheckbox_.checked =
!this.highlightVSyncCheckbox_.checked;
},
setFindCtlText: function(string) {
this.findCtl_.setText(string);
}
};
return {
TimelineView: TimelineView
};
});
</script>