Title: [240301] trunk/Tools
Revision
240301
Author
wenson_hs...@apple.com
Date
2019-01-22 15:21:45 -0800 (Tue, 22 Jan 2019)

Log Message

[iOS] Multiple WKWebViewAutofillTests are flaky failures
https://bugs.webkit.org/show_bug.cgi?id=189165
<rdar://problem/47433765>

Reviewed by Tim Horton.

These tests are currently flaky because they expect an invocation of "Element.blur()" in the web process to
immediately dispatch an IPC message to notify the UI process that the element has been blurred. In particular,
the -textInputHasAutofillContext helper assumes that waiting for the next remote layer tree commit in the UI
process in sufficient to ensure that any previous action that blurred the focused element in the web process
would make its way to the UI process by the time the layer tree commit is finished.

However, WebPage::elementDidBlur sends its IPC message to the UI process asynchronously, using callOnMainThread.
This means that if a layer tree flush was already scheduled in the web process before the element was blurred,
the element blur IPC message to the UI process will lose the race against the layer tree commit, and the test
will fail because it asks for -_autofillContext too early.

To fix this, we tweak these tests to actually wait until the intended input session change triggered by script
is handled in the UI process.

* TestWebKitAPI/Tests/ios/WKWebViewAutofillTests.mm:

Tweak some of these tests to wait for input session changes before checking for the presence of an autofill
context. The only exception is an existing test that doesn't allow programmatic focus to begin input sessions
by default; to fix this test, we simply wait for _WKInputDelegate to be invoked, instead of waiting for a new
input session.

(-[AutofillTestView textInputHasAutofillContext]):

Remove the incorrect presentation update here. This helper now assumes that the UI process is up to date.

* TestWebKitAPI/cocoa/TestWKWebView.h:
* TestWebKitAPI/cocoa/TestWKWebView.mm:
(nextInputSessionChangeCount):

Monotonically increasing identifier that's incremented whenever an input session is started in the UI process.
This includes changing the focused element from one to another.

(-[TestWKWebView initWithFrame:configuration:addToWindow:]):
(-[TestWKWebView didStartFormControlInteraction]):
(-[TestWKWebView didEndFormControlInteraction]):
(-[TestWKWebView evaluateJavaScriptAndWaitForInputSessionToChange:]):

Add a helper to evaluate _javascript_ and wait for this script to cause some change in the input session. This
handles three cases: (1) changing focus from an element that doesn't require an input session to one that does,
(2) changing focus between elements that require input sessions, and (3) changing focus from an input session
that doesn't require an input session to one that doesn't.

Modified Paths

Diff

Modified: trunk/Tools/ChangeLog (240300 => 240301)


--- trunk/Tools/ChangeLog	2019-01-22 23:08:36 UTC (rev 240300)
+++ trunk/Tools/ChangeLog	2019-01-22 23:21:45 UTC (rev 240301)
@@ -1,3 +1,53 @@
+2019-01-22  Wenson Hsieh  <wenson_hs...@apple.com>
+
+        [iOS] Multiple WKWebViewAutofillTests are flaky failures
+        https://bugs.webkit.org/show_bug.cgi?id=189165
+        <rdar://problem/47433765>
+
+        Reviewed by Tim Horton.
+
+        These tests are currently flaky because they expect an invocation of "Element.blur()" in the web process to
+        immediately dispatch an IPC message to notify the UI process that the element has been blurred. In particular,
+        the -textInputHasAutofillContext helper assumes that waiting for the next remote layer tree commit in the UI
+        process in sufficient to ensure that any previous action that blurred the focused element in the web process
+        would make its way to the UI process by the time the layer tree commit is finished.
+
+        However, WebPage::elementDidBlur sends its IPC message to the UI process asynchronously, using callOnMainThread.
+        This means that if a layer tree flush was already scheduled in the web process before the element was blurred,
+        the element blur IPC message to the UI process will lose the race against the layer tree commit, and the test
+        will fail because it asks for -_autofillContext too early.
+
+        To fix this, we tweak these tests to actually wait until the intended input session change triggered by script
+        is handled in the UI process.
+
+        * TestWebKitAPI/Tests/ios/WKWebViewAutofillTests.mm:
+
+        Tweak some of these tests to wait for input session changes before checking for the presence of an autofill
+        context. The only exception is an existing test that doesn't allow programmatic focus to begin input sessions
+        by default; to fix this test, we simply wait for _WKInputDelegate to be invoked, instead of waiting for a new
+        input session.
+
+        (-[AutofillTestView textInputHasAutofillContext]):
+
+        Remove the incorrect presentation update here. This helper now assumes that the UI process is up to date.
+
+        * TestWebKitAPI/cocoa/TestWKWebView.h:
+        * TestWebKitAPI/cocoa/TestWKWebView.mm:
+        (nextInputSessionChangeCount):
+
+        Monotonically increasing identifier that's incremented whenever an input session is started in the UI process.
+        This includes changing the focused element from one to another.
+
+        (-[TestWKWebView initWithFrame:configuration:addToWindow:]):
+        (-[TestWKWebView didStartFormControlInteraction]):
+        (-[TestWKWebView didEndFormControlInteraction]):
+        (-[TestWKWebView evaluateJavaScriptAndWaitForInputSessionToChange:]):
+
+        Add a helper to evaluate _javascript_ and wait for this script to cause some change in the input session. This
+        handles three cases: (1) changing focus from an element that doesn't require an input session to one that does,
+        (2) changing focus between elements that require input sessions, and (3) changing focus from an input session
+        that doesn't require an input session to one that doesn't.
+
 2019-01-22  David Kilzer  <ddkil...@apple.com>
 
         check-webkit-style reports false-positive whitespace/init warning in C++ initialization parameters

