Title: [286641] trunk
Revision
286641
Author
akeer...@apple.com
Date
2021-12-07 21:30:53 -0800 (Tue, 07 Dec 2021)

Log Message

[iOS] Add initial support for find-in-page SPI
https://bugs.webkit.org/show_bug.cgi?id=233915
rdar://86140501

Reviewed by Wenson Hsieh.

Source/WebKit:

Expose new find-in-page SPI for use by clients.

* Platform/spi/ios/UIKitSPI.h:
* UIProcess/API/Cocoa/WKWebViewPrivate.h:
* UIProcess/API/ios/WKWebViewIOS.mm:
(-[WKWebView selectedTextRange]):
(-[WKWebView offsetFromPosition:toPosition:inDocument:]):
(-[WKWebView performTextSearchWithQueryString:usingOptions:resultAggregator:]):
(-[WKWebView decorateFoundTextRange:inDocument:usingStyle:]):
(-[WKWebView clearAllDecoratedFoundText]):
* UIProcess/WebPageProxy.cpp:
(WebKit::WebPageProxy::findRectsForStringMatches):
(WebKit::WebPageProxy::hideFindIndicator):
* UIProcess/WebPageProxy.h:
* UIProcess/ios/WKContentViewInteraction.h:
* UIProcess/ios/WKContentViewInteraction.mm:
(-[WKContentView offsetFromPosition:toPosition:]):

The implementation here is needed to determine the relative ordering of
two search results.

(-[WKContentView performTextSearchWithQueryString:usingOptions:resultAggregator:]):

Call into the WebProcess to perform the search, and asynchronously
mark the search as complete.

A search result is represented as a WKFoundTextRange, and contains
two pieces of information.

1. A rect – so that clients can determine whether or not the result
   is visible.

2. An index – to allow for highlighting of a result and to enable
   determining relative ordering between two results.

(-[WKContentView decorateFoundTextRange:usingStyle:]):
(-[WKContentView clearAllDecoratedFoundText]):
(+[WKFoundTextRange foundTextRangeWithRect:index:]):
(-[WKFoundTextRange start]):
(-[WKFoundTextRange end]):
(-[WKFoundTextRange isEmpty]):
(+[WKFoundTextPosition textPositionWithIndex:]):
* WebProcess/WebPage/FindController.cpp:
(WebKit::FindController::findString):

Prevent Editor from revealing the selection on all platforms, as the
existing call to didFindString is now responsible for revealing
selection on all platforms.

(WebKit::FindController::findRectsForStringMatches):

Gather rects for all search results and return them to the UIProcess.

(WebKit::FindController::indicateFindMatch):

This method was previously unused on iOS, and required some changes
to work correctly.

Wrap the call to select the current match with calls to
{will|did}FindString to account for iOS specific selection behavior.
See the existing comment in didFindString for more details.

No behavior change on other platforms, since willFindString is empty,
and didFindString now reveals the selection.

(WebKit::FindController::didFindString):
* WebProcess/WebPage/FindController.h:
* WebProcess/WebPage/WebPage.cpp:
(WebKit::WebPage::findRectsForStringMatches):
(WebKit::WebPage::hideFindIndicator):
* WebProcess/WebPage/WebPage.h:
* WebProcess/WebPage/WebPage.messages.in:

Added two new WebPage messages.

1. findRectsForStringMatches returns an vector of string match rects
   for use in the UIProcess.

2. Expose hideFindIndicator via IPC so that the UIProcess can hide
   the indicator without dismissing the overlay entirely.

Source/WTF:

* wtf/PlatformHave.h:

Tools:

* TestWebKitAPI/Tests/WebKitCocoa/FindInPage.mm:
(-[TestSearchAggregator initWithCompletionHandler:]):
(-[TestSearchAggregator foundRange:forSearchString:inDocument:]):
(-[TestSearchAggregator finishedSearching]):
(TEST):

Added an API test to verify that search results are found correctly.

Modified Paths

Diff

Modified: trunk/Source/WTF/ChangeLog (286640 => 286641)


--- trunk/Source/WTF/ChangeLog	2021-12-08 04:24:38 UTC (rev 286640)
+++ trunk/Source/WTF/ChangeLog	2021-12-08 05:30:53 UTC (rev 286641)
@@ -1,3 +1,13 @@
+2021-12-07  Aditya Keerthi  <akeer...@apple.com>
+
+        [iOS] Add initial support for find-in-page SPI
+        https://bugs.webkit.org/show_bug.cgi?id=233915
+        rdar://86140501
+
+        Reviewed by Wenson Hsieh.
+
+        * wtf/PlatformHave.h:
+
 2021-12-07  Wenson Hsieh  <wenson_hs...@apple.com>
 
         Add support for `navigator.requestCookieConsent()` behind a disabled feature flag

Modified: trunk/Source/WTF/wtf/PlatformHave.h (286640 => 286641)


