Title: [288760] trunk/Source/WebKit
Revision
288760
Author
[email protected]
Date
2022-01-28 13:46:26 -0800 (Fri, 28 Jan 2022)

Log Message

Add support for decorating and scrolling to ranges in WebFoundTextRangeController
https://bugs.webkit.org/show_bug.cgi?id=235692
rdar://88117232

Reviewed by Wenson Hsieh.

* Shared/WebFoundTextRange.h:
(WTF::HashTraits<WebKit::WebFoundTextRange>::deletedValue):

The empty and deleted values should not be the same.

(WTF::HashTraits<WebKit::WebFoundTextRange>::constructDeletedValue):
(WTF::HashTraits<WebKit::WebFoundTextRange>::isDeletedValue):

* WebProcess/WebPage/WebFoundTextRangeController.cpp:
(WebKit::WebFoundTextRangeController::findTextRangesForStringMatches):

Cache the found ranges and their corresponding SimpleRange, to avoid
unnecessary tree traversal each time we want to decorate or scroll to
a found result.

(WebKit::WebFoundTextRangeController::decorateTextRangeWithStyle):

Found ranges can currently be decorated with 3 styles: "Normal",
"Found", and "Highlighted". The "Found" style is drawn using
document markers. The "Highlighted" style is drawn using a
TextIndicator.

(WebKit::WebFoundTextRangeController::scrollTextRangeToVisible):

Use TemporarySelectionChange to make the range visible.

(WebKit::WebFoundTextRangeController::clearAllDecoratedFoundText):
(WebKit::WebFoundTextRangeController::didBeginTextSearchOperation):
(WebKit::WebFoundTextRangeController::didEndTextSearchOperation):
(WebKit::WebFoundTextRangeController::willMoveToPage):
(WebKit::WebFoundTextRangeController::didMoveToPage):
(WebKit::WebFoundTextRangeController::drawRect):
(WebKit::WebFoundTextRangeController::rectsForTextMatchesInRect):
(WebKit::WebFoundTextRangeController::documentForFoundTextRange const):

Use the frame identifer of the WebFoundTextRange to retrieve the
corresponding Document, so that the range can be appropriately
mapped for decoration/scrolling.

(WebKit::WebFoundTextRangeController::simpleRangeFromFoundTextRange):

A helper method to cache WebFoundTextRanges and SimpleRanges.

* WebProcess/WebPage/WebFoundTextRangeController.h:

Modified Paths

Diff

Modified: trunk/Source/WebKit/ChangeLog (288759 => 288760)


--- trunk/Source/WebKit/ChangeLog	2022-01-28 20:56:26 UTC (rev 288759)
+++ trunk/Source/WebKit/ChangeLog	2022-01-28 21:46:26 UTC (rev 288760)
@@ -1,3 +1,56 @@
+2022-01-28  Aditya Keerthi  <[email protected]>
+
+        Add support for decorating and scrolling to ranges in WebFoundTextRangeController
+        https://bugs.webkit.org/show_bug.cgi?id=235692
+        rdar://88117232
+
+        Reviewed by Wenson Hsieh.
+
+        * Shared/WebFoundTextRange.h:
+        (WTF::HashTraits<WebKit::WebFoundTextRange>::deletedValue):
+
+        The empty and deleted values should not be the same.
+
+        (WTF::HashTraits<WebKit::WebFoundTextRange>::constructDeletedValue):
+        (WTF::HashTraits<WebKit::WebFoundTextRange>::isDeletedValue):
+
+        * WebProcess/WebPage/WebFoundTextRangeController.cpp:
+        (WebKit::WebFoundTextRangeController::findTextRangesForStringMatches):
+
+        Cache the found ranges and their corresponding SimpleRange, to avoid
+        unnecessary tree traversal each time we want to decorate or scroll to
+        a found result.
+
+        (WebKit::WebFoundTextRangeController::decorateTextRangeWithStyle):
+
+        Found ranges can currently be decorated with 3 styles: "Normal",
+        "Found", and "Highlighted". The "Found" style is drawn using
+        document markers. The "Highlighted" style is drawn using a
+        TextIndicator.
+
+        (WebKit::WebFoundTextRangeController::scrollTextRangeToVisible):
+
+        Use TemporarySelectionChange to make the range visible.
+
+        (WebKit::WebFoundTextRangeController::clearAllDecoratedFoundText):
+        (WebKit::WebFoundTextRangeController::didBeginTextSearchOperation):
+        (WebKit::WebFoundTextRangeController::didEndTextSearchOperation):
+        (WebKit::WebFoundTextRangeController::willMoveToPage):
+        (WebKit::WebFoundTextRangeController::didMoveToPage):
+        (WebKit::WebFoundTextRangeController::drawRect):
+        (WebKit::WebFoundTextRangeController::rectsForTextMatchesInRect):
+        (WebKit::WebFoundTextRangeController::documentForFoundTextRange const):
+
+        Use the frame identifer of the WebFoundTextRange to retrieve the
+        corresponding Document, so that the range can be appropriately
+        mapped for decoration/scrolling.
+
+        (WebKit::WebFoundTextRangeController::simpleRangeFromFoundTextRange):
+
+        A helper method to cache WebFoundTextRanges and SimpleRanges.
+
+        * WebProcess/WebPage/WebFoundTextRangeController.h:
+
 2022-01-28  Sihui Liu  <[email protected]>
 
         Ensure session is added in network process when it creates connection to web process

