2 * Copyright (C) 2017 Igalia S.L.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
7 * 1. Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
13 * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
14 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
15 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
17 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
18 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
19 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
20 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
21 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
22 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
23 * THE POSSIBILITY OF SUCH DAMAGE.
27 #include "WebDriverService.h"
29 #include "Capabilities.h"
30 #include "CommandResult.h"
31 #include "SessionHost.h"
32 #include <inspector/InspectorValues.h>
33 #include <wtf/RunLoop.h>
34 #include <wtf/text/WTFString.h>
36 using namespace Inspector;
40 WebDriverService::WebDriverService()
45 static void printUsageStatement(const char* programName)
47 printf("Usage: %s options\n", programName);
48 printf(" -h, --help Prints this help message\n");
49 printf(" -p <port>, --port=<port> Port number the driver will use\n");
53 int WebDriverService::run(int argc, char** argv)
56 for (int i = 1 ; i < argc; ++i) {
57 const char* arg = argv[i];
58 if (!strcmp(arg, "-h") || !strcmp(arg, "--help")) {
59 printUsageStatement(argv[0]);
63 if (!strcmp(arg, "-p") && portString.isNull()) {
65 printUsageStatement(argv[0]);
72 static const unsigned portStrLength = strlen("--port=");
73 if (!strncmp(arg, "--port=", portStrLength) && portString.isNull()) {
74 portString = String(arg + portStrLength);
79 if (portString.isNull()) {
80 printUsageStatement(argv[0]);
85 unsigned port = portString.toUInt(&ok);
87 fprintf(stderr, "Invalid port %s provided\n", portString.ascii().data());
91 RunLoop::initializeMainRunLoop();
93 if (!m_server.listen(port))
98 m_server.disconnect();
103 const WebDriverService::Command WebDriverService::s_commands[] = {
104 { HTTPMethod::Post, "/session", &WebDriverService::newSession },
105 { HTTPMethod::Delete, "/session/$sessionId", &WebDriverService::deleteSession },
106 { HTTPMethod::Post, "/session/$sessionId/timeouts", &WebDriverService::setTimeouts },
108 { HTTPMethod::Post, "/session/$sessionId/url", &WebDriverService::go },
109 { HTTPMethod::Get, "/session/$sessionId/url", &WebDriverService::getCurrentURL },
110 { HTTPMethod::Post, "/session/$sessionId/back", &WebDriverService::back },
111 { HTTPMethod::Post, "/session/$sessionId/forward", &WebDriverService::forward },
112 { HTTPMethod::Post, "/session/$sessionId/refresh", &WebDriverService::refresh },
113 { HTTPMethod::Get, "/session/$sessionId/title", &WebDriverService::getTitle },
115 { HTTPMethod::Get, "/session/$sessionId/window", &WebDriverService::getWindowHandle },
116 { HTTPMethod::Delete, "/session/$sessionId/window", &WebDriverService::closeWindow },
117 { HTTPMethod::Post, "/session/$sessionId/window", &WebDriverService::switchToWindow },
118 { HTTPMethod::Get, "/session/$sessionId/window/handles", &WebDriverService::getWindowHandles },
119 { HTTPMethod::Post, "/session/$sessionId/frame", &WebDriverService::switchToFrame },
120 { HTTPMethod::Post, "/session/$sessionId/frame/parent", &WebDriverService::switchToParentFrame },
121 { HTTPMethod::Get, "/session/$sessionId/window/rect", &WebDriverService::getWindowRect },
122 { HTTPMethod::Post, "/session/$sessionId/window/rect", &WebDriverService::setWindowRect },
124 { HTTPMethod::Post, "/session/$sessionId/element", &WebDriverService::findElement },
125 { HTTPMethod::Post, "/session/$sessionId/elements", &WebDriverService::findElements },
126 { HTTPMethod::Post, "/session/$sessionId/element/$elementId/element", &WebDriverService::findElementFromElement },
127 { HTTPMethod::Post, "/session/$sessionId/element/$elementId/elements", &WebDriverService::findElementsFromElement },
129 { HTTPMethod::Get, "/session/$sessionId/element/$elementId/selected", &WebDriverService::isElementSelected },
130 { HTTPMethod::Get, "/session/$sessionId/element/$elementId/attribute/$name", &WebDriverService::getElementAttribute },
131 { HTTPMethod::Get, "/session/$sessionId/element/$elementId/text", &WebDriverService::getElementText },
132 { HTTPMethod::Get, "/session/$sessionId/element/$elementId/name", &WebDriverService::getElementTagName },
133 { HTTPMethod::Get, "/session/$sessionId/element/$elementId/rect", &WebDriverService::getElementRect },
134 { HTTPMethod::Get, "/session/$sessionId/element/$elementId/enabled", &WebDriverService::isElementEnabled },
136 { HTTPMethod::Post, "/session/$sessionId/element/$elementId/click", &WebDriverService::elementClick },
137 { HTTPMethod::Post, "/session/$sessionId/element/$elementId/clear", &WebDriverService::elementClear },
138 { HTTPMethod::Post, "/session/$sessionId/element/$elementId/value", &WebDriverService::elementSendKeys },
140 // FIXME: Not in the spec, but still used by Selenium.
141 { HTTPMethod::Post, "/session/$sessionId/element/$elementId/submit", &WebDriverService::elementSubmit },
143 { HTTPMethod::Post, "/session/$sessionId/execute/sync", &WebDriverService::executeScript },
144 { HTTPMethod::Post, "/session/$sessionId/execute/async", &WebDriverService::executeAsyncScript },
146 { HTTPMethod::Get, "/session/$sessionId/cookie", &WebDriverService::getAllCookies },
147 { HTTPMethod::Get, "/session/$sessionId/cookie/$name", &WebDriverService::getNamedCookie },
148 { HTTPMethod::Post, "/session/$sessionId/cookie", &WebDriverService::addCookie },
149 { HTTPMethod::Delete, "/session/$sessionId/cookie/$name", &WebDriverService::deleteCookie },
150 { HTTPMethod::Delete, "/session/$sessionId/cookie", &WebDriverService::deleteAllCookies },
152 { HTTPMethod::Post, "/session/$sessionId/alert/dismiss", &WebDriverService::dismissAlert },
153 { HTTPMethod::Post, "/session/$sessionId/alert/accept", &WebDriverService::acceptAlert },
154 { HTTPMethod::Get, "/session/$sessionId/alert/text", &WebDriverService::getAlertText },
155 { HTTPMethod::Post, "/session/$sessionId/alert/text", &WebDriverService::sendAlertText },
157 { HTTPMethod::Get, "/session/$sessionId/screenshot", &WebDriverService::takeScreenshot },
158 { HTTPMethod::Get, "/session/$sessionId/element/$elementId/screenshot", &WebDriverService::takeElementScreenshot },
161 { HTTPMethod::Get, "/session/$sessionId/element/$elementId/displayed", &WebDriverService::isElementDisplayed },
164 std::optional<WebDriverService::HTTPMethod> WebDriverService::toCommandHTTPMethod(const String& method)
166 auto lowerCaseMethod = method.convertToASCIILowercase();
167 if (lowerCaseMethod == "get")
168 return WebDriverService::HTTPMethod::Get;
169 if (lowerCaseMethod == "post" || lowerCaseMethod == "put")
170 return WebDriverService::HTTPMethod::Post;
171 if (lowerCaseMethod == "delete")
172 return WebDriverService::HTTPMethod::Delete;
177 bool WebDriverService::findCommand(const String& method, const String& path, CommandHandler* handler, HashMap<String, String>& parameters)
179 auto commandMethod = toCommandHTTPMethod(method);
183 size_t length = WTF_ARRAY_LENGTH(s_commands);
184 for (size_t i = 0; i < length; ++i) {
185 if (s_commands[i].method != *commandMethod)
188 Vector<String> pathTokens;
189 path.split("/", pathTokens);
190 Vector<String> commandTokens;
191 String::fromUTF8(s_commands[i].uriTemplate).split("/", commandTokens);
192 if (pathTokens.size() != commandTokens.size())
195 bool allMatched = true;
196 for (size_t j = 0; j < pathTokens.size() && allMatched; ++j) {
197 if (commandTokens[j][0] == '$')
198 parameters.set(commandTokens[j].substring(1), pathTokens[j]);
199 else if (commandTokens[j] != pathTokens[j])
204 *handler = s_commands[i].handler;
214 void WebDriverService::handleRequest(HTTPRequestHandler::Request&& request, Function<void (HTTPRequestHandler::Response&&)>&& replyHandler)
216 CommandHandler handler;
217 HashMap<String, String> parameters;
218 if (!findCommand(request.method, request.path, &handler, parameters)) {
219 sendResponse(WTFMove(replyHandler), CommandResult::fail(CommandResult::ErrorCode::UnknownCommand, String("Unknown command: " + request.path)));
223 RefPtr<InspectorObject> parametersObject;
224 if (request.dataLength) {
225 RefPtr<InspectorValue> messageValue;
226 if (!InspectorValue::parseJSON(String::fromUTF8(request.data, request.dataLength), messageValue)) {
227 sendResponse(WTFMove(replyHandler), CommandResult::fail(CommandResult::ErrorCode::InvalidArgument));
231 if (!messageValue->asObject(parametersObject)) {
232 sendResponse(WTFMove(replyHandler), CommandResult::fail(CommandResult::ErrorCode::InvalidArgument));
236 parametersObject = InspectorObject::create();
237 for (const auto& parameter : parameters)
238 parametersObject->setString(parameter.key, parameter.value);
240 ((*this).*handler)(WTFMove(parametersObject), [this, replyHandler = WTFMove(replyHandler)](CommandResult&& result) mutable {
241 sendResponse(WTFMove(replyHandler), WTFMove(result));
245 void WebDriverService::sendResponse(Function<void (HTTPRequestHandler::Response&&)>&& replyHandler, CommandResult&& result) const
247 // §6.3 Processing Model.
248 // https://w3c.github.io/webdriver/webdriver-spec.html#processing-model
249 RefPtr<InspectorValue> resultValue;
250 if (result.isError()) {
251 // When required to send an error.
252 // https://w3c.github.io/webdriver/webdriver-spec.html#dfn-send-an-error
253 // Let body be a new JSON Object initialised with the following properties: "error", "message", "stacktrace".
254 auto errorObject = InspectorObject::create();
255 errorObject->setString(ASCIILiteral("error"), result.errorString());
256 errorObject->setString(ASCIILiteral("message"), result.errorMessage().value_or(emptyString()));
257 errorObject->setString(ASCIILiteral("stacktrace"), emptyString());
258 // If the error data dictionary contains any entries, set the "data" field on body to a new JSON Object populated with the dictionary.
259 if (auto& additionalData = result.additionalErrorData())
260 errorObject->setObject(ASCIILiteral("data"), RefPtr<InspectorObject> { additionalData });
261 // Send a response with status and body as arguments.
262 resultValue = WTFMove(errorObject);
263 } else if (auto value = result.result())
264 resultValue = WTFMove(value);
266 resultValue = InspectorValue::null();
268 // When required to send a response.
269 // https://w3c.github.io/webdriver/webdriver-spec.html#dfn-send-a-response
270 RefPtr<InspectorObject> responseObject = InspectorObject::create();
271 responseObject->setValue(ASCIILiteral("value"), WTFMove(resultValue));
272 replyHandler({ result.httpStatusCode(), responseObject->toJSONString().utf8(), ASCIILiteral("application/json; charset=utf-8") });
275 static std::optional<Timeouts> deserializeTimeouts(InspectorObject& timeoutsObject)
277 // §8.5 Set Timeouts.
278 // https://w3c.github.io/webdriver/webdriver-spec.html#dfn-deserialize-as-a-timeout
280 auto end = timeoutsObject.end();
281 for (auto it = timeoutsObject.begin(); it != end; ++it) {
282 if (it->key == "sessionId")
286 if (!it->value->asInteger(timeoutMS) || timeoutMS < 0 || timeoutMS > INT_MAX)
289 if (it->key == "script")
290 timeouts.script = Seconds::fromMilliseconds(timeoutMS);
291 else if (it->key == "pageLoad")
292 timeouts.pageLoad = Seconds::fromMilliseconds(timeoutMS);
293 else if (it->key == "implicit")
294 timeouts.implicit = Seconds::fromMilliseconds(timeoutMS);
301 static std::optional<PageLoadStrategy> deserializePageLoadStrategy(const String& pageLoadStrategy)
303 if (pageLoadStrategy == "none")
304 return PageLoadStrategy::None;
305 if (pageLoadStrategy == "normal")
306 return PageLoadStrategy::Normal;
307 if (pageLoadStrategy == "eager")
308 return PageLoadStrategy::Eager;
312 static std::optional<UnhandledPromptBehavior> deserializeUnhandledPromptBehavior(const String& unhandledPromptBehavior)
314 if (unhandledPromptBehavior == "dismiss")
315 return UnhandledPromptBehavior::Dismiss;
316 if (unhandledPromptBehavior == "accept")
317 return UnhandledPromptBehavior::Accept;
318 if (unhandledPromptBehavior == "ignore")
319 return UnhandledPromptBehavior::Ignore;
323 void WebDriverService::parseCapabilities(const InspectorObject& matchedCapabilities, Capabilities& capabilities) const
325 // Matched capabilities have already been validated.
326 bool acceptInsecureCerts;
327 if (matchedCapabilities.getBoolean(ASCIILiteral("acceptInsecureCerts"), acceptInsecureCerts))
328 capabilities.acceptInsecureCerts = acceptInsecureCerts;
330 if (matchedCapabilities.getString(ASCIILiteral("browserName"), browserName))
331 capabilities.browserName = browserName;
332 String browserVersion;
333 if (matchedCapabilities.getString(ASCIILiteral("browserVersion"), browserVersion))
334 capabilities.browserVersion = browserVersion;
336 if (matchedCapabilities.getString(ASCIILiteral("platformName"), platformName))
337 capabilities.platformName = platformName;
338 RefPtr<InspectorObject> timeouts;
339 if (matchedCapabilities.getObject(ASCIILiteral("timeouts"), timeouts))
340 capabilities.timeouts = deserializeTimeouts(*timeouts);
341 String pageLoadStrategy;
342 if (matchedCapabilities.getString(ASCIILiteral("pageLoadStrategy"), pageLoadStrategy))
343 capabilities.pageLoadStrategy = deserializePageLoadStrategy(pageLoadStrategy);
344 String unhandledPromptBehavior;
345 if (matchedCapabilities.getString(ASCIILiteral("unhandledPromptBehavior"), unhandledPromptBehavior))
346 capabilities.unhandledPromptBehavior = deserializeUnhandledPromptBehavior(unhandledPromptBehavior);
347 platformParseCapabilities(matchedCapabilities, capabilities);
350 RefPtr<Session> WebDriverService::findSessionOrCompleteWithError(InspectorObject& parameters, Function<void (CommandResult&&)>& completionHandler)
353 if (!parameters.getString(ASCIILiteral("sessionId"), sessionID)) {
354 completionHandler(CommandResult::fail(CommandResult::ErrorCode::InvalidArgument));
358 auto session = m_sessions.get(sessionID);
360 completionHandler(CommandResult::fail(CommandResult::ErrorCode::InvalidSessionID));
367 RefPtr<InspectorObject> WebDriverService::validatedCapabilities(const InspectorObject& capabilities) const
369 // §7.2 Processing Capabilities.
370 // https://w3c.github.io/webdriver/webdriver-spec.html#dfn-validate-capabilities
371 RefPtr<InspectorObject> result = InspectorObject::create();
372 auto end = capabilities.end();
373 for (auto it = capabilities.begin(); it != end; ++it) {
374 if (it->value->isNull())
375 result->setValue(it->key, RefPtr<InspectorValue>(it->value));
376 else if (it->key == "acceptInsecureCerts") {
377 bool acceptInsecureCerts;
378 if (!it->value->asBoolean(acceptInsecureCerts))
380 result->setBoolean(it->key, acceptInsecureCerts);
381 } else if (it->key == "browserName" || it->key == "browserVersion" || it->key == "platformName") {
383 if (!it->value->asString(stringValue))
385 result->setString(it->key, stringValue);
386 } else if (it->key == "pageLoadStrategy") {
387 String pageLoadStrategy;
388 if (!it->value->asString(pageLoadStrategy) || !deserializePageLoadStrategy(pageLoadStrategy))
390 result->setString(it->key, pageLoadStrategy);
391 } else if (it->key == "proxy") {
392 // FIXME: implement proxy support.
393 } else if (it->key == "timeouts") {
394 RefPtr<InspectorObject> timeouts;
395 if (!it->value->asObject(timeouts) || !deserializeTimeouts(*timeouts))
397 result->setValue(it->key, RefPtr<InspectorValue>(it->value));
398 } else if (it->key == "unhandledPromptBehavior") {
399 String unhandledPromptBehavior;
400 if (!it->value->asString(unhandledPromptBehavior) || !deserializeUnhandledPromptBehavior(unhandledPromptBehavior))
402 result->setString(it->key, unhandledPromptBehavior);
403 } else if (it->key.find(":") != notFound) {
404 if (!platformValidateCapability(it->key, it->value))
406 result->setValue(it->key, RefPtr<InspectorValue>(it->value));
412 RefPtr<InspectorObject> WebDriverService::mergeCapabilities(const InspectorObject& requiredCapabilities, const InspectorObject& firstMatchCapabilities) const
414 // §7.2 Processing Capabilities.
415 // https://w3c.github.io/webdriver/webdriver-spec.html#dfn-merging-capabilities
416 RefPtr<InspectorObject> result = InspectorObject::create();
417 auto requiredEnd = requiredCapabilities.end();
418 for (auto it = requiredCapabilities.begin(); it != requiredEnd; ++it)
419 result->setValue(it->key, RefPtr<InspectorValue>(it->value));
421 auto firstMatchEnd = firstMatchCapabilities.end();
422 for (auto it = firstMatchCapabilities.begin(); it != firstMatchEnd; ++it) {
423 if (requiredCapabilities.find(it->key) != requiredEnd)
426 result->setValue(it->key, RefPtr<InspectorValue>(it->value));
432 std::optional<String> WebDriverService::matchCapabilities(const InspectorObject& mergedCapabilities) const
434 // §7.2 Processing Capabilities.
435 // https://w3c.github.io/webdriver/webdriver-spec.html#dfn-matching-capabilities
436 Capabilities matchedCapabilities = platformCapabilities();
438 // Some capabilities like browser name and version might need to launch the browser,
439 // so we only reject the known capabilities that don't match.
440 auto end = mergedCapabilities.end();
441 for (auto it = mergedCapabilities.begin(); it != end; ++it) {
442 if (it->key == "browserName" && matchedCapabilities.browserName) {
444 it->value->asString(browserName);
445 if (!equalIgnoringASCIICase(matchedCapabilities.browserName.value(), browserName))
446 return makeString("expected browserName ", matchedCapabilities.browserName.value(), " but got ", browserName);
447 } else if (it->key == "browserVersion" && matchedCapabilities.browserVersion) {
448 String browserVersion;
449 it->value->asString(browserVersion);
450 if (!platformCompareBrowserVersions(browserVersion, matchedCapabilities.browserVersion.value()))
451 return makeString("requested browserVersion is ", browserVersion, " but actual version is ", matchedCapabilities.browserVersion.value());
452 } else if (it->key == "platformName" && matchedCapabilities.platformName) {
454 it->value->asString(platformName);
455 if (!equalLettersIgnoringASCIICase(platformName, "any") && !equalIgnoringASCIICase(matchedCapabilities.platformName.value(), platformName))
456 return makeString("expected platformName ", matchedCapabilities.platformName.value(), " but got ", platformName);
457 } else if (it->key == "acceptInsecureCerts" && matchedCapabilities.acceptInsecureCerts) {
458 bool acceptInsecureCerts;
459 it->value->asBoolean(acceptInsecureCerts);
460 if (acceptInsecureCerts && !matchedCapabilities.acceptInsecureCerts.value())
461 return String("browser doesn't accept insecure TLS certificates");
462 } else if (it->key == "proxy") {
463 // FIXME: implement proxy support.
464 } else if (auto errorString = platformMatchCapability(it->key, it->value))
471 RefPtr<InspectorObject> WebDriverService::processCapabilities(const InspectorObject& parameters, Function<void (CommandResult&&)>& completionHandler) const
473 // §7.2 Processing Capabilities.
474 // https://w3c.github.io/webdriver/webdriver-spec.html#processing-capabilities
476 // 1. Let capabilities request be the result of getting the property "capabilities" from parameters.
477 RefPtr<InspectorObject> capabilitiesObject;
478 if (!parameters.getObject(ASCIILiteral("capabilities"), capabilitiesObject)) {
479 completionHandler(CommandResult::fail(CommandResult::ErrorCode::InvalidArgument));
483 // 2. Let required capabilities be the result of getting the property "alwaysMatch" from capabilities request.
484 RefPtr<InspectorValue> requiredCapabilitiesValue;
485 RefPtr<InspectorObject> requiredCapabilities;
486 if (!capabilitiesObject->getValue(ASCIILiteral("alwaysMatch"), requiredCapabilitiesValue))
487 // 2.1. If required capabilities is undefined, set the value to an empty JSON Object.
488 requiredCapabilities = InspectorObject::create();
489 else if (!requiredCapabilitiesValue->asObject(requiredCapabilities)) {
490 completionHandler(CommandResult::fail(CommandResult::ErrorCode::InvalidArgument, String("alwaysMatch is invalid in capabilities")));
494 // 2.2. Let required capabilities be the result of trying to validate capabilities with argument required capabilities.
495 requiredCapabilities = validatedCapabilities(*requiredCapabilities);
496 if (!requiredCapabilities) {
497 completionHandler(CommandResult::fail(CommandResult::ErrorCode::InvalidArgument, String("Invalid alwaysMatch capabilities")));
501 // 3. Let all first match capabilities be the result of getting the property "firstMatch" from capabilities request.
502 RefPtr<InspectorValue> firstMatchCapabilitiesValue;
503 RefPtr<InspectorArray> firstMatchCapabilitiesList;
504 if (!capabilitiesObject->getValue(ASCIILiteral("firstMatch"), firstMatchCapabilitiesValue)) {
505 // 3.1. If all first match capabilities is undefined, set the value to a JSON List with a single entry of an empty JSON Object.
506 firstMatchCapabilitiesList = InspectorArray::create();
507 firstMatchCapabilitiesList->pushObject(InspectorObject::create());
508 } else if (!firstMatchCapabilitiesValue->asArray(firstMatchCapabilitiesList)) {
509 // 3.2. If all first match capabilities is not a JSON List, return error with error code invalid argument.
510 completionHandler(CommandResult::fail(CommandResult::ErrorCode::InvalidArgument, String("firstMatch is invalid in capabilities")));
514 // 4. Let validated first match capabilities be an empty JSON List.
515 Vector<RefPtr<InspectorObject>> validatedFirstMatchCapabilitiesList;
516 auto firstMatchCapabilitiesListLength = firstMatchCapabilitiesList->length();
517 validatedFirstMatchCapabilitiesList.reserveInitialCapacity(firstMatchCapabilitiesListLength);
518 // 5. For each first match capabilities corresponding to an indexed property in all first match capabilities.
519 for (unsigned i = 0; i < firstMatchCapabilitiesListLength; ++i) {
520 RefPtr<InspectorValue> firstMatchCapabilitiesValue = firstMatchCapabilitiesList->get(i);
521 RefPtr<InspectorObject> firstMatchCapabilities;
522 if (!firstMatchCapabilitiesValue->asObject(firstMatchCapabilities)) {
523 completionHandler(CommandResult::fail(CommandResult::ErrorCode::InvalidArgument, String("Invalid capabilities found in firstMatch")));
526 // 5.1. Let validated capabilities be the result of trying to validate capabilities with argument first match capabilities.
527 firstMatchCapabilities = validatedCapabilities(*firstMatchCapabilities);
528 if (!firstMatchCapabilities) {
529 completionHandler(CommandResult::fail(CommandResult::ErrorCode::InvalidArgument, String("Invalid firstMatch capabilities")));
532 // 5.2. Append validated capabilities to validated first match capabilities.
533 validatedFirstMatchCapabilitiesList.uncheckedAppend(WTFMove(firstMatchCapabilities));
536 // 6. For each first match capabilities corresponding to an indexed property in validated first match capabilities.
537 std::optional<String> errorString;
538 for (auto& validatedFirstMatchCapabilies : validatedFirstMatchCapabilitiesList) {
539 // 6.1. Let merged capabilities be the result of trying to merge capabilities with required capabilities and first match capabilities as arguments.
540 auto mergedCapabilities = mergeCapabilities(*requiredCapabilities, *validatedFirstMatchCapabilies);
541 if (!mergedCapabilities) {
542 completionHandler(CommandResult::fail(CommandResult::ErrorCode::InvalidArgument, String("Same capability found in firstMatch and alwaysMatch")));
545 // 6.2. Let matched capabilities be the result of trying to match capabilities with merged capabilities as an argument.
546 errorString = matchCapabilities(*mergedCapabilities);
548 // 6.3. If matched capabilities is not null return matched capabilities.
549 return mergedCapabilities;
553 completionHandler(CommandResult::fail(CommandResult::ErrorCode::InvalidArgument, errorString ? errorString.value() : String("Invalid capabilities")));
557 void WebDriverService::newSession(RefPtr<InspectorObject>&& parameters, Function<void (CommandResult&&)>&& completionHandler)
560 // https://www.w3.org/TR/webdriver/#new-session
561 auto matchedCapabilities = processCapabilities(*parameters, completionHandler);
562 if (!matchedCapabilities)
565 Capabilities capabilities;
566 parseCapabilities(*matchedCapabilities, capabilities);
567 auto sessionHost = std::make_unique<SessionHost>(WTFMove(capabilities));
568 auto* sessionHostPtr = sessionHost.get();
569 sessionHostPtr->connectToBrowser([this, sessionHost = WTFMove(sessionHost), completionHandler = WTFMove(completionHandler)](SessionHost::Succeeded succeeded) mutable {
570 if (succeeded == SessionHost::Succeeded::No) {
571 completionHandler(CommandResult::fail(CommandResult::ErrorCode::SessionNotCreated, String("Failed to connect to browser")));
575 RefPtr<Session> session = Session::create(WTFMove(sessionHost));
576 session->createTopLevelBrowsingContext([this, session, completionHandler = WTFMove(completionHandler)](CommandResult&& result) mutable {
577 if (result.isError()) {
578 completionHandler(CommandResult::fail(CommandResult::ErrorCode::SessionNotCreated, result.errorMessage()));
582 m_activeSession = session.get();
583 m_sessions.add(session->id(), session);
585 const auto& capabilities = session->capabilities();
586 if (capabilities.timeouts)
587 session->setTimeouts(capabilities.timeouts.value(), [](CommandResult&&) { });
589 RefPtr<InspectorObject> resultObject = InspectorObject::create();
590 resultObject->setString(ASCIILiteral("sessionId"), session->id());
591 RefPtr<InspectorObject> capabilitiesObject = InspectorObject::create();
592 if (capabilities.browserName)
593 capabilitiesObject->setString(ASCIILiteral("browserName"), capabilities.browserName.value());
594 if (capabilities.browserVersion)
595 capabilitiesObject->setString(ASCIILiteral("browserVersion"), capabilities.browserVersion.value());
596 if (capabilities.platformName)
597 capabilitiesObject->setString(ASCIILiteral("platformName"), capabilities.platformName.value());
598 if (capabilities.acceptInsecureCerts)
599 capabilitiesObject->setBoolean(ASCIILiteral("acceptInsecureCerts"), capabilities.acceptInsecureCerts.value());
600 if (capabilities.timeouts) {
601 RefPtr<InspectorObject> timeoutsObject = InspectorObject::create();
602 if (capabilities.timeouts.value().script)
603 timeoutsObject->setInteger(ASCIILiteral("script"), capabilities.timeouts.value().script.value().millisecondsAs<int>());
604 if (capabilities.timeouts.value().pageLoad)
605 timeoutsObject->setInteger(ASCIILiteral("pageLoad"), capabilities.timeouts.value().pageLoad.value().millisecondsAs<int>());
606 if (capabilities.timeouts.value().implicit)
607 timeoutsObject->setInteger(ASCIILiteral("implicit"), capabilities.timeouts.value().implicit.value().millisecondsAs<int>());
608 capabilitiesObject->setObject(ASCIILiteral("timeouts"), WTFMove(timeoutsObject));
610 if (capabilities.pageLoadStrategy) {
611 switch (capabilities.pageLoadStrategy.value()) {
612 case PageLoadStrategy::None:
613 capabilitiesObject->setString(ASCIILiteral("pageLoadStrategy"), "none");
615 case PageLoadStrategy::Normal:
616 capabilitiesObject->setString(ASCIILiteral("pageLoadStrategy"), "normal");
618 case PageLoadStrategy::Eager:
619 capabilitiesObject->setString(ASCIILiteral("pageLoadStrategy"), "eager");
623 if (capabilities.unhandledPromptBehavior) {
624 switch (capabilities.unhandledPromptBehavior.value()) {
625 case UnhandledPromptBehavior::Dismiss:
626 capabilitiesObject->setString(ASCIILiteral("unhandledPromptBehavior"), "dismiss");
628 case UnhandledPromptBehavior::Accept:
629 capabilitiesObject->setString(ASCIILiteral("unhandledPromptBehavior"), "accept");
631 case UnhandledPromptBehavior::Ignore:
632 capabilitiesObject->setString(ASCIILiteral("unhandledPromptBehavior"), "ignore");
636 resultObject->setObject(ASCIILiteral("capabilities"), WTFMove(capabilitiesObject));
637 completionHandler(CommandResult::success(WTFMove(resultObject)));
642 void WebDriverService::deleteSession(RefPtr<InspectorObject>&& parameters, Function<void (CommandResult&&)>&& completionHandler)
644 // §8.2 Delete Session.
645 // https://www.w3.org/TR/webdriver/#delete-session
647 if (!parameters->getString(ASCIILiteral("sessionId"), sessionID)) {
648 completionHandler(CommandResult::fail(CommandResult::ErrorCode::InvalidArgument));
652 auto session = m_sessions.take(sessionID);
654 completionHandler(CommandResult::success());
658 session->close([this, session, completionHandler = WTFMove(completionHandler)](CommandResult&& result) mutable {
659 if (m_activeSession == session.get())
660 m_activeSession = nullptr;
661 completionHandler(WTFMove(result));
665 void WebDriverService::setTimeouts(RefPtr<InspectorObject>&& parameters, Function<void (CommandResult&&)>&& completionHandler)
667 // §8.5 Set Timeouts.
668 // https://www.w3.org/TR/webdriver/#set-timeouts
669 auto session = findSessionOrCompleteWithError(*parameters, completionHandler);
673 auto timeouts = deserializeTimeouts(*parameters);
675 completionHandler(CommandResult::fail(CommandResult::ErrorCode::InvalidArgument));
679 session->setTimeouts(timeouts.value(), WTFMove(completionHandler));
682 void WebDriverService::go(RefPtr<InspectorObject>&& parameters, Function<void (CommandResult&&)>&& completionHandler)
685 // https://www.w3.org/TR/webdriver/#go
686 auto session = findSessionOrCompleteWithError(*parameters, completionHandler);
691 if (!parameters->getString(ASCIILiteral("url"), url)) {
692 completionHandler(CommandResult::fail(CommandResult::ErrorCode::InvalidArgument));
696 session->waitForNavigationToComplete([session, url = WTFMove(url), completionHandler = WTFMove(completionHandler)](CommandResult&& result) mutable {
697 if (result.isError()) {
698 completionHandler(WTFMove(result));
701 session->go(url, WTFMove(completionHandler));
705 void WebDriverService::getCurrentURL(RefPtr<InspectorObject>&& parameters, Function<void (CommandResult&&)>&& completionHandler)
707 // §9.2 Get Current URL.
708 // https://www.w3.org/TR/webdriver/#get-current-url
709 auto session = findSessionOrCompleteWithError(*parameters, completionHandler);
713 session->waitForNavigationToComplete([session, completionHandler = WTFMove(completionHandler)](CommandResult&& result) mutable {
714 if (result.isError()) {
715 completionHandler(WTFMove(result));
718 session->getCurrentURL(WTFMove(completionHandler));
722 void WebDriverService::back(RefPtr<InspectorObject>&& parameters, Function<void (CommandResult&&)>&& completionHandler)
725 // https://www.w3.org/TR/webdriver/#back
726 auto session = findSessionOrCompleteWithError(*parameters, completionHandler);
730 session->waitForNavigationToComplete([session, completionHandler = WTFMove(completionHandler)](CommandResult&& result) mutable {
731 if (result.isError()) {
732 completionHandler(WTFMove(result));
735 session->back(WTFMove(completionHandler));
739 void WebDriverService::forward(RefPtr<InspectorObject>&& parameters, Function<void (CommandResult&&)>&& completionHandler)
742 // https://www.w3.org/TR/webdriver/#forward
743 auto session = findSessionOrCompleteWithError(*parameters, completionHandler);
747 session->waitForNavigationToComplete([session, completionHandler = WTFMove(completionHandler)](CommandResult&& result) mutable {
748 if (result.isError()) {
749 completionHandler(WTFMove(result));
752 session->forward(WTFMove(completionHandler));
756 void WebDriverService::refresh(RefPtr<InspectorObject>&& parameters, Function<void (CommandResult&&)>&& completionHandler)
759 // https://www.w3.org/TR/webdriver/#refresh
760 auto session = findSessionOrCompleteWithError(*parameters, completionHandler);
764 session->waitForNavigationToComplete([session, completionHandler = WTFMove(completionHandler)](CommandResult&& result) mutable {
765 if (result.isError()) {
766 completionHandler(WTFMove(result));
769 session->refresh(WTFMove(completionHandler));
773 void WebDriverService::getTitle(RefPtr<InspectorObject>&& parameters, Function<void (CommandResult&&)>&& completionHandler)
776 // https://www.w3.org/TR/webdriver/#get-title
777 auto session = findSessionOrCompleteWithError(*parameters, completionHandler);
781 session->waitForNavigationToComplete([session, completionHandler = WTFMove(completionHandler)](CommandResult&& result) mutable {
782 if (result.isError()) {
783 completionHandler(WTFMove(result));
786 session->getTitle(WTFMove(completionHandler));
790 void WebDriverService::getWindowHandle(RefPtr<InspectorObject>&& parameters, Function<void (CommandResult&&)>&& completionHandler)
792 // §10.1 Get Window Handle.
793 // https://www.w3.org/TR/webdriver/#get-window-handle
794 if (auto session = findSessionOrCompleteWithError(*parameters, completionHandler))
795 session->getWindowHandle(WTFMove(completionHandler));
798 void WebDriverService::getWindowRect(RefPtr<InspectorObject>&& parameters, Function<void (CommandResult&&)>&& completionHandler)
800 // §10.7.1 Get Window Rect.
801 // https://w3c.github.io/webdriver/webdriver-spec.html#get-window-rect
802 if (auto session = findSessionOrCompleteWithError(*parameters, completionHandler))
803 session->getWindowRect(WTFMove(completionHandler));
806 static std::optional<double> valueAsNumberInRange(const InspectorValue& value, double minAllowed = 0, double maxAllowed = INT_MAX)
809 if (!value.asDouble(number))
812 if (std::isnan(number) || std::isinf(number))
815 if (number < minAllowed || number > maxAllowed)
821 void WebDriverService::setWindowRect(RefPtr<InspectorObject>&& parameters, Function<void (CommandResult&&)>&& completionHandler)
823 // §10.7.2 Set Window Rect.
824 // https://w3c.github.io/webdriver/webdriver-spec.html#set-window-rect
825 RefPtr<InspectorValue> value;
826 std::optional<double> width;
827 if (parameters->getValue(ASCIILiteral("width"), value)) {
828 if (auto number = valueAsNumberInRange(*value))
830 else if (!value->isNull()) {
831 completionHandler(CommandResult::fail(CommandResult::ErrorCode::InvalidArgument));
835 std::optional<double> height;
836 if (parameters->getValue(ASCIILiteral("height"), value)) {
837 if (auto number = valueAsNumberInRange(*value))
839 else if (!value->isNull()) {
840 completionHandler(CommandResult::fail(CommandResult::ErrorCode::InvalidArgument));
844 std::optional<double> x;
845 if (parameters->getValue(ASCIILiteral("x"), value)) {
846 if (auto number = valueAsNumberInRange(*value, INT_MIN))
848 else if (!value->isNull()) {
849 completionHandler(CommandResult::fail(CommandResult::ErrorCode::InvalidArgument));
853 std::optional<double> y;
854 if (parameters->getValue(ASCIILiteral("y"), value)) {
855 if (auto number = valueAsNumberInRange(*value, INT_MIN))
857 else if (!value->isNull()) {
858 completionHandler(CommandResult::fail(CommandResult::ErrorCode::InvalidArgument));
863 // FIXME: If the remote end does not support the Set Window Rect command for the current
864 // top-level browsing context for any reason, return error with error code unsupported operation.
866 if (auto session = findSessionOrCompleteWithError(*parameters, completionHandler))
867 session->setWindowRect(x, y, width, height, WTFMove(completionHandler));
870 void WebDriverService::closeWindow(RefPtr<InspectorObject>&& parameters, Function<void (CommandResult&&)>&& completionHandler)
872 // §10.2 Close Window.
873 // https://www.w3.org/TR/webdriver/#close-window
874 auto session = findSessionOrCompleteWithError(*parameters, completionHandler);
878 session->closeWindow([this, session, completionHandler = WTFMove(completionHandler)](CommandResult&& result) mutable {
879 if (result.isError()) {
880 completionHandler(WTFMove(result));
884 RefPtr<InspectorArray> handles;
885 if (result.result()->asArray(handles) && !handles->length()) {
886 m_sessions.remove(session->id());
887 if (m_activeSession == session.get())
888 m_activeSession = nullptr;
890 completionHandler(WTFMove(result));
894 void WebDriverService::switchToWindow(RefPtr<InspectorObject>&& parameters, Function<void (CommandResult&&)>&& completionHandler)
896 // §10.3 Switch To Window.
897 // https://www.w3.org/TR/webdriver/#switch-to-window
898 auto session = findSessionOrCompleteWithError(*parameters, completionHandler);
903 if (!parameters->getString(ASCIILiteral("handle"), handle)) {
904 completionHandler(CommandResult::fail(CommandResult::ErrorCode::InvalidArgument));
908 session->switchToWindow(handle, WTFMove(completionHandler));
911 void WebDriverService::getWindowHandles(RefPtr<InspectorObject>&& parameters, Function<void (CommandResult&&)>&& completionHandler)
913 // §10.4 Get Window Handles.
914 // https://www.w3.org/TR/webdriver/#get-window-handles
915 if (auto session = findSessionOrCompleteWithError(*parameters, completionHandler))
916 session->getWindowHandles(WTFMove(completionHandler));
919 void WebDriverService::switchToFrame(RefPtr<InspectorObject>&& parameters, Function<void (CommandResult&&)>&& completionHandler)
921 // §10.5 Switch To Frame.
922 // https://www.w3.org/TR/webdriver/#switch-to-frame
923 auto session = findSessionOrCompleteWithError(*parameters, completionHandler);
927 RefPtr<InspectorValue> frameID;
928 if (!parameters->getValue(ASCIILiteral("id"), frameID)) {
929 completionHandler(CommandResult::fail(CommandResult::ErrorCode::InvalidArgument));
933 session->waitForNavigationToComplete([session, frameID, completionHandler = WTFMove(completionHandler)](CommandResult&& result) mutable {
934 if (result.isError()) {
935 completionHandler(WTFMove(result));
938 session->switchToFrame(WTFMove(frameID), WTFMove(completionHandler));
942 void WebDriverService::switchToParentFrame(RefPtr<InspectorObject>&& parameters, Function<void (CommandResult&&)>&& completionHandler)
944 // §10.6 Switch To Parent Frame.
945 // https://www.w3.org/TR/webdriver/#switch-to-parent-frame
946 auto session = findSessionOrCompleteWithError(*parameters, completionHandler);
950 session->waitForNavigationToComplete([session, completionHandler = WTFMove(completionHandler)](CommandResult&& result) mutable {
951 if (result.isError()) {
952 completionHandler(WTFMove(result));
955 session->switchToParentFrame(WTFMove(completionHandler));
959 static std::optional<String> findElementOrCompleteWithError(InspectorObject& parameters, Function<void (CommandResult&&)>& completionHandler)
962 if (!parameters.getString(ASCIILiteral("elementId"), elementID) || elementID.isEmpty()) {
963 completionHandler(CommandResult::fail(CommandResult::ErrorCode::InvalidArgument));
969 static bool findStrategyAndSelectorOrCompleteWithError(InspectorObject& parameters, Function<void (CommandResult&&)>& completionHandler, String& strategy, String& selector)
971 if (!parameters.getString(ASCIILiteral("using"), strategy)) {
972 completionHandler(CommandResult::fail(CommandResult::ErrorCode::InvalidArgument));
975 if (!parameters.getString(ASCIILiteral("value"), selector)) {
976 completionHandler(CommandResult::fail(CommandResult::ErrorCode::InvalidArgument));
982 void WebDriverService::findElement(RefPtr<InspectorObject>&& parameters, Function<void (CommandResult&&)>&& completionHandler)
984 // §12.2 Find Element.
985 // https://www.w3.org/TR/webdriver/#find-element
986 auto session = findSessionOrCompleteWithError(*parameters, completionHandler);
990 String strategy, selector;
991 if (!findStrategyAndSelectorOrCompleteWithError(*parameters, completionHandler, strategy, selector))
994 session->waitForNavigationToComplete([session, strategy = WTFMove(strategy), selector = WTFMove(selector), completionHandler = WTFMove(completionHandler)](CommandResult&& result) mutable {
995 if (result.isError()) {
996 completionHandler(WTFMove(result));
999 session->findElements(strategy, selector, Session::FindElementsMode::Single, emptyString(), WTFMove(completionHandler));
1003 void WebDriverService::findElements(RefPtr<InspectorObject>&& parameters, Function<void (CommandResult&&)>&& completionHandler)
1005 // §12.3 Find Elements.
1006 // https://www.w3.org/TR/webdriver/#find-elements
1007 auto session = findSessionOrCompleteWithError(*parameters, completionHandler);
1011 String strategy, selector;
1012 if (!findStrategyAndSelectorOrCompleteWithError(*parameters, completionHandler, strategy, selector))
1015 session->waitForNavigationToComplete([session, strategy = WTFMove(strategy), selector = WTFMove(selector), completionHandler = WTFMove(completionHandler)](CommandResult&& result) mutable {
1016 if (result.isError()) {
1017 completionHandler(WTFMove(result));
1020 session->findElements(strategy, selector, Session::FindElementsMode::Multiple, emptyString(), WTFMove(completionHandler));
1024 void WebDriverService::findElementFromElement(RefPtr<InspectorObject>&& parameters, Function<void (CommandResult&&)>&& completionHandler)
1026 // §12.4 Find Element From Element.
1027 // https://www.w3.org/TR/webdriver/#find-element-from-element
1028 auto session = findSessionOrCompleteWithError(*parameters, completionHandler);
1032 auto elementID = findElementOrCompleteWithError(*parameters, completionHandler);
1036 String strategy, selector;
1037 if (!findStrategyAndSelectorOrCompleteWithError(*parameters, completionHandler, strategy, selector))
1040 session->findElements(strategy, selector, Session::FindElementsMode::Single, elementID.value(), WTFMove(completionHandler));
1043 void WebDriverService::findElementsFromElement(RefPtr<InspectorObject>&& parameters, Function<void (CommandResult&&)>&& completionHandler)
1045 // §12.5 Find Elements From Element.
1046 // https://www.w3.org/TR/webdriver/#find-elements-from-element
1047 auto session = findSessionOrCompleteWithError(*parameters, completionHandler);
1051 auto elementID = findElementOrCompleteWithError(*parameters, completionHandler);
1055 String strategy, selector;
1056 if (!findStrategyAndSelectorOrCompleteWithError(*parameters, completionHandler, strategy, selector))
1059 session->findElements(strategy, selector, Session::FindElementsMode::Multiple, elementID.value(), WTFMove(completionHandler));
1062 void WebDriverService::isElementSelected(RefPtr<InspectorObject>&& parameters, Function<void (CommandResult&&)>&& completionHandler)
1064 // §13.1 Is Element Selected.
1065 // https://www.w3.org/TR/webdriver/#is-element-selected
1066 auto session = findSessionOrCompleteWithError(*parameters, completionHandler);
1070 auto elementID = findElementOrCompleteWithError(*parameters, completionHandler);
1074 session->isElementSelected(elementID.value(), WTFMove(completionHandler));
1077 void WebDriverService::getElementAttribute(RefPtr<InspectorObject>&& parameters, Function<void (CommandResult&&)>&& completionHandler)
1079 // §13.2 Get Element Attribute.
1080 // https://www.w3.org/TR/webdriver/#get-element-attribute
1081 auto session = findSessionOrCompleteWithError(*parameters, completionHandler);
1085 auto elementID = findElementOrCompleteWithError(*parameters, completionHandler);
1090 if (!parameters->getString(ASCIILiteral("name"), attribute)) {
1091 completionHandler(CommandResult::fail(CommandResult::ErrorCode::InvalidArgument));
1095 session->getElementAttribute(elementID.value(), attribute, WTFMove(completionHandler));
1098 void WebDriverService::getElementText(RefPtr<InspectorObject>&& parameters, Function<void (CommandResult&&)>&& completionHandler)
1100 // §13.5 Get Element Text.
1101 // https://www.w3.org/TR/webdriver/#get-element-text
1102 auto session = findSessionOrCompleteWithError(*parameters, completionHandler);
1106 auto elementID = findElementOrCompleteWithError(*parameters, completionHandler);
1110 session->getElementText(elementID.value(), WTFMove(completionHandler));
1113 void WebDriverService::getElementTagName(RefPtr<InspectorObject>&& parameters, Function<void (CommandResult&&)>&& completionHandler)
1115 // §13.6 Get Element Tag Name.
1116 // https://www.w3.org/TR/webdriver/#get-element-tag-name
1117 auto session = findSessionOrCompleteWithError(*parameters, completionHandler);
1121 auto elementID = findElementOrCompleteWithError(*parameters, completionHandler);
1125 session->getElementTagName(elementID.value(), WTFMove(completionHandler));
1128 void WebDriverService::getElementRect(RefPtr<InspectorObject>&& parameters, Function<void (CommandResult&&)>&& completionHandler)
1130 // §13.7 Get Element Rect.
1131 // https://www.w3.org/TR/webdriver/#get-element-rect
1132 auto session = findSessionOrCompleteWithError(*parameters, completionHandler);
1136 auto elementID = findElementOrCompleteWithError(*parameters, completionHandler);
1140 session->getElementRect(elementID.value(), WTFMove(completionHandler));
1143 void WebDriverService::isElementEnabled(RefPtr<InspectorObject>&& parameters, Function<void (CommandResult&&)>&& completionHandler)
1145 // §13.8 Is Element Enabled.
1146 // https://www.w3.org/TR/webdriver/#is-element-enabled
1147 auto session = findSessionOrCompleteWithError(*parameters, completionHandler);
1151 auto elementID = findElementOrCompleteWithError(*parameters, completionHandler);
1155 session->isElementEnabled(elementID.value(), WTFMove(completionHandler));
1158 void WebDriverService::isElementDisplayed(RefPtr<InspectorObject>&& parameters, Function<void (CommandResult&&)>&& completionHandler)
1160 // §C. Element Displayedness.
1161 // https://www.w3.org/TR/webdriver/#element-displayedness
1162 auto session = findSessionOrCompleteWithError(*parameters, completionHandler);
1166 auto elementID = findElementOrCompleteWithError(*parameters, completionHandler);
1170 session->isElementDisplayed(elementID.value(), WTFMove(completionHandler));
1173 void WebDriverService::elementClick(RefPtr<InspectorObject>&& parameters, Function<void (CommandResult&&)>&& completionHandler)
1175 // §14.1 Element Click.
1176 // https://www.w3.org/TR/webdriver/#element-click
1177 auto session = findSessionOrCompleteWithError(*parameters, completionHandler);
1181 auto elementID = findElementOrCompleteWithError(*parameters, completionHandler);
1185 session->elementClick(elementID.value(), WTFMove(completionHandler));
1188 void WebDriverService::elementClear(RefPtr<InspectorObject>&& parameters, Function<void (CommandResult&&)>&& completionHandler)
1190 // §14.2 Element Clear.
1191 // https://www.w3.org/TR/webdriver/#element-clear
1192 auto session = findSessionOrCompleteWithError(*parameters, completionHandler);
1196 auto elementID = findElementOrCompleteWithError(*parameters, completionHandler);
1200 session->elementClear(elementID.value(), WTFMove(completionHandler));
1203 void WebDriverService::elementSendKeys(RefPtr<InspectorObject>&& parameters, Function<void (CommandResult&&)>&& completionHandler)
1205 // §14.3 Element Send Keys.
1206 // https://www.w3.org/TR/webdriver/#element-send-keys
1207 auto session = findSessionOrCompleteWithError(*parameters, completionHandler);
1211 auto elementID = findElementOrCompleteWithError(*parameters, completionHandler);
1215 RefPtr<InspectorArray> valueArray;
1216 if (!parameters->getArray(ASCIILiteral("value"), valueArray)) {
1217 completionHandler(CommandResult::fail(CommandResult::ErrorCode::InvalidArgument));
1221 unsigned valueArrayLength = valueArray->length();
1222 if (!valueArrayLength) {
1223 completionHandler(CommandResult::fail(CommandResult::ErrorCode::InvalidArgument));
1226 Vector<String> value;
1227 value.reserveInitialCapacity(valueArrayLength);
1228 for (unsigned i = 0; i < valueArrayLength; ++i) {
1229 if (auto keyValue = valueArray->get(i)) {
1231 if (keyValue->asString(key))
1232 value.uncheckedAppend(WTFMove(key));
1235 if (!value.size()) {
1236 completionHandler(CommandResult::fail(CommandResult::ErrorCode::InvalidArgument));
1240 session->elementSendKeys(elementID.value(), WTFMove(value), WTFMove(completionHandler));
1243 void WebDriverService::elementSubmit(RefPtr<InspectorObject>&& parameters, Function<void (CommandResult&&)>&& completionHandler)
1245 auto session = findSessionOrCompleteWithError(*parameters, completionHandler);
1249 auto elementID = findElementOrCompleteWithError(*parameters, completionHandler);
1253 session->elementSubmit(elementID.value(), WTFMove(completionHandler));
1256 static bool findScriptAndArgumentsOrCompleteWithError(InspectorObject& parameters, Function<void (CommandResult&&)>& completionHandler, String& script, RefPtr<InspectorArray>& arguments)
1258 if (!parameters.getString(ASCIILiteral("script"), script)) {
1259 completionHandler(CommandResult::fail(CommandResult::ErrorCode::InvalidArgument));
1262 if (!parameters.getArray(ASCIILiteral("args"), arguments)) {
1263 completionHandler(CommandResult::fail(CommandResult::ErrorCode::InvalidArgument));
1269 void WebDriverService::executeScript(RefPtr<InspectorObject>&& parameters, Function<void (CommandResult&&)>&& completionHandler)
1271 // §15.2.1 Execute Script.
1272 // https://www.w3.org/TR/webdriver/#execute-script
1273 auto session = findSessionOrCompleteWithError(*parameters, completionHandler);
1278 RefPtr<InspectorArray> arguments;
1279 if (!findScriptAndArgumentsOrCompleteWithError(*parameters, completionHandler, script, arguments))
1282 session->waitForNavigationToComplete([session, script = WTFMove(script), arguments = WTFMove(arguments), completionHandler = WTFMove(completionHandler)](CommandResult&& result) mutable {
1283 if (result.isError()) {
1284 completionHandler(WTFMove(result));
1287 session->executeScript(script, WTFMove(arguments), Session::ExecuteScriptMode::Sync, WTFMove(completionHandler));
1291 void WebDriverService::executeAsyncScript(RefPtr<InspectorObject>&& parameters, Function<void (CommandResult&&)>&& completionHandler)
1293 // §15.2.2 Execute Async Script.
1294 // https://www.w3.org/TR/webdriver/#execute-async-script
1295 auto session = findSessionOrCompleteWithError(*parameters, completionHandler);
1300 RefPtr<InspectorArray> arguments;
1301 if (!findScriptAndArgumentsOrCompleteWithError(*parameters, completionHandler, script, arguments))
1304 session->waitForNavigationToComplete([session, script = WTFMove(script), arguments = WTFMove(arguments), completionHandler = WTFMove(completionHandler)](CommandResult&& result) mutable {
1305 if (result.isError()) {
1306 completionHandler(WTFMove(result));
1309 session->executeScript(script, WTFMove(arguments), Session::ExecuteScriptMode::Async, WTFMove(completionHandler));
1313 void WebDriverService::getAllCookies(RefPtr<InspectorObject>&& parameters, Function<void (CommandResult&&)>&& completionHandler)
1315 // §16.1 Get All Cookies.
1316 // https://w3c.github.io/webdriver/webdriver-spec.html#get-all-cookies
1317 auto session = findSessionOrCompleteWithError(*parameters, completionHandler);
1321 session->waitForNavigationToComplete([session, completionHandler = WTFMove(completionHandler)](CommandResult&& result) mutable {
1322 if (result.isError()) {
1323 completionHandler(WTFMove(result));
1326 session->getAllCookies(WTFMove(completionHandler));
1330 void WebDriverService::getNamedCookie(RefPtr<InspectorObject>&& parameters, Function<void (CommandResult&&)>&& completionHandler)
1332 // §16.2 Get Named Cookie.
1333 // https://w3c.github.io/webdriver/webdriver-spec.html#get-named-cookie
1334 auto session = findSessionOrCompleteWithError(*parameters, completionHandler);
1339 if (!parameters->getString(ASCIILiteral("name"), name)) {
1340 completionHandler(CommandResult::fail(CommandResult::ErrorCode::InvalidArgument));
1344 session->waitForNavigationToComplete([session, name = WTFMove(name), completionHandler = WTFMove(completionHandler)](CommandResult&& result) mutable {
1345 if (result.isError()) {
1346 completionHandler(WTFMove(result));
1349 session->getNamedCookie(name, WTFMove(completionHandler));
1353 static std::optional<Session::Cookie> deserializeCookie(InspectorObject& cookieObject)
1355 Session::Cookie cookie;
1356 if (!cookieObject.getString(ASCIILiteral("name"), cookie.name) || cookie.name.isEmpty())
1357 return std::nullopt;
1358 if (!cookieObject.getString(ASCIILiteral("value"), cookie.value) || cookie.value.isEmpty())
1359 return std::nullopt;
1361 RefPtr<InspectorValue> value;
1362 if (cookieObject.getValue(ASCIILiteral("path"), value)) {
1364 if (!value->asString(path))
1365 return std::nullopt;
1368 if (cookieObject.getValue(ASCIILiteral("domain"), value)) {
1370 if (!value->asString(domain))
1371 return std::nullopt;
1372 cookie.domain = domain;
1374 if (cookieObject.getValue(ASCIILiteral("secure"), value)) {
1376 if (!value->asBoolean(secure))
1377 return std::nullopt;
1378 cookie.secure = secure;
1380 if (cookieObject.getValue(ASCIILiteral("httpOnly"), value)) {
1382 if (!value->asBoolean(httpOnly))
1383 return std::nullopt;
1384 cookie.httpOnly = httpOnly;
1386 if (cookieObject.getValue(ASCIILiteral("expiry"), value)) {
1388 if (!value->asInteger(expiry) || expiry < 0 || expiry > INT_MAX)
1389 return std::nullopt;
1391 cookie.expiry = static_cast<unsigned>(expiry);
1397 void WebDriverService::addCookie(RefPtr<InspectorObject>&& parameters, Function<void (CommandResult&&)>&& completionHandler)
1399 // §16.3 Add Cookie.
1400 // https://w3c.github.io/webdriver/webdriver-spec.html#add-cookie
1401 auto session = findSessionOrCompleteWithError(*parameters, completionHandler);
1405 RefPtr<InspectorObject> cookieObject;
1406 if (!parameters->getObject(ASCIILiteral("cookie"), cookieObject)) {
1407 completionHandler(CommandResult::fail(CommandResult::ErrorCode::InvalidArgument));
1411 auto cookie = deserializeCookie(*cookieObject);
1413 completionHandler(CommandResult::fail(CommandResult::ErrorCode::InvalidArgument));
1417 session->waitForNavigationToComplete([session, cookie = WTFMove(cookie), completionHandler = WTFMove(completionHandler)](CommandResult&& result) mutable {
1418 if (result.isError()) {
1419 completionHandler(WTFMove(result));
1422 session->addCookie(cookie.value(), WTFMove(completionHandler));
1426 void WebDriverService::deleteCookie(RefPtr<InspectorObject>&& parameters, Function<void (CommandResult&&)>&& completionHandler)
1428 // §16.4 Delete Cookie.
1429 // https://w3c.github.io/webdriver/webdriver-spec.html#delete-cookie
1430 auto session = findSessionOrCompleteWithError(*parameters, completionHandler);
1435 if (!parameters->getString(ASCIILiteral("name"), name)) {
1436 completionHandler(CommandResult::fail(CommandResult::ErrorCode::InvalidArgument));
1440 session->waitForNavigationToComplete([session, name = WTFMove(name), completionHandler = WTFMove(completionHandler)](CommandResult&& result) mutable {
1441 if (result.isError()) {
1442 completionHandler(WTFMove(result));
1445 session->deleteCookie(name, WTFMove(completionHandler));
1449 void WebDriverService::deleteAllCookies(RefPtr<InspectorObject>&& parameters, Function<void (CommandResult&&)>&& completionHandler)
1451 // §16.5 Delete All Cookies.
1452 // https://w3c.github.io/webdriver/webdriver-spec.html#delete-all-cookies
1453 auto session = findSessionOrCompleteWithError(*parameters, completionHandler);
1457 session->waitForNavigationToComplete([session, completionHandler = WTFMove(completionHandler)](CommandResult&& result) mutable {
1458 if (result.isError()) {
1459 completionHandler(WTFMove(result));
1462 session->deleteAllCookies(WTFMove(completionHandler));
1466 void WebDriverService::dismissAlert(RefPtr<InspectorObject>&& parameters, Function<void (CommandResult&&)>&& completionHandler)
1468 // §18.1 Dismiss Alert.
1469 // https://w3c.github.io/webdriver/webdriver-spec.html#dismiss-alert
1470 auto session = findSessionOrCompleteWithError(*parameters, completionHandler);
1474 session->waitForNavigationToComplete([session, completionHandler = WTFMove(completionHandler)](CommandResult&& result) mutable {
1475 if (result.isError()) {
1476 completionHandler(WTFMove(result));
1479 session->dismissAlert(WTFMove(completionHandler));
1483 void WebDriverService::acceptAlert(RefPtr<InspectorObject>&& parameters, Function<void (CommandResult&&)>&& completionHandler)
1485 // §18.2 Accept Alert.
1486 // https://w3c.github.io/webdriver/webdriver-spec.html#accept-alert
1487 auto session = findSessionOrCompleteWithError(*parameters, completionHandler);
1491 session->waitForNavigationToComplete([session, completionHandler = WTFMove(completionHandler)](CommandResult&& result) mutable {
1492 if (result.isError()) {
1493 completionHandler(WTFMove(result));
1496 session->acceptAlert(WTFMove(completionHandler));
1500 void WebDriverService::getAlertText(RefPtr<InspectorObject>&& parameters, Function<void (CommandResult&&)>&& completionHandler)
1502 // §18.3 Get Alert Text.
1503 // https://w3c.github.io/webdriver/webdriver-spec.html#get-alert-text
1504 auto session = findSessionOrCompleteWithError(*parameters, completionHandler);
1508 session->waitForNavigationToComplete([session, completionHandler = WTFMove(completionHandler)](CommandResult&& result) mutable {
1509 if (result.isError()) {
1510 completionHandler(WTFMove(result));
1513 session->getAlertText(WTFMove(completionHandler));
1517 void WebDriverService::sendAlertText(RefPtr<InspectorObject>&& parameters, Function<void (CommandResult&&)>&& completionHandler)
1519 // §18.4 Send Alert Text.
1520 // https://w3c.github.io/webdriver/webdriver-spec.html#send-alert-text
1521 auto session = findSessionOrCompleteWithError(*parameters, completionHandler);
1526 if (!parameters->getString(ASCIILiteral("text"), text)) {
1527 completionHandler(CommandResult::fail(CommandResult::ErrorCode::InvalidArgument));
1531 session->waitForNavigationToComplete([session, text = WTFMove(text), completionHandler = WTFMove(completionHandler)](CommandResult&& result) mutable {
1532 if (result.isError()) {
1533 completionHandler(WTFMove(result));
1536 session->sendAlertText(text, WTFMove(completionHandler));
1540 void WebDriverService::takeScreenshot(RefPtr<InspectorObject>&& parameters, Function<void (CommandResult&&)>&& completionHandler)
1542 // §19.1 Take Screenshot.
1543 // https://w3c.github.io/webdriver/webdriver-spec.html#take-screenshot
1544 auto session = findSessionOrCompleteWithError(*parameters, completionHandler);
1548 session->waitForNavigationToComplete([session, completionHandler = WTFMove(completionHandler)](CommandResult&& result) mutable {
1549 if (result.isError()) {
1550 completionHandler(WTFMove(result));
1553 session->takeScreenshot(std::nullopt, std::nullopt, WTFMove(completionHandler));
1557 void WebDriverService::takeElementScreenshot(RefPtr<InspectorObject>&& parameters, Function<void (CommandResult&&)>&& completionHandler)
1559 // §19.2 Take Element Screenshot.
1560 // https://w3c.github.io/webdriver/webdriver-spec.html#take-element-screenshot
1561 auto session = findSessionOrCompleteWithError(*parameters, completionHandler);
1565 auto elementID = findElementOrCompleteWithError(*parameters, completionHandler);
1569 bool scrollIntoView = true;
1570 parameters->getBoolean(ASCIILiteral("scroll"), scrollIntoView);
1572 session->waitForNavigationToComplete([session, elementID, scrollIntoView, completionHandler = WTFMove(completionHandler)](CommandResult&& result) mutable {
1573 if (result.isError()) {
1574 completionHandler(WTFMove(result));
1577 session->takeScreenshot(elementID.value(), scrollIntoView, WTFMove(completionHandler));
1581 } // namespace WebDriver