--- trunk/Source/WTF/wtf/PlatformHave.h	2021-12-08 04:24:38 UTC (rev 286640)
+++ trunk/Source/WTF/wtf/PlatformHave.h	2021-12-08 05:30:53 UTC (rev 286641)
@@ -1111,3 +1111,7 @@
     || (PLATFORM(IOS) && __IPHONE_OS_VERSION_MIN_REQUIRED >= 160000))
 #define HAVE_SANDBOX_STATE_FLAGS 1
 #endif
+
+#if ((PLATFORM(IOS) || PLATFORM(MACCATALYST)) && __IPHONE_OS_VERSION_MIN_REQUIRED >= 160000)
+#define HAVE_UIFINDINTERACTION 1
+#endif

Modified: trunk/Source/WebKit/ChangeLog (286640 => 286641)


--- trunk/Source/WebKit/ChangeLog	2021-12-08 04:24:38 UTC (rev 286640)
+++ trunk/Source/WebKit/ChangeLog	2021-12-08 05:30:53 UTC (rev 286641)
@@ -1,3 +1,92 @@
+2021-12-07  Aditya Keerthi  <akeer...@apple.com>
+
+        [iOS] Add initial support for find-in-page SPI
+        https://bugs.webkit.org/show_bug.cgi?id=233915
+        rdar://86140501
+
+        Reviewed by Wenson Hsieh.
+
+        Expose new find-in-page SPI for use by clients.
+
+        * Platform/spi/ios/UIKitSPI.h:
+        * UIProcess/API/Cocoa/WKWebViewPrivate.h:
+        * UIProcess/API/ios/WKWebViewIOS.mm:
+        (-[WKWebView selectedTextRange]):
+        (-[WKWebView offsetFromPosition:toPosition:inDocument:]):
+        (-[WKWebView performTextSearchWithQueryString:usingOptions:resultAggregator:]):
+        (-[WKWebView decorateFoundTextRange:inDocument:usingStyle:]):
+        (-[WKWebView clearAllDecoratedFoundText]):
+        * UIProcess/WebPageProxy.cpp:
+        (WebKit::WebPageProxy::findRectsForStringMatches):
+        (WebKit::WebPageProxy::hideFindIndicator):
+        * UIProcess/WebPageProxy.h:
+        * UIProcess/ios/WKContentViewInteraction.h:
+        * UIProcess/ios/WKContentViewInteraction.mm:
+        (-[WKContentView offsetFromPosition:toPosition:]):
+
+        The implementation here is needed to determine the relative ordering of
+        two search results.
+
+        (-[WKContentView performTextSearchWithQueryString:usingOptions:resultAggregator:]):
+
+        Call into the WebProcess to perform the search, and asynchronously
+        mark the search as complete.
+
+        A search result is represented as a WKFoundTextRange, and contains
+        two pieces of information.
+
+        1. A rect – so that clients can determine whether or not the result
+           is visible.
+
+        2. An index – to allow for highlighting of a result and to enable
+           determining relative ordering between two results.
+
+        (-[WKContentView decorateFoundTextRange:usingStyle:]):
+        (-[WKContentView clearAllDecoratedFoundText]):
+        (+[WKFoundTextRange foundTextRangeWithRect:index:]):
+        (-[WKFoundTextRange start]):
+        (-[WKFoundTextRange end]):
+        (-[WKFoundTextRange isEmpty]):
+        (+[WKFoundTextPosition textPositionWithIndex:]):
+        * WebProcess/WebPage/FindController.cpp:
+        (WebKit::FindController::findString):
+
+        Prevent Editor from revealing the selection on all platforms, as the
+        existing call to didFindString is now responsible for revealing
+        selection on all platforms.
+
+        (WebKit::FindController::findRectsForStringMatches):
+
+        Gather rects for all search results and return them to the UIProcess.
+
+        (WebKit::FindController::indicateFindMatch):
+
+        This method was previously unused on iOS, and required some changes
+        to work correctly.
+
+        Wrap the call to select the current match with calls to
+        {will|did}FindString to account for iOS specific selection behavior.
+        See the existing comment in didFindString for more details.
+
+        No behavior change on other platforms, since willFindString is empty,
+        and didFindString now reveals the selection.
+
+        (WebKit::FindController::didFindString):
+        * WebProcess/WebPage/FindController.h:
+        * WebProcess/WebPage/WebPage.cpp:
+        (WebKit::WebPage::findRectsForStringMatches):
+        (WebKit::WebPage::hideFindIndicator):
+        * WebProcess/WebPage/WebPage.h:
+        * WebProcess/WebPage/WebPage.messages.in:
+
+        Added two new WebPage messages.
+
+        1. findRectsForStringMatches returns an vector of string match rects
+           for use in the UIProcess.
+
+        2. Expose hideFindIndicator via IPC so that the UIProcess can hide
+           the indicator without dismissing the overlay entirely.
+
 2021-12-07  Wenson Hsieh  <wenson_hs...@apple.com>
 
         Add support for `navigator.requestCookieConsent()` behind a disabled feature flag