Modified: trunk/Source/WebKit/Shared/WebFoundTextRange.h (288759 => 288760)


--- trunk/Source/WebKit/Shared/WebFoundTextRange.h	2022-01-28 20:56:26 UTC (rev 288759)
+++ trunk/Source/WebKit/Shared/WebFoundTextRange.h	2022-01-28 21:46:26 UTC (rev 288760)
@@ -55,9 +55,10 @@
 
 template<> struct HashTraits<WebKit::WebFoundTextRange> : GenericHashTraits<WebKit::WebFoundTextRange> {
     static WebKit::WebFoundTextRange emptyValue() { return { }; }
+    static WebKit::WebFoundTextRange deletedValue() { return { std::numeric_limits<uint64_t>::max(), 0, { }, 0 }; }
 
-    static void constructDeletedValue(WebKit::WebFoundTextRange& range) { range = { }; }
-    static bool isDeletedValue(const WebKit::WebFoundTextRange& range) { return range == WebKit::WebFoundTextRange { }; }
+    static void constructDeletedValue(WebKit::WebFoundTextRange& range) { range = deletedValue(); }
+    static bool isDeletedValue(const WebKit::WebFoundTextRange& range) { return range == deletedValue(); }
 };
 
 template<> struct DefaultHash<WebKit::WebFoundTextRange> : WebFoundTextRangeHash { };

Modified: trunk/Source/WebKit/WebProcess/WebPage/WebFoundTextRangeController.cpp (288759 => 288760)


--- trunk/Source/WebKit/WebProcess/WebPage/WebFoundTextRangeController.cpp	2022-01-28 20:56:26 UTC (rev 288759)
+++ trunk/Source/WebKit/WebProcess/WebPage/WebFoundTextRangeController.cpp	2022-01-28 21:46:26 UTC (rev 288760)
@@ -29,10 +29,16 @@
 #include "WebPage.h"
 #include <WebCore/CharacterRange.h>
 #include <WebCore/Document.h>
+#include <WebCore/DocumentMarkerController.h>
 #include <WebCore/Editor.h>
+#include <WebCore/Frame.h>
+#include <WebCore/FrameView.h>
+#include <WebCore/GeometryUtilities.h>
 #include <WebCore/GraphicsContext.h>
+#include <WebCore/ImageOverlay.h>
 #include <WebCore/Page.h>
 #include <WebCore/PageOverlayController.h>
+#include <WebCore/PathUtilities.h>
 #include <WebCore/PlatformMouseEvent.h>
 #include <WebCore/SimpleRange.h>
 #include <WebCore/TextIterator.h>
@@ -49,6 +55,9 @@
     auto result = m_webPage->corePage()->findTextMatches(string, core(options), maxMatchCount, false);
     Vector<WebCore::SimpleRange> findMatches = WTFMove(result.ranges);
 
+    if (!findMatches.isEmpty())
+        m_cachedFoundRanges.clear();
+
     String frameName;
     uint64_t order = 0;
     Vector<WebFoundTextRange> foundTextRanges;
@@ -57,7 +66,7 @@
 
         auto* element = document.documentElement();
         if (!element)
