| /* |
| * Copyright (C) 2017 Igalia S.L. |
| * |
| * 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" |
| #include "WebDriverService.h" |
| |
| #include "Capabilities.h" |
| #include "CommandResult.h" |
| #include "SessionHost.h" |
| #include <inspector/InspectorValues.h> |
| #include <wtf/RunLoop.h> |
| #include <wtf/text/WTFString.h> |
| |
| using namespace Inspector; |
| |
| namespace WebDriver { |
| |
| WebDriverService::WebDriverService() |
| : m_server(*this) |
| { |
| } |
| |
| static void printUsageStatement(const char* programName) |
| { |
| printf("Usage: %s options\n", programName); |
| printf(" -h, --help Prints this help message\n"); |
| printf(" -p <port>, --port=<port> Port number the driver will use\n"); |
| printf("\n"); |
| } |
| |
| int WebDriverService::run(int argc, char** argv) |
| { |
| String portString; |
| for (unsigned i = 1 ; i < argc; ++i) { |
| const char* arg = argv[i]; |
| if (!strcmp(arg, "-h") || !strcmp(arg, "--help")) { |
| printUsageStatement(argv[0]); |
| return EXIT_SUCCESS; |
| } |
| |
| if (!strcmp(arg, "-p") && portString.isNull()) { |
| if (++i == argc) { |
| printUsageStatement(argv[0]); |
| return EXIT_FAILURE; |
| } |
| portString = argv[i]; |
| continue; |
| } |
| |
| static const unsigned portStrLength = strlen("--port="); |
| if (!strncmp(arg, "--port=", portStrLength) && portString.isNull()) { |
| portString = String(arg + portStrLength); |
| continue; |
| } |
| } |
| |
| if (portString.isNull()) { |
| printUsageStatement(argv[0]); |
| return EXIT_FAILURE; |
| } |
| |
| bool ok; |
| unsigned port = portString.toUInt(&ok); |
| if (!ok) { |
| fprintf(stderr, "Invalid port %s provided\n", portString.ascii().data()); |
| return EXIT_FAILURE; |
| } |
| |
| RunLoop::initializeMainRunLoop(); |
| |
| if (!m_server.listen(port)) |
| return EXIT_FAILURE; |
| |
| RunLoop::run(); |
| |
| return EXIT_SUCCESS; |
| } |
| |
| void WebDriverService::quit() |
| { |
| m_server.disconnect(); |
| RunLoop::main().stop(); |
| } |
| |
| const WebDriverService::Command WebDriverService::s_commands[] = { |
| { HTTPMethod::Post, "/session", &WebDriverService::newSession }, |
| { HTTPMethod::Delete, "/session/$sessionId", &WebDriverService::deleteSession }, |
| { HTTPMethod::Post, "/session/$sessionId/timeouts", &WebDriverService::setTimeouts }, |
| |
| { HTTPMethod::Post, "/session/$sessionId/url", &WebDriverService::go }, |
| { HTTPMethod::Get, "/session/$sessionId/url", &WebDriverService::getCurrentURL }, |
| { HTTPMethod::Post, "/session/$sessionId/back", &WebDriverService::back }, |
| { HTTPMethod::Post, "/session/$sessionId/forward", &WebDriverService::forward }, |
| { HTTPMethod::Post, "/session/$sessionId/refresh", &WebDriverService::refresh }, |
| { HTTPMethod::Get, "/session/$sessionId/title", &WebDriverService::getTitle }, |
| |
| { HTTPMethod::Get, "/session/$sessionId/window", &WebDriverService::getWindowHandle }, |
| { HTTPMethod::Delete, "/session/$sessionId/window", &WebDriverService::closeWindow }, |
| { HTTPMethod::Post, "/session/$sessionId/window", &WebDriverService::switchToWindow }, |
| { HTTPMethod::Get, "/session/$sessionId/window/handles", &WebDriverService::getWindowHandles }, |
| { HTTPMethod::Post, "/session/$sessionId/frame", &WebDriverService::switchToFrame }, |
| { HTTPMethod::Post, "/session/$sessionId/frame/parent", &WebDriverService::switchToParentFrame }, |
| |
| // FIXME: Not in the spec, but still used by Selenium. |
| { HTTPMethod::Get, "/session/$sessionId/window/position", &WebDriverService::getWindowPosition }, |
| { HTTPMethod::Post, "/session/$sessionId/window/position", &WebDriverService::setWindowPosition }, |
| { HTTPMethod::Get, "/session/$sessionId/window/size", &WebDriverService::getWindowSize }, |
| { HTTPMethod::Post, "/session/$sessionId/window/size", &WebDriverService::setWindowSize }, |
| |
| { HTTPMethod::Post, "/session/$sessionId/element", &WebDriverService::findElement }, |
| { HTTPMethod::Post, "/session/$sessionId/elements", &WebDriverService::findElements }, |
| { HTTPMethod::Post, "/session/$sessionId/element/$elementId/element", &WebDriverService::findElementFromElement }, |
| { HTTPMethod::Post, "/session/$sessionId/element/$elementId/elements", &WebDriverService::findElementsFromElement }, |
| |
| { HTTPMethod::Get, "/session/$sessionId/element/$elementId/selected", &WebDriverService::isElementSelected }, |
| { HTTPMethod::Get, "/session/$sessionId/element/$elementId/attribute/$name", &WebDriverService::getElementAttribute }, |
| { HTTPMethod::Get, "/session/$sessionId/element/$elementId/text", &WebDriverService::getElementText }, |
| { HTTPMethod::Get, "/session/$sessionId/element/$elementId/name", &WebDriverService::getElementTagName }, |
| { HTTPMethod::Get, "/session/$sessionId/element/$elementId/rect", &WebDriverService::getElementRect }, |
| { HTTPMethod::Get, "/session/$sessionId/element/$elementId/enabled", &WebDriverService::isElementEnabled }, |
| |
| { HTTPMethod::Post, "/session/$sessionId/element/$elementId/click", &WebDriverService::elementClick }, |
| { HTTPMethod::Post, "/session/$sessionId/element/$elementId/clear", &WebDriverService::elementClear }, |
| { HTTPMethod::Post, "/session/$sessionId/element/$elementId/value", &WebDriverService::elementSendKeys }, |
| |
| // FIXME: Not in the spec, but still used by Selenium. |
| { HTTPMethod::Post, "/session/$sessionId/element/$elementId/submit", &WebDriverService::elementSubmit }, |
| |
| { HTTPMethod::Post, "/session/$sessionId/execute/sync", &WebDriverService::executeScript }, |
| { HTTPMethod::Post, "/session/$sessionId/execute/async", &WebDriverService::executeAsyncScript }, |
| |
| { HTTPMethod::Get, "/session/$sessionId/element/$elementId/displayed", &WebDriverService::isElementDisplayed }, |
| }; |
| |
| std::optional<WebDriverService::HTTPMethod> WebDriverService::toCommandHTTPMethod(const String& method) |
| { |
| auto lowerCaseMethod = method.convertToASCIILowercase(); |
| if (lowerCaseMethod == "get") |
| return WebDriverService::HTTPMethod::Get; |
| if (lowerCaseMethod == "post" || lowerCaseMethod == "put") |
| return WebDriverService::HTTPMethod::Post; |
| if (lowerCaseMethod == "delete") |
| return WebDriverService::HTTPMethod::Delete; |
| |
| return std::nullopt; |
| } |
| |
| bool WebDriverService::findCommand(const String& method, const String& path, CommandHandler* handler, HashMap<String, String>& parameters) |
| { |
| auto commandMethod = toCommandHTTPMethod(method); |
| if (!commandMethod) |
| return false; |
| |
| size_t length = WTF_ARRAY_LENGTH(s_commands); |
| for (size_t i = 0; i < length; ++i) { |
| if (s_commands[i].method != *commandMethod) |
| continue; |
| |
| Vector<String> pathTokens; |
| path.split("/", pathTokens); |
| Vector<String> commandTokens; |
| String::fromUTF8(s_commands[i].uriTemplate).split("/", commandTokens); |
| if (pathTokens.size() != commandTokens.size()) |
| continue; |
| |
| bool allMatched = true; |
| for (size_t j = 0; j < pathTokens.size() && allMatched; ++j) { |
| if (commandTokens[j][0] == '$') |
| parameters.set(commandTokens[j].substring(1), pathTokens[j]); |
| else if (commandTokens[j] != pathTokens[j]) |
| allMatched = false; |
| } |
| |
| if (allMatched) { |
| *handler = s_commands[i].handler; |
| return true; |
| } |
| |
| parameters.clear(); |
| } |
| |
| return false; |
| } |
| |
| void WebDriverService::handleRequest(HTTPRequestHandler::Request&& request, Function<void (HTTPRequestHandler::Response&&)>&& replyHandler) |
| { |
| CommandHandler handler; |
| HashMap<String, String> parameters; |
| if (!findCommand(request.method, request.path, &handler, parameters)) { |
| sendResponse(WTFMove(replyHandler), CommandResult::fail(CommandResult::ErrorCode::UnknownCommand, String("Unknown command: " + request.path))); |
| return; |
| } |
| |
| RefPtr<InspectorObject> parametersObject; |
| if (request.dataLength) { |
| RefPtr<InspectorValue> messageValue; |
| if (!InspectorValue::parseJSON(String::fromUTF8(request.data, request.dataLength), messageValue)) { |
| sendResponse(WTFMove(replyHandler), CommandResult::fail(CommandResult::ErrorCode::InvalidArgument)); |
| return; |
| } |
| |
| if (!messageValue->asObject(parametersObject)) { |
| sendResponse(WTFMove(replyHandler), CommandResult::fail(CommandResult::ErrorCode::InvalidArgument)); |
| return; |
| } |
| } else |
| parametersObject = InspectorObject::create(); |
| for (const auto& parameter : parameters) |
| parametersObject->setString(parameter.key, parameter.value); |
| |
| ((*this).*handler)(WTFMove(parametersObject), [this, replyHandler = WTFMove(replyHandler)](CommandResult&& result) mutable { |
| sendResponse(WTFMove(replyHandler), WTFMove(result)); |
| }); |
| } |
| |
| void WebDriverService::sendResponse(Function<void (HTTPRequestHandler::Response&&)>&& replyHandler, CommandResult&& result) const |
| { |
| RefPtr<InspectorObject> responseObject; |
| if (result.isError()) { |
| responseObject = InspectorObject::create(); |
| responseObject->setString(ASCIILiteral("error"), result.errorString()); |
| responseObject->setString(ASCIILiteral("message"), result.errorMessage().value_or(emptyString())); |
| responseObject->setString(ASCIILiteral("stacktrace"), emptyString()); |
| } else { |
| responseObject = InspectorObject::create(); |
| auto resultValue = result.result(); |
| responseObject->setValue(ASCIILiteral("value"), resultValue ? WTFMove(resultValue) : InspectorValue::null()); |
| } |
| replyHandler({ result.httpStatusCode(), responseObject->toJSONString().utf8(), ASCIILiteral("application/json; charset=utf-8") }); |
| } |
| |
| bool WebDriverService::parseCapabilities(InspectorObject& desiredCapabilities, Capabilities& capabilities, Function<void (CommandResult&&)>& completionHandler) |
| { |
| RefPtr<InspectorValue> value; |
| if (desiredCapabilities.getValue(ASCIILiteral("browserName"), value) && !value->asString(capabilities.browserName)) { |
| completionHandler(CommandResult::fail(CommandResult::ErrorCode::SessionNotCreated, String("browserName parameter is invalid in capabilities"))); |
| return false; |
| } |
| if (desiredCapabilities.getValue(ASCIILiteral("version"), value) && !value->asString(capabilities.browserVersion)) { |
| completionHandler(CommandResult::fail(CommandResult::ErrorCode::SessionNotCreated, String("version parameter is invalid in capabilities"))); |
| return false; |
| } |
| if (desiredCapabilities.getValue(ASCIILiteral("platform"), value) && !value->asString(capabilities.platform)) { |
| completionHandler(CommandResult::fail(CommandResult::ErrorCode::SessionNotCreated, String("platform parameter is invalid in capabilities"))); |
| return false; |
| } |
| // FIXME: parse all other well-known capabilities: acceptInsecureCerts, pageLoadStrategy, proxy, setWindowRect, timeouts, unhandledPromptBehavior. |
| return platformParseCapabilities(desiredCapabilities, capabilities, completionHandler); |
| } |
| |
| RefPtr<Session> WebDriverService::findSessionOrCompleteWithError(InspectorObject& parameters, Function<void (CommandResult&&)>& completionHandler) |
| { |
| String sessionID; |
| if (!parameters.getString(ASCIILiteral("sessionId"), sessionID)) { |
| completionHandler(CommandResult::fail(CommandResult::ErrorCode::InvalidArgument)); |
| return nullptr; |
| } |
| |
| auto session = m_sessions.get(sessionID); |
| if (!session) { |
| completionHandler(CommandResult::fail(CommandResult::ErrorCode::InvalidSessionID)); |
| return nullptr; |
| } |
| |
| return session; |
| } |
| |
| void WebDriverService::newSession(RefPtr<InspectorObject>&& parameters, Function<void (CommandResult&&)>&& completionHandler) |
| { |
| // §8.1 New Session. |
| // https://www.w3.org/TR/webdriver/#new-session |
| RefPtr<InspectorObject> capabilitiesObject; |
| if (!parameters->getObject(ASCIILiteral("capabilities"), capabilitiesObject)) { |
| completionHandler(CommandResult::fail(CommandResult::ErrorCode::SessionNotCreated)); |
| return; |
| } |
| RefPtr<InspectorValue> requiredCapabilitiesValue; |
| RefPtr<InspectorObject> requiredCapabilities; |
| if (!capabilitiesObject->getValue(ASCIILiteral("alwaysMatch"), requiredCapabilitiesValue)) |
| requiredCapabilities = InspectorObject::create(); |
| else if (!requiredCapabilitiesValue->asObject(requiredCapabilities)) { |
| completionHandler(CommandResult::fail(CommandResult::ErrorCode::SessionNotCreated, String("alwaysMatch is invalid in capabilities"))); |
| return; |
| } |
| // FIXME: process firstMatch capabilities. |
| |
| Capabilities capabilities; |
| if (!parseCapabilities(*requiredCapabilities, capabilities, completionHandler)) |
| return; |
| |
| auto sessionHost = std::make_unique<SessionHost>(WTFMove(capabilities)); |
| auto* sessionHostPtr = sessionHost.get(); |
| sessionHostPtr->connectToBrowser([this, sessionHost = WTFMove(sessionHost), completionHandler = WTFMove(completionHandler)](SessionHost::Succeeded succeeded) mutable { |
| if (succeeded == SessionHost::Succeeded::No) { |
| completionHandler(CommandResult::fail(CommandResult::ErrorCode::SessionNotCreated, String("Failed to connect to browser"))); |
| return; |
| } |
| |
| RefPtr<Session> session = Session::create(WTFMove(sessionHost)); |
| session->createTopLevelBrowsingContext([this, session, completionHandler = WTFMove(completionHandler)](CommandResult&& result) mutable { |
| if (result.isError()) { |
| completionHandler(CommandResult::fail(CommandResult::ErrorCode::SessionNotCreated, result.errorString())); |
| return; |
| } |
| |
| m_activeSession = session.get(); |
| m_sessions.add(session->id(), session); |
| RefPtr<InspectorObject> resultObject = InspectorObject::create(); |
| resultObject->setString(ASCIILiteral("sessionId"), session->id()); |
| RefPtr<InspectorObject> capabilities = InspectorObject::create(); |
| capabilities->setString(ASCIILiteral("browserName"), session->capabilities().browserName); |
| capabilities->setString(ASCIILiteral("version"), session->capabilities().browserVersion); |
| capabilities->setString(ASCIILiteral("platform"), session->capabilities().platform); |
| resultObject->setObject(ASCIILiteral("value"), WTFMove(capabilities)); |
| completionHandler(CommandResult::success(WTFMove(resultObject))); |
| }); |
| }); |
| } |
| |
| void WebDriverService::deleteSession(RefPtr<InspectorObject>&& parameters, Function<void (CommandResult&&)>&& completionHandler) |
| { |
| // §8.2 Delete Session. |
| // https://www.w3.org/TR/webdriver/#delete-session |
| String sessionID; |
| if (!parameters->getString(ASCIILiteral("sessionId"), sessionID)) { |
| completionHandler(CommandResult::fail(CommandResult::ErrorCode::InvalidArgument)); |
| return; |
| } |
| |
| auto session = m_sessions.take(sessionID); |
| if (!session) { |
| completionHandler(CommandResult::fail(CommandResult::ErrorCode::InvalidSessionID)); |
| return; |
| } |
| |
| if (m_activeSession == session.get()) |
| m_activeSession = nullptr; |
| |
| session->close(WTFMove(completionHandler)); |
| } |
| |
| void WebDriverService::setTimeouts(RefPtr<InspectorObject>&& parameters, Function<void (CommandResult&&)>&& completionHandler) |
| { |
| // §8.5 Set Timeouts. |
| // https://www.w3.org/TR/webdriver/#set-timeouts |
| auto session = findSessionOrCompleteWithError(*parameters, completionHandler); |
| if (!session) |
| return; |
| |
| Session::Timeouts timeouts; |
| auto end = parameters->end(); |
| for (auto it = parameters->begin(); it != end; ++it) { |
| if (it->key == "sessionId") |
| continue; |
| |
| int timeoutMS; |
| if (!it->value->asInteger(timeoutMS) || timeoutMS < 0 || timeoutMS > INT_MAX) { |
| completionHandler(CommandResult::fail(CommandResult::ErrorCode::InvalidArgument)); |
| return; |
| } |
| |
| if (it->key == "script") |
| timeouts.script = Seconds::fromMilliseconds(timeoutMS); |
| else if (it->key == "pageLoad") |
| timeouts.pageLoad = Seconds::fromMilliseconds(timeoutMS); |
| else if (it->key == "implicit") |
| timeouts.implicit = Seconds::fromMilliseconds(timeoutMS); |
| else { |
| completionHandler(CommandResult::fail(CommandResult::ErrorCode::InvalidArgument)); |
| return; |
| } |
| } |
| |
| session->setTimeouts(timeouts, WTFMove(completionHandler)); |
| } |
| |
| void WebDriverService::go(RefPtr<InspectorObject>&& parameters, Function<void (CommandResult&&)>&& completionHandler) |
| { |
| // §9.1 Go. |
| // https://www.w3.org/TR/webdriver/#go |
| auto session = findSessionOrCompleteWithError(*parameters, completionHandler); |
| if (!session) |
| return; |
| |
| String url; |
| if (!parameters->getString(ASCIILiteral("url"), url)) { |
| completionHandler(CommandResult::fail(CommandResult::ErrorCode::InvalidArgument)); |
| return; |
| } |
| |
| 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 |
| 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 |
| 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 |
| 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 |
| 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 |
| 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) |
| { |
| // §10.1 Get Window Handle. |
| // https://www.w3.org/TR/webdriver/#get-window-handle |
| if (auto session = findSessionOrCompleteWithError(*parameters, completionHandler)) |
| session->getWindowHandle(WTFMove(completionHandler)); |
| } |
| |
| void WebDriverService::getWindowPosition(RefPtr<InspectorObject>&& parameters, Function<void (CommandResult&&)>&& completionHandler) |
| { |
| if (auto session = findSessionOrCompleteWithError(*parameters, completionHandler)) |
| session->getWindowPosition(WTFMove(completionHandler)); |
| } |
| |
| void WebDriverService::setWindowPosition(RefPtr<InspectorObject>&& parameters, Function<void (CommandResult&&)>&& completionHandler) |
| { |
| auto session = findSessionOrCompleteWithError(*parameters, completionHandler); |
| if (!session) |
| return; |
| |
| int windowX; |
| if (!parameters->getInteger(ASCIILiteral("x"), windowX)) { |
| completionHandler(CommandResult::fail(CommandResult::ErrorCode::InvalidArgument)); |
| return; |
| } |
| |
| int windowY; |
| if (!parameters->getInteger(ASCIILiteral("y"), windowY)) { |
| completionHandler(CommandResult::fail(CommandResult::ErrorCode::InvalidArgument)); |
| return; |
| } |
| |
| session->setWindowPosition(windowX, windowY, WTFMove(completionHandler)); |
| } |
| |
| void WebDriverService::getWindowSize(RefPtr<InspectorObject>&& parameters, Function<void (CommandResult&&)>&& completionHandler) |
| { |
| if (auto session = findSessionOrCompleteWithError(*parameters, completionHandler)) |
| session->getWindowSize(WTFMove(completionHandler)); |
| } |
| |
| void WebDriverService::setWindowSize(RefPtr<InspectorObject>&& parameters, Function<void (CommandResult&&)>&& completionHandler) |
| { |
| auto session = findSessionOrCompleteWithError(*parameters, completionHandler); |
| if (!session) |
| return; |
| |
| int windowWidth; |
| if (!parameters->getInteger(ASCIILiteral("width"), windowWidth)) { |
| completionHandler(CommandResult::fail(CommandResult::ErrorCode::InvalidArgument)); |
| return; |
| } |
| |
| int windowHeight; |
| if (!parameters->getInteger(ASCIILiteral("height"), windowHeight)) { |
| completionHandler(CommandResult::fail(CommandResult::ErrorCode::InvalidArgument)); |
| return; |
| } |
| |
| session->setWindowSize(windowWidth, windowHeight, WTFMove(completionHandler)); |
| } |
| |
| void WebDriverService::closeWindow(RefPtr<InspectorObject>&& parameters, Function<void (CommandResult&&)>&& completionHandler) |
| { |
| // §10.2 Close Window. |
| // https://www.w3.org/TR/webdriver/#close-window |
| if (auto session = findSessionOrCompleteWithError(*parameters, completionHandler)) |
| session->closeWindow(WTFMove(completionHandler)); |
| } |
| |
| void WebDriverService::switchToWindow(RefPtr<InspectorObject>&& parameters, Function<void (CommandResult&&)>&& completionHandler) |
| { |
| // §10.3 Switch To Window. |
| // https://www.w3.org/TR/webdriver/#switch-to-window |
| auto session = findSessionOrCompleteWithError(*parameters, completionHandler); |
| if (!session) |
| return; |
| |
| String handle; |
| if (!parameters->getString(ASCIILiteral("handle"), handle)) { |
| completionHandler(CommandResult::fail(CommandResult::ErrorCode::InvalidArgument)); |
| return; |
| } |
| |
| session->switchToWindow(handle, WTFMove(completionHandler)); |
| } |
| |
| void WebDriverService::getWindowHandles(RefPtr<InspectorObject>&& parameters, Function<void (CommandResult&&)>&& completionHandler) |
| { |
| // §10.4 Get Window Handles. |
| // https://www.w3.org/TR/webdriver/#get-window-handles |
| if (auto session = findSessionOrCompleteWithError(*parameters, completionHandler)) |
| session->getWindowHandles(WTFMove(completionHandler)); |
| } |
| |
| void WebDriverService::switchToFrame(RefPtr<InspectorObject>&& parameters, Function<void (CommandResult&&)>&& completionHandler) |
| { |
| // §10.5 Switch To Frame. |
| // https://www.w3.org/TR/webdriver/#switch-to-frame |
| auto session = findSessionOrCompleteWithError(*parameters, completionHandler); |
| if (!session) |
| return; |
| |
| RefPtr<InspectorValue> frameID; |
| if (!parameters->getValue(ASCIILiteral("id"), frameID)) { |
| completionHandler(CommandResult::fail(CommandResult::ErrorCode::InvalidArgument)); |
| return; |
| } |
| |
| 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 |
| 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) |
| { |
| String elementID; |
| if (!parameters.getString(ASCIILiteral("elementId"), elementID) || elementID.isEmpty()) { |
| completionHandler(CommandResult::fail(CommandResult::ErrorCode::InvalidArgument)); |
| return std::nullopt; |
| } |
| return elementID; |
| } |
| |
| static bool findStrategyAndSelectorOrCompleteWithError(InspectorObject& parameters, Function<void (CommandResult&&)>& completionHandler, String& strategy, String& selector) |
| { |
| if (!parameters.getString(ASCIILiteral("using"), strategy)) { |
| completionHandler(CommandResult::fail(CommandResult::ErrorCode::InvalidArgument)); |
| return false; |
| } |
| if (!parameters.getString(ASCIILiteral("value"), selector)) { |
| completionHandler(CommandResult::fail(CommandResult::ErrorCode::InvalidArgument)); |
| return false; |
| } |
| return true; |
| } |
| |
| void WebDriverService::findElement(RefPtr<InspectorObject>&& parameters, Function<void (CommandResult&&)>&& completionHandler) |
| { |
| // §12.2 Find Element. |
| // https://www.w3.org/TR/webdriver/#find-element |
| auto session = findSessionOrCompleteWithError(*parameters, completionHandler); |
| if (!session) |
| return; |
| |
| String strategy, selector; |
| if (!findStrategyAndSelectorOrCompleteWithError(*parameters, completionHandler, strategy, selector)) |
| return; |
| |
| 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) |
| { |
| // §12.3 Find Elements. |
| // https://www.w3.org/TR/webdriver/#find-elements |
| auto session = findSessionOrCompleteWithError(*parameters, completionHandler); |
| if (!session) |
| return; |
| |
| String strategy, selector; |
| if (!findStrategyAndSelectorOrCompleteWithError(*parameters, completionHandler, strategy, selector)) |
| return; |
| |
| 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) |
| { |
| // §12.4 Find Element From Element. |
| // https://www.w3.org/TR/webdriver/#find-element-from-element |
| auto session = findSessionOrCompleteWithError(*parameters, completionHandler); |
| if (!session) |
| return; |
| |
| auto elementID = findElementOrCompleteWithError(*parameters, completionHandler); |
| if (!elementID) |
| return; |
| |
| String strategy, selector; |
| if (!findStrategyAndSelectorOrCompleteWithError(*parameters, completionHandler, strategy, selector)) |
| return; |
| |
| session->findElements(strategy, selector, Session::FindElementsMode::Single, elementID.value(), WTFMove(completionHandler)); |
| } |
| |
| void WebDriverService::findElementsFromElement(RefPtr<InspectorObject>&& parameters, Function<void (CommandResult&&)>&& completionHandler) |
| { |
| // §12.5 Find Elements From Element. |
| // https://www.w3.org/TR/webdriver/#find-elements-from-element |
| auto session = findSessionOrCompleteWithError(*parameters, completionHandler); |
| if (!session) |
| return; |
| |
| auto elementID = findElementOrCompleteWithError(*parameters, completionHandler); |
| if (!elementID) |
| return; |
| |
| String strategy, selector; |
| if (!findStrategyAndSelectorOrCompleteWithError(*parameters, completionHandler, strategy, selector)) |
| return; |
| |
| session->findElements(strategy, selector, Session::FindElementsMode::Multiple, elementID.value(), WTFMove(completionHandler)); |
| } |
| |
| void WebDriverService::isElementSelected(RefPtr<InspectorObject>&& parameters, Function<void (CommandResult&&)>&& completionHandler) |
| { |
| // §13.1 Is Element Selected. |
| // https://www.w3.org/TR/webdriver/#is-element-selected |
| auto session = findSessionOrCompleteWithError(*parameters, completionHandler); |
| if (!session) |
| return; |
| |
| auto elementID = findElementOrCompleteWithError(*parameters, completionHandler); |
| if (!elementID) |
| return; |
| |
| session->isElementSelected(elementID.value(), WTFMove(completionHandler)); |
| } |
| |
| void WebDriverService::getElementAttribute(RefPtr<InspectorObject>&& parameters, Function<void (CommandResult&&)>&& completionHandler) |
| { |
| // §13.2 Get Element Attribute. |
| // https://www.w3.org/TR/webdriver/#get-element-attribute |
| auto session = findSessionOrCompleteWithError(*parameters, completionHandler); |
| if (!session) |
| return; |
| |
| auto elementID = findElementOrCompleteWithError(*parameters, completionHandler); |
| if (!elementID) |
| return; |
| |
| String attribute; |
| if (!parameters->getString(ASCIILiteral("name"), attribute)) { |
| completionHandler(CommandResult::fail(CommandResult::ErrorCode::InvalidArgument)); |
| return; |
| } |
| |
| session->getElementAttribute(elementID.value(), attribute, WTFMove(completionHandler)); |
| } |
| |
| void WebDriverService::getElementText(RefPtr<InspectorObject>&& parameters, Function<void (CommandResult&&)>&& completionHandler) |
| { |
| // §13.5 Get Element Text. |
| // https://www.w3.org/TR/webdriver/#get-element-text |
| auto session = findSessionOrCompleteWithError(*parameters, completionHandler); |
| if (!session) |
| return; |
| |
| auto elementID = findElementOrCompleteWithError(*parameters, completionHandler); |
| if (!elementID) |
| return; |
| |
| session->getElementText(elementID.value(), WTFMove(completionHandler)); |
| } |
| |
| void WebDriverService::getElementTagName(RefPtr<InspectorObject>&& parameters, Function<void (CommandResult&&)>&& completionHandler) |
| { |
| // §13.6 Get Element Tag Name. |
| // https://www.w3.org/TR/webdriver/#get-element-tag-name |
| auto session = findSessionOrCompleteWithError(*parameters, completionHandler); |
| if (!session) |
| return; |
| |
| auto elementID = findElementOrCompleteWithError(*parameters, completionHandler); |
| if (!elementID) |
| return; |
| |
| session->getElementTagName(elementID.value(), WTFMove(completionHandler)); |
| } |
| |
| void WebDriverService::getElementRect(RefPtr<InspectorObject>&& parameters, Function<void (CommandResult&&)>&& completionHandler) |
| { |
| // §13.7 Get Element Rect. |
| // https://www.w3.org/TR/webdriver/#get-element-rect |
| auto session = findSessionOrCompleteWithError(*parameters, completionHandler); |
| if (!session) |
| return; |
| |
| auto elementID = findElementOrCompleteWithError(*parameters, completionHandler); |
| if (!elementID) |
| return; |
| |
| session->getElementRect(elementID.value(), WTFMove(completionHandler)); |
| } |
| |
| void WebDriverService::isElementEnabled(RefPtr<InspectorObject>&& parameters, Function<void (CommandResult&&)>&& completionHandler) |
| { |
| // §13.8 Is Element Enabled. |
| // https://www.w3.org/TR/webdriver/#is-element-enabled |
| auto session = findSessionOrCompleteWithError(*parameters, completionHandler); |
| if (!session) |
| return; |
| |
| auto elementID = findElementOrCompleteWithError(*parameters, completionHandler); |
| if (!elementID) |
| return; |
| |
| session->isElementEnabled(elementID.value(), WTFMove(completionHandler)); |
| } |
| |
| void WebDriverService::isElementDisplayed(RefPtr<InspectorObject>&& parameters, Function<void (CommandResult&&)>&& completionHandler) |
| { |
| // §C. Element Displayedness. |
| // https://www.w3.org/TR/webdriver/#element-displayedness |
| auto session = findSessionOrCompleteWithError(*parameters, completionHandler); |
| if (!session) |
| return; |
| |
| auto elementID = findElementOrCompleteWithError(*parameters, completionHandler); |
| if (!elementID) |
| return; |
| |
| session->isElementDisplayed(elementID.value(), WTFMove(completionHandler)); |
| } |
| |
| void WebDriverService::elementClick(RefPtr<InspectorObject>&& parameters, Function<void (CommandResult&&)>&& completionHandler) |
| { |
| // §14.1 Element Click. |
| // https://www.w3.org/TR/webdriver/#element-click |
| auto session = findSessionOrCompleteWithError(*parameters, completionHandler); |
| if (!session) |
| return; |
| |
| auto elementID = findElementOrCompleteWithError(*parameters, completionHandler); |
| if (!elementID) |
| return; |
| |
| session->elementClick(elementID.value(), WTFMove(completionHandler)); |
| } |
| |
| void WebDriverService::elementClear(RefPtr<InspectorObject>&& parameters, Function<void (CommandResult&&)>&& completionHandler) |
| { |
| // §14.2 Element Clear. |
| // https://www.w3.org/TR/webdriver/#element-clear |
| auto session = findSessionOrCompleteWithError(*parameters, completionHandler); |
| if (!session) |
| return; |
| |
| auto elementID = findElementOrCompleteWithError(*parameters, completionHandler); |
| if (!elementID) |
| return; |
| |
| session->elementClear(elementID.value(), WTFMove(completionHandler)); |
| } |
| |
| void WebDriverService::elementSendKeys(RefPtr<InspectorObject>&& parameters, Function<void (CommandResult&&)>&& completionHandler) |
| { |
| // §14.3 Element Send Keys. |
| // https://www.w3.org/TR/webdriver/#element-send-keys |
| auto session = findSessionOrCompleteWithError(*parameters, completionHandler); |
| if (!session) |
| return; |
| |
| auto elementID = findElementOrCompleteWithError(*parameters, completionHandler); |
| if (!elementID) |
| return; |
| |
| RefPtr<InspectorArray> valueArray; |
| if (!parameters->getArray(ASCIILiteral("value"), valueArray)) { |
| completionHandler(CommandResult::fail(CommandResult::ErrorCode::InvalidArgument)); |
| return; |
| } |
| |
| unsigned valueArrayLength = valueArray->length(); |
| if (!valueArrayLength) { |
| completionHandler(CommandResult::fail(CommandResult::ErrorCode::InvalidArgument)); |
| return; |
| } |
| Vector<String> value; |
| value.reserveInitialCapacity(valueArrayLength); |
| for (unsigned i = 0; i < valueArrayLength; ++i) { |
| if (auto keyValue = valueArray->get(i)) { |
| String key; |
| if (keyValue->asString(key)) |
| value.uncheckedAppend(WTFMove(key)); |
| } |
| } |
| if (!value.size()) { |
| completionHandler(CommandResult::fail(CommandResult::ErrorCode::InvalidArgument)); |
| return; |
| } |
| |
| session->elementSendKeys(elementID.value(), WTFMove(value), WTFMove(completionHandler)); |
| } |
| |
| void WebDriverService::elementSubmit(RefPtr<InspectorObject>&& parameters, Function<void (CommandResult&&)>&& completionHandler) |
| { |
| auto session = findSessionOrCompleteWithError(*parameters, completionHandler); |
| if (!session) |
| return; |
| |
| auto elementID = findElementOrCompleteWithError(*parameters, completionHandler); |
| if (!elementID) |
| return; |
| |
| session->elementSubmit(elementID.value(), WTFMove(completionHandler)); |
| } |
| |
| static bool findScriptAndArgumentsOrCompleteWithError(InspectorObject& parameters, Function<void (CommandResult&&)>& completionHandler, String& script, RefPtr<InspectorArray>& arguments) |
| { |
| if (!parameters.getString(ASCIILiteral("script"), script)) { |
| completionHandler(CommandResult::fail(CommandResult::ErrorCode::InvalidArgument)); |
| return false; |
| } |
| if (!parameters.getArray(ASCIILiteral("args"), arguments)) { |
| completionHandler(CommandResult::fail(CommandResult::ErrorCode::InvalidArgument)); |
| return false; |
| } |
| return true; |
| } |
| |
| void WebDriverService::executeScript(RefPtr<InspectorObject>&& parameters, Function<void (CommandResult&&)>&& completionHandler) |
| { |
| // §15.2.1 Execute Script. |
| // https://www.w3.org/TR/webdriver/#execute-script |
| auto session = findSessionOrCompleteWithError(*parameters, completionHandler); |
| if (!session) |
| return; |
| |
| String script; |
| RefPtr<InspectorArray> arguments; |
| if (!findScriptAndArgumentsOrCompleteWithError(*parameters, completionHandler, script, arguments)) |
| return; |
| |
| 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) |
| { |
| // §15.2.2 Execute Async Script. |
| // https://www.w3.org/TR/webdriver/#execute-async-script |
| auto session = findSessionOrCompleteWithError(*parameters, completionHandler); |
| if (!session) |
| return; |
| |
| String script; |
| RefPtr<InspectorArray> arguments; |
| if (!findScriptAndArgumentsOrCompleteWithError(*parameters, completionHandler, script, arguments)) |
| return; |
| |
| 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 |