WebDriver: wait until navigation is complete before running new commands and after...
authorcarlosgc@webkit.org <carlosgc@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Fri, 21 Jul 2017 09:50:49 +0000 (09:50 +0000)
committercarlosgc@webkit.org <carlosgc@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Fri, 21 Jul 2017 09:50:49 +0000 (09:50 +0000)
https://bugs.webkit.org/show_bug.cgi?id=174670

Reviewed by Brian Burg.

Source/WebDriver:

We are already waiting for navigation to complete after navigation commands (go, back, forward, refresh), but
the spec says we should always wait before executing a new command and also after a click. This is causing test
testShouldBeAbleToNavigateBackInTheBrowserHistoryInPresenceOfIframes to sometimes fail, because it does .click()
+ .title and expects the title to tbe the one of the page loaded by the click. Since the load happens very fast,
the test usually passes, but in a real case with a slower load, the title of the previous page will be returned
most of the times.

6.3 Processing Model. Step 7. Wait for navigation to complete. If this returns an error return its value and
jump to step 1 in this overall algorithm, otherwise continue.
https://www.w3.org/TR/webdriver/#processing-model

14.1 Element Click. Step 10. If the click causes navigation: 1. Run the post-navigation checks and return its
value if it is an error. 2. Try to wait for navigation to complete.
https://www.w3.org/TR/webdriver/#element-click

* Session.cpp:
(WebDriver::Session::waitForNavigationToComplete): Send waitForNavigationToComplete message to the browser to
wait for any pending navigation of current browsing context to complete.
(WebDriver::Session::elementClick): Call waitForNavigationToComplete() after the click.
* Session.h:
* WebDriverService.cpp:
(WebDriver::WebDriverService::go): Wait for navigations to complete before running the command.
(WebDriver::WebDriverService::getCurrentURL): Ditto.
(WebDriver::WebDriverService::back): Ditto.
(WebDriver::WebDriverService::forward): Ditto.
(WebDriver::WebDriverService::refresh): Ditto.
(WebDriver::WebDriverService::getTitle): Ditto.
(WebDriver::WebDriverService::switchToFrame): Ditto.
(WebDriver::WebDriverService::switchToParentFrame): Ditto.
(WebDriver::WebDriverService::findElement): Ditto.
(WebDriver::WebDriverService::findElements): Ditto.
(WebDriver::WebDriverService::executeScript): Ditto.
(WebDriver::WebDriverService::executeAsyncScript): Ditto.

Source/WebKit:

Add waitForNavigationToComplete method to Automation to allow WebDriver to wait for pending navigations to
complete. The new method already receives page load strategy and timeout, but they are not implemented yet.

* UIProcess/Automation/Automation.json: Add waitForNavigationToComplete method and PageLoadStrategy new type.
* UIProcess/Automation/WebAutomationSession.cpp:
(WebKit::WebAutomationSession::waitForNavigationToComplete): Call waitForNavigationToCompleteOnPage or
waitForNavigationToCompleteOnFrame depending on whether the current borwsing context is the main frame or not.
(WebKit::WebAutomationSession::waitForNavigationToCompleteOnPage): Check if there's an ongoing load for the page,
and wait for it to complete if needed.
(WebKit::WebAutomationSession::waitForNavigationToCompleteOnFrame): Check if there's an ongoing load for the frame,
and wait for it to complete if needed.
(WebKit::WebAutomationSession::navigateBrowsingContext): Use waitForNavigationToCompleteOnPage() now.
(WebKit::WebAutomationSession::goBackInBrowsingContext): Ditto.
(WebKit::WebAutomationSession::goForwardInBrowsingContext): Ditto.
(WebKit::WebAutomationSession::reloadBrowsingContext): Ditto.
(WebKit::WebAutomationSession::navigationOccurredForFrame): Renamed since it now receives the notification for
all the frames. Complete page operations if it's a main frame, or frame operations otherwise.
* UIProcess/Automation/WebAutomationSession.h:
* UIProcess/WebPageProxy.cpp:
(WebKit::WebPageProxy::didFinishLoadForFrame): Notify about all frames, not only the main one.
(WebKit::WebPageProxy::didFailLoadForFrame): Ditto.
(WebKit::WebPageProxy::didSameDocumentNavigationForFrame): Ditto.

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

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

