Web Inspector: hook up grid row filtering in the new Timelines UI
authormattbaker@apple.com <mattbaker@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Tue, 26 Apr 2016 01:00:17 +0000 (01:00 +0000)
committermattbaker@apple.com <mattbaker@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Tue, 26 Apr 2016 01:00:17 +0000 (01:00 +0000)
https://bugs.webkit.org/show_bug.cgi?id=154924
<rdar://problem/24934607>

Reviewed by Timothy Hatcher.

Re-implement timeline data grid filtering that previously existed in the
navigation sidebar. This patch adds support for filter text, scope bars,
and filtering based on ruler selection.

Multi-column filter support is now part of DataGrid. The grid checks compares
filter text against cell data of type string. DataGridNode subclasses may
provide custom string data for columns that format complex objects (such
as SourceCodeLocations). Cells containing data of type number are not
considered for filtering at this time.

* UserInterface/Views/DataGrid.js:
(WebInspector.DataGrid):
(WebInspector.DataGrid.prototype.set filterText):
(WebInspector.DataGrid.prototype.get filterDelegate):
(WebInspector.DataGrid.prototype.set filterDelegate):
(WebInspector.DataGrid.prototype.filterDidChange):
Called internally by the grid whenever the filter text or delegate changes.
Also called by clients that implement a filter delegate, to inform the
grid that a custom filter has changed.

(WebInspector.DataGrid.prototype.hasCustomFilters):
(WebInspector.DataGrid.prototype.matchNodeAgainstCustomFilters):
Calls the filter delegate, if it exists, and provides a hook for
subclasses to provide custom filtering.

(WebInspector.DataGrid.prototype._applyFiltersToNode.matchTextFilter):
(WebInspector.DataGrid.prototype._applyFiltersToNode.makeVisible):
(WebInspector.DataGrid.prototype._applyFiltersToNode):
Filters data grid nodes and fires filter events as needed.
(WebInspector.DataGrid.prototype._hasFilterDelegate):
Helper function.
(WebInspector.DataGrid.prototype._updateVisibleRows):
Exclude hidden nodes from revealed rows.
(WebInspector.DataGrid.prototype._updateFilter):
Filtering entry point, called on an animation frame. Updates visible
rows if any node was filtered/unfiltered.

(WebInspector.DataGridNode):
(WebInspector.DataGridNode.prototype.get filterableData):
Gets an array of filterable strings for the node.
(WebInspector.DataGridNode.prototype.refresh):
Resets cached filterable strings.
(WebInspector.DataGridNode.prototype.filterableDataForColumn):
Can be overridden by subclasses to provide filterable text for complex
cell data, like as objects formatted as document fragments.

* UserInterface/Views/LayoutTimelineDataGridNode.js:
(WebInspector.LayoutTimelineDataGridNode.prototype.get data):

* UserInterface/Views/LayoutTimelineView.js:
(WebInspector.LayoutTimelineView):
Register grid and remove logic that has been moved to the base class.
(WebInspector.LayoutTimelineView.prototype.filterDidChange):
Update highlight after grid filter change.
(WebInspector.LayoutTimelineView.prototype._dataGridSelectedNodeChanged):
Update highlight when selection changes.
(WebInspector.LayoutTimelineView.prototype.matchTreeElementAgainstCustomFilters): Deleted.
(WebInspector.LayoutTimelineView.prototype.treeElementDeselected): Deleted.
(WebInspector.LayoutTimelineView.prototype._dataGridFiltersDidChange): Deleted.
(WebInspector.LayoutTimelineView.prototype._dataGridNodeSelected): Deleted.
No longer needed.

* UserInterface/Views/NetworkTimelineView.js:
(WebInspector.NetworkTimelineView):
Register grid and remove logic that has been moved to the base class.
(WebInspector.NetworkTimelineView.prototype.matchTreeElementAgainstCustomFilters): Deleted.
(WebInspector.NetworkTimelineView.prototype._dataGridFiltersDidChange): Deleted.
(WebInspector.NetworkTimelineView.prototype._dataGridNodeSelected): Deleted.
No longer needed.

* UserInterface/Views/OverviewTimelineView.js:
(WebInspector.OverviewTimelineView):
Register grid and remove logic that has been moved to the base class.
(WebInspector.OverviewTimelineView.prototype._dataGridNodeSelected): Deleted.
No longer needed.

* UserInterface/Views/RenderingFrameTimelineView.js:
(WebInspector.RenderingFrameTimelineView):
Register grid and remove logic that has been moved to the base class.
(WebInspector.RenderingFrameTimelineView.prototype.get filterStartTime):
(WebInspector.RenderingFrameTimelineView.prototype.get filterEndTime):
Convert selection indices into filter start and end times.
(WebInspector.RenderingFrameTimelineView.prototype.matchDataGridNodeAgainstCustomFilters):
Perform custom filtering on rendering frame duration.
(WebInspector.RenderingFrameTimelineView.prototype._scopeBarSelectionDidChange):
Inform grid of custom filter change.
(WebInspector.RenderingFrameTimelineView.prototype.matchTreeElementAgainstCustomFilters): Deleted.
(WebInspector.RenderingFrameTimelineView.prototype._dataGridNodeSelected): Deleted.
No longer needed.

* UserInterface/Views/ResourceTimelineDataGridNode.js:
(WebInspector.ResourceTimelineDataGridNode.prototype.filterableDataForColumn):
Use URL string for filtering "name" column.

* UserInterface/Views/ScriptClusterTimelineView.js:
(WebInspector.ScriptClusterTimelineView.prototype.updateFilter):
Forwarding for TimelineView API.
(WebInspector.ScriptClusterTimelineView.prototype.matchDataGridNodeAgainstCustomFilters):
(WebInspector.ScriptClusterTimelineView.prototype.matchTreeElementAgainstCustomFilters): Deleted.
Renamed to matchDataGridNodeAgainstCustomFilters.
(WebInspector.ScriptClusterTimelineView.prototype._scriptClusterViewCurrentContentViewDidChange): Deleted.
Removed FIXME comment. Updating TimelineView times is sufficient to trigger filtering.

* UserInterface/Views/ScriptDetailsTimelineView.js:
(WebInspector.ScriptDetailsTimelineView):
Register grid and remove logic that has been moved to the base class.
(WebInspector.ScriptDetailsTimelineView.prototype._dataGridFiltersDidChange): Deleted.
(WebInspector.ScriptDetailsTimelineView.prototype._dataGridNodeSelected): Deleted.
No longer needed.

* UserInterface/Views/ScriptTimelineDataGridNode.js:
(WebInspector.ScriptTimelineDataGridNode.prototype.filterableDataForColumn):
Use main title and subtitle strings for filtering "name" column.
(WebInspector.ScriptTimelineDataGridNode.prototype._createNameCellDocumentFragment):
(WebInspector.ScriptTimelineDataGridNode.prototype._subtitle):
Break out for use in filterableDataForColumn.

* UserInterface/Views/TimelineDataGrid.js:
(WebInspector.TimelineDataGrid):
Cleanup variable names.
(WebInspector.TimelineDataGrid.prototype.hasCustomFilters):
Always true because filtering on ruler selection always occurs.
(WebInspector.TimelineDataGrid.prototype.matchNodeAgainstCustomFilters):
Match nodes against scope bar filters.
(WebInspector.TimelineDataGrid.prototype._scopeBarSelectedItemsDidChange):
Inform grid of custom filter change.
(WebInspector.TimelineDataGrid.prototype.treeElementMatchesActiveScopeFilters): Deleted.
Re-implemented as _nodeMatchesActiveScopeFilters.
(WebInspector.TimelineDataGrid.prototype._updateScopeBarForcedVisibility): Deleted.
Old UI. No longer needed.

* UserInterface/Views/TimelineDataGridNode.js:
(WebInspector.TimelineDataGridNode.prototype.filterableDataForColumn):
Filter strings for SourceCodeLocation and CallFrame objects.

* UserInterface/Views/TimelineRecordingContentView.js:
(WebInspector.TimelineRecordingContentView):
Listen for FilterBar changes and TimelineView record filtering.
(WebInspector.TimelineRecordingContentView.prototype._filterDidChange):
Update grid filters when filter bar changes.
(WebInspector.TimelineRecordingContentView.prototype._recordWasFiltered):
Update overview when records are filtered/unfiltered.
(WebInspector.TimelineRecordingContentView.prototype.filterDidChange): Deleted.
(WebInspector.TimelineRecordingContentView.prototype.recordWasFiltered): Deleted.
(WebInspector.TimelineRecordingContentView.prototype.matchTreeElementAgainstCustomFilters.checkTimeBounds): Deleted.
(WebInspector.TimelineRecordingContentView.prototype.matchTreeElementAgainstCustomFilters): Deleted.
Re-implemented in DataGrid.
(WebInspector.TimelineRecordingContentView.prototype._updateTimes): Deleted.
FIXME comment removed. Filtering occurs when TimelineView times are updated.
(WebInspector.TimelineRecordingContentView.prototype._timeRangeSelectionChanged): Deleted.

