Adds Focus, Exclude and Restore buttons to the Profile view
authortimothy@apple.com <timothy@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Fri, 23 May 2008 05:15:28 +0000 (05:15 +0000)
committertimothy@apple.com <timothy@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Fri, 23 May 2008 05:15:28 +0000 (05:15 +0000)
Status bar. Also adds a Record button to create new profiles.

<rdar://problem/5950867> JSProfiler: Allow the profiler to "Focus" a profile node.
<rdar://problem/5951529> JSProfiler: Allow the profiler to "Exclude" a profile node.

Reviewed by Adam Roben.

* English.lproj/localizedStrings.js: Added new strings.
* page/JavaScriptProfile.cpp:
(WebCore::restoreAll): Call Profile::restoreAll.
(WebCore::ProfileClass): Added restoreAll to the static functions.
* page/inspector/Images/excludeButtons.png: Added.
* page/inspector/Images/focusButtons.png: Added.
* page/inspector/Images/recordButtons.png: Added.
* page/inspector/Images/reloadButtons.png: Added.
* page/inspector/ProfileView.js:
(WebInspector.ProfileView): Create the buttons elements.
(WebInspector.ProfileView.prototype.get statusBarItems): Return the three
status bar buttons.
(WebInspector.ProfileView.prototype.refresh): Only create ProfileDataGridNodes
for ProfileNodes that are visible.
(WebInspector.ProfileView.prototype.refreshShowAsPercents): Just call
refresh on the children, now that they have access to the ProfileView's properties.
(WebInspector.ProfileView.prototype._focusClicked): Call focus
on the profile, refresh the ProfileView and show the reset button.
(WebInspector.ProfileView.prototype._excludeClicked): Call exclude
on the profile, refresh the ProfileView and show the reset button.
(WebInspector.ProfileView.prototype._resetClicked): Call restoreAll
on the profile, refresh the ProfileView and hide the reset button.
(WebInspector.ProfileView.prototype._dataGridNodeSelected): Enable the
focus and exclude buttons.
(WebInspector.ProfileView.prototype._dataGridNodeDeselected): Disable the
focus and exclude buttons.
(WebInspector.ProfileDataGridNode): Take a ProfileView, and remove the
showPercentAs* arguments.
* page/inspector/ProfilesPanel.js: Add a record status bar button and
th ability to have per-view status bar buttons.
* page/inspector/inspector.css: New styles.

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@34051 268f45cc-cd09-0410-ab3c-d52691b4dbfc

13 files changed:
JavaScriptCore/ChangeLog
JavaScriptCore/JavaScriptCore.exp
JavaScriptCore/profiler/Profile.h
WebCore/ChangeLog
WebCore/English.lproj/localizedStrings.js
WebCore/page/JavaScriptProfile.cpp
WebCore/page/inspector/Images/excludeButtons.png [new file with mode: 0644]
WebCore/page/inspector/Images/focusButtons.png [new file with mode: 0644]
WebCore/page/inspector/Images/recordButtons.png [new file with mode: 0644]
WebCore/page/inspector/Images/reloadButtons.png [new file with mode: 0644]
WebCore/page/inspector/ProfileView.js
WebCore/page/inspector/ProfilesPanel.js
WebCore/page/inspector/inspector.css

index e638127..4f3c752 100644 (file)
@@ -1,3 +1,13 @@
+2008-05-22  Timothy Hatcher  <timothy@apple.com>
+
+        Added Profile::restoreAll and added ProfileNode::restoreAll
+        to the export file.
+
+        Reviewed by Adam Roben.
+
+        * JavaScriptCore.exp:
+        * profiler/Profile.h:
+
 2008-05-22  Alp Toker  <alp@nuanti.com>
 
         GTK+ build fix. Add JavaScriptCore/profiler to include path.
index 0fb6ef8..1938d65 100644 (file)
@@ -91,6 +91,7 @@ __ZN3KJS11Interpreter8evaluateEPNS_9ExecStateERNS_10ScopeChainERKNS_7UStringEiS7
 __ZN3KJS11JSImmediate8toObjectEPKNS_7JSValueEPNS_9ExecStateE
 __ZN3KJS11JSImmediate8toStringEPKNS_7JSValueE
 __ZN3KJS11ProfileNode5focusERKNS_14CallIdentifierEb
+__ZN3KJS11ProfileNode10restoreAllEv
 __ZN3KJS11ProfileNode7excludeERKNS_14CallIdentifierE
 __ZN3KJS11ProfileNode18sortCallsAscendingEv
 __ZN3KJS11ProfileNode19sortCallsDescendingEv
