Changeset 199143 in webkit


Ignore:
Timestamp:
Apr 6, 2016 7:09:07 PM (8 years ago)
Author:
Matt Baker
Message:

Web Inspector: Improve filtering in OpenResourceDialog
https://bugs.webkit.org/show_bug.cgi?id=155324
<rdar://problem/25094504>

Source/WebInspectorUI:

Reviewed by Joseph Pecoraro and Timothy Hatcher.

  • UserInterface/Base/Utilities.js:

(value):
Added String methods isLowerCase, isUpperCase, removeWhitespace.

  • UserInterface/Controllers/ResourceQueryController.js: Added.

(WebInspector.ResourceQueryController):
(WebInspector.ResourceQueryController.prototype.addResource):
(WebInspector.ResourceQueryController.prototype.removeResource):
Add and remove the resources to be queried.

(WebInspector.ResourceQueryController.prototype.reset):
Reset controller state. Current just clears resources.

(WebInspector.ResourceQueryController.prototype.executeQuery):
Executes a query against the list of resources and returns a list of
QueryResult objects, with at most one result per resource, ordered by
descending rank.

The query string is stripped of whitespace characters and lowercased
before use. Prior to running the query, resources undergo a one-time
pre-processing step to locate special characters.

(WebInspector.ResourceQueryController.prototype._findQueryMatches.pushMatch):
(WebInspector.ResourceQueryController.prototype._findQueryMatches.matchNextSpecialCharacter):
(WebInspector.ResourceQueryController.prototype._findQueryMatches.backtrack):
(WebInspector.ResourceQueryController.prototype._findQueryMatches):
Returns a list of query matches for a single resource, along with metadata
which is used to rank the matches. The algorithm attempts to match the
entire query, first comparing each query character against "special" characters
in the resource (commonly used filename separators, the first character,
and camel-case word boundaries).

If there are remaining query characters after exhausting special characters,
regular characters are matched starting from the last matched special
character. Failing that, the algorithm attempts to find a match by backtracking.
To backtrack, the last match is discarded and the query position decremented.
If a special match is now the last match, matching starts again from the
next character in the filename after the match. If a normal match is now
the last match, keep discarding until a special match is found or no matches
remain. The query fails if no matches remain. For example, consider:

Query: "abcd"
Filename: "AxBcdCx"

The capital A, B, and C are all special characters, and are successfully
matched with the first three query characters. Having exhausted the special
characters the "d" at the end of the query is compared with the "x" at
the end of the filename, and fails to match. Backtracking then kicks in.
The last match, "C", is discarded and the search position in the query
decremented. The search resumes after the next to last match, "B", and now
matches the non-special characters "cd", yielding the following: "A Bcd ".

(WebInspector.ResourceQueryController.prototype._findSpecialCharacterIndices):
Pre-processing step for resources. Locates the positions of special
characters in the resource filename. Special characters are defined as:

  1. The first character
  2. Common filename separators, and the character immediately following.
  3. A capital letter that follows a lowercase character.
  • UserInterface/Models/ResourceQueryMatch.js: Added.

Helper class used internally by the controller and QueryResult classes.
(WebInspector.ResourceQueryMatch):
(WebInspector.ResourceQueryMatch.prototype.get type):
(WebInspector.ResourceQueryMatch.prototype.get index):
(WebInspector.ResourceQueryMatch.prototype.get queryIndex):

  • UserInterface/Models/ResourceQueryResult.js: Added.

Holds a resource that matched the executed query.
(WebInspector.ResourceQueryResult):
(WebInspector.ResourceQueryResult.prototype.get resource):
(WebInspector.ResourceQueryResult.prototype.get rank):
Ranking relative to other results returned by the query. Used by
the ResourceQueryController to sort results.

(WebInspector.ResourceQueryResult.prototype.get matchingTextRanges):
Get TextRanges for matching substrings in the resource display name.

(WebInspector.ResourceQueryResult.prototype._calculateRank):
Calculate the rank of the result. Matches are scored based on the type
of match (Special vs. Normal), the location of the match within the filename
(matches closer to the beginning are scored higher), and whether the match
is adjacent to the previous match.

