[iOS] Page not defocused when Find-in-page becomes first responder
authordbates@webkit.org <dbates@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Wed, 28 Nov 2018 21:55:13 +0000 (21:55 +0000)
committerdbates@webkit.org <dbates@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Wed, 28 Nov 2018 21:55:13 +0000 (21:55 +0000)
https://bugs.webkit.org/show_bug.cgi?id=192084
<rdar://problem/46146777>

Reviewed by Tim Horton.

Source/WebKit:

When -becomeFirstResponder and -resignFirstResponder is called the page should become focused
and defocused, respectively. WebKit adjusts it behavior depending on whether it knows it has
focus. This includes decisions about whether to allow an element to be focused. We need to
ensure that WebKit knows whether the web view is focused for it to make good decisions.

* UIProcess/ios/PageClientImplIOS.mm:
(WebKit::PageClientImpl::isViewFocused): Check if the content view is the first responder and
hence is accepting keyboard input. Note that picture-in-picture views do not accept keyboard
input.
* UIProcess/ios/WKContentViewInteraction.mm:
(-[WKContentView becomeFirstResponderForWebView]): Update activity state when the view
becomes focused.
(-[WKContentView resignFirstResponderForWebView]): Update activity state when the view
becomes defocused.

Tools:

Add tests for both Mac and iOS to ensure that when finding a string on the page
that we do not start an input session if a match is found in an <input> and the
WKWebView is not the first responder.

* TestWebKitAPI/TestWebKitAPI.xcodeproj/project.pbxproj:
* TestWebKitAPI/Tests/WebKitCocoa/WKWebViewFindString.mm: Added.
(-[WKWebViewFindStringInputDelegate _webView:didStartInputSession:]):
(-[WKWebViewFindStringFindDelegate findString]):
(-[WKWebViewFindStringFindDelegate _webView:didCountMatches:forString:]):
(-[WKWebViewFindStringFindDelegate _webView:didFindMatches:forString:withMatchIndex:]):
(-[WKWebViewFindStringFindDelegate _webView:didFailToFindString:]):
(returnNo):
(returnYes):
(viewIsFirstResponder):
(TestWebKitAPI::TEST):
* WebKitTestRunner/ios/PlatformWebViewIOS.mm:
(WTR::PlatformWebView::PlatformWebView): Make the view the first responder before the
window key. This matches the ordering used in PlatformWebView::focus(). No behavior change.

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

Source/WebKit/ChangeLog
Source/WebKit/UIProcess/ios/PageClientImplIOS.mm
Source/WebKit/UIProcess/ios/WKContentViewInteraction.mm
Tools/ChangeLog
Tools/TestWebKitAPI/TestWebKitAPI.xcodeproj/project.pbxproj
Tools/TestWebKitAPI/Tests/WebKitCocoa/WKWebViewFindString.mm [new file with mode: 0644]
Tools/WebKitTestRunner/ios/PlatformWebViewIOS.mm

index a5722af..8c25fe6 100644 (file)
@@ -1,3 +1,26 @@
+2018-11-28  Daniel Bates  <dabates@apple.com>
+
+        [iOS] Page not defocused when Find-in-page becomes first responder
+        https://bugs.webkit.org/show_bug.cgi?id=192084
+        <rdar://problem/46146777>
+
+        Reviewed by Tim Horton.
+
+        When -becomeFirstResponder and -resignFirstResponder is called the page should become focused
+        and defocused, respectively. WebKit adjusts it behavior depending on whether it knows it has
+        focus. This includes decisions about whether to allow an element to be focused. We need to
+        ensure that WebKit knows whether the web view is focused for it to make good decisions.
+
+        * UIProcess/ios/PageClientImplIOS.mm:
+        (WebKit::PageClientImpl::isViewFocused): Check if the content view is the first responder and
+        hence is accepting keyboard input. Note that picture-in-picture views do not accept keyboard
+        input.
+        * UIProcess/ios/WKContentViewInteraction.mm:
+        (-[WKContentView becomeFirstResponderForWebView]): Update activity state when the view
+        becomes focused.
+        (-[WKContentView resignFirstResponderForWebView]): Update activity state when the view
+        becomes defocused.
+
 2018-11-28  Alex Christensen  <achristensen@webkit.org>
 
         Remove dead code from an earlier attempt at implementing safe browsing
