Web Inspector: Add back support for a heavy / bottom up profile view
https://bugs.webkit.org/show_bug.cgi?id=140578
<rdar://problem/19506794>

Reviewed by Timothy Hatcher.

* Localizations/en.lproj/localizedStrings.js:
* UserInterface/Main.html:
New strings and resources.

* UserInterface/Base/Utilities.js:
(Number.secondsToMillisecondsString):
Helper for providing a consistent milliseconds string used in profiles.

* UserInterface/Controllers/TimelineManager.js:
(WebInspector.TimelineManager.prototype.scriptProfilerTrackingCompleted):
The calling context tree should be stored on a Recording, not on the global
TimelineManager. Also create two trees, one top down and one bottom up.

* UserInterface/Models/CallingContextTree.js:
(WebInspector.CallingContextTree):
(WebInspector.CallingContextTree.prototype.get type):
(WebInspector.CallingContextTree.prototype.get totalExecutionTime):
(WebInspector.CallingContextTree.prototype.reset):
(WebInspector.CallingContextTree.prototype.numberOfSamplesInTimeRange):
(WebInspector.CallingContextTree.prototype.increaseExecutionTime):
Give a CallingContextTree a type (TopDown / BottomUp) and some getters.

(WebInspector.CallingContextTree.prototype.updateTreeWithStackTrace):
Build a bottom up or top down tree from samples.

(WebInspector.CallingContextTree.prototype.forEachChild):
Allow iterating from the root.

(WebInspector.CCTNode):
(WebInspector.CCTNode.prototype.hasChildrenInTimeRange):
(WebInspector.CCTNode.prototype.numberOfLeafTimestamps):
(WebInspector.CCTNode.prototype.addTimestampAndExpressionLocation):
(WebInspector.CCTNode.prototype.equals):
(WebInspector.CCTNode.prototype.hasChildren): Deleted.
Give a CCTNode a list of leaf timestamps alongside the list of all timestamps.
Leaf timestamps will count as "self time" in a profile view.

* UserInterface/Models/SourceCodeLocation.js:
(WebInspector.SourceCodeLocation.prototype._locationString):
Nobody was using "ColumnStyle.Hidden" so repurpose it to be even simpler.

* UserInterface/Models/TimelineRecording.js:
(WebInspector.TimelineRecording):
(WebInspector.TimelineRecording.prototype.get topDownCallingContextTree):
(WebInspector.TimelineRecording.prototype.get bottomUpCallingContextTree):
(WebInspector.TimelineRecording.prototype.reset):
Store the two types of calling context trees and allow reseting them.

* UserInterface/Protocol/InspectorFrontendAPI.js:
(InspectorFrontendAPI.contextMenuItemSelected):
Helper for debugging uncaught exceptions in context menus.

* UserInterface/Views/ContentView.js:
(WebInspector.ContentView.createFromRepresentedObject):
(WebInspector.ContentView.isViewable):
A ScriptTimeline now has a cluster view.
A CallingContextTree now has a ProfileView.

* UserInterface/Views/DataGrid.js:
(WebInspector.DataGrid.prototype.insertChild):
(WebInspector.DataGrid.prototype._contextMenuInDataTable):
(WebInspector.DataGridNode.prototype.refreshRecursively):
(WebInspector.DataGridNode.prototype.elementWithColumnIdentifier):
(WebInspector.DataGridNode.prototype.forEachImmediateChild):
(WebInspector.DataGridNode.prototype.forEachChildInSubtree):
(WebInspector.DataGridNode.prototype.isInSubtreeOfNode):
Provide some helpers for iterating DataGridNodes, useful when the
actual DataGrid comes from a DataGridTree.

(WebInspector.DataGridNode.prototype.select):
(WebInspector.DataGridNode.prototype.deselect):
The indent width of DataGridNodes was not getting reset when a
node was removed and re-added to a tree due to a cached padding.

(WebInspector.DataGridNode.prototype.appendContextMenuItems):
Allow DataGridNodes to provide context menu items by overriding this method.

* UserInterface/Views/PathComponentIcons.css:
(.function-icon .icon):
(.native-icon .icon):
(.program-icon .icon):
Icons for profile nodes in path components.