* UserInterface/Views/TimelineView.js:
(WebInspector.TimelineView):
(WebInspector.TimelineView.prototype.get navigationItems):
Used by TimelineRecordingContentView to add scope bar items to the
lower content browser's navigation bar.

(WebInspector.TimelineView.prototype.set startTime):
(WebInspector.TimelineView.prototype.set endTime):
(WebInspector.TimelineView.prototype.set currentTime):
Update grid filter when recording times change.
(WebInspector.TimelineView.prototype.get filterStartTime):
(WebInspector.TimelineView.prototype.get filterEndTime):
Let subclasses (RenderingFrameTimelineView) provide filter start/end times.
(WebInspector.TimelineView.prototype.setupDataGrid):
Register the grid used by the TimelineView subclass, allowing the base
class to hook into common event listeners and provide boilerplate functionality.

(WebInspector.TimelineView.prototype.updateFilter):
For data grid views, updates grid filters and sets new filter text.
(WebInspector.TimelineView.prototype.matchDataGridNodeAgainstCustomFilters):
(WebInspector.TimelineView.prototype.dataGridMatchNodeAgainstCustomFilters.checkTimeBounds):
(WebInspector.TimelineView.prototype.dataGridMatchNodeAgainstCustomFilters):
DataGrid filter delegate. Lets subclasses apply custom filters first,
then filters based on ruler selection if needed.

(WebInspector.TimelineView.prototype.filterDidChange):
Hook for subclasses to respond to filter changes.
(WebInspector.TimelineView.prototype._filterTimesDidChange.delayedWork):
(WebInspector.TimelineView.prototype._filterTimesDidChange):
Helper function for coalescing ruler selection updates into a single
filter update.

(WebInspector.TimelineView.prototype.matchTreeElementAgainstCustomFilters): Deleted.
(WebInspector.TimelineView.prototype.filterUpdated): Deleted.
No longer needed.

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

15 files changed:
Source/WebInspectorUI/ChangeLog
Source/WebInspectorUI/UserInterface/Views/DataGrid.js
Source/WebInspectorUI/UserInterface/Views/LayoutTimelineDataGridNode.js
Source/WebInspectorUI/UserInterface/Views/LayoutTimelineView.js
Source/WebInspectorUI/UserInterface/Views/NetworkTimelineView.js
Source/WebInspectorUI/UserInterface/Views/OverviewTimelineView.js
Source/WebInspectorUI/UserInterface/Views/RenderingFrameTimelineView.js
Source/WebInspectorUI/UserInterface/Views/ResourceTimelineDataGridNode.js
Source/WebInspectorUI/UserInterface/Views/ScriptClusterTimelineView.js
Source/WebInspectorUI/UserInterface/Views/ScriptDetailsTimelineView.js
Source/WebInspectorUI/UserInterface/Views/ScriptTimelineDataGridNode.js
Source/WebInspectorUI/UserInterface/Views/TimelineDataGrid.js
Source/WebInspectorUI/UserInterface/Views/TimelineDataGridNode.js
Source/WebInspectorUI/UserInterface/Views/TimelineRecordingContentView.js
Source/WebInspectorUI/UserInterface/Views/TimelineView.js