index 233bd89..a2e39a7 100644 (file)
@@ -166,8 +166,9 @@ bool PageClientImpl::isViewWindowActive()
 
 bool PageClientImpl::isViewFocused()
 {
-    // FIXME: https://bugs.webkit.org/show_bug.cgi?id=133098
-    return isViewWindowActive() || [m_webView _isRetainingActiveFocusedState];
+    if (isViewInWindow() && ![m_webView _isBackground])
+        return [m_webView _contentViewIsFirstResponder];
+    return [m_webView _isRetainingActiveFocusedState];
 }
 
 bool PageClientImpl::isViewVisible()
index 3b6a186..60cfb8f 100644 (file)
@@ -1034,6 +1034,9 @@ static inline bool hasAssistedNode(WebKit::AssistedNodeInformation assistedNodeI
         didBecomeFirstResponder = [super becomeFirstResponder];
     }
 
+    if (didBecomeFirstResponder)
+        _page->activityStateDidChange(WebCore::ActivityState::IsFocused);
+
     if (didBecomeFirstResponder && [self canShowNonEmptySelectionView])
         [_textSelectionAssistant activateSelection];
 
@@ -1064,6 +1067,9 @@ static inline bool hasAssistedNode(WebKit::AssistedNodeInformation assistedNodeI
 
     bool superDidResign = [super resignFirstResponder];
 
+    if (superDidResign)
+        _page->activityStateDidChange(WebCore::ActivityState::IsFocused);
+
     _resigningFirstResponder = NO;
 
     return superDidResign;
index 750172c..937df73 100644 (file)
@@ -1,3 +1,30 @@
+2018-11-28  Daniel Bates  <dabates@apple.com>
+
+        [iOS] Page not defocused when Find-in-page becomes first responder
+        https://bugs.webkit.org/show_bug.cgi?id=192084
+        <rdar://problem/46146777>
+
+        Reviewed by Tim Horton.
+
+        Add tests for both Mac and iOS to ensure that when finding a string on the page
+        that we do not start an input session if a match is found in an <input> and the
+        WKWebView is not the first responder.
+
+        * TestWebKitAPI/TestWebKitAPI.xcodeproj/project.pbxproj:
+        * TestWebKitAPI/Tests/WebKitCocoa/WKWebViewFindString.mm: Added.
+        (-[WKWebViewFindStringInputDelegate _webView:didStartInputSession:]):
+        (-[WKWebViewFindStringFindDelegate findString]):
+        (-[WKWebViewFindStringFindDelegate _webView:didCountMatches:forString:]):
+        (-[WKWebViewFindStringFindDelegate _webView:didFindMatches:forString:withMatchIndex:]):
+        (-[WKWebViewFindStringFindDelegate _webView:didFailToFindString:]):
+        (returnNo):
+        (returnYes):
+        (viewIsFirstResponder):
+        (TestWebKitAPI::TEST):
+        * WebKitTestRunner/ios/PlatformWebViewIOS.mm:
+        (WTR::PlatformWebView::PlatformWebView): Make the view the first responder before the
+        window key. This matches the ordering used in PlatformWebView::focus(). No behavior change.
+
 2018-11-28  Aakash Jain  <aakash_jain@apple.com>
 
         [ews-app] Add BeautifulSoup
index b005fc2..ff4293f 100644 (file)
                CE3524F81B1431F60028A7C5 /* TextFieldDidBeginAndEndEditing_Bundle.cpp in Sources */ = {isa = PBXBuildFile; fileRef = CE3524F21B142B8D0028A7C5 /* TextFieldDidBeginAndEndEditing_Bundle.cpp */; };
                CE3524F91B1441C40028A7C5 /* TextFieldDidBeginAndEndEditing.cpp in Sources */ = {isa = PBXBuildFile; fileRef = CE3524F11B142B8D0028A7C5 /* TextFieldDidBeginAndEndEditing.cpp */; };
                CE3524FA1B1443890028A7C5 /* input-focus-blur.html in Copy Resources */ = {isa = PBXBuildFile; fileRef = CE3524F51B142BBB0028A7C5 /* input-focus-blur.html */; };
+               CE449E1121AE0F7200E7ADA1 /* WKWebViewFindString.mm in Sources */ = {isa = PBXBuildFile; fileRef = CE449E1021AE0F7200E7ADA1 /* WKWebViewFindString.mm */; };
                CE4D5DE71F6743BA0072CFC6 /* StringWithDirection.cpp in Sources */ = {isa = PBXBuildFile; fileRef = CE4D5DE51F6743BA0072CFC6 /* StringWithDirection.cpp */; };
                CE6E81A020A6935F00E2C80F /* SetTimeoutFunction.mm in Sources */ = {isa = PBXBuildFile; fileRef = CE6E819F20A6935F00E2C80F /* SetTimeoutFunction.mm */; };
                CE6E81A420A933D500E2C80F /* set-timeout-function.html in Copy Resources */ = {isa = PBXBuildFile; fileRef = CE6E81A320A933B800E2C80F /* set-timeout-function.html */; };
                CE3524F11B142B8D0028A7C5 /* TextFieldDidBeginAndEndEditing.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = TextFieldDidBeginAndEndEditing.cpp; sourceTree = "<group>"; };
                CE3524F21B142B8D0028A7C5 /* TextFieldDidBeginAndEndEditing_Bundle.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = TextFieldDidBeginAndEndEditing_Bundle.cpp; sourceTree = "<group>"; };
                CE3524F51B142BBB0028A7C5 /* input-focus-blur.html */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.html; path = "input-focus-blur.html"; sourceTree = "<group>"; };
