Title: [202010] trunk/Source/WebInspectorUI
Revision
202010
Author
sbar...@apple.com
Date
2016-06-13 15:43:20 -0700 (Mon, 13 Jun 2016)

Log Message

Web Inspector: Call Trees view should have a 'Top Functions'-like mode
https://bugs.webkit.org/show_bug.cgi?id=158555
<rdar://problem/26712544>

Reviewed by Timothy Hatcher.

This patch adds a Top Functions view that is similar to Instruments'
Top Functions view. I really wanted to add this view because I've
been doing a lot of performance debugging and I've exclusively
used the Top Functions view and I want the Inspector to also have
this view. I like to think of it as a more sophisticated version of the bottom-up view.

Top Functions works by treating every frame as a root in the tree.
Top functions view then presents a list of "roots". This is the same
as all other views, which also present a list of roots, but in this case,
every frame is a root. Bottom Up is great for nailing in specific performance
problems in exactly one frame. But Bottom Up doesn't give you good context about where
a specific frame is in the call tree and how frames are related by having
a caller or some path of shared callers. For example, consider this call tree:
         (program)
         /        \
        /          \
   (many nodes...)
      /
     /
    (parent)
    /   \
   /     \
 (leaf1)  (leaf2)

Suppose that 'leaf1' is super hot, and 'leaf2' is moderately hot.
If we look at this through Bottom Up view, we will see 'leaf1'
is super hot, but it will take more scrolling to see that 'leaf2'
is moderately hot. Lets say that 'parent' is also moderately hot,
but that the majority of its time isn't self time. With Bottom Up view,
there is no good way to see that 'leaf1' and 'leaf2' are both nodes under 'parent'.
With Top Down, you can find this information, but it requires a ton of drilling down into
the tree (i.e, you must expand past the 'many nodes...' I drew above). It's inconvenient to
use Top Down here for indentation alone. Bottom up will tell you that 'leaf1' is super hot,
and that 'leaf2' and 'parent' are moderately hot, but it doesn't show how they're related
in the original tree. It's important to see that 'parent's total time is very high
because it itself is moderately hot, and it has a child node that is super hot, and
another child that's moderately 'hot'. For the sake of this example, let's pretend
that 85% of the program's time is spent inside 'parent'. Seeing this information through
'Top Functions' is easy because this information filters to the top of the list. Specifically,
when using 'Top Functions' sorted by Total Time. Because every node is a root, there will be
a top-level entry for every frame in the program. Specifically, there will be a top-level node
for 'parent' in my above example. Because I've sorted this view by Total Time, I will see '(program)'
first. That's because 100% of execution time is under the '(program)' frame. Then, I might see
a few other nodes that also run the entire time because '(program)' calls them, and they eventually
call into other things that never leave the stack. These will also have time ranges near 100%.
But, only a few nodes after that, I'll see 'parent' in the list because it accounts for 85% of
execution time. Immediately, I will see that it has some self time, and that it has two child
nodes that have self time. This is really helpful.

Let's consider another example where it's not easy in Top Down to get the full picture of 'parent':
           (program)
            /  |  \
         (... many nodes...)
          /           \
    (many nodes...)   (many nodes...)
         /             \
       parent         parent
         |              |
        leaf1          leaf2

If we viewed this program in Top Down, we don't get a full picture of 'parent'
because it has its time distributed in two different subsections of the tree.
Specifically, lets say it has 70% of time in the leaf1 path, and 30% of the
time in the leaf2 path. We want a way to see these things together. It's impossible
to do this in Top Down or Bottom Up. But, in Top Functions view, we get the view that
we want to see because we treat 'parent' as a root of the tree. Because we do this,
we will create the following sub tree in the Top Functions view:
        parent
       /      \
     leaf1   leaf2
This happens naturally because when 'parent' is a root, we add all its children
to its subtree.

Constructing this tree is really easy. What we do is take any arbitrary stack
trace of length n, and treat is as n separate stack traces. Specifically, we
perform the following operation for any stack trace S.

S = [A, B, C, D]
(A is the entry frame, and D is the top of the stack).
We will transform this into a list of stack traces S' like so:
S' = [[A, B, C, D], [B, C, D], [C, D], [D]]

If we then run the normal top down tree algorithm on this set of stack
traces, all nodes get treated as roots, and voila, we get the Top Functions view.