* UserInterface/Views/ProfileDataGridNode.js: Added.
(WebInspector.ProfileDataGridNode):
(WebInspector.ProfileDataGridNode.prototype.get node):
(WebInspector.ProfileDataGridNode.prototype.displayName):
(WebInspector.ProfileDataGridNode.prototype.iconClassName):
(WebInspector.ProfileDataGridNode.prototype.get data):
(WebInspector.ProfileDataGridNode.prototype.createCellContent):
(WebInspector.ProfileDataGridNode.prototype.sort):
(WebInspector.ProfileDataGridNode.prototype.refresh):
(WebInspector.ProfileDataGridNode.prototype.appendContextMenuItems):
(WebInspector.ProfileDataGridNode.prototype._updateChildrenForModifiers):
(WebInspector.ProfileDataGridNode.prototype._recalculateData):
(WebInspector.ProfileDataGridNode.prototype._totalTimeContent):
(WebInspector.ProfileDataGridNode.prototype._displayContent):
(WebInspector.ProfileDataGridNode.prototype._populate):
CCTNode DataGridNode. A row in the ProfileDataGridTree. Handles
tree modifiers like charge to caller.

* UserInterface/Views/ProfileDataGridTree.js: Added.
(WebInspector.ProfileDataGridTree):
(WebInspector.ProfileDataGridTree.buildSortComparator):
(WebInspector.ProfileDataGridTree.prototype.get callingContextTree):
(WebInspector.ProfileDataGridTree.prototype.get sampleInterval):
(WebInspector.ProfileDataGridTree.prototype.get focusNodes):
(WebInspector.ProfileDataGridTree.prototype.get currentFocusNode):
(WebInspector.ProfileDataGridTree.prototype.get modifiers):
(WebInspector.ProfileDataGridTree.prototype.get startTime):
(WebInspector.ProfileDataGridTree.prototype.get endTime):
(WebInspector.ProfileDataGridTree.prototype.get numberOfSamples):
(WebInspector.ProfileDataGridTree.prototype.get children):
(WebInspector.ProfileDataGridTree.prototype.appendChild):
(WebInspector.ProfileDataGridTree.prototype.insertChild):
(WebInspector.ProfileDataGridTree.prototype.removeChildren):
(WebInspector.ProfileDataGridTree.prototype.set sortComparator):
(WebInspector.ProfileDataGridTree.prototype.sort):
(WebInspector.ProfileDataGridTree.prototype.refresh):
(WebInspector.ProfileDataGridTree.prototype.addFocusNode):
(WebInspector.ProfileDataGridTree.prototype.rollbackFocusNode):
(WebInspector.ProfileDataGridTree.prototype.clearFocusNodes):
(WebInspector.ProfileDataGridTree.prototype.hasModifiers):
(WebInspector.ProfileDataGridTree.prototype.addModifier):
(WebInspector.ProfileDataGridTree.prototype.clearModifiers):
(WebInspector.ProfileDataGridTree.prototype._repopulate):
(WebInspector.ProfileDataGridTree.prototype._focusChanged):
(WebInspector.ProfileDataGridTree.prototype._updateCurrentFocusDetails):
(WebInspector.ProfileDataGridTree.prototype._restoreFocusedNodeToOriginalParent):
(WebInspector.ProfileDataGridTree.prototype._modifiersChanged):
Start of a DataGridTree for a CallingContextTree.
Contains special logic for focused nodes and modifiers.

* UserInterface/Views/ProfileView.css: Added.
(.profile > .data-grid):
(.profile > .data-grid th):
(.profile > .data-grid td .icon):
(.profile > .data-grid td .percentage):
(.profile > .data-grid td .location):
(.profile > .data-grid:matches(:focus, .force-focus) tr.selected td .location):
(.profile > .data-grid td .icon.function-icon):
(.profile > .data-grid td .icon.native-icon):
(.profile > .data-grid td .icon.program-icon):
(.profile > .data-grid tr:matches(.selected, :hover) .go-to-arrow):
(.profile > .data-grid td.function-column):
(.profile > .data-grid td .guidance):
(.profile > .data-grid td .guidance.hover):
(.profile > .data-grid td .guidance.base):
(.profile > .data-grid tr:not(.expanded) td .guidance.base):
(.profile > .data-grid tr.expanded td .guidance.base):
* UserInterface/Views/ProfileView.js: Added.
(WebInspector.ProfileView):
(WebInspector.ProfileView.prototype.get callingContextTree):
(WebInspector.ProfileView.prototype.get startTime):
(WebInspector.ProfileView.prototype.get endTime):
(WebInspector.ProfileView.prototype.setStartAndEndTime):
(WebInspector.ProfileView.prototype.hasFocusNodes):
(WebInspector.ProfileView.prototype.clearFocusNodes):
(WebInspector.ProfileView.prototype.get selectionPathComponents):
(WebInspector.ProfileView.prototype._recreate):
(WebInspector.ProfileView.prototype._repopulateDataGridFromTree):
(WebInspector.ProfileView.prototype._pathComponentClicked):
(WebInspector.ProfileView.prototype._dataGridTreeFocusChanged):
(WebInspector.ProfileView.prototype._dataGridTreeModifiersChanged):
(WebInspector.ProfileView.prototype._dataGridSortChanged):
(WebInspector.ProfileView.prototype._dataGridNodeSelected):
(WebInspector.ProfileView.prototype._dataGridNodeExpanded):
(WebInspector.ProfileView.prototype._mouseOverDataGrid):
(WebInspector.ProfileView.prototype._mouseLeaveDataGrid):
(WebInspector.ProfileView.prototype._guidanceElementKey):
(WebInspector.ProfileView.prototype._removeGuidanceElement):
(WebInspector.ProfileView.prototype._appendGuidanceElement):
ProfileView holds a data grid which is populated from the data grid tree.
Special handing for guidance markers when hovering / selecting parts of the tree.