+               CE449E1021AE0F7200E7ADA1 /* WKWebViewFindString.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = WKWebViewFindString.mm; sourceTree = "<group>"; };
                CE4D5DE51F6743BA0072CFC6 /* StringWithDirection.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = StringWithDirection.cpp; sourceTree = "<group>"; };
                CE50D8C81C8665CE0072EA5A /* OptionSet.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = OptionSet.cpp; sourceTree = "<group>"; };
                CE6E819F20A6935F00E2C80F /* SetTimeoutFunction.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = SetTimeoutFunction.mm; sourceTree = "<group>"; };
                                2DB647871F4161F70051A89E /* WKWebViewDoesNotLogDuringInitialization.mm */,
                                F4811E5821940B4400A5E0FD /* WKWebViewEditActions.mm */,
                                0F3B94A51A77266C00DE3272 /* WKWebViewEvaluateJavaScript.mm */,
+                               CE449E1021AE0F7200E7ADA1 /* WKWebViewFindString.mm */,
                                D3BE5E341E4CE85E00FD563A /* WKWebViewGetContents.mm */,
                                37A9DBE7213B4C9300D261A2 /* WKWebViewServerTrustKVC.mm */,
                                93F56DA81E5F9181003EDE84 /* WKWebViewSnapshot.mm */,
                                2DB647881F4163D60051A89E /* WKWebViewDoesNotLogDuringInitialization.mm in Sources */,
                                F4811E5921940BDE00A5E0FD /* WKWebViewEditActions.mm in Sources */,
                                0F3B94A71A77267400DE3272 /* WKWebViewEvaluateJavaScript.mm in Sources */,