* Localizations/en.lproj/localizedStrings.js:
* UserInterface/Controllers/TimelineManager.js:
* UserInterface/Main.html:
* UserInterface/Models/CallingContextTree.js:
* UserInterface/Models/TimelineRecording.js:
* UserInterface/Views/ScriptProfileTimelineView.js:
* UserInterface/Views/TextToggleButtonNavigationItem.css: Added.
* UserInterface/Views/TextToggleButtonNavigationItem.js: Added.

Modified Paths

Added Paths

Diff

Modified: trunk/Source/WebInspectorUI/ChangeLog (202009 => 202010)


--- trunk/Source/WebInspectorUI/ChangeLog	2016-06-13 22:41:54 UTC (rev 202009)
+++ trunk/Source/WebInspectorUI/ChangeLog	2016-06-13 22:43:20 UTC (rev 202010)
@@ -1,3 +1,106 @@
+2016-06-13  Saam Barati  <sbar...@apple.com>
+
+        Web Inspector: Call Trees view should have a 'Top Functions'-like mode
+        https://bugs.webkit.org/show_bug.cgi?id=158555
+        <rdar://problem/26712544>
+
+        Reviewed by Timothy Hatcher.
+
+        This patch adds a Top Functions view that is similar to Instruments'
+        Top Functions view. I really wanted to add this view because I've
+        been doing a lot of performance debugging and I've exclusively 
+        used the Top Functions view and I want the Inspector to also have
+        this view. I like to think of it as a more sophisticated version of the bottom-up view.
+        
+        Top Functions works by treating every frame as a root in the tree.
+        Top functions view then presents a list of "roots". This is the same
+        as all other views, which also present a list of roots, but in this case,
+        every frame is a root. Bottom Up is great for nailing in specific performance
+        problems in exactly one frame. But Bottom Up doesn't give you good context about where
+        a specific frame is in the call tree and how frames are related by having
+        a caller or some path of shared callers. For example, consider this call tree:
+                 (program)
+                 /        \
+                /          \
+           (many nodes...)  
+              / 
+             /
+            (parent)
+            /   \
+           /     \
+         (leaf1)  (leaf2)
+        
+        Suppose that 'leaf1' is super hot, and 'leaf2' is moderately hot.
+        If we look at this through Bottom Up view, we will see 'leaf1'
+        is super hot, but it will take more scrolling to see that 'leaf2'
+        is moderately hot. Lets say that 'parent' is also moderately hot,
+        but that the majority of its time isn't self time. With Bottom Up view,
+        there is no good way to see that 'leaf1' and 'leaf2' are both nodes under 'parent'.
+        With Top Down, you can find this information, but it requires a ton of drilling down into
+        the tree (i.e, you must expand past the 'many nodes...' I drew above). It's inconvenient to
+        use Top Down here for indentation alone. Bottom up will tell you that 'leaf1' is super hot,
+        and that 'leaf2' and 'parent' are moderately hot, but it doesn't show how they're related
+        in the original tree. It's important to see that 'parent's total time is very high
+        because it itself is moderately hot, and it has a child node that is super hot, and
+        another child that's moderately 'hot'. For the sake of this example, let's pretend
+        that 85% of the program's time is spent inside 'parent'. Seeing this information through
+        'Top Functions' is easy because this information filters to the top of the list. Specifically,
+        when using 'Top Functions' sorted by Total Time. Because every node is a root, there will be
+        a top-level entry for every frame in the program. Specifically, there will be a top-level node
+        for 'parent' in my above example. Because I've sorted this view by Total Time, I will see '(program)'
+        first. That's because 100% of execution time is under the '(program)' frame. Then, I might see
+        a few other nodes that also run the entire time because '(program)' calls them, and they eventually
+        call into other things that never leave the stack. These will also have time ranges near 100%.
+        But, only a few nodes after that, I'll see 'parent' in the list because it accounts for 85% of
+        execution time. Immediately, I will see that it has some self time, and that it has two child
+        nodes that have self time. This is really helpful.
+        
+        Let's consider another example where it's not easy in Top Down to get the full picture of 'parent':
+                   (program)
+                    /  |  \
+                 (... many nodes...)
+                  /           \
+            (many nodes...)   (many nodes...)
+                 /             \
+               parent         parent
+                 |              |
+                leaf1          leaf2
+        
+        
+        If we viewed this program in Top Down, we don't get a full picture of 'parent'
+        because it has its time distributed in two different subsections of the tree.
+        Specifically, lets say it has 70% of time in the leaf1 path, and 30% of the
+        time in the leaf2 path. We want a way to see these things together. It's impossible
+        to do this in Top Down or Bottom Up. But, in Top Functions view, we get the view that
+        we want to see because we treat 'parent' as a root of the tree. Because we do this,
+        we will create the following sub tree in the Top Functions view:
+                parent
+               /      \
+             leaf1   leaf2
+        This happens naturally because when 'parent' is a root, we add all its children
+        to its subtree.
+        
+        Constructing this tree is really easy. What we do is take any arbitrary stack
+        trace of length n, and treat is as n separate stack traces. Specifically, we
+        perform the following operation for any stack trace S.
+        
+        S = [A, B, C, D]
+        (A is the entry frame, and D is the top of the stack).
+        We will transform this into a list of stack traces S' like so:
+        S' = [[A, B, C, D], [B, C, D], [C, D], [D]]
+        
+        If we then run the normal top down tree algorithm on this set of stack
+        traces, all nodes get treated as roots, and voila, we get the Top Functions view.
+
+        * Localizations/en.lproj/localizedStrings.js:
+        * UserInterface/Controllers/TimelineManager.js:
+        * UserInterface/Main.html:
+        * UserInterface/Models/CallingContextTree.js:
+        * UserInterface/Models/TimelineRecording.js:
+        * UserInterface/Views/ScriptProfileTimelineView.js:
+        * UserInterface/Views/TextToggleButtonNavigationItem.css: Added.
+        * UserInterface/Views/TextToggleButtonNavigationItem.js: Added.
+
 2016-06-13  Matt Baker  <mattba...@apple.com>
 
         Web Inspector: Add ability to show/hide DataGird columns