Modified: trunk/Source/WebKit/Platform/spi/ios/UIKitSPI.h (286640 => 286641)


--- trunk/Source/WebKit/Platform/spi/ios/UIKitSPI.h	2021-12-08 04:24:38 UTC (rev 286640)
+++ trunk/Source/WebKit/Platform/spi/ios/UIKitSPI.h	2021-12-08 05:30:53 UTC (rev 286641)
@@ -130,6 +130,10 @@
 #import <UIKit/_UITextDragCaretView.h>
 #endif
 
+#if HAVE(UIFINDINTERACTION)
+#import <UIKit/_UITextSearching.h>
+#endif
+
 #if __has_include(<UIKit/UITargetedPreview_Private.h>)
 #import <UIKit/UITargetedPreview_Private.h>
 #endif
@@ -284,6 +288,48 @@
 - (UIEventButtonMask)_buttonMask;
 @end
 
+#if HAVE(UIFINDINTERACTION)
+
+typedef NS_ENUM(NSUInteger, _UIFoundTextStyle) {
+    _UIFoundTextStyleNormal,
+    _UIFoundTextStyleFound,
+    _UIFoundTextStyleHighlighted,
+};
+
+typedef NS_ENUM(NSInteger, _UITextSearchMatchMethod) {
+    _UITextSearchMatchMethodContains,
+    _UITextSearchMatchMethodStartsWith,
+    _UITextSearchMatchMethodFullWord,
+};
+
+typedef id<NSCoding, NSCopying> _UITextSearchDocumentIdentifier;
+
+@interface _UITextSearchOptions : NSObject
+@property (nonatomic, readonly) _UITextSearchMatchMethod wordMatchMethod;
+@property (nonatomic, readonly) NSStringCompareOptions stringCompareOptions;
+@end
+
+@protocol _UITextSearchAggregator <NSObject>
+- (void)foundRange:(UITextRange *)range forSearchString:(NSString *)string inDocument:(_UITextSearchDocumentIdentifier)document;
+- (void)finishedSearching;
+@end
+
+@protocol _UITextSearching <NSObject>
+
+@property (readonly) UITextRange *selectedTextRange;
+
+- (NSInteger)offsetFromPosition:(UITextPosition *)from toPosition:(UITextPosition *)toPosition inDocument:(_UITextSearchDocumentIdentifier)document;
+
+- (void)performTextSearchWithQueryString:(NSString *)string usingOptions:(_UITextSearchOptions *)options resultAggregator:(id<_UITextSearchAggregator>)aggregator;
+
+- (void)decorateFoundTextRange:(UITextRange *)range inDocument:(_UITextSearchDocumentIdentifier)document usingStyle:(_UIFoundTextStyle)style;
+
+- (void)clearAllDecoratedFoundText;
+
+@end
+
+#endif // HAVE(UIFINDINTERACTION)
+
 typedef enum {
     UIFontTraitPlain = 0,
     UIFontTraitItalic = 1 << 0,

Modified: trunk/Source/WebKit/UIProcess/API/Cocoa/WKWebViewPrivate.h (286640 => 286641)


--- trunk/Source/WebKit/UIProcess/API/Cocoa/WKWebViewPrivate.h	2021-12-08 04:24:38 UTC (rev 286640)
+++ trunk/Source/WebKit/UIProcess/API/Cocoa/WKWebViewPrivate.h	2021-12-08 05:30:53 UTC (rev 286641)
@@ -23,6 +23,12 @@
  * THE POSSIBILITY OF SUCH DAMAGE.
  */
 
+#if TARGET_OS_IPHONE
+#if __has_include(<UIKit/_UITextSearching.h>)
+#import <UIKit/_UITextSearching.h>
+#endif
+#endif
+
 #import <WebKit/WKDataDetectorTypes.h>
 #import <WebKit/WKWebView.h>
 #import <WebKit/_WKActivatedElementInfo.h>
@@ -420,7 +426,11 @@
 
 #if TARGET_OS_IPHONE
 
+#if __has_include(<UIKit/_UITextSearching.h>)
+@interface WKWebView (WKPrivateIOS) <_UITextSearching>
+#else
 @interface WKWebView (WKPrivateIOS)
+#endif
 
 #if !TARGET_OS_TV && !TARGET_OS_WATCH
 @property (nonatomic, copy, setter=_setUIEventAttribution:) UIEventAttribution *_uiEventAttribution WK_API_AVAILABLE(ios(15.0));

Modified: trunk/Source/WebKit/UIProcess/API/ios/WKWebViewIOS.mm (286640 => 286641)


--- trunk/Source/WebKit/UIProcess/API/ios/WKWebViewIOS.mm	2021-12-08 04:24:38 UTC (rev 286640)
+++ trunk/Source/WebKit/UIProcess/API/ios/WKWebViewIOS.mm	2021-12-08 05:30:53 UTC (rev 286641)
@@ -3481,6 +3481,35 @@
     });
 }
 
