Add layout test coverage for <https://webkit.org/b/200215>
authorwenson_hsieh@apple.com <wenson_hsieh@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Tue, 30 Jul 2019 03:44:01 +0000 (03:44 +0000)
committerwenson_hsieh@apple.com <wenson_hsieh@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Tue, 30 Jul 2019 03:44:01 +0000 (03:44 +0000)
https://bugs.webkit.org/show_bug.cgi?id=200245
<rdar://problem/52976965>

Reviewed by Tim Horton.

Tools:

Add new testing infrastructure. See below for more details.

* TestRunnerShared/UIScriptContext/Bindings/UIScriptController.idl:
* TestRunnerShared/UIScriptContext/UIScriptContext.h:
* TestRunnerShared/UIScriptContext/UIScriptController.cpp:
(WTR::UIScriptController::setWillCreateNewPageCallback):
(WTR::UIScriptController::willCreateNewPageCallback const):

Add platform-agnostic UIScriptController callback hooks to notify a test when a new page is being created. The
new test in this patch uses this opportunity to remove and reinsert the web view into the window's hierarchy.

* TestRunnerShared/UIScriptContext/UIScriptController.h:
(WTR::UIScriptController::becomeFirstResponder):

Add and implement a Cocoa platform hook to make the web view first responder.

* WebKitTestRunner/TestController.cpp:
(WTR::TestController::createOtherPage):
* WebKitTestRunner/TestInvocation.cpp:
(WTR::TestInvocation::willCreateNewPage):
* WebKitTestRunner/TestInvocation.h:
* WebKitTestRunner/cocoa/UIScriptControllerCocoa.h:
* WebKitTestRunner/cocoa/UIScriptControllerCocoa.mm:
(WTR::UIScriptControllerCocoa::becomeFirstResponder):

LayoutTests:

Add a new layout test to exercise a scenario where the web view is removed from the view hierarchy, added back
into the view hierarchy, and then made first responder all under the scope of a synchronous autocorrection
context request. See <https://trac.webkit.org/changeset/247914> for more details.

The test here involves two parts: first, we attempt to interact with an input field which, when focused, will
try to open a new window, which then triggers code in the UI process that reinserts the web view in the view
hierarchy. Before r247345, this would result in a crash.

The second part involves tapping an input field on the page. Without the fix in r247914, this would result in a
permanent hang in the UI process, due to the keyboard task queue being unable to dequeue and handle any further
tasks.

* fast/forms/ios/remove-and-add-view-during-focus-expected.txt: Added.
* fast/forms/ios/remove-and-add-view-during-focus.html: Added.

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

13 files changed:
LayoutTests/ChangeLog
LayoutTests/fast/forms/ios/remove-and-add-view-during-focus-expected.txt [new file with mode: 0644]
LayoutTests/fast/forms/ios/remove-and-add-view-during-focus.html [new file with mode: 0644]
Tools/ChangeLog
Tools/TestRunnerShared/UIScriptContext/Bindings/UIScriptController.idl
Tools/TestRunnerShared/UIScriptContext/UIScriptContext.h
Tools/TestRunnerShared/UIScriptContext/UIScriptController.cpp
Tools/TestRunnerShared/UIScriptContext/UIScriptController.h
Tools/WebKitTestRunner/TestController.cpp
Tools/WebKitTestRunner/TestInvocation.cpp
Tools/WebKitTestRunner/TestInvocation.h
Tools/WebKitTestRunner/cocoa/UIScriptControllerCocoa.h
Tools/WebKitTestRunner/cocoa/UIScriptControllerCocoa.mm

index f71b483..c7229b9 100644 (file)
@@ -1,5 +1,28 @@
 2019-07-29  Wenson Hsieh  <wenson_hsieh@apple.com>
 
+        Add layout test coverage for <https://webkit.org/b/200215>
+        https://bugs.webkit.org/show_bug.cgi?id=200245
+        <rdar://problem/52976965>
+
+        Reviewed by Tim Horton.
+
+        Add a new layout test to exercise a scenario where the web view is removed from the view hierarchy, added back
+        into the view hierarchy, and then made first responder all under the scope of a synchronous autocorrection
+        context request. See <https://trac.webkit.org/changeset/247914> for more details.
+
+        The test here involves two parts: first, we attempt to interact with an input field which, when focused, will
+        try to open a new window, which then triggers code in the UI process that reinserts the web view in the view
+        hierarchy. Before r247345, this would result in a crash.
+
+        The second part involves tapping an input field on the page. Without the fix in r247914, this would result in a
+        permanent hang in the UI process, due to the keyboard task queue being unable to dequeue and handle any further
+        tasks.
+
+        * fast/forms/ios/remove-and-add-view-during-focus-expected.txt: Added.
+        * fast/forms/ios/remove-and-add-view-during-focus.html: Added.
+
+2019-07-29  Wenson Hsieh  <wenson_hsieh@apple.com>
+
         YouTube search field shows RTL text outside its border on iPadOS
         https://bugs.webkit.org/show_bug.cgi?id=200253
         <rdar://problem/53680603>
