Web Inspector: Network Tab - Search Headers Detail View
authorjoepeck@webkit.org <joepeck@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Mon, 9 Oct 2017 19:27:40 +0000 (19:27 +0000)
committerjoepeck@webkit.org <joepeck@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Mon, 9 Oct 2017 19:27:40 +0000 (19:27 +0000)
https://bugs.webkit.org/show_bug.cgi?id=177981

Reviewed by Brian Burg.

* UserInterface/Base/Main.js:
* UserInterface/Views/LogContentView.js:
(WI.LogContentView.prototype.performSearch):
Rename "Dom" to "DOM" in utility function.

* UserInterface/Views/ResourceHeadersContentView.css:
(.resource-headers.showing-find-banner .search-highlight):
Search highlight styles.

* UserInterface/Views/ResourceHeadersContentView.js:
(WI.ResourceHeadersContentView.prototype.get supportsSearch):
(WI.ResourceHeadersContentView.prototype.get numberOfSearchResults):
(WI.ResourceHeadersContentView.prototype.get hasPerformedSearch):
(WI.ResourceHeadersContentView.prototype.set automaticallyRevealFirstSearchResult):
(WI.ResourceHeadersContentView.prototype.performSearch):
(WI.ResourceHeadersContentView.prototype.searchCleared):
(WI.ResourceHeadersContentView.prototype.revealPreviousSearchResult):
(WI.ResourceHeadersContentView.prototype.revealNextSearchResult):
(WI.ResourceHeadersContentView.prototype._perfomSearchOnKeyValuePairs):
(WI.ResourceHeadersContentView.prototype._revealSearchResult):
Implement ContentView search behavior.

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

Source/WebInspectorUI/ChangeLog
Source/WebInspectorUI/UserInterface/Base/Main.js
Source/WebInspectorUI/UserInterface/Views/LogContentView.js
Source/WebInspectorUI/UserInterface/Views/ResourceHeadersContentView.css
Source/WebInspectorUI/UserInterface/Views/ResourceHeadersContentView.js

index 111fb8b..2dadf61 100644 (file)
@@ -1,3 +1,32 @@
+2017-10-09  Joseph Pecoraro  <pecoraro@apple.com>
+
+        Web Inspector: Network Tab - Search Headers Detail View
+        https://bugs.webkit.org/show_bug.cgi?id=177981
+
+        Reviewed by Brian Burg.
+
+        * UserInterface/Base/Main.js:
+        * UserInterface/Views/LogContentView.js:
+        (WI.LogContentView.prototype.performSearch):
+        Rename "Dom" to "DOM" in utility function.
+
+        * UserInterface/Views/ResourceHeadersContentView.css:
+        (.resource-headers.showing-find-banner .search-highlight):
+        Search highlight styles.
+
+        * UserInterface/Views/ResourceHeadersContentView.js:
+        (WI.ResourceHeadersContentView.prototype.get supportsSearch):
+        (WI.ResourceHeadersContentView.prototype.get numberOfSearchResults):
+        (WI.ResourceHeadersContentView.prototype.get hasPerformedSearch):
+        (WI.ResourceHeadersContentView.prototype.set automaticallyRevealFirstSearchResult):
+        (WI.ResourceHeadersContentView.prototype.performSearch):
+        (WI.ResourceHeadersContentView.prototype.searchCleared):
+        (WI.ResourceHeadersContentView.prototype.revealPreviousSearchResult):
+        (WI.ResourceHeadersContentView.prototype.revealNextSearchResult):
+        (WI.ResourceHeadersContentView.prototype._perfomSearchOnKeyValuePairs):
+        (WI.ResourceHeadersContentView.prototype._revealSearchResult):
+        Implement ContentView search behavior.
+
 2017-10-08  Devin Rousso  <webkit@devinrousso.com>
 
         Web Inspector: add autocompletion for min/max within a CSS calc
index 2c87a55..170fe5f 100644 (file)
@@ -2551,7 +2551,7 @@ WI.highlightRangesWithStyleClass = function(element, resultRanges, styleClass, c
     return highlightNodes;
 };
 