* UserInterface/Views/ScriptClusterTimelineView.js: Added.
(WebInspector.ScriptClusterTimelineView.createPathComponent):
(WebInspector.ScriptClusterTimelineView):
(WebInspector.ScriptClusterTimelineView.prototype.get zeroTime):
(WebInspector.ScriptClusterTimelineView.prototype.set zeroTime):
(WebInspector.ScriptClusterTimelineView.prototype.get startTime):
(WebInspector.ScriptClusterTimelineView.prototype.set startTime):
(WebInspector.ScriptClusterTimelineView.prototype.get endTime):
(WebInspector.ScriptClusterTimelineView.prototype.set endTime):
(WebInspector.ScriptClusterTimelineView.prototype.get currentTime):
(WebInspector.ScriptClusterTimelineView.prototype.set currentTime):
(WebInspector.ScriptClusterTimelineView.prototype.get navigationSidebarTreeOutline):
(WebInspector.ScriptClusterTimelineView.prototype.reset):
(WebInspector.ScriptClusterTimelineView.prototype.filterDidChange):
(WebInspector.ScriptClusterTimelineView.prototype.matchTreeElementAgainstCustomFilters):
(WebInspector.ScriptClusterTimelineView.prototype.get detailsContentView):
(WebInspector.ScriptClusterTimelineView.prototype.get profileContentView):
(WebInspector.ScriptClusterTimelineView.prototype.get selectionPathComponents):
(WebInspector.ScriptClusterTimelineView.prototype.saveToCookie):
(WebInspector.ScriptClusterTimelineView.prototype.restoreFromCookie):
(WebInspector.ScriptClusterTimelineView.prototype.showDetails):
(WebInspector.ScriptClusterTimelineView.prototype.showProfile):
(WebInspector.ScriptClusterTimelineView.prototype._pathComponentForContentView):
(WebInspector.ScriptClusterTimelineView.prototype._identifierForContentView):
(WebInspector.ScriptClusterTimelineView.prototype._showContentViewForIdentifier):
(WebInspector.ScriptClusterTimelineView.prototype._pathComponentSelected):
(WebInspector.ScriptClusterTimelineView.prototype._scriptClusterViewCurrentContentViewDidChange):
Script Timeline ClusterContentView. Toggle between the normal "Details" data grid
and the new "Call Tree" profile view. Currently the recording expects child content
views to be TimelineViews, this ClusterContentView forwards TimelineView relevant
methods to the real TimelineView children.