index 93aa752ee6126f6fcbfe00327f496b798a7acca7..a2f68841a37f0e199039d7dd54d30deb81bc34a3 100644 (file)
@@ -1,3 +1,44 @@
+2017-07-21  Carlos Garcia Campos  <cgarcia@igalia.com>
+
+        WebDriver: wait until navigation is complete before running new commands and after a click
+        https://bugs.webkit.org/show_bug.cgi?id=174670
+
+        Reviewed by Brian Burg.
+
+        We are already waiting for navigation to complete after navigation commands (go, back, forward, refresh), but
+        the spec says we should always wait before executing a new command and also after a click. This is causing test
+        testShouldBeAbleToNavigateBackInTheBrowserHistoryInPresenceOfIframes to sometimes fail, because it does .click()
+        + .title and expects the title to tbe the one of the page loaded by the click. Since the load happens very fast,
+        the test usually passes, but in a real case with a slower load, the title of the previous page will be returned
+        most of the times.
+
+        6.3 Processing Model. Step 7. Wait for navigation to complete. If this returns an error return its value and
+        jump to step 1 in this overall algorithm, otherwise continue.
+        https://www.w3.org/TR/webdriver/#processing-model
+
+        14.1 Element Click. Step 10. If the click causes navigation: 1. Run the post-navigation checks and return its
+        value if it is an error. 2. Try to wait for navigation to complete.
+        https://www.w3.org/TR/webdriver/#element-click
+
+        * Session.cpp:
+        (WebDriver::Session::waitForNavigationToComplete): Send waitForNavigationToComplete message to the browser to
+        wait for any pending navigation of current browsing context to complete.
+        (WebDriver::Session::elementClick): Call waitForNavigationToComplete() after the click.
+        * Session.h:
+        * WebDriverService.cpp:
+        (WebDriver::WebDriverService::go): Wait for navigations to complete before running the command.
+        (WebDriver::WebDriverService::getCurrentURL): Ditto.
+        (WebDriver::WebDriverService::back): Ditto.
+        (WebDriver::WebDriverService::forward): Ditto.
+        (WebDriver::WebDriverService::refresh): Ditto.
+        (WebDriver::WebDriverService::getTitle): Ditto.
+        (WebDriver::WebDriverService::switchToFrame): Ditto.
+        (WebDriver::WebDriverService::switchToParentFrame): Ditto.
+        (WebDriver::WebDriverService::findElement): Ditto.
+        (WebDriver::WebDriverService::findElements): Ditto.
+        (WebDriver::WebDriverService::executeScript): Ditto.
+        (WebDriver::WebDriverService::executeAsyncScript): Ditto.
+
 2017-07-21  Carlos Garcia Campos  <cgarcia@igalia.com>
 
         WebDriver: correctly handle main frame handles
index fdd24dfbbed63422a7c5955d94786efbb1c387ad..cfbbbeffcea7ad10d660fa417ddc94ade8de9159 100644 (file)
@@ -942,6 +942,32 @@ void Session::getElementAttribute(const String& elementID, const String& attribu
     });
 }
 
+void Session::waitForNavigationToComplete(Function<void (CommandResult&&)>&& completionHandler)
+{
+    if (!m_toplevelBrowsingContext) {
+        completionHandler(CommandResult::fail(CommandResult::ErrorCode::NoSuchWindow));
+        return;
+    }
+
+    RefPtr<InspectorObject> parameters = InspectorObject::create();
+    parameters->setString(ASCIILiteral("browsingContextHandle"), m_toplevelBrowsingContext.value());
+    if (m_browsingContext)
+        parameters->setString(ASCIILiteral("frameHandle"), m_browsingContext.value());
+    m_host->sendCommandToBackend(ASCIILiteral("waitForNavigationToComplete"), WTFMove(parameters), [this, protectedThis = makeRef(*this), completionHandler = WTFMove(completionHandler)](SessionHost::CommandResponse&& response) {
+        if (response.isError) {
+            auto result = CommandResult::fail(WTFMove(response.responseObject));
+            if (result.errorCode() == CommandResult::ErrorCode::NoSuchFrame) {
+                // Navigation destroyed the current frame, switch to top level browsing context and ignore the error.
+                switchToBrowsingContext(std::nullopt);
+            } else {
+                completionHandler(WTFMove(result));
+                return;
+            }
+        }
+        completionHandler(CommandResult::success());
+    });
+}
+
 void Session::elementClick(const String& elementID, Function<void (CommandResult&&)>&& completionHandler)
 {
     if (!m_toplevelBrowsingContext) {
@@ -960,6 +986,8 @@ void Session::elementClick(const String& elementID, Function<void (CommandResult
         // FIXME: the center of the bounding box is not always part of the element.
         performMouseInteraction(rect.value().origin.x + rect.value().size.width / 2, rect.value().origin.y + rect.value().size.height / 2,
             MouseButton::Left, MouseInteraction::SingleClick, WTFMove(completionHandler));
+
+        waitForNavigationToComplete(WTFMove(completionHandler));
     });
 }
 
index 19d585eff04ef606c70034ad36945737ed1b0e3b..137b82adfe265727cb306bb6b9ba6c521accdc5f 100644 (file)
@@ -66,6 +66,7 @@ public:
         std::optional<Seconds> implicit;
     };
 
