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