WebDriver: handle user prompts shown while executing scripts
authorcarlosgc@webkit.org <carlosgc@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Sat, 2 Dec 2017 15:13:29 +0000 (15:13 +0000)
committercarlosgc@webkit.org <carlosgc@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Sat, 2 Dec 2017 15:13:29 +0000 (15:13 +0000)
https://bugs.webkit.org/show_bug.cgi?id=179979

Reviewed by Brian Burg.

Source/WebDriver:

15.2 Executing Script
https://w3c.github.io/webdriver/webdriver-spec.html#executing-script

The rules to execute a function body are as follows. The algorithm will return success with the JSON
representation of the function’s return value, or an error if the evaluation of the function results in a
JavaScript exception being thrown or at any point during its execution an unhandled user prompt appears.

If at any point during the algorithm a user prompt appears, the user prompt handler must be invoked. If its
return value is an error, it must immediately return with that error and abort all subsequent substeps of this
algorithm.

This will be covered by new WPT tests that will be available after the next upgrade.

* CommandResult.cpp:
(WebDriver::CommandResult::CommandResult): Handle UnexpectedAlertOpen internal error.
* Session.cpp:
(WebDriver::Session::handleUserPrompts): Move code to handleUnexpectedAlertOpen() and call it instead.
(WebDriver::Session::handleUnexpectedAlertOpen): Code moved here to be used also by executeScript().
(WebDriver::Session::executeScript): In case of UnexpectedAlertOpen error, call handleUnexpectedAlertOpen().
* Session.h:

Source/WebKit:

* UIProcess/Automation/Automation.json: Add UnexpectedAlertOpen error.
* UIProcess/Automation/WebAutomationSession.cpp:
(WebKit::WebAutomationSession::willShowJavaScriptDialog): Finish pending evaluateJavaScriptFunction operations
with UnexpectedAlertOpen error.

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

Source/WebDriver/ChangeLog
Source/WebDriver/CommandResult.cpp
Source/WebDriver/Session.cpp
Source/WebDriver/Session.h
Source/WebKit/ChangeLog
Source/WebKit/UIProcess/Automation/Automation.json
Source/WebKit/UIProcess/Automation/WebAutomationSession.cpp

index 5b09f8d..e7cd759 100644 (file)
@@ -1,3 +1,31 @@
+2017-12-02  Carlos Garcia Campos  <cgarcia@igalia.com>
+
+        WebDriver: handle user prompts shown while executing scripts
+        https://bugs.webkit.org/show_bug.cgi?id=179979
+
+        Reviewed by Brian Burg.
+
+        15.2 Executing Script
+        https://w3c.github.io/webdriver/webdriver-spec.html#executing-script
+
+        The rules to execute a function body are as follows. The algorithm will return success with the JSON
+        representation of the function’s return value, or an error if the evaluation of the function results in a
+        JavaScript exception being thrown or at any point during its execution an unhandled user prompt appears.
+
+        If at any point during the algorithm a user prompt appears, the user prompt handler must be invoked. If its
+        return value is an error, it must immediately return with that error and abort all subsequent substeps of this
+        algorithm.
+
+        This will be covered by new WPT tests that will be available after the next upgrade.
+
+        * CommandResult.cpp:
+        (WebDriver::CommandResult::CommandResult): Handle UnexpectedAlertOpen internal error.
+        * Session.cpp:
+        (WebDriver::Session::handleUserPrompts): Move code to handleUnexpectedAlertOpen() and call it instead.
+        (WebDriver::Session::handleUnexpectedAlertOpen): Code moved here to be used also by executeScript().
+        (WebDriver::Session::executeScript): In case of UnexpectedAlertOpen error, call handleUnexpectedAlertOpen().
+        * Session.h:
+
 2017-12-01  Carlos Garcia Campos  <cgarcia@igalia.com>
 
         WebDriver: implement status command