+#if HAVE(UIFINDINTERACTION)
+
+- (UITextRange *)selectedTextRange
+{
+    return nil;
+}
+
+- (NSInteger)offsetFromPosition:(UITextPosition *)from toPosition:(UITextPosition *)toPosition inDocument:(_UITextSearchDocumentIdentifier)document
+{
+    return [_contentView offsetFromPosition:from toPosition:toPosition];
+}
+
+- (void)performTextSearchWithQueryString:(NSString *)string usingOptions:(_UITextSearchOptions *)options resultAggregator:(id<_UITextSearchAggregator>)aggregator
+{
+    [_contentView performTextSearchWithQueryString:string usingOptions:options resultAggregator:aggregator];
+}
+
+- (void)decorateFoundTextRange:(UITextRange *)range inDocument:(_UITextSearchDocumentIdentifier)document usingStyle:(_UIFoundTextStyle)style
+{
+    [_contentView decorateFoundTextRange:range usingStyle:style];
+}
+
+- (void)clearAllDecoratedFoundText
+{
+    [_contentView clearAllDecoratedFoundText];
+}
+
+#endif // HAVE(UIFINDINTERACTION)
+
 @end // WKWebView (WKPrivateIOS)
 
 #if ENABLE(FULLSCREEN_API)

Modified: trunk/Source/WebKit/UIProcess/WebPageProxy.cpp (286640 => 286641)


--- trunk/Source/WebKit/UIProcess/WebPageProxy.cpp	2021-12-08 04:24:38 UTC (rev 286640)
+++ trunk/Source/WebKit/UIProcess/WebPageProxy.cpp	2021-12-08 05:30:53 UTC (rev 286641)
@@ -4273,6 +4273,11 @@
     sendWithAsyncReply(Messages::WebPage::FindString(string, options, maxMatchCount), WTFMove(callbackFunction));
 }
 
+void WebPageProxy::findRectsForStringMatches(const String& string, OptionSet<WebKit::FindOptions> options, unsigned maxMatchCount, CompletionHandler<void(Vector<WebCore::FloatRect>&&)>&& callbackFunction)
+{
+    sendWithAsyncReply(Messages::WebPage::FindRectsForStringMatches(string, options, maxMatchCount), WTFMove(callbackFunction));
+}
+
 void WebPageProxy::getImageForFindMatch(int32_t matchIndex)
 {
     send(Messages::WebPage::GetImageForFindMatch(matchIndex));
@@ -4293,6 +4298,11 @@
     send(Messages::WebPage::HideFindUI());
 }
 
