40d24532b31f861a769168ea15d92cca7eb4ddf7
[WebKit-https.git] / Source / WebDriver / WebDriverService.cpp
1 /*
2  * Copyright (C) 2017 Igalia S.L.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
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.
12  *
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.
24  */
25
26 #include "config.h"
27 #include "WebDriverService.h"
28
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>
35
36 using namespace Inspector;
37
38 namespace WebDriver {
39
40 WebDriverService::WebDriverService()
41     : m_server(*this)
42 {
43 }
44
45 static void printUsageStatement(const char* programName)
46 {
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");
50     printf("\n");
51 }
52
53 int WebDriverService::run(int argc, char** argv)
54 {
55     String portString;
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]);
60             return EXIT_SUCCESS;
61         }
62
63         if (!strcmp(arg, "-p") && portString.isNull()) {
64             if (++i == argc) {
65                 printUsageStatement(argv[0]);
66                 return EXIT_FAILURE;
67             }
68             portString = argv[i];
69             continue;
70         }
71
72         static const unsigned portStrLength = strlen("--port=");
73         if (!strncmp(arg, "--port=", portStrLength) && portString.isNull()) {
74             portString = String(arg + portStrLength);
75             continue;
76         }
77     }
78
79     if (portString.isNull()) {
80         printUsageStatement(argv[0]);
81         return EXIT_FAILURE;
82     }
83
84     bool ok;
85     unsigned port = portString.toUInt(&ok);
86     if (!ok) {
87         fprintf(stderr, "Invalid port %s provided\n", portString.ascii().data());
88         return EXIT_FAILURE;
89     }
90
91     RunLoop::initializeMainRunLoop();
92
93     if (!m_server.listen(port))
94         return EXIT_FAILURE;
95
96     RunLoop::run();
97
98     return EXIT_SUCCESS;
99 }
100
101 void WebDriverService::quit()
102 {
103     m_server.disconnect();
104     RunLoop::main().stop();
105 }
106
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 },
111
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 },
118
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 },
125
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 },
131
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 },
136
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 },
143
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 },
147
148     // FIXME: Not in the spec, but still used by Selenium.
149     { HTTPMethod::Post, "/session/$sessionId/element/$elementId/submit", &WebDriverService::elementSubmit },
150
151     { HTTPMethod::Post, "/session/$sessionId/execute/sync", &WebDriverService::executeScript },
152     { HTTPMethod::Post, "/session/$sessionId/execute/async", &WebDriverService::executeAsyncScript },
153
154     { HTTPMethod::Get, "/session/$sessionId/element/$elementId/displayed", &WebDriverService::isElementDisplayed },
155 };
156
157 std::optional<WebDriverService::HTTPMethod> WebDriverService::toCommandHTTPMethod(const String& method)
158 {
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;
166
167     return std::nullopt;
168 }
169
170 bool WebDriverService::findCommand(const String& method, const String& path, CommandHandler* handler, HashMap<String, String>& parameters)
171 {
172     auto commandMethod = toCommandHTTPMethod(method);
173     if (!commandMethod)
174         return false;
175
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)
179             continue;
180
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())
186             continue;
187
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])
193                 allMatched = false;
194         }
195
196         if (allMatched) {
197             *handler = s_commands[i].handler;
198             return true;
199         }
200
201         parameters.clear();
202     }
203
204     return false;
205 }
206
207 void WebDriverService::handleRequest(HTTPRequestHandler::Request&& request, Function<void (HTTPRequestHandler::Response&&)>&& replyHandler)
208 {
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)));
213         return;
214     }
215
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));
221             return;
222         }
223
224         if (!messageValue->asObject(parametersObject)) {
225             sendResponse(WTFMove(replyHandler), CommandResult::fail(CommandResult::ErrorCode::InvalidArgument));
226             return;
227         }
228     } else
229         parametersObject = InspectorObject::create();
230     for (const auto& parameter : parameters)
231         parametersObject->setString(parameter.key, parameter.value);
232
233     ((*this).*handler)(WTFMove(parametersObject), [this, replyHandler = WTFMove(replyHandler)](CommandResult&& result) mutable {
234         sendResponse(WTFMove(replyHandler), WTFMove(result));
235     });
236 }
237
238 void WebDriverService::sendResponse(Function<void (HTTPRequestHandler::Response&&)>&& replyHandler, CommandResult&& result) const
239 {
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());
246     } else {
247         responseObject = InspectorObject::create();
248         auto resultValue = result.result();
249         responseObject->setValue(ASCIILiteral("value"), resultValue ? WTFMove(resultValue) : InspectorValue::null());
250     }
251     replyHandler({ result.httpStatusCode(), responseObject->toJSONString().utf8(), ASCIILiteral("application/json; charset=utf-8") });
252 }
253
254 static std::optional<Timeouts> deserializeTimeouts(InspectorObject& timeoutsObject)
255 {
256     // §8.5 Set Timeouts.
257     // https://w3c.github.io/webdriver/webdriver-spec.html#dfn-deserialize-as-a-timeout
258     Timeouts timeouts;
259     auto end = timeoutsObject.end();
260     for (auto it = timeoutsObject.begin(); it != end; ++it) {
261         if (it->key == "sessionId")
262             continue;
263
264         int timeoutMS;
265         if (!it->value->asInteger(timeoutMS) || timeoutMS < 0 || timeoutMS > INT_MAX)
266             return std::nullopt;
267
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);
274         else
275             return std::nullopt;
276     }
277     return timeouts;
278 }
279
280 void WebDriverService::parseCapabilities(const InspectorObject& matchedCapabilities, Capabilities& capabilities) const
281 {
282     // Matched capabilities have already been validated.
283     bool acceptInsecureCerts;
284     if (matchedCapabilities.getBoolean(ASCIILiteral("acceptInsecureCerts"), acceptInsecureCerts))
285         capabilities.acceptInsecureCerts = acceptInsecureCerts;
286     String browserName;
287     if (matchedCapabilities.getString(ASCIILiteral("browserName"), browserName))
288         capabilities.browserName = browserName;
289     String browserVersion;
290     if (matchedCapabilities.getString(ASCIILiteral("browserVersion"), browserVersion))
291         capabilities.browserVersion = browserVersion;
292     String platformName;
293     if (matchedCapabilities.getString(ASCIILiteral("platformName"), platformName))
294         capabilities.platformName = platformName;
295     RefPtr<InspectorObject> timeouts;
296     if (matchedCapabilities.getObject(ASCIILiteral("timeouts"), timeouts))
297         capabilities.timeouts = deserializeTimeouts(*timeouts);
298     platformParseCapabilities(matchedCapabilities, capabilities);
299 }
300
301 RefPtr<Session> WebDriverService::findSessionOrCompleteWithError(InspectorObject& parameters, Function<void (CommandResult&&)>& completionHandler)
302 {
303     String sessionID;
304     if (!parameters.getString(ASCIILiteral("sessionId"), sessionID)) {
305         completionHandler(CommandResult::fail(CommandResult::ErrorCode::InvalidArgument));
306         return nullptr;
307     }
308
309     auto session = m_sessions.get(sessionID);
310     if (!session) {
311         completionHandler(CommandResult::fail(CommandResult::ErrorCode::InvalidSessionID));
312         return nullptr;
313     }
314
315     return session;
316 }
317
318 RefPtr<InspectorObject> WebDriverService::validatedCapabilities(const InspectorObject& capabilities) const
319 {
320     // §7.2 Processing Capabilities.
321     // https://w3c.github.io/webdriver/webdriver-spec.html#dfn-validate-capabilities
322     RefPtr<InspectorObject> result = InspectorObject::create();
323     auto end = capabilities.end();
324     for (auto it = capabilities.begin(); it != end; ++it) {
325         if (it->value->isNull())
326             result->setValue(it->key, RefPtr<InspectorValue>(it->value));
327         else if (it->key == "acceptInsecureCerts") {
328             bool acceptInsecureCerts;
329             if (!it->value->asBoolean(acceptInsecureCerts))
330                 return nullptr;
331             result->setBoolean(it->key, acceptInsecureCerts);
332         } else if (it->key == "browserName" || it->key == "browserVersion" || it->key == "platformName") {
333             String stringValue;
334             if (!it->value->asString(stringValue))
335                 return nullptr;
336             result->setString(it->key, stringValue);
337         } else if (it->key == "pageLoadStrategy") {
338             String pageLoadStrategy;
339             if (!it->value->asString(pageLoadStrategy))
340                 return nullptr;
341             // FIXME: implement pageLoadStrategy.
342             result->setString(it->key, pageLoadStrategy);
343         } else if (it->key == "proxy") {
344             // FIXME: implement proxy support.
345         } else if (it->key == "timeouts") {
346             RefPtr<InspectorObject> timeouts;
347             if (!it->value->asObject(timeouts) || !deserializeTimeouts(*timeouts))
348                 return nullptr;
349             result->setValue(it->key, RefPtr<InspectorValue>(it->value));
350         } else if (it->key == "unhandledPromptBehavior") {
351             String unhandledPromptBehavior;
352             if (!it->value->asString(unhandledPromptBehavior))
353                 return nullptr;
354             // FIXME: implement prompts support.
355             result->setString(it->key, unhandledPromptBehavior);
356         } else if (it->key.find(":") != notFound) {
357             if (!platformValidateCapability(it->key, it->value))
358                 return nullptr;
359             result->setValue(it->key, RefPtr<InspectorValue>(it->value));
360         }
361     }
362     return result;
363 }
364
365 RefPtr<InspectorObject> WebDriverService::mergeCapabilities(const InspectorObject& requiredCapabilities, const InspectorObject& firstMatchCapabilities) const
366 {
367     // §7.2 Processing Capabilities.
368     // https://w3c.github.io/webdriver/webdriver-spec.html#dfn-merging-capabilities
369     RefPtr<InspectorObject> result = InspectorObject::create();
370     auto requiredEnd = requiredCapabilities.end();
371     for (auto it = requiredCapabilities.begin(); it != requiredEnd; ++it)
372         result->setValue(it->key, RefPtr<InspectorValue>(it->value));
373
374     auto firstMatchEnd = firstMatchCapabilities.end();
375     for (auto it = firstMatchCapabilities.begin(); it != firstMatchEnd; ++it) {
376         if (requiredCapabilities.find(it->key) != requiredEnd)
377             return nullptr;
378
379         result->setValue(it->key, RefPtr<InspectorValue>(it->value));
380     }
381
382     return result;
383 }
384
385 std::optional<String> WebDriverService::matchCapabilities(const InspectorObject& mergedCapabilities) const
386 {
387     // §7.2 Processing Capabilities.
388     // https://w3c.github.io/webdriver/webdriver-spec.html#dfn-matching-capabilities
389     Capabilities matchedCapabilities = platformCapabilities();
390
391     // Some capabilities like browser name and version might need to launch the browser,
392     // so we only reject the known capabilities that don't match.
393     auto end = mergedCapabilities.end();
394     for (auto it = mergedCapabilities.begin(); it != end; ++it) {
395         if (it->key == "browserName" && matchedCapabilities.browserName) {
396             String browserName;
397             it->value->asString(browserName);
398             if (!equalIgnoringASCIICase(matchedCapabilities.browserName.value(), browserName))
399                 return makeString("expected browserName ", matchedCapabilities.browserName.value(), " but got ", browserName);
400         } else if (it->key == "browserVersion" && matchedCapabilities.browserVersion) {
401             String browserVersion;
402             it->value->asString(browserVersion);
403             if (!platformCompareBrowserVersions(browserVersion, matchedCapabilities.browserVersion.value()))
404                 return makeString("requested browserVersion is ", browserVersion, " but actual version is ", matchedCapabilities.browserVersion.value());
405         } else if (it->key == "platformName" && matchedCapabilities.platformName) {
406             String platformName;
407             it->value->asString(platformName);
408             if (!equalLettersIgnoringASCIICase(platformName, "any") && !equalIgnoringASCIICase(matchedCapabilities.platformName.value(), platformName))
409                 return makeString("expected platformName ", matchedCapabilities.platformName.value(), " but got ", platformName);
410         } else if (it->key == "acceptInsecureCerts" && matchedCapabilities.acceptInsecureCerts) {
411             bool acceptInsecureCerts;
412             it->value->asBoolean(acceptInsecureCerts);
413             if (acceptInsecureCerts && !matchedCapabilities.acceptInsecureCerts.value())
414                 return String("browser doesn't accept insecure TLS certificates");
415         } else if (it->key == "proxy") {
416             // FIXME: implement proxy support.
417         } else if (auto errorString = platformMatchCapability(it->key, it->value))
418             return errorString;
419     }
420
421     return std::nullopt;
422 }
423
424 RefPtr<InspectorObject> WebDriverService::processCapabilities(const InspectorObject& parameters, Function<void (CommandResult&&)>& completionHandler) const
425 {
426     // §7.2 Processing Capabilities.
427     // https://w3c.github.io/webdriver/webdriver-spec.html#processing-capabilities
428
429     // 1. Let capabilities request be the result of getting the property "capabilities" from parameters.
430     RefPtr<InspectorObject> capabilitiesObject;
431     if (!parameters.getObject(ASCIILiteral("capabilities"), capabilitiesObject)) {
432         completionHandler(CommandResult::fail(CommandResult::ErrorCode::SessionNotCreated));
433         return nullptr;
434     }
435
436     // 2. Let required capabilities be the result of getting the property "alwaysMatch" from capabilities request.
437     RefPtr<InspectorValue> requiredCapabilitiesValue;
438     RefPtr<InspectorObject> requiredCapabilities;
439     if (!capabilitiesObject->getValue(ASCIILiteral("alwaysMatch"), requiredCapabilitiesValue))
440         // 2.1. If required capabilities is undefined, set the value to an empty JSON Object.
441         requiredCapabilities = InspectorObject::create();
442     else if (!requiredCapabilitiesValue->asObject(requiredCapabilities)) {
443         completionHandler(CommandResult::fail(CommandResult::ErrorCode::SessionNotCreated, String("alwaysMatch is invalid in capabilities")));
444         return nullptr;
445     }
446
447     // 2.2. Let required capabilities be the result of trying to validate capabilities with argument required capabilities.
448     requiredCapabilities = validatedCapabilities(*requiredCapabilities);
449     if (!requiredCapabilities) {
450         completionHandler(CommandResult::fail(CommandResult::ErrorCode::SessionNotCreated, String("Invalid alwaysMatch capabilities")));
451         return nullptr;
452     }
453
454     // 3. Let all first match capabilities be the result of getting the property "firstMatch" from capabilities request.
455     RefPtr<InspectorValue> firstMatchCapabilitiesValue;
456     RefPtr<InspectorArray> firstMatchCapabilitiesList;
457     if (!capabilitiesObject->getValue(ASCIILiteral("firstMatch"), firstMatchCapabilitiesValue)) {
458         // 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.
459         firstMatchCapabilitiesList = InspectorArray::create();
460         firstMatchCapabilitiesList->pushObject(InspectorObject::create());
461     } else if (!firstMatchCapabilitiesValue->asArray(firstMatchCapabilitiesList)) {
462         // 3.2. If all first match capabilities is not a JSON List, return error with error code invalid argument.
463         completionHandler(CommandResult::fail(CommandResult::ErrorCode::SessionNotCreated, String("firstMatch is invalid in capabilities")));
464         return nullptr;
465     }
466
467     // 4. Let validated first match capabilities be an empty JSON List.
468     Vector<RefPtr<InspectorObject>> validatedFirstMatchCapabilitiesList;
469     auto firstMatchCapabilitiesListLength = firstMatchCapabilitiesList->length();
470     validatedFirstMatchCapabilitiesList.reserveInitialCapacity(firstMatchCapabilitiesListLength);
471     // 5. For each first match capabilities corresponding to an indexed property in all first match capabilities.
472     for (unsigned i = 0; i < firstMatchCapabilitiesListLength; ++i) {
473         RefPtr<InspectorValue> firstMatchCapabilitiesValue = firstMatchCapabilitiesList->get(i);
474         RefPtr<InspectorObject> firstMatchCapabilities;
475         if (!firstMatchCapabilitiesValue->asObject(firstMatchCapabilities)) {
476             completionHandler(CommandResult::fail(CommandResult::ErrorCode::SessionNotCreated, String("Invalid capabilities found in firstMatch")));
477             return nullptr;
478         }
479         // 5.1. Let validated capabilities be the result of trying to validate capabilities with argument first match capabilities.
480         firstMatchCapabilities = validatedCapabilities(*firstMatchCapabilities);
481         if (!firstMatchCapabilities) {
482             completionHandler(CommandResult::fail(CommandResult::ErrorCode::SessionNotCreated, String("Invalid firstMatch capabilities")));
483             return nullptr;
484         }
485         // 5.2. Append validated capabilities to validated first match capabilities.
486         validatedFirstMatchCapabilitiesList.uncheckedAppend(WTFMove(firstMatchCapabilities));
487     }
488
489     // 6. For each first match capabilities corresponding to an indexed property in validated first match capabilities.
490     std::optional<String> errorString;
491     for (auto& validatedFirstMatchCapabilies : validatedFirstMatchCapabilitiesList) {
492         // 6.1. Let merged capabilities be the result of trying to merge capabilities with required capabilities and first match capabilities as arguments.
493         auto mergedCapabilities = mergeCapabilities(*requiredCapabilities, *validatedFirstMatchCapabilies);
494         if (!mergedCapabilities) {
495             completionHandler(CommandResult::fail(CommandResult::ErrorCode::SessionNotCreated, String("Same capability found in firstMatch and alwaysMatch")));
496             return nullptr;
497         }
498         // 6.2. Let matched capabilities be the result of trying to match capabilities with merged capabilities as an argument.
499         errorString = matchCapabilities(*mergedCapabilities);
500         if (!errorString) {
501             // 6.3. If matched capabilities is not null return matched capabilities.
502             return mergedCapabilities;
503         }
504     }
505
506     completionHandler(CommandResult::fail(CommandResult::ErrorCode::SessionNotCreated, errorString ? errorString.value() : String("Invalid capabilities")));
507     return nullptr;
508 }
509
510 void WebDriverService::newSession(RefPtr<InspectorObject>&& parameters, Function<void (CommandResult&&)>&& completionHandler)
511 {
512     // §8.1 New Session.
513     // https://www.w3.org/TR/webdriver/#new-session
514     auto matchedCapabilities = processCapabilities(*parameters, completionHandler);
515     if (!matchedCapabilities)
516         return;
517
518     Capabilities capabilities;
519     parseCapabilities(*matchedCapabilities, capabilities);
520     auto sessionHost = std::make_unique<SessionHost>(WTFMove(capabilities));
521     auto* sessionHostPtr = sessionHost.get();
522     sessionHostPtr->connectToBrowser([this, sessionHost = WTFMove(sessionHost), completionHandler = WTFMove(completionHandler)](SessionHost::Succeeded succeeded) mutable {
523         if (succeeded == SessionHost::Succeeded::No) {
524             completionHandler(CommandResult::fail(CommandResult::ErrorCode::SessionNotCreated, String("Failed to connect to browser")));
525             return;
526         }
527
528         RefPtr<Session> session = Session::create(WTFMove(sessionHost));
529         session->createTopLevelBrowsingContext([this, session, completionHandler = WTFMove(completionHandler)](CommandResult&& result) mutable {
530             if (result.isError()) {
531                 completionHandler(CommandResult::fail(CommandResult::ErrorCode::SessionNotCreated, result.errorMessage()));
532                 return;
533             }
534
535             m_activeSession = session.get();
536             m_sessions.add(session->id(), session);
537
538             const auto& capabilities = session->capabilities();
539             if (capabilities.timeouts)
540                 session->setTimeouts(capabilities.timeouts.value(), [](CommandResult&&) { });
541
542             RefPtr<InspectorObject> resultObject = InspectorObject::create();
543             resultObject->setString(ASCIILiteral("sessionId"), session->id());
544             RefPtr<InspectorObject> capabilitiesObject = InspectorObject::create();
545             if (capabilities.browserName)
546                 capabilitiesObject->setString(ASCIILiteral("browserName"), capabilities.browserName.value());
547             if (capabilities.browserVersion)
548                 capabilitiesObject->setString(ASCIILiteral("browserVersion"), capabilities.browserVersion.value());
549             if (capabilities.platformName)
550                 capabilitiesObject->setString(ASCIILiteral("platformName"), capabilities.platformName.value());
551             if (capabilities.acceptInsecureCerts)
552                 capabilitiesObject->setBoolean(ASCIILiteral("acceptInsecureCerts"), capabilities.acceptInsecureCerts.value());
553             if (capabilities.timeouts) {
554                 RefPtr<InspectorObject> timeoutsObject = InspectorObject::create();
555                 if (capabilities.timeouts.value().script)
556                     timeoutsObject->setInteger(ASCIILiteral("script"), capabilities.timeouts.value().script.value().millisecondsAs<int>());
557                 if (capabilities.timeouts.value().pageLoad)
558                     timeoutsObject->setInteger(ASCIILiteral("pageLoad"), capabilities.timeouts.value().pageLoad.value().millisecondsAs<int>());
559                 if (capabilities.timeouts.value().implicit)
560                     timeoutsObject->setInteger(ASCIILiteral("implicit"), capabilities.timeouts.value().implicit.value().millisecondsAs<int>());
561                 capabilitiesObject->setObject(ASCIILiteral("timeouts"), WTFMove(timeoutsObject));
562             }
563             resultObject->setObject(ASCIILiteral("value"), WTFMove(capabilitiesObject));
564             completionHandler(CommandResult::success(WTFMove(resultObject)));
565         });
566     });
567 }
568
569 void WebDriverService::deleteSession(RefPtr<InspectorObject>&& parameters, Function<void (CommandResult&&)>&& completionHandler)
570 {
571     // §8.2 Delete Session.
572     // https://www.w3.org/TR/webdriver/#delete-session
573     String sessionID;
574     if (!parameters->getString(ASCIILiteral("sessionId"), sessionID)) {
575         completionHandler(CommandResult::fail(CommandResult::ErrorCode::InvalidArgument));
576         return;
577     }
578
579     auto session = m_sessions.take(sessionID);
580     if (!session) {
581         completionHandler(CommandResult::fail(CommandResult::ErrorCode::InvalidSessionID));
582         return;
583     }
584
585     if (m_activeSession == session.get())
586         m_activeSession = nullptr;
587
588     session->close(WTFMove(completionHandler));
589 }
590
591 void WebDriverService::setTimeouts(RefPtr<InspectorObject>&& parameters, Function<void (CommandResult&&)>&& completionHandler)
592 {
593     // §8.5 Set Timeouts.
594     // https://www.w3.org/TR/webdriver/#set-timeouts
595     auto session = findSessionOrCompleteWithError(*parameters, completionHandler);
596     if (!session)
597         return;
598
599     auto timeouts = deserializeTimeouts(*parameters);
600     if (!timeouts) {
601         completionHandler(CommandResult::fail(CommandResult::ErrorCode::InvalidArgument));
602         return;
603     }
604
605     session->setTimeouts(timeouts.value(), WTFMove(completionHandler));
606 }
607
608 void WebDriverService::go(RefPtr<InspectorObject>&& parameters, Function<void (CommandResult&&)>&& completionHandler)
609 {
610     // §9.1 Go.
611     // https://www.w3.org/TR/webdriver/#go
612     auto session = findSessionOrCompleteWithError(*parameters, completionHandler);
613     if (!session)
614         return;
615
616     String url;
617     if (!parameters->getString(ASCIILiteral("url"), url)) {
618         completionHandler(CommandResult::fail(CommandResult::ErrorCode::InvalidArgument));
619         return;
620     }
621
622     session->waitForNavigationToComplete([session, url = WTFMove(url), completionHandler = WTFMove(completionHandler)](CommandResult&& result) mutable {
623         if (result.isError()) {
624             completionHandler(WTFMove(result));
625             return;
626         }
627         session->go(url, WTFMove(completionHandler));
628     });
629 }
630
631 void WebDriverService::getCurrentURL(RefPtr<InspectorObject>&& parameters, Function<void (CommandResult&&)>&& completionHandler)
632 {
633     // §9.2 Get Current URL.
634     // https://www.w3.org/TR/webdriver/#get-current-url
635     auto session = findSessionOrCompleteWithError(*parameters, completionHandler);
636     if (!session)
637         return;
638
639     session->waitForNavigationToComplete([session, completionHandler = WTFMove(completionHandler)](CommandResult&& result) mutable {
640         if (result.isError()) {
641             completionHandler(WTFMove(result));
642             return;
643         }
644         session->getCurrentURL(WTFMove(completionHandler));
645     });
646 }
647
648 void WebDriverService::back(RefPtr<InspectorObject>&& parameters, Function<void (CommandResult&&)>&& completionHandler)
649 {
650     // §9.3 Back.
651     // https://www.w3.org/TR/webdriver/#back
652     auto session = findSessionOrCompleteWithError(*parameters, completionHandler);
653     if (!session)
654         return;
655
656     session->waitForNavigationToComplete([session, completionHandler = WTFMove(completionHandler)](CommandResult&& result) mutable {
657         if (result.isError()) {
658             completionHandler(WTFMove(result));
659             return;
660         }
661         session->back(WTFMove(completionHandler));
662     });
663 }
664
665 void WebDriverService::forward(RefPtr<InspectorObject>&& parameters, Function<void (CommandResult&&)>&& completionHandler)
666 {
667     // §9.4 Forward.
668     // https://www.w3.org/TR/webdriver/#forward
669     auto session = findSessionOrCompleteWithError(*parameters, completionHandler);
670     if (!session)
671         return;
672
673     session->waitForNavigationToComplete([session, completionHandler = WTFMove(completionHandler)](CommandResult&& result) mutable {
674         if (result.isError()) {
675             completionHandler(WTFMove(result));
676             return;
677         }
678         session->forward(WTFMove(completionHandler));
679     });
680 }
681
682 void WebDriverService::refresh(RefPtr<InspectorObject>&& parameters, Function<void (CommandResult&&)>&& completionHandler)
683 {
684     // §9.5 Refresh.
685     // https://www.w3.org/TR/webdriver/#refresh
686     auto session = findSessionOrCompleteWithError(*parameters, completionHandler);
687     if (!session)
688         return;
689
690     session->waitForNavigationToComplete([session, completionHandler = WTFMove(completionHandler)](CommandResult&& result) mutable {
691         if (result.isError()) {
692             completionHandler(WTFMove(result));
693             return;
694         }
695         session->refresh(WTFMove(completionHandler));
696     });
697 }
698
699 void WebDriverService::getTitle(RefPtr<InspectorObject>&& parameters, Function<void (CommandResult&&)>&& completionHandler)
700 {
701     // §9.6 Get Title.
702     // https://www.w3.org/TR/webdriver/#get-title
703     auto session = findSessionOrCompleteWithError(*parameters, completionHandler);
704     if (!session)
705         return;
706
707     session->waitForNavigationToComplete([session, completionHandler = WTFMove(completionHandler)](CommandResult&& result) mutable {
708         if (result.isError()) {
709             completionHandler(WTFMove(result));
710             return;
711         }
712         session->getTitle(WTFMove(completionHandler));
713     });
714 }
715
716 void WebDriverService::getWindowHandle(RefPtr<InspectorObject>&& parameters, Function<void (CommandResult&&)>&& completionHandler)
717 {
718     // §10.1 Get Window Handle.
719     // https://www.w3.org/TR/webdriver/#get-window-handle
720     if (auto session = findSessionOrCompleteWithError(*parameters, completionHandler))
721         session->getWindowHandle(WTFMove(completionHandler));
722 }
723
724 void WebDriverService::getWindowPosition(RefPtr<InspectorObject>&& parameters, Function<void (CommandResult&&)>&& completionHandler)
725 {
726     if (auto session = findSessionOrCompleteWithError(*parameters, completionHandler))
727         session->getWindowPosition(WTFMove(completionHandler));
728 }
729
730 void WebDriverService::setWindowPosition(RefPtr<InspectorObject>&& parameters, Function<void (CommandResult&&)>&& completionHandler)
731 {
732     auto session = findSessionOrCompleteWithError(*parameters, completionHandler);
733     if (!session)
734         return;
735
736     int windowX;
737     if (!parameters->getInteger(ASCIILiteral("x"), windowX)) {
738         completionHandler(CommandResult::fail(CommandResult::ErrorCode::InvalidArgument));
739         return;
740     }
741
742     int windowY;
743     if (!parameters->getInteger(ASCIILiteral("y"), windowY)) {
744         completionHandler(CommandResult::fail(CommandResult::ErrorCode::InvalidArgument));
745         return;
746     }
747
748     session->setWindowPosition(windowX, windowY, WTFMove(completionHandler));
749 }
750
751 void WebDriverService::getWindowSize(RefPtr<InspectorObject>&& parameters, Function<void (CommandResult&&)>&& completionHandler)
752 {
753     if (auto session = findSessionOrCompleteWithError(*parameters, completionHandler))
754         session->getWindowSize(WTFMove(completionHandler));
755 }
756
757 void WebDriverService::setWindowSize(RefPtr<InspectorObject>&& parameters, Function<void (CommandResult&&)>&& completionHandler)
758 {
759     auto session = findSessionOrCompleteWithError(*parameters, completionHandler);
760     if (!session)
761         return;
762
763     int windowWidth;
764     if (!parameters->getInteger(ASCIILiteral("width"), windowWidth)) {
765         completionHandler(CommandResult::fail(CommandResult::ErrorCode::InvalidArgument));
766         return;
767     }
768
769     int windowHeight;
770     if (!parameters->getInteger(ASCIILiteral("height"), windowHeight)) {
771         completionHandler(CommandResult::fail(CommandResult::ErrorCode::InvalidArgument));
772         return;
773     }
774
775     session->setWindowSize(windowWidth, windowHeight, WTFMove(completionHandler));
776 }
777
778 void WebDriverService::closeWindow(RefPtr<InspectorObject>&& parameters, Function<void (CommandResult&&)>&& completionHandler)
779 {
780     // §10.2 Close Window.
781     // https://www.w3.org/TR/webdriver/#close-window
782     if (auto session = findSessionOrCompleteWithError(*parameters, completionHandler))
783         session->closeWindow(WTFMove(completionHandler));
784 }
785
786 void WebDriverService::switchToWindow(RefPtr<InspectorObject>&& parameters, Function<void (CommandResult&&)>&& completionHandler)
787 {
788     // §10.3 Switch To Window.
789     // https://www.w3.org/TR/webdriver/#switch-to-window
790     auto session = findSessionOrCompleteWithError(*parameters, completionHandler);
791     if (!session)
792         return;
793
794     String handle;
795     if (!parameters->getString(ASCIILiteral("handle"), handle)) {
796         completionHandler(CommandResult::fail(CommandResult::ErrorCode::InvalidArgument));
797         return;
798     }
799
800     session->switchToWindow(handle, WTFMove(completionHandler));
801 }
802
803 void WebDriverService::getWindowHandles(RefPtr<InspectorObject>&& parameters, Function<void (CommandResult&&)>&& completionHandler)
804 {
805     // §10.4 Get Window Handles.
806     // https://www.w3.org/TR/webdriver/#get-window-handles
807     if (auto session = findSessionOrCompleteWithError(*parameters, completionHandler))
808         session->getWindowHandles(WTFMove(completionHandler));
809 }
810
811 void WebDriverService::switchToFrame(RefPtr<InspectorObject>&& parameters, Function<void (CommandResult&&)>&& completionHandler)
812 {
813     // §10.5 Switch To Frame.
814     // https://www.w3.org/TR/webdriver/#switch-to-frame
815     auto session = findSessionOrCompleteWithError(*parameters, completionHandler);
816     if (!session)
817         return;
818
819     RefPtr<InspectorValue> frameID;
820     if (!parameters->getValue(ASCIILiteral("id"), frameID)) {
821         completionHandler(CommandResult::fail(CommandResult::ErrorCode::InvalidArgument));
822         return;
823     }
824
825     session->waitForNavigationToComplete([session, frameID, completionHandler = WTFMove(completionHandler)](CommandResult&& result) mutable {
826         if (result.isError()) {
827             completionHandler(WTFMove(result));
828             return;
829         }
830         session->switchToFrame(WTFMove(frameID), WTFMove(completionHandler));
831     });
832 }
833
834 void WebDriverService::switchToParentFrame(RefPtr<InspectorObject>&& parameters, Function<void (CommandResult&&)>&& completionHandler)
835 {
836     // §10.6 Switch To Parent Frame.
837     // https://www.w3.org/TR/webdriver/#switch-to-parent-frame
838     auto session = findSessionOrCompleteWithError(*parameters, completionHandler);
839     if (!session)
840         return;
841
842     session->waitForNavigationToComplete([session, completionHandler = WTFMove(completionHandler)](CommandResult&& result) mutable {
843         if (result.isError()) {
844             completionHandler(WTFMove(result));
845             return;
846         }
847         session->switchToParentFrame(WTFMove(completionHandler));
848     });
849 }
850
851 static std::optional<String> findElementOrCompleteWithError(InspectorObject& parameters, Function<void (CommandResult&&)>& completionHandler)
852 {
853     String elementID;
854     if (!parameters.getString(ASCIILiteral("elementId"), elementID) || elementID.isEmpty()) {
855         completionHandler(CommandResult::fail(CommandResult::ErrorCode::InvalidArgument));
856         return std::nullopt;
857     }
858     return elementID;
859 }
860
861 static bool findStrategyAndSelectorOrCompleteWithError(InspectorObject& parameters, Function<void (CommandResult&&)>& completionHandler, String& strategy, String& selector)
862 {
863     if (!parameters.getString(ASCIILiteral("using"), strategy)) {
864         completionHandler(CommandResult::fail(CommandResult::ErrorCode::InvalidArgument));
865         return false;
866     }
867     if (!parameters.getString(ASCIILiteral("value"), selector)) {
868         completionHandler(CommandResult::fail(CommandResult::ErrorCode::InvalidArgument));
869         return false;
870     }
871     return true;
872 }
873
874 void WebDriverService::findElement(RefPtr<InspectorObject>&& parameters, Function<void (CommandResult&&)>&& completionHandler)
875 {
876     // §12.2 Find Element.
877     // https://www.w3.org/TR/webdriver/#find-element
878     auto session = findSessionOrCompleteWithError(*parameters, completionHandler);
879     if (!session)
880         return;
881
882     String strategy, selector;
883     if (!findStrategyAndSelectorOrCompleteWithError(*parameters, completionHandler, strategy, selector))
884         return;
885
886     session->waitForNavigationToComplete([session, strategy = WTFMove(strategy), selector = WTFMove(selector), completionHandler = WTFMove(completionHandler)](CommandResult&& result) mutable {
887         if (result.isError()) {
888             completionHandler(WTFMove(result));
889             return;
890         }
891         session->findElements(strategy, selector, Session::FindElementsMode::Single, emptyString(), WTFMove(completionHandler));
892     });
893 }
894
895 void WebDriverService::findElements(RefPtr<InspectorObject>&& parameters, Function<void (CommandResult&&)>&& completionHandler)
896 {
897     // §12.3 Find Elements.
898     // https://www.w3.org/TR/webdriver/#find-elements
899     auto session = findSessionOrCompleteWithError(*parameters, completionHandler);
900     if (!session)
901         return;
902
903     String strategy, selector;
904     if (!findStrategyAndSelectorOrCompleteWithError(*parameters, completionHandler, strategy, selector))
905         return;
906
907     session->waitForNavigationToComplete([session, strategy = WTFMove(strategy), selector = WTFMove(selector), completionHandler = WTFMove(completionHandler)](CommandResult&& result) mutable {
908         if (result.isError()) {
909             completionHandler(WTFMove(result));
910             return;
911         }
912         session->findElements(strategy, selector, Session::FindElementsMode::Multiple, emptyString(), WTFMove(completionHandler));
913     });
914 }
915
916 void WebDriverService::findElementFromElement(RefPtr<InspectorObject>&& parameters, Function<void (CommandResult&&)>&& completionHandler)
917 {
918     // §12.4 Find Element From Element.
919     // https://www.w3.org/TR/webdriver/#find-element-from-element
920     auto session = findSessionOrCompleteWithError(*parameters, completionHandler);
921     if (!session)
922         return;
923
924     auto elementID = findElementOrCompleteWithError(*parameters, completionHandler);
925     if (!elementID)
926         return;
927
928     String strategy, selector;
929     if (!findStrategyAndSelectorOrCompleteWithError(*parameters, completionHandler, strategy, selector))
930         return;
931
932     session->findElements(strategy, selector, Session::FindElementsMode::Single, elementID.value(), WTFMove(completionHandler));
933 }
934
935 void WebDriverService::findElementsFromElement(RefPtr<InspectorObject>&& parameters, Function<void (CommandResult&&)>&& completionHandler)
936 {
937     // §12.5 Find Elements From Element.
938     // https://www.w3.org/TR/webdriver/#find-elements-from-element
939     auto session = findSessionOrCompleteWithError(*parameters, completionHandler);
940     if (!session)
941         return;
942
943     auto elementID = findElementOrCompleteWithError(*parameters, completionHandler);
944     if (!elementID)
945         return;
946
947     String strategy, selector;
948     if (!findStrategyAndSelectorOrCompleteWithError(*parameters, completionHandler, strategy, selector))
949         return;
950
951     session->findElements(strategy, selector, Session::FindElementsMode::Multiple, elementID.value(), WTFMove(completionHandler));
952 }
953
954 void WebDriverService::isElementSelected(RefPtr<InspectorObject>&& parameters, Function<void (CommandResult&&)>&& completionHandler)
955 {
956     // §13.1 Is Element Selected.
957     // https://www.w3.org/TR/webdriver/#is-element-selected
958     auto session = findSessionOrCompleteWithError(*parameters, completionHandler);
959     if (!session)
960         return;
961
962     auto elementID = findElementOrCompleteWithError(*parameters, completionHandler);
963     if (!elementID)
964         return;
965
966     session->isElementSelected(elementID.value(), WTFMove(completionHandler));
967 }
968
969 void WebDriverService::getElementAttribute(RefPtr<InspectorObject>&& parameters, Function<void (CommandResult&&)>&& completionHandler)
970 {
971     // §13.2 Get Element Attribute.
972     // https://www.w3.org/TR/webdriver/#get-element-attribute
973     auto session = findSessionOrCompleteWithError(*parameters, completionHandler);
974     if (!session)
975         return;
976
977     auto elementID = findElementOrCompleteWithError(*parameters, completionHandler);
978     if (!elementID)
979         return;
980
981     String attribute;
982     if (!parameters->getString(ASCIILiteral("name"), attribute)) {
983         completionHandler(CommandResult::fail(CommandResult::ErrorCode::InvalidArgument));
984         return;
985     }
986
987     session->getElementAttribute(elementID.value(), attribute, WTFMove(completionHandler));
988 }
989
990 void WebDriverService::getElementText(RefPtr<InspectorObject>&& parameters, Function<void (CommandResult&&)>&& completionHandler)
991 {
992     // §13.5 Get Element Text.
993     // https://www.w3.org/TR/webdriver/#get-element-text
994     auto session = findSessionOrCompleteWithError(*parameters, completionHandler);
995     if (!session)
996         return;
997
998     auto elementID = findElementOrCompleteWithError(*parameters, completionHandler);
999     if (!elementID)
1000         return;
1001
1002     session->getElementText(elementID.value(), WTFMove(completionHandler));
1003 }
1004
1005 void WebDriverService::getElementTagName(RefPtr<InspectorObject>&& parameters, Function<void (CommandResult&&)>&& completionHandler)
1006 {
1007     // §13.6 Get Element Tag Name.
1008     // https://www.w3.org/TR/webdriver/#get-element-tag-name
1009     auto session = findSessionOrCompleteWithError(*parameters, completionHandler);
1010     if (!session)
1011         return;
1012
1013     auto elementID = findElementOrCompleteWithError(*parameters, completionHandler);
1014     if (!elementID)
1015         return;
1016
1017     session->getElementTagName(elementID.value(), WTFMove(completionHandler));
1018 }
1019
1020 void WebDriverService::getElementRect(RefPtr<InspectorObject>&& parameters, Function<void (CommandResult&&)>&& completionHandler)
1021 {
1022     // §13.7 Get Element Rect.
1023     // https://www.w3.org/TR/webdriver/#get-element-rect
1024     auto session = findSessionOrCompleteWithError(*parameters, completionHandler);
1025     if (!session)
1026         return;
1027
1028     auto elementID = findElementOrCompleteWithError(*parameters, completionHandler);
1029     if (!elementID)
1030         return;
1031
1032     session->getElementRect(elementID.value(), WTFMove(completionHandler));
1033 }
1034
1035 void WebDriverService::isElementEnabled(RefPtr<InspectorObject>&& parameters, Function<void (CommandResult&&)>&& completionHandler)
1036 {
1037     // §13.8 Is Element Enabled.
1038     // https://www.w3.org/TR/webdriver/#is-element-enabled
1039     auto session = findSessionOrCompleteWithError(*parameters, completionHandler);
1040     if (!session)
1041         return;
1042
1043     auto elementID = findElementOrCompleteWithError(*parameters, completionHandler);
1044     if (!elementID)
1045         return;
1046
1047     session->isElementEnabled(elementID.value(), WTFMove(completionHandler));
1048 }
1049
1050 void WebDriverService::isElementDisplayed(RefPtr<InspectorObject>&& parameters, Function<void (CommandResult&&)>&& completionHandler)
1051 {
1052     // §C. Element Displayedness.
1053     // https://www.w3.org/TR/webdriver/#element-displayedness
1054     auto session = findSessionOrCompleteWithError(*parameters, completionHandler);
1055     if (!session)
1056         return;
1057
1058     auto elementID = findElementOrCompleteWithError(*parameters, completionHandler);
1059     if (!elementID)
1060         return;
1061
1062     session->isElementDisplayed(elementID.value(), WTFMove(completionHandler));
1063 }
1064
1065 void WebDriverService::elementClick(RefPtr<InspectorObject>&& parameters, Function<void (CommandResult&&)>&& completionHandler)
1066 {
1067     // §14.1 Element Click.
1068     // https://www.w3.org/TR/webdriver/#element-click
1069     auto session = findSessionOrCompleteWithError(*parameters, completionHandler);
1070     if (!session)
1071         return;
1072
1073     auto elementID = findElementOrCompleteWithError(*parameters, completionHandler);
1074     if (!elementID)
1075         return;
1076
1077     session->elementClick(elementID.value(), WTFMove(completionHandler));
1078 }
1079
1080 void WebDriverService::elementClear(RefPtr<InspectorObject>&& parameters, Function<void (CommandResult&&)>&& completionHandler)
1081 {
1082     // §14.2 Element Clear.
1083     // https://www.w3.org/TR/webdriver/#element-clear
1084     auto session = findSessionOrCompleteWithError(*parameters, completionHandler);
1085     if (!session)
1086         return;
1087
1088     auto elementID = findElementOrCompleteWithError(*parameters, completionHandler);
1089     if (!elementID)
1090         return;
1091
1092     session->elementClear(elementID.value(), WTFMove(completionHandler));
1093 }
1094
1095 void WebDriverService::elementSendKeys(RefPtr<InspectorObject>&& parameters, Function<void (CommandResult&&)>&& completionHandler)
1096 {
1097     // §14.3 Element Send Keys.
1098     // https://www.w3.org/TR/webdriver/#element-send-keys
1099     auto session = findSessionOrCompleteWithError(*parameters, completionHandler);
1100     if (!session)
1101         return;
1102
1103     auto elementID = findElementOrCompleteWithError(*parameters, completionHandler);
1104     if (!elementID)
1105         return;
1106
1107     RefPtr<InspectorArray> valueArray;
1108     if (!parameters->getArray(ASCIILiteral("value"), valueArray)) {
1109         completionHandler(CommandResult::fail(CommandResult::ErrorCode::InvalidArgument));
1110         return;
1111     }
1112
1113     unsigned valueArrayLength = valueArray->length();
1114     if (!valueArrayLength) {
1115         completionHandler(CommandResult::fail(CommandResult::ErrorCode::InvalidArgument));
1116         return;
1117     }
1118     Vector<String> value;
1119     value.reserveInitialCapacity(valueArrayLength);
1120     for (unsigned i = 0; i < valueArrayLength; ++i) {
1121         if (auto keyValue = valueArray->get(i)) {
1122             String key;
1123             if (keyValue->asString(key))
1124                 value.uncheckedAppend(WTFMove(key));
1125         }
1126     }
1127     if (!value.size()) {
1128         completionHandler(CommandResult::fail(CommandResult::ErrorCode::InvalidArgument));
1129         return;
1130     }
1131
1132     session->elementSendKeys(elementID.value(), WTFMove(value), WTFMove(completionHandler));
1133 }
1134
1135 void WebDriverService::elementSubmit(RefPtr<InspectorObject>&& parameters, Function<void (CommandResult&&)>&& completionHandler)
1136 {
1137     auto session = findSessionOrCompleteWithError(*parameters, completionHandler);
1138     if (!session)
1139         return;
1140
1141     auto elementID = findElementOrCompleteWithError(*parameters, completionHandler);
1142     if (!elementID)
1143         return;
1144
1145     session->elementSubmit(elementID.value(), WTFMove(completionHandler));
1146 }
1147
1148 static bool findScriptAndArgumentsOrCompleteWithError(InspectorObject& parameters, Function<void (CommandResult&&)>& completionHandler, String& script, RefPtr<InspectorArray>& arguments)
1149 {
1150     if (!parameters.getString(ASCIILiteral("script"), script)) {
1151         completionHandler(CommandResult::fail(CommandResult::ErrorCode::InvalidArgument));
1152         return false;
1153     }
1154     if (!parameters.getArray(ASCIILiteral("args"), arguments)) {
1155         completionHandler(CommandResult::fail(CommandResult::ErrorCode::InvalidArgument));
1156         return false;
1157     }
1158     return true;
1159 }
1160
1161 void WebDriverService::executeScript(RefPtr<InspectorObject>&& parameters, Function<void (CommandResult&&)>&& completionHandler)
1162 {
1163     // §15.2.1 Execute Script.
1164     // https://www.w3.org/TR/webdriver/#execute-script
1165     auto session = findSessionOrCompleteWithError(*parameters, completionHandler);
1166     if (!session)
1167         return;
1168
1169     String script;
1170     RefPtr<InspectorArray> arguments;
1171     if (!findScriptAndArgumentsOrCompleteWithError(*parameters, completionHandler, script, arguments))
1172         return;
1173
1174     session->waitForNavigationToComplete([session, script = WTFMove(script), arguments = WTFMove(arguments), completionHandler = WTFMove(completionHandler)](CommandResult&& result) mutable {
1175         if (result.isError()) {
1176             completionHandler(WTFMove(result));
1177             return;
1178         }
1179         session->executeScript(script, WTFMove(arguments), Session::ExecuteScriptMode::Sync, WTFMove(completionHandler));
1180     });
1181 }
1182
1183 void WebDriverService::executeAsyncScript(RefPtr<InspectorObject>&& parameters, Function<void (CommandResult&&)>&& completionHandler)
1184 {
1185     // §15.2.2 Execute Async Script.
1186     // https://www.w3.org/TR/webdriver/#execute-async-script
1187     auto session = findSessionOrCompleteWithError(*parameters, completionHandler);
1188     if (!session)
1189         return;
1190
1191     String script;
1192     RefPtr<InspectorArray> arguments;
1193     if (!findScriptAndArgumentsOrCompleteWithError(*parameters, completionHandler, script, arguments))
1194         return;
1195
1196     session->waitForNavigationToComplete([session, script = WTFMove(script), arguments = WTFMove(arguments), completionHandler = WTFMove(completionHandler)](CommandResult&& result) mutable {
1197         if (result.isError()) {
1198             completionHandler(WTFMove(result));
1199             return;
1200         }
1201         session->executeScript(script, WTFMove(arguments), Session::ExecuteScriptMode::Async, WTFMove(completionHandler));
1202     });
1203 }
1204
1205 } // namespace WebDriver