index 91b35da..fa8fa88 100644 (file)
@@ -106,6 +106,8 @@ CommandResult::CommandResult(RefPtr<JSON::Value>&& result, std::optional<ErrorCo
             m_errorCode = ErrorCode::ElementNotSelectable;
         else if (errorName == "ScreenshotError")
             m_errorCode = ErrorCode::UnableToCaptureScreen;
+        else if (errorName == "UnexpectedAlertOpen")
+            m_errorCode = ErrorCode::UnexpectedAlertOpen;
 
         break;
     }
index b4d0452..2f6d5ee 100644 (file)
@@ -184,31 +184,36 @@ void Session::handleUserPrompts(Function<void (CommandResult&&)>&& completionHan
             return;
         }
 
-        if (!capabilities().unhandledPromptBehavior) {
-            reportUnexpectedAlertOpen([this, completionHandler = WTFMove(completionHandler)](CommandResult&& result) mutable {
-                dismissAlert([this, errorResult = WTFMove(result), completionHandler = WTFMove(completionHandler)](CommandResult&& result) mutable {
-                    if (result.isError()) {
-                        completionHandler(WTFMove(result));
-                        return;
-                    }
-                    completionHandler(WTFMove(errorResult));
-                });
+        handleUnexpectedAlertOpen(WTFMove(completionHandler));
+    });
+}
+
+void Session::handleUnexpectedAlertOpen(Function<void (CommandResult&&)>&& completionHandler)
+{
+    if (!capabilities().unhandledPromptBehavior) {
+        reportUnexpectedAlertOpen([this, completionHandler = WTFMove(completionHandler)](CommandResult&& result) mutable {
+            dismissAlert([this, errorResult = WTFMove(result), completionHandler = WTFMove(completionHandler)](CommandResult&& result) mutable {
+                if (result.isError()) {
+                    completionHandler(WTFMove(result));
+                    return;
+                }
+                completionHandler(WTFMove(errorResult));
             });
-            return;
-        }
+        });
+        return;
+    }
 
-        switch (capabilities().unhandledPromptBehavior.value()) {
-        case UnhandledPromptBehavior::Dismiss:
-            dismissAlert(WTFMove(completionHandler));
-            break;
-        case UnhandledPromptBehavior::Accept:
-            acceptAlert(WTFMove(completionHandler));
-            break;
-        case UnhandledPromptBehavior::Ignore:
-            reportUnexpectedAlertOpen(WTFMove(completionHandler));
-            break;
-        }
-    });
+    switch (capabilities().unhandledPromptBehavior.value()) {
+    case UnhandledPromptBehavior::Dismiss:
+        dismissAlert(WTFMove(completionHandler));
+        break;
+    case UnhandledPromptBehavior::Accept:
+        acceptAlert(WTFMove(completionHandler));
+        break;
+    case UnhandledPromptBehavior::Ignore:
+        reportUnexpectedAlertOpen(WTFMove(completionHandler));
+        break;
+    }
 }
 
 void Session::reportUnexpectedAlertOpen(Function<void (CommandResult&&)>&& completionHandler)
@@ -1663,9 +1668,13 @@ void Session::executeScript(const String& script, RefPtr<JSON::Array>&& argument
             if (m_timeouts.script)
                 parameters->setInteger(ASCIILiteral("callbackTimeout"), m_timeouts.script.value().millisecondsAs<int>());
         }
