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 (unsigned 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))
101 void WebDriverService::quit()
103 m_server.disconnect();
104 RunLoop::main().stop();
107 const WebDriverService::Command WebDriverService::s_commands[] = {
108 { HTTPMethod::Post, "/session", &WebDriverService::newSession },
109 { HTTPMethod::Delete, "/session/$sessionId", &WebDriverService::deleteSession },
110 { HTTPMethod::Post, "/session/$sessionId/timeouts", &WebDriverService::setTimeouts },
112 { HTTPMethod::Post, "/session/$sessionId/url", &WebDriverService::go },
113 { HTTPMethod::Get, "/session/$sessionId/url", &WebDriverService::getCurrentURL },
114 { HTTPMethod::Post, "/session/$sessionId/back", &WebDriverService::back },
115 { HTTPMethod::Post, "/session/$sessionId/forward", &WebDriverService::forward },
116 { HTTPMethod::Post, "/session/$sessionId/refresh", &WebDriverService::refresh },
117 { HTTPMethod::Get, "/session/$sessionId/title", &WebDriverService::getTitle },
119 { HTTPMethod::Get, "/session/$sessionId/window", &WebDriverService::getWindowHandle },
120 { HTTPMethod::Delete, "/session/$sessionId/window", &WebDriverService::closeWindow },
121 { HTTPMethod::Post, "/session/$sessionId/window", &WebDriverService::switchToWindow },
122 { HTTPMethod::Get, "/session/$sessionId/window/handles", &WebDriverService::getWindowHandles },
123 { HTTPMethod::Post, "/session/$sessionId/frame", &WebDriverService::switchToFrame },
124 { HTTPMethod::Post, "/session/$sessionId/frame/parent", &WebDriverService::switchToParentFrame },
126 // FIXME: Not in the spec, but still used by Selenium.
127 { HTTPMethod::Get, "/session/$sessionId/window/position", &WebDriverService::getWindowPosition },
128 { HTTPMethod::Post, "/session/$sessionId/window/position", &WebDriverService::setWindowPosition },
129 { HTTPMethod::Get, "/session/$sessionId/window/size", &WebDriverService::getWindowSize },
130 { HTTPMethod::Post, "/session/$sessionId/window/size", &WebDriverService::setWindowSize },
132 { HTTPMethod::Post, "/session/$sessionId/element", &WebDriverService::findElement },
133 { HTTPMethod::Post, "/session/$sessionId/elements", &WebDriverService::findElements },
134 { HTTPMethod::Post, "/session/$sessionId/element/$elementId/element", &WebDriverService::findElementFromElement },
135 { HTTPMethod::Post, "/session/$sessionId/element/$elementId/elements", &WebDriverService::findElementsFromElement },
137 { HTTPMethod::Get, "/session/$sessionId/element/$elementId/selected", &WebDriverService::isElementSelected },
138 { HTTPMethod::Get, "/session/$sessionId/element/$elementId/attribute/$name", &WebDriverService::getElementAttribute },
139 { HTTPMethod::Get, "/session/$sessionId/element/$elementId/text", &WebDriverService::getElementText },
140 { HTTPMethod::Get, "/session/$sessionId/element/$elementId/name", &WebDriverService::getElementTagName },
141 { HTTPMethod::Get, "/session/$sessionId/element/$elementId/rect", &WebDriverService::getElementRect },
142 { HTTPMethod::Get, "/session/$sessionId/element/$elementId/enabled", &WebDriverService::isElementEnabled },
144 { HTTPMethod::Post, "/session/$sessionId/element/$elementId/click", &WebDriverService::elementClick },
145 { HTTPMethod::Post, "/session/$sessionId/element/$elementId/clear", &WebDriverService::elementClear },
146 { HTTPMethod::Post, "/session/$sessionId/element/$elementId/value", &WebDriverService::elementSendKeys },
148 // FIXME: Not in the spec, but still used by Selenium.
149 { HTTPMethod::Post, "/session/$sessionId/element/$elementId/submit", &WebDriverService::elementSubmit },
151 { HTTPMethod::Post, "/session/$sessionId/execute/sync", &WebDriverService::executeScript },
152 { HTTPMethod::Post, "/session/$sessionId/execute/async", &WebDriverService::executeAsyncScript },
154 { HTTPMethod::Get, "/session/$sessionId/element/$elementId/displayed", &WebDriverService::isElementDisplayed },
157 std::optional<WebDriverService::HTTPMethod> WebDriverService::toCommandHTTPMethod(const String& method)
159 auto lowerCaseMethod = method.convertToASCIILowercase();
160 if (lowerCaseMethod == "get")
161 return WebDriverService::HTTPMethod::Get;
162 if (lowerCaseMethod == "post" || lowerCaseMethod == "put")
163 return WebDriverService::HTTPMethod::Post;
164 if (lowerCaseMethod == "delete")
165 return WebDriverService::HTTPMethod::Delete;
170 bool WebDriverService::findCommand(const String& method, const String& path, CommandHandler* handler, HashMap<String, String>& parameters)
172 auto commandMethod = toCommandHTTPMethod(method);
176 size_t length = WTF_ARRAY_LENGTH(s_commands);
177 for (size_t i = 0; i < length; ++i) {
178 if (s_commands[i].method != *commandMethod)
181 Vector<String> pathTokens;
182 path.split("/", pathTokens);
183 Vector<String> commandTokens;
184 String::fromUTF8(s_commands[i].uriTemplate).split("/", commandTokens);
185 if (pathTokens.size() != commandTokens.size())
188 bool allMatched = true;
189 for (size_t j = 0; j < pathTokens.size() && allMatched; ++j) {
190 if (commandTokens[j][0] == '$')
191 parameters.set(commandTokens[j].substring(1), pathTokens[j]);
192 else if (commandTokens[j] != pathTokens[j])
197 *handler = s_commands[i].handler;
207 void WebDriverService::handleRequest(HTTPRequestHandler::Request&& request, Function<void (HTTPRequestHandler::Response&&)>&& replyHandler)
209 CommandHandler handler;
210 HashMap<String, String> parameters;
211 if (!findCommand(request.method, request.path, &handler, parameters)) {
212 sendResponse(WTFMove(replyHandler), CommandResult::fail(CommandResult::ErrorCode::UnknownCommand, String("Unknown command: " + request.path)));
216 RefPtr<InspectorObject> parametersObject;
217 if (request.dataLength) {
218 RefPtr<InspectorValue> messageValue;
219 if (!InspectorValue::parseJSON(String::fromUTF8(request.data, request.dataLength), messageValue)) {
220 sendResponse(WTFMove(replyHandler), CommandResult::fail(CommandResult::ErrorCode::InvalidArgument));
224 if (!messageValue->asObject(parametersObject)) {
225 sendResponse(WTFMove(replyHandler), CommandResult::fail(CommandResult::ErrorCode::InvalidArgument));
229 parametersObject = InspectorObject::create();
230 for (const auto& parameter : parameters)
231 parametersObject->setString(parameter.key, parameter.value);
233 ((*this).*handler)(WTFMove(parametersObject), [this, replyHandler = WTFMove(replyHandler)](CommandResult&& result) mutable {
234 sendResponse(WTFMove(replyHandler), WTFMove(result));
238 void WebDriverService::sendResponse(Function<void (HTTPRequestHandler::Response&&)>&& replyHandler, CommandResult&& result) const
240 RefPtr<InspectorObject> responseObject;
241 if (result.isError()) {
242 responseObject = InspectorObject::create();
243 responseObject->setString(ASCIILiteral("error"), result.errorString());
244 responseObject->setString(ASCIILiteral("message"), result.errorMessage().value_or(emptyString()));
245 responseObject->setString(ASCIILiteral("stacktrace"), emptyString());
247 responseObject = InspectorObject::create();
248 auto resultValue = result.result();
249 responseObject->setValue(ASCIILiteral("value"), resultValue ? WTFMove(resultValue) : InspectorValue::null());
251 replyHandler({ result.httpStatusCode(), responseObject->toJSONString().utf8(), ASCIILiteral("application/json; charset=utf-8") });
254 static std::optional<Timeouts> deserializeTimeouts(InspectorObject& timeoutsObject)
256 // §8.5 Set Timeouts.
257 // https://w3c.github.io/webdriver/webdriver-spec.html#dfn-deserialize-as-a-timeout
259 auto end = timeoutsObject.end();
260 for (auto it = timeoutsObject.begin(); it != end; ++it) {
261 if (it->key == "sessionId")
265 if (!it->value->asInteger(timeoutMS) || timeoutMS < 0 || timeoutMS > INT_MAX)
268 if (it->key == "script")
269 timeouts.script = Seconds::fromMilliseconds(timeoutMS);
270 else if (it->key == "pageLoad")
271 timeouts.pageLoad = Seconds::fromMilliseconds(timeoutMS);
272 else if (it->key == "implicit")
273 timeouts.implicit = Seconds::fromMilliseconds(timeoutMS);
280 static std::optional<PageLoadStrategy> deserializePageLoadStrategy(const String& pageLoadStrategy)
282 if (pageLoadStrategy == "none")
283 return PageLoadStrategy::None;
284 if (pageLoadStrategy == "normal")
285 return PageLoadStrategy::Normal;
286 if (pageLoadStrategy == "eager")
287 return PageLoadStrategy::Eager;
291 void WebDriverService::parseCapabilities(const InspectorObject& matchedCapabilities, Capabilities& capabilities) const
293 // Matched capabilities have already been validated.
294 bool acceptInsecureCerts;
295 if (matchedCapabilities.getBoolean(ASCIILiteral("acceptInsecureCerts"), acceptInsecureCerts))
296 capabilities.acceptInsecureCerts = acceptInsecureCerts;
298 if (matchedCapabilities.getString(ASCIILiteral("browserName"), browserName))
299 capabilities.browserName = browserName;
300 String browserVersion;
301 if (matchedCapabilities.getString(ASCIILiteral("browserVersion"), browserVersion))
302 capabilities.browserVersion = browserVersion;
304 if (matchedCapabilities.getString(ASCIILiteral("platformName"), platformName))
305 capabilities.platformName = platformName;
306 RefPtr<InspectorObject> timeouts;
307 if (matchedCapabilities.getObject(ASCIILiteral("timeouts"), timeouts))
308 capabilities.timeouts = deserializeTimeouts(*timeouts);
309 String pageLoadStrategy;
310 if (matchedCapabilities.getString(ASCIILiteral("pageLoadStrategy"), pageLoadStrategy))
311 capabilities.pageLoadStrategy = deserializePageLoadStrategy(pageLoadStrategy);
312 platformParseCapabilities(matchedCapabilities, capabilities);
315 RefPtr<Session> WebDriverService::findSessionOrCompleteWithError(InspectorObject& parameters, Function<void (CommandResult&&)>& completionHandler)
318 if (!parameters.getString(ASCIILiteral("sessionId"), sessionID)) {
319 completionHandler(CommandResult::fail(CommandResult::ErrorCode::InvalidArgument));
323 auto session = m_sessions.get(sessionID);
325 completionHandler(CommandResult::fail(CommandResult::ErrorCode::InvalidSessionID));
332 RefPtr<InspectorObject> WebDriverService::validatedCapabilities(const InspectorObject& capabilities) const
334 // §7.2 Processing Capabilities.
335 // https://w3c.github.io/webdriver/webdriver-spec.html#dfn-validate-capabilities
336 RefPtr<InspectorObject> result = InspectorObject::create();
337 auto end = capabilities.end();
338 for (auto it = capabilities.begin(); it != end; ++it) {
339 if (it->value->isNull())
340 result->setValue(it->key, RefPtr<InspectorValue>(it->value));
341 else if (it->key == "acceptInsecureCerts") {
342 bool acceptInsecureCerts;
343 if (!it->value->asBoolean(acceptInsecureCerts))
345 result->setBoolean(it->key, acceptInsecureCerts);
346 } else if (it->key == "browserName" || it->key == "browserVersion" || it->key == "platformName") {
348 if (!it->value->asString(stringValue))
350 result->setString(it->key, stringValue);
351 } else if (it->key == "pageLoadStrategy") {
352 String pageLoadStrategy;
353 if (!it->value->asString(pageLoadStrategy) || !deserializePageLoadStrategy(pageLoadStrategy))
355 result->setString(it->key, pageLoadStrategy);
356 } else if (it->key == "proxy") {
357 // FIXME: implement proxy support.
358 } else if (it->key == "timeouts") {
359 RefPtr<InspectorObject> timeouts;
360 if (!it->value->asObject(timeouts) || !deserializeTimeouts(*timeouts))
362 result->setValue(it->key, RefPtr<InspectorValue>(it->value));
363 } else if (it->key == "unhandledPromptBehavior") {
364 String unhandledPromptBehavior;
365 if (!it->value->asString(unhandledPromptBehavior))
367 // FIXME: implement prompts support.
368 result->setString(it->key, unhandledPromptBehavior);
369 } else if (it->key.find(":") != notFound) {
370 if (!platformValidateCapability(it->key, it->value))
372 result->setValue(it->key, RefPtr<InspectorValue>(it->value));
378 RefPtr<InspectorObject> WebDriverService::mergeCapabilities(const InspectorObject& requiredCapabilities, const InspectorObject& firstMatchCapabilities) const
380 // §7.2 Processing Capabilities.
381 // https://w3c.github.io/webdriver/webdriver-spec.html#dfn-merging-capabilities
382 RefPtr<InspectorObject> result = InspectorObject::create();
383 auto requiredEnd = requiredCapabilities.end();
384 for (auto it = requiredCapabilities.begin(); it != requiredEnd; ++it)
385 result->setValue(it->key, RefPtr<InspectorValue>(it->value));
387 auto firstMatchEnd = firstMatchCapabilities.end();
388 for (auto it = firstMatchCapabilities.begin(); it != firstMatchEnd; ++it) {
389 if (requiredCapabilities.find(it->key) != requiredEnd)
392 result->setValue(it->key, RefPtr<InspectorValue>(it->value));
398 std::optional<String> WebDriverService::matchCapabilities(const InspectorObject& mergedCapabilities) const
400 // §7.2 Processing Capabilities.
401 // https://w3c.github.io/webdriver/webdriver-spec.html#dfn-matching-capabilities
402 Capabilities matchedCapabilities = platformCapabilities();
404 // Some capabilities like browser name and version might need to launch the browser,
405 // so we only reject the known capabilities that don't match.
406 auto end = mergedCapabilities.end();
407 for (auto it = mergedCapabilities.begin(); it != end; ++it) {
408 if (it->key == "browserName" && matchedCapabilities.browserName) {
410 it->value->asString(browserName);
411 if (!equalIgnoringASCIICase(matchedCapabilities.browserName.value(), browserName))
412 return makeString("expected browserName ", matchedCapabilities.browserName.value(), " but got ", browserName);
413 } else if (it->key == "browserVersion" && matchedCapabilities.browserVersion) {
414 String browserVersion;
415 it->value->asString(browserVersion);
416 if (!platformCompareBrowserVersions(browserVersion, matchedCapabilities.browserVersion.value()))
417 return makeString("requested browserVersion is ", browserVersion, " but actual version is ", matchedCapabilities.browserVersion.value());
418 } else if (it->key == "platformName" && matchedCapabilities.platformName) {
420 it->value->asString(platformName);
421 if (!equalLettersIgnoringASCIICase(platformName, "any") && !equalIgnoringASCIICase(matchedCapabilities.platformName.value(), platformName))
422 return makeString("expected platformName ", matchedCapabilities.platformName.value(), " but got ", platformName);
423 } else if (it->key == "acceptInsecureCerts" && matchedCapabilities.acceptInsecureCerts) {
424 bool acceptInsecureCerts;
425 it->value->asBoolean(acceptInsecureCerts);
426 if (acceptInsecureCerts && !matchedCapabilities.acceptInsecureCerts.value())
427 return String("browser doesn't accept insecure TLS certificates");
428 } else if (it->key == "proxy") {
429 // FIXME: implement proxy support.
430 } else if (auto errorString = platformMatchCapability(it->key, it->value))
437 RefPtr<InspectorObject> WebDriverService::processCapabilities(const InspectorObject& parameters, Function<void (CommandResult&&)>& completionHandler) const
439 // §7.2 Processing Capabilities.
440 // https://w3c.github.io/webdriver/webdriver-spec.html#processing-capabilities
442 // 1. Let capabilities request be the result of getting the property "capabilities" from parameters.
443 RefPtr<InspectorObject> capabilitiesObject;
444 if (!parameters.getObject(ASCIILiteral("capabilities"), capabilitiesObject)) {
445 completionHandler(CommandResult::fail(CommandResult::ErrorCode::SessionNotCreated));
449 // 2. Let required capabilities be the result of getting the property "alwaysMatch" from capabilities request.
450 RefPtr<InspectorValue> requiredCapabilitiesValue;
451 RefPtr<InspectorObject> requiredCapabilities;
452 if (!capabilitiesObject->getValue(ASCIILiteral("alwaysMatch"), requiredCapabilitiesValue))
453 // 2.1. If required capabilities is undefined, set the value to an empty JSON Object.
454 requiredCapabilities = InspectorObject::create();
455 else if (!requiredCapabilitiesValue->asObject(requiredCapabilities)) {
456 completionHandler(CommandResult::fail(CommandResult::ErrorCode::SessionNotCreated, String("alwaysMatch is invalid in capabilities")));
460 // 2.2. Let required capabilities be the result of trying to validate capabilities with argument required capabilities.
461 requiredCapabilities = validatedCapabilities(*requiredCapabilities);
462 if (!requiredCapabilities) {
463 completionHandler(CommandResult::fail(CommandResult::ErrorCode::SessionNotCreated, String("Invalid alwaysMatch capabilities")));
467 // 3. Let all first match capabilities be the result of getting the property "firstMatch" from capabilities request.
468 RefPtr<InspectorValue> firstMatchCapabilitiesValue;
469 RefPtr<InspectorArray> firstMatchCapabilitiesList;
470 if (!capabilitiesObject->getValue(ASCIILiteral("firstMatch"), firstMatchCapabilitiesValue)) {
471 // 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.
472 firstMatchCapabilitiesList = InspectorArray::create();
473 firstMatchCapabilitiesList->pushObject(InspectorObject::create());
474 } else if (!firstMatchCapabilitiesValue->asArray(firstMatchCapabilitiesList)) {
475 // 3.2. If all first match capabilities is not a JSON List, return error with error code invalid argument.
476 completionHandler(CommandResult::fail(CommandResult::ErrorCode::SessionNotCreated, String("firstMatch is invalid in capabilities")));
480 // 4. Let validated first match capabilities be an empty JSON List.
481 Vector<RefPtr<InspectorObject>> validatedFirstMatchCapabilitiesList;
482 auto firstMatchCapabilitiesListLength = firstMatchCapabilitiesList->length();
483 validatedFirstMatchCapabilitiesList.reserveInitialCapacity(firstMatchCapabilitiesListLength);
484 // 5. For each first match capabilities corresponding to an indexed property in all first match capabilities.
485 for (unsigned i = 0; i < firstMatchCapabilitiesListLength; ++i) {
486 RefPtr<InspectorValue> firstMatchCapabilitiesValue = firstMatchCapabilitiesList->get(i);
487 RefPtr<InspectorObject> firstMatchCapabilities;
488 if (!firstMatchCapabilitiesValue->asObject(firstMatchCapabilities)) {
489 completionHandler(CommandResult::fail(CommandResult::ErrorCode::SessionNotCreated, String("Invalid capabilities found in firstMatch")));
492 // 5.1. Let validated capabilities be the result of trying to validate capabilities with argument first match capabilities.
493 firstMatchCapabilities = validatedCapabilities(*firstMatchCapabilities);
494 if (!firstMatchCapabilities) {
495 completionHandler(CommandResult::fail(CommandResult::ErrorCode::SessionNotCreated, String("Invalid firstMatch capabilities")));
498 // 5.2. Append validated capabilities to validated first match capabilities.
499 validatedFirstMatchCapabilitiesList.uncheckedAppend(WTFMove(firstMatchCapabilities));
502 // 6. For each first match capabilities corresponding to an indexed property in validated first match capabilities.
503 std::optional<String> errorString;
504 for (auto& validatedFirstMatchCapabilies : validatedFirstMatchCapabilitiesList) {
505 // 6.1. Let merged capabilities be the result of trying to merge capabilities with required capabilities and first match capabilities as arguments.
506 auto mergedCapabilities = mergeCapabilities(*requiredCapabilities, *validatedFirstMatchCapabilies);
507 if (!mergedCapabilities) {
508 completionHandler(CommandResult::fail(CommandResult::ErrorCode::SessionNotCreated, String("Same capability found in firstMatch and alwaysMatch")));
511 // 6.2. Let matched capabilities be the result of trying to match capabilities with merged capabilities as an argument.
512 errorString = matchCapabilities(*mergedCapabilities);
514 // 6.3. If matched capabilities is not null return matched capabilities.
515 return mergedCapabilities;
519 completionHandler(CommandResult::fail(CommandResult::ErrorCode::SessionNotCreated, errorString ? errorString.value() : String("Invalid capabilities")));
523 void WebDriverService::newSession(RefPtr<InspectorObject>&& parameters, Function<void (CommandResult&&)>&& completionHandler)
526 // https://www.w3.org/TR/webdriver/#new-session
527 auto matchedCapabilities = processCapabilities(*parameters, completionHandler);
528 if (!matchedCapabilities)
531 Capabilities capabilities;
532 parseCapabilities(*matchedCapabilities, capabilities);
533 auto sessionHost = std::make_unique<SessionHost>(WTFMove(capabilities));
534 auto* sessionHostPtr = sessionHost.get();
535 sessionHostPtr->connectToBrowser([this, sessionHost = WTFMove(sessionHost), completionHandler = WTFMove(completionHandler)](SessionHost::Succeeded succeeded) mutable {
536 if (succeeded == SessionHost::Succeeded::No) {
537 completionHandler(CommandResult::fail(CommandResult::ErrorCode::SessionNotCreated, String("Failed to connect to browser")));
541 RefPtr<Session> session = Session::create(WTFMove(sessionHost));
542 session->createTopLevelBrowsingContext([this, session, completionHandler = WTFMove(completionHandler)](CommandResult&& result) mutable {
543 if (result.isError()) {
544 completionHandler(CommandResult::fail(CommandResult::ErrorCode::SessionNotCreated, result.errorMessage()));
548 m_activeSession = session.get();
549 m_sessions.add(session->id(), session);
551 const auto& capabilities = session->capabilities();
552 if (capabilities.timeouts)
553 session->setTimeouts(capabilities.timeouts.value(), [](CommandResult&&) { });
555 RefPtr<InspectorObject> resultObject = InspectorObject::create();
556 resultObject->setString(ASCIILiteral("sessionId"), session->id());
557 RefPtr<InspectorObject> capabilitiesObject = InspectorObject::create();
558 if (capabilities.browserName)
559 capabilitiesObject->setString(ASCIILiteral("browserName"), capabilities.browserName.value());
560 if (capabilities.browserVersion)
561 capabilitiesObject->setString(ASCIILiteral("browserVersion"), capabilities.browserVersion.value());
562 if (capabilities.platformName)
563 capabilitiesObject->setString(ASCIILiteral("platformName"), capabilities.platformName.value());
564 if (capabilities.acceptInsecureCerts)
565 capabilitiesObject->setBoolean(ASCIILiteral("acceptInsecureCerts"), capabilities.acceptInsecureCerts.value());
566 if (capabilities.timeouts) {
567 RefPtr<InspectorObject> timeoutsObject = InspectorObject::create();
568 if (capabilities.timeouts.value().script)
569 timeoutsObject->setInteger(ASCIILiteral("script"), capabilities.timeouts.value().script.value().millisecondsAs<int>());
570 if (capabilities.timeouts.value().pageLoad)
571 timeoutsObject->setInteger(ASCIILiteral("pageLoad"), capabilities.timeouts.value().pageLoad.value().millisecondsAs<int>());
572 if (capabilities.timeouts.value().implicit)
573 timeoutsObject->setInteger(ASCIILiteral("implicit"), capabilities.timeouts.value().implicit.value().millisecondsAs<int>());
574 capabilitiesObject->setObject(ASCIILiteral("timeouts"), WTFMove(timeoutsObject));
576 if (capabilities.pageLoadStrategy) {
577 switch (capabilities.pageLoadStrategy.value()) {
578 case PageLoadStrategy::None:
579 capabilitiesObject->setString(ASCIILiteral("pageLoadStrategy"), "none");
581 case PageLoadStrategy::Normal:
582 capabilitiesObject->setString(ASCIILiteral("pageLoadStrategy"), "normal");
584 case PageLoadStrategy::Eager:
585 capabilitiesObject->setString(ASCIILiteral("pageLoadStrategy"), "eager");
589 resultObject->setObject(ASCIILiteral("value"), WTFMove(capabilitiesObject));
590 completionHandler(CommandResult::success(WTFMove(resultObject)));
595 void WebDriverService::deleteSession(RefPtr<InspectorObject>&& parameters, Function<void (CommandResult&&)>&& completionHandler)
597 // §8.2 Delete Session.
598 // https://www.w3.org/TR/webdriver/#delete-session
600 if (!parameters->getString(ASCIILiteral("sessionId"), sessionID)) {
601 completionHandler(CommandResult::fail(CommandResult::ErrorCode::InvalidArgument));
605 auto session = m_sessions.take(sessionID);
607 completionHandler(CommandResult::fail(CommandResult::ErrorCode::InvalidSessionID));
611 if (m_activeSession == session.get())
612 m_activeSession = nullptr;
614 session->close(WTFMove(completionHandler));
617 void WebDriverService::setTimeouts(RefPtr<InspectorObject>&& parameters, Function<void (CommandResult&&)>&& completionHandler)
619 // §8.5 Set Timeouts.
620 // https://www.w3.org/TR/webdriver/#set-timeouts
621 auto session = findSessionOrCompleteWithError(*parameters, completionHandler);
625 auto timeouts = deserializeTimeouts(*parameters);
627 completionHandler(CommandResult::fail(CommandResult::ErrorCode::InvalidArgument));
631 session->setTimeouts(timeouts.value(), WTFMove(completionHandler));
634 void WebDriverService::go(RefPtr<InspectorObject>&& parameters, Function<void (CommandResult&&)>&& completionHandler)
637 // https://www.w3.org/TR/webdriver/#go
638 auto session = findSessionOrCompleteWithError(*parameters, completionHandler);
643 if (!parameters->getString(ASCIILiteral("url"), url)) {
644 completionHandler(CommandResult::fail(CommandResult::ErrorCode::InvalidArgument));
648 session->waitForNavigationToComplete([session, url = WTFMove(url), completionHandler = WTFMove(completionHandler)](CommandResult&& result) mutable {
649 if (result.isError()) {
650 completionHandler(WTFMove(result));
653 session->go(url, WTFMove(completionHandler));
657 void WebDriverService::getCurrentURL(RefPtr<InspectorObject>&& parameters, Function<void (CommandResult&&)>&& completionHandler)
659 // §9.2 Get Current URL.
660 // https://www.w3.org/TR/webdriver/#get-current-url
661 auto session = findSessionOrCompleteWithError(*parameters, completionHandler);
665 session->waitForNavigationToComplete([session, completionHandler = WTFMove(completionHandler)](CommandResult&& result) mutable {
666 if (result.isError()) {
667 completionHandler(WTFMove(result));
670 session->getCurrentURL(WTFMove(completionHandler));
674 void WebDriverService::back(RefPtr<InspectorObject>&& parameters, Function<void (CommandResult&&)>&& completionHandler)
677 // https://www.w3.org/TR/webdriver/#back
678 auto session = findSessionOrCompleteWithError(*parameters, completionHandler);
682 session->waitForNavigationToComplete([session, completionHandler = WTFMove(completionHandler)](CommandResult&& result) mutable {
683 if (result.isError()) {
684 completionHandler(WTFMove(result));
687 session->back(WTFMove(completionHandler));
691 void WebDriverService::forward(RefPtr<InspectorObject>&& parameters, Function<void (CommandResult&&)>&& completionHandler)
694 // https://www.w3.org/TR/webdriver/#forward
695 auto session = findSessionOrCompleteWithError(*parameters, completionHandler);
699 session->waitForNavigationToComplete([session, completionHandler = WTFMove(completionHandler)](CommandResult&& result) mutable {
700 if (result.isError()) {
701 completionHandler(WTFMove(result));
704 session->forward(WTFMove(completionHandler));
708 void WebDriverService::refresh(RefPtr<InspectorObject>&& parameters, Function<void (CommandResult&&)>&& completionHandler)
711 // https://www.w3.org/TR/webdriver/#refresh
712 auto session = findSessionOrCompleteWithError(*parameters, completionHandler);
716 session->waitForNavigationToComplete([session, completionHandler = WTFMove(completionHandler)](CommandResult&& result) mutable {
717 if (result.isError()) {
718 completionHandler(WTFMove(result));
721 session->refresh(WTFMove(completionHandler));
725 void WebDriverService::getTitle(RefPtr<InspectorObject>&& parameters, Function<void (CommandResult&&)>&& completionHandler)
728 // https://www.w3.org/TR/webdriver/#get-title
729 auto session = findSessionOrCompleteWithError(*parameters, completionHandler);
733 session->waitForNavigationToComplete([session, completionHandler = WTFMove(completionHandler)](CommandResult&& result) mutable {
734 if (result.isError()) {
735 completionHandler(WTFMove(result));
738 session->getTitle(WTFMove(completionHandler));
742 void WebDriverService::getWindowHandle(RefPtr<InspectorObject>&& parameters, Function<void (CommandResult&&)>&& completionHandler)
744 // §10.1 Get Window Handle.
745 // https://www.w3.org/TR/webdriver/#get-window-handle
746 if (auto session = findSessionOrCompleteWithError(*parameters, completionHandler))
747 session->getWindowHandle(WTFMove(completionHandler));
750 void WebDriverService::getWindowPosition(RefPtr<InspectorObject>&& parameters, Function<void (CommandResult&&)>&& completionHandler)
752 if (auto session = findSessionOrCompleteWithError(*parameters, completionHandler))
753 session->getWindowPosition(WTFMove(completionHandler));
756 void WebDriverService::setWindowPosition(RefPtr<InspectorObject>&& parameters, Function<void (CommandResult&&)>&& completionHandler)
758 auto session = findSessionOrCompleteWithError(*parameters, completionHandler);
763 if (!parameters->getInteger(ASCIILiteral("x"), windowX)) {
764 completionHandler(CommandResult::fail(CommandResult::ErrorCode::InvalidArgument));
769 if (!parameters->getInteger(ASCIILiteral("y"), windowY)) {
770 completionHandler(CommandResult::fail(CommandResult::ErrorCode::InvalidArgument));
774 session->setWindowPosition(windowX, windowY, WTFMove(completionHandler));
777 void WebDriverService::getWindowSize(RefPtr<InspectorObject>&& parameters, Function<void (CommandResult&&)>&& completionHandler)
779 if (auto session = findSessionOrCompleteWithError(*parameters, completionHandler))
780 session->getWindowSize(WTFMove(completionHandler));
783 void WebDriverService::setWindowSize(RefPtr<InspectorObject>&& parameters, Function<void (CommandResult&&)>&& completionHandler)
785 auto session = findSessionOrCompleteWithError(*parameters, completionHandler);
790 if (!parameters->getInteger(ASCIILiteral("width"), windowWidth)) {
791 completionHandler(CommandResult::fail(CommandResult::ErrorCode::InvalidArgument));
796 if (!parameters->getInteger(ASCIILiteral("height"), windowHeight)) {
797 completionHandler(CommandResult::fail(CommandResult::ErrorCode::InvalidArgument));
801 session->setWindowSize(windowWidth, windowHeight, WTFMove(completionHandler));
804 void WebDriverService::closeWindow(RefPtr<InspectorObject>&& parameters, Function<void (CommandResult&&)>&& completionHandler)
806 // §10.2 Close Window.
807 // https://www.w3.org/TR/webdriver/#close-window
808 if (auto session = findSessionOrCompleteWithError(*parameters, completionHandler))
809 session->closeWindow(WTFMove(completionHandler));
812 void WebDriverService::switchToWindow(RefPtr<InspectorObject>&& parameters, Function<void (CommandResult&&)>&& completionHandler)
814 // §10.3 Switch To Window.
815 // https://www.w3.org/TR/webdriver/#switch-to-window
816 auto session = findSessionOrCompleteWithError(*parameters, completionHandler);
821 if (!parameters->getString(ASCIILiteral("handle"), handle)) {
822 completionHandler(CommandResult::fail(CommandResult::ErrorCode::InvalidArgument));
826 session->switchToWindow(handle, WTFMove(completionHandler));
829 void WebDriverService::getWindowHandles(RefPtr<InspectorObject>&& parameters, Function<void (CommandResult&&)>&& completionHandler)
831 // §10.4 Get Window Handles.
832 // https://www.w3.org/TR/webdriver/#get-window-handles
833 if (auto session = findSessionOrCompleteWithError(*parameters, completionHandler))
834 session->getWindowHandles(WTFMove(completionHandler));
837 void WebDriverService::switchToFrame(RefPtr<InspectorObject>&& parameters, Function<void (CommandResult&&)>&& completionHandler)
839 // §10.5 Switch To Frame.
840 // https://www.w3.org/TR/webdriver/#switch-to-frame
841 auto session = findSessionOrCompleteWithError(*parameters, completionHandler);
845 RefPtr<InspectorValue> frameID;
846 if (!parameters->getValue(ASCIILiteral("id"), frameID)) {
847 completionHandler(CommandResult::fail(CommandResult::ErrorCode::InvalidArgument));
851 session->waitForNavigationToComplete([session, frameID, completionHandler = WTFMove(completionHandler)](CommandResult&& result) mutable {
852 if (result.isError()) {
853 completionHandler(WTFMove(result));
856 session->switchToFrame(WTFMove(frameID), WTFMove(completionHandler));
860 void WebDriverService::switchToParentFrame(RefPtr<InspectorObject>&& parameters, Function<void (CommandResult&&)>&& completionHandler)
862 // §10.6 Switch To Parent Frame.
863 // https://www.w3.org/TR/webdriver/#switch-to-parent-frame
864 auto session = findSessionOrCompleteWithError(*parameters, completionHandler);
868 session->waitForNavigationToComplete([session, completionHandler = WTFMove(completionHandler)](CommandResult&& result) mutable {
869 if (result.isError()) {
870 completionHandler(WTFMove(result));
873 session->switchToParentFrame(WTFMove(completionHandler));
877 static std::optional<String> findElementOrCompleteWithError(InspectorObject& parameters, Function<void (CommandResult&&)>& completionHandler)
880 if (!parameters.getString(ASCIILiteral("elementId"), elementID) || elementID.isEmpty()) {
881 completionHandler(CommandResult::fail(CommandResult::ErrorCode::InvalidArgument));
887 static bool findStrategyAndSelectorOrCompleteWithError(InspectorObject& parameters, Function<void (CommandResult&&)>& completionHandler, String& strategy, String& selector)
889 if (!parameters.getString(ASCIILiteral("using"), strategy)) {
890 completionHandler(CommandResult::fail(CommandResult::ErrorCode::InvalidArgument));
893 if (!parameters.getString(ASCIILiteral("value"), selector)) {
894 completionHandler(CommandResult::fail(CommandResult::ErrorCode::InvalidArgument));
900 void WebDriverService::findElement(RefPtr<InspectorObject>&& parameters, Function<void (CommandResult&&)>&& completionHandler)
902 // §12.2 Find Element.
903 // https://www.w3.org/TR/webdriver/#find-element
904 auto session = findSessionOrCompleteWithError(*parameters, completionHandler);
908 String strategy, selector;
909 if (!findStrategyAndSelectorOrCompleteWithError(*parameters, completionHandler, strategy, selector))
912 session->waitForNavigationToComplete([session, strategy = WTFMove(strategy), selector = WTFMove(selector), completionHandler = WTFMove(completionHandler)](CommandResult&& result) mutable {
913 if (result.isError()) {
914 completionHandler(WTFMove(result));
917 session->findElements(strategy, selector, Session::FindElementsMode::Single, emptyString(), WTFMove(completionHandler));
921 void WebDriverService::findElements(RefPtr<InspectorObject>&& parameters, Function<void (CommandResult&&)>&& completionHandler)
923 // §12.3 Find Elements.
924 // https://www.w3.org/TR/webdriver/#find-elements
925 auto session = findSessionOrCompleteWithError(*parameters, completionHandler);
929 String strategy, selector;
930 if (!findStrategyAndSelectorOrCompleteWithError(*parameters, completionHandler, strategy, selector))
933 session->waitForNavigationToComplete([session, strategy = WTFMove(strategy), selector = WTFMove(selector), completionHandler = WTFMove(completionHandler)](CommandResult&& result) mutable {
934 if (result.isError()) {
935 completionHandler(WTFMove(result));
938 session->findElements(strategy, selector, Session::FindElementsMode::Multiple, emptyString(), WTFMove(completionHandler));
942 void WebDriverService::findElementFromElement(RefPtr<InspectorObject>&& parameters, Function<void (CommandResult&&)>&& completionHandler)
944 // §12.4 Find Element From Element.
945 // https://www.w3.org/TR/webdriver/#find-element-from-element
946 auto session = findSessionOrCompleteWithError(*parameters, completionHandler);
950 auto elementID = findElementOrCompleteWithError(*parameters, completionHandler);
954 String strategy, selector;
955 if (!findStrategyAndSelectorOrCompleteWithError(*parameters, completionHandler, strategy, selector))
958 session->findElements(strategy, selector, Session::FindElementsMode::Single, elementID.value(), WTFMove(completionHandler));
961 void WebDriverService::findElementsFromElement(RefPtr<InspectorObject>&& parameters, Function<void (CommandResult&&)>&& completionHandler)
963 // §12.5 Find Elements From Element.
964 // https://www.w3.org/TR/webdriver/#find-elements-from-element
965 auto session = findSessionOrCompleteWithError(*parameters, completionHandler);
969 auto elementID = findElementOrCompleteWithError(*parameters, completionHandler);
973 String strategy, selector;
974 if (!findStrategyAndSelectorOrCompleteWithError(*parameters, completionHandler, strategy, selector))
977 session->findElements(strategy, selector, Session::FindElementsMode::Multiple, elementID.value(), WTFMove(completionHandler));
980 void WebDriverService::isElementSelected(RefPtr<InspectorObject>&& parameters, Function<void (CommandResult&&)>&& completionHandler)
982 // §13.1 Is Element Selected.
983 // https://www.w3.org/TR/webdriver/#is-element-selected
984 auto session = findSessionOrCompleteWithError(*parameters, completionHandler);
988 auto elementID = findElementOrCompleteWithError(*parameters, completionHandler);
992 session->isElementSelected(elementID.value(), WTFMove(completionHandler));
995 void WebDriverService::getElementAttribute(RefPtr<InspectorObject>&& parameters, Function<void (CommandResult&&)>&& completionHandler)
997 // §13.2 Get Element Attribute.
998 // https://www.w3.org/TR/webdriver/#get-element-attribute
999 auto session = findSessionOrCompleteWithError(*parameters, completionHandler);
1003 auto elementID = findElementOrCompleteWithError(*parameters, completionHandler);
1008 if (!parameters->getString(ASCIILiteral("name"), attribute)) {
1009 completionHandler(CommandResult::fail(CommandResult::ErrorCode::InvalidArgument));
1013 session->getElementAttribute(elementID.value(), attribute, WTFMove(completionHandler));
1016 void WebDriverService::getElementText(RefPtr<InspectorObject>&& parameters, Function<void (CommandResult&&)>&& completionHandler)
1018 // §13.5 Get Element Text.
1019 // https://www.w3.org/TR/webdriver/#get-element-text
1020 auto session = findSessionOrCompleteWithError(*parameters, completionHandler);
1024 auto elementID = findElementOrCompleteWithError(*parameters, completionHandler);
1028 session->getElementText(elementID.value(), WTFMove(completionHandler));
1031 void WebDriverService::getElementTagName(RefPtr<InspectorObject>&& parameters, Function<void (CommandResult&&)>&& completionHandler)
1033 // §13.6 Get Element Tag Name.
1034 // https://www.w3.org/TR/webdriver/#get-element-tag-name
1035 auto session = findSessionOrCompleteWithError(*parameters, completionHandler);
1039 auto elementID = findElementOrCompleteWithError(*parameters, completionHandler);
1043 session->getElementTagName(elementID.value(), WTFMove(completionHandler));
1046 void WebDriverService::getElementRect(RefPtr<InspectorObject>&& parameters, Function<void (CommandResult&&)>&& completionHandler)
1048 // §13.7 Get Element Rect.
1049 // https://www.w3.org/TR/webdriver/#get-element-rect
1050 auto session = findSessionOrCompleteWithError(*parameters, completionHandler);
1054 auto elementID = findElementOrCompleteWithError(*parameters, completionHandler);
1058 session->getElementRect(elementID.value(), WTFMove(completionHandler));
1061 void WebDriverService::isElementEnabled(RefPtr<InspectorObject>&& parameters, Function<void (CommandResult&&)>&& completionHandler)
1063 // §13.8 Is Element Enabled.
1064 // https://www.w3.org/TR/webdriver/#is-element-enabled
1065 auto session = findSessionOrCompleteWithError(*parameters, completionHandler);
1069 auto elementID = findElementOrCompleteWithError(*parameters, completionHandler);
1073 session->isElementEnabled(elementID.value(), WTFMove(completionHandler));
1076 void WebDriverService::isElementDisplayed(RefPtr<InspectorObject>&& parameters, Function<void (CommandResult&&)>&& completionHandler)
1078 // §C. Element Displayedness.
1079 // https://www.w3.org/TR/webdriver/#element-displayedness
1080 auto session = findSessionOrCompleteWithError(*parameters, completionHandler);
1084 auto elementID = findElementOrCompleteWithError(*parameters, completionHandler);
1088 session->isElementDisplayed(elementID.value(), WTFMove(completionHandler));
1091 void WebDriverService::elementClick(RefPtr<InspectorObject>&& parameters, Function<void (CommandResult&&)>&& completionHandler)
1093 // §14.1 Element Click.
1094 // https://www.w3.org/TR/webdriver/#element-click
1095 auto session = findSessionOrCompleteWithError(*parameters, completionHandler);
1099 auto elementID = findElementOrCompleteWithError(*parameters, completionHandler);
1103 session->elementClick(elementID.value(), WTFMove(completionHandler));
1106 void WebDriverService::elementClear(RefPtr<InspectorObject>&& parameters, Function<void (CommandResult&&)>&& completionHandler)
1108 // §14.2 Element Clear.
1109 // https://www.w3.org/TR/webdriver/#element-clear
1110 auto session = findSessionOrCompleteWithError(*parameters, completionHandler);
1114 auto elementID = findElementOrCompleteWithError(*parameters, completionHandler);
1118 session->elementClear(elementID.value(), WTFMove(completionHandler));
1121 void WebDriverService::elementSendKeys(RefPtr<InspectorObject>&& parameters, Function<void (CommandResult&&)>&& completionHandler)
1123 // §14.3 Element Send Keys.
1124 // https://www.w3.org/TR/webdriver/#element-send-keys
1125 auto session = findSessionOrCompleteWithError(*parameters, completionHandler);
1129 auto elementID = findElementOrCompleteWithError(*parameters, completionHandler);
1133 RefPtr<InspectorArray> valueArray;
1134 if (!parameters->getArray(ASCIILiteral("value"), valueArray)) {
1135 completionHandler(CommandResult::fail(CommandResult::ErrorCode::InvalidArgument));
1139 unsigned valueArrayLength = valueArray->length();
1140 if (!valueArrayLength) {
1141 completionHandler(CommandResult::fail(CommandResult::ErrorCode::InvalidArgument));
1144 Vector<String> value;
1145 value.reserveInitialCapacity(valueArrayLength);
1146 for (unsigned i = 0; i < valueArrayLength; ++i) {
1147 if (auto keyValue = valueArray->get(i)) {
1149 if (keyValue->asString(key))
1150 value.uncheckedAppend(WTFMove(key));
1153 if (!value.size()) {
1154 completionHandler(CommandResult::fail(CommandResult::ErrorCode::InvalidArgument));
1158 session->elementSendKeys(elementID.value(), WTFMove(value), WTFMove(completionHandler));
1161 void WebDriverService::elementSubmit(RefPtr<InspectorObject>&& parameters, Function<void (CommandResult&&)>&& completionHandler)
1163 auto session = findSessionOrCompleteWithError(*parameters, completionHandler);
1167 auto elementID = findElementOrCompleteWithError(*parameters, completionHandler);
1171 session->elementSubmit(elementID.value(), WTFMove(completionHandler));
1174 static bool findScriptAndArgumentsOrCompleteWithError(InspectorObject& parameters, Function<void (CommandResult&&)>& completionHandler, String& script, RefPtr<InspectorArray>& arguments)
1176 if (!parameters.getString(ASCIILiteral("script"), script)) {
1177 completionHandler(CommandResult::fail(CommandResult::ErrorCode::InvalidArgument));
1180 if (!parameters.getArray(ASCIILiteral("args"), arguments)) {
1181 completionHandler(CommandResult::fail(CommandResult::ErrorCode::InvalidArgument));
1187 void WebDriverService::executeScript(RefPtr<InspectorObject>&& parameters, Function<void (CommandResult&&)>&& completionHandler)
1189 // §15.2.1 Execute Script.
1190 // https://www.w3.org/TR/webdriver/#execute-script
1191 auto session = findSessionOrCompleteWithError(*parameters, completionHandler);
1196 RefPtr<InspectorArray> arguments;
1197 if (!findScriptAndArgumentsOrCompleteWithError(*parameters, completionHandler, script, arguments))
1200 session->waitForNavigationToComplete([session, script = WTFMove(script), arguments = WTFMove(arguments), completionHandler = WTFMove(completionHandler)](CommandResult&& result) mutable {
1201 if (result.isError()) {
1202 completionHandler(WTFMove(result));
1205 session->executeScript(script, WTFMove(arguments), Session::ExecuteScriptMode::Sync, WTFMove(completionHandler));
1209 void WebDriverService::executeAsyncScript(RefPtr<InspectorObject>&& parameters, Function<void (CommandResult&&)>&& completionHandler)
1211 // §15.2.2 Execute Async Script.
1212 // https://www.w3.org/TR/webdriver/#execute-async-script
1213 auto session = findSessionOrCompleteWithError(*parameters, completionHandler);
1218 RefPtr<InspectorArray> arguments;
1219 if (!findScriptAndArgumentsOrCompleteWithError(*parameters, completionHandler, script, arguments))
1222 session->waitForNavigationToComplete([session, script = WTFMove(script), arguments = WTFMove(arguments), completionHandler = WTFMove(completionHandler)](CommandResult&& result) mutable {
1223 if (result.isError()) {
1224 completionHandler(WTFMove(result));
1227 session->executeScript(script, WTFMove(arguments), Session::ExecuteScriptMode::Async, WTFMove(completionHandler));
1231 } // namespace WebDriver