+    void waitForNavigationToComplete(Function<void (CommandResult&&)>&&);
     void createTopLevelBrowsingContext(Function<void (CommandResult&&)>&&);
     void close(Function<void (CommandResult&&)>&&);
     void getTimeouts(Function<void (CommandResult&&)>&&);
index f7f1f772b2ff1e58edab2c2847c34dae128078d6..f82044d0329c1df511ce03ba25460e804075f2e7 100644 (file)
@@ -410,47 +410,98 @@ void WebDriverService::go(RefPtr<InspectorObject>&& parameters, Function<void (C
         return;
     }
 
-    session->go(url, WTFMove(completionHandler));
+    session->waitForNavigationToComplete([session, url = WTFMove(url), completionHandler = WTFMove(completionHandler)](CommandResult&& result) mutable {
+        if (result.isError()) {
+            completionHandler(WTFMove(result));
+            return;
+        }
+        session->go(url, WTFMove(completionHandler));
+    });
 }
 
 void WebDriverService::getCurrentURL(RefPtr<InspectorObject>&& parameters, Function<void (CommandResult&&)>&& completionHandler)
 {
     // §9.2 Get Current URL.
     // https://www.w3.org/TR/webdriver/#get-current-url
-    if (auto session = findSessionOrCompleteWithError(*parameters, completionHandler))
+    auto session = findSessionOrCompleteWithError(*parameters, completionHandler);
+    if (!session)
+        return;
+
+    session->waitForNavigationToComplete([session, completionHandler = WTFMove(completionHandler)](CommandResult&& result) mutable {
+        if (result.isError()) {
+            completionHandler(WTFMove(result));
+            return;
+        }
         session->getCurrentURL(WTFMove(completionHandler));
+    });
 }
 
 void WebDriverService::back(RefPtr<InspectorObject>&& parameters, Function<void (CommandResult&&)>&& completionHandler)
 {
     // §9.3 Back.
     // https://www.w3.org/TR/webdriver/#back
-    if (auto session = findSessionOrCompleteWithError(*parameters, completionHandler))
+    auto session = findSessionOrCompleteWithError(*parameters, completionHandler);
+    if (!session)
+        return;
+
+    session->waitForNavigationToComplete([session, completionHandler = WTFMove(completionHandler)](CommandResult&& result) mutable {
+        if (result.isError()) {
+            completionHandler(WTFMove(result));
+            return;
+        }
         session->back(WTFMove(completionHandler));
+    });
 }
 
 void WebDriverService::forward(RefPtr<InspectorObject>&& parameters, Function<void (CommandResult&&)>&& completionHandler)
 {
     // §9.4 Forward.
     // https://www.w3.org/TR/webdriver/#forward
-    if (auto session = findSessionOrCompleteWithError(*parameters, completionHandler))
+    auto session = findSessionOrCompleteWithError(*parameters, completionHandler);
+    if (!session)
+        return;
+
+    session->waitForNavigationToComplete([session, completionHandler = WTFMove(completionHandler)](CommandResult&& result) mutable {
+        if (result.isError()) {
+            completionHandler(WTFMove(result));
+            return;
+        }
         session->forward(WTFMove(completionHandler));
+    });
 }
 
 void WebDriverService::refresh(RefPtr<InspectorObject>&& parameters, Function<void (CommandResult&&)>&& completionHandler)
 {
     // §9.5 Refresh.
     // https://www.w3.org/TR/webdriver/#refresh
-    if (auto session = findSessionOrCompleteWithError(*parameters, completionHandler))
+    auto session = findSessionOrCompleteWithError(*parameters, completionHandler);
+    if (!session)
+        return;
+
+    session->waitForNavigationToComplete([session, completionHandler = WTFMove(completionHandler)](CommandResult&& result) mutable {
+        if (result.isError()) {
+            completionHandler(WTFMove(result));
+            return;
+        }
         session->refresh(WTFMove(completionHandler));
+    });
 }
 
 void WebDriverService::getTitle(RefPtr<InspectorObject>&& parameters, Function<void (CommandResult&&)>&& completionHandler)
 {
     // §9.6 Get Title.
     // https://www.w3.org/TR/webdriver/#get-title
-    if (auto session = findSessionOrCompleteWithError(*parameters, completionHandler))
+    auto session = findSessionOrCompleteWithError(*parameters, completionHandler);
+    if (!session)
+        return;
+
+    session->waitForNavigationToComplete([session, completionHandler = WTFMove(completionHandler)](CommandResult&& result) mutable {
+        if (result.isError()) {
+            completionHandler(WTFMove(result));
+            return;
+        }
         session->getTitle(WTFMove(completionHandler));
+    });
 }
 
 void WebDriverService::getWindowHandle(RefPtr<InspectorObject>&& parameters, Function<void (CommandResult&&)>&& completionHandler)