+void WebPageProxy::hideFindIndicator()
+{
+    send(Messages::WebPage::HideFindIndicator());
+}
+
 void WebPageProxy::countStringMatches(const String& string, OptionSet<FindOptions> options, unsigned maxMatchCount)
 {
     if (!hasRunningProcess())

Modified: trunk/Source/WebKit/UIProcess/WebPageProxy.h (286640 => 286641)


--- trunk/Source/WebKit/UIProcess/WebPageProxy.h	2021-12-08 04:24:38 UTC (rev 286640)
+++ trunk/Source/WebKit/UIProcess/WebPageProxy.h	2021-12-08 05:30:53 UTC (rev 286641)
@@ -1200,11 +1200,13 @@
     // Find.
     void findString(const String&, OptionSet<FindOptions>, unsigned maxMatchCount, CompletionHandler<void(bool)>&& = [](bool) { });
     void findStringMatches(const String&, OptionSet<FindOptions>, unsigned maxMatchCount);
+    void findRectsForStringMatches(const String&, OptionSet<WebKit::FindOptions>, unsigned maxMatchCount, CompletionHandler<void(Vector<WebCore::FloatRect>&&)>&&);
     void getImageForFindMatch(int32_t matchIndex);
     void selectFindMatch(int32_t matchIndex);
     void indicateFindMatch(int32_t matchIndex);
     void didGetImageForFindMatch(const ShareableBitmap::Handle& contentImageHandle, uint32_t matchIndex);
     void hideFindUI();
+    void hideFindIndicator();
     void countStringMatches(const String&, OptionSet<FindOptions>, unsigned maxMatchCount);
     void replaceMatches(Vector<uint32_t>&& matchIndices, const String& replacementText, bool selectionOnly, CompletionHandler<void(uint64_t)>&&);
     void didCountStringMatches(const String&, uint32_t matchCount);

Modified: trunk/Source/WebKit/UIProcess/ios/WKContentViewInteraction.h (286640 => 286641)


--- trunk/Source/WebKit/UIProcess/ios/WKContentViewInteraction.h	2021-12-08 04:24:38 UTC (rev 286640)
+++ trunk/Source/WebKit/UIProcess/ios/WKContentViewInteraction.h	2021-12-08 05:30:53 UTC (rev 286641)
@@ -409,6 +409,10 @@
     WeakObjCPtr<WKDataListSuggestionsControl> _dataListSuggestionsControl;
 #endif
 
+#if HAVE(UIFINDINTERACTION)
+    RetainPtr<UITextRange> _foundHighlightedTextRange;
+#endif
+
     BOOL _isEditable;
     BOOL _showingTextStyleOptions;
     BOOL _hasValidPositionInformation;
@@ -711,6 +715,12 @@
 - (void)setTextIndicatorAnimationProgress:(float)NSAnimationProgress;
 - (void)clearTextIndicator:(WebCore::TextIndicatorDismissalAnimation)animation;
 
+#if HAVE(UIFINDINTERACTION)
+- (void)performTextSearchWithQueryString:(NSString *)string usingOptions:(_UITextSearchOptions *)options resultAggregator:(id<_UITextSearchAggregator>)aggregator;
+- (void)decorateFoundTextRange:(UITextRange *)range usingStyle:(_UIFoundTextStyle)style;
+- (void)clearAllDecoratedFoundText;
+#endif
+
 @property (nonatomic, readonly) BOOL _shouldUseContextMenus;
 @property (nonatomic, readonly) BOOL _shouldUseContextMenusForFormControls;
 @property (nonatomic, readonly) BOOL _shouldAvoidResizingWhenInputViewBoundsChange;

Modified: trunk/Source/WebKit/UIProcess/ios/WKContentViewInteraction.mm (286640 => 286641)


--- trunk/Source/WebKit/UIProcess/ios/WKContentViewInteraction.mm	2021-12-08 04:24:38 UTC (rev 286640)
+++ trunk/Source/WebKit/UIProcess/ios/WKContentViewInteraction.mm	2021-12-08 05:30:53 UTC (rev 286641)
@@ -401,6 +401,27 @@
 
 @end
 
+#if HAVE(UIFINDINTERACTION)
+
+@interface WKFoundTextRange : UITextRange
+
+@property (nonatomic) CGRect rect;
+@property (nonatomic) NSUInteger index;
+
++ (WKFoundTextRange *)foundTextRangeWithRect:(CGRect)rect index:(NSUInteger)index;
+
+@end
+
+@interface WKFoundTextPosition : UITextPosition
+
+@property (nonatomic) NSUInteger index;
+
++ (WKFoundTextPosition *)textPositionWithIndex:(NSUInteger)index;
+
+@end
+
+#endif
+
 @interface WKAutocorrectionRects : UIWKAutocorrectionRects
 + (WKAutocorrectionRects *)autocorrectionRectsWithFirstCGRect:(CGRect)firstRect lastCGRect:(CGRect)lastRect;
 @end
@@ -5238,6 +5259,11 @@
 
 - (NSInteger)offsetFromPosition:(UITextPosition *)from toPosition:(UITextPosition *)toPosition
 {
+#if HAVE(UIFINDINTERACTION)
+    if ([from isKindOfClass:[WKFoundTextPosition class]] && [toPosition isKindOfClass:[WKFoundTextPosition class]])
+        return ((WKFoundTextPosition *)from).index - ((WKFoundTextPosition *)toPosition).index;
+#endif
+
     return 0;
 }
 
@@ -9949,6 +9975,47 @@
     }];
 }
 
+#if HAVE(UIFINDINTERACTION)
+
+- (void)performTextSearchWithQueryString:(NSString *)string usingOptions:(_UITextSearchOptions *)options resultAggregator:(id<_UITextSearchAggregator>)aggregator
+{
+    // FIXME: (rdar://86140673) Account for _UITextSearchOptions when performing the search.
+    OptionSet<WebKit::FindOptions> findOptions;
+    findOptions.add(WebKit::FindOptions::ShowOverlay);
+
+    _page->findRectsForStringMatches(string, findOptions, 1000, [string, aggregator = retainPtr(aggregator)](const Vector<WebCore::FloatRect>& rects) {
+        NSUInteger index = 0;
+        for (auto& rect : rects) {
+            WKFoundTextRange *range = [WKFoundTextRange foundTextRangeWithRect:rect index:index];
+            [aggregator foundRange:range forSearchString:string inDocument:nil];
+            index++;
+        }
+
+        [aggregator finishedSearching];
+    });
+}
+
+- (void)decorateFoundTextRange:(UITextRange *)range usingStyle:(_UIFoundTextStyle)style
+{
+    if (![range isKindOfClass:[WKFoundTextRange class]])
+        return;
+
+    if (style == _UIFoundTextStyleHighlighted) {
+        _foundHighlightedTextRange = range;
+        WKFoundTextRange *foundRange = (WKFoundTextRange *)range;
+        _page->indicateFindMatch(foundRange.index);
+    } else if (style == _UIFoundTextStyleFound && _foundHighlightedTextRange == range)
+        _page->hideFindIndicator();
+}
+
+- (void)clearAllDecoratedFoundText
+{
+    _foundHighlightedTextRange = nil;
+    _page->hideFindUI();
+}
+
+#endif // HAVE(UIFINDINTERACTION)
+
 #if ENABLE(IMAGE_ANALYSIS)
 
 #if USE(QUICK_LOOK)