-            return;
+            continue;
 
         String currentFrameName = document.frame()->tree().uniqueName();
         if (frameName != currentFrameName) {
@@ -69,6 +78,7 @@
         auto range = characterRange(makeBoundaryPointBeforeNodeContents(*element), simpleRange, WebCore::findIteratorOptions());
         auto foundTextRange = WebFoundTextRange { range.location, range.length, frameName.length() ? frameName : emptyString(), order };
 
+        m_cachedFoundRanges.add(foundTextRange, simpleRange);
         foundTextRanges.append(foundTextRange);
     }
 
@@ -77,38 +87,102 @@
 
 void WebFoundTextRangeController::decorateTextRangeWithStyle(const WebFoundTextRange& range, FindDecorationStyle style)
 {
-    UNUSED_PARAM(range);
-    UNUSED_PARAM(style);
+    auto simpleRange = simpleRangeFromFoundTextRange(range);
+    if (!simpleRange)
+        return;
+
+    auto currentStyleForRange = m_decoratedRanges.get(range);
+    if (style == currentStyleForRange)
+        return;
+
+    m_decoratedRanges.set(range, style);
+
+    if (currentStyleForRange == FindDecorationStyle::Highlighted && range == m_highlightedRange) {
+        m_textIndicator = nullptr;
+        m_highlightedRange = { };
+    }
+
+    if (style == FindDecorationStyle::Normal)
+        simpleRange->start.document().markers().removeMarkers(*simpleRange, WebCore::DocumentMarker::TextMatch);
+    else if (style == FindDecorationStyle::Found)
+        simpleRange->start.document().markers().addMarker(*simpleRange, WebCore::DocumentMarker::TextMatch);
+    else if (style == FindDecorationStyle::Highlighted) {
+        m_highlightedRange = range;
+
+        constexpr int indicatorMargin = 1;
+
+        OptionSet options { WebCore::TextIndicatorOption::IncludeMarginIfRangeMatchesSelection, WebCore::TextIndicatorOption::DoNotClipToVisibleRect };
+        if (WebCore::ImageOverlay::isInsideOverlay(*simpleRange))
+            options.add({ WebCore::TextIndicatorOption::PaintAllContent, WebCore::TextIndicatorOption::PaintBackgrounds });
+
+        m_textIndicator = WebCore::TextIndicator::createWithRange(*simpleRange, options, WebCore::TextIndicatorPresentationTransition::None, WebCore::FloatSize(indicatorMargin, indicatorMargin));
+    }
+
+    if (m_findPageOverlay)
+        m_findPageOverlay->setNeedsDisplay();
 }
 
 void WebFoundTextRangeController::scrollTextRangeToVisible(const WebFoundTextRange& range)
 {
-    UNUSED_PARAM(range);
+    auto simpleRange = simpleRangeFromFoundTextRange(range);
+    if (!simpleRange)
+        return;
+
+    auto* document = documentForFoundTextRange(range);
+    if (!document)
+        return;
+
+    WebCore::VisibleSelection visibleSelection(*simpleRange);
+    OptionSet temporarySelectionOptions { WebCore::TemporarySelectionOption::DelegateMainFrameScroll, WebCore::TemporarySelectionOption::RevealSelectionBounds };
+
+    if (document->isTopDocument())
+        temporarySelectionOptions.add(WebCore::TemporarySelectionOption::SmoothScroll);
+
+    WebCore::TemporarySelectionChange selectionChange(*document, visibleSelection, temporarySelectionOptions);
 }
 
 void WebFoundTextRangeController::clearAllDecoratedFoundText()
 {
+    m_cachedFoundRanges.clear();
+    m_decoratedRanges.clear();
+    m_webPage->corePage()->unmarkAllTextMatches();
 
+    m_highlightedRange = { };
+    m_textIndicator = nullptr;
+
+    if (m_findPageOverlay)
+        m_findPageOverlay->setNeedsDisplay();
 }
 
 void WebFoundTextRangeController::didBeginTextSearchOperation()
 {
+    if (!m_findPageOverlay) {
+        m_findPageOverlay = WebCore::PageOverlay::create(*this, WebCore::PageOverlay::OverlayType::Document);
+        m_webPage->corePage()->pageOverlayController().installPageOverlay(*m_findPageOverlay, WebCore::PageOverlay::FadeMode::Fade);
+    }
 
+    m_findPageOverlay->setNeedsDisplay();
 }
 
 void WebFoundTextRangeController::didEndTextSearchOperation()
 {
+    if (m_findPageOverlay)
+        m_webPage->corePage()->pageOverlayController().uninstallPageOverlay(*m_findPageOverlay, WebCore::PageOverlay::FadeMode::Fade);
 
+    m_findPageOverlay = nullptr;
 }
 
 void WebFoundTextRangeController::willMoveToPage(WebCore::PageOverlay&, WebCore::Page* page)
 {
-    UNUSED_PARAM(page);
+    if (page)
+        return;
+
+    ASSERT(m_findPageOverlay);
+    m_findPageOverlay = nullptr;
 }
 
 void WebFoundTextRangeController::didMoveToPage(WebCore::PageOverlay&, WebCore::Page*)
 {
-
 }
 
 bool WebFoundTextRangeController::mouseEvent(WebCore::PageOverlay&, const WebCore::PlatformMouseEvent&)