Modified: trunk/Tools/TestWebKitAPI/Tests/ios/WKWebViewAutofillTests.mm (240300 => 240301)


--- trunk/Tools/TestWebKitAPI/Tests/ios/WKWebViewAutofillTests.mm	2019-01-22 23:08:36 UTC (rev 240300)
+++ trunk/Tools/TestWebKitAPI/Tests/ios/WKWebViewAutofillTests.mm	2019-01-22 23:21:45 UTC (rev 240301)
@@ -64,7 +64,6 @@
 
 - (BOOL)textInputHasAutofillContext
 {
-    [self waitForNextPresentationUpdate];
     NSURL *url = "" objectForKey:@"_WebViewURL"];
     if (![url isKindOfClass:[NSURL class]])
         return NO;
@@ -81,10 +80,10 @@
 {
     auto webView = adoptNS([[AutofillTestView alloc] initWithFrame:CGRectMake(0, 0, 320, 500)]);
     [webView synchronouslyLoadHTMLString:@"<input id='user' type='email'><input id='password' type='password'>"];
-    [webView stringByEvaluatingJavaScript:@"user.focus()"];
+    [webView evaluateJavaScriptAndWaitForInputSessionToChange:@"user.focus()"];
     EXPECT_TRUE([webView textInputHasAutofillContext]);
 
-    [webView stringByEvaluatingJavaScript:@"password.focus()"];
+    [webView evaluateJavaScriptAndWaitForInputSessionToChange:@"password.focus()"];
     EXPECT_TRUE([webView textInputHasAutofillContext]);
 
     auto credentialSuggestion = [UITextAutofillSuggestion autofillSuggestionWithUsername:@"frederik" password:@"famos"];
@@ -92,7 +91,7 @@
 
     EXPECT_WK_STREQ("famos", [webView stringByEvaluatingJavaScript:@"password.value"]);
 
-    [webView stringByEvaluatingJavaScript:@"document.activeElement.blur()"];
+    [webView evaluateJavaScriptAndWaitForInputSessionToChange:@"document.activeElement.blur()"];
     EXPECT_FALSE([webView textInputHasAutofillContext]);
 }
 
@@ -100,10 +99,10 @@
 {
     auto webView = adoptNS([[AutofillTestView alloc] initWithFrame:CGRectMake(0, 0, 320, 500)]);
     [webView synchronouslyLoadHTMLString:@"<input id='user' type='email'><input type='radio' name='radio_button' value='radio'><input id='password' type='password'>"];
-    [webView stringByEvaluatingJavaScript:@"user.focus()"];
+    [webView evaluateJavaScriptAndWaitForInputSessionToChange:@"user.focus()"];
     EXPECT_TRUE([webView textInputHasAutofillContext]);
 
-    [webView stringByEvaluatingJavaScript:@"password.focus()"];
+    [webView evaluateJavaScriptAndWaitForInputSessionToChange:@"password.focus()"];
     EXPECT_TRUE([webView textInputHasAutofillContext]);
 
     auto credentialSuggestion = [UITextAutofillSuggestion autofillSuggestionWithUsername:@"frederik" password:@"famos"];
@@ -112,7 +111,7 @@
     EXPECT_WK_STREQ("frederik", [webView stringByEvaluatingJavaScript:@"user.value"]);
     EXPECT_WK_STREQ("famos", [webView stringByEvaluatingJavaScript:@"password.value"]);
 
-    [webView stringByEvaluatingJavaScript:@"document.activeElement.blur()"];
+    [webView evaluateJavaScriptAndWaitForInputSessionToChange:@"document.activeElement.blur()"];
     EXPECT_FALSE([webView textInputHasAutofillContext]);
 }
 