+                               CE449E1121AE0F7200E7ADA1 /* WKWebViewFindString.mm in Sources */,
                                F4106C6921ACBF84004B89A1 /* WKWebViewFirstResponderTests.mm in Sources */,
                                D34E08761E4E42E1005FF14A /* WKWebViewGetContents.mm in Sources */,
                                F4FA91811E61849B007B8C1D /* WKWebViewMacEditingTests.mm in Sources */,
diff --git a/Tools/TestWebKitAPI/Tests/WebKitCocoa/WKWebViewFindString.mm b/Tools/TestWebKitAPI/Tests/WebKitCocoa/WKWebViewFindString.mm
new file mode 100644 (file)
index 0000000..a3633fa
--- /dev/null
@@ -0,0 +1,177 @@
+/*
+ * Copyright (C) 2018 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+
+#import "PlatformUtilities.h"
+#import "TestWKWebView.h"
+#import <WebKit/WKWebViewPrivate.h>
+#import <WebKit/_WKFindDelegate.h>
+#import <WebKit/_WKInputDelegate.h>
+
+#if PLATFORM(IOS_FAMILY)
+#import "ClassMethodSwizzler.h"
+#import "UIKitSPI.h"
+#endif
+
+#if WK_API_ENABLED
+
+static bool isDone;
+static bool focusDidStartInputSession;
+static const NSUInteger maxCount = 100;
+
+@interface WKWebViewFindStringInputDelegate : NSObject <_WKInputDelegate>
+@end
+
+@implementation WKWebViewFindStringInputDelegate
+
+- (void)_webView:(WKWebView *)webView didStartInputSession:(id <_WKFormInputSession>)inputSession
+{
+    focusDidStartInputSession = YES;
+}
+
+@end
+
+@interface WKWebViewFindStringFindDelegate : NSObject <_WKFindDelegate>
+@property (nonatomic, readonly) NSString *findString;
+@property (nonatomic, readonly) NSUInteger matchesCount;
+@property (nonatomic, readonly) NSInteger matchIndex;
+@property (nonatomic, readonly) BOOL didFail;
+@end
+
+@implementation WKWebViewFindStringFindDelegate {
+    RetainPtr<NSString> _findString;
+}
+
+- (NSString *)findString
+{
+    return _findString.get();
+}
+
+- (void)_webView:(WKWebView *)webView didCountMatches:(NSUInteger)matches forString:(NSString *)string
+{
+    _findString = string;
+    _matchesCount = matches;
+    _didFail = NO;
+    isDone = YES;
+}
+
+- (void)_webView:(WKWebView *)webView didFindMatches:(NSUInteger)matches forString:(NSString *)string withMatchIndex:(NSInteger)matchIndex
+{
+    _findString = string;
+    _matchesCount = matches;
+    _matchIndex = matchIndex;
+    _didFail = NO;
+    isDone = YES;
+}
+
+- (void)_webView:(WKWebView *)webView didFailToFindString:(NSString *)string
+{
+    _findString = string;
+    _didFail = YES;
+    isDone = YES;
+}
+
+@end
+
+#if PLATFORM(IOS_FAMILY)
+static BOOL returnNo()
+{
+    return NO;
+}
+
+static BOOL returnYes()
+{
+    return YES;
+}
+
+static BOOL viewIsFirstResponder(UIView *view)
+{
+    return [view isFirstResponder];
+}
+#else
+static BOOL viewIsFirstResponder(NSView *view)
+{
+    return view.window.firstResponder == view;
+}
+#endif
+
+namespace TestWebKitAPI {
+
+TEST(WKWebViewFindString, DoNotFocusMatchWhenWebViewResignedAndHardwareKeyboardAttached)
+{
+#if PLATFORM(IOS_FAMILY)
+    ClassMethodSwizzler swizzler([UIKeyboard class], @selector(isInHardwareKeyboardMode), reinterpret_cast<IMP>(returnYes));
+#endif
+
+    auto inputDelegate = adoptNS([[WKWebViewFindStringInputDelegate alloc] init]);
+    auto findDelegate = adoptNS([[WKWebViewFindStringFindDelegate alloc] init]);
+    auto configuration = adoptNS([[WKWebViewConfiguration alloc] init]);
+    auto firstWebView = adoptNS([[TestWKWebView alloc] initWithFrame:CGRectMake(0, 0, 300, 200) configuration:configuration.get() addToWindow:YES]);
+    [firstWebView synchronouslyLoadHTMLString:@"<input type='text' value='hello'>"];
+    [firstWebView _setInputDelegate:inputDelegate.get()];
+    [firstWebView _setFindDelegate:findDelegate.get()];
+
+    auto secondWebView = adoptNS([[TestWKWebView alloc] initWithFrame:CGRectMake(300, 0, 300, 200) configuration:configuration.get() addToWindow:YES]);
+    EXPECT_TRUE([secondWebView becomeFirstResponder]);
+    EXPECT_FALSE(viewIsFirstResponder(firstWebView.get()));
+
+    [firstWebView _findString:@"hello" options:0 maxCount:maxCount];
+    Util::run(&isDone);
+
+    EXPECT_FALSE(viewIsFirstResponder(firstWebView.get()));
+    EXPECT_WK_STREQ("hello", [findDelegate findString]);
+    EXPECT_FALSE(focusDidStartInputSession);
+}
+
+#if PLATFORM(IOS_FAMILY)
+TEST(WKWebViewFindString, DoNotFocusMatchWhenWebViewResigned)
+{
+    ClassMethodSwizzler swizzler([UIKeyboard class], @selector(isInHardwareKeyboardMode), reinterpret_cast<IMP>(returnNo));
+
+    auto inputDelegate = adoptNS([[WKWebViewFindStringInputDelegate alloc] init]);
+    auto findDelegate = adoptNS([[WKWebViewFindStringFindDelegate alloc] init]);
+    auto configuration = adoptNS([[WKWebViewConfiguration alloc] init]);
+    auto firstWebView = adoptNS([[TestWKWebView alloc] initWithFrame:CGRectMake(0, 0, 300, 200) configuration:configuration.get() addToWindow:YES]);
+    [firstWebView synchronouslyLoadHTMLString:@"<input type='text' value='hello'>"];
+    [firstWebView _setInputDelegate:inputDelegate.get()];
+    [firstWebView _setFindDelegate:findDelegate.get()];
+
+    auto secondWebView = adoptNS([[TestWKWebView alloc] initWithFrame:CGRectMake(300, 0, 300, 200) configuration:configuration.get() addToWindow:YES]);
+    EXPECT_TRUE([secondWebView becomeFirstResponder]);
+    EXPECT_FALSE([firstWebView isFirstResponder]);
+
+    [firstWebView _findString:@"hello" options:0 maxCount:maxCount];
+    Util::run(&isDone);
+
+    EXPECT_FALSE([firstWebView isFirstResponder]);
+    EXPECT_WK_STREQ("hello", [findDelegate findString]);
+    EXPECT_FALSE(focusDidStartInputSession);
+}
+#endif
+
+} // namespace TestWebKitAPI
+
+#endif
index 317320b..a91cc6d 100644 (file)
@@ -181,8 +181,8 @@ PlatformWebView::PlatformWebView(WKWebViewConfiguration* configuration, const Te
     m_view = [[TestRunnerWKWebView alloc] initWithFrame:viewRectForWindowRect(rect, WebViewSizingMode::Default) configuration:configuration];
 
     [m_window.rootViewController.view addSubview:m_view];
-    [m_window makeKeyAndVisible];
     [m_view becomeFirstResponder];
+    [m_window makeKeyAndVisible];
 }
 
 PlatformWebView::~PlatformWebView()