Modified: trunk/Source/WebInspectorUI/Localizations/en.lproj/localizedStrings.js (202009 => 202010)


--- trunk/Source/WebInspectorUI/Localizations/en.lproj/localizedStrings.js	2016-06-13 22:41:54 UTC (rev 202009)
+++ trunk/Source/WebInspectorUI/Localizations/en.lproj/localizedStrings.js	2016-06-13 22:43:20 UTC (rev 202010)
@@ -104,7 +104,6 @@
 localizedStrings["Blur"] = "Blur";
 localizedStrings["Border"] = "Border";
 localizedStrings["Bottom"] = "Bottom";
-localizedStrings["Bottom Up"] = "Bottom Up";
 localizedStrings["Boundary"] = "Boundary";
 localizedStrings["Box Model"] = "Box Model";
 localizedStrings["Box Shadow"] = "Box Shadow";
@@ -398,6 +397,7 @@
 localizedStrings["Inset"] = "Inset";
 localizedStrings["Instances"] = "Instances";
 localizedStrings["Invalid"] = "Invalid";
+localizedStrings["Inverted"] = "Inverted";
 localizedStrings["Invoke getter"] = "Invoke getter";
 localizedStrings["Iterations"] = "Iterations";
 localizedStrings["_javascript_"] = "_javascript_";
@@ -713,7 +713,7 @@
 localizedStrings["Timing"] = "Timing";
 localizedStrings["Toggle Classes"] = "Toggle Classes";
 localizedStrings["Top"] = "Top";
-localizedStrings["Top Down"] = "Top Down";
+localizedStrings["Top Functions"] = "Top Functions";
 localizedStrings["Total Time"] = "Total Time";
 localizedStrings["Total memory size at the end of the selected time range"] = "Total memory size at the end of the selected time range";
 localizedStrings["Total number of resources, click to show the Resources tab"] = "Total number of resources, click to show the Resources tab";

Modified: trunk/Source/WebInspectorUI/UserInterface/Controllers/TimelineManager.js (202009 => 202010)