index d1b2f11..8379626 100644 (file)
@@ -1,3 +1,198 @@
+2016-04-25  Matt Baker  <mattbaker@apple.com>
+
+        Web Inspector: hook up grid row filtering in the new Timelines UI
+        https://bugs.webkit.org/show_bug.cgi?id=154924
+        <rdar://problem/24934607>
+
+        Reviewed by Timothy Hatcher.
+
+        Re-implement timeline data grid filtering that previously existed in the
+        navigation sidebar. This patch adds support for filter text, scope bars,
+        and filtering based on ruler selection.
+
+        Multi-column filter support is now part of DataGrid. The grid checks compares
+        filter text against cell data of type string. DataGridNode subclasses may
+        provide custom string data for columns that format complex objects (such
+        as SourceCodeLocations). Cells containing data of type number are not
+        considered for filtering at this time.
+
+        * UserInterface/Views/DataGrid.js:
+        (WebInspector.DataGrid):
+        (WebInspector.DataGrid.prototype.set filterText):
+        (WebInspector.DataGrid.prototype.get filterDelegate):
+        (WebInspector.DataGrid.prototype.set filterDelegate):
+        (WebInspector.DataGrid.prototype.filterDidChange):
+        Called internally by the grid whenever the filter text or delegate changes.
+        Also called by clients that implement a filter delegate, to inform the
+        grid that a custom filter has changed.
+
+        (WebInspector.DataGrid.prototype.hasCustomFilters):
+        (WebInspector.DataGrid.prototype.matchNodeAgainstCustomFilters):
+        Calls the filter delegate, if it exists, and provides a hook for
+        subclasses to provide custom filtering.
+
+        (WebInspector.DataGrid.prototype._applyFiltersToNode.matchTextFilter):
+        (WebInspector.DataGrid.prototype._applyFiltersToNode.makeVisible):
+        (WebInspector.DataGrid.prototype._applyFiltersToNode):
+        Filters data grid nodes and fires filter events as needed.
+        (WebInspector.DataGrid.prototype._hasFilterDelegate):
+        Helper function.
+        (WebInspector.DataGrid.prototype._updateVisibleRows):
+        Exclude hidden nodes from revealed rows.
+        (WebInspector.DataGrid.prototype._updateFilter):
+        Filtering entry point, called on an animation frame. Updates visible
+        rows if any node was filtered/unfiltered.
+
+        (WebInspector.DataGridNode):
+        (WebInspector.DataGridNode.prototype.get filterableData):
+        Gets an array of filterable strings for the node.
+        (WebInspector.DataGridNode.prototype.refresh):
+        Resets cached filterable strings.
+        (WebInspector.DataGridNode.prototype.filterableDataForColumn):
+        Can be overridden by subclasses to provide filterable text for complex
+        cell data, like as objects formatted as document fragments.
+
+        * UserInterface/Views/LayoutTimelineDataGridNode.js:
+        (WebInspector.LayoutTimelineDataGridNode.prototype.get data):
+
+        * UserInterface/Views/LayoutTimelineView.js:
+        (WebInspector.LayoutTimelineView):
+        Register grid and remove logic that has been moved to the base class.
+        (WebInspector.LayoutTimelineView.prototype.filterDidChange):
+        Update highlight after grid filter change.
+        (WebInspector.LayoutTimelineView.prototype._dataGridSelectedNodeChanged):
+        Update highlight when selection changes.
+        (WebInspector.LayoutTimelineView.prototype.matchTreeElementAgainstCustomFilters): Deleted.
+        (WebInspector.LayoutTimelineView.prototype.treeElementDeselected): Deleted.
+        (WebInspector.LayoutTimelineView.prototype._dataGridFiltersDidChange): Deleted.
+        (WebInspector.LayoutTimelineView.prototype._dataGridNodeSelected): Deleted.
+        No longer needed.
+
+        * UserInterface/Views/NetworkTimelineView.js:
+        (WebInspector.NetworkTimelineView):
+        Register grid and remove logic that has been moved to the base class.
+        (WebInspector.NetworkTimelineView.prototype.matchTreeElementAgainstCustomFilters): Deleted.
+        (WebInspector.NetworkTimelineView.prototype._dataGridFiltersDidChange): Deleted.
+        (WebInspector.NetworkTimelineView.prototype._dataGridNodeSelected): Deleted.
+        No longer needed.
+
+        * UserInterface/Views/OverviewTimelineView.js:
+        (WebInspector.OverviewTimelineView):
+        Register grid and remove logic that has been moved to the base class.
+        (WebInspector.OverviewTimelineView.prototype._dataGridNodeSelected): Deleted.
+        No longer needed.
+
+        * UserInterface/Views/RenderingFrameTimelineView.js:
+        (WebInspector.RenderingFrameTimelineView):
+        Register grid and remove logic that has been moved to the base class.
+        (WebInspector.RenderingFrameTimelineView.prototype.get filterStartTime):
+        (WebInspector.RenderingFrameTimelineView.prototype.get filterEndTime):
+        Convert selection indices into filter start and end times.
+        (WebInspector.RenderingFrameTimelineView.prototype.matchDataGridNodeAgainstCustomFilters):
+        Perform custom filtering on rendering frame duration.
+        (WebInspector.RenderingFrameTimelineView.prototype._scopeBarSelectionDidChange):
+        Inform grid of custom filter change.
+        (WebInspector.RenderingFrameTimelineView.prototype.matchTreeElementAgainstCustomFilters): Deleted.
+        (WebInspector.RenderingFrameTimelineView.prototype._dataGridNodeSelected): Deleted.
+        No longer needed.
+
+        * UserInterface/Views/ResourceTimelineDataGridNode.js:
+        (WebInspector.ResourceTimelineDataGridNode.prototype.filterableDataForColumn):
+        Use URL string for filtering "name" column.
+
+        * UserInterface/Views/ScriptClusterTimelineView.js:
+        (WebInspector.ScriptClusterTimelineView.prototype.updateFilter):
+        Forwarding for TimelineView API.
+        (WebInspector.ScriptClusterTimelineView.prototype.matchDataGridNodeAgainstCustomFilters):
+        (WebInspector.ScriptClusterTimelineView.prototype.matchTreeElementAgainstCustomFilters): Deleted.
+        Renamed to matchDataGridNodeAgainstCustomFilters.
+        (WebInspector.ScriptClusterTimelineView.prototype._scriptClusterViewCurrentContentViewDidChange): Deleted.
+        Removed FIXME comment. Updating TimelineView times is sufficient to trigger filtering.
+
+        * UserInterface/Views/ScriptDetailsTimelineView.js:
+        (WebInspector.ScriptDetailsTimelineView):
+        Register grid and remove logic that has been moved to the base class.
+        (WebInspector.ScriptDetailsTimelineView.prototype._dataGridFiltersDidChange): Deleted.
+        (WebInspector.ScriptDetailsTimelineView.prototype._dataGridNodeSelected): Deleted.
+        No longer needed.
+
+        * UserInterface/Views/ScriptTimelineDataGridNode.js:
+        (WebInspector.ScriptTimelineDataGridNode.prototype.filterableDataForColumn):
+        Use main title and subtitle strings for filtering "name" column.
+        (WebInspector.ScriptTimelineDataGridNode.prototype._createNameCellDocumentFragment):
+        (WebInspector.ScriptTimelineDataGridNode.prototype._subtitle):
+        Break out for use in filterableDataForColumn.
+
+        * UserInterface/Views/TimelineDataGrid.js:
+        (WebInspector.TimelineDataGrid):
+        Cleanup variable names.
+        (WebInspector.TimelineDataGrid.prototype.hasCustomFilters):
+        Always true because filtering on ruler selection always occurs.
+        (WebInspector.TimelineDataGrid.prototype.matchNodeAgainstCustomFilters):
+        Match nodes against scope bar filters.
+        (WebInspector.TimelineDataGrid.prototype._scopeBarSelectedItemsDidChange):
+        Inform grid of custom filter change.
+        (WebInspector.TimelineDataGrid.prototype.treeElementMatchesActiveScopeFilters): Deleted.
+        Re-implemented as _nodeMatchesActiveScopeFilters.
+        (WebInspector.TimelineDataGrid.prototype._updateScopeBarForcedVisibility): Deleted.
+        Old UI. No longer needed.
+
+        * UserInterface/Views/TimelineDataGridNode.js:
+        (WebInspector.TimelineDataGridNode.prototype.filterableDataForColumn):
+        Filter strings for SourceCodeLocation and CallFrame objects.
+
+        * UserInterface/Views/TimelineRecordingContentView.js:
+        (WebInspector.TimelineRecordingContentView):
+        Listen for FilterBar changes and TimelineView record filtering.
+        (WebInspector.TimelineRecordingContentView.prototype._filterDidChange):
+        Update grid filters when filter bar changes.
+        (WebInspector.TimelineRecordingContentView.prototype._recordWasFiltered):
+        Update overview when records are filtered/unfiltered.
+        (WebInspector.TimelineRecordingContentView.prototype.filterDidChange): Deleted.
+        (WebInspector.TimelineRecordingContentView.prototype.recordWasFiltered): Deleted.
+        (WebInspector.TimelineRecordingContentView.prototype.matchTreeElementAgainstCustomFilters.checkTimeBounds): Deleted.
+        (WebInspector.TimelineRecordingContentView.prototype.matchTreeElementAgainstCustomFilters): Deleted.
+        Re-implemented in DataGrid.
+        (WebInspector.TimelineRecordingContentView.prototype._updateTimes): Deleted.
+        FIXME comment removed. Filtering occurs when TimelineView times are updated.
+        (WebInspector.TimelineRecordingContentView.prototype._timeRangeSelectionChanged): Deleted.
+
+        * UserInterface/Views/TimelineView.js:
+        (WebInspector.TimelineView):
+        (WebInspector.TimelineView.prototype.get navigationItems):
+        Used by TimelineRecordingContentView to add scope bar items to the
+        lower content browser's navigation bar.
+
+        (WebInspector.TimelineView.prototype.set startTime):
+        (WebInspector.TimelineView.prototype.set endTime):
+        (WebInspector.TimelineView.prototype.set currentTime):
+        Update grid filter when recording times change.
+        (WebInspector.TimelineView.prototype.get filterStartTime):
+        (WebInspector.TimelineView.prototype.get filterEndTime):
+        Let subclasses (RenderingFrameTimelineView) provide filter start/end times.
+        (WebInspector.TimelineView.prototype.setupDataGrid):
+        Register the grid used by the TimelineView subclass, allowing the base
+        class to hook into common event listeners and provide boilerplate functionality.
+
+        (WebInspector.TimelineView.prototype.updateFilter):
+        For data grid views, updates grid filters and sets new filter text.
+        (WebInspector.TimelineView.prototype.matchDataGridNodeAgainstCustomFilters):
+        (WebInspector.TimelineView.prototype.dataGridMatchNodeAgainstCustomFilters.checkTimeBounds):
+        (WebInspector.TimelineView.prototype.dataGridMatchNodeAgainstCustomFilters):
+        DataGrid filter delegate. Lets subclasses apply custom filters first,
+        then filters based on ruler selection if needed.
+
+        (WebInspector.TimelineView.prototype.filterDidChange):
+        Hook for subclasses to respond to filter changes.
+        (WebInspector.TimelineView.prototype._filterTimesDidChange.delayedWork):
+        (WebInspector.TimelineView.prototype._filterTimesDidChange):
+        Helper function for coalescing ruler selection updates into a single
+        filter update.
+
+        (WebInspector.TimelineView.prototype.matchTreeElementAgainstCustomFilters): Deleted.
+        (WebInspector.TimelineView.prototype.filterUpdated): Deleted.
+        No longer needed.
+
 2016-04-25  Joseph Pecoraro  <pecoraro@apple.com>
 
         Web Inspector: React.js JSXTransformer produces bogus error locations
index db87359..9283932 100644 (file)
@@ -53,6 +53,9 @@ WebInspector.DataGrid = class DataGrid extends WebInspector.View
         this.resizers = [];
         this._columnWidthsInitialized = false;
 
+        this._filterText = "";
+        this._filterDelegate = null;
+
         this.element.className = "data-grid";
         this.element.tabIndex = 0;
         this.element.addEventListener("keydown", this._keyDown.bind(this), false);
@@ -281,6 +284,112 @@ WebInspector.DataGrid = class DataGrid extends WebInspector.View
             this._scrollContainerElement.addEventListener("scroll", this._scrollListener);
     }
 