@@ -562,15 +613,30 @@ void WebDriverService::switchToFrame(RefPtr<InspectorObject>&& parameters, Funct
         return;
     }
 
-    session->switchToFrame(WTFMove(frameID), WTFMove(completionHandler));
+    session->waitForNavigationToComplete([session, frameID, completionHandler = WTFMove(completionHandler)](CommandResult&& result) mutable {
+        if (result.isError()) {
+            completionHandler(WTFMove(result));
+            return;
+        }
+        session->switchToFrame(WTFMove(frameID), WTFMove(completionHandler));
+    });
 }
 
 void WebDriverService::switchToParentFrame(RefPtr<InspectorObject>&& parameters, Function<void (CommandResult&&)>&& completionHandler)
 {
     // §10.6 Switch To Parent Frame.
     // https://www.w3.org/TR/webdriver/#switch-to-parent-frame
-    if (auto session = findSessionOrCompleteWithError(*parameters, completionHandler))
+    auto session = findSessionOrCompleteWithError(*parameters, completionHandler);
+    if (!session)
+        return;
+
+    session->waitForNavigationToComplete([session, completionHandler = WTFMove(completionHandler)](CommandResult&& result) mutable {
+        if (result.isError()) {
+            completionHandler(WTFMove(result));
+            return;
+        }
         session->switchToParentFrame(WTFMove(completionHandler));
+    });
 }
 
 static std::optional<String> findElementOrCompleteWithError(InspectorObject& parameters, Function<void (CommandResult&&)>& completionHandler)
@@ -608,7 +674,13 @@ void WebDriverService::findElement(RefPtr<InspectorObject>&& parameters, Functio
     if (!findStrategyAndSelectorOrCompleteWithError(*parameters, completionHandler, strategy, selector))
         return;
 
-    session->findElements(strategy, selector, Session::FindElementsMode::Single, emptyString(), WTFMove(completionHandler));
+    session->waitForNavigationToComplete([session, strategy = WTFMove(strategy), selector = WTFMove(selector), completionHandler = WTFMove(completionHandler)](CommandResult&& result) mutable {
+        if (result.isError()) {
+            completionHandler(WTFMove(result));
+            return;
+        }
+        session->findElements(strategy, selector, Session::FindElementsMode::Single, emptyString(), WTFMove(completionHandler));
+    });
 }
 
 void WebDriverService::findElements(RefPtr<InspectorObject>&& parameters, Function<void (CommandResult&&)>&& completionHandler)
@@ -623,7 +695,13 @@ void WebDriverService::findElements(RefPtr<InspectorObject>&& parameters, Functi
     if (!findStrategyAndSelectorOrCompleteWithError(*parameters, completionHandler, strategy, selector))
         return;
 
-    session->findElements(strategy, selector, Session::FindElementsMode::Multiple, emptyString(), WTFMove(completionHandler));
+    session->waitForNavigationToComplete([session, strategy = WTFMove(strategy), selector = WTFMove(selector), completionHandler = WTFMove(completionHandler)](CommandResult&& result) mutable {
+        if (result.isError()) {
+            completionHandler(WTFMove(result));
+            return;
+        }
+        session->findElements(strategy, selector, Session::FindElementsMode::Multiple, emptyString(), WTFMove(completionHandler));
+    });
 }
 
 void WebDriverService::findElementFromElement(RefPtr<InspectorObject>&& parameters, Function<void (CommandResult&&)>&& completionHandler)
@@ -884,7 +962,13 @@ void WebDriverService::executeScript(RefPtr<InspectorObject>&& parameters, Funct
     if (!findScriptAndArgumentsOrCompleteWithError(*parameters, completionHandler, script, arguments))
         return;
 
-    session->executeScript(script, WTFMove(arguments), Session::ExecuteScriptMode::Sync, WTFMove(completionHandler));
+    session->waitForNavigationToComplete([session, script = WTFMove(script), arguments = WTFMove(arguments), completionHandler = WTFMove(completionHandler)](CommandResult&& result) mutable {
+        if (result.isError()) {
+            completionHandler(WTFMove(result));
+            return;
+        }
+        session->executeScript(script, WTFMove(arguments), Session::ExecuteScriptMode::Sync, WTFMove(completionHandler));
+    });
 }
 
 void WebDriverService::executeAsyncScript(RefPtr<InspectorObject>&& parameters, Function<void (CommandResult&&)>&& completionHandler)
