monaca-lib
Version:
Monaca cloud API bindings for JavaScript
1,312 lines (1,154 loc) • 56.5 kB
JavaScript
/*
* Copyright (C) 2012 Google Inc. All rights reserved.
* Copyright (C) 2012 Intel Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following disclaimer
* in the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Google Inc. nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/**
* @constructor
* @extends {WebInspector.Panel}
* @implements {WebInspector.TimelineModeViewDelegate}
* @implements {WebInspector.Searchable}
*/
WebInspector.TimelinePanel = function()
{
WebInspector.Panel.call(this, "timeline");
this.registerRequiredCSS("timeline/timelinePanel.css");
this.registerRequiredCSS("ui/filter.css");
this.element.addEventListener("contextmenu", this._contextMenu.bind(this), false);
this._detailsLinkifier = new WebInspector.Linkifier();
this._windowStartTime = 0;
this._windowEndTime = Infinity;
this._millisecondsToRecordAfterLoadEvent = 3000;
// Create model.
this._tracingModel = new WebInspector.TracingModel();
this._model = new WebInspector.TimelineModel(this._tracingModel, WebInspector.TimelineUIUtils.hiddenRecordsFilter());
this._model.addEventListener(WebInspector.TimelineModel.Events.RecordingStarted, this._onRecordingStarted, this);
this._model.addEventListener(WebInspector.TimelineModel.Events.RecordingStopped, this._onRecordingStopped, this);
this._model.addEventListener(WebInspector.TimelineModel.Events.RecordsCleared, this._onRecordsCleared, this);
this._model.addEventListener(WebInspector.TimelineModel.Events.RecordFilterChanged, this._refreshViews, this);
this._model.addEventListener(WebInspector.TimelineModel.Events.BufferUsage, this._onTracingBufferUsage, this);
this._model.addEventListener(WebInspector.TimelineModel.Events.RetrieveEventsProgress, this._onRetrieveEventsProgress, this);
this._categoryFilter = new WebInspector.TimelineCategoryFilter();
this._durationFilter = new WebInspector.TimelineIsLongFilter();
this._textFilter = new WebInspector.TimelineTextFilter();
this._model.addFilter(new WebInspector.TimelineRecordHiddenEmptyTypeFilter([
WebInspector.TimelineModel.RecordType.EventDispatch
]));
this._model.addFilter(WebInspector.TimelineUIUtils.hiddenRecordsFilter());
this._model.addFilter(this._categoryFilter);
this._model.addFilter(this._durationFilter);
this._model.addFilter(this._textFilter);
/** @type {!Array.<!WebInspector.TimelineModeView>} */
this._currentViews = [];
this._overviewModeSetting = WebInspector.settings.createSetting("timelineOverviewMode", WebInspector.TimelinePanel.OverviewMode.Frames);
this._flameChartEnabledSetting = WebInspector.settings.createSetting("timelineFlameChartEnabled", true);
this._createStatusBarItems();
var topPaneElement = this.element.createChild("div", "hbox");
topPaneElement.id = "timeline-overview-panel";
// Create top overview component.
this._overviewPane = new WebInspector.TimelineOverviewPane(this._model);
this._overviewPane.addEventListener(WebInspector.TimelineOverviewPane.Events.WindowChanged, this._onWindowChanged.bind(this));
this._overviewPane.show(topPaneElement);
this._createFileSelector();
this._registerShortcuts();
WebInspector.targetManager.addEventListener(WebInspector.TargetManager.Events.WillReloadPage, this._willReloadPage, this);
WebInspector.targetManager.addEventListener(WebInspector.TargetManager.Events.Load, this._loadEventFired, this);
// Create top level properties splitter.
this._detailsSplitView = new WebInspector.SplitView(false, true, "timelinePanelDetailsSplitViewState");
this._detailsSplitView.element.classList.add("timeline-details-split");
this._detailsView = new WebInspector.TimelineDetailsView(this._model);
this._detailsSplitView.installResizer(this._detailsView.headerElement());
this._detailsSplitView.setSidebarView(this._detailsView);
this._searchableView = new WebInspector.SearchableView(this);
this._searchableView.setMinimumSize(0, 25);
this._searchableView.element.classList.add("searchable-view");
this._detailsSplitView.setMainView(this._searchableView);
this._stackView = new WebInspector.StackView(false);
this._stackView.show(this._searchableView.element);
this._stackView.element.classList.add("timeline-view-stack");
this._onModeChanged();
this._detailsSplitView.show(this.element);
WebInspector.targetManager.addEventListener(WebInspector.TargetManager.Events.SuspendStateChanged, this._onSuspendStateChanged, this);
}
WebInspector.TimelinePanel.OverviewMode = {
Events: "Events",
Frames: "Frames"
};
/**
* @enum {string}
*/
WebInspector.TimelinePanel.DetailsTab = {
Details: "Details",
BottomUpChart: "BottomUpChart",
PaintProfiler: "PaintProfiler",
LayerViewer: "LayerViewer"
};
// Define row and header height, should be in sync with styles for timeline graphs.
WebInspector.TimelinePanel.rowHeight = 18;
WebInspector.TimelinePanel.headerHeight = 20;
WebInspector.TimelinePanel._aggregatedStatsKey = Symbol("aggregatedStats");
WebInspector.TimelinePanel.durationFilterPresetsMs = [0, 1, 15];
WebInspector.TimelinePanel.prototype = {
/**
* @override
* @return {?WebInspector.SearchableView}
*/
searchableView: function()
{
return this._searchableView;
},
wasShown: function()
{
if (!WebInspector.TimelinePanel._categoryStylesInitialized) {
WebInspector.TimelinePanel._categoryStylesInitialized = true;
var style = createElement("style");
var categories = WebInspector.TimelineUIUtils.categories();
style.textContent = Object.values(categories).map(WebInspector.TimelineUIUtils.createStyleRuleForCategory).join("\n");
this.element.ownerDocument.head.appendChild(style);
}
},
/**
* @return {number}
*/
windowStartTime: function()
{
if (this._windowStartTime)
return this._windowStartTime;
return this._model.minimumRecordTime();
},
/**
* @return {number}
*/
windowEndTime: function()
{
if (this._windowEndTime < Infinity)
return this._windowEndTime;
return this._model.maximumRecordTime() || Infinity;
},
/**
* @param {!WebInspector.Event} event
*/
_sidebarResized: function(event)
{
var width = /** @type {number} */ (event.data);
for (var i = 0; i < this._currentViews.length; ++i)
this._currentViews[i].setSidebarSize(width);
},
/**
* @param {!WebInspector.Event} event
*/
_onWindowChanged: function(event)
{
this._windowStartTime = event.data.startTime;
this._windowEndTime = event.data.endTime;
for (var i = 0; i < this._currentViews.length; ++i)
this._currentViews[i].setWindowTimes(this._windowStartTime, this._windowEndTime);
},
/**
* @override
* @param {number} windowStartTime
* @param {number} windowEndTime
*/
requestWindowTimes: function(windowStartTime, windowEndTime)
{
this._overviewPane.requestWindowTimes(windowStartTime, windowEndTime);
},
/**
* @return {!WebInspector.TimelineFrameModelBase}
*/
_frameModel: function()
{
if (!this._lazyFrameModel) {
var tracingFrameModel = new WebInspector.TracingTimelineFrameModel();
tracingFrameModel.addTraceEvents(this._model.target(), this._model.inspectedTargetEvents(), this._tracingModel.sessionId() || "");
this._lazyFrameModel = tracingFrameModel;
}
return this._lazyFrameModel;
},
/**
* @return {!WebInspector.TimelineView}
*/
_timelineView: function()
{
if (!this._lazyTimelineView)
this._lazyTimelineView = new WebInspector.TimelineView(this, this._model);
return this._lazyTimelineView;
},
/**
* @return {!WebInspector.View}
*/
_layersView: function()
{
if (this._lazyLayersView)
return this._lazyLayersView;
this._lazyLayersView = new WebInspector.TimelineLayersView();
this._lazyLayersView.setTimelineModelAndDelegate(this._model, this);
return this._lazyLayersView;
},
_paintProfilerView: function()
{
if (this._lazyPaintProfilerView)
return this._lazyPaintProfilerView;
this._lazyPaintProfilerView = new WebInspector.TimelinePaintProfilerView(/** @type {!WebInspector.TracingTimelineFrameModel} */(this._frameModel()));
return this._lazyPaintProfilerView;
},
/**
* @param {!WebInspector.TimelineModeView} modeView
*/
_addModeView: function(modeView)
{
modeView.setWindowTimes(this.windowStartTime(), this.windowEndTime());
modeView.refreshRecords(this._textFilter._regex);
this._stackView.appendView(modeView.view(), "timelinePanelTimelineStackSplitViewState");
modeView.view().addEventListener(WebInspector.SplitView.Events.SidebarSizeChanged, this._sidebarResized, this);
this._currentViews.push(modeView);
},
_removeAllModeViews: function()
{
for (var i = 0; i < this._currentViews.length; ++i) {
this._currentViews[i].removeEventListener(WebInspector.SplitView.Events.SidebarSizeChanged, this._sidebarResized, this);
this._currentViews[i].dispose();
}
this._currentViews = [];
this._stackView.detachChildViews();
},
/**
* @param {string} name
* @param {!WebInspector.Setting} setting
* @param {string} tooltip
* @return {!WebInspector.StatusBarItem}
*/
_createSettingCheckbox: function(name, setting, tooltip)
{
if (!this._recordingOptionUIControls)
this._recordingOptionUIControls = [];
var checkboxItem = new WebInspector.StatusBarCheckbox(name, tooltip, setting);
this._recordingOptionUIControls.push(checkboxItem);
return checkboxItem;
},
_createStatusBarItems: function()
{
this._panelToolbar = new WebInspector.StatusBar(this.element);
this.toggleTimelineButton = new WebInspector.StatusBarButton("Record timeline", "record-status-bar-item");
this.toggleTimelineButton.addEventListener("click", this._toggleTimelineButtonClicked, this);
this._panelToolbar.appendStatusBarItem(this.toggleTimelineButton);
this._updateToggleTimelineButton(false);
var clearButton = new WebInspector.StatusBarButton(WebInspector.UIString("Clear recording"), "clear-status-bar-item");
clearButton.addEventListener("click", this._onClearButtonClick, this);
this._panelToolbar.appendStatusBarItem(clearButton);
this._filterBar = this._createFilterBar();
this._panelToolbar.appendStatusBarItem(this._filterBar.filterButton());
var garbageCollectButton = new WebInspector.StatusBarButton(WebInspector.UIString("Collect garbage"), "garbage-collect-status-bar-item");
garbageCollectButton.addEventListener("click", this._garbageCollectButtonClicked, this);
this._panelToolbar.appendStatusBarItem(garbageCollectButton);
var framesToggleButton = new WebInspector.StatusBarButton(WebInspector.UIString("Frames view. (Activity split into frames)"), "histogram-status-bar-item");
framesToggleButton.setToggled(this._overviewModeSetting.get() === WebInspector.TimelinePanel.OverviewMode.Frames);
framesToggleButton.addEventListener("click", this._overviewModeChanged.bind(this, framesToggleButton));
this._panelToolbar.appendStatusBarItem(framesToggleButton);
this._flameChartToggleButton = new WebInspector.StatusBarButton(WebInspector.UIString("Flame chart view. (Use WASD or time selection to navigate)"), "flame-chart-status-bar-item");
this._flameChartToggleButton.setToggled(this._flameChartEnabledSetting.get());
this._flameChartToggleButton.addEventListener("click", this._flameChartEnabledChanged.bind(this));
this._panelToolbar.appendStatusBarItem(this._flameChartToggleButton);
this._captureCausesSetting = WebInspector.settings.createSetting("timelineCaptureCauses", true);
this._captureCausesSetting.addChangeListener(this._refreshViews, this);
this._panelToolbar.appendStatusBarItem(this._createSettingCheckbox(WebInspector.UIString("Causes"),
this._captureCausesSetting,
WebInspector.UIString("Capture causes (e.g., stack traces) for timeline events. (Has performance overhead)")));
this._enableJSSamplingSettingSetting = WebInspector.settings.createSetting("timelineEnableJSSampling", false);
this._panelToolbar.appendStatusBarItem(this._createSettingCheckbox(WebInspector.UIString("JS Profiler"),
this._enableJSSamplingSettingSetting,
WebInspector.UIString("Enable JavaScript sampling profiler. (Has performance overhead)")));
this._captureMemorySetting = WebInspector.settings.createSetting("timelineCaptureMemory", false);
this._panelToolbar.appendStatusBarItem(this._createSettingCheckbox(WebInspector.UIString("Memory"),
this._captureMemorySetting,
WebInspector.UIString("Capture memory information on every timeline event")));
this._captureMemorySetting.addChangeListener(this._onModeChanged, this);
this._captureLayersAndPicturesSetting = WebInspector.settings.createSetting("timelineCaptureLayersAndPictures", false);
this._panelToolbar.appendStatusBarItem(this._createSettingCheckbox(WebInspector.UIString("Paint"),
this._captureLayersAndPicturesSetting,
WebInspector.UIString("Capture graphics layer positions and painted pictures. (Has performance overhead)")));
this._progressStatusBarItem = new WebInspector.StatusBarItemWrapper(createElement("div"));
this._progressStatusBarItem.setVisible(false);
this._panelToolbar.appendStatusBarItem(this._progressStatusBarItem);
this._filtersContainer = this.element.createChild("div", "timeline-filters-header hidden");
this._filtersContainer.appendChild(this._filterBar.filtersElement());
this._filterBar.addEventListener(WebInspector.FilterBar.Events.FiltersToggled, this._onFiltersToggled, this);
this._filterBar.setName("timelinePanel");
},
/**
* @return {!WebInspector.FilterBar}
*/
_createFilterBar: function()
{
this._filterBar = new WebInspector.FilterBar();
this._filters = {};
this._filters._textFilterUI = new WebInspector.TextFilterUI();
this._filters._textFilterUI.addEventListener(WebInspector.FilterUI.Events.FilterChanged, this._textFilterChanged, this);
this._filterBar.addFilter(this._filters._textFilterUI);
var durationOptions = [];
for (var presetIndex = 0; presetIndex < WebInspector.TimelinePanel.durationFilterPresetsMs.length; ++presetIndex) {
var durationMs = WebInspector.TimelinePanel.durationFilterPresetsMs[presetIndex];
var durationOption = {};
if (!durationMs) {
durationOption.label = WebInspector.UIString("All");
durationOption.title = WebInspector.UIString("Show all records");
} else {
durationOption.label = WebInspector.UIString("\u2265 %dms", durationMs);
durationOption.title = WebInspector.UIString("Hide records shorter than %dms", durationMs);
}
durationOption.value = durationMs;
durationOptions.push(durationOption);
}
this._filters._durationFilterUI = new WebInspector.ComboBoxFilterUI(durationOptions);
this._filters._durationFilterUI.addEventListener(WebInspector.FilterUI.Events.FilterChanged, this._durationFilterChanged, this);
this._filterBar.addFilter(this._filters._durationFilterUI);
this._filters._categoryFiltersUI = {};
var categories = WebInspector.TimelineUIUtils.categories();
for (var categoryName in categories) {
var category = categories[categoryName];
if (category.overviewStripGroupIndex < 0)
continue;
var filter = new WebInspector.CheckboxFilterUI(category.name, category.title);
this._filters._categoryFiltersUI[category.name] = filter;
filter.addEventListener(WebInspector.FilterUI.Events.FilterChanged, this._categoriesFilterChanged.bind(this, categoryName), this);
this._filterBar.addFilter(filter);
}
return this._filterBar;
},
_textFilterChanged: function(event)
{
var searchQuery = this._filters._textFilterUI.value();
this.searchCanceled();
this._textFilter.setRegex(searchQuery ? createPlainTextSearchRegex(searchQuery, "i") : null);
},
_durationFilterChanged: function()
{
var duration = this._filters._durationFilterUI.value();
var minimumRecordDuration = parseInt(duration, 10);
this._durationFilter.setMinimumRecordDuration(minimumRecordDuration);
},
_categoriesFilterChanged: function(name, event)
{
var categories = WebInspector.TimelineUIUtils.categories();
categories[name].hidden = !this._filters._categoryFiltersUI[name].checked();
this._categoryFilter.notifyFilterChanged();
},
_onFiltersToggled: function(event)
{
var toggled = /** @type {boolean} */ (event.data);
this._filtersContainer.classList.toggle("hidden", !toggled);
this.doResize();
},
/**
* @return {?WebInspector.ProgressIndicator}
*/
_prepareToLoadTimeline: function()
{
if (this._operationInProgress)
return null;
if (this._recordingInProgress()) {
this._updateToggleTimelineButton(false);
this._stopRecording();
}
/**
* @this {!WebInspector.TimelinePanel}
*/
function finishLoading()
{
this._setOperationInProgress(null);
this._updateToggleTimelineButton(false);
this._hideProgressPane();
}
var progressIndicator = new WebInspector.ProgressIndicator();
progressIndicator.addEventListener(WebInspector.Progress.Events.Done, finishLoading.bind(this));
this._setOperationInProgress(progressIndicator);
return progressIndicator;
},
/**
* @param {?WebInspector.ProgressIndicator} indicator
*/
_setOperationInProgress: function(indicator)
{
this._operationInProgress = !!indicator;
this._panelToolbar.setEnabled(!this._operationInProgress);
this._progressStatusBarItem.setVisible(this._operationInProgress);
this._progressStatusBarItem.element.removeChildren();
if (indicator)
this._progressStatusBarItem.element.appendChild(indicator.element);
},
_registerShortcuts: function()
{
this.registerShortcuts(WebInspector.ShortcutsScreen.TimelinePanelShortcuts.StartStopRecording, this._toggleTimelineButtonClicked.bind(this));
this.registerShortcuts(WebInspector.ShortcutsScreen.TimelinePanelShortcuts.SaveToFile, this._saveToFile.bind(this));
this.registerShortcuts(WebInspector.ShortcutsScreen.TimelinePanelShortcuts.LoadFromFile, this._selectFileToLoad.bind(this));
},
_createFileSelector: function()
{
if (this._fileSelectorElement)
this._fileSelectorElement.remove();
this._fileSelectorElement = WebInspector.createFileSelectorElement(this._loadFromFile.bind(this));
this.element.appendChild(this._fileSelectorElement);
},
_contextMenu: function(event)
{
var contextMenu = new WebInspector.ContextMenu(event);
contextMenu.appendItem(WebInspector.UIString.capitalize("Save Timeline ^data\u2026"), this._saveToFile.bind(this), this._operationInProgress);
contextMenu.appendItem(WebInspector.UIString.capitalize("Load Timeline ^data\u2026"), this._selectFileToLoad.bind(this), this._operationInProgress);
contextMenu.show();
},
/**
* @return {boolean}
*/
_saveToFile: function()
{
if (this._operationInProgress)
return true;
this._model.saveToFile();
return true;
},
/**
* @return {boolean}
*/
_selectFileToLoad: function() {
this._fileSelectorElement.click();
return true;
},
/**
* @param {!File} file
*/
_loadFromFile: function(file)
{
var progressIndicator = this._prepareToLoadTimeline();
if (!progressIndicator)
return;
this._model.loadFromFile(file, progressIndicator);
this._createFileSelector();
},
_refreshViews: function()
{
for (var i = 0; i < this._currentViews.length; ++i) {
var view = this._currentViews[i];
view.refreshRecords(this._textFilter._regex);
}
this._updateSelectionDetails();
},
/**
* @param {!WebInspector.StatusBarButton} button
*/
_overviewModeChanged: function(button)
{
var oldMode = this._overviewModeSetting.get();
if (oldMode === WebInspector.TimelinePanel.OverviewMode.Events) {
this._overviewModeSetting.set(WebInspector.TimelinePanel.OverviewMode.Frames);
button.setToggled(true);
} else {
this._overviewModeSetting.set(WebInspector.TimelinePanel.OverviewMode.Events);
button.setToggled(false);
}
this._onModeChanged();
},
_flameChartEnabledChanged: function()
{
var oldValue = this._flameChartEnabledSetting.get();
var newValue = !oldValue;
this._flameChartEnabledSetting.set(newValue);
this._flameChartToggleButton.setToggled(newValue);
this._onModeChanged();
},
_onModeChanged: function()
{
this._stackView.detach();
var isFrameMode = this._overviewModeSetting.get() === WebInspector.TimelinePanel.OverviewMode.Frames;
this._removeAllModeViews();
this._overviewControls = [];
if (isFrameMode)
this._overviewControls.push(new WebInspector.TimelineFrameOverview(this._model, this._frameModel()));
else
this._overviewControls.push(new WebInspector.TimelineEventOverview(this._model));
if (this._flameChartEnabledSetting.get()) {
this._filterBar.filterButton().setEnabled(false);
this._filtersContainer.classList.toggle("hidden", true);
var dataProvider = new WebInspector.TimelineFlameChartDataProvider(this._model, this._frameModel());
this._addModeView(new WebInspector.TimelineFlameChart(this, this._model, dataProvider));
} else {
this._filterBar.filterButton().setEnabled(true);
this._filtersContainer.classList.toggle("hidden", !this._filterBar.filtersToggled());
this._addModeView(this._timelineView());
}
if (this._captureMemorySetting.get()) {
if (!isFrameMode) // Frame mode skews time, don't render aux overviews.
this._overviewControls.push(new WebInspector.TimelineMemoryOverview(this._model));
this._addModeView(new WebInspector.MemoryCountersGraph(this, this._model));
}
var mainTarget = WebInspector.targetManager.mainTarget();
if (this._lazyTimelineView)
this._lazyTimelineView.setFrameModel(isFrameMode ? this._frameModel() : null);
this._overviewPane.setOverviewControls(this._overviewControls);
this.doResize();
this._selection = null;
this._updateSelectionDetails();
this._stackView.show(this._searchableView.element);
},
/**
* @param {boolean} enabled
*/
_setUIControlsEnabled: function(enabled) {
/**
* @param {!WebInspector.StatusBarButton} statusBarItem
*/
function handler(statusBarItem)
{
statusBarItem.setEnabled(enabled);
}
this._recordingOptionUIControls.forEach(handler);
},
/**
* @param {boolean} userInitiated
*/
_startRecording: function(userInitiated)
{
this._autoRecordGeneration = userInitiated ? null : {};
var enableJSSampling = this._enableJSSamplingSettingSetting && this._enableJSSamplingSettingSetting.get();
this._model.startRecording(this._captureCausesSetting.get(), enableJSSampling, this._captureMemorySetting.get(), this._captureLayersAndPicturesSetting && this._captureLayersAndPicturesSetting.get());
if (this._lazyFrameModel)
this._lazyFrameModel.setMergeRecords(false);
for (var i = 0; i < this._overviewControls.length; ++i)
this._overviewControls[i].timelineStarted();
if (userInitiated)
WebInspector.userMetrics.TimelineStarted.record();
this._setUIControlsEnabled(false);
},
_stopRecording: function()
{
this._stopPending = true;
this._updateToggleTimelineButton(false);
this._autoRecordGeneration = null;
this._model.stopRecording();
if (this._progressElement)
this._updateProgress(WebInspector.UIString("Retrieving events\u2026"));
for (var i = 0; i < this._overviewControls.length; ++i)
this._overviewControls[i].timelineStopped();
this._setUIControlsEnabled(true);
},
_onSuspendStateChanged: function()
{
this._updateToggleTimelineButton(this.toggleTimelineButton.toggled());
},
/**
* @param {boolean} toggled
*/
_updateToggleTimelineButton: function(toggled)
{
this.toggleTimelineButton.setToggled(toggled);
if (toggled) {
this.toggleTimelineButton.setTitle(WebInspector.UIString("Stop"));
this.toggleTimelineButton.setEnabled(true);
} else if (this._stopPending) {
this.toggleTimelineButton.setTitle(WebInspector.UIString("Stop pending"));
this.toggleTimelineButton.setEnabled(false);
} else if (WebInspector.targetManager.allTargetsSuspended()) {
this.toggleTimelineButton.setTitle(WebInspector.anotherProfilerActiveLabel());
this.toggleTimelineButton.setEnabled(false);
} else {
this.toggleTimelineButton.setTitle(WebInspector.UIString("Record"));
this.toggleTimelineButton.setEnabled(true);
}
},
/**
* @return {boolean}
*/
_toggleTimelineButtonClicked: function()
{
if (!this.toggleTimelineButton.enabled())
return true;
if (this._operationInProgress)
return true;
if (this._recordingInProgress())
this._stopRecording();
else
this._startRecording(true);
return true;
},
_garbageCollectButtonClicked: function()
{
var targets = WebInspector.targetManager.targets();
for (var i = 0; i < targets.length; ++i)
targets[i].heapProfilerAgent().collectGarbage();
},
_onClearButtonClick: function()
{
this._tracingModel.reset();
this._model.reset();
},
_onRecordsCleared: function()
{
this.requestWindowTimes(0, Infinity);
delete this._selection;
if (this._lazyFrameModel)
this._lazyFrameModel.reset();
for (var i = 0; i < this._currentViews.length; ++i)
this._currentViews[i].reset();
for (var i = 0; i < this._overviewControls.length; ++i)
this._overviewControls[i].reset();
this._selection = null;
this._updateSelectionDetails();
},
/**
* @param {!WebInspector.Event} event
*/
_onRecordingStarted: function(event)
{
this._updateToggleTimelineButton(true);
if (event.data && event.data.fromFile)
this._updateProgress(WebInspector.UIString("Loading from file..."));
else
this._updateProgress(WebInspector.UIString("%d events collected", 0));
},
_recordingInProgress: function()
{
return this.toggleTimelineButton.toggled();
},
/**
* @param {!WebInspector.Event} event
*/
_onTracingBufferUsage: function(event)
{
var usage = /** @type {number} */ (event.data);
this._updateProgress(WebInspector.UIString("Buffer usage %d%", Math.round(usage * 100)));
},
/**
* @param {!WebInspector.Event} event
*/
_onRetrieveEventsProgress: function(event)
{
var progress = /** @type {number} */ (event.data);
this._updateProgress(WebInspector.UIString("Retrieving events\u2026 %d%", Math.round(progress * 100)));
},
/**
* @param {string} progressMessage
*/
_updateProgress: function(progressMessage)
{
if (!this._progressElement)
this._showProgressPane();
this._progressElement.textContent = progressMessage;
},
_showProgressPane: function()
{
this._hideProgressPane();
this._progressElement = this._searchableView.element.createChild("div", "timeline-progress-pane fill");
},
_hideProgressPane: function()
{
if (this._progressElement)
this._progressElement.remove();
delete this._progressElement;
},
_onRecordingStopped: function()
{
this._stopPending = false;
this._updateToggleTimelineButton(false);
if (this._lazyFrameModel) {
this._lazyFrameModel.reset();
this._lazyFrameModel.addTraceEvents(this._model.target(), this._model.inspectedTargetEvents(), this._tracingModel.sessionId());
}
this.requestWindowTimes(this._model.minimumRecordTime(), this._model.maximumRecordTime());
this._refreshViews();
this._hideProgressPane();
this._overviewPane.update();
this._updateSearchHighlight(false, true);
},
/**
* @param {!WebInspector.Event} event
*/
_willReloadPage: function(event)
{
if (this._operationInProgress || this._recordingInProgress() || !this.isShowing())
return;
this._startRecording(false);
},
/**
* @param {!WebInspector.Event} event
*/
_loadEventFired: function(event)
{
if (!this._recordingInProgress() || !this._autoRecordGeneration)
return;
setTimeout(stopRecordingOnReload.bind(this, this._autoRecordGeneration), this._millisecondsToRecordAfterLoadEvent);
/**
* @this {WebInspector.TimelinePanel}
* @param {!Object} recordGeneration
*/
function stopRecordingOnReload(recordGeneration)
{
// Check if we're still in the same recording session.
if (this._autoRecordGeneration !== recordGeneration)
return;
this._stopRecording();
}
},
// WebInspector.Searchable implementation
/**
* @override
*/
jumpToNextSearchResult: function()
{
if (!this._searchResults || !this._searchResults.length)
return;
var index = this._selectedSearchResult ? this._searchResults.indexOf(this._selectedSearchResult) : -1;
this._jumpToSearchResult(index + 1);
},
/**
* @override
*/
jumpToPreviousSearchResult: function()
{
if (!this._searchResults || !this._searchResults.length)
return;
var index = this._selectedSearchResult ? this._searchResults.indexOf(this._selectedSearchResult) : 0;
this._jumpToSearchResult(index - 1);
},
/**
* @override
* @return {boolean}
*/
supportsCaseSensitiveSearch: function()
{
return false;
},
/**
* @override
* @return {boolean}
*/
supportsRegexSearch: function()
{
return false;
},
_jumpToSearchResult: function(index)
{
this._selectSearchResult((index + this._searchResults.length) % this._searchResults.length);
this._currentViews[0].highlightSearchResult(this._selectedSearchResult, this._searchRegex, true);
},
_selectSearchResult: function(index)
{
this._selectedSearchResult = this._searchResults[index];
this._searchableView.updateCurrentMatchIndex(index);
},
_clearHighlight: function()
{
this._currentViews[0].highlightSearchResult(null);
},
/**
* @param {boolean} revealRecord
* @param {boolean} shouldJump
* @param {boolean=} jumpBackwards
*/
_updateSearchHighlight: function(revealRecord, shouldJump, jumpBackwards)
{
if (!this._textFilter.isEmpty() || !this._searchRegex) {
this._clearHighlight();
return;
}
if (!this._searchResults)
this._updateSearchResults(shouldJump, jumpBackwards);
this._currentViews[0].highlightSearchResult(this._selectedSearchResult, this._searchRegex, revealRecord);
},
/**
* @param {boolean} shouldJump
* @param {boolean=} jumpBackwards
*/
_updateSearchResults: function(shouldJump, jumpBackwards)
{
var searchRegExp = this._searchRegex;
if (!searchRegExp)
return;
var matches = [];
/**
* @param {!WebInspector.TimelineModel.Record} record
* @this {WebInspector.TimelinePanel}
*/
function processRecord(record)
{
if (record.endTime() < this._windowStartTime ||
record.startTime() > this._windowEndTime)
return;
if (WebInspector.TimelineUIUtils.testContentMatching(record, searchRegExp))
matches.push(record);
}
this._model.forAllFilteredRecords(processRecord.bind(this));
var matchesCount = matches.length;
if (matchesCount) {
this._searchResults = matches;
this._searchableView.updateSearchMatchesCount(matchesCount);
var selectedIndex = matches.indexOf(this._selectedSearchResult);
if (shouldJump && selectedIndex === -1)
selectedIndex = jumpBackwards ? this._searchResults.length - 1 : 0;
this._selectSearchResult(selectedIndex);
} else {
this._searchableView.updateSearchMatchesCount(0);
delete this._selectedSearchResult;
}
},
/**
* @override
*/
searchCanceled: function()
{
this._clearHighlight();
delete this._searchResults;
delete this._selectedSearchResult;
delete this._searchRegex;
},
/**
* @override
* @param {!WebInspector.SearchableView.SearchConfig} searchConfig
* @param {boolean} shouldJump
* @param {boolean=} jumpBackwards
*/
performSearch: function(searchConfig, shouldJump, jumpBackwards)
{
var query = searchConfig.query;
this._searchRegex = createPlainTextSearchRegex(query, "i");
delete this._searchResults;
this._updateSearchHighlight(true, shouldJump, jumpBackwards);
},
_updateSelectionDetails: function()
{
if (!this._selection)
this._selection = WebInspector.TimelineSelection.fromRange(this._windowStartTime, this._windowEndTime);
switch (this._selection.type()) {
case WebInspector.TimelineSelection.Type.Record:
var record = /** @type {!WebInspector.TimelineModel.Record} */ (this._selection.object());
var event = record.traceEvent();
WebInspector.TimelineUIUtils.buildTraceEventDetails(event, this._model, this._detailsLinkifier, this._appendDetailsTabsForTraceEventAndShowDetails.bind(this, event));
break;
case WebInspector.TimelineSelection.Type.TraceEvent:
var event = /** @type {!WebInspector.TracingModel.Event} */ (this._selection.object());
WebInspector.TimelineUIUtils.buildTraceEventDetails(event, this._model, this._detailsLinkifier, this._appendDetailsTabsForTraceEventAndShowDetails.bind(this, event));
break;
case WebInspector.TimelineSelection.Type.Frame:
var frame = /** @type {!WebInspector.TimelineFrame} */ (this._selection.object());
this.showInDetails(WebInspector.TimelineUIUtils.generateDetailsContentForFrame(this._lazyFrameModel, frame));
if (frame.layerTree) {
var layersView = this._layersView();
layersView.showLayerTree(frame.layerTree, frame.paints);
this._detailsView.appendTab(WebInspector.TimelinePanel.DetailsTab.LayerViewer, WebInspector.UIString("Layers"), layersView);
}
break;
case WebInspector.TimelineSelection.Type.Range:
this._updateSelectedRangeStats(this._selection._startTime, this._selection._endTime);
break;
}
this._detailsView.updateContents(this._selection);
},
/**
* @param {!WebInspector.TracingModel.Event} event
* @param {!Node} content
*/
_appendDetailsTabsForTraceEventAndShowDetails: function(event, content)
{
this.showInDetails(content);
if (event.name === WebInspector.TimelineModel.RecordType.Paint || event.name === WebInspector.TimelineModel.RecordType.RasterTask)
this._showEventInPaintProfiler(event);
},
/**
* @param {!WebInspector.TracingModel.Event} event
* @param {boolean=} isCloseable
*/
_showEventInPaintProfiler: function(event, isCloseable)
{
var target = this._model.target();
if (!target)
return;
var paintProfilerView = this._paintProfilerView();
var hasProfileData = paintProfilerView.setEvent(target, event);
if (!hasProfileData)
return;
if (!this._detailsView.hasTab(WebInspector.TimelinePanel.DetailsTab.PaintProfiler))
this._detailsView.appendTab(WebInspector.TimelinePanel.DetailsTab.PaintProfiler, WebInspector.UIString("Paint Profiler"), paintProfilerView, undefined, undefined, isCloseable);
},
/**
* @param {!WebInspector.TimelineModel.Record} record
* @param {number} startTime
* @param {number} endTime
* @param {!Object} aggregatedStats
*/
_collectAggregatedStatsForRecord: function(record, startTime, endTime, aggregatedStats)
{
var records = [];
if (!record.endTime() || record.endTime() < startTime || record.startTime() > endTime)
return;
var childrenTime = 0;
var children = record.children() || [];
for (var i = 0; i < children.length; ++i) {
var child = children[i];
if (!child.endTime() || child.endTime() < startTime || child.startTime() > endTime)
continue;
childrenTime += Math.min(endTime, child.endTime()) - Math.max(startTime, child.startTime());
this._collectAggregatedStatsForRecord(child, startTime, endTime, aggregatedStats);
}
var categoryName = WebInspector.TimelineUIUtils.categoryForRecord(record).name;
var ownTime = Math.min(endTime, record.endTime()) - Math.max(startTime, record.startTime()) - childrenTime;
aggregatedStats[categoryName] = (aggregatedStats[categoryName] || 0) + ownTime;
},
/**
* @param {number} startTime
* @param {number} endTime
*/
_updateSelectedRangeStats: function(startTime, endTime)
{
// Return early in case 0 selection window.
if (startTime < 0)
return;
var aggregatedStats = {};
/**
* @param {number} value
* @param {!WebInspector.TimelineModel.Record} task
* @return {number}
*/
function compareEndTime(value, task)
{
return value < task.endTime() ? -1 : 1;
}
var mainThreadTasks = this._model.mainThreadTasks();
var taskIndex = insertionIndexForObjectInListSortedByFunction(startTime, mainThreadTasks, compareEndTime);
for (; taskIndex < mainThreadTasks.length; ++taskIndex) {
var task = mainThreadTasks[taskIndex];
if (task.startTime() > endTime)
break;
if (task.startTime() > startTime && task.endTime() < endTime) {
// cache stats for top-level entries that fit the range entirely.
var taskStats = task[WebInspector.TimelinePanel._aggregatedStatsKey];
if (!taskStats) {
taskStats = {};
this._collectAggregatedStatsForRecord(task, startTime, endTime, taskStats);
task[WebInspector.TimelinePanel._aggregatedStatsKey] = taskStats;
}
for (var key in taskStats)
aggregatedStats[key] = (aggregatedStats[key] || 0) + taskStats[key];
continue;
}
this._collectAggregatedStatsForRecord(task, startTime, endTime, aggregatedStats);
}
var aggregatedTotal = 0;
for (var categoryName in aggregatedStats)
aggregatedTotal += aggregatedStats[categoryName];
aggregatedStats["idle"] = Math.max(0, endTime - startTime - aggregatedTotal);
var contentHelper = new WebInspector.TimelineDetailsContentHelper(null, null, true);
var pieChart = WebInspector.TimelineUIUtils.generatePieChart(aggregatedStats);
var startOffset = startTime - this._model.minimumRecordTime();
var endOffset = endTime - this._model.minimumRecordTime();
contentHelper.appendTextRow(WebInspector.UIString("Range"), WebInspector.UIString("%s \u2013 %s", Number.millisToString(startOffset), Number.millisToString(endOffset)));
contentHelper.appendElementRow(WebInspector.UIString("Aggregated Time"), pieChart);
this.showInDetails(contentHelper.element);
},
/**
* @override
* @param {?WebInspector.TimelineSelection} selection
* @param {!WebInspector.TimelinePanel.DetailsTab=} preferredTab
*/
select: function(selection, preferredTab)
{
this._detailsLinkifier.reset();
this._selection = selection;
if (preferredTab)
this._detailsView.setPreferredTab(preferredTab);
for (var i = 0; i < this._currentViews.length; ++i) {
var view = this._currentViews[i];
view.setSelection(selection);
}
this._updateSelectionDetails();
},
/**
* @override
* @param {!WebInspector.TimelineModel.Record} record
*/
showNestedRecordDetails: function(record)
{
var event = record.traceEvent();
this._showEventInPaintProfiler(event, true);
this._detailsView.selectTab(WebInspector.TimelinePanel.DetailsTab.PaintProfiler, true);
},
/**
* @override
* @param {!Node} node
*/
showInDetails: function(node)
{
this._detailsView.setContent(node);
},
__proto__: WebInspector.Panel.prototype
}
/**
* @constructor
* @extends {WebInspector.TabbedPane}
* @param {!WebInspector.TimelineModel} timelineModel
*/
WebInspector.TimelineDetailsView = function(timelineModel)
{
WebInspector.TabbedPane.call(this);
this.element.classList.add("timeline-details");
this._defaultDetailsView = new WebInspector.VBox();
this._defaultDetailsView.element.classList.add("timeline-details-view");
this._defaultDetailsContentElement = this._defaultDetailsView.element.createChild("div", "timeline-details-view-body vbox");
this.appendTab(WebInspector.TimelinePanel.DetailsTab.Details, WebInspector.UIString("Summary"), this._defaultDetailsView);
this.setPreferredTab(WebInspector.TimelinePanel.DetailsTab.Details);
if (Runtime.experiments.isEnabled("timelineDetailsChart")) {
this._heavyChartView = new WebInspector.TimelineDetailsView.BottomUpChartView(timelineModel);
this.appendTab(WebInspector.TimelinePanel.DetailsTab.BottomUpChart, WebInspector.UIString("Costly Functions"), this._heavyChartView);
}
this._staticTabs = [
WebInspector.TimelinePanel.DetailsTab.Details,
WebInspector.TimelinePanel.DetailsTab.BottomUpChart
];
this.addEventListener(WebInspector.TabbedPane.EventTypes.TabSelected, this._tabSelected, this);
}
WebInspector.TimelineDetailsView.prototype = {
/**
* @param {!Node} node
*/
setContent: function(node)
{
var allTabs = this.allTabs();
for (var i = 0; i < allTabs.length; ++i) {
var tabId = allTabs[i].id;
if (this._staticTabs.indexOf(tabId) !== -1)
this.closeTab(tabId);
}
this._defaultDetailsContentElement.removeChildren();
this._defaultDetailsContentElement.appendChild(node);
},
/**
* @param {!WebInspector.TimelineSelection} selection
*/
updateContents: function(selection)
{
this._selection = selection;
if (this.selectedTabId === WebInspector.TimelinePanel.DetailsTab.BottomUpChart && this._heavyChartView)
this._heavyChartView.updateContents(selection);
},
/**
* @override
* @param {string} id
* @param {string} tabTitle
* @param {!WebInspector.View} view
* @param {string=} tabTooltip
* @param {boolean=} userGesture
* @param {boolean=} isCloseable
*/
appendTab: function(id, tabTitle, view, tabTooltip, userGesture, isCloseable)
{
WebInspector.TabbedPane.prototype.appendTab.call(this, id, tabTitle, view, tabTooltip, userGesture, isCloseable);
if (this._preferredTabId !== this.selectedTabId)
this.selectTab(id);
},
/**
* @param {string} tabId
*/
setPreferredTab: function(tabId)
{
this._preferredTabId = tabId;
},
/**
* @param {!WebInspector.Event} event
*/
_tabSelected: function(event)
{
if (!event.data.isUserGesture)
return;
this.setPreferredTab(event.data.tabId);
this.updateContents(this._selection);
},
__proto__: WebInspector.TabbedPane.prototype
}
/**
* @constructor
* @implements {WebInspector.TimelineModeViewDelegate}
* @extends {WebInspector.VBox}
*/
WebInspector.TimelineDetailsView.BottomUpChartView = function(model) {
WebInspector.VBox.call(this);
this._model = model;
}
WebInspector.TimelineDetailsView.BottomUpChartView.prototype = {
_init: function()
{
if (this._heavyChart)
return;
this._dataProvider = new WebInspector.TimelineFlameChartBottomUpDataProvider(this._model);
this._heavyChart = new WebInspector.TimelineFlameChart(this, this._model, this._dataProvider);
this._heavyChart.show(this.element);
},
/**
* @param {!WebInspector.TimelineSelection} selection
*/
updateContents: function(selection)
{
this._init();
this._heavyChart.reset();
this._dataProvider.setWindowTimes(selection._startTime, selection._endTime);
this._dataProvider.timelineData();
this._heavyChart.setWindowTimes(0, this._dataProvider.totalTime());
},
/**
* @override
* @param {number} startTime
* @param {number} endTime
*/
requestWindowTimes: function(startTime, endTime)
{
this._heavyChart.setWindowTimes(startTime, endTime);
},
/**
* @override
* @param {?WebInspector.TimelineSelection} selection
* @param {!WebInspector.TimelinePanel.DetailsTab=} preferredTab
*/
select: function(selection, preferredTab) {},
/**
* @override
* @param {!WebInspector.TimelineModel.Record} record
*/
showNestedRecordDetails: function(record) {},
/**
* @override
* @param {!Node} node
*/
showInDetails: function(node) {},
__proto__: WebInspector.VBox.prototype
}
/**
* @constructor
*/
WebInspector.TimelineSelection = function()
{
}
/**
* @enum {string}
*/
WebInspector.TimelineSe