@@ -11805,6 +11872,49 @@
 
 @end
 
+#if HAVE(UIFINDINTERACTION)
+
+@implementation WKFoundTextRange
+
++ (WKFoundTextRange *)foundTextRangeWithRect:(CGRect)rect index:(NSUInteger)index
+{
+    auto range = adoptNS([[WKFoundTextRange alloc] init]);
+    [range setRect:rect];
+    [range setIndex:index];
+    return range.autorelease();
+}
+
+- (WKFoundTextPosition *)start
+{
+    WKFoundTextPosition *position = [WKFoundTextPosition textPositionWithIndex:self.index];
+    return position;
+}
+
+- (UITextPosition *)end
+{
+    return self.start;
+}
+
+- (BOOL)isEmpty
+{
+    return NO;
+}
+
+@end
+
+@implementation WKFoundTextPosition
+
++ (WKFoundTextPosition *)textPositionWithIndex:(NSUInteger)index
+{
+    auto pos = adoptNS([[WKFoundTextPosition alloc] init]);
+    [pos setIndex:index];
+    return pos.autorelease();
+}
+
+@end
+
+#endif
+
 @implementation WKAutocorrectionRects
 
 + (WKAutocorrectionRects *)autocorrectionRectsWithFirstCGRect:(CGRect)firstRect lastCGRect:(CGRect)lastRect

Modified: trunk/Source/WebKit/WebProcess/WebPage/FindController.cpp (286640 => 286641)


--- trunk/Source/WebKit/WebProcess/WebPage/FindController.cpp	2021-12-08 04:24:38 UTC (rev 286640)
+++ trunk/Source/WebKit/WebProcess/WebPage/FindController.cpp	2021-12-08 05:30:53 UTC (rev 286641)
@@ -39,6 +39,7 @@
 #include <WebCore/Frame.h>
 #include <WebCore/FrameSelection.h>
 #include <WebCore/FrameView.h>
+#include <WebCore/GeometryUtilities.h>
 #include <WebCore/GraphicsContext.h>
 #include <WebCore/ImageOverlay.h>
 #include <WebCore/Page.h>
@@ -235,9 +236,10 @@
     // iOS will reveal the selection through a different mechanism, and
     // we need to avoid sending the non-painted selection change to the UI process
     // so that it does not clear the selection out from under us.
-#if PLATFORM(IOS_FAMILY)
+    //
+    // To share logic between platforms, prevent Editor from revealing the selection
+    // and reveal the selection in FindController::didFindString.
     coreOptions.add(DoNotRevealSelection);
-#endif
 
     willFindString();
 
@@ -301,6 +303,27 @@
     });
 }
 