@@ -900,7 +984,13 @@ void WebDriverService::executeAsyncScript(RefPtr<InspectorObject>&& parameters,
     if (!findScriptAndArgumentsOrCompleteWithError(*parameters, completionHandler, script, arguments))
         return;
 
-    session->executeScript(script, WTFMove(arguments), Session::ExecuteScriptMode::Async, WTFMove(completionHandler));
+    session->waitForNavigationToComplete([session, script = WTFMove(script), arguments = WTFMove(arguments), completionHandler = WTFMove(completionHandler)](CommandResult&& result) mutable {
+        if (result.isError()) {
+            completionHandler(WTFMove(result));
+            return;
+        }
+        session->executeScript(script, WTFMove(arguments), Session::ExecuteScriptMode::Async, WTFMove(completionHandler));
+    });
 }
 
 } // namespace WebDriver
index dfbedb1a182f8fcbae05960e49480ea95350fa75..2ee0b230f86a645fe5e068b3435c6dbbc59c737f 100644 (file)
@@ -1,3 +1,33 @@
+2017-07-21  Carlos Garcia Campos  <cgarcia@igalia.com>
+
+        WebDriver: wait until navigation is complete before running new commands and after a click
+        https://bugs.webkit.org/show_bug.cgi?id=174670
+
+        Reviewed by Brian Burg.
+
+        Add waitForNavigationToComplete method to Automation to allow WebDriver to wait for pending navigations to
+        complete. The new method already receives page load strategy and timeout, but they are not implemented yet.
+
+        * UIProcess/Automation/Automation.json: Add waitForNavigationToComplete method and PageLoadStrategy new type.
+        * UIProcess/Automation/WebAutomationSession.cpp:
+        (WebKit::WebAutomationSession::waitForNavigationToComplete): Call waitForNavigationToCompleteOnPage or
+        waitForNavigationToCompleteOnFrame depending on whether the current borwsing context is the main frame or not.
+        (WebKit::WebAutomationSession::waitForNavigationToCompleteOnPage): Check if there's an ongoing load for the page,
+        and wait for it to complete if needed.
+        (WebKit::WebAutomationSession::waitForNavigationToCompleteOnFrame): Check if there's an ongoing load for the frame,
+        and wait for it to complete if needed.
+        (WebKit::WebAutomationSession::navigateBrowsingContext): Use waitForNavigationToCompleteOnPage() now.
+        (WebKit::WebAutomationSession::goBackInBrowsingContext): Ditto.
+        (WebKit::WebAutomationSession::goForwardInBrowsingContext): Ditto.
+        (WebKit::WebAutomationSession::reloadBrowsingContext): Ditto.
+        (WebKit::WebAutomationSession::navigationOccurredForFrame): Renamed since it now receives the notification for
+        all the frames. Complete page operations if it's a main frame, or frame operations otherwise.
+        * UIProcess/Automation/WebAutomationSession.h:
+        * UIProcess/WebPageProxy.cpp:
+        (WebKit::WebPageProxy::didFinishLoadForFrame): Notify about all frames, not only the main one.
+        (WebKit::WebPageProxy::didFailLoadForFrame): Ditto.
+        (WebKit::WebPageProxy::didSameDocumentNavigationForFrame): Ditto.
+
 2017-07-20  Carlos Garcia Campos  <cgarcia@igalia.com>
 
         Unreviewed. Remove WKIconDatabaseCairo.
index b3f2a3313c1927d04e112fedb2bac46e53c3e205..37ea8787cb8ba3ae8cd4e52074664584e82dee53 100644 (file)
                 "InvalidSelector"
             ]
         },
+        {
+            "id": "PageLoadStrategy",
+            "type": "string",
+            "description": "Enumerates different page load strategies.",
+            "enum": [
+                "None",
+                "Eager",
+                "Normal"
+            ]
+        },
         {
             "id": "BrowsingContext",
             "type": "object",
             ],
             "async": true
         },