+    set filterText(x)
+    {
+        if (this._filterText === x)
+            return;
+
+        this._filterText = x;
+        this.filterDidChange();
+    }
+
+    get filterDelegate() { return this._filterDelegate; }
+
+    set filterDelegate(delegate)
+    {
+        this._filterDelegate = delegate;
+        this.filterDidChange();
+    }
+
+    filterDidChange()
+    {
+        if (this._scheduledFilterUpdateIdentifier)
+            return;
+
+        this._scheduledFilterUpdateIdentifier = requestAnimationFrame(this._updateFilter.bind(this));
+    }
+
+    hasCustomFilters()
+    {
+        return this._hasFilterDelegate();
+    }
+
+    matchNodeAgainstCustomFilters(node)
+    {
+        if (!this._hasFilterDelegate())
+            return true;
+        return this._filterDelegate.dataGridMatchNodeAgainstCustomFilters(node);
+    }
+
+    _applyFiltersToNode(node)
+    {
+        if (!this._textFilterRegex && !this.hasCustomFilters()) {
+            // No filters, so make everything visible.
+            node.hidden = false;
+
+            // If the node was expanded during filtering, collapse it again.
+            if (node.expanded && node[WebInspector.DataGrid.WasExpandedDuringFilteringSymbol]) {
+                node[WebInspector.DataGrid.WasExpandedDuringFilteringSymbol] = false;
+                node.collapse();
+            }
+
+            return;
+        }
+
+        let filterableData = node.filterableData || [];
+        let flags = {expandNode: false};
+        let filterRegex = this._textFilterRegex;
+
+        function matchTextFilter()
+        {
+            if (!filterableData.length || !filterRegex)
+                return true;
+
+            if (filterableData.some((value) => filterRegex.test(value))) {
+                flags.expandNode = true;
+                return true;
+            }
+
+            return false;
+        }
+
+        function makeVisible()
+        {
+            // Make this element visible.
+            node.hidden = false;
+
+            // Make the ancestors visible and expand them.
+            let currentAncestor = node.parent;
+            while (currentAncestor && !currentAncestor.root) {
+                currentAncestor.hidden = false;
+
+                // Only expand if the built-in filters matched, not custom filters.
+                if (flags.expandNode && !currentAncestor.expanded) {
+                    currentAncestor[WebInspector.DataGrid.WasExpandedDuringFilteringSymbol] = true;
+                    currentAncestor.expand();
+                }
+
+                currentAncestor = currentAncestor.parent;
+            }
+        }
+
+        if (matchTextFilter() && this.matchNodeAgainstCustomFilters(node)) {
+            // Make the node visible since it matches.
+            makeVisible();
+
+            // If the node didn't match a built-in filter and was expanded earlier during filtering, collapse it again.
+            if (!flags.expandNode && node.expanded && node[WebInspector.DataGrid.WasExpandedDuringFilteringSymbol]) {
+                node[WebInspector.DataGrid.WasExpandedDuringFilteringSymbol] = false;
+                node.collapse();
+            }
+
+            return;
+        }
+
+        // Make the node invisible since it does not match.
+        node.hidden = true;
+    }
+
     _updateSortedColumn(oldSortColumnIdentifier)
     {
         if (this._sortColumnIdentifierSetting)
@@ -301,6 +410,11 @@ WebInspector.DataGrid = class DataGrid extends WebInspector.View
         this.dispatchEventToListeners(WebInspector.DataGrid.Event.SortChanged);
     }
 
+    _hasFilterDelegate()
+    {
+        return this._filterDelegate && typeof this._filterDelegate.dataGridMatchNodeAgainstCustomFilters === "function";
+    }
+
     _ondblclick(event)
     {
         if (this._editing || this._editingNode)
@@ -867,7 +981,7 @@ WebInspector.DataGrid = class DataGrid extends WebInspector.View
         let rowHeight = this.rowHeight;
         let updateOffsetThreshold = rowHeight * 5;
 
-        let revealedRows = this._rows.filter((row) => row.revealed);
+        let revealedRows = this._rows.filter((row) => row.revealed && !row.hidden);
 
         let scrollTop = this._scrollContainerElement.scrollTop;
         let scrollHeight = this._scrollContainerElement.offsetHeight;
@@ -1569,13 +1683,51 @@ WebInspector.DataGrid = class DataGrid extends WebInspector.View
 
         this._currentResizer = null;
     }
+
+    _updateFilter()
+    {
+        if (this._scheduledFilterUpdateIdentifier) {
+            cancelAnimationFrame(this._scheduledFilterUpdateIdentifier);
+            this._scheduledFilterUpdateIdentifier = undefined;
+        }
+
+        if (!this._rows.length)
+            return;
+
+        this._textFilterRegex = simpleGlobStringToRegExp(this._filterText, "i");
+
+        // Don't populate if we don't have any active filters.
+        // We only need to populate when a filter needs to reveal.
+        let dontPopulate = !this._textFilterRegex && !this.hasCustomFilters();
+
+        let filterDidModifyNode = false;
+        let currentNode = this._rows[0];
+        while (currentNode && !currentNode.root) {
+            const currentNodeWasHidden = currentNode.hidden;
+            this._applyFiltersToNode(currentNode);
+            if (currentNodeWasHidden !== currentNode.hidden) {
+                this.dispatchEventToListeners(WebInspector.DataGrid.Event.NodeWasFiltered, {node: currentNode});
+                filterDidModifyNode = true;
+            }
+
+            currentNode = currentNode.traverseNextNode(false, null, dontPopulate);
+        }
+
+        if (!filterDidModifyNode)
+            return;
+
+        this._updateVisibleRows();
+        this.dispatchEventToListeners(WebInspector.DataGrid.Event.FilterDidChange);
+    }
 };
 
 WebInspector.DataGrid.Event = {
     SortChanged: "datagrid-sort-changed",
     SelectedNodeChanged: "datagrid-selected-node-changed",
     ExpandedNode: "datagrid-expanded-node",
-    CollapsedNode: "datagrid-collapsed-node"
+    CollapsedNode: "datagrid-collapsed-node",
+    FilterDidChange: "datagrid-filter-did-change",
+    NodeWasFiltered: "datagrid-node-was-filtered"
 };
 
 WebInspector.DataGrid.ResizeMethod = {
@@ -1592,6 +1744,7 @@ WebInspector.DataGrid.SortOrder = {
 
 WebInspector.DataGrid.PreviousColumnOrdinalSymbol = Symbol("previous-column-ordinal");
 WebInspector.DataGrid.NextColumnOrdinalSymbol = Symbol("next-column-ordinal");
+WebInspector.DataGrid.WasExpandedDuringFilteringSymbol = Symbol("was-expanded-during-filtering");
 
 WebInspector.DataGrid.ColumnResizePadding = 10;
 WebInspector.DataGrid.CenterResizerOverBorderAdjustment = 3;
@@ -1726,6 +1879,30 @@ WebInspector.DataGridNode = class DataGridNode extends WebInspector.Object
         this.needsRefresh();
     }
 
+    get filterableData()
+    {
+        if (this._cachedFilterableData)
+            return this._cachedFilterableData;
+
+        this._cachedFilterableData = [];
+
+        for (let column of this.dataGrid.columns.values()) {
+            let value = this.filterableDataForColumn(column.columnIdentifier);
+            if (!value)
+                continue;
+
+            if (!(value instanceof Array))
+                value = [value];
+
+            if (!value.length)
+                continue;
+
+            this._cachedFilterableData = this._cachedFilterableData.concat(value);
+        }
+
+        return this._cachedFilterableData;
+    }
+
     get revealed()
     {
         if ("_revealed" in this)
@@ -1856,6 +2033,7 @@ WebInspector.DataGridNode = class DataGridNode extends WebInspector.Object
             this._scheduledRefreshIdentifier = undefined;
         }
 
+        this._cachedFilterableData = null;
         this._needsRefresh = false;
 
         this._element.removeChildren();
@@ -2254,6 +2432,14 @@ WebInspector.DataGridNode = class DataGridNode extends WebInspector.Object
         // Subclasses may override
         return null;
     }
+
+    // Protected
+
+    filterableDataForColumn(columnIdentifier)
+    {
+        let value = this.data[columnIdentifier];
+        return typeof value === "string" ? value : null;
+    }
 };
 
 // Used to create a new table row when entering new data by editing cells.