--- trunk/Source/WebInspectorUI/UserInterface/Controllers/TimelineManager.js	2016-06-13 22:41:54 UTC (rev 202009)
+++ trunk/Source/WebInspectorUI/UserInterface/Controllers/TimelineManager.js	2016-06-13 22:43:20 UTC (rev 202010)
@@ -865,6 +865,8 @@
             let {stackTraces} = samples;
             let topDownCallingContextTree = this.activeRecording.topDownCallingContextTree;
             let bottomUpCallingContextTree = this.activeRecording.bottomUpCallingContextTree;
+            let topFunctionsTopDownCallingContextTree = this.activeRecording.topFunctionsTopDownCallingContextTree;
+            let topFunctionsBottomUpCallingContextTree = this.activeRecording.topFunctionsBottomUpCallingContextTree;
 
             // Calculate a per-sample duration.
             let timestampIndex = 0;
@@ -901,6 +903,8 @@
             for (let i = 0; i < stackTraces.length; i++) {
                 topDownCallingContextTree.updateTreeWithStackTrace(stackTraces[i], sampleDurations[i]);
                 bottomUpCallingContextTree.updateTreeWithStackTrace(stackTraces[i], sampleDurations[i]);
+                topFunctionsTopDownCallingContextTree.updateTreeWithStackTrace(stackTraces[i], sampleDurations[i]);
+                topFunctionsBottomUpCallingContextTree.updateTreeWithStackTrace(stackTraces[i], sampleDurations[i]);
             }
 
             // FIXME: This transformation should not be needed after introducing ProfileView.

Modified: trunk/Source/WebInspectorUI/UserInterface/Main.html (202009 => 202010)


--- trunk/Source/WebInspectorUI/UserInterface/Main.html	2016-06-13 22:41:54 UTC (rev 202009)
+++ trunk/Source/WebInspectorUI/UserInterface/Main.html	2016-06-13 22:43:20 UTC (rev 202010)
@@ -161,6 +161,7 @@
     <link rel="stylesheet" href=""
     <link rel="stylesheet" href=""
     <link rel="stylesheet" href=""
+    <link rel="stylesheet" href=""
     <link rel="stylesheet" href=""
     <link rel="stylesheet" href=""
     <link rel="stylesheet" href=""
@@ -629,6 +630,7 @@
     <script src=""
     <script src=""
     <script src=""
+    <script src=""
     <script src=""
     <script src=""
     <script src=""

Modified: trunk/Source/WebInspectorUI/UserInterface/Models/CallingContextTree.js (202009 => 202010)


--- trunk/Source/WebInspectorUI/UserInterface/Models/CallingContextTree.js	2016-06-13 22:41:54 UTC (rev 202009)
+++ trunk/Source/WebInspectorUI/UserInterface/Models/CallingContextTree.js	2016-06-13 22:43:20 UTC (rev 202010)
@@ -63,12 +63,31 @@
                 node = node.findOrMakeChild(stackFrame);
                 node.addTimestampAndExpressionLocation(timestamp, duration, stackFrame.expressionLocation || null, i === 0);
             }
-        } else {
+        } else if (this._type === WebInspector.CallingContextTree.Type.BottomUp) {
             for (let i = 0; i < stackFrames.length; ++i) {
                 let stackFrame = stackFrames[i];
                 node = node.findOrMakeChild(stackFrame);
                 node.addTimestampAndExpressionLocation(timestamp, duration, stackFrame.expressionLocation || null, i === 0);
             }
+        } else if (this._type === WebInspector.CallingContextTree.Type.TopFunctionsTopDown){
+            for (let i = stackFrames.length; i--; ) {
+                node = this._root;
+                for (let j = i + 1; j--; ) {
+                    let stackFrame = stackFrames[j];
+                    node = node.findOrMakeChild(stackFrame);
+                    node.addTimestampAndExpressionLocation(timestamp, duration, stackFrame.expressionLocation || null, j === 0);
+                }
+            }
+        } else {
+            console.assert(this._type === WebInspector.CallingContextTree.Type.TopFunctionsBottomUp);
+            for (let i = 0; i < stackFrames.length; i++) {
+                node = this._root;
+                for (let j = i; j < stackFrames.length; j++) {
+                    let stackFrame = stackFrames[j];
+                    node = node.findOrMakeChild(stackFrame);
+                    node.addTimestampAndExpressionLocation(timestamp, duration, stackFrame.expressionLocation || null, j === 0);
+                }
+            }
         }
     }
 