+void FindController::findRectsForStringMatches(const String& string, OptionSet<FindOptions> options, unsigned maxMatchCount, CompletionHandler<void(Vector<FloatRect>&&)>&& completionHandler)
+{
+    auto result = m_webPage->corePage()->findTextMatches(string, core(options), maxMatchCount);
+    m_findMatches = WTFMove(result.ranges);
+
+    auto rects = m_findMatches.map([&] (auto& range) {
+        FloatRect rect = unionRect(RenderObject::absoluteTextRects(range));
+        return range.startContainer().document().frame()->view()->contentsToRootView(rect);
+    });
+
+    completionHandler(WTFMove(rects));
+
+    if (!options.contains(FindOptions::ShowOverlay) && !options.contains(FindOptions::ShowFindIndicator))
+        return;
+
+    bool found = !m_findMatches.isEmpty();
+    m_webPage->drawingArea()->dispatchAfterEnsuringUpdatedScrollPosition([protectedWebPage = RefPtr { m_webPage }, found, string, options, maxMatchCount] () {
+        protectedWebPage->findController().updateFindUIAfterPageScroll(found, string, options, maxMatchCount, DidWrap::No, FindUIOriginator::FindStringMatches);
+    });
+}
+
 void FindController::getImageForFindMatch(uint32_t matchIndex)
 {
     if (matchIndex >= m_findMatches.size())
@@ -344,6 +367,8 @@
 
 void FindController::indicateFindMatch(uint32_t matchIndex)
 {
+    willFindString();
+
     selectFindMatch(matchIndex);
 
     Frame* selectedFrame = frameWithSelection(m_webPage->corePage());
@@ -350,7 +375,7 @@
     if (!selectedFrame)
         return;
 
-    selectedFrame->selection().revealSelection();
+    didFindString();
 
     updateFindIndicator(*selectedFrame, !!m_findPageOverlay);
 }
@@ -412,6 +437,11 @@
 
 void FindController::didFindString()
 {
+    Frame* selectedFrame = frameWithSelection(m_webPage->corePage());
+    if (!selectedFrame)
+        return;
+
+    selectedFrame->selection().revealSelection();
 }
 
 void FindController::didFailToFindString()

Modified: trunk/Source/WebKit/WebProcess/WebPage/FindController.h (286640 => 286641)


--- trunk/Source/WebKit/WebProcess/WebPage/FindController.h	2021-12-08 04:24:38 UTC (rev 286640)
+++ trunk/Source/WebKit/WebProcess/WebPage/FindController.h	2021-12-08 05:30:53 UTC (rev 286641)
@@ -60,6 +60,7 @@
 
     void findString(const String&, OptionSet<FindOptions>, unsigned maxMatchCount, CompletionHandler<void(bool)>&&);
     void findStringMatches(const String&, OptionSet<FindOptions>, unsigned maxMatchCount);
+    void findRectsForStringMatches(const String&, OptionSet<WebKit::FindOptions>, unsigned maxMatchCount, CompletionHandler<void(Vector<WebCore::FloatRect>&&)>&&);
     void getImageForFindMatch(uint32_t matchIndex);
     void selectFindMatch(uint32_t matchIndex);
     void indicateFindMatch(uint32_t matchIndex);

Modified: trunk/Source/WebKit/WebProcess/WebPage/WebPage.cpp (286640 => 286641)


--- trunk/Source/WebKit/WebProcess/WebPage/WebPage.cpp	2021-12-08 04:24:38 UTC (rev 286640)
+++ trunk/Source/WebKit/WebProcess/WebPage/WebPage.cpp	2021-12-08 05:30:53 UTC (rev 286641)
@@ -4716,6 +4716,16 @@
     findController().findStringMatches(string, options, maxMatchCount);
 }
 