index b859efc..ab0266e 100644 (file)
@@ -44,6 +44,7 @@ WebInspector.LayoutTimelineDataGridNode = class LayoutTimelineDataGridNode exten
     {
         if (!this._cachedData) {
             this._cachedData = {
+                type: this._record.eventType,
                 name: this.displayName(),
                 width: this._record.width,
                 height: this._record.height,
index e1b9f99..6575e50 100644 (file)
@@ -31,7 +31,7 @@ WebInspector.LayoutTimelineView = class LayoutTimelineView extends WebInspector.
 
         console.assert(timeline.type === WebInspector.TimelineRecord.Type.Layout, timeline);
 
-        let columns = {name: {}, location: {}, width: {}, height: {}, startTime: {}, totalTime: {}};
+        let columns = {type: {}, name: {}, location: {}, width: {}, height: {}, startTime: {}, totalTime: {}};
 
         columns.name.title = WebInspector.UIString("Type");
         columns.name.width = "15%";
@@ -42,11 +42,13 @@ WebInspector.LayoutTimelineView = class LayoutTimelineView extends WebInspector.
             typeToLabelMap.set(value, WebInspector.LayoutTimelineRecord.displayNameForEventType(value));
         }
 
-        columns.name.scopeBar = WebInspector.TimelineDataGrid.createColumnScopeBar("layout", typeToLabelMap);
+        columns.type.scopeBar = WebInspector.TimelineDataGrid.createColumnScopeBar("layout", typeToLabelMap);
+        columns.type.hidden = true;
+
         columns.name.disclosure = true;
         columns.name.icon = true;
 
-        this._scopeBar = columns.name.scopeBar;
+        this._scopeBar = columns.type.scopeBar;
 
         columns.location.title = WebInspector.UIString("Initiator");
         columns.location.width = "25%";
@@ -69,8 +71,9 @@ WebInspector.LayoutTimelineView = class LayoutTimelineView extends WebInspector.
             columns[column].sortable = true;
 
         this._dataGrid = new WebInspector.LayoutTimelineDataGrid(columns);
-        this._dataGrid.addEventListener(WebInspector.TimelineDataGrid.Event.FiltersDidChange, this._dataGridFiltersDidChange, this);
-        this._dataGrid.addEventListener(WebInspector.DataGrid.Event.SelectedNodeChanged, this._dataGridNodeSelected, this);
+        this._dataGrid.addEventListener(WebInspector.DataGrid.Event.SelectedNodeChanged, this._dataGridSelectedNodeChanged, this);
+
+        this.setupDataGrid(this._dataGrid);
 
         this._dataGrid.sortColumnIdentifierSetting = new WebInspector.Setting("layout-timeline-view-sort", "startTime");
         this._dataGrid.sortOrderSetting = new WebInspector.Setting("layout-timeline-view-sort-order", WebInspector.DataGrid.SortOrder.Ascending);
@@ -141,18 +144,6 @@ WebInspector.LayoutTimelineView = class LayoutTimelineView extends WebInspector.
         this._dataGrid.closed();
     }
 
-    filterDidChange()
-    {
-        super.filterDidChange();
-
-        this._updateHighlight();
-    }
-
-    matchTreeElementAgainstCustomFilters(treeElement)
-    {
-        return this._dataGrid.treeElementMatchesActiveScopeFilters(treeElement);
-    }
-
     reset()
     {
         super.reset();
@@ -174,9 +165,9 @@ WebInspector.LayoutTimelineView = class LayoutTimelineView extends WebInspector.
         dataGridNode.revealAndSelect();
     }
 
-    treeElementDeselected(treeElement)
+    filterDidChange()
     {
-        super.treeElementDeselected(treeElement);
+        super.filterDidChange();
 
         this._updateHighlight();
     }
@@ -246,16 +237,6 @@ WebInspector.LayoutTimelineView = class LayoutTimelineView extends WebInspector.
         this.needsLayout();
     }
 
-    _dataGridFiltersDidChange(event)
-    {
-        // FIXME: <https://webkit.org/b/154924> Web Inspector: hook up grid row filtering in the new Timelines UI
-    }
-
-    _dataGridNodeSelected(event)
-    {
-        this.dispatchEventToListeners(WebInspector.ContentView.Event.SelectionPathComponentsDidChange);
-    }
-
     _updateHighlight()
     {
         var record = this._hoveredOrSelectedRecord();
@@ -327,4 +308,9 @@ WebInspector.LayoutTimelineView = class LayoutTimelineView extends WebInspector.
         this._hoveredDataGridNode = null;
         this._updateHighlight();
     }
+
+    _dataGridSelectedNodeChanged(event)
+    {
+        this._updateHighlight();
+    }
 };
index c62faa9..5f2208c 100644 (file)
@@ -88,12 +88,12 @@ WebInspector.NetworkTimelineView = class NetworkTimelineView extends WebInspecto
             columns[column].sortable = true;
 
         this._dataGrid = new WebInspector.TimelineDataGrid(columns);
-        this._dataGrid.addEventListener(WebInspector.TimelineDataGrid.Event.FiltersDidChange, this._dataGridFiltersDidChange, this);
-        this._dataGrid.addEventListener(WebInspector.DataGrid.Event.SelectedNodeChanged, this._dataGridNodeSelected, this);
         this._dataGrid.sortDelegate = this;
         this._dataGrid.sortColumnIdentifierSetting = new WebInspector.Setting("network-timeline-view-sort", "requestSent");
         this._dataGrid.sortOrderSetting = new WebInspector.Setting("network-timeline-view-sort-order", WebInspector.DataGrid.SortOrder.Ascending);
 
+        this.setupDataGrid(this._dataGrid);
+
         this.element.classList.add("network");
         this.addSubview(this._dataGrid);
 
@@ -139,11 +139,6 @@ WebInspector.NetworkTimelineView = class NetworkTimelineView extends WebInspecto
         this._dataGrid.closed();
     }
 
-    matchTreeElementAgainstCustomFilters(treeElement)
-    {
-        return this._dataGrid.treeElementMatchesActiveScopeFilters(treeElement);
-    }
-
     reset()
     {
         super.reset();
@@ -235,14 +230,4 @@ WebInspector.NetworkTimelineView = class NetworkTimelineView extends WebInspecto
 
         this.needsLayout();
     }
-
-    _dataGridFiltersDidChange(event)
-    {
-        // FIXME: <https://webkit.org/b/154924> Web Inspector: hook up grid row filtering in the new Timelines UI
-    }
-
-    _dataGridNodeSelected(event)
-    {
-        this.dispatchEventToListeners(WebInspector.ContentView.Event.SelectionPathComponentsDidChange);
-    }
 };
index 41541d7..c65bd10 100644 (file)
@@ -45,7 +45,8 @@ WebInspector.OverviewTimelineView = class OverviewTimelineView extends WebInspec
         columns.graph.headerView = this._timelineRuler;
 
         this._dataGrid = new WebInspector.DataGrid(columns);
-        this._dataGrid.addEventListener(WebInspector.DataGrid.Event.SelectedNodeChanged, this._dataGridNodeSelected, this);
+
+        this.setupDataGrid(this._dataGrid);
 
         this._currentTimeMarker = new WebInspector.TimelineMarker(0, WebInspector.TimelineMarker.Type.CurrentTime);
         this._timelineRuler.addMarker(this._currentTimeMarker);
@@ -294,11 +295,6 @@ WebInspector.OverviewTimelineView = class OverviewTimelineView extends WebInspec
         this._timelineRuler.addMarker(event.data.marker);
     }
 
-    _dataGridNodeSelected(event)
-    {
-        this.dispatchEventToListeners(WebInspector.ContentView.Event.SelectionPathComponentsDidChange);
-    }
-
     _recordingReset(event)
     {
         this._timelineRuler.clearMarkers();
index 6e285c6..4c24af4 100644 (file)
@@ -77,10 +77,11 @@ WebInspector.RenderingFrameTimelineView = class RenderingFrameTimelineView exten
             columns[column].sortable = true;
 
         this._dataGrid = new WebInspector.TimelineDataGrid(columns);
-        this._dataGrid.addEventListener(WebInspector.DataGrid.Event.SelectedNodeChanged, this._dataGridNodeSelected, this);
         this._dataGrid.sortColumnIdentifierSetting = new WebInspector.Setting("rendering-frame-timeline-view-sort", "startTime");
         this._dataGrid.sortOrderSetting = new WebInspector.Setting("rendering-frame-timeline-view-sort-order", WebInspector.DataGrid.SortOrder.Ascending);
 
+        this.setupDataGrid(this._dataGrid);
+
         this.element.classList.add("rendering-frame");
         this.addSubview(this._dataGrid);
 
@@ -151,22 +152,24 @@ WebInspector.RenderingFrameTimelineView = class RenderingFrameTimelineView exten
         return pathComponents;
     }
 
-    matchTreeElementAgainstCustomFilters(treeElement)
+    get filterStartTime()
     {
-        console.assert(this._scopeBar.selectedItems.length === 1);
-        var selectedScopeBarItem = this._scopeBar.selectedItems[0];
-        if (!selectedScopeBarItem || selectedScopeBarItem.id === WebInspector.RenderingFrameTimelineView.DurationFilter.All)
-            return true;
+        let records = this.representedObject.records;
+        let startIndex = this.startTime;
+        if (startIndex >= records.length)
+            return Infinity;
 
-        while (treeElement && !(treeElement.record instanceof WebInspector.RenderingFrameTimelineRecord))
-            treeElement = treeElement.parent;
+        return records[startIndex].startTime;
+    }
 