@@ -362,4 +381,6 @@
 WebInspector.CallingContextTree.Type = {
     TopDown: Symbol("TopDown"),
     BottomUp: Symbol("BottomUp"),
+    TopFunctionsTopDown: Symbol("TopFunctionsTopDown"),
+    TopFunctionsBottomUp: Symbol("TopFunctionsTopDown"),
 };

Modified: trunk/Source/WebInspectorUI/UserInterface/Models/TimelineRecording.js (202009 => 202010)


--- trunk/Source/WebInspectorUI/UserInterface/Models/TimelineRecording.js	2016-06-13 22:41:54 UTC (rev 202009)
+++ trunk/Source/WebInspectorUI/UserInterface/Models/TimelineRecording.js	2016-06-13 22:43:20 UTC (rev 202010)
@@ -37,6 +37,8 @@
         this._instruments = instruments || [];
         this._topDownCallingContextTree = new WebInspector.CallingContextTree(WebInspector.CallingContextTree.Type.TopDown);
         this._bottomUpCallingContextTree = new WebInspector.CallingContextTree(WebInspector.CallingContextTree.Type.BottomUp);
+        this._topFunctionsTopDownCallingContextTree = new WebInspector.CallingContextTree(WebInspector.CallingContextTree.Type.TopFunctionsTopDown);
+        this._topFunctionsBottomUpCallingContextTree = new WebInspector.CallingContextTree(WebInspector.CallingContextTree.Type.TopFunctionsBottomUp);
 
         for (let type of WebInspector.TimelineManager.availableTimelineTypes()) {
             let timeline = WebInspector.Timeline.create(type);
@@ -104,6 +106,16 @@
         return this._bottomUpCallingContextTree;
     }
 
+    get topFunctionsTopDownCallingContextTree()
+    {
+        return this._topFunctionsTopDownCallingContextTree;
+    }
+
+    get topFunctionsBottomUpCallingContextTree()
+    {
+        return this._topFunctionsBottomUpCallingContextTree;
+    }
+
     start()
     {
         console.assert(!this._capturing, "Attempted to start an already started session.");
@@ -163,6 +175,8 @@
 
         this._topDownCallingContextTree.reset();
         this._bottomUpCallingContextTree.reset();
+        this._topFunctionsTopDownCallingContextTree.reset();
+        this._topFunctionsBottomUpCallingContextTree.reset();
 
         for (var timeline of this._timelines.values())
             timeline.reset(suppressEvents);

Modified: trunk/Source/WebInspectorUI/UserInterface/Views/ScriptProfileTimelineView.js (202009 => 202010)


--- trunk/Source/WebInspectorUI/UserInterface/Views/ScriptProfileTimelineView.js	2016-06-13 22:41:54 UTC (rev 202009)
+++ trunk/Source/WebInspectorUI/UserInterface/Views/ScriptProfileTimelineView.js	2016-06-13 22:43:20 UTC (rev 202010)
@@ -41,22 +41,30 @@
 
         if (!WebInspector.ScriptProfileTimelineView.profileOrientationSetting)
             WebInspector.ScriptProfileTimelineView.profileOrientationSetting = new WebInspector.Setting("script-profile-timeline-view-profile-orientation-setting", WebInspector.ScriptProfileTimelineView.ProfileOrientation.TopDown);
+        if (!WebInspector.ScriptProfileTimelineView.profileTypeSetting)
+            WebInspector.ScriptProfileTimelineView.profileTypeSetting = new WebInspector.Setting("script-profile-timeline-view-profile-type-setting", WebInspector.ScriptProfileTimelineView.ProfileViewType.Hierarchy);
 
-        this._showProfileViewForOrientation(WebInspector.ScriptProfileTimelineView.profileOrientationSetting.value);
+        this._showProfileViewForOrientation(WebInspector.ScriptProfileTimelineView.profileOrientationSetting.value, WebInspector.ScriptProfileTimelineView.profileTypeSetting.value);
 
-        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();
 
+        this._profileOrientationButton = new WebInspector.TextToggleButtonNavigationItem("profile-orientation", WebInspector.UIString("Inverted"));
+        this._profileOrientationButton.addEventListener(WebInspector.ButtonNavigationItem.Event.Clicked, this._profileOrientationButtonClicked, this);
+        if (WebInspector.ScriptProfileTimelineView.profileOrientationSetting.value == WebInspector.ScriptProfileTimelineView.ProfileOrientation.TopDown)
+            this._profileOrientationButton.activated = false;
+        else
+            this._profileOrientationButton.activated = true;
+
+        this._topFunctionsButton = new WebInspector.TextToggleButtonNavigationItem("top-functions", WebInspector.UIString("Top Functions"));
+        this._topFunctionsButton.addEventListener(WebInspector.ButtonNavigationItem.Event.Clicked, this._topFunctionsButtonClicked, this);
+        if (WebInspector.ScriptProfileTimelineView.profileTypeSetting.value == WebInspector.ScriptProfileTimelineView.ProfileViewType.Hierarchy)
+            this._topFunctionsButton.activated = false;
+        else
+            this._topFunctionsButton.activated = true;
+
         timeline.addEventListener(WebInspector.Timeline.Event.Refreshed, this._scriptTimelineRecordRefreshed, this);
     }
 