* UserInterface/Views/ScriptDetailsTimelineView.js: Renamed from Source/WebInspectorUI/UserInterface/Views/ScriptTimelineView.js.
(WebInspector.ScriptDetailsTimelineView):
(WebInspector.ScriptDetailsTimelineView.prototype.get navigationSidebarTreeOutlineLabel):
(WebInspector.ScriptDetailsTimelineView.prototype.shown):
(WebInspector.ScriptDetailsTimelineView.prototype.hidden):
(WebInspector.ScriptDetailsTimelineView.prototype.closed):
(WebInspector.ScriptDetailsTimelineView.prototype.get selectionPathComponents):
(WebInspector.ScriptDetailsTimelineView.prototype.reset):
(WebInspector.ScriptDetailsTimelineView.prototype.canShowContentViewForTreeElement):
(WebInspector.ScriptDetailsTimelineView.prototype.showContentViewForTreeElement):
(WebInspector.ScriptDetailsTimelineView.prototype.treeElementPathComponentSelected):
(WebInspector.ScriptDetailsTimelineView.prototype.treeElementSelected):
(WebInspector.ScriptDetailsTimelineView.prototype.dataGridNodeForTreeElement):
(WebInspector.ScriptDetailsTimelineView.prototype.populateProfileNodeTreeElement):
(WebInspector.ScriptDetailsTimelineView.prototype.layout):
(WebInspector.ScriptDetailsTimelineView.prototype._processPendingRecords):
(WebInspector.ScriptDetailsTimelineView.prototype._scriptTimelineRecordAdded):
(WebInspector.ScriptDetailsTimelineView.prototype._scriptTimelineRecordRefreshed):
(WebInspector.ScriptDetailsTimelineView.prototype._dataGridFiltersDidChange):
(WebInspector.ScriptDetailsTimelineView.prototype._dataGridNodeSelected):
* UserInterface/Views/ScriptProfileTimelineView.js: Added.
(WebInspector.ScriptProfileTimelineView):
(WebInspector.ScriptProfileTimelineView.prototype.closed):
(WebInspector.ScriptProfileTimelineView.prototype.get navigationItems):
(WebInspector.ScriptProfileTimelineView.prototype.get selectionPathComponents):
(WebInspector.ScriptProfileTimelineView.prototype.layout):
(WebInspector.ScriptProfileTimelineView.prototype._callingContextTreeForOrientation):
(WebInspector.ScriptProfileTimelineView.prototype._profileViewSelectionPathComponentsDidChange):
(WebInspector.ScriptProfileTimelineView.prototype._scriptTimelineRecordRefreshed):
(WebInspector.ScriptProfileTimelineView.prototype._updateProfileOrientationButtonItem):
(WebInspector.ScriptProfileTimelineView.prototype._toggleProfileOrientation):
(WebInspector.ScriptProfileTimelineView.prototype._updateClearFocusNodesButtonItem):
(WebInspector.ScriptProfileTimelineView.prototype._clearFocusNodes):
The two TimelineViews.

* UserInterface/Views/TimelineRecordingContentView.js:
(WebInspector.TimelineRecordingContentView.prototype.get currentTimelineView):
(WebInspector.TimelineRecordingContentView.prototype.contentBrowserTreeElementForRepresentedObject):
The timeline content browser may now hold a ClusterContentView. It is not exactly a TimelineView,
but it holds TimelineViews, so treat it like one. Assume the ClusterContentView will add its own
path components.

(WebInspector.TimelineRecordingContentView.prototype._instrumentAdded):
Add extra information other than the sidebar to TimelineViews. The ProfileView looks
at the recording for the calling context trees.