-        console.assert(treeElement, "Cannot apply duration filter: no RenderingFrameTimelineRecord found.");
-        if (!treeElement)
-            return false;
+    get filterEndTime()
+    {
+        let records = this.representedObject.records;
+        let endIndex = this.endTime - 1;
+        if (endIndex >= records.length)
+            return Infinity;
 
-        var minimumDuration = selectedScopeBarItem.id === WebInspector.RenderingFrameTimelineView.DurationFilter.OverOneMillisecond ? 0.001 : 0.015;
-        return treeElement.record.duration > minimumDuration;
+        return records[endIndex].endTime;
     }
 
     reset()
@@ -195,6 +198,28 @@ WebInspector.RenderingFrameTimelineView = class RenderingFrameTimelineView exten
         return null;
     }
 
+    matchDataGridNodeAgainstCustomFilters(node)
+    {
+        if (!super.matchDataGridNodeAgainstCustomFilters(node))
+            return false;
+
+        console.assert(node instanceof WebInspector.TimelineDataGridNode);
+        console.assert(this._scopeBar.selectedItems.length === 1);
+        let selectedScopeBarItem = this._scopeBar.selectedItems[0];
+        if (!selectedScopeBarItem || selectedScopeBarItem.id === WebInspector.RenderingFrameTimelineView.DurationFilter.All)
+            return true;
+
+        while (node && !(node.record instanceof WebInspector.RenderingFrameTimelineRecord))
+            node = node.parent;
+
+        console.assert(node, "Cannot apply duration filter: no RenderingFrameTimelineRecord found.");
+        if (!node)
+            return false;
+
+        let minimumDuration = selectedScopeBarItem.id === WebInspector.RenderingFrameTimelineView.DurationFilter.OverOneMillisecond ? 0.001 : 0.015;
+        return node.record.duration > minimumDuration;
+    }
+
     layout()
     {
         this._processPendingRecords();
@@ -264,14 +289,9 @@ WebInspector.RenderingFrameTimelineView = class RenderingFrameTimelineView exten
         this.needsLayout();
     }
 
-    _dataGridNodeSelected(event)
-    {
-        this.dispatchEventToListeners(WebInspector.ContentView.Event.SelectionPathComponentsDidChange);
-    }
-
-    _scopeBarSelectionDidChange(event)
+    _scopeBarSelectionDidChange()
     {
-        // FIXME: <https://webkit.org/b/154924> Web Inspector: hook up grid row filtering in the new Timelines UI
+        this.filterDidChange();
     }
 };
 
index 4ff372f..ffd130d 100644 (file)
@@ -146,6 +146,15 @@ WebInspector.ResourceTimelineDataGridNode = class ResourceTimelineDataGridNode e
         return [WebInspector.ResourceTreeElement.ResourceIconStyleClassName, this.resource.type];
     }
 
+    // Protected
+
+    filterableDataForColumn(columnIdentifier)
+    {
+        if (columnIdentifier === "name")
+            return this._resource.url;
+        return super.filterableDataForColumn(columnIdentifier);
+    }
+
     // Private
 
     _createNameCellDocumentFragment()
index 6c41787..2521e92 100644 (file)
@@ -71,8 +71,9 @@ WebInspector.ScriptClusterTimelineView = class ScriptClusterTimelineView extends
     set currentTime(x) { this._contentViewContainer.currentContentView.currentTime = x; }
     get navigationSidebarTreeOutline() { return this._contentViewContainer.currentContentView.navigationSidebarTreeOutline; }
     reset() { return this._contentViewContainer.currentContentView.reset(); }
+    updateFilter(filters) { return this._contentViewContainer.currentContentView.updateFilter(filters); }
     filterDidChange() { return this._contentViewContainer.currentContentView.filterDidChange(); }
-    matchTreeElementAgainstCustomFilters(treeElement) { return this._contentViewContainer.currentContentView.matchTreeElementAgainstCustomFilters(treeElement); }
+    matchDataGridNodeAgainstCustomFilters(node) { return this._contentViewContainer.currentContentView.matchDataGridNodeAgainstCustomFilters(node); }
 
     // Public
 
@@ -196,8 +197,6 @@ WebInspector.ScriptClusterTimelineView = class ScriptClusterTimelineView extends
         currentContentView.startTime = previousContentView.startTime;
         currentContentView.endTime = previousContentView.endTime;
         currentContentView.currentTime = previousContentView.currentTime;
-
-        // FIXME: <https://webkit.org/b/154924> Web Inspector: hook up grid row filtering in the new Timelines UI
     }
 };
 
index 4120902..cd6bfc3 100644 (file)
@@ -72,12 +72,12 @@ WebInspector.ScriptDetailsTimelineView = class ScriptDetailsTimelineView extends
             columns[column].sortable = true;
 
         this._dataGrid = new WebInspector.ScriptTimelineDataGrid(columns);
-        this._dataGrid.addEventListener(WebInspector.TimelineDataGrid.Event.FiltersDidChange, this._dataGridFiltersDidChange, this);
-        this._dataGrid.addEventListener(WebInspector.DataGrid.Event.SelectedNodeChanged, this._dataGridNodeSelected, this);
         this._dataGrid.sortDelegate = this;
         this._dataGrid.sortColumnIdentifierSetting = new WebInspector.Setting("script-timeline-view-sort", "startTime");
         this._dataGrid.sortOrderSetting = new WebInspector.Setting("script-timeline-view-sort-order", WebInspector.DataGrid.SortOrder.Ascending);
 
+        this.setupDataGrid(this._dataGrid);
+
         this.element.classList.add("script");
         this.addSubview(this._dataGrid);
 
@@ -236,14 +236,4 @@ WebInspector.ScriptDetailsTimelineView = class ScriptDetailsTimelineView extends
     {
         this.needsLayout();
     }
-
-    _dataGridFiltersDidChange(event)
-    {
-        // FIXME: <https://webkit.org/b/154924> Web Inspector: hook up grid row filtering in the new Timelines UI
-    }
-
-    _dataGridNodeSelected(event)
-    {
-        this.dispatchEventToListeners(WebInspector.ContentView.Event.SelectionPathComponentsDidChange);
-    }
 };
index 3fb484e..8650086 100644 (file)
@@ -156,6 +156,16 @@ WebInspector.ScriptTimelineDataGridNode = class ScriptTimelineDataGridNode exten
         return super.createCellContent(columnIdentifier, cell);
     }
 
+    // Protected
+
+    filterableDataForColumn(columnIdentifier)
+    {
+        if (columnIdentifier === "name")
+            return [this.displayName(), this.subtitle];
+
+        return super.filterableDataForColumn(columnIdentifier);
+    }
+
     // Private
 
     _createNameCellDocumentFragment(cellElement)