Values assigned to each ranking criteria are somewhat arbitrary, and may
be fine-tuned over time to produce better results.

(WebInspector.ResourceQueryResult.prototype._createMatchingTextRanges):
(WebInspector.ResourceQueryResult.prototype.test_createMatchesMask):
Test API for visualizing matches. For a result returned from the query
"abce", run against a filename "abcde", the mask is "a c e".

  • UserInterface/Main.html:
  • UserInterface/Test.html:

New files.

  • UserInterface/Views/Dialog.js:

(WebInspector.Dialog.prototype.dismiss):
(WebInspector.Dialog.prototype.didDismissDialog):
Add hook for subclasses to perform actions after on dialog dismissal.

  • UserInterface/Views/OpenResourceDialog.js:

(WebInspector.OpenResourceDialog):
(WebInspector.OpenResourceDialog.prototype._populateResourceTreeOutline.createHighlightedTitleFragment):
(WebInspector.OpenResourceDialog.prototype._populateResourceTreeOutline):
Add tree elements for each QueryResult returned by the last query, creating
titles with contiguous matching query characters wrapped in highlight spans.

(WebInspector.OpenResourceDialog.prototype.didDismissDialog):
Clear resources from the ResourceQueryController.
(WebInspector.OpenResourceDialog.prototype.didPresentDialog):
Add resources to the ResourceQueryController.
(WebInspector.OpenResourceDialog.prototype._updateFilter):
Execute the filter text as a resource query.

  • UserInterface/Views/TreeOutline.css:

(.tree-outline.large .item .titles): Deleted.
Line height too small, hid the bottom border of highlighted matches in
tree element title spans. Removing the style had no negative visual impact
on the Quick Open or Timelines tree outlines (the only "large" trees).

LayoutTests:

Reviewed by Joseph Pecoraro.

Add test coverage for ResourceQueryController.

  • inspector/unit-tests/resource-query-controller-expected.txt: Added.
  • inspector/unit-tests/resource-query-controller.html: Added.
Location:
trunk
Files:
5 added
8 edited

Legend:

Unmodified
Added
Removed
  • trunk/LayoutTests/ChangeLog

    r199135 r199143  
     12016-04-06  Matt Baker  <mattbaker@apple.com>
     2
     3        Web Inspector: Improve filtering in OpenResourceDialog
     4        https://bugs.webkit.org/show_bug.cgi?id=155324
     5        <rdar://problem/25094504>
     6
     7        Reviewed by Joseph Pecoraro.
     8
     9        Add test coverage for ResourceQueryController.
     10
     11        * inspector/unit-tests/resource-query-controller-expected.txt: Added.
     12        * inspector/unit-tests/resource-query-controller.html: Added.
     13
    1142016-04-06  Saam barati  <sbarati@apple.com>
    215
  • trunk/Source/WebInspectorUI/ChangeLog

    r199095 r199143  
     12016-04-06  Matt Baker  <mattbaker@apple.com>
     2
     3        Web Inspector: Improve filtering in OpenResourceDialog
     4        https://bugs.webkit.org/show_bug.cgi?id=155324
     5        <rdar://problem/25094504>
     6
     7        Reviewed by Joseph Pecoraro and Timothy Hatcher.
     8
     9        * UserInterface/Base/Utilities.js:
     10        (value):
     11        Added String methods isLowerCase, isUpperCase, removeWhitespace.
     12
     13        * UserInterface/Controllers/ResourceQueryController.js: Added.
     14        (WebInspector.ResourceQueryController):
     15        (WebInspector.ResourceQueryController.prototype.addResource):
     16        (WebInspector.ResourceQueryController.prototype.removeResource):
     17        Add and remove the resources to be queried.
     18
     19        (WebInspector.ResourceQueryController.prototype.reset):
     20        Reset controller state. Current just clears resources.
     21
     22        (WebInspector.ResourceQueryController.prototype.executeQuery):
     23        Executes a query against the list of resources and returns a list of
     24        QueryResult objects, with at most one result per resource, ordered by
     25        descending rank.
     26
     27        The query string is stripped of whitespace characters and lowercased
     28        before use. Prior to running the query, resources undergo a one-time
     29        pre-processing step to locate special characters.
     30
     31        (WebInspector.ResourceQueryController.prototype._findQueryMatches.pushMatch):
     32        (WebInspector.ResourceQueryController.prototype._findQueryMatches.matchNextSpecialCharacter):
     33        (WebInspector.ResourceQueryController.prototype._findQueryMatches.backtrack):
     34        (WebInspector.ResourceQueryController.prototype._findQueryMatches):
     35        Returns a list of query matches for a single resource, along with metadata
     36        which is used to rank the matches. The algorithm attempts to match the
     37        entire query, first comparing each query character against "special" characters
     38        in the resource (commonly used filename separators, the first character,
     39        and camel-case word boundaries).
     40
     41        If there are remaining query characters after exhausting special characters,
     42        regular characters are matched starting from the last matched special
     43        character. Failing that, the algorithm attempts to find a match by backtracking.
     44        To backtrack, the last match is discarded and the query position decremented.
     45        If a special match is now the last match, matching starts again from the
     46        next character in the filename after the match. If a normal match is now
     47        the last match, keep discarding until a special match is found or no matches
     48        remain. The query fails if no matches remain. For example, consider:
     49
     50           Query: "abcd"
     51           Filename: "AxBcdCx"
     52
     53        The capital A, B, and C are all special characters, and are successfully
     54        matched with the first three query characters. Having exhausted the special
     55        characters the "d" at the end of the query is compared with the "x" at
     56        the end of the filename, and fails to match. Backtracking then kicks in.
     57        The last match, "C", is discarded and the search position in the query
     58        decremented. The search resumes after the next to last match, "B", and now
     59        matches the non-special characters "cd", yielding the following: "A Bcd  ".
     60
     61        (WebInspector.ResourceQueryController.prototype._findSpecialCharacterIndices):
     62        Pre-processing step for resources. Locates the positions of special
     63        characters in the resource filename. Special characters are defined as:
     64
     65           1. The first character
     66           2. Common filename separators, and the character immediately following.
     67           3. A capital letter that follows a lowercase character.
     68
     69        * UserInterface/Models/ResourceQueryMatch.js: Added.
     70        Helper class used internally by the controller and QueryResult classes.
     71        (WebInspector.ResourceQueryMatch):
     72        (WebInspector.ResourceQueryMatch.prototype.get type):
     73        (WebInspector.ResourceQueryMatch.prototype.get index):
     74        (WebInspector.ResourceQueryMatch.prototype.get queryIndex):
     75
     76        * UserInterface/Models/ResourceQueryResult.js: Added.
     77        Holds a resource that matched the executed query.
     78        (WebInspector.ResourceQueryResult):
     79        (WebInspector.ResourceQueryResult.prototype.get resource):
     80        (WebInspector.ResourceQueryResult.prototype.get rank):
     81        Ranking relative to other results returned by the query. Used by
     82        the ResourceQueryController to sort results.
     83
     84        (WebInspector.ResourceQueryResult.prototype.get matchingTextRanges):
     85        Get TextRanges for matching substrings in the resource display name.
     86
     87        (WebInspector.ResourceQueryResult.prototype._calculateRank):
     88        Calculate the rank of the result. Matches are scored based on the type
     89        of match (Special vs. Normal), the location of the match within the filename
     90        (matches closer to the beginning are scored higher), and whether the match
     91        is adjacent to the previous match.
     92
     93        Values assigned to each ranking criteria are somewhat arbitrary, and may
     94        be fine-tuned over time to produce better results.
     95
     96        (WebInspector.ResourceQueryResult.prototype._createMatchingTextRanges):
     97        (WebInspector.ResourceQueryResult.prototype.__test_createMatchesMask):
     98        Test API for visualizing matches. For a result returned from the query
     99        "abce", run against a filename "abcde", the mask is "a c e".
     100
     101        * UserInterface/Main.html:
     102        * UserInterface/Test.html:
     103        New files.
     104
     105        * UserInterface/Views/Dialog.js:
     106        (WebInspector.Dialog.prototype.dismiss):
     107        (WebInspector.Dialog.prototype.didDismissDialog):
     108        Add hook for subclasses to perform actions after on dialog dismissal.
     109
     110        * UserInterface/Views/OpenResourceDialog.js:
     111        (WebInspector.OpenResourceDialog):
     112        (WebInspector.OpenResourceDialog.prototype._populateResourceTreeOutline.createHighlightedTitleFragment):
     113        (WebInspector.OpenResourceDialog.prototype._populateResourceTreeOutline):
     114        Add tree elements for each QueryResult returned by the last query, creating
     115        titles with contiguous matching query characters wrapped in highlight spans.
     116
     117        (WebInspector.OpenResourceDialog.prototype.didDismissDialog):
     118        Clear resources from the ResourceQueryController.
     119        (WebInspector.OpenResourceDialog.prototype.didPresentDialog):
     120        Add resources to the ResourceQueryController.
     121        (WebInspector.OpenResourceDialog.prototype._updateFilter):
     122        Execute the filter text as a resource query.
     123
     124        * UserInterface/Views/TreeOutline.css:
     125        (.tree-outline.large .item .titles): Deleted.
     126        Line height too small, hid the bottom border of highlighted matches in
     127        tree element title spans. Removing the style had no negative visual impact
     128        on the Quick Open or Timelines tree outlines (the only "large" trees).
     129
    11302016-04-05  Joseph Pecoraro  <pecoraro@apple.com>
    2131
  • trunk/Source/WebInspectorUI/UserInterface/Base/Utilities.js

    r199031 r199143  
    485485});
    486486
     487Object.defineProperty(String.prototype, "isLowerCase",
     488{
     489    value: function()
     490    {
     491        return String(this) === this.toLowerCase();
     492    }
     493});
     494
     495Object.defineProperty(String.prototype, "isUpperCase",
     496{
     497    value: function()
     498    {
     499        return String(this) === this.toUpperCase();
     500    }
     501});
     502
    487503Object.defineProperty(String.prototype, "trimMiddle",
    488504{
     
    530546    {
    531547        return this.replace(/[\s\xA0]+/g, " ");
     548    }
     549});
     550
     551Object.defineProperty(String.prototype, "removeWhitespace",
     552{
     553    value: function()
     554    {
     555        return this.replace(/[\s\xA0]+/g, "");
    532556    }
    533557});
  • trunk/Source/WebInspectorUI/UserInterface/Main.html

    r198537 r199143  
    352352    <script src="Models/Resource.js"></script>
    353353    <script src="Models/ResourceCollection.js"></script>
     354    <script src="Models/ResourceQueryMatch.js"></script>
     355    <script src="Models/ResourceQueryResult.js"></script>
    354356    <script src="Models/ResourceTimelineRecord.js"></script>
    355357    <script src="Models/Revision.js"></script>
     
    691693    <script src="Controllers/ProbeManager.js"></script>
    692694    <script src="Controllers/ReplayManager.js"></script>
     695    <script src="Controllers/ResourceQueryController.js"></script>
    693696    <script src="Controllers/RuntimeManager.js"></script>
    694697    <script src="Controllers/SourceMapManager.js"></script>
  • trunk/Source/WebInspectorUI/UserInterface/Test.html

    r198353 r199143  
    143143    <script src="Models/Resource.js"></script>
    144144    <script src="Models/ResourceCollection.js"></script>
     145    <script src="Models/ResourceQueryMatch.js"></script>
     146    <script src="Models/ResourceQueryResult.js"></script>
    145147    <script src="Models/ResourceTimelineRecord.js"></script>
    146148    <script src="Models/Revision.js"></script>
     
    181183    <script src="Controllers/Formatter.js"></script>
    182184    <script src="Controllers/FormatterContentBuilder.js"></script>
     185    <script src="Controllers/ResourceQueryController.js"></script>
    183186    <script src="Views/CodeMirrorAdditions.js"></script>
    184187    <script src="Views/CodeMirrorFormatters.js"></script>
  • trunk/Source/WebInspectorUI/UserInterface/Views/Dialog.js

    r197961 r199143  
    8383
    8484        this._dismissing = false;
     85
     86        this.didDismissDialog();
    8587    }
    8688
    8789    // Protected
     90
     91    didDismissDialog()
     92    {
     93        // Implemented by subclasses.
     94    }
    8895
    8996    didPresetDialog()
  • trunk/Source/WebInspectorUI/UserInterface/Views/OpenResourceDialog.js

    r198690 r199143  
    5959        this.element.appendChild(this._treeOutline.element);
    6060
    61         this._resources = [];
    62         this._filteredResources = [];
     61        this._queryController = new WebInspector.ResourceQueryController;
     62        this._filteredResults = [];
    6363    }
    6464
     
    6767    _populateResourceTreeOutline()
    6868    {
     69        function createHighlightedTitleFragment(title, highlightTextRanges)
     70        {
     71            let titleFragment = document.createDocumentFragment();
     72            let lastIndex = 0;
     73            for (let textRange of highlightTextRanges) {
     74                if (textRange.startColumn > lastIndex)
     75                    titleFragment.append(title.substring(lastIndex, textRange.startColumn));
     76
     77                let highlightSpan = document.createElement("span");
     78                highlightSpan.classList.add("highlighted");
     79                highlightSpan.append(title.substring(textRange.startColumn, textRange.endColumn));
     80                titleFragment.append(highlightSpan);
     81                lastIndex = textRange.endColumn;
     82            }
     83
     84            if (lastIndex < title.length)
     85                titleFragment.append(title.substring(lastIndex, title.length));
     86
     87            return titleFragment;
     88        }
     89
    6990        function createTreeElement(representedObject)
    7091        {
     
    81102        }
    82103
    83         for (let resource of this._filteredResources) {
     104        for (let result of this._filteredResults) {
     105            let resource = result.resource;
     106            if (this._treeOutline.findTreeElement(resource))
     107                continue;
     108
    84109            let treeElement = createTreeElement(resource);
    85110            if (!treeElement)
    86111                continue;
    87112
    88             if (this._treeOutline.findTreeElement(resource))
    89                 continue;
    90 
     113            treeElement.mainTitle = createHighlightedTitleFragment(resource.displayName, result.matchingTextRanges);
    91114            this._treeOutline.appendChild(treeElement);
    92115        }
     
    94117        if (this._treeOutline.children.length)
    95118            this._treeOutline.children[0].select(true, false, true, true);
     119    }
     120
     121    didDismissDialog()
     122    {
     123        this._queryController.reset();
    96124    }
    97125
     
    104132        while (frames.length) {
    105133            let frame = frames.shift();
    106             for (let resource of frame.resources) {
     134            let resources = [frame.mainResource].concat(frame.resources);
     135            for (let resource of resources) {
    107136                if (!this.representedObjectIsValid(resource))
    108137                    continue;
    109138
    110                 this._resources.push(resource);
     139                this._queryController.addResource(resource);
    111140            }
    112141
     
    194223    _updateFilter()
    195224    {
    196         this._filteredResources = [];
     225        this._filteredResults = [];
    197226        this._treeOutline.hidden = true;
    198227        this._treeOutline.removeChildren();
     
    202231            return;
    203232
    204         // FIXME: <https://webkit.org/b/155324> Web Inspector: Improve filtering in OpenResourceDialog
    205         let filters = [simpleGlobStringToRegExp(filterText)];
    206 
    207         for (let resource of this._resources) {
    208             for (let i = 0; i < filters.length; ++i) {
    209                 if (!filters[i].test(resource.displayName))
    210                     continue;
    211 
    212                 resource.__weight = filters.length - i;
    213                 this._filteredResources.push(resource);
    214                 break;
    215             }
    216         }
    217 
    218         // Sort filtered resources by weight, then alphabetically.
    219         this._filteredResources.sort((a, b) => {
    220             if (a.__weight === b.__weight)
    221                 return a.displayName.localeCompare(b.displayName);
    222 
    223             return b.__weight - a.__weight;
    224         });
     233        this._filteredResults = this._queryController.executeQuery(filterText);
    225234
    226235        this._populateResourceTreeOutline();
  • trunk/Source/WebInspectorUI/UserInterface/Views/TreeOutline.css

    r197961 r199143  
    184184.tree-outline.large .item .titles {
    185185    top: 10px;
    186     line-height: 11px;
    187186}
    188187
Note: See TracChangeset for help on using the changeset viewer.