+void WebPage::findRectsForStringMatches(const String& string, OptionSet<FindOptions> options, uint32_t maxMatchCount, CompletionHandler<void(Vector<FloatRect>&&)>&& completionHandler)
+{
+    findController().findRectsForStringMatches(string, options, maxMatchCount, WTFMove(completionHandler));
+}
+
+void WebPage::hideFindIndicator()
+{
+    findController().hideFindIndicator();
+}
+
 void WebPage::getImageForFindMatch(uint32_t matchIndex)
 {
     findController().getImageForFindMatch(matchIndex);

Modified: trunk/Source/WebKit/WebProcess/WebPage/WebPage.h (286640 => 286641)


--- trunk/Source/WebKit/WebProcess/WebPage/WebPage.h	2021-12-08 04:24:38 UTC (rev 286640)
+++ trunk/Source/WebKit/WebProcess/WebPage/WebPage.h	2021-12-08 05:30:53 UTC (rev 286641)
@@ -1763,6 +1763,8 @@
     void hideFindUI();
     void countStringMatches(const String&, OptionSet<FindOptions>, uint32_t maxMatchCount);
     void replaceMatches(const Vector<uint32_t>& matchIndices, const String& replacementText, bool selectionOnly, CompletionHandler<void(uint64_t)>&&);
+    void findRectsForStringMatches(const String&, OptionSet<FindOptions>, uint32_t maxMatchCount, CompletionHandler<void(Vector<WebCore::FloatRect>&&)>&&);
+    void hideFindIndicator();
 
 #if USE(COORDINATED_GRAPHICS)
     void sendViewportAttributesChanged(const WebCore::ViewportArguments&);

Modified: trunk/Source/WebKit/WebProcess/WebPage/WebPage.messages.in (286640 => 286641)


--- trunk/Source/WebKit/WebProcess/WebPage/WebPage.messages.in	2021-12-08 04:24:38 UTC (rev 286640)
+++ trunk/Source/WebKit/WebProcess/WebPage/WebPage.messages.in	2021-12-08 05:30:53 UTC (rev 286641)
@@ -312,6 +312,9 @@
     HideFindUI()
     CountStringMatches(String string, OptionSet<WebKit::FindOptions> findOptions, unsigned maxMatchCount)
     ReplaceMatches(Vector<uint32_t> matchIndices, String replacementText, bool selectionOnly) -> (uint64_t numberOfReplacements) Async
+
+    FindRectsForStringMatches(String string, OptionSet<WebKit::FindOptions> findOptions, unsigned maxMatchCount) -> (Vector<WebCore::FloatRect> matches) Async
+    HideFindIndicator()
     
     AddMIMETypeWithCustomContentProvider(String mimeType)
 

Modified: trunk/Tools/ChangeLog (286640 => 286641)


--- trunk/Tools/ChangeLog	2021-12-08 04:24:38 UTC (rev 286640)
+++ trunk/Tools/ChangeLog	2021-12-08 05:30:53 UTC (rev 286641)
@@ -1,3 +1,19 @@
+2021-12-07  Aditya Keerthi  <akeer...@apple.com>
+
+        [iOS] Add initial support for find-in-page SPI
+        https://bugs.webkit.org/show_bug.cgi?id=233915
+        rdar://86140501
+
+        Reviewed by Wenson Hsieh.
+
+        * TestWebKitAPI/Tests/WebKitCocoa/FindInPage.mm:
+        (-[TestSearchAggregator initWithCompletionHandler:]):
+        (-[TestSearchAggregator foundRange:forSearchString:inDocument:]):
+        (-[TestSearchAggregator finishedSearching]):
+        (TEST):
+
+        Added an API test to verify that search results are found correctly.
+
 2021-12-07  Wenson Hsieh  <wenson_hs...@apple.com>
 
         Add support for `navigator.requestCookieConsent()` behind a disabled feature flag

Modified: trunk/Tools/TestWebKitAPI/Tests/WebKitCocoa/FindInPage.mm (286640 => 286641)


--- trunk/Tools/TestWebKitAPI/Tests/WebKitCocoa/FindInPage.mm	2021-12-08 04:24:38 UTC (rev 286640)
+++ trunk/Tools/TestWebKitAPI/Tests/WebKitCocoa/FindInPage.mm	2021-12-08 05:30:53 UTC (rev 286641)
@@ -30,8 +30,13 @@
 #import "TestWKWebView.h"
 #import "WKWebViewConfigurationExtras.h"
 #import <WebKit/WKWebViewPrivate.h>
+#import <wtf/BlockPtr.h>
 #import <wtf/RetainPtr.h>
 
+#if PLATFORM(IOS_FAMILY)
+#import "UIKitSPI.h"
+#endif
+
 #if !PLATFORM(IOS_FAMILY)
 
 typedef enum : NSUInteger {
@@ -321,3 +326,72 @@
 #endif // ENABLE(IMAGE_ANALYSIS)
 
 #endif // !PLATFORM(IOS_FAMILY)
+
+#if HAVE(UIFINDINTERACTION)
+
+@interface TestTextSearchOptions : NSObject
+@property (nonatomic, readonly) _UITextSearchMatchMethod wordMatchMethod;
+@property (nonatomic, readonly) NSStringCompareOptions stringCompareOptions;
+@end
+
+@implementation TestTextSearchOptions
+@end
+
+@interface TestSearchAggregator : NSObject <_UITextSearchAggregator>
+
+@property (readonly) NSUInteger count;
+
+- (instancetype)initWithCompletionHandler:(dispatch_block_t)completionHandler;
+
+@end
+
+@implementation TestSearchAggregator {
+    BlockPtr<void()> _completionHandler;
+}
+
+- (instancetype)initWithCompletionHandler:(dispatch_block_t)completionHandler
+{
+    if (!(self = [super init]))
+        return nil;
+
+    _count = 0;
+    _completionHandler = makeBlockPtr(completionHandler);
+
+    return self;
+}
+
+- (void)foundRange:(UITextRange *)range forSearchString:(NSString *)string inDocument:(_UITextSearchDocumentIdentifier)document
+{
+    _count++;
+}
+
+- (void)finishedSearching
+{
+    if (_completionHandler)
+        _completionHandler();
+}
+
+@end
+
+TEST(WebKit, FindInPage)
+{
+    RetainPtr<WKWebView> webView = adoptNS([[WKWebView alloc] initWithFrame:CGRectMake(0, 0, 200, 200)]);
+
+    NSURLRequest *request = [NSURLRequest requestWithURL:[[NSBundle mainBundle] URLForResource:@"lots-of-text" withExtension:@"html" subdirectory:@"TestWebKitAPI.resources"]];
+    [webView loadRequest:request];
+    [webView _test_waitForDidFinishNavigation];
+
+    __block bool finishedSearching = false;
+    RetainPtr aggregator = adoptNS([[TestSearchAggregator alloc] initWithCompletionHandler:^{
+        finishedSearching = true;
+    }]);
+
+    // FIXME: (rdar://86140914) Use _UITextSearchOptions directly when the symbol is exported.
+    [webView performTextSearchWithQueryString:@"Birthday" usingOptions:(_UITextSearchOptions *)[[TestTextSearchOptions alloc] init] resultAggregator:aggregator.get()];
+
+    TestWebKitAPI::Util::run(&finishedSearching);
+
+    EXPECT_EQ([aggregator count], 360UL);
+}
+
+#endif // HAVE(UIFINDINTERACTION)
_______________________________________________
webkit-changes mailing list
webkit-changes@lists.webkit.org
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to