Web Inspector: Script ProfileViews should be searchable
https://bugs.webkit.org/show_bug.cgi?id=157581
<rdar://problem/26228530>

Reviewed by Joseph Pecoraro.

* UserInterface/Views/DataGrid.js:
(WebInspector.DataGrid.prototype.get filterText):
Make filterText readable.

* UserInterface/Views/ProfileDataGridNode.js:
(WebInspector.ProfileDataGridNode.prototype.get callingContextTreeNode):
(WebInspector.ProfileDataGridNode.prototype.filterableDataForColumn):
Add filterable data for the "function" column.
(WebInspector.ProfileDataGridNode.prototype._updateChildrenForModifiers):
(WebInspector.ProfileDataGridNode.prototype.get node): Deleted.
Renamed callingContextTreeNode to be less ambiguous.

* UserInterface/Views/ProfileDataGridTree.js:
(WebInspector.ProfileDataGridTree.prototype._updateCurrentFocusDetails):

* UserInterface/Views/ProfileView.js:
(WebInspector.ProfileView.prototype.get dataGrid):
Expose data grid for use in parent view.

* UserInterface/Views/ScriptClusterTimelineView.js:
(WebInspector.ScriptClusterTimelineView.prototype.selectRecord):
Drive-by fix: forward property to current child TimelineView.

* UserInterface/Views/ScriptProfileTimelineView.js:
(WebInspector.ScriptProfileTimelineView):
(WebInspector.ScriptProfileTimelineView.prototype._scopeBarSelectionDidChange):
(WebInspector.ScriptProfileTimelineView.prototype._showProfileViewForOrientation):
Helper function to switch profile views. Persist filter text when
switching to the new profile view.

(WebInspector.ScriptProfileTimelineView.prototype.get showsFilterBar): Deleted.
Remove FIXME and show filter bar.

* UserInterface/Views/TimelineView.js:
(WebInspector.TimelineView.prototype.setupDataGrid):
Support switching to a new data grid.
(WebInspector.TimelineView.prototype.dataGridMatchNodeAgainstCustomFilters):
Hooking up filtering causes data grid nodes to be filtered based on the
ruler selection. Although ScriptProfileTimelineView performs its own
time-based filtering, this is necessary to prevent an assert.

(WebInspector.TimelineView.prototype._timelineDataGridSelectedNodeChanged):
(WebInspector.TimelineView.prototype._timelineDataGridNodeWasFiltered):
Converted arrow functions to member functions to allow unregistering
event listeners on outgoing data grid when swapping grids.


git-svn-id: http://svn.webkit.org/repository/webkit/trunk@200873 268f45cc-cd09-0410-ab3c-d52691b4dbfc
diff --git a/Source/WebInspectorUI/ChangeLog b/Source/WebInspectorUI/ChangeLog
index dbb1ba7..a3d51f6 100644
--- a/Source/WebInspectorUI/ChangeLog
+++ b/Source/WebInspectorUI/ChangeLog
@@ -1,3 +1,57 @@
+2016-05-13  Matt Baker  <mattbaker@apple.com>
+
+        Web Inspector: Script ProfileViews should be searchable
+        https://bugs.webkit.org/show_bug.cgi?id=157581
+        <rdar://problem/26228530>
+
+        Reviewed by Joseph Pecoraro.
+
+        * UserInterface/Views/DataGrid.js:
+        (WebInspector.DataGrid.prototype.get filterText):
+        Make filterText readable.
+
+        * UserInterface/Views/ProfileDataGridNode.js:
+        (WebInspector.ProfileDataGridNode.prototype.get callingContextTreeNode):
+        (WebInspector.ProfileDataGridNode.prototype.filterableDataForColumn):
+        Add filterable data for the "function" column.
+        (WebInspector.ProfileDataGridNode.prototype._updateChildrenForModifiers):
+        (WebInspector.ProfileDataGridNode.prototype.get node): Deleted.
+        Renamed callingContextTreeNode to be less ambiguous.
+
+        * UserInterface/Views/ProfileDataGridTree.js:
+        (WebInspector.ProfileDataGridTree.prototype._updateCurrentFocusDetails):
+
+        * UserInterface/Views/ProfileView.js:
+        (WebInspector.ProfileView.prototype.get dataGrid):
+        Expose data grid for use in parent view.
+
+        * UserInterface/Views/ScriptClusterTimelineView.js:
+        (WebInspector.ScriptClusterTimelineView.prototype.selectRecord):
+        Drive-by fix: forward property to current child TimelineView.
+
+        * UserInterface/Views/ScriptProfileTimelineView.js:
+        (WebInspector.ScriptProfileTimelineView):
+        (WebInspector.ScriptProfileTimelineView.prototype._scopeBarSelectionDidChange):
+        (WebInspector.ScriptProfileTimelineView.prototype._showProfileViewForOrientation):
+        Helper function to switch profile views. Persist filter text when
+        switching to the new profile view.
+
+        (WebInspector.ScriptProfileTimelineView.prototype.get showsFilterBar): Deleted.
+        Remove FIXME and show filter bar.
+
+        * UserInterface/Views/TimelineView.js:
+        (WebInspector.TimelineView.prototype.setupDataGrid):
+        Support switching to a new data grid.
+        (WebInspector.TimelineView.prototype.dataGridMatchNodeAgainstCustomFilters):
+        Hooking up filtering causes data grid nodes to be filtered based on the
+        ruler selection. Although ScriptProfileTimelineView performs its own
+        time-based filtering, this is necessary to prevent an assert.
+
+        (WebInspector.TimelineView.prototype._timelineDataGridSelectedNodeChanged):
+        (WebInspector.TimelineView.prototype._timelineDataGridNodeWasFiltered):
+        Converted arrow functions to member functions to allow unregistering
+        event listeners on outgoing data grid when swapping grids.
+
 2016-05-12  Matt Baker  <mattbaker@apple.com>
 
         Web Inspector: Remove "Extra Scripts" folder from Resources sidebar if all children removed