+        {
+            "name": "waitForNavigationToComplete",
+            "description": "Wait for pending navigation to complete.",
+            "parameters": [
+                { "name": "browsingContextHandle", "$ref": "BrowsingContextHandle", "description": "The handle for the browsing context in which wait for pending navigation to complete." },
+                { "name": "frameHandle", "$ref": "FrameHandle", "optional": true, "description": "The handle for the frame in which wait for pending navigation to complete. The main frame is used if this parameter empty string or excluded." },
+                { "name": "pageLoadStrategy", "$ref": "PageLoadStrategy", "optional": true, "description": "The page load strategy to use when determining when navigation is complete." },
+                { "name": "pageLoadTimeout", "type": "integer", "optional": true, "description": "The timeout in milliseconds that the navigation is expected to be completed in, otherwise a <code>Timeout</code> error is returned." }
+            ],
+            "async": true
+        },
         {
             "name": "inspectBrowsingContext",
             "description": "Inspect the specified browsing context using Web Inspector.",
index 06ca8a420da86af3c79b2323b9140b7b7b403074..25a5dc4e9f9747663ab0fc05b1c05ce98876198f 100644 (file)
@@ -375,17 +375,60 @@ void WebAutomationSession::moveWindowOfBrowsingContext(Inspector::ErrorString& e
 #endif
 }
 
-void WebAutomationSession::navigateBrowsingContext(Inspector::ErrorString& errorString, const String& handle, const String& url, Ref<NavigateBrowsingContextCallback>&& callback)
+void WebAutomationSession::waitForNavigationToComplete(Inspector::ErrorString& errorString, const String& browsingContextHandle, const String* optionalFrameHandle, const String* optionalPageLoadStrategyString, const int* optionalPageLoadTimeout, Ref<WaitForNavigationToCompleteCallback>&& callback)
 {
-    WebPageProxy* page = webPageProxyForHandle(handle);
+    WebPageProxy* page = webPageProxyForHandle(browsingContextHandle);
     if (!page)
         FAIL_WITH_PREDEFINED_ERROR(WindowNotFound);
 
-    if (auto callback = m_pendingNavigationInBrowsingContextCallbacksPerPage.take(page->pageID()))
+    // FIXME: Implement page load strategy and timeout.
+
+    if (optionalFrameHandle && !optionalFrameHandle->isEmpty()) {
+        std::optional<uint64_t> frameID = webFrameIDForHandle(*optionalFrameHandle);
+        if (!frameID)
+            FAIL_WITH_PREDEFINED_ERROR(FrameNotFound);
+        WebFrameProxy* frame = page->process().webFrame(frameID.value());
+        if (!frame)
+            FAIL_WITH_PREDEFINED_ERROR(FrameNotFound);
+        waitForNavigationToCompleteOnFrame(*frame, WTFMove(callback));
+    } else
+        waitForNavigationToCompleteOnPage(*page, WTFMove(callback));
+}
+
+void WebAutomationSession::waitForNavigationToCompleteOnPage(WebPageProxy& page, Ref<Inspector::BackendDispatcher::CallbackBase>&& callback)
+{
+    if (auto callback = m_pendingNavigationInBrowsingContextCallbacksPerPage.take(page.pageID()))
+        callback->sendFailure(STRING_FOR_PREDEFINED_ERROR_NAME(Timeout));
+
+    if (!page.pageLoadState().isLoading()) {
+        callback->sendSuccess(InspectorObject::create());
+        return;
+    }
+
+    m_pendingNavigationInBrowsingContextCallbacksPerPage.set(page.pageID(), WTFMove(callback));
+}
+
+void WebAutomationSession::waitForNavigationToCompleteOnFrame(WebFrameProxy& frame, Ref<Inspector::BackendDispatcher::CallbackBase>&& callback)
+{
+    if (auto callback = m_pendingNavigationInBrowsingContextCallbacksPerFrame.take(frame.frameID()))
         callback->sendFailure(STRING_FOR_PREDEFINED_ERROR_NAME(Timeout));
-    m_pendingNavigationInBrowsingContextCallbacksPerPage.set(page->pageID(), WTFMove(callback));
+
+    if (frame.frameLoadState().state() == FrameLoadState::State::Finished) {
+        callback->sendSuccess(InspectorObject::create());
+        return;
+    }
+
+    m_pendingNavigationInBrowsingContextCallbacksPerFrame.set(frame.frameID(), WTFMove(callback));
+}
+
+void WebAutomationSession::navigateBrowsingContext(Inspector::ErrorString& errorString, const String& handle, const String& url, Ref<NavigateBrowsingContextCallback>&& callback)
+{
+    WebPageProxy* page = webPageProxyForHandle(handle);
+    if (!page)
+        FAIL_WITH_PREDEFINED_ERROR(WindowNotFound);
 
     page->loadRequest(WebCore::URL(WebCore::URL(), url));
+    waitForNavigationToCompleteOnPage(*page, WTFMove(callback));
 }
 
 void WebAutomationSession::goBackInBrowsingContext(Inspector::ErrorString& errorString, const String& handle, Ref<GoBackInBrowsingContextCallback>&& callback)
@@ -394,13 +437,8 @@ void WebAutomationSession::goBackInBrowsingContext(Inspector::ErrorString& error
     if (!page)
         FAIL_WITH_PREDEFINED_ERROR(WindowNotFound);
 
-    if (auto callback = m_pendingNavigationInBrowsingContextCallbacksPerPage.take(page->pageID()))
-        callback->sendFailure(STRING_FOR_PREDEFINED_ERROR_NAME(Timeout));
-
-    if (page->goBack())
-        m_pendingNavigationInBrowsingContextCallbacksPerPage.set(page->pageID(), WTFMove(callback));
-    else
-        callback->sendSuccess();
+    page->goBack();
+    waitForNavigationToCompleteOnPage(*page, WTFMove(callback));
 }
 
 void WebAutomationSession::goForwardInBrowsingContext(Inspector::ErrorString& errorString, const String& handle, Ref<GoForwardInBrowsingContextCallback>&& callback)
@@ -409,13 +447,8 @@ void WebAutomationSession::goForwardInBrowsingContext(Inspector::ErrorString& er
     if (!page)
         FAIL_WITH_PREDEFINED_ERROR(WindowNotFound);
 
-    if (auto callback = m_pendingNavigationInBrowsingContextCallbacksPerPage.take(page->pageID()))
-        callback->sendFailure(STRING_FOR_PREDEFINED_ERROR_NAME(Timeout));
-
-    if (page->goForward())
-        m_pendingNavigationInBrowsingContextCallbacksPerPage.set(page->pageID(), WTFMove(callback));
-    else
-        callback->sendSuccess();
+    page->goForward();
+    waitForNavigationToCompleteOnPage(*page, WTFMove(callback));
 }
 
 void WebAutomationSession::reloadBrowsingContext(Inspector::ErrorString& errorString, const String& handle, Ref<ReloadBrowsingContextCallback>&& callback)
@@ -424,17 +457,19 @@ void WebAutomationSession::reloadBrowsingContext(Inspector::ErrorString& errorSt
     if (!page)
         FAIL_WITH_PREDEFINED_ERROR(WindowNotFound);
 
-    if (auto callback = m_pendingNavigationInBrowsingContextCallbacksPerPage.take(page->pageID()))
-        callback->sendFailure(STRING_FOR_PREDEFINED_ERROR_NAME(Timeout));
-    m_pendingNavigationInBrowsingContextCallbacksPerPage.set(page->pageID(), WTFMove(callback));
-
     page->reload({ });
+    waitForNavigationToCompleteOnPage(*page, WTFMove(callback));
 }
 
-void WebAutomationSession::navigationOccurredForPage(const WebPageProxy& page)
+void WebAutomationSession::navigationOccurredForFrame(const WebFrameProxy& frame)
 {
-    if (auto callback = m_pendingNavigationInBrowsingContextCallbacksPerPage.take(page.pageID()))
-        callback->sendSuccess(InspectorObject::create());
+    if (frame.isMainFrame()) {
+        if (auto callback = m_pendingNavigationInBrowsingContextCallbacksPerPage.take(frame.page()->pageID()))
+            callback->sendSuccess(InspectorObject::create());
+    } else {
+        if (auto callback = m_pendingNavigationInBrowsingContextCallbacksPerFrame.take(frame.frameID()))
+            callback->sendSuccess(InspectorObject::create());
+    }
 }
 
 void WebAutomationSession::inspectorFrontendLoaded(const WebPageProxy& page)
index e5fb5f09e531083add6da99b931845b309b7910d..cac6ccd1a48fd843b0dc47a5b424c0e545260b35 100644 (file)
@@ -86,7 +86,7 @@ public:
     WebProcessPool* processPool() const { return m_processPool; }
     void setProcessPool(WebProcessPool*);
 
-    void navigationOccurredForPage(const WebPageProxy&);
+    void navigationOccurredForFrame(const WebFrameProxy&);
     void inspectorFrontendLoaded(const WebPageProxy&);
     void keyboardEventsFlushedForPage(const WebPageProxy&);
 
@@ -115,6 +115,7 @@ public:
     void goBackInBrowsingContext(Inspector::ErrorString&, const String&, Ref<GoBackInBrowsingContextCallback>&&) override;
     void goForwardInBrowsingContext(Inspector::ErrorString&, const String&, Ref<GoForwardInBrowsingContextCallback>&&) override;
     void reloadBrowsingContext(Inspector::ErrorString&, const String&, Ref<ReloadBrowsingContextCallback>&&) override;
+    void waitForNavigationToComplete(Inspector::ErrorString&, const String& browsingContextHandle, const String* optionalFrameHandle, const String* optionalPageLoadStrategyString, const int* optionalPageLoadTimeout, Ref<WaitForNavigationToCompleteCallback>&&) override;
     void evaluateJavaScriptFunction(Inspector::ErrorString&, const String& browsingContextHandle, const String* optionalFrameHandle, const String& function, const Inspector::InspectorArray& arguments, const bool* optionalExpectsImplicitCallbackArgument, const int* optionalCallbackTimeout, Ref<Inspector::AutomationBackendDispatcherHandler::EvaluateJavaScriptFunctionCallback>&&) override;
     void performMouseInteraction(Inspector::ErrorString&, const String& handle, const Inspector::InspectorObject& requestedPosition, const String& mouseButton, const String& mouseInteraction, const Inspector::InspectorArray& keyModifiers, RefPtr<Inspector::Protocol::Automation::Point>& updatedPosition) override;
     void performKeyboardInteractions(Inspector::ErrorString&, const String& handle, const Inspector::InspectorArray& interactions, Ref<PerformKeyboardInteractionsCallback>&&) override;
@@ -152,6 +153,9 @@ private:
     String handleForWebFrameID(uint64_t frameID);
     String handleForWebFrameProxy(const WebFrameProxy&);
 
+    void waitForNavigationToCompleteOnPage(WebPageProxy&, Ref<Inspector::BackendDispatcher::CallbackBase>&&);
+    void waitForNavigationToCompleteOnFrame(WebFrameProxy&, Ref<Inspector::BackendDispatcher::CallbackBase>&&);
+
     // Implemented in generated WebAutomationSessionMessageReceiver.cpp.
     void didReceiveMessage(IPC::Connection&, IPC::Decoder&) override;
 
@@ -197,6 +201,7 @@ private:
     HashMap<String, uint64_t> m_handleWebFrameMap;
 
     HashMap<uint64_t, RefPtr<Inspector::BackendDispatcher::CallbackBase>> m_pendingNavigationInBrowsingContextCallbacksPerPage;
+    HashMap<uint64_t, RefPtr<Inspector::BackendDispatcher::CallbackBase>> m_pendingNavigationInBrowsingContextCallbacksPerFrame;
     HashMap<uint64_t, RefPtr<Inspector::BackendDispatcher::CallbackBase>> m_pendingInspectorCallbacksPerPage;
     HashMap<uint64_t, RefPtr<Inspector::BackendDispatcher::CallbackBase>> m_pendingKeyboardEventsFlushedCallbacksPerPage;
 
index bbd398b440c6e419839ee9b5262abcddc2fd805e..9fb3f2f8b207dec9c51ed76c574ad84672b8b124 100644 (file)
@@ -3391,9 +3391,9 @@ void WebPageProxy::didFinishLoadForFrame(uint64_t frameID, uint64_t navigationID
     if (isMainFrame)
         m_pageLoadState.didFinishLoad(transaction);
 
-    if (isMainFrame && m_controlledByAutomation) {
+    if (m_controlledByAutomation) {
         if (auto* automationSession = process().processPool().automationSession())
-            automationSession->navigationOccurredForPage(*this);
+            automationSession->navigationOccurredForFrame(*frame);
     }
 
     frame->didFinishLoad();
@@ -3432,9 +3432,9 @@ void WebPageProxy::didFailLoadForFrame(uint64_t frameID, uint64_t navigationID,
     if (isMainFrame)
         m_pageLoadState.didFailLoad(transaction);
 
-    if (isMainFrame && m_controlledByAutomation) {
+    if (m_controlledByAutomation) {
         if (auto* automationSession = process().processPool().automationSession())
-            automationSession->navigationOccurredForPage(*this);
+            automationSession->navigationOccurredForFrame(*frame);
     }
 
     frame->didFailLoad();
@@ -3469,9 +3469,9 @@ void WebPageProxy::didSameDocumentNavigationForFrame(uint64_t frameID, uint64_t
     if (isMainFrame)
         m_pageLoadState.didSameDocumentNavigation(transaction, url);
 
-    if (isMainFrame && m_controlledByAutomation) {
+    if (m_controlledByAutomation) {
         if (auto* automationSession = process().processPool().automationSession())
-            automationSession->navigationOccurredForPage(*this);
+            automationSession->navigationOccurredForFrame(*frame);
     }
 
     m_pageLoadState.clearPendingAPIRequestURL(transaction);