-WI.revertDomChanges = function(domChanges)
+WI.revertDOMChanges = function(domChanges)
 {
     for (var i = domChanges.length - 1; i >= 0; --i) {
         var entry = domChanges[i];
index c51ab0d..dbc9a52 100644 (file)
@@ -950,7 +950,7 @@ WI.LogContentView = class LogContentView extends WI.ContentView
     performSearch(searchQuery)
     {
         if (!isEmptyObject(this._searchHighlightDOMChanges))
-            WI.revertDomChanges(this._searchHighlightDOMChanges);
+            WI.revertDOMChanges(this._searchHighlightDOMChanges);
 
         this._currentSearchQuery = searchQuery;
         this._searchHighlightDOMChanges = [];
index 9475fa2..3581051 100644 (file)
@@ -77,3 +77,9 @@ body[dir=rtl] .resource-headers .details .pair {
     vertical-align: top;
     bottom: 1px;
 }
+
+.resource-headers.showing-find-banner .search-highlight {
+    color: black;
+    background-color: hsla(53, 83%, 53%, 0.2);
+    border-bottom: 1px solid hsl(47, 82%, 60%);
+}
index 197e90a..18604b0 100644 (file)
@@ -38,7 +38,15 @@ WI.ResourceHeadersContentView = class ResourceHeadersContentView extends WI.Cont
 
         this._delegate = delegate || null;
 
+        this._searchQuery = null;
+        this._searchResults = null;
+        this._searchDOMChanges = [];
+        this._searchIndex = -1;
+        this._automaticallyRevealFirstSearchResult = false;
+        this._bouncyHighlightElement = null;
+
         this.element.classList.add("resource-details", "resource-headers");
+        this.element.tabIndex = 0;
 
         this._needsSummaryRefresh = false;
         this._needsRequestHeadersRefresh = false;
@@ -109,6 +117,88 @@ WI.ResourceHeadersContentView = class ResourceHeadersContentView extends WI.Cont
         super.closed();
     }
 
+    get supportsSearch()
+    {
+        return true;
+    }
+
+    get numberOfSearchResults()
+    {
+        return this._searchResults ? this._searchResults.length : null;
+    }
+
+    get hasPerformedSearch()
+    {
+        return this._searchResults !== null;
+    }
+
+    set automaticallyRevealFirstSearchResult(reveal)
+    {
+        this._automaticallyRevealFirstSearchResult = reveal;
+
+        // If we haven't shown a search result yet, reveal one now.
+        if (this._automaticallyRevealFirstSearchResult && this.numberOfSearchResults > 0) {
+            if (this._searchIndex === -1)
+                this.revealNextSearchResult();
+        }
+    }
+
+    performSearch(query)
+    {
+        if (query === this._searchQuery)
+            return;
+
+        WI.revertDOMChanges(this._searchDOMChanges);
+
+        this._searchQuery = query;
+        this._searchResults = [];
+        this._searchDOMChanges = [];
+        this._searchIndex = -1;
+
+        this._perfomSearchOnKeyValuePairs();
+
+        this.dispatchEventToListeners(WI.ContentView.Event.NumberOfSearchResultsDidChange);
+
+        if (this._automaticallyRevealFirstSearchResult && this._searchResults.length > 0)
+            this.revealNextSearchResult();
+    }
+
+    searchCleared()
+    {
+        WI.revertDOMChanges(this._searchDOMChanges);
+
+        this._searchQuery = null;
+        this._searchResults = null;
+        this._searchDOMChanges = [];
+        this._searchIndex = -1;
+    }
+
+    revealPreviousSearchResult(changeFocus)
+    {
+        if (!this.numberOfSearchResults)
+            return;
+
+        if (this._searchIndex > 0)
+            --this._searchIndex;
+        else
+            this._searchIndex = this._searchResults.length - 1;
+
+        this._revealSearchResult(this._searchIndex, changeFocus);
+    }
+
+    revealNextSearchResult(changeFocus)
+    {
+        if (!this.numberOfSearchResults)
+            return;
+
+        if (this._searchIndex + 1 < this._searchResults.length)
+            ++this._searchIndex;
+        else
+            this._searchIndex = 0;
+
+        this._revealSearchResult(this._searchIndex, changeFocus);
+    }
+
     // Private
 
     _incompleteSectionWithMessage(section, message)
@@ -314,6 +404,57 @@ WI.ResourceHeadersContentView = class ResourceHeadersContentView extends WI.Cont
         this.needsLayout();
     }
 
+    _perfomSearchOnKeyValuePairs()
+    {
+        let searchRegex = new RegExp(this._searchQuery.escapeForRegExp(), "gi");
+
+        let elements = this.element.querySelectorAll(".key, .value");
+        for (let element of elements) {
+            let matchRanges = [];
+            let text = element.textContent;
+            let match;
+            while (match = searchRegex.exec(text))
+                matchRanges.push({offset: match.index, length: match[0].length});
+
+            if (matchRanges.length) {
+                let highlightedNodes = WI.highlightRangesWithStyleClass(element, matchRanges, "search-highlight", this._searchDOMChanges);
+                this._searchResults = this._searchResults.concat(highlightedNodes);
+            }
+        }
+    }
+
+    _revealSearchResult(index, changeFocus)
+    {
+        let highlightElement = this._searchResults[index];
+        if (!highlightElement)
+            return;
+
+        highlightElement.scrollIntoViewIfNeeded();
+
+        if (!this._bouncyHighlightElement) {
+            this._bouncyHighlightElement = document.createElement("div");
+            this._bouncyHighlightElement.className = "bouncy-highlight";
+            this._bouncyHighlightElement.addEventListener("animationend", (event) => {
+                this._bouncyHighlightElement.remove();
+            });
+        }
+
+        this._bouncyHighlightElement.remove();
+
+        let computedStyles = window.getComputedStyle(highlightElement);
+        let highlightElementRect = highlightElement.getBoundingClientRect();
+        let contentViewRect = this.element.getBoundingClientRect();
+        let contentViewScrollTop = this.element.scrollTop;
+        let contentViewScrollLeft = this.element.scrollLeft;
+
+        this._bouncyHighlightElement.textContent = highlightElement.textContent;
+        this._bouncyHighlightElement.style.top = (highlightElementRect.top - contentViewRect.top + contentViewScrollTop) + "px";
+        this._bouncyHighlightElement.style.left = (highlightElementRect.left - contentViewRect.left + contentViewScrollLeft) + "px";
+        this._bouncyHighlightElement.style.fontWeight = computedStyles.fontWeight;
+
+        this.element.appendChild(this._bouncyHighlightElement);
+    }
+
     _resourceRequestHeadersDidChange(event)
     {
         this._needsRequestHeadersRefresh = true;