diff --git a/Source/WebInspectorUI/UserInterface/Views/DataGrid.js b/Source/WebInspectorUI/UserInterface/Views/DataGrid.js
index 4205492..ca4b961 100644
--- a/Source/WebInspectorUI/UserInterface/Views/DataGrid.js
+++ b/Source/WebInspectorUI/UserInterface/Views/DataGrid.js
@@ -278,6 +278,8 @@
         this._updateScrollListeners();
     }
 
+    get filterText() { return this._filterText; }
+
     set filterText(x)
     {
         if (this._filterText === x)
diff --git a/Source/WebInspectorUI/UserInterface/Views/ProfileDataGridNode.js b/Source/WebInspectorUI/UserInterface/Views/ProfileDataGridNode.js
index f839332..25f161f 100644
--- a/Source/WebInspectorUI/UserInterface/Views/ProfileDataGridNode.js
+++ b/Source/WebInspectorUI/UserInterface/Views/ProfileDataGridNode.js
@@ -46,7 +46,7 @@
 
     // Public
 
-    get node() { return this._node; }
+    get callingContextTreeNode() { return this._node; }
 
     displayName()
     {
@@ -124,6 +124,21 @@
         contextMenu.appendSeparator();
     }
 
+    // Protected
+
+    filterableDataForColumn(columnIdentifier)
+    {
+        if (columnIdentifier === "function") {
+            let filterableData = [this.displayName()];
+            let script = WebInspector.debuggerManager.scriptForIdentifier(this._node.sourceID);
+            if (script && script.url && this._node.line >= 0 && this._node.column >= 0)
+                filterableData.push(script.url);
+            return filterableData;
+        }
+
+        return super.filterableDataForColumn(columnIdentifier);
+    }
+
     // Private
 
     _updateChildrenForModifiers()
@@ -174,7 +189,7 @@
         // Remove child data grid nodes that have been charged to us.
         if (!this.shouldRefreshChildren && this._childrenToChargeToSelf.size) {
             for (let childDataGridNode of this.children) {
-                if (this._childrenToChargeToSelf.has(childDataGridNode.node))
+                if (this._childrenToChargeToSelf.has(childDataGridNode.callingContextTreeNode))
                     this.removeChild(childDataGridNode);
             }
         }
diff --git a/Source/WebInspectorUI/UserInterface/Views/ProfileDataGridTree.js b/Source/WebInspectorUI/UserInterface/Views/ProfileDataGridTree.js
index 00bc0c2..dcfd2c9 100644
--- a/Source/WebInspectorUI/UserInterface/Views/ProfileDataGridTree.js
+++ b/Source/WebInspectorUI/UserInterface/Views/ProfileDataGridTree.js
@@ -239,7 +239,7 @@
 
     _updateCurrentFocusDetails(focusDataGridNode)
     {
-        let cctNode = focusDataGridNode.node;
+        let cctNode = focusDataGridNode.callingContextTreeNode;
         let {timestamps, duration} = cctNode.filteredTimestampsAndDuration(this._startTime, this._endTime);
 
         this._currentFocusStartTime = timestamps[0];
diff --git a/Source/WebInspectorUI/UserInterface/Views/ProfileView.js b/Source/WebInspectorUI/UserInterface/Views/ProfileView.js
index f40893b..64bb7e3 100644
--- a/Source/WebInspectorUI/UserInterface/Views/ProfileView.js
+++ b/Source/WebInspectorUI/UserInterface/Views/ProfileView.js
@@ -76,6 +76,7 @@
     get callingContextTree() { return this._callingContextTree; }
     get startTime() { return this._startTime; }
     get endTime() { return this._endTime; }
+    get dataGrid() { return this._dataGrid; }
 
     setStartAndEndTime(startTime, endTime)
     {
diff --git a/Source/WebInspectorUI/UserInterface/Views/ScriptClusterTimelineView.js b/Source/WebInspectorUI/UserInterface/Views/ScriptClusterTimelineView.js
index cce1a49..ef2ae30 100644
--- a/Source/WebInspectorUI/UserInterface/Views/ScriptClusterTimelineView.js
+++ b/Source/WebInspectorUI/UserInterface/Views/ScriptClusterTimelineView.js
@@ -71,6 +71,8 @@
     set endTime(x) { this._contentViewContainer.currentContentView.endTime = x; }
     get currentTime() { return this._contentViewContainer.currentContentView.currentTime; }
     set currentTime(x) { this._contentViewContainer.currentContentView.currentTime = x; }
+
+    selectRecord(record) { this._contentViewContainer.currentContentView.selectRecord(record); }
     updateFilter(filters) { return this._contentViewContainer.currentContentView.updateFilter(filters); }
     filterDidChange() { return this._contentViewContainer.currentContentView.filterDidChange(); }
     matchDataGridNodeAgainstCustomFilters(node) { return this._contentViewContainer.currentContentView.matchDataGridNodeAgainstCustomFilters(node); }
diff --git a/Source/WebInspectorUI/UserInterface/Views/ScriptProfileTimelineView.js b/Source/WebInspectorUI/UserInterface/Views/ScriptProfileTimelineView.js
index 1540510..6d59e94 100644
--- a/Source/WebInspectorUI/UserInterface/Views/ScriptProfileTimelineView.js
+++ b/Source/WebInspectorUI/UserInterface/Views/ScriptProfileTimelineView.js
@@ -42,10 +42,7 @@
         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);
+        this._showProfileViewForOrientation(WebInspector.ScriptProfileTimelineView.profileOrientationSetting.value);
 
         let scopeBarItems = [
             new WebInspector.ScopeBarItem(WebInspector.ScriptProfileTimelineView.ProfileOrientation.TopDown, WebInspector.UIString("Top Down"), true),
@@ -61,17 +58,12 @@
         this._updateClearFocusNodesButtonItem();
 
         timeline.addEventListener(WebInspector.Timeline.Event.Refreshed, this._scriptTimelineRecordRefreshed, this);
-
-        // FIXME: Support filtering the ProfileView.
     }
 
     // Public
 
     get showsLiveRecordingData() { return false; }
 
-    // FIXME: <https://webkit.org/b/157581> Web Inspector: Script ProfileViews should be searchable
-    get showsFilterBar() { return false; }
-
     // Protected
 
     closed()
@@ -130,17 +122,9 @@
     {
         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._showProfileViewForOrientation(newOrientation);
 
         this.dispatchEventToListeners(WebInspector.ContentView.Event.SelectionPathComponentsDidChange);
 
@@ -148,6 +132,26 @@
         this.needsLayout();
     }
 
+    _showProfileViewForOrientation(orientation)
+    {
+        let filterText;
+        if (this._profileView) {
+            this._profileView.removeEventListener(WebInspector.ContentView.Event.SelectionPathComponentsDidChange, this._profileViewSelectionPathComponentsDidChange, this);
+            this.removeSubview(this._profileView);
+            filterText = this._profileView.dataGrid.filterText;
+        }
+
+        let callingContextTree = this._callingContextTreeForOrientation(orientation);
+        this._profileView = new WebInspector.ProfileView(callingContextTree);
+        this._profileView.addEventListener(WebInspector.ContentView.Event.SelectionPathComponentsDidChange, this._profileViewSelectionPathComponentsDidChange, this);
+
+        this.addSubview(this._profileView);
+        this.setupDataGrid(this._profileView.dataGrid);
+
+        if (filterText)
+            this._profileView.dataGrid.filterText = filterText;
+    }
+
     _updateClearFocusNodesButtonItem()
     {
         this._clearFocusNodesButtonItem.enabled = this._profileView.hasFocusNodes();
diff --git a/Source/WebInspectorUI/UserInterface/Views/TimelineView.js b/Source/WebInspectorUI/UserInterface/Views/TimelineView.js
index 628c743..56dc701 100644
--- a/Source/WebInspectorUI/UserInterface/Views/TimelineView.js
+++ b/Source/WebInspectorUI/UserInterface/Views/TimelineView.js
@@ -158,25 +158,18 @@
 
     setupDataGrid(dataGrid)
     {
-        console.assert(!this._timelineDataGrid);
+        if (this._timelineDataGrid) {
+            this._timelineDataGrid.filterDelegate = null;
+            this._timelineDataGrid.removeEventListener(WebInspector.DataGrid.Event.SelectedNodeChanged, this._timelineDataGridSelectedNodeChanged, this);
+            this._timelineDataGrid.removeEventListener(WebInspector.DataGrid.Event.NodeWasFiltered, this._timelineDataGridNodeWasFiltered, this);
+            this._timelineDataGrid.removeEventListener(WebInspector.DataGrid.Event.FilterDidChange, this.filterDidChange, this);
+        }
 
         this._timelineDataGrid = dataGrid;
         this._timelineDataGrid.filterDelegate = this;
-        this._timelineDataGrid.addEventListener(WebInspector.DataGrid.Event.SelectedNodeChanged, () => {
-            this.dispatchEventToListeners(WebInspector.ContentView.Event.SelectionPathComponentsDidChange);
-        });
-
-        this._timelineDataGrid.addEventListener(WebInspector.DataGrid.Event.NodeWasFiltered, (event) => {
-            let node = event.data.node;
-            if (!(node instanceof WebInspector.TimelineDataGridNode))
-                return;
-
-            this.dispatchEventToListeners(WebInspector.TimelineView.Event.RecordWasFiltered, {record: node.record, filtered: node.hidden});
-        });
-
-        this._timelineDataGrid.addEventListener(WebInspector.DataGrid.Event.FilterDidChange, (event) => {
-            this.filterDidChange();
-        });
+        this._timelineDataGrid.addEventListener(WebInspector.DataGrid.Event.SelectedNodeChanged, this._timelineDataGridSelectedNodeChanged, this);
+        this._timelineDataGrid.addEventListener(WebInspector.DataGrid.Event.NodeWasFiltered, this._timelineDataGridNodeWasFiltered, this);
+        this._timelineDataGrid.addEventListener(WebInspector.DataGrid.Event.FilterDidChange, this.filterDidChange, this);
     }
 
     selectRecord(record)
@@ -284,6 +277,9 @@
             return checkTimeBounds(record.startTime, record.endTime);
         }
 
+        if (node instanceof WebInspector.ProfileDataGridNode)
+            return node.callingContextTreeNode.hasStackTraceInTimeRange(startTime, endTime);
+
         console.error("Unknown DataGridNode, can't filter by time.");
         return true;
     }
@@ -302,6 +298,20 @@
 
     // Private
 
+    _timelineDataGridSelectedNodeChanged(event)
+    {
+        this.dispatchEventToListeners(WebInspector.ContentView.Event.SelectionPathComponentsDidChange);
+    }
+
+    _timelineDataGridNodeWasFiltered(event)
+    {
+        let node = event.data.node;
+        if (!(node instanceof WebInspector.TimelineDataGridNode))
+            return;
+
+        this.dispatchEventToListeners(WebInspector.TimelineView.Event.RecordWasFiltered, {record: node.record, filtered: node.hidden});
+    }
+
     _timesDidChange()
     {
         if (!WebInspector.timelineManager.isCapturing() || this.showsLiveRecordingData)