index 7f3e3ea..def13e9 100644 (file)
 
 WebInspector.TimelineDataGrid = class TimelineDataGrid extends WebInspector.DataGrid
 {
-    constructor(columns, treeOutline, delegate, editCallback, deleteCallback)
+    constructor(columns, treeOutline, synchronizerDelegate, editCallback, deleteCallback)
     {
         super(columns, editCallback, deleteCallback);
 
         if (treeOutline)
-            this._treeOutlineDataGridSynchronizer = new WebInspector.TreeOutlineDataGridSynchronizer(treeOutline, this, delegate);
+            this._treeOutlineDataGridSynchronizer = new WebInspector.TreeOutlineDataGridSynchronizer(treeOutline, this, synchronizerDelegate);
 
         this.element.classList.add("timeline");
 
-        this._filterableColumns = [];
         this._sortDelegate = null;
+        this._scopeBarColumns = [];
 
         // Check if any of the cells can be filtered.
         for (var [identifier, column] of this.columns) {
@@ -44,12 +44,12 @@ WebInspector.TimelineDataGrid = class TimelineDataGrid extends WebInspector.Data
             if (!scopeBar)
                 continue;
 
-            this._filterableColumns.push(identifier);
+            this._scopeBarColumns.push(identifier);
             scopeBar.columnIdentifier = identifier;
             scopeBar.addEventListener(WebInspector.ScopeBar.Event.SelectionChanged, this._scopeBarSelectedItemsDidChange, this);
         }
 
-        if (this._filterableColumns.length > 1) {
+        if (this._scopeBarColumns.length > 1) {
             console.error("Creating a TimelineDataGrid with more than one filterable column is not yet supported.");
             return;
         }
@@ -149,31 +149,6 @@ WebInspector.TimelineDataGrid = class TimelineDataGrid extends WebInspector.Data
         return null;
     }
 
-    treeElementMatchesActiveScopeFilters(treeElement)
-    {
-        if (!this._treeOutlineDataGridSynchronizer)
-            return false;
-
-        var dataGridNode = this._treeOutlineDataGridSynchronizer.dataGridNodeForTreeElement(treeElement);
-        console.assert(dataGridNode);
-
-        for (var identifier of this._filterableColumns) {
-            var scopeBar = this.columns.get(identifier).scopeBar;
-            if (!scopeBar || scopeBar.defaultItem.selected)
-                continue;
-
-            var value = dataGridNode.data[identifier];
-            var matchesFilter = scopeBar.selectedItems.some(function(scopeBarItem) {
-                return scopeBarItem.value === value;
-            });
-
-            if (!matchesFilter)
-                return false;
-        }
-
-        return true;
-    }
-
     addRowInSortOrder(treeElement, dataGridNode, parentTreeElementOrDataGridNode)
     {
         let parentDataGridNode;
@@ -235,6 +210,29 @@ WebInspector.TimelineDataGrid = class TimelineDataGrid extends WebInspector.Data
         this._scheduledDataGridNodeRefreshIdentifier = requestAnimationFrame(this._refreshDirtyDataGridNodes.bind(this));
     }
 
+    hasCustomFilters()
+    {
+        return true;
+    }
+
+    matchNodeAgainstCustomFilters(node)
+    {
+        if (!super.matchNodeAgainstCustomFilters(node))
+            return false;
+
+        for (let identifier of this._scopeBarColumns) {
+            let scopeBar = this.columns.get(identifier).scopeBar;
+            if (!scopeBar || scopeBar.defaultItem.selected)
+                continue;
+
+            let value = node.data[identifier];
+            if (!scopeBar.selectedItems.some((scopeBarItem) => scopeBarItem.value === value))
+                return false;
+        }
+
+        return true;
+    }
+
     // Private
 
     _refreshDirtyDataGridNodes()
@@ -420,23 +418,9 @@ WebInspector.TimelineDataGrid = class TimelineDataGrid extends WebInspector.Data
         return (value1 < value2 ? -1 : (value1 > value2 ? 1 : 0)) * sortDirection;
     }
 
-    _updateScopeBarForcedVisibility()
-    {
-        for (var identifier of this._filterableColumns) {
-            var scopeBar = this.columns.get(identifier).scopeBar;
-            if (scopeBar) {
-                this.element.classList.toggle(WebInspector.TimelineDataGrid.HasNonDefaultFilterStyleClassName, scopeBar.hasNonDefaultItemSelected());
-                break;
-            }
-        }
-    }
-
     _scopeBarSelectedItemsDidChange(event)
     {
-        this._updateScopeBarForcedVisibility();
-
-        var columnIdentifier = event.target.columnIdentifier;
-        this.dispatchEventToListeners(WebInspector.TimelineDataGrid.Event.FiltersDidChange, {columnIdentifier});
+        this.filterDidChange();
     }
 
     _dataGridSelectedNodeChanged(event)
@@ -564,11 +548,8 @@ WebInspector.TimelineDataGrid = class TimelineDataGrid extends WebInspector.Data
     }
 };
 
+WebInspector.TimelineDataGrid.WasExpandedDuringFilteringSymbol = Symbol("was-expanded-during-filtering");
+
 WebInspector.TimelineDataGrid.HasNonDefaultFilterStyleClassName = "has-non-default-filter";
 WebInspector.TimelineDataGrid.DelayedPopoverShowTimeout = 250;
 WebInspector.TimelineDataGrid.DelayedPopoverHideContentClearTimeout = 500;
-
-WebInspector.TimelineDataGrid.Event = {
-    FiltersDidChange: "timelinedatagrid-filters-did-change"
-};
-
index c4a7763..36cce5b 100644 (file)
@@ -364,4 +364,16 @@ WebInspector.TimelineDataGridNode = class TimelineDataGridNode extends WebInspec
 
         return true;
     }
+
+    filterableDataForColumn(columnIdentifier)
+    {
+        let value = this.data[columnIdentifier];
+        if (value instanceof WebInspector.SourceCodeLocation)
+            return value.displayLocationString();
+
+        if (value instanceof WebInspector.CallFrame)
+            return [value.functionName, value.sourceCodeLocation.displayLocationString()];
+
+        return super.filterableDataForColumn(columnIdentifier);
+    }
 };
index 1d3afcf..95215fd 100644 (file)
@@ -53,6 +53,7 @@ WebInspector.TimelineRecordingContentView = class TimelineRecordingContentView e
 
         this._filterBarNavigationItem = new WebInspector.FilterBarNavigationItem;
         this._filterBarNavigationItem.filterBar.placeholder = WebInspector.UIString("Filter Records");
+        this._filterBarNavigationItem.filterBar.addEventListener(WebInspector.FilterBar.Event.FilterDidChange, this._filterDidChange, this);
         this._timelineContentBrowser.navigationBar.addNavigationItem(this._filterBarNavigationItem);
         this.addSubview(this._timelineContentBrowser);
 
@@ -85,6 +86,8 @@ WebInspector.TimelineRecordingContentView = class TimelineRecordingContentView e
         WebInspector.ContentView.addEventListener(WebInspector.ContentView.Event.SelectionPathComponentsDidChange, this._contentViewSelectionPathComponentDidChange, this);
         WebInspector.ContentView.addEventListener(WebInspector.ContentView.Event.SupplementalRepresentedObjectsDidChange, this._contentViewSupplementalRepresentedObjectsDidChange, this);
 
+        WebInspector.TimelineView.addEventListener(WebInspector.TimelineView.Event.RecordWasFiltered, this._recordWasFiltered, this);
+
         for (let instrument of this._recording.instruments)
             this._instrumentAdded(instrument);
 
@@ -215,94 +218,6 @@ WebInspector.TimelineRecordingContentView = class TimelineRecordingContentView e
         this._timelineContentBrowser.goForward();
     }
 
-    filterDidChange()
-    {
-        if (!this.currentTimelineView)
-            return;
-
-        this.currentTimelineView.filterDidChange();
-    }
-
-    recordWasFiltered(record, filtered)
-    {
-        if (!this.currentTimelineView)
-            return;
-
-        this._timelineOverview.recordWasFiltered(this.currentTimelineView.representedObject, record, filtered);
-    }
-
-    matchTreeElementAgainstCustomFilters(treeElement)
-    {
-        if (this.currentTimelineView && !this.currentTimelineView.matchTreeElementAgainstCustomFilters(treeElement))
-            return false;
-
-        let startTime = this._timelineOverview.selectionStartTime;
-        let endTime = startTime + this._timelineOverview.selectionDuration;
-        let currentTime = this._currentTime || this._recording.startTime;
-
-        if (this._timelineOverview.viewMode === WebInspector.TimelineOverview.ViewMode.RenderingFrames) {
-            console.assert(this._renderingFrameTimeline);
-
-            if (this._renderingFrameTimeline && this._renderingFrameTimeline.records.length) {
-                let records = this._renderingFrameTimeline.records;
-                let startIndex = this._timelineOverview.timelineRuler.snapInterval ? startTime : Math.floor(startTime);
-                if (startIndex >= records.length)
-                    return false;
-
-                let endIndex = this._timelineOverview.timelineRuler.snapInterval ? endTime - 1: Math.floor(endTime);
-                endIndex = Math.min(endIndex, records.length - 1);
-                console.assert(startIndex <= endIndex, startIndex);
-
-                startTime = records[startIndex].startTime;
-                endTime = records[endIndex].endTime;
-            }
-        }
-
-        function checkTimeBounds(itemStartTime, itemEndTime)
-        {
-            itemStartTime = itemStartTime || currentTime;
-            itemEndTime = itemEndTime || currentTime;
-
-            return startTime <= itemEndTime && itemStartTime <= endTime;
-        }
-
-        if (treeElement instanceof WebInspector.ResourceTreeElement) {
-            var resource = treeElement.resource;
-            return checkTimeBounds(resource.requestSentTimestamp, resource.finishedOrFailedTimestamp);
-        }
-
-        if (treeElement instanceof WebInspector.SourceCodeTimelineTreeElement) {
-            var sourceCodeTimeline = treeElement.sourceCodeTimeline;
-
-            // Do a quick check of the timeline bounds before we check each record.
-            if (!checkTimeBounds(sourceCodeTimeline.startTime, sourceCodeTimeline.endTime))
-                return false;
-
-            for (var record of sourceCodeTimeline.records) {
-                if (checkTimeBounds(record.startTime, record.endTime))
-                    return true;
-            }
-
-            return false;
-        }
-
-        if (treeElement instanceof WebInspector.ProfileNodeTreeElement) {
-            var profileNode = treeElement.profileNode;
-            if (checkTimeBounds(profileNode.startTime, profileNode.endTime))
-                return true;
-
-            return false;
-        }
-
-        if (treeElement instanceof WebInspector.TimelineRecordTreeElement) {
-            var record = treeElement.record;
-            return checkTimeBounds(record.startTime, record.endTime);
-        }
-
-        console.error("Unknown TreeElement, can't filter by time.");
-        return true;
-    }
-
     // ContentBrowser delegate
 
     contentBrowserTreeElementForRepresentedObject(contentBrowser, representedObject)