diff --git a/LayoutTests/fast/forms/ios/remove-and-add-view-during-focus-expected.txt b/LayoutTests/fast/forms/ios/remove-and-add-view-during-focus-expected.txt
new file mode 100644 (file)
index 0000000..50b5bbe
--- /dev/null
@@ -0,0 +1,11 @@
+This test verifies that unparenting, reparenting and then making the web view first responder while immediately after focusing an input field does not either cause a crash, or a hang when subsequently attempting to interact with the page. To test manually, tap the top field, dismiss the keyboard, and then tap the bottom field. The test passes if the UI process does not crash or hang.
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+PASS document.activeElement is normalInput
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
+
diff --git a/LayoutTests/fast/forms/ios/remove-and-add-view-during-focus.html b/LayoutTests/fast/forms/ios/remove-and-add-view-during-focus.html
new file mode 100644 (file)
index 0000000..e45565f
--- /dev/null
@@ -0,0 +1,60 @@
+<!DOCTYPE html> <!-- webkit-test-runner [ useFlexibleViewport=true ] -->
+<html>
+<head>
+    <meta name="viewport" content="width=device-width, initial-scale=1">
+    <script src="../../../resources/js-test.js"></script>
+    <script src="../../../resources/ui-helper.js"></script>
+    <style>
+        input {
+            font-size: 20px;
+        }
+    </style>
+</head>
+<body>
+    <div><input id="create-new-page"></input></div>
+    <div><input id="normal-input"></input></div>
+</body>
+<script>
+    jsTestIsAsync = true;
+    createNewPageInput = document.getElementById("create-new-page");
+    normalInput = document.getElementById("normal-input");
+
+    async function activateAndWaitForInputSessionWithNewPageHandler(element)
+    {
+        const x = element.offsetLeft + element.offsetWidth / 2;
+        const y = element.offsetTop + element.offsetHeight / 2;
+        await new Promise(resolve => {
+            testRunner.runUIScript(`
+                (function() {
+                    uiController.willCreateNewPageCallback = () => {
+                        uiController.removeViewFromWindow();
+                        uiController.addViewToWindow();
+                        uiController.becomeFirstResponder();
+                    };
+                    uiController.didShowKeyboardCallback = () => uiController.uiScriptComplete();
+                    uiController.singleTapAtPoint(${x}, ${y}, function() { });
+                })()`, resolve);
+        });
+    }
+
+    createNewPageInput.addEventListener("focus", () => open("about:blank"), { "once" : true });
+
+    addEventListener("load", async () => {
+        description("This test verifies that unparenting, reparenting and then making the web view first responder while immediately after focusing an input field does not either cause a crash, or a hang when subsequently attempting to interact with the page. To test manually, tap the top field, dismiss the keyboard, and then tap the bottom field. The test passes if the UI process does not crash or hang.");
+
+        if (!window.testRunner)
+            return;
+
+        await activateAndWaitForInputSessionWithNewPageHandler(createNewPageInput);
+        createNewPageInput.blur();
+        await UIHelper.waitForKeyboardToHide();
+
+        await UIHelper.activateElementAndWaitForInputSession(normalInput);
+        shouldBe("document.activeElement", "normalInput");
+        normalInput.blur();
+        await UIHelper.waitForKeyboardToHide();
+
+        finishJSTest();
+    });
+</script>
+</html>
\ No newline at end of file
index 5409fe2..f0f75f3 100644 (file)
@@ -1,3 +1,36 @@
+2019-07-29  Wenson Hsieh  <wenson_hsieh@apple.com>
+
+        Add layout test coverage for <https://webkit.org/b/200215>
+        https://bugs.webkit.org/show_bug.cgi?id=200245
+        <rdar://problem/52976965>
+
+        Reviewed by Tim Horton.
+
+        Add new testing infrastructure. See below for more details.
+
+        * TestRunnerShared/UIScriptContext/Bindings/UIScriptController.idl:
+        * TestRunnerShared/UIScriptContext/UIScriptContext.h:
+        * TestRunnerShared/UIScriptContext/UIScriptController.cpp:
+        (WTR::UIScriptController::setWillCreateNewPageCallback):
+        (WTR::UIScriptController::willCreateNewPageCallback const):
+
+        Add platform-agnostic UIScriptController callback hooks to notify a test when a new page is being created. The
+        new test in this patch uses this opportunity to remove and reinsert the web view into the window's hierarchy.
+
+        * TestRunnerShared/UIScriptContext/UIScriptController.h:
+        (WTR::UIScriptController::becomeFirstResponder):
+
+        Add and implement a Cocoa platform hook to make the web view first responder.
+
+        * WebKitTestRunner/TestController.cpp:
+        (WTR::TestController::createOtherPage):
+        * WebKitTestRunner/TestInvocation.cpp:
+        (WTR::TestInvocation::willCreateNewPage):
+        * WebKitTestRunner/TestInvocation.h:
+        * WebKitTestRunner/cocoa/UIScriptControllerCocoa.h:
+        * WebKitTestRunner/cocoa/UIScriptControllerCocoa.mm:
+        (WTR::UIScriptControllerCocoa::becomeFirstResponder):
+
 2019-07-29  Fujii Hironori  <Hironori.Fujii@sony.com>
 
         [Win][MiniBrowser] Add 'reload' menu item and 'reload' toolbar button