@@ -120,10 +119,10 @@
 {
     auto webView = adoptNS([[AutofillTestView alloc] initWithFrame:CGRectMake(0, 0, 320, 500)]);
     [webView synchronouslyLoadHTMLString:@"<input id='text1' type='email'><input id='text2' type='text'>"];
-    [webView stringByEvaluatingJavaScript:@"text1.focus()"];
+    [webView evaluateJavaScriptAndWaitForInputSessionToChange:@"text1.focus()"];
     EXPECT_FALSE([webView textInputHasAutofillContext]);
 
-    [webView stringByEvaluatingJavaScript:@"text2.focus()"];
+    [webView evaluateJavaScriptAndWaitForInputSessionToChange:@"text2.focus()"];
     EXPECT_FALSE([webView textInputHasAutofillContext]);
 }
 
@@ -131,7 +130,7 @@
 {
     auto webView = adoptNS([[AutofillTestView alloc] initWithFrame:CGRectMake(0, 0, 320, 500)]);
     [webView synchronouslyLoadHTMLString:@"<input id='password' type='password'>"];
-    [webView stringByEvaluatingJavaScript:@"password.focus()"];
+    [webView evaluateJavaScriptAndWaitForInputSessionToChange:@"password.focus()"];
     EXPECT_TRUE([webView textInputHasAutofillContext]);
 
     auto credentialSuggestion = [UITextAutofillSuggestion autofillSuggestionWithUsername:@"frederik" password:@"famos"];
@@ -139,7 +138,7 @@
 
     EXPECT_WK_STREQ("famos", [webView stringByEvaluatingJavaScript:@"password.value"]);
 
-    [webView stringByEvaluatingJavaScript:@"document.activeElement.blur()"];
+    [webView evaluateJavaScriptAndWaitForInputSessionToChange:@"document.activeElement.blur()"];
     EXPECT_FALSE([webView textInputHasAutofillContext]);
 }
 
@@ -147,7 +146,7 @@
 {
     auto webView = adoptNS([[AutofillTestView alloc] initWithFrame:CGRectMake(0, 0, 320, 500)]);
     [webView synchronouslyLoadHTMLString:@"<input id='textfield' type='text'>"];
-    [webView stringByEvaluatingJavaScript:@"textfield.focus()"];
+    [webView evaluateJavaScriptAndWaitForInputSessionToChange:@"textfield.focus()"];
     EXPECT_FALSE([webView textInputHasAutofillContext]);
 }
 
@@ -155,13 +154,13 @@
 {
     auto webView = adoptNS([[AutofillTestView alloc] initWithFrame:CGRectMake(0, 0, 320, 500)]);
     [webView synchronouslyLoadHTMLString:@"<input id='user' type='email'><input id='password' type='password'><input id='confirm_password' type='password'>"];
-    [webView stringByEvaluatingJavaScript:@"user.focus()"];
+    [webView evaluateJavaScriptAndWaitForInputSessionToChange:@"user.focus()"];
     EXPECT_FALSE([webView textInputHasAutofillContext]);
 
-    [webView stringByEvaluatingJavaScript:@"password.focus()"];
+    [webView evaluateJavaScriptAndWaitForInputSessionToChange:@"password.focus()"];
     EXPECT_FALSE([webView textInputHasAutofillContext]);
 
-    [webView stringByEvaluatingJavaScript:@"confirm_password.focus()"];
+    [webView evaluateJavaScriptAndWaitForInputSessionToChange:@"confirm_password.focus()"];
     EXPECT_FALSE([webView textInputHasAutofillContext]);
 }
 
