c08803e5430ff5a31b431289ea14b3325d278aa9
[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 static std::optional<PageLoadStrategy> deserializePageLoadStrategy(const String& pageLoadStrategy)
281 {
282     if (pageLoadStrategy == "none")
283         return PageLoadStrategy::None;
284     if (pageLoadStrategy == "normal")
285         return PageLoadStrategy::Normal;
286     if (pageLoadStrategy == "eager")
287         return PageLoadStrategy::Eager;
288     return std::nullopt;
289 }
290
291 void WebDriverService::parseCapabilities(const InspectorObject& matchedCapabilities, Capabilities& capabilities) const
292 {
293     // Matched capabilities have already been validated.
294     bool acceptInsecureCerts;
295     if (matchedCapabilities.getBoolean(ASCIILiteral("acceptInsecureCerts"), acceptInsecureCerts))
296         capabilities.acceptInsecureCerts = acceptInsecureCerts;
297     String browserName;
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;
303     String platformName;
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);
313 }
314
315 RefPtr<Session> WebDriverService::findSessionOrCompleteWithError(InspectorObject& parameters, Function<void (CommandResult&&)>& completionHandler)
316 {
317     String sessionID;
318     if (!parameters.getString(ASCIILiteral("sessionId"), sessionID)) {
319         completionHandler(CommandResult::fail(CommandResult::ErrorCode::InvalidArgument));
320         return nullptr;
321     }
322
323     auto session = m_sessions.get(sessionID);
324     if (!session) {
325         completionHandler(CommandResult::fail(CommandResult::ErrorCode::InvalidSessionID));
326         return nullptr;
327     }
328
329     return session;
330 }
331
332 RefPtr<InspectorObject> WebDriverService::validatedCapabilities(const InspectorObject& capabilities) const
333 {
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))
344                 return nullptr;
345             result->setBoolean(it->key, acceptInsecureCerts);
346         } else if (it->key == "browserName" || it->key == "browserVersion" || it->key == "platformName") {
347             String stringValue;
348             if (!it->value->asString(stringValue))
349                 return nullptr;
350             result->setString(it->key, stringValue);
351         } else if (it->key == "pageLoadStrategy") {
352             String pageLoadStrategy;
353             if (!it->value->asString(pageLoadStrategy) || !deserializePageLoadStrategy(pageLoadStrategy))
354                 return nullptr;
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))
361                 return nullptr;
362             result->setValue(it->key, RefPtr<InspectorValue>(it->value));
363         } else if (it->key == "unhandledPromptBehavior") {
364             String unhandledPromptBehavior;
365             if (!it->value->asString(unhandledPromptBehavior))
366                 return nullptr;
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))
371                 return nullptr;
372             result->setValue(it->key, RefPtr<InspectorValue>(it->value));
373         }
374     }
375     return result;
376 }
377
378 RefPtr<InspectorObject> WebDriverService::mergeCapabilities(const InspectorObject& requiredCapabilities, const InspectorObject& firstMatchCapabilities) const
379 {
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));
386
387     auto firstMatchEnd = firstMatchCapabilities.end();
388     for (auto it = firstMatchCapabilities.begin(); it != firstMatchEnd; ++it) {
389         if (requiredCapabilities.find(it->key) != requiredEnd)
390             return nullptr;
391
392         result->setValue(it->key, RefPtr<InspectorValue>(it->value));
393     }
394
395     return result;
396 }
397
398 std::optional<String> WebDriverService::matchCapabilities(const InspectorObject& mergedCapabilities) const
399 {
400     // §7.2 Processing Capabilities.
401     // https://w3c.github.io/webdriver/webdriver-spec.html#dfn-matching-capabilities
402     Capabilities matchedCapabilities = platformCapabilities();
403
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) {
409             String 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) {
419             String 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))
431             return errorString;
432     }
433
434     return std::nullopt;
435 }
436
437 RefPtr<InspectorObject> WebDriverService::processCapabilities(const InspectorObject& parameters, Function<void (CommandResult&&)>& completionHandler) const
438 {
439     // §7.2 Processing Capabilities.
440     // https://w3c.github.io/webdriver/webdriver-spec.html#processing-capabilities
441
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));
446         return nullptr;
447     }
448
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")));
457         return nullptr;
458     }
459
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")));
464         return nullptr;
465     }
466
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")));
477         return nullptr;
478     }
479
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")));
490             return nullptr;
491         }
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")));
496             return nullptr;
497         }
498         // 5.2. Append validated capabilities to validated first match capabilities.
499         validatedFirstMatchCapabilitiesList.uncheckedAppend(WTFMove(firstMatchCapabilities));
500     }
501
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")));
509             return nullptr;
510         }
511         // 6.2. Let matched capabilities be the result of trying to match capabilities with merged capabilities as an argument.
512         errorString = matchCapabilities(*mergedCapabilities);
513         if (!errorString) {
514             // 6.3. If matched capabilities is not null return matched capabilities.
515             return mergedCapabilities;
516         }
517     }
518
519     completionHandler(CommandResult::fail(CommandResult::ErrorCode::SessionNotCreated, errorString ? errorString.value() : String("Invalid capabilities")));
520     return nullptr;
521 }
522
523 void WebDriverService::newSession(RefPtr<InspectorObject>&& parameters, Function<void (CommandResult&&)>&& completionHandler)
524 {
525     // §8.1 New Session.
526     // https://www.w3.org/TR/webdriver/#new-session
527     auto matchedCapabilities = processCapabilities(*parameters, completionHandler);
528     if (!matchedCapabilities)
529         return;
530
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")));
538             return;
539         }
540
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()));
545                 return;
546             }
547
548             m_activeSession = session.get();
549             m_sessions.add(session->id(), session);
550
551             const auto& capabilities = session->capabilities();
552             if (capabilities.timeouts)
553                 session->setTimeouts(capabilities.timeouts.value(), [](CommandResult&&) { });
554
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));
575             }
576             if (capabilities.pageLoadStrategy) {
577                 switch (capabilities.pageLoadStrategy.value()) {
578                 case PageLoadStrategy::None:
579                     capabilitiesObject->setString(ASCIILiteral("pageLoadStrategy"), "none");
580                     break;
581                 case PageLoadStrategy::Normal:
582                     capabilitiesObject->setString(ASCIILiteral("pageLoadStrategy"), "normal");
583                     break;
584                 case PageLoadStrategy::Eager:
585                     capabilitiesObject->setString(ASCIILiteral("pageLoadStrategy"), "eager");
586                     break;
587                 }
588             }
589             resultObject->setObject(ASCIILiteral("value"), WTFMove(capabilitiesObject));
590             completionHandler(CommandResult::success(WTFMove(resultObject)));
591         });
592     });
593 }
594
595 void WebDriverService::deleteSession(RefPtr<InspectorObject>&& parameters, Function<void (CommandResult&&)>&& completionHandler)
596 {
597     // §8.2 Delete Session.
598     // https://www.w3.org/TR/webdriver/#delete-session
599     String sessionID;
600     if (!parameters->getString(ASCIILiteral("sessionId"), sessionID)) {
601         completionHandler(CommandResult::fail(CommandResult::ErrorCode::InvalidArgument));
602         return;
603     }
604
605     auto session = m_sessions.take(sessionID);
606     if (!session) {
607         completionHandler(CommandResult::fail(CommandResult::ErrorCode::InvalidSessionID));
608         return;
609     }
610
611     if (m_activeSession == session.get())
612         m_activeSession = nullptr;
613
614     session->close(WTFMove(completionHandler));
615 }
616
617 void WebDriverService::setTimeouts(RefPtr<InspectorObject>&& parameters, Function<void (CommandResult&&)>&& completionHandler)
618 {
619     // §8.5 Set Timeouts.
620     // https://www.w3.org/TR/webdriver/#set-timeouts
621     auto session = findSessionOrCompleteWithError(*parameters, completionHandler);
622     if (!session)
623         return;
624
625     auto timeouts = deserializeTimeouts(*parameters);
626     if (!timeouts) {
627         completionHandler(CommandResult::fail(CommandResult::ErrorCode::InvalidArgument));
628         return;
629     }
630
631     session->setTimeouts(timeouts.value(), WTFMove(completionHandler));
632 }
633
634 void WebDriverService::go(RefPtr<InspectorObject>&& parameters, Function<void (CommandResult&&)>&& completionHandler)
635 {
636     // §9.1 Go.
637     // https://www.w3.org/TR/webdriver/#go
638     auto session = findSessionOrCompleteWithError(*parameters, completionHandler);
639     if (!session)
640         return;
641
642     String url;
643     if (!parameters->getString(ASCIILiteral("url"), url)) {
644         completionHandler(CommandResult::fail(CommandResult::ErrorCode::InvalidArgument));
645         return;
646     }
647
648     session->waitForNavigationToComplete([session, url = WTFMove(url), completionHandler = WTFMove(completionHandler)](CommandResult&& result) mutable {
649         if (result.isError()) {
650             completionHandler(WTFMove(result));
651             return;
652         }
653         session->go(url, WTFMove(completionHandler));
654     });
655 }
656
657 void WebDriverService::getCurrentURL(RefPtr<InspectorObject>&& parameters, Function<void (CommandResult&&)>&& completionHandler)
658 {
659     // §9.2 Get Current URL.
660     // https://www.w3.org/TR/webdriver/#get-current-url
661     auto session = findSessionOrCompleteWithError(*parameters, completionHandler);
662     if (!session)
663         return;
664
665     session->waitForNavigationToComplete([session, completionHandler = WTFMove(completionHandler)](CommandResult&& result) mutable {
666         if (result.isError()) {
667             completionHandler(WTFMove(result));
668             return;
669         }
670         session->getCurrentURL(WTFMove(completionHandler));
671     });
672 }
673
674 void WebDriverService::back(RefPtr<InspectorObject>&& parameters, Function<void (CommandResult&&)>&& completionHandler)
675 {
676     // §9.3 Back.
677     // https://www.w3.org/TR/webdriver/#back
678     auto session = findSessionOrCompleteWithError(*parameters, completionHandler);
679     if (!session)
680         return;
681
682     session->waitForNavigationToComplete([session, completionHandler = WTFMove(completionHandler)](CommandResult&& result) mutable {
683         if (result.isError()) {
684             completionHandler(WTFMove(result));
685             return;
686         }
687         session->back(WTFMove(completionHandler));
688     });
689 }
690
691 void WebDriverService::forward(RefPtr<InspectorObject>&& parameters, Function<void (CommandResult&&)>&& completionHandler)
692 {
693     // §9.4 Forward.
694     // https://www.w3.org/TR/webdriver/#forward
695     auto session = findSessionOrCompleteWithError(*parameters, completionHandler);
696     if (!session)
697         return;
698
699     session->waitForNavigationToComplete([session, completionHandler = WTFMove(completionHandler)](CommandResult&& result) mutable {
700         if (result.isError()) {
701             completionHandler(WTFMove(result));
702             return;
703         }
704         session->forward(WTFMove(completionHandler));
705     });
706 }
707
708 void WebDriverService::refresh(RefPtr<InspectorObject>&& parameters, Function<void (CommandResult&&)>&& completionHandler)
709 {
710     // §9.5 Refresh.
711     // https://www.w3.org/TR/webdriver/#refresh
712     auto session = findSessionOrCompleteWithError(*parameters, completionHandler);
713     if (!session)
714         return;
715
716     session->waitForNavigationToComplete([session, completionHandler = WTFMove(completionHandler)](CommandResult&& result) mutable {
717         if (result.isError()) {
718             completionHandler(WTFMove(result));
719             return;
720         }
721         session->refresh(WTFMove(completionHandler));
722     });
723 }
724
725 void WebDriverService::getTitle(RefPtr<InspectorObject>&& parameters, Function<void (CommandResult&&)>&& completionHandler)
726 {
727     // §9.6 Get Title.
728     // https://www.w3.org/TR/webdriver/#get-title
729     auto session = findSessionOrCompleteWithError(*parameters, completionHandler);
730     if (!session)
731         return;
732
733     session->waitForNavigationToComplete([session, completionHandler = WTFMove(completionHandler)](CommandResult&& result) mutable {
734         if (result.isError()) {
735             completionHandler(WTFMove(result));
736             return;
737         }
738         session->getTitle(WTFMove(completionHandler));
739     });
740 }
741
742 void WebDriverService::getWindowHandle(RefPtr<InspectorObject>&& parameters, Function<void (CommandResult&&)>&& completionHandler)
743 {
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));
748 }
749
750 void WebDriverService::getWindowPosition(RefPtr<InspectorObject>&& parameters, Function<void (CommandResult&&)>&& completionHandler)
751 {
752     if (auto session = findSessionOrCompleteWithError(*parameters, completionHandler))
753         session->getWindowPosition(WTFMove(completionHandler));
754 }
755
756 void WebDriverService::setWindowPosition(RefPtr<InspectorObject>&& parameters, Function<void (CommandResult&&)>&& completionHandler)
757 {
758     auto session = findSessionOrCompleteWithError(*parameters, completionHandler);
759     if (!session)
760         return;
761
762     int windowX;
763     if (!parameters->getInteger(ASCIILiteral("x"), windowX)) {
764         completionHandler(CommandResult::fail(CommandResult::ErrorCode::InvalidArgument));
765         return;
766     }
767
768     int windowY;
769     if (!parameters->getInteger(ASCIILiteral("y"), windowY)) {
770         completionHandler(CommandResult::fail(CommandResult::ErrorCode::InvalidArgument));
771         return;
772     }
773
774     session->setWindowPosition(windowX, windowY, WTFMove(completionHandler));
775 }
776
777 void WebDriverService::getWindowSize(RefPtr<InspectorObject>&& parameters, Function<void (CommandResult&&)>&& completionHandler)
778 {
779     if (auto session = findSessionOrCompleteWithError(*parameters, completionHandler))
780         session->getWindowSize(WTFMove(completionHandler));
781 }
782
783 void WebDriverService::setWindowSize(RefPtr<InspectorObject>&& parameters, Function<void (CommandResult&&)>&& completionHandler)
784 {
785     auto session = findSessionOrCompleteWithError(*parameters, completionHandler);
786     if (!session)
787         return;
788
789     int windowWidth;
790     if (!parameters->getInteger(ASCIILiteral("width"), windowWidth)) {
791         completionHandler(CommandResult::fail(CommandResult::ErrorCode::InvalidArgument));
792         return;
793     }
794
795     int windowHeight;
796     if (!parameters->getInteger(ASCIILiteral("height"), windowHeight)) {
797         completionHandler(CommandResult::fail(CommandResult::ErrorCode::InvalidArgument));
798         return;
799     }
800
801     session->setWindowSize(windowWidth, windowHeight, WTFMove(completionHandler));
802 }
803
804 void WebDriverService::closeWindow(RefPtr<InspectorObject>&& parameters, Function<void (CommandResult&&)>&& completionHandler)
805 {
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));
810 }
811
812 void WebDriverService::switchToWindow(RefPtr<InspectorObject>&& parameters, Function<void (CommandResult&&)>&& completionHandler)
813 {
814     // §10.3 Switch To Window.
815     // https://www.w3.org/TR/webdriver/#switch-to-window
816     auto session = findSessionOrCompleteWithError(*parameters, completionHandler);
817     if (!session)
818         return;
819
820     String handle;
821     if (!parameters->getString(ASCIILiteral("handle"), handle)) {
822         completionHandler(CommandResult::fail(CommandResult::ErrorCode::InvalidArgument));
823         return;
824     }
825
826     session->switchToWindow(handle, WTFMove(completionHandler));
827 }
828
829 void WebDriverService::getWindowHandles(RefPtr<InspectorObject>&& parameters, Function<void (CommandResult&&)>&& completionHandler)
830 {
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));
835 }
836
837 void WebDriverService::switchToFrame(RefPtr<InspectorObject>&& parameters, Function<void (CommandResult&&)>&& completionHandler)
838 {
839     // §10.5 Switch To Frame.
840     // https://www.w3.org/TR/webdriver/#switch-to-frame
841     auto session = findSessionOrCompleteWithError(*parameters, completionHandler);
842     if (!session)
843         return;
844
845     RefPtr<InspectorValue> frameID;
846     if (!parameters->getValue(ASCIILiteral("id"), frameID)) {
847         completionHandler(CommandResult::fail(CommandResult::ErrorCode::InvalidArgument));
848         return;
849     }
850
851     session->waitForNavigationToComplete([session, frameID, completionHandler = WTFMove(completionHandler)](CommandResult&& result) mutable {
852         if (result.isError()) {
853             completionHandler(WTFMove(result));
854             return;
855         }
856         session->switchToFrame(WTFMove(frameID), WTFMove(completionHandler));
857     });
858 }
859
860 void WebDriverService::switchToParentFrame(RefPtr<InspectorObject>&& parameters, Function<void (CommandResult&&)>&& completionHandler)
861 {
862     // §10.6 Switch To Parent Frame.
863     // https://www.w3.org/TR/webdriver/#switch-to-parent-frame
864     auto session = findSessionOrCompleteWithError(*parameters, completionHandler);
865     if (!session)
866         return;
867
868     session->waitForNavigationToComplete([session, completionHandler = WTFMove(completionHandler)](CommandResult&& result) mutable {
869         if (result.isError()) {
870             completionHandler(WTFMove(result));
871             return;
872         }
873         session->switchToParentFrame(WTFMove(completionHandler));
874     });
875 }
876
877 static std::optional<String> findElementOrCompleteWithError(InspectorObject& parameters, Function<void (CommandResult&&)>& completionHandler)
878 {
879     String elementID;
880     if (!parameters.getString(ASCIILiteral("elementId"), elementID) || elementID.isEmpty()) {
881         completionHandler(CommandResult::fail(CommandResult::ErrorCode::InvalidArgument));
882         return std::nullopt;
883     }
884     return elementID;
885 }
886
887 static bool findStrategyAndSelectorOrCompleteWithError(InspectorObject& parameters, Function<void (CommandResult&&)>& completionHandler, String& strategy, String& selector)
888 {
889     if (!parameters.getString(ASCIILiteral("using"), strategy)) {
890         completionHandler(CommandResult::fail(CommandResult::ErrorCode::InvalidArgument));
891         return false;
892     }
893     if (!parameters.getString(ASCIILiteral("value"), selector)) {
894         completionHandler(CommandResult::fail(CommandResult::ErrorCode::InvalidArgument));
895         return false;
896     }
897     return true;
898 }
899
900 void WebDriverService::findElement(RefPtr<InspectorObject>&& parameters, Function<void (CommandResult&&)>&& completionHandler)
901 {
902     // §12.2 Find Element.
903     // https://www.w3.org/TR/webdriver/#find-element
904     auto session = findSessionOrCompleteWithError(*parameters, completionHandler);
905     if (!session)
906         return;
907
908     String strategy, selector;
909     if (!findStrategyAndSelectorOrCompleteWithError(*parameters, completionHandler, strategy, selector))
910         return;
911
912     session->waitForNavigationToComplete([session, strategy = WTFMove(strategy), selector = WTFMove(selector), completionHandler = WTFMove(completionHandler)](CommandResult&& result) mutable {
913         if (result.isError()) {
914             completionHandler(WTFMove(result));
915             return;
916         }
917         session->findElements(strategy, selector, Session::FindElementsMode::Single, emptyString(), WTFMove(completionHandler));
918     });
919 }
920
921 void WebDriverService::findElements(RefPtr<InspectorObject>&& parameters, Function<void (CommandResult&&)>&& completionHandler)
922 {
923     // §12.3 Find Elements.
924     // https://www.w3.org/TR/webdriver/#find-elements
925     auto session = findSessionOrCompleteWithError(*parameters, completionHandler);
926     if (!session)
927         return;
928
929     String strategy, selector;
930     if (!findStrategyAndSelectorOrCompleteWithError(*parameters, completionHandler, strategy, selector))
931         return;
932
933     session->waitForNavigationToComplete([session, strategy = WTFMove(strategy), selector = WTFMove(selector), completionHandler = WTFMove(completionHandler)](CommandResult&& result) mutable {
934         if (result.isError()) {
935             completionHandler(WTFMove(result));
936             return;
937         }
938         session->findElements(strategy, selector, Session::FindElementsMode::Multiple, emptyString(), WTFMove(completionHandler));
939     });
940 }
941
942 void WebDriverService::findElementFromElement(RefPtr<InspectorObject>&& parameters, Function<void (CommandResult&&)>&& completionHandler)
943 {
944     // §12.4 Find Element From Element.
945     // https://www.w3.org/TR/webdriver/#find-element-from-element
946     auto session = findSessionOrCompleteWithError(*parameters, completionHandler);
947     if (!session)
948         return;
949
950     auto elementID = findElementOrCompleteWithError(*parameters, completionHandler);
951     if (!elementID)
952         return;
953
954     String strategy, selector;
955     if (!findStrategyAndSelectorOrCompleteWithError(*parameters, completionHandler, strategy, selector))
956         return;
957
958     session->findElements(strategy, selector, Session::FindElementsMode::Single, elementID.value(), WTFMove(completionHandler));
959 }
960
961 void WebDriverService::findElementsFromElement(RefPtr<InspectorObject>&& parameters, Function<void (CommandResult&&)>&& completionHandler)
962 {
963     // §12.5 Find Elements From Element.
964     // https://www.w3.org/TR/webdriver/#find-elements-from-element
965     auto session = findSessionOrCompleteWithError(*parameters, completionHandler);
966     if (!session)
967         return;
968
969     auto elementID = findElementOrCompleteWithError(*parameters, completionHandler);
970     if (!elementID)
971         return;
972
973     String strategy, selector;
974     if (!findStrategyAndSelectorOrCompleteWithError(*parameters, completionHandler, strategy, selector))
975         return;
976
977     session->findElements(strategy, selector, Session::FindElementsMode::Multiple, elementID.value(), WTFMove(completionHandler));
978 }
979
980 void WebDriverService::isElementSelected(RefPtr<InspectorObject>&& parameters, Function<void (CommandResult&&)>&& completionHandler)
981 {
982     // §13.1 Is Element Selected.
983     // https://www.w3.org/TR/webdriver/#is-element-selected
984     auto session = findSessionOrCompleteWithError(*parameters, completionHandler);
985     if (!session)
986         return;
987
988     auto elementID = findElementOrCompleteWithError(*parameters, completionHandler);
989     if (!elementID)
990         return;
991
992     session->isElementSelected(elementID.value(), WTFMove(completionHandler));
993 }
994
995 void WebDriverService::getElementAttribute(RefPtr<InspectorObject>&& parameters, Function<void (CommandResult&&)>&& completionHandler)
996 {
997     // §13.2 Get Element Attribute.
998     // https://www.w3.org/TR/webdriver/#get-element-attribute
999     auto session = findSessionOrCompleteWithError(*parameters, completionHandler);
1000     if (!session)
1001         return;
1002
1003     auto elementID = findElementOrCompleteWithError(*parameters, completionHandler);
1004     if (!elementID)
1005         return;
1006
1007     String attribute;
1008     if (!parameters->getString(ASCIILiteral("name"), attribute)) {
1009         completionHandler(CommandResult::fail(CommandResult::ErrorCode::InvalidArgument));
1010         return;
1011     }
1012
1013     session->getElementAttribute(elementID.value(), attribute, WTFMove(completionHandler));
1014 }
1015
1016 void WebDriverService::getElementText(RefPtr<InspectorObject>&& parameters, Function<void (CommandResult&&)>&& completionHandler)
1017 {
1018     // §13.5 Get Element Text.
1019     // https://www.w3.org/TR/webdriver/#get-element-text
1020     auto session = findSessionOrCompleteWithError(*parameters, completionHandler);
1021     if (!session)
1022         return;
1023
1024     auto elementID = findElementOrCompleteWithError(*parameters, completionHandler);
1025     if (!elementID)
1026         return;
1027
1028     session->getElementText(elementID.value(), WTFMove(completionHandler));
1029 }
1030
1031 void WebDriverService::getElementTagName(RefPtr<InspectorObject>&& parameters, Function<void (CommandResult&&)>&& completionHandler)
1032 {
1033     // §13.6 Get Element Tag Name.
1034     // https://www.w3.org/TR/webdriver/#get-element-tag-name
1035     auto session = findSessionOrCompleteWithError(*parameters, completionHandler);
1036     if (!session)
1037         return;
1038
1039     auto elementID = findElementOrCompleteWithError(*parameters, completionHandler);
1040     if (!elementID)
1041         return;
1042
1043     session->getElementTagName(elementID.value(), WTFMove(completionHandler));
1044 }
1045
1046 void WebDriverService::getElementRect(RefPtr<InspectorObject>&& parameters, Function<void (CommandResult&&)>&& completionHandler)
1047 {
1048     // §13.7 Get Element Rect.
1049     // https://www.w3.org/TR/webdriver/#get-element-rect
1050     auto session = findSessionOrCompleteWithError(*parameters, completionHandler);
1051     if (!session)
1052         return;
1053
1054     auto elementID = findElementOrCompleteWithError(*parameters, completionHandler);
1055     if (!elementID)
1056         return;
1057
1058     session->getElementRect(elementID.value(), WTFMove(completionHandler));
1059 }
1060
1061 void WebDriverService::isElementEnabled(RefPtr<InspectorObject>&& parameters, Function<void (CommandResult&&)>&& completionHandler)
1062 {
1063     // §13.8 Is Element Enabled.
1064     // https://www.w3.org/TR/webdriver/#is-element-enabled
1065     auto session = findSessionOrCompleteWithError(*parameters, completionHandler);
1066     if (!session)
1067         return;
1068
1069     auto elementID = findElementOrCompleteWithError(*parameters, completionHandler);
1070     if (!elementID)
1071         return;
1072
1073     session->isElementEnabled(elementID.value(), WTFMove(completionHandler));
1074 }
1075
1076 void WebDriverService::isElementDisplayed(RefPtr<InspectorObject>&& parameters, Function<void (CommandResult&&)>&& completionHandler)
1077 {
1078     // §C. Element Displayedness.
1079     // https://www.w3.org/TR/webdriver/#element-displayedness
1080     auto session = findSessionOrCompleteWithError(*parameters, completionHandler);
1081     if (!session)
1082         return;
1083
1084     auto elementID = findElementOrCompleteWithError(*parameters, completionHandler);
1085     if (!elementID)
1086         return;
1087
1088     session->isElementDisplayed(elementID.value(), WTFMove(completionHandler));
1089 }
1090
1091 void WebDriverService::elementClick(RefPtr<InspectorObject>&& parameters, Function<void (CommandResult&&)>&& completionHandler)
1092 {
1093     // §14.1 Element Click.
1094     // https://www.w3.org/TR/webdriver/#element-click
1095     auto session = findSessionOrCompleteWithError(*parameters, completionHandler);
1096     if (!session)
1097         return;
1098
1099     auto elementID = findElementOrCompleteWithError(*parameters, completionHandler);
1100     if (!elementID)
1101         return;
1102
1103     session->elementClick(elementID.value(), WTFMove(completionHandler));
1104 }
1105
1106 void WebDriverService::elementClear(RefPtr<InspectorObject>&& parameters, Function<void (CommandResult&&)>&& completionHandler)
1107 {
1108     // §14.2 Element Clear.
1109     // https://www.w3.org/TR/webdriver/#element-clear
1110     auto session = findSessionOrCompleteWithError(*parameters, completionHandler);
1111     if (!session)
1112         return;
1113
1114     auto elementID = findElementOrCompleteWithError(*parameters, completionHandler);
1115     if (!elementID)
1116         return;
1117
1118     session->elementClear(elementID.value(), WTFMove(completionHandler));
1119 }
1120
1121 void WebDriverService::elementSendKeys(RefPtr<InspectorObject>&& parameters, Function<void (CommandResult&&)>&& completionHandler)
1122 {
1123     // §14.3 Element Send Keys.
1124     // https://www.w3.org/TR/webdriver/#element-send-keys
1125     auto session = findSessionOrCompleteWithError(*parameters, completionHandler);
1126     if (!session)
1127         return;
1128
1129     auto elementID = findElementOrCompleteWithError(*parameters, completionHandler);
1130     if (!elementID)
1131         return;
1132
1133     RefPtr<InspectorArray> valueArray;
1134     if (!parameters->getArray(ASCIILiteral("value"), valueArray)) {
1135         completionHandler(CommandResult::fail(CommandResult::ErrorCode::InvalidArgument));
1136         return;
1137     }
1138
1139     unsigned valueArrayLength = valueArray->length();
1140     if (!valueArrayLength) {
1141         completionHandler(CommandResult::fail(CommandResult::ErrorCode::InvalidArgument));
1142         return;
1143     }
1144     Vector<String> value;
1145     value.reserveInitialCapacity(valueArrayLength);
1146     for (unsigned i = 0; i < valueArrayLength; ++i) {
1147         if (auto keyValue = valueArray->get(i)) {
1148             String key;
1149             if (keyValue->asString(key))
1150                 value.uncheckedAppend(WTFMove(key));
1151         }
1152     }
1153     if (!value.size()) {
1154         completionHandler(CommandResult::fail(CommandResult::ErrorCode::InvalidArgument));
1155         return;
1156     }
1157
1158     session->elementSendKeys(elementID.value(), WTFMove(value), WTFMove(completionHandler));
1159 }
1160
1161 void WebDriverService::elementSubmit(RefPtr<InspectorObject>&& parameters, Function<void (CommandResult&&)>&& completionHandler)
1162 {
1163     auto session = findSessionOrCompleteWithError(*parameters, completionHandler);
1164     if (!session)
1165         return;
1166
1167     auto elementID = findElementOrCompleteWithError(*parameters, completionHandler);
1168     if (!elementID)
1169         return;
1170
1171     session->elementSubmit(elementID.value(), WTFMove(completionHandler));
1172 }
1173
1174 static bool findScriptAndArgumentsOrCompleteWithError(InspectorObject& parameters, Function<void (CommandResult&&)>& completionHandler, String& script, RefPtr<InspectorArray>& arguments)
1175 {
1176     if (!parameters.getString(ASCIILiteral("script"), script)) {
1177         completionHandler(CommandResult::fail(CommandResult::ErrorCode::InvalidArgument));
1178         return false;
1179     }
1180     if (!parameters.getArray(ASCIILiteral("args"), arguments)) {
1181         completionHandler(CommandResult::fail(CommandResult::ErrorCode::InvalidArgument));
1182         return false;
1183     }
1184     return true;
1185 }
1186
1187 void WebDriverService::executeScript(RefPtr<InspectorObject>&& parameters, Function<void (CommandResult&&)>&& completionHandler)
1188 {
1189     // §15.2.1 Execute Script.
1190     // https://www.w3.org/TR/webdriver/#execute-script
1191     auto session = findSessionOrCompleteWithError(*parameters, completionHandler);
1192     if (!session)
1193         return;
1194
1195     String script;
1196     RefPtr<InspectorArray> arguments;
1197     if (!findScriptAndArgumentsOrCompleteWithError(*parameters, completionHandler, script, arguments))
1198         return;
1199
1200     session->waitForNavigationToComplete([session, script = WTFMove(script), arguments = WTFMove(arguments), completionHandler = WTFMove(completionHandler)](CommandResult&& result) mutable {
1201         if (result.isError()) {
1202             completionHandler(WTFMove(result));
1203             return;
1204         }
1205         session->executeScript(script, WTFMove(arguments), Session::ExecuteScriptMode::Sync, WTFMove(completionHandler));
1206     });
1207 }
1208
1209 void WebDriverService::executeAsyncScript(RefPtr<InspectorObject>&& parameters, Function<void (CommandResult&&)>&& completionHandler)
1210 {
1211     // §15.2.2 Execute Async Script.
1212     // https://www.w3.org/TR/webdriver/#execute-async-script
1213     auto session = findSessionOrCompleteWithError(*parameters, completionHandler);
1214     if (!session)
1215         return;
1216
1217     String script;
1218     RefPtr<InspectorArray> arguments;
1219     if (!findScriptAndArgumentsOrCompleteWithError(*parameters, completionHandler, script, arguments))
1220         return;
1221
1222     session->waitForNavigationToComplete([session, script = WTFMove(script), arguments = WTFMove(arguments), completionHandler = WTFMove(completionHandler)](CommandResult&& result) mutable {
1223         if (result.isError()) {
1224             completionHandler(WTFMove(result));
1225             return;
1226         }
1227         session->executeScript(script, WTFMove(arguments), Session::ExecuteScriptMode::Async, WTFMove(completionHandler));
1228     });
1229 }
1230
1231 } // namespace WebDriver