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