@@ -118,8 +192,112 @@
 
 void WebFoundTextRangeController::drawRect(WebCore::PageOverlay&, WebCore::GraphicsContext& graphicsContext, const WebCore::IntRect& dirtyRect)
 {
-    UNUSED_PARAM(graphicsContext);
-    UNUSED_PARAM(dirtyRect);
+    constexpr int indicatorRadius = 3;
+    constexpr int indicatorBorderWidth = 1;
+
+    constexpr auto highlightColor = WebCore::SRGBA<uint8_t> { 255, 228, 56 };
+    constexpr auto foundColor = WebCore::Color::white;
+    constexpr auto overlayBackgroundColor = WebCore::SRGBA<uint8_t> { 26, 26, 26, 64 };
+    constexpr auto shadowColor = WebCore::Color::black.colorWithAlphaByte(128);
+
+    constexpr float shadowOffsetX = 0;
+    constexpr float shadowOffsetY = 0;
+    constexpr float shadowBlurRadius = 1;
+
+    WebCore::IntRect borderInflatedDirtyRect = dirtyRect;
+    borderInflatedDirtyRect.inflate(indicatorBorderWidth);
+    Vector<WebCore::FloatRect> rects = rectsForTextMatchesInRect(borderInflatedDirtyRect);
+
+    graphicsContext.fillRect(dirtyRect, overlayBackgroundColor);
+
+    auto foundFramePaths = WebCore::PathUtilities::pathsWithShrinkWrappedRects(rects, indicatorRadius);
+
+    WebCore::GraphicsContextStateSaver stateSaver(graphicsContext);
+
+    graphicsContext.setShadow(WebCore::FloatSize(shadowOffsetX, shadowOffsetY), shadowBlurRadius, shadowColor);
+    graphicsContext.setStrokeColor(foundColor);
+    graphicsContext.setStrokeThickness(indicatorBorderWidth * 2);
+    for (auto& path : foundFramePaths)
+        graphicsContext.strokePath(path);
+
+    graphicsContext.clearShadow();
+
+    graphicsContext.setCompositeOperation(WebCore::CompositeOperator::Clear);
+    for (auto& path : foundFramePaths)
+        graphicsContext.fillPath(path);
+
+    if (m_textIndicator) {
+        graphicsContext.setCompositeOperation(WebCore::CompositeOperator::SourceOver);
+
+        auto* indicatorImage = m_textIndicator->contentImage();
+        if (!indicatorImage)
+            return;
+
+        auto textBoundingRectInRootViewCoordinates = m_textIndicator->textBoundingRectInRootViewCoordinates();
+        auto textRectsInBoundingRectCoordinates = m_textIndicator->textRectsInBoundingRectCoordinates();
+
+        Vector<WebCore::FloatRect> textRectsInRootViewCoordinates;
+        for (auto rect : textRectsInBoundingRectCoordinates) {
+            rect.moveBy(textBoundingRectInRootViewCoordinates.location());
+            textRectsInRootViewCoordinates.append(rect);
+        }
+
+        auto paths = WebCore::PathUtilities::pathsWithShrinkWrappedRects(textRectsInRootViewCoordinates, indicatorRadius);
+
+        graphicsContext.setFillColor(highlightColor);
+        for (const auto& path : paths)
+            graphicsContext.fillPath(path);
+
+        graphicsContext.drawImage(*indicatorImage, textBoundingRectInRootViewCoordinates);
+    }
 }
 