@@ -76,7 +84,7 @@
 
     get navigationItems()
     {
-        return [this._clearFocusNodesButtonItem, this._profileOrientationScopeBar];
+        return [this._clearFocusNodesButtonItem, this._profileOrientationButton, this._topFunctionsButton];
     }
 
     get selectionPathComponents()
@@ -98,14 +106,17 @@
 
     // Private
 
-    _callingContextTreeForOrientation(orientation)
+    _callingContextTreeForOrientation(profileOrientation, profileViewType)
     {
-        switch (orientation) {
+        switch (profileOrientation) {
         case WebInspector.ScriptProfileTimelineView.ProfileOrientation.TopDown:
-            return this._recording.topDownCallingContextTree;
+            return profileViewType === WebInspector.ScriptProfileTimelineView.ProfileViewType.Hierarchy ? this._recording.topDownCallingContextTree : this._recording.topFunctionsTopDownCallingContextTree;
         case WebInspector.ScriptProfileTimelineView.ProfileOrientation.BottomUp:
-            return this._recording.bottomUpCallingContextTree;
+            return profileViewType === WebInspector.ScriptProfileTimelineView.ProfileViewType.Hierarchy ? this._recording.bottomUpCallingContextTree : this._recording.topFunctionsBottomUpCallingContextTree;
         }
+
+        console.assert(false, "Should not be reached.");
+        return this._recording.topDownCallingContextTree;
     }
 
     _profileViewSelectionPathComponentsDidChange(event)
@@ -120,13 +131,18 @@
         this.needsLayout();
     }
 
-    _scopeBarSelectionDidChange()
+    _profileOrientationButtonClicked()
     {
-        let currentOrientation = WebInspector.ScriptProfileTimelineView.profileOrientationSetting.value;
-        let newOrientation = this._profileOrientationScopeBar.selectedItems[0].id;
+        this._profileOrientationButton.activated = !this._profileOrientationButton.activated;
+        let isInverted = this._profileOrientationButton.activated;
+        let newOrientation;
+        if (isInverted)
+            newOrientation = WebInspector.ScriptProfileTimelineView.ProfileOrientation.BottomUp;
+        else
+            newOrientation = WebInspector.ScriptProfileTimelineView.ProfileOrientation.TopDown;
 
         WebInspector.ScriptProfileTimelineView.profileOrientationSetting.value = newOrientation;
-        this._showProfileViewForOrientation(newOrientation);
+        this._showProfileViewForOrientation(newOrientation, WebInspector.ScriptProfileTimelineView.profileTypeSetting);
 
         this.dispatchEventToListeners(WebInspector.ContentView.Event.SelectionPathComponentsDidChange);
 
@@ -134,8 +150,27 @@
         this.needsLayout();
     }
 