@@ -470,9 +385,6 @@ WebInspector.TimelineRecordingContentView = class TimelineRecordingContentView e
         if (this._timelineOverview.timelineRuler.entireRangeSelected)
             this._updateTimelineViewSelection(this._overviewTimelineView);
 
-        // Filter records on new recording times.
-        // FIXME: <https://webkit.org/b/154924> Web Inspector: hook up grid row filtering in the new Timelines UI
-
         // Force a layout now since we are already in an animation frame and don't need to delay it until the next.
         this._timelineOverview.updateLayoutIfNeeded();
         if (this.currentTimelineView)
@@ -699,18 +611,6 @@ WebInspector.TimelineRecordingContentView = class TimelineRecordingContentView e
             this._selectedTimeRangePathComponent = selectedPathComponent;
             this.dispatchEventToListeners(WebInspector.ContentView.Event.SelectionPathComponentsDidChange);
         }
-
-        // Delay until the next frame to stay in sync with the current timeline view's time-based layout changes.
-        requestAnimationFrame(function() {
-            var selectedTreeElement = this.currentTimelineView && this.currentTimelineView.navigationSidebarTreeOutline ? this.currentTimelineView.navigationSidebarTreeOutline.selectedTreeElement : null;
-            var selectionWasHidden = selectedTreeElement && selectedTreeElement.hidden;
-
-            // Filter records on new timeline selection.
-            // FIXME: <https://webkit.org/b/154924> Web Inspector: hook up grid row filtering in the new Timelines UI
-
-            if (selectedTreeElement && selectedTreeElement.hidden !== selectionWasHidden)
-                this.dispatchEventToListeners(WebInspector.ContentView.Event.SelectionPathComponentsDidChange);
-        }.bind(this));
     }
 
     _recordSelected(event)
@@ -796,4 +696,22 @@ WebInspector.TimelineRecordingContentView = class TimelineRecordingContentView e
 
         this._updateTimelineOverviewHeight();
     }
+
+    _filterDidChange()
+    {
+        if (!this.currentTimelineView)
+            return;
+
+        this.currentTimelineView.updateFilter(this._filterBarNavigationItem.filterBar.filters);
+    }
+
+    _recordWasFiltered(event)
+    {
+        if (event.target !== this.currentTimelineView)
+            return;
+
+        let record = event.data.record;
+        let filtered = event.data.filtered;
+        this._timelineOverview.recordWasFiltered(this.currentTimelineView.representedObject, record, filtered);
+    }
 };
index f211e39..5ddf7eb 100644 (file)
@@ -43,6 +43,11 @@ WebInspector.TimelineView = class TimelineView extends WebInspector.ContentView
 
     // Public
 
+    get navigationItems()
+    {
+        return this._scopeBar ? [this._scopeBar] : [];
+    }
+
     get navigationSidebarTreeOutlineScopeBar()
     {
         return this._scopeBar;
@@ -85,6 +90,7 @@ WebInspector.TimelineView = class TimelineView extends WebInspector.ContentView
 
         this._startTime = x;
 
+        this._filterTimesDidChange();
         this.needsLayout();
     }
 
@@ -102,6 +108,7 @@ WebInspector.TimelineView = class TimelineView extends WebInspector.ContentView
 
         this._endTime = x;
 
+        this._filterTimesDidChange();
         this.needsLayout();
     }
 
@@ -128,18 +135,45 @@ WebInspector.TimelineView = class TimelineView extends WebInspector.ContentView
             return this._startTime - wiggleTime <= currentTime && currentTime <= this._endTime + wiggleTime;
         }
 
-        if (checkIfLayoutIsNeeded.call(this, oldCurrentTime) || checkIfLayoutIsNeeded.call(this, this._currentTime))
+        if (checkIfLayoutIsNeeded.call(this, oldCurrentTime) || checkIfLayoutIsNeeded.call(this, this._currentTime)) {
+            this._filterTimesDidChange();
             this.needsLayout();
+        }
     }
 
-    reset()
+    get filterStartTime()
     {
         // Implemented by sub-classes if needed.
+        return this.startTime;
     }
 
-    filterDidChange()
+    get filterEndTime()
     {
         // Implemented by sub-classes if needed.
+        return this.endTime;
+    }
+
+    setupDataGrid(dataGrid)
+    {
+        console.assert(!this._timelineDataGrid);
+
+        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();
+        });
     }
 
     selectRecord(record)
@@ -166,15 +200,23 @@ WebInspector.TimelineView = class TimelineView extends WebInspector.ContentView
         dataGridNode.revealAndSelect();
     }
 
-    matchTreeElementAgainstCustomFilters(treeElement)
+    reset()
     {
         // Implemented by sub-classes if needed.
-        return true;
     }
 
-    filterUpdated()
+    updateFilter(filters)
     {
-        this.dispatchEventToListeners(WebInspector.ContentView.Event.SelectionPathComponentsDidChange);
+        if (!this._timelineDataGrid)
+            return;
+
+        this._timelineDataGrid.filterText = filters ? filters.text : "";
+    }
+
+    matchDataGridNodeAgainstCustomFilters(node)
+    {
+        // Implemented by sub-classes if needed.
+        return true;
     }
 
     needsLayout()
@@ -186,10 +228,92 @@ WebInspector.TimelineView = class TimelineView extends WebInspector.ContentView
         super.needsLayout();
     }
 
+    // DataGrid filter delegate
+
+    dataGridMatchNodeAgainstCustomFilters(node)
+    {
+        console.assert(node);
+        if (!this.matchDataGridNodeAgainstCustomFilters(node))
+            return false;
+
+        let startTime = this.filterStartTime;
+        let endTime = this.filterEndTime;
+        let currentTime = this.currentTime;
+
+        function checkTimeBounds(itemStartTime, itemEndTime)
+        {
+            itemStartTime = itemStartTime || currentTime;
+            itemEndTime = itemEndTime || currentTime;
+
+            return startTime <= itemEndTime && itemStartTime <= endTime;
+        }
+
+        if (node instanceof WebInspector.ResourceTimelineDataGridNode) {
+            let resource = node.resource;
+            return checkTimeBounds(resource.requestSentTimestamp, resource.finishedOrFailedTimestamp);
+        }
+
+        if (node instanceof WebInspector.SourceCodeTimelineTimelineDataGridNode) {
+            let sourceCodeTimeline = node.sourceCodeTimeline;
+
+            // Do a quick check of the timeline bounds before we check each record.
+            if (!checkTimeBounds(sourceCodeTimeline.startTime, sourceCodeTimeline.endTime))
+                return false;
+
+            for (let record of sourceCodeTimeline.records) {
+                if (checkTimeBounds(record.startTime, record.endTime))
+                    return true;
+            }
+
+            return false;
+        }
+
+        if (node instanceof WebInspector.ProfileNodeDataGridNode) {
+            let profileNode = node.profileNode;
+            if (checkTimeBounds(profileNode.startTime, profileNode.endTime))
+                return true;
+
+            return false;
+        }
+
+        if (node instanceof WebInspector.TimelineDataGridNode) {
+            let record = node.record;
+            return checkTimeBounds(record.startTime, record.endTime);
+        }
+
+        console.error("Unknown DataGridNode, can't filter by time.");
+        return true;
+    }
+
     // Protected
 
     userSelectedRecordFromOverview(timelineRecord)
     {
         // Implemented by sub-classes if needed.
     }
+
+    filterDidChange()
+    {
+        // Implemented by sub-classes if needed.
+    }
+
+    // Private
+
+    _filterTimesDidChange()
+    {
+        if (!this._timelineDataGrid || this._updateFilterTimeout)
+            return;
+
+        function delayedWork()
+        {
+            this._updateFilterTimeout = undefined;
+            this._timelineDataGrid.filterDidChange();
+        }
+
+        this._updateFilterTimeout = setTimeout(delayedWork.bind(this), 0);
+    }
+};
+
+WebInspector.TimelineView.Event = {
+    RecordWasFiltered: "record-was-filtered"
 };