index 2144cf7..d90efa6 100644 (file)
@@ -240,12 +240,15 @@ interface UIScriptController {
     attribute object willBeginZoomingCallback;
     attribute object didEndZoomingCallback;
 
+    attribute object willCreateNewPageCallback;
+
     void zoomToScale(double scale, object callback);
 
     void setViewScale(double scale);
     void setMinimumEffectiveWidth(double effectiveWidth);
     void setAllowsViewportShrinkToFit(boolean allows);
 
+    void becomeFirstResponder();
     void resignFirstResponder();
     readonly attribute boolean isPresentingModally;
 
index 47ffe8f..4e020f1 100644 (file)
@@ -63,6 +63,7 @@ typedef enum  {
     CallbackTypeDidEndFormControlInteraction,
     CallbackTypeDidShowForcePressPreview,
     CallbackTypeDidDismissForcePressPreview,
+    CallbackTypeWillCreateNewPage,
     CallbackTypeNonPersistent = firstNonPersistentCallbackID
 } CallbackType;
 
index f896604..f5b715e 100644 (file)
@@ -145,6 +145,16 @@ JSValueRef UIScriptController::didEndZoomingCallback() const
     return m_context->callbackWithID(CallbackTypeDidEndZooming);
 }
 
+void UIScriptController::setWillCreateNewPageCallback(JSValueRef callback)
+{
+    m_context->registerCallback(callback, CallbackTypeWillCreateNewPage);
+}
+
+JSValueRef UIScriptController::willCreateNewPageCallback() const
+{
+    return m_context->callbackWithID(CallbackTypeWillCreateNewPage);
+}
+
 void UIScriptController::setDidEndScrollingCallback(JSValueRef callback)
 {
     m_context->registerCallback(callback, CallbackTypeDidEndScrolling);
index c6990c3..7349c8e 100644 (file)
@@ -102,6 +102,7 @@ public:
 
     // View Parenting and Visibility
 
+    virtual void becomeFirstResponder() { notImplemented(); }
     virtual void resignFirstResponder() { notImplemented(); }
 
     virtual void firstResponderSuppressionForWebView(bool) { notImplemented(); }
@@ -270,6 +271,9 @@ public:
     virtual void setDidEndZoomingCallback(JSValueRef);
     JSValueRef didEndZoomingCallback() const;
 
+    virtual void setWillCreateNewPageCallback(JSValueRef);
+    JSValueRef willCreateNewPageCallback() const;
+
     virtual void setDidShowKeyboardCallback(JSValueRef);
     JSValueRef didShowKeyboardCallback() const;
 
index 880eaa3..663fac7 100644 (file)
@@ -300,6 +300,8 @@ WKPageRef TestController::createOtherPage(WKPageRef, WKPageConfigurationRef conf
 
 WKPageRef TestController::createOtherPage(PlatformWebView* parentView, WKPageConfigurationRef configuration, WKNavigationActionRef navigationAction, WKWindowFeaturesRef windowFeatures)
 {
+    m_currentInvocation->willCreateNewPage();
+
     // The test needs to call testRunner.setCanOpenWindows() to open new windows.
     if (!m_currentInvocation->canOpenWindows())
         return nullptr;
index c4d77b8..a7b77ca 100644 (file)
@@ -1860,4 +1860,10 @@ void TestInvocation::done()
     });
 }
 
+void TestInvocation::willCreateNewPage()
+{
+    if (m_UIScriptContext && m_UIScriptContext->callbackWithID(CallbackTypeWillCreateNewPage))
+        m_UIScriptContext->fireCallback(CallbackTypeWillCreateNewPage);
+}
+
 } // namespace WTR
index c1a8b64..7880dd8 100644 (file)
@@ -91,6 +91,8 @@ public:
     void dumpAdClickAttribution();
     void performCustomMenuAction();
 
+    void willCreateNewPage();
+
 private:
     WKRetainPtr<WKMutableDictionaryRef> createTestSettingsDictionary();
 
index 1c077b5..dd57dc0 100644 (file)
@@ -35,6 +35,7 @@ class UIScriptControllerCocoa : public UIScriptController {
 public:
     void setViewScale(double) override;
     void setMinimumEffectiveWidth(double) override;
+    void becomeFirstResponder() override;
     void resignFirstResponder() override;
     void doAsyncTask(JSValueRef) override;
     void setShareSheetCompletesImmediatelyWithResolution(bool) override;
index 1d4174d..dcefa76 100644 (file)
@@ -56,6 +56,11 @@ void UIScriptControllerCocoa::setMinimumEffectiveWidth(double effectiveWidth)
     webView()._minimumEffectiveDeviceWidth = effectiveWidth;
 }
 
+void UIScriptControllerCocoa::becomeFirstResponder()
+{
+    [webView() becomeFirstResponder];
+}
+
 void UIScriptControllerCocoa::resignFirstResponder()
 {
     [webView() resignFirstResponder];