Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
Find on page intermittently fails to show results in PDFs in Safari
https://bugs.webkit.org/show_bug.cgi?id=251311
rdar://100155151

Reviewed by Aditya Keerthi.

When viewing a PDF in Safari, Find in Page fails after the following sequence
of events:

1. Open a PDF in Safari
2. Activate Find in Page and search for a string
3. Refresh the page
4. Activate Find in Page again

After the last step, Find In Page will continue to return zero results regardless
of the search string.

This is because the `WKPDFView` instance is used as the `UIFindSession`'s "searchable object"
to process the search. When the page is refreshed, a new `WKPDFView` instance is created, but
not a new `UIFindSession`, and so the session has lost its searchable object.

This PR fixes this by setting the session's searchable object to the new `WKPDFView` in the
`_didCommitLoadForMainFrame` method, which is called after the new `WKPDFView` has been created.
This ensures that the session's searchable object is up to date.

* Source/WebKit/Platform/spi/ios/UIKitSPI.h:
* Source/WebKit/UIProcess/API/ios/WKWebViewIOS.mm:
(-[WKWebView _didCommitLoadForMainFrame]):
* Tools/TestWebKitAPI/Tests/WebKitCocoa/FindInPage.mm:
(swizzledPerformTextSearchWithQueryString):
(TEST):

Canonical link: https://commits.webkit.org/259655@main
  • Loading branch information
rr-codes committed Feb 1, 2023
1 parent b0d7dee commit dd929cd
Show file tree
Hide file tree
Showing 3 changed files with 48 additions and 2 deletions.
5 changes: 5 additions & 0 deletions Source/WebKit/Platform/spi/ios/UIKitSPI.h
Expand Up @@ -136,6 +136,7 @@
#endif

#if HAVE(UIFINDINTERACTION)
#import <UIKit/UIFindSession_Private.h>
#import <UIKit/_UIFindInteraction.h>
#import <UIKit/_UITextSearching.h>
#endif
Expand Down Expand Up @@ -364,6 +365,10 @@ typedef id<NSCoding, NSCopying> _UITextSearchDocumentIdentifier;
@property (class, nonatomic, copy, getter=_globalFindBuffer, setter=_setGlobalFindBuffer:) NSString *_globalFindBuffer;
@end

@interface UITextSearchingFindSession ()
@property (nonatomic, readwrite, weak) id<UITextSearching> searchableObject;
@end

#endif // HAVE(UIFINDINTERACTION)

typedef enum {
Expand Down
6 changes: 5 additions & 1 deletion Source/WebKit/UIProcess/API/ios/WKWebViewIOS.mm
Expand Up @@ -818,8 +818,12 @@ - (void)_didCommitLoadForMainFrame
[_scrollView _stopScrollingAndZoomingAnimations];

#if HAVE(UIFINDINTERACTION)
if (_findInteractionEnabled)
if (_findInteractionEnabled) {
[_findInteraction dismissFindNavigator];

if (auto *findSession = dynamic_objc_cast<UITextSearchingFindSession>([_findInteraction activeFindSession]))
findSession.searchableObject = [self _searchableObject];
}
#endif
}

Expand Down
39 changes: 38 additions & 1 deletion Tools/TestWebKitAPI/Tests/WebKitCocoa/FindInPage.mm
Expand Up @@ -862,16 +862,19 @@ static void testPerformTextSearchWithQueryStringInWebView(WKWebView *webView, NS
EXPECT_EQ(overlayCount(webView.get()), 1U);
}

static bool hasPerformedTextSearchWithQueryString = false;

static void swizzledPerformTextSearchWithQueryString(id, SEL, NSString *, UITextSearchOptions *, id<UITextSearchAggregator> aggregator)
{
[aggregator finishedSearching];
hasPerformedTextSearchWithQueryString = true;
}

TEST(WebKit, FindInPDF)
{
// Swizzle out the method that performs searching, since PDFHostViewController (a remote view
// (controller) cannot be created in TestWebKitAPI, and we cannot actually search the PDF.
std::unique_ptr<InstanceMethodSwizzler> isInBackgroundSwizzler = makeUnique<InstanceMethodSwizzler>(NSClassFromString(@"WKPDFView"), @selector(performTextSearchWithQueryString:usingOptions:resultAggregator:), reinterpret_cast<IMP>(swizzledPerformTextSearchWithQueryString));
std::unique_ptr<InstanceMethodSwizzler> performTextSearchInPDFWithQueryStringSwizzler = makeUnique<InstanceMethodSwizzler>(NSClassFromString(@"WKPDFView"), @selector(performTextSearchWithQueryString:usingOptions:resultAggregator:), reinterpret_cast<IMP>(swizzledPerformTextSearchWithQueryString));

auto webView = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600)]);

Expand All @@ -881,6 +884,40 @@ static void swizzledPerformTextSearchWithQueryString(id, SEL, NSString *, UIText

auto searchOptions = adoptNS([[UITextSearchOptions alloc] init]);
testPerformTextSearchWithQueryStringInWebView(webView.get(), @"Birthday", searchOptions.get(), 0UL);

hasPerformedTextSearchWithQueryString = false;
}

TEST(WebKit, FindInPDFAfterReload)
{
// Swizzle out the method that performs searching, since PDFHostViewController (a remote view
// (controller) cannot be created in TestWebKitAPI, and we cannot actually search the PDF.
std::unique_ptr<InstanceMethodSwizzler> performTextSearchInPDFWithQueryStringSwizzler = makeUnique<InstanceMethodSwizzler>(NSClassFromString(@"WKPDFView"), @selector(performTextSearchWithQueryString:usingOptions:resultAggregator:), reinterpret_cast<IMP>(swizzledPerformTextSearchWithQueryString));

auto webView = adoptNS([[FindInPageTestWKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600)]);

auto searchForText = [&] {
NSURLRequest *request = [NSURLRequest requestWithURL:[[NSBundle mainBundle] URLForResource:@"test" withExtension:@"pdf" subdirectory:@"TestWebKitAPI.resources"]];
[webView loadRequest:request];
[webView _test_waitForDidFinishNavigation];

auto *findInteraction = [webView findInteraction];
[findInteraction presentFindNavigatorShowingReplace:NO];
[webView waitForNextPresentationUpdate];

auto *findSession = [findInteraction activeFindSession];
[findSession performSearchWithQuery:@"Birthday" options:0];

TestWebKitAPI::Util::run(&hasPerformedTextSearchWithQueryString);

[findInteraction dismissFindNavigator];
[webView waitForNextPresentationUpdate];

hasPerformedTextSearchWithQueryString = false;
};

searchForText();
searchForText();
}

TEST(WebKit, FindInteractionSupportsTextReplacement)
Expand Down

0 comments on commit dd929cd

Please sign in to comment.