+Vector<WebCore::FloatRect> WebFoundTextRangeController::rectsForTextMatchesInRect(WebCore::IntRect clipRect)
+{
+    Vector<WebCore::FloatRect> rects;
+
+    RefPtr mainFrameView = m_webPage->corePage()->mainFrame().view();
+
+    for (auto* frame = &m_webPage->corePage()->mainFrame(); frame; frame = frame->tree().traverseNext()) {
+        RefPtr document = frame->document();
+        if (!document)
+            continue;
+
+        for (auto rect : document->markers().renderedRectsForMarkers(WebCore::DocumentMarker::TextMatch)) {
+            if (!frame->isMainFrame())
+                rect = mainFrameView->windowToContents(frame->view()->contentsToWindow(enclosingIntRect(rect)));
+
+            if (rect.isEmpty() || !rect.intersects(clipRect))
+                continue;
+
+            rects.append(rect);
+        }
+    }
+
+    return rects;
+}
+
+WebCore::Document* WebFoundTextRangeController::documentForFoundTextRange(const WebFoundTextRange& range) const
+{
+    auto& mainFrame = m_webPage->corePage()->mainFrame();
+    if (range.frameIdentifier.isEmpty())
+        return mainFrame.document();
+
+    if (auto* frame = mainFrame.tree().find(range.frameIdentifier, mainFrame))
+        return frame->document();
+
+    return nullptr;
+}
+
+std::optional<WebCore::SimpleRange> WebFoundTextRangeController::simpleRangeFromFoundTextRange(WebFoundTextRange range)
+{
+    return m_cachedFoundRanges.ensure(range, [&] () -> std::optional<WebCore::SimpleRange> {
+        auto* document = documentForFoundTextRange(range);
+        if (!document)
+            return std::nullopt;
+
+        return resolveCharacterRange(makeRangeSelectingNodeContents(*document->documentElement()), { range.location, range.length }, WebCore::findIteratorOptions());
+    }).iterator->value;
+}
+
 } // namespace WebKit

Modified: trunk/Source/WebKit/WebProcess/WebPage/WebFoundTextRangeController.h (288759 => 288760)


--- trunk/Source/WebKit/WebProcess/WebPage/WebFoundTextRangeController.h	2022-01-28 20:56:26 UTC (rev 288759)
+++ trunk/Source/WebKit/WebProcess/WebPage/WebFoundTextRangeController.h	2022-01-28 21:46:26 UTC (rev 288760)
@@ -30,6 +30,8 @@
 #include <WebCore/FindOptions.h>
 #include <WebCore/IntRect.h>
 #include <WebCore/PageOverlay.h>
+#include <WebCore/SimpleRange.h>
+#include <WebCore/TextIndicator.h>
 #include <wtf/Forward.h>
 #include <wtf/HashMap.h>
 #include <wtf/Noncopyable.h>
@@ -67,7 +69,20 @@
     bool mouseEvent(WebCore::PageOverlay&, const WebCore::PlatformMouseEvent&) override;
     void drawRect(WebCore::PageOverlay&, WebCore::GraphicsContext&, const WebCore::IntRect& dirtyRect) override;
 
+    Vector<WebCore::FloatRect> rectsForTextMatchesInRect(WebCore::IntRect clipRect);
+
+    WebCore::Document* documentForFoundTextRange(const WebFoundTextRange&) const;
+    std::optional<WebCore::SimpleRange> simpleRangeFromFoundTextRange(WebFoundTextRange);
+
     WeakPtr<WebPage> m_webPage;
+    RefPtr<WebCore::PageOverlay> m_findPageOverlay;
+
+    WebFoundTextRange m_highlightedRange;
+
+    HashMap<WebFoundTextRange, std::optional<WebCore::SimpleRange>> m_cachedFoundRanges;
+    HashMap<WebFoundTextRange, FindDecorationStyle> m_decoratedRanges;
+
+    RefPtr<WebCore::TextIndicator> m_textIndicator;
 };
 
 } // namespace WebKit
_______________________________________________
webkit-changes mailing list
[email protected]
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to