git-svn-id: http://svn.webkit.org/repository/webkit/trunk@197619 268f45cc-cd09-0410-ab3c-d52691b4dbfc
diff --git a/Source/WebInspectorUI/UserInterface/Views/ScriptProfileTimelineView.js b/Source/WebInspectorUI/UserInterface/Views/ScriptProfileTimelineView.js
new file mode 100644
index 0000000..faa2dac
--- /dev/null
+++ b/Source/WebInspectorUI/UserInterface/Views/ScriptProfileTimelineView.js
@@ -0,0 +1,158 @@
+/*
+ * Copyright (C) 2016 Apple 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:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS 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 APPLE INC. OR ITS 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.
+ */
+
+WebInspector.ScriptProfileTimelineView = class ScriptProfileTimelineView extends WebInspector.TimelineView
+{
+    constructor(timeline, extraArguments)
+    {
+        super(timeline, extraArguments);
+
+        console.assert(timeline.type === WebInspector.TimelineRecord.Type.Script);
+
+        this.element.classList.add("script");
+
+        this._recording = extraArguments.recording;
+
+        this._forceNextLayout = false;
+        this._lastLayoutStartTime = undefined;
+        this._lastLayoutEndTime = undefined;
+
+        if (!WebInspector.ScriptProfileTimelineView.profileOrientationSetting)
+            WebInspector.ScriptProfileTimelineView.profileOrientationSetting = new WebInspector.Setting("script-profile-timeline-view-profile-orientation-setting", WebInspector.ScriptProfileTimelineView.ProfileOrientation.TopDown);
+
+        let callingContextTree = this._callingContextTreeForOrientation(WebInspector.ScriptProfileTimelineView.profileOrientationSetting.value);
+        this._profileView = new WebInspector.ProfileView(callingContextTree);
+        this._profileView.addEventListener(WebInspector.ContentView.Event.SelectionPathComponentsDidChange, this._profileViewSelectionPathComponentsDidChange, this);
+        this.addSubview(this._profileView);
+
+        let scopeBarItems = [
+            new WebInspector.ScopeBarItem(WebInspector.ScriptProfileTimelineView.ProfileOrientation.TopDown, WebInspector.UIString("Top Down"), true),
+            new WebInspector.ScopeBarItem(WebInspector.ScriptProfileTimelineView.ProfileOrientation.BottomUp, WebInspector.UIString("Bottom Up"), true),
+        ];
+        let defaultScopeBarItem = WebInspector.ScriptProfileTimelineView.profileOrientationSetting.value === WebInspector.ScriptProfileTimelineView.ProfileOrientation.TopDown ? scopeBarItems[0] : scopeBarItems[1];
+        this._profileOrientationScopeBar = new WebInspector.ScopeBar("profile-orientation", scopeBarItems, defaultScopeBarItem);
+        this._profileOrientationScopeBar.addEventListener(WebInspector.ScopeBar.Event.SelectionChanged, this._scopeBarSelectionDidChange, this);
+
+        let clearTooltip = WebInspector.UIString("Clear focus");
+        this._clearFocusNodesButtonItem = new WebInspector.ButtonNavigationItem("clear-profile-focus", clearTooltip, "Images/Close.svg", 16, 16);
+        this._clearFocusNodesButtonItem.addEventListener(WebInspector.ButtonNavigationItem.Event.Clicked, this._clearFocusNodes, this);
+        this._updateClearFocusNodesButtonItem();
+
+        timeline.addEventListener(WebInspector.Timeline.Event.Refreshed, this._scriptTimelineRecordRefreshed, this);
+
+        // FIXME: Support filtering the ProfileView.
+    }
+
+    // Protected
+
+    closed()
+    {
+        console.assert(this.representedObject instanceof WebInspector.Timeline);
+        this.representedObject.removeEventListener(null, null, this);
+    }
+
+    get navigationItems()
+    {
+        return [this._clearFocusNodesButtonItem, this._profileOrientationScopeBar];
+    }
+
+    get selectionPathComponents()
+    {
+        return this._profileView.selectionPathComponents;
+    }
+
+    layout()
+    {
+        if (!this._forceNextLayout && (this._lastLayoutStartTime === this.startTime && this._lastLayoutEndTime === this.endTime))
+            return;
+
+        this._forceNextLayout = false;
+        this._lastLayoutStartTime = this.startTime;
+        this._lastLayoutEndTime = this.endTime;
+
+        this._profileView.setStartAndEndTime(this.startTime, this.endTime);
+    }
+
+    // Private
+
+    _callingContextTreeForOrientation(orientation)
+    {
+        switch (orientation) {
+        case WebInspector.ScriptProfileTimelineView.ProfileOrientation.TopDown:
+            return this._recording.topDownCallingContextTree;
+        case WebInspector.ScriptProfileTimelineView.ProfileOrientation.BottomUp:
+            return this._recording.bottomUpCallingContextTree;
+        }
+    }
+
+    _profileViewSelectionPathComponentsDidChange(event)
+    {
+        this._updateClearFocusNodesButtonItem();
+        this.dispatchEventToListeners(WebInspector.ContentView.Event.SelectionPathComponentsDidChange);
+    }
+
+    _scriptTimelineRecordRefreshed(event)
+    {
+        this._forceNextLayout = true;
+        this.needsLayout();
+    }
+
+    _scopeBarSelectionDidChange()
+    {
+        let currentOrientation = WebInspector.ScriptProfileTimelineView.profileOrientationSetting.value;
+        let newOrientation = this._profileOrientationScopeBar.selectedItems[0].id;
+        let callingContextTree = this._callingContextTreeForOrientation(newOrientation);
+
+        WebInspector.ScriptProfileTimelineView.profileOrientationSetting.value = newOrientation;
+
+        this.removeSubview(this._profileView);
+        this._profileView.removeEventListener(WebInspector.ContentView.Event.SelectionPathComponentsDidChange, this._profileViewSelectionPathComponentsDidChange, this);
+
+        this._profileView = new WebInspector.ProfileView(callingContextTree);
+
+        this._profileView.addEventListener(WebInspector.ContentView.Event.SelectionPathComponentsDidChange, this._profileViewSelectionPathComponentsDidChange, this);
+        this.addSubview(this._profileView);
+
+        this.dispatchEventToListeners(WebInspector.ContentView.Event.SelectionPathComponentsDidChange);
+
+        this._forceNextLayout = true;
+        this.needsLayout();
+    }
+
+    _updateClearFocusNodesButtonItem()
+    {
+        this._clearFocusNodesButtonItem.enabled = this._profileView.hasFocusNodes();
+    }
+
+    _clearFocusNodes()
+    {
+        this._profileView.clearFocusNodes();
+    }
+};
+
+WebInspector.ScriptProfileTimelineView.ProfileOrientation = {
+    BottomUp: "bottom-up",
+    TopDown: "top-down",
+};