-        m_host->sendCommandToBackend(ASCIILiteral("evaluateJavaScriptFunction"), WTFMove(parameters), [this, protectedThis = makeRef(*this), completionHandler = WTFMove(completionHandler)](SessionHost::CommandResponse&& response) {
+        m_host->sendCommandToBackend(ASCIILiteral("evaluateJavaScriptFunction"), WTFMove(parameters), [this, protectedThis = makeRef(*this), completionHandler = WTFMove(completionHandler)](SessionHost::CommandResponse&& response) mutable {
             if (response.isError || !response.responseObject) {
-                completionHandler(CommandResult::fail(WTFMove(response.responseObject)));
+                auto result = CommandResult::fail(WTFMove(response.responseObject));
+                if (result.errorCode() == CommandResult::ErrorCode::UnexpectedAlertOpen)
+                    handleUnexpectedAlertOpen(WTFMove(completionHandler));
+                else
+                    completionHandler(WTFMove(result));
                 return;
             }
             String valueString;
index ac6e741..373113d 100644 (file)
@@ -122,6 +122,7 @@ private:
     std::optional<String> pageLoadStrategyString() const;
 
     void handleUserPrompts(Function<void (CommandResult&&)>&&);
+    void handleUnexpectedAlertOpen(Function<void (CommandResult&&)>&&);
     void reportUnexpectedAlertOpen(Function<void (CommandResult&&)>&&);
 
     RefPtr<JSON::Object> createElement(RefPtr<JSON::Value>&&);
index 17f9145..2cf840d 100644 (file)
@@ -1,3 +1,15 @@
+2017-12-02  Carlos Garcia Campos  <cgarcia@igalia.com>
+
+        WebDriver: handle user prompts shown while executing scripts
+        https://bugs.webkit.org/show_bug.cgi?id=179979
+
+        Reviewed by Brian Burg.
+
+        * UIProcess/Automation/Automation.json: Add UnexpectedAlertOpen error.
+        * UIProcess/Automation/WebAutomationSession.cpp:
+        (WebKit::WebAutomationSession::willShowJavaScriptDialog): Finish pending evaluateJavaScriptFunction operations
+        with UnexpectedAlertOpen error.
+
 2017-12-02  Wenson Hsieh  <wenson_hsieh@apple.com>
 
         Make some minor adjustments to TouchBarMenuData and TouchBarMenuItemData
index 63b0c69..9467ef3 100644 (file)
@@ -71,7 +71,8 @@
                 "InvalidSelector",
                 "ElementNotInteractable",
                 "ElementNotSelectable",
-                "ScreenshotError"
+                "ScreenshotError",
+                "UnexpectedAlertOpen"
             ]
         },
         {
index 57b3b64..896168e 100644 (file)
@@ -551,14 +551,26 @@ void WebAutomationSession::loadTimerFired()
 void WebAutomationSession::willShowJavaScriptDialog(WebPageProxy& page)
 {
     // Wait until the next run loop iteration to give time for the client to show the dialog,
-    // then check if page is loading and the dialog is still present. The dialog will block the
-    // load in case of normal strategy, so we want to dispatch all pending navigation callbacks.
+    // then check if the dialog is still present. If the page is loading, the dialog will block
+    // the load in case of normal strategy, so we want to dispatch all pending navigation callbacks.
+    // If the dialog was shown during a script execution, we want to finish the evaluateJavaScriptFunction
+    // operation with an unexpected alert open error.
     RunLoop::main().dispatch([this, protectedThis = makeRef(*this), page = makeRef(page)] {
-        if (!page->isValid() || !page->pageLoadState().isLoading() || !m_client || !m_client->isShowingJavaScriptDialogOnPage(*this, page))
+        if (!page->isValid() || !m_client || !m_client->isShowingJavaScriptDialogOnPage(*this, page))
             return;
 
-        respondToPendingFrameNavigationCallbacksWithTimeout(m_pendingNormalNavigationInBrowsingContextCallbacksPerFrame);
-        respondToPendingPageNavigationCallbacksWithTimeout(m_pendingNormalNavigationInBrowsingContextCallbacksPerPage);
+        if (page->pageLoadState().isLoading()) {
+            respondToPendingFrameNavigationCallbacksWithTimeout(m_pendingNormalNavigationInBrowsingContextCallbacksPerFrame);
+            respondToPendingPageNavigationCallbacksWithTimeout(m_pendingNormalNavigationInBrowsingContextCallbacksPerPage);
+        }
+
+        if (!m_evaluateJavaScriptFunctionCallbacks.isEmpty()) {
+            Inspector::ErrorString unexpectedAlertOpenError = STRING_FOR_PREDEFINED_ERROR_NAME(UnexpectedAlertOpen);
+            for (auto key : m_evaluateJavaScriptFunctionCallbacks.keys()) {
+                auto callback = m_evaluateJavaScriptFunctionCallbacks.take(key);
+                callback->sendFailure(unexpectedAlertOpenError);
+            }
+        }
     });
 }