@@ -174,12 +173,15 @@
 {
     ClassMethodSwizzler swizzler([UIKeyboard class], @selector(isInHardwareKeyboardMode), reinterpret_cast<IMP>(overrideIsInHardwareKeyboardMode));
 
+    bool done = false;
     auto webView = adoptNS([[AutofillTestView alloc] initWithFrame:CGRectMake(0, 0, 320, 500)]);
-    [(TestInputDelegate *)[webView _inputDelegate] setFocusStartsInputSessionPolicyHandler:[] (WKWebView *, id <_WKFocusedElementInfo>) -> _WKFocusStartsInputSessionPolicy {
+    [(TestInputDelegate *)[webView _inputDelegate] setFocusStartsInputSessionPolicyHandler:[&done] (WKWebView *, id <_WKFocusedElementInfo>) -> _WKFocusStartsInputSessionPolicy {
+        done = true;
         return _WKFocusStartsInputSessionPolicyAuto;
     }];
     [webView synchronouslyLoadHTMLString:@"<input id='user' type='email'><input id='password' type='password'>"];
     [webView stringByEvaluatingJavaScript:@"user.focus()"];
+    Util::run(&done);
 
     EXPECT_FALSE([webView textInputHasAutofillContext]);
 }

Modified: trunk/Tools/TestWebKitAPI/cocoa/TestWKWebView.h (240300 => 240301)


--- trunk/Tools/TestWebKitAPI/cocoa/TestWKWebView.h	2019-01-22 23:08:36 UTC (rev 240300)
+++ trunk/Tools/TestWebKitAPI/cocoa/TestWKWebView.h	2019-01-22 23:21:45 UTC (rev 240301)
@@ -87,6 +87,7 @@
 @property (nonatomic, readonly) CGRect caretViewRectInContentCoordinates;
 @property (nonatomic, readonly) NSArray<NSValue *> *selectionViewRectsInContentCoordinates;
 - (_WKActivatedElementInfo *)activatedElementAtPosition:(CGPoint)position;
+- (void)evaluateJavaScriptAndWaitForInputSessionToChange:(NSString *)script;
 @end
 #endif
 

Modified: trunk/Tools/TestWebKitAPI/cocoa/TestWKWebView.mm (240300 => 240301)


--- trunk/Tools/TestWebKitAPI/cocoa/TestWKWebView.mm	2019-01-22 23:08:36 UTC (rev 240300)
+++ trunk/Tools/TestWebKitAPI/cocoa/TestWKWebView.mm	2019-01-22 23:21:45 UTC (rev 240301)
@@ -249,11 +249,23 @@
 
 @end
 
+#if PLATFORM(IOS_FAMILY)
+
+using InputSessionChangeCount = NSUInteger;
+static InputSessionChangeCount nextInputSessionChangeCount()
+{
+    static InputSessionChangeCount gInputSessionChangeCount = 0;
+    return ++gInputSessionChangeCount;
+}
+
+#endif
+
 @implementation TestWKWebView {
     RetainPtr<TestWKWebViewHostWindow> _hostWindow;
     RetainPtr<TestMessageHandler> _testHandler;
 #if PLATFORM(IOS_FAMILY)
     std::unique_ptr<ClassMethodSwizzler> _sharedCalloutBarSwizzler;
+    InputSessionChangeCount _inputSessionChangeCount;
 #endif
 }
 
@@ -289,6 +301,7 @@
 #if PLATFORM(IOS_FAMILY)
     // FIXME: Remove this workaround once <https://webkit.org/b/175204> is fixed.
     _sharedCalloutBarSwizzler = std::make_unique<ClassMethodSwizzler>([UICalloutBar class], @selector(sharedCalloutBar), reinterpret_cast<IMP>(suppressUICalloutBar));
+    _inputSessionChangeCount = 0;
 #endif
 
     return self;
@@ -409,6 +422,20 @@
     [self evaluateJavaScript:@"getSelection().collapseToEnd()" completionHandler:nil];
 }
 
+#if PLATFORM(IOS_FAMILY)
+
+- (void)didStartFormControlInteraction
+{
+    _inputSessionChangeCount = nextInputSessionChangeCount();
+}
+
+- (void)didEndFormControlInteraction
+{
+    _inputSessionChangeCount = 0;
+}
+
+#endif // PLATFORM(IOS_FAMILY)
+
 @end
 
 #if PLATFORM(IOS_FAMILY)
@@ -415,6 +442,26 @@
 
 @implementation TestWKWebView (IOSOnly)
 
+- (void)evaluateJavaScriptAndWaitForInputSessionToChange:(NSString *)script
+{
+    auto initialChangeCount = _inputSessionChangeCount;
+    BOOL hasEmittedWarning = NO;
+    NSTimeInterval secondsToWaitUntilWarning = 2;
+    NSTimeInterval startTime = [NSDate timeIntervalSinceReferenceDate];
+
+    [self objectByEvaluatingJavaScript:script];
+    while ([[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantPast]]) {
+        if (_inputSessionChangeCount != initialChangeCount)
+            break;
+
+        if (hasEmittedWarning || startTime + secondsToWaitUntilWarning >= [NSDate timeIntervalSinceReferenceDate])
+            continue;
+
+        NSLog(@"Warning: expecting input session change count to differ from %tu", initialChangeCount);
+        hasEmittedWarning = YES;
+    }
+}
+
 - (UIView <UITextInputPrivate, UITextInputMultiDocument> *)textInputContentView
 {
     return (UIView <UITextInputPrivate, UITextInputMultiDocument> *)[self valueForKey:@"_currentContentView"];
_______________________________________________
webkit-changes mailing list
webkit-changes@lists.webkit.org
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to