index 6a693b3..7f80dae 100644 (file)
@@ -61,6 +61,7 @@ namespace KJS {
 
         void focus(const ProfileNode* profileNode) { if (!profileNode) return; m_headNode->focus(profileNode->callIdentifier()); }
         void exclude(const ProfileNode* profileNode) { if (!profileNode) return; m_headNode->exclude(profileNode->callIdentifier()); }
+        void restoreAll() { m_headNode->restoreAll(); }
 
 #ifndef NDEBUG
         void debugPrintData() const;
index 22a8389..f023439 100644 (file)
@@ -1,3 +1,45 @@
+2008-05-22  Timothy Hatcher  <timothy@apple.com>
+
+        Adds Focus, Exclude and Restore buttons to the Profile view
+        Status bar. Also adds a Record button to create new profiles.
+
+        <rdar://problem/5950867> JSProfiler: Allow the profiler to "Focus" a profile node.
+        <rdar://problem/5951529> JSProfiler: Allow the profiler to "Exclude" a profile node.
+
+        Reviewed by Adam Roben.
+
+        * English.lproj/localizedStrings.js: Added new strings.
+        * page/JavaScriptProfile.cpp:
+        (WebCore::restoreAll): Call Profile::restoreAll.
+        (WebCore::ProfileClass): Added restoreAll to the static functions.
+        * page/inspector/Images/excludeButtons.png: Added.
+        * page/inspector/Images/focusButtons.png: Added.
+        * page/inspector/Images/recordButtons.png: Added.
+        * page/inspector/Images/reloadButtons.png: Added.
+        * page/inspector/ProfileView.js:
+        (WebInspector.ProfileView): Create the buttons elements.
+        (WebInspector.ProfileView.prototype.get statusBarItems): Return the three
+        status bar buttons.
+        (WebInspector.ProfileView.prototype.refresh): Only create ProfileDataGridNodes
+        for ProfileNodes that are visible.
+        (WebInspector.ProfileView.prototype.refreshShowAsPercents): Just call
+        refresh on the children, now that they have access to the ProfileView's properties.
+        (WebInspector.ProfileView.prototype._focusClicked): Call focus
+        on the profile, refresh the ProfileView and show the reset button.
+        (WebInspector.ProfileView.prototype._excludeClicked): Call exclude
+        on the profile, refresh the ProfileView and show the reset button.
+        (WebInspector.ProfileView.prototype._resetClicked): Call restoreAll
+        on the profile, refresh the ProfileView and hide the reset button.
+        (WebInspector.ProfileView.prototype._dataGridNodeSelected): Enable the 
+        focus and exclude buttons.
+        (WebInspector.ProfileView.prototype._dataGridNodeDeselected): Disable the
+        focus and exclude buttons.
+        (WebInspector.ProfileDataGridNode): Take a ProfileView, and remove the 
+        showPercentAs* arguments.
+        * page/inspector/ProfilesPanel.js: Add a record status bar button and
+        th ability to have per-view status bar buttons.
+        * page/inspector/inspector.css: New styles.
+
 2008-05-22  Mark Rowe  <mrowe@apple.com>
 
         Reviewed by Tim Hatcher.
index 6224323..56147f1 100644 (file)
Binary files a/WebCore/English.lproj/localizedStrings.js and b/WebCore/English.lproj/localizedStrings.js differ
index 1cedabf..73f531d 100644 (file)
@@ -70,7 +70,6 @@ static JSValueRef getHeadCallback(JSContextRef ctx, JSObjectRef thisObject, JSSt
     return toRef(toJS(toJS(ctx), profile->callTree()));
 }
 
-
 static JSValueRef focus(JSContextRef ctx, JSObjectRef /*function*/, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* /*exception*/)
 {
     if (!JSValueIsObjectOfClass(ctx, thisObject, ProfileClass()))
@@ -105,6 +104,17 @@ static JSValueRef exclude(JSContextRef ctx, JSObjectRef /*function*/, JSObjectRe
     return JSValueMakeUndefined(ctx);
 }
 
+static JSValueRef restoreAll(JSContextRef ctx, JSObjectRef /*function*/, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* /*exception*/)
+{
+    if (!JSValueIsObjectOfClass(ctx, thisObject, ProfileClass()))
+        return JSValueMakeUndefined(ctx);
+
+    Profile* profile = static_cast<Profile*>(JSObjectGetPrivate(thisObject));
+    profile->restoreAll();
+
+    return JSValueMakeUndefined(ctx);
+}
+
 static void finalize(JSObjectRef object)
 {
     Profile* profile = static_cast<Profile*>(JSObjectGetPrivate(object));
@@ -123,6 +133,7 @@ JSClassRef ProfileClass()
     static JSStaticFunction staticFunctions[] = {
         { "focus", focus, kJSPropertyAttributeNone },
         { "exclude", exclude, kJSPropertyAttributeNone },
+        { "restoreAll", restoreAll, kJSPropertyAttributeNone },
         { 0, 0, 0 }
     };
 
diff --git a/WebCore/page/inspector/Images/excludeButtons.png b/WebCore/page/inspector/Images/excludeButtons.png
new file mode 100644 (file)
index 0000000..f1c53a9
Binary files /dev/null and b/WebCore/page/inspector/Images/excludeButtons.png differ
diff --git a/WebCore/page/inspector/Images/focusButtons.png b/WebCore/page/inspector/Images/focusButtons.png
new file mode 100644 (file)
index 0000000..47eaa04
Binary files /dev/null and b/WebCore/page/inspector/Images/focusButtons.png differ
diff --git a/WebCore/page/inspector/Images/recordButtons.png b/WebCore/page/inspector/Images/recordButtons.png
new file mode 100644 (file)
index 0000000..3676154
Binary files /dev/null and b/WebCore/page/inspector/Images/recordButtons.png differ
diff --git a/WebCore/page/inspector/Images/reloadButtons.png b/WebCore/page/inspector/Images/reloadButtons.png
new file mode 100644 (file)
index 0000000..1e45671
Binary files /dev/null and b/WebCore/page/inspector/Images/reloadButtons.png differ
index 9d7f3c1..cabfe81 100644 (file)
@@ -44,6 +44,23 @@ WebInspector.ProfileView = function(profile)
     this.dataGrid.element.addEventListener("mousedown", this._mouseDownInDataGrid.bind(this), true);
     this.element.appendChild(this.dataGrid.element);
 
+    this.focusButton = document.createElement("button");
+    this.focusButton.title = WebInspector.UIString("Focus selected function.");
+    this.focusButton.className = "focus-profile-node-status-bar-item status-bar-item";
+    this.focusButton.disabled = true;
+    this.focusButton.addEventListener("click", this._focusClicked.bind(this), false);
+
+    this.excludeButton = document.createElement("button");
+    this.excludeButton.title = WebInspector.UIString("Exclude selected function.");
+    this.excludeButton.className = "exclude-profile-node-status-bar-item status-bar-item";
+    this.excludeButton.disabled = true;
+    this.excludeButton.addEventListener("click", this._excludeClicked.bind(this), false);
+
+    this.resetButton = document.createElement("button");
+    this.resetButton.title = WebInspector.UIString("Restore all functions.");
+    this.resetButton.className = "reset-profile-status-bar-item status-bar-item hidden";
+    this.resetButton.addEventListener("click", this._resetClicked.bind(this), false);
+
     // By default the profile isn't sorted, so sort based on our default sort
     // column and direction padded to the DataGrid above.
     profile.head.sortTotalTimeDescending();
@@ -52,6 +69,11 @@ WebInspector.ProfileView = function(profile)
 }
 
 WebInspector.ProfileView.prototype = {
+    get statusBarItems()
+    {
+        return [this.focusButton, this.excludeButton, this.resetButton];
+    },
+
     refresh: function()
     {
         var selectedProfileNode = this.dataGrid.selectedNode ? this.dataGrid.selectedNode.profileNode : null;
@@ -61,7 +83,8 @@ WebInspector.ProfileView.prototype = {
         var children = this.profile.head.children;
         var childrenLength = children.length;
         for (var i = 0; i < childrenLength; ++i)
-            this.dataGrid.appendChild(new WebInspector.ProfileDataGridNode(children[i], this.showSelfTimeAsPercent, this.showTotalTimeAsPercent));
+            if (children[i].visible)
+                this.dataGrid.appendChild(new WebInspector.ProfileDataGridNode(this, children[i]));
 
         if (selectedProfileNode && selectedProfileNode._dataGridNode)
             selectedProfileNode._dataGridNode.selected = true;
@@ -71,13 +94,48 @@ WebInspector.ProfileView.prototype = {
     {
         var child = this.dataGrid.children[0];
         while (child) {
-            child.showTotalTimeAsPercent = this.showTotalTimeAsPercent;
-            child.showSelfTimeAsPercent = this.showSelfTimeAsPercent;
             child.refresh();
             child = child.traverseNextNode(false, null, true);
         }
     },
 
+    _focusClicked: function(event)
+    {
+        if (!this.dataGrid.selectedNode || !this.dataGrid.selectedNode.profileNode)
+            return;
+        this.resetButton.removeStyleClass("hidden");
+        this.profile.focus(this.dataGrid.selectedNode.profileNode);
+        this.refresh();
+    },
+
+    _excludeClicked: function(event)
+    {
+        if (!this.dataGrid.selectedNode || !this.dataGrid.selectedNode.profileNode)
+            return;
+        this.resetButton.removeStyleClass("hidden");
+        this.profile.exclude(this.dataGrid.selectedNode.profileNode);
+        this.refresh();
+    },
+
+    _resetClicked: function(event)
+    {
+        this.resetButton.addStyleClass("hidden");
+        this.profile.restoreAll();
+        this.refresh();
+    },
+
+    _dataGridNodeSelected: function(node)
+    {
+        this.focusButton.disabled = false;
+        this.excludeButton.disabled = false;
+    },
+
+    _dataGridNodeDeselected: function(node)
+    {
+        this.focusButton.disabled = true;
+        this.excludeButton.disabled = true;
+    },
+
     _sortData: function(event)
     {
         var sortOrder = this.dataGrid.sortOrder;
@@ -130,14 +188,13 @@ WebInspector.ProfileView.prototype = {
 
 WebInspector.ProfileView.prototype.__proto__ = WebInspector.View.prototype;
 
-WebInspector.ProfileDataGridNode = function(profileNode, showSelfTimeAsPercent, showTotalTimeAsPercent)
+WebInspector.ProfileDataGridNode = function(profileView, profileNode)
 {
+    this.profileView = profileView;
+
     this.profileNode = profileNode;
     profileNode._dataGridNode = this;
 
-    this.showSelfTimeAsPercent = showSelfTimeAsPercent;
-    this.showTotalTimeAsPercent = showTotalTimeAsPercent;
-
     var hasChildren = (profileNode.children.length ? true : false);
     WebInspector.DataGridNode.call(this, null, hasChildren);
 
@@ -158,12 +215,12 @@ WebInspector.ProfileDataGridNode.prototype = {
         data["function"] = this.profileNode.functionName;
         data["calls"] = this.profileNode.numberOfCalls;
 
-        if (this.showSelfTimeAsPercent)
+        if (this.profileView.showSelfTimeAsPercent)
             data["self"] = WebInspector.UIString("%.2f%%", this.profileNode.selfPercent);
         else
             data["self"] = formatMilliseconds(this.profileNode.selfTime);
 
-        if (this.showTotalTimeAsPercent)
+        if (this.profileView.showTotalTimeAsPercent)
             data["total"] = WebInspector.UIString("%.2f%%", this.profileNode.totalPercent);
         else
             data["total"] = formatMilliseconds(this.profileNode.totalTime);
@@ -197,6 +254,18 @@ WebInspector.ProfileDataGridNode.prototype = {
         return cell;
     },
 
+    select: function(supressSelectedEvent)
+    {
+        WebInspector.DataGridNode.prototype.select.call(this, supressSelectedEvent);
+        this.profileView._dataGridNodeSelected(this);
+    },
+
+    deselect: function(supressDeselectedEvent)
+    {
+        WebInspector.DataGridNode.prototype.deselect.call(this, supressDeselectedEvent);
+        this.profileView._dataGridNodeDeselected(this);
+    },
+
     expand: function()
     {
         WebInspector.DataGridNode.prototype.expand.call(this);
@@ -214,7 +283,8 @@ WebInspector.ProfileDataGridNode.prototype = {
         var children = this.profileNode.children;
         var childrenLength = children.length;
         for (var i = 0; i < childrenLength; ++i)
-            this.appendChild(new WebInspector.ProfileDataGridNode(children[i], this.showSelfTimeAsPercent, this.showTotalTimeAsPercent));
+            if (children[i].visible)
+                this.appendChild(new WebInspector.ProfileDataGridNode(this.profileView, children[i]));
         this.removeEventListener("populate", this._populate, this);
     }
 }
index 95a656b..91a48ab 100644 (file)
@@ -49,6 +49,17 @@ WebInspector.ProfilesPanel = function()
     this.profileViews.id = "profile-views";
     this.element.appendChild(this.profileViews);
 
+    this.recordButton = document.createElement("button");
+    this.recordButton.title = WebInspector.UIString("Start profiling.");
+    this.recordButton.id = "record-profile-status-bar-item";
+    this.recordButton.className = "status-bar-item";
+    this.recordButton.addEventListener("click", this._recordClicked.bind(this), false);
+
+    this.recording = false;
+
+    this.profileViewStatusBarItemsContainer = document.createElement("div");
+    this.profileViewStatusBarItemsContainer.id = "profile-view-status-bar-items";
+
     this.reset();
 }
 
@@ -60,6 +71,11 @@ WebInspector.ProfilesPanel.prototype = {
         return WebInspector.UIString("Profiles");
     },
 
+    get statusBarItems()
+    {
+        return [this.recordButton, this.profileViewStatusBarItemsContainer];
+    },
+
     show: function()
     {
         WebInspector.Panel.prototype.show.call(this);
@@ -126,6 +142,12 @@ WebInspector.ProfilesPanel.prototype = {
         view.show(this.profileViews);
 
         this.visibleProfileView = view;
+
+        this.profileViewStatusBarItemsContainer.removeChildren();
+
+        var statusBarItems = view.statusBarItems;
+        for (var i = 0; i < statusBarItems.length; ++i)
+            this.profileViewStatusBarItemsContainer.appendChild(statusBarItems[i]);
     },
 
     closeVisibleView: function()
@@ -135,6 +157,21 @@ WebInspector.ProfilesPanel.prototype = {
         delete this.visibleProfileView;
     },
 
+    _recordClicked: function()
+    {
+        this.recording = !this.recording;
+
+        if (this.recording) {
+            this.recordButton.addStyleClass("toggled-on");
+            this.recordButton.title = WebInspector.UIString("Stop profiling.");
+            InspectorController.inspectedWindow().console.profile("org.webkit.profiles.user-initiated");
+        } else {
+            this.recordButton.removeStyleClass("toggled-on");
+            this.recordButton.title = WebInspector.UIString("Start profiling.");
+            InspectorController.inspectedWindow().console.profileEnd("org.webkit.profiles.user-initiated");
+        }
+    },
+
     _populateProfiles: function()
     {
         this.sidebarTree.removeChildren();
@@ -187,6 +224,7 @@ WebInspector.ProfilesPanel.prototype = {
 
         this.sidebarElement.style.width = width + "px";
         this.profileViews.style.left = width + "px";
+        this.profileViewStatusBarItemsContainer.style.left = width + "px";
         this.sidebarResizeElement.style.left = (width - 3) + "px";
     }
 }
index 478e9d3..ca548aa 100644 (file)
@@ -280,6 +280,7 @@ button.status-bar-item:active {
 }
 
 button.status-bar-item:disabled {
+    opacity: 0.5;
     background-position: 0 0 !important;
 }
 
@@ -1739,10 +1740,6 @@ body.inactive .data-grid th.sort-ascending, body.inactive .data-grid th.sort-des
     margin-top: 2px;
 }
 
-#scripts-status-bar .status-bar-item:disabled img {
-    opacity: 0.5;
-}
-
 #scripts-back img {
     content: url(Images/back.png);
 }
@@ -2451,6 +2448,16 @@ body.inactive .sidebar-tree-item.selected {
     bottom: 0;
 }
 
+#profile-view-status-bar-items {
+    position: absolute;
+    top: 0;
+    bottom: 0;
+    left: 200px;
+    overflow: hidden;
+    border-left: 1px solid rgb(184, 184, 184);
+    margin-left: -1px;
+}
+
 .profile-sidebar-tree-item .icon {
     content: url(Images/profileIcon.png);
 }
@@ -2507,3 +2514,43 @@ body.inactive .sidebar-tree-item.selected {
 .data-grid:focus tr.selected .profile-node-file {
     color: white;
 }
+
+#record-profile-status-bar-item {
+    background-image: url(Images/recordButtons.png);
+}
+
+#record-profile-status-bar-item:active {
+    background-position: 32px 0;
+}
+
+#record-profile-status-bar-item.toggled-on {
+    background-position: 0 24px;
+}
+
+#record-profile-status-bar-item.toggled-on:active {
+    background-position: 32px 24px;
+}
+
+.focus-profile-node-status-bar-item {
+    background-image: url(Images/focusButtons.png) !important;
+}
+
+.focus-profile-node-status-bar-item:active {
+    background-position: 32px 0;
+}
+
+.exclude-profile-node-status-bar-item {
+    background-image: url(Images/excludeButtons.png) !important;
+}
+
+.exclude-profile-node-status-bar-item:active {
+    background-position: 32px 0;
+}
+
+.reset-profile-status-bar-item {
+    background-image: url(Images/reloadButtons.png) !important;
+}
+
+.reset-profile-status-bar-item:active {
+    background-position: 32px 0;
+}