-    _showProfileViewForOrientation(orientation)
+    _topFunctionsButtonClicked()
     {
+        this._topFunctionsButton.activated = !this._topFunctionsButton.activated;
+        let isTopFunctionsEnabled = this._topFunctionsButton.activated;
+        let newOrientation;
+        if (isTopFunctionsEnabled)
+            newOrientation = WebInspector.ScriptProfileTimelineView.ProfileViewType.TopFunctions;
+        else
+            newOrientation = WebInspector.ScriptProfileTimelineView.ProfileViewType.Hierarchy;
+
+        WebInspector.ScriptProfileTimelineView.profileTypeSetting.value = newOrientation;
+        this._showProfileViewForOrientation(WebInspector.ScriptProfileTimelineView.profileOrientationSetting.value, newOrientation);
+
+        this.dispatchEventToListeners(WebInspector.ContentView.Event.SelectionPathComponentsDidChange);
+
+        this._forceNextLayout = true;
+        this.needsLayout();
+    }
+
+    _showProfileViewForOrientation(profileOrientation, profileViewType)
+    {
         let filterText;
         if (this._profileView) {
             this._profileView.removeEventListener(WebInspector.ContentView.Event.SelectionPathComponentsDidChange, this._profileViewSelectionPathComponentsDidChange, this);
@@ -143,7 +178,7 @@
             filterText = this._profileView.dataGrid.filterText;
         }
 
-        let callingContextTree = this._callingContextTreeForOrientation(orientation);
+        let callingContextTree = this._callingContextTreeForOrientation(profileOrientation, profileViewType);
         this._profileView = new WebInspector.ProfileView(callingContextTree);
         this._profileView.addEventListener(WebInspector.ContentView.Event.SelectionPathComponentsDidChange, this._profileViewSelectionPathComponentsDidChange, this);
 
@@ -169,3 +204,8 @@
     BottomUp: "bottom-up",
     TopDown: "top-down",
 };
+
+WebInspector.ScriptProfileTimelineView.ProfileViewType = {
+    Hierarchy: "hierarchy",
+    TopFunctions: "top-functions",
+};

Added: trunk/Source/WebInspectorUI/UserInterface/Views/TextToggleButtonNavigationItem.css (0 => 202010)


--- trunk/Source/WebInspectorUI/UserInterface/Views/TextToggleButtonNavigationItem.css	                        (rev 0)
+++ trunk/Source/WebInspectorUI/UserInterface/Views/TextToggleButtonNavigationItem.css	2016-06-13 22:43:20 UTC (rev 202010)
@@ -0,0 +1,51 @@
+/*
+ * 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.
+ */
+
+.navigation-bar .item.text-toggle.button.text-only {
+    width: auto;
+    height: 17px;
+    margin: auto;
+    margin-left: 8px;
+    margin-right: 8px;
+}
+
+.navigation-bar .item.text-toggle.button.text-only:hover {
+    color: var(--selected-foreground-color);
+    background-color: var(--selected-background-color-hover);
+}
+
+.navigation-bar .item.text-toggle.button.text-only.selected {
+    color: var(--selected-foreground-color);
+    background-color: var(--selected-background-color);
+}
+
+.navigation-bar .item.text-toggle.button.text-only:active {
+    color: var(--selected-foreground-color);
+    background-color: hsla(212, 92%, 54%, 0.55);
+}
+
+.navigation-bar .item.text-toggle.button.text-only.selected:active {
+    background-color: var(--selected-background-color-active);
+}

Added: trunk/Source/WebInspectorUI/UserInterface/Views/TextToggleButtonNavigationItem.js (0 => 202010)


--- trunk/Source/WebInspectorUI/UserInterface/Views/TextToggleButtonNavigationItem.js	                        (rev 0)
+++ trunk/Source/WebInspectorUI/UserInterface/Views/TextToggleButtonNavigationItem.js	2016-06-13 22:43:20 UTC (rev 202010)
@@ -0,0 +1,62 @@
+/*
+ * 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.
+ */
+
+{
+    const selectedStyleClassName = "selected";
+
+    WebInspector.TextToggleButtonNavigationItem = class TextToggleButtonNavigationItem extends WebInspector.ButtonNavigationItem
+    {
+        constructor(identifier, title)
+        {
+            super(identifier, title);
+
+            this._title = title;
+        }
+
+        // Public
+
+        get title()
+        {
+            return this._title;
+        }
+
+        get activated()
+        {
+            return this.element.classList.contains(selectedStyleClassName);
+        }
+
+        set activated(flag)
+        {
+            this.element.classList.toggle(selectedStyleClassName, flag);
+        }
+
+        // Protected
+
+        get additionalClassNames()
+        {
+            return ["text-toggle", "button"];
+        }
+    };
+}
_______________________________________________
webkit-changes mailing list
webkit-changes@lists.webkit.org
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to