3d95ab4208e2b981fbb58c6852f2c9c9f117562a
[WebKit-https.git] / Source / WebDriver / Session.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 "Session.h"
28
29 #include "CommandResult.h"
30 #include "SessionHost.h"
31 #include "WebDriverAtoms.h"
32 #include <wtf/CryptographicallyRandomNumber.h>
33 #include <wtf/HashSet.h>
34 #include <wtf/HexNumber.h>
35 #include <wtf/NeverDestroyed.h>
36
37 namespace WebDriver {
38
39 // https://w3c.github.io/webdriver/webdriver-spec.html#dfn-session-script-timeout
40 static const Seconds defaultScriptTimeout = 30_s;
41 // https://w3c.github.io/webdriver/webdriver-spec.html#dfn-session-page-load-timeout
42 static const Seconds defaultPageLoadTimeout = 300_s;
43 // https://w3c.github.io/webdriver/webdriver-spec.html#dfn-session-implicit-wait-timeout
44 static const Seconds defaultImplicitWaitTimeout = 0_s;
45
46 const String& Session::webElementIdentifier()
47 {
48     // The web element identifier is a constant defined by the spec in Section 11 Elements.
49     // https://www.w3.org/TR/webdriver/#elements
50     static NeverDestroyed<String> webElementID { ASCIILiteral("element-6066-11e4-a52e-4f735466cecf") };
51     return webElementID;
52 }
53
54 Session::Session(std::unique_ptr<SessionHost>&& host)
55     : m_host(WTFMove(host))
56     , m_scriptTimeout(defaultScriptTimeout)
57     , m_pageLoadTimeout(defaultPageLoadTimeout)
58     , m_implicitWaitTimeout(defaultImplicitWaitTimeout)
59 {
60     if (capabilities().timeouts)
61         setTimeouts(capabilities().timeouts.value(), [](CommandResult&&) { });
62 }
63
64 Session::~Session()
65 {
66 }
67
68 const String& Session::id() const
69 {
70     return m_host->sessionID();
71 }
72
73 const Capabilities& Session::capabilities() const
74 {
75     return m_host->capabilities();
76 }
77
78 bool Session::isConnected() const
79 {
80     return m_host->isConnected();
81 }
82
83 static std::optional<String> firstWindowHandleInResult(JSON::Value& result)
84 {
85     RefPtr<JSON::Array> handles;
86     if (result.asArray(handles) && handles->length()) {
87         auto handleValue = handles->get(0);
88         String handle;
89         if (handleValue->asString(handle))
90             return handle;
91     }
92     return std::nullopt;
93 }
94
95 void Session::closeAllToplevelBrowsingContexts(const String& toplevelBrowsingContext, Function<void (CommandResult&&)>&& completionHandler)
96 {
97     closeTopLevelBrowsingContext(toplevelBrowsingContext, [this, protectedThis = makeRef(*this), completionHandler = WTFMove(completionHandler)](CommandResult&& result) mutable {
98         if (result.isError()) {
99             completionHandler(WTFMove(result));
100             return;
101         }
102         if (auto handle = firstWindowHandleInResult(*result.result())) {
103             closeAllToplevelBrowsingContexts(handle.value(), WTFMove(completionHandler));
104             return;
105         }
106         completionHandler(CommandResult::success());
107     });
108 }
109
110 void Session::close(Function<void (CommandResult&&)>&& completionHandler)
111 {
112     m_toplevelBrowsingContext = std::nullopt;
113     getWindowHandles([this, completionHandler = WTFMove(completionHandler)](CommandResult&& result) mutable {
114         if (result.isError()) {
115             completionHandler(WTFMove(result));
116             return;
117         }
118         if (auto handle = firstWindowHandleInResult(*result.result())) {
119             closeAllToplevelBrowsingContexts(handle.value(), WTFMove(completionHandler));
120             return;
121         }
122         completionHandler(CommandResult::success());
123     });
124 }
125
126 void Session::getTimeouts(Function<void (CommandResult&&)>&& completionHandler)
127 {
128     RefPtr<JSON::Object> parameters = JSON::Object::create();
129     parameters->setInteger(ASCIILiteral("script"), m_scriptTimeout.millisecondsAs<int>());
130     parameters->setInteger(ASCIILiteral("pageLoad"), m_pageLoadTimeout.millisecondsAs<int>());
131     parameters->setInteger(ASCIILiteral("implicit"), m_implicitWaitTimeout.millisecondsAs<int>());
132     completionHandler(CommandResult::success(WTFMove(parameters)));
133 }
134
135 void Session::setTimeouts(const Timeouts& timeouts, Function<void (CommandResult&&)>&& completionHandler)
136 {
137     if (timeouts.script)
138         m_scriptTimeout = timeouts.script.value();
139     if (timeouts.pageLoad)
140         m_pageLoadTimeout = timeouts.pageLoad.value();
141     if (timeouts.implicit)
142         m_implicitWaitTimeout = timeouts.implicit.value();
143     completionHandler(CommandResult::success());
144 }
145
146 void Session::switchToTopLevelBrowsingContext(std::optional<String> toplevelBrowsingContext)
147 {
148     m_toplevelBrowsingContext = toplevelBrowsingContext;
149     m_currentBrowsingContext = std::nullopt;
150 }
151
152 void Session::switchToBrowsingContext(std::optional<String> browsingContext)
153 {
154     // Automation sends empty strings for main frame.
155     if (!browsingContext || browsingContext.value().isEmpty())
156         m_currentBrowsingContext = std::nullopt;
157     else
158         m_currentBrowsingContext = browsingContext;
159 }
160
161 std::optional<String> Session::pageLoadStrategyString() const
162 {
163     if (!capabilities().pageLoadStrategy)
164         return std::nullopt;
165
166     switch (capabilities().pageLoadStrategy.value()) {
167     case PageLoadStrategy::None:
168         return String("None");
169     case PageLoadStrategy::Normal:
170         return String("Normal");
171     case PageLoadStrategy::Eager:
172         return String("Eager");
173     }
174
175     return std::nullopt;
176 }
177
178 void Session::createTopLevelBrowsingContext(Function<void (CommandResult&&)>&& completionHandler)
179 {
180     ASSERT(!m_toplevelBrowsingContext.value());
181     m_host->sendCommandToBackend(ASCIILiteral("createBrowsingContext"), nullptr, [this, protectedThis = makeRef(*this), completionHandler = WTFMove(completionHandler)](SessionHost::CommandResponse&& response) mutable {
182         if (response.isError || !response.responseObject) {
183             completionHandler(CommandResult::fail(WTFMove(response.responseObject)));
184             return;
185         }
186         String handle;
187         if (!response.responseObject->getString(ASCIILiteral("handle"), handle)) {
188             completionHandler(CommandResult::fail(CommandResult::ErrorCode::UnknownError));
189             return;
190         }
191         switchToTopLevelBrowsingContext(handle);
192         completionHandler(CommandResult::success());
193     });
194 }
195
196 void Session::handleUserPrompts(Function<void (CommandResult&&)>&& completionHandler)
197 {
198     RefPtr<JSON::Object> parameters = JSON::Object::create();
199     parameters->setString(ASCIILiteral("browsingContextHandle"), m_toplevelBrowsingContext.value());
200     m_host->sendCommandToBackend(ASCIILiteral("isShowingJavaScriptDialog"), WTFMove(parameters), [this, protectedThis = makeRef(*this), completionHandler = WTFMove(completionHandler)](SessionHost::CommandResponse&& response) mutable {
201         if (response.isError || !response.responseObject) {
202             completionHandler(CommandResult::fail(WTFMove(response.responseObject)));
203             return;
204         }
205         bool isShowingJavaScriptDialog;
206         if (!response.responseObject->getBoolean("result", isShowingJavaScriptDialog)) {
207             completionHandler(CommandResult::fail(CommandResult::ErrorCode::UnknownError));
208             return;
209         }
210
211         if (!isShowingJavaScriptDialog) {
212             completionHandler(CommandResult::success());
213             return;
214         }
215
216         handleUnexpectedAlertOpen(WTFMove(completionHandler));
217     });
218 }
219
220 void Session::handleUnexpectedAlertOpen(Function<void (CommandResult&&)>&& completionHandler)
221 {
222     switch (capabilities().unhandledPromptBehavior.value_or(UnhandledPromptBehavior::DismissAndNotify)) {
223     case UnhandledPromptBehavior::Dismiss:
224         dismissAlert(WTFMove(completionHandler));
225         break;
226     case UnhandledPromptBehavior::Accept:
227         acceptAlert(WTFMove(completionHandler));
228         break;
229     case UnhandledPromptBehavior::DismissAndNotify:
230         dismissAndNotifyAlert(WTFMove(completionHandler));
231         break;
232     case UnhandledPromptBehavior::AcceptAndNotify:
233         acceptAndNotifyAlert(WTFMove(completionHandler));
234         break;
235     case UnhandledPromptBehavior::Ignore:
236         reportUnexpectedAlertOpen(WTFMove(completionHandler));
237         break;
238     }
239 }
240
241 void Session::dismissAndNotifyAlert(Function<void (CommandResult&&)>&& completionHandler)
242 {
243     reportUnexpectedAlertOpen([this, completionHandler = WTFMove(completionHandler)](CommandResult&& result) mutable {
244         dismissAlert([this, errorResult = WTFMove(result), completionHandler = WTFMove(completionHandler)](CommandResult&& result) mutable {
245             if (result.isError()) {
246                 completionHandler(WTFMove(result));
247                 return;
248             }
249             completionHandler(WTFMove(errorResult));
250         });
251     });
252 }
253
254 void Session::acceptAndNotifyAlert(Function<void (CommandResult&&)>&& completionHandler)
255 {
256     reportUnexpectedAlertOpen([this, completionHandler = WTFMove(completionHandler)](CommandResult&& result) mutable {
257         acceptAlert([this, errorResult = WTFMove(result), completionHandler = WTFMove(completionHandler)](CommandResult&& result) mutable {
258             if (result.isError()) {
259                 completionHandler(WTFMove(result));
260                 return;
261             }
262             completionHandler(WTFMove(errorResult));
263         });
264     });
265 }
266
267 void Session::reportUnexpectedAlertOpen(Function<void (CommandResult&&)>&& completionHandler)
268 {
269     getAlertText([this, completionHandler = WTFMove(completionHandler)](CommandResult&& result) {
270         std::optional<String> alertText;
271         if (!result.isError()) {
272             String valueString;
273             if (result.result()->asString(valueString))
274                 alertText = valueString;
275         }
276         auto errorResult = CommandResult::fail(CommandResult::ErrorCode::UnexpectedAlertOpen);
277         if (alertText) {
278             RefPtr<JSON::Object> additonalData = JSON::Object::create();
279             additonalData->setString(ASCIILiteral("text"), alertText.value());
280             errorResult.setAdditionalErrorData(WTFMove(additonalData));
281         }
282         completionHandler(WTFMove(errorResult));
283     });
284 }
285
286 void Session::go(const String& url, Function<void (CommandResult&&)>&& completionHandler)
287 {
288     if (!m_toplevelBrowsingContext) {
289         completionHandler(CommandResult::fail(CommandResult::ErrorCode::NoSuchWindow));
290         return;
291     }
292
293     handleUserPrompts([this, url, completionHandler = WTFMove(completionHandler)](CommandResult&& result) mutable {
294         if (result.isError()) {
295             completionHandler(WTFMove(result));
296             return;
297         }
298
299         RefPtr<JSON::Object> parameters = JSON::Object::create();
300         parameters->setString(ASCIILiteral("handle"), m_toplevelBrowsingContext.value());
301         parameters->setString(ASCIILiteral("url"), url);
302         parameters->setInteger(ASCIILiteral("pageLoadTimeout"), m_pageLoadTimeout.millisecondsAs<int>());
303         if (auto pageLoadStrategy = pageLoadStrategyString())
304             parameters->setString(ASCIILiteral("pageLoadStrategy"), pageLoadStrategy.value());
305         m_host->sendCommandToBackend(ASCIILiteral("navigateBrowsingContext"), WTFMove(parameters), [this, protectedThis = makeRef(*this), completionHandler = WTFMove(completionHandler)](SessionHost::CommandResponse&& response) {
306             if (response.isError) {
307                 completionHandler(CommandResult::fail(WTFMove(response.responseObject)));
308                 return;
309             }
310             switchToBrowsingContext(std::nullopt);
311             completionHandler(CommandResult::success());
312         });
313     });
314 }
315
316 void Session::getCurrentURL(Function<void (CommandResult&&)>&& completionHandler)
317 {
318     if (!m_toplevelBrowsingContext) {
319         completionHandler(CommandResult::fail(CommandResult::ErrorCode::NoSuchWindow));
320         return;
321     }
322
323     handleUserPrompts([this, completionHandler = WTFMove(completionHandler)](CommandResult&& result) mutable {
324         if (result.isError()) {
325             completionHandler(WTFMove(result));
326             return;
327         }
328
329         RefPtr<JSON::Object> parameters = JSON::Object::create();
330         parameters->setString(ASCIILiteral("handle"), m_toplevelBrowsingContext.value());
331         m_host->sendCommandToBackend(ASCIILiteral("getBrowsingContext"), WTFMove(parameters), [this, protectedThis = makeRef(*this), completionHandler = WTFMove(completionHandler)](SessionHost::CommandResponse&& response) {
332             if (response.isError || !response.responseObject) {
333                 completionHandler(CommandResult::fail(WTFMove(response.responseObject)));
334                 return;
335             }
336             RefPtr<JSON::Object> browsingContext;
337             if (!response.responseObject->getObject("context", browsingContext)) {
338                 completionHandler(CommandResult::fail(CommandResult::ErrorCode::UnknownError));
339                 return;
340             }
341             String url;
342             if (!browsingContext->getString("url", url)) {
343                 completionHandler(CommandResult::fail(CommandResult::ErrorCode::UnknownError));
344                 return;
345             }
346             completionHandler(CommandResult::success(JSON::Value::create(url)));
347         });
348     });
349 }
350
351 void Session::back(Function<void (CommandResult&&)>&& completionHandler)
352 {
353     if (!m_toplevelBrowsingContext) {
354         completionHandler(CommandResult::fail(CommandResult::ErrorCode::NoSuchWindow));
355         return;
356     }
357
358     handleUserPrompts([this, completionHandler = WTFMove(completionHandler)](CommandResult&& result) mutable {
359         if (result.isError()) {
360             completionHandler(WTFMove(result));
361             return;
362         }
363         RefPtr<JSON::Object> parameters = JSON::Object::create();
364         parameters->setString(ASCIILiteral("handle"), m_toplevelBrowsingContext.value());
365         parameters->setInteger(ASCIILiteral("pageLoadTimeout"), m_pageLoadTimeout.millisecondsAs<int>());
366         if (auto pageLoadStrategy = pageLoadStrategyString())
367             parameters->setString(ASCIILiteral("pageLoadStrategy"), pageLoadStrategy.value());
368         m_host->sendCommandToBackend(ASCIILiteral("goBackInBrowsingContext"), WTFMove(parameters), [this, protectedThis = makeRef(*this), completionHandler = WTFMove(completionHandler)](SessionHost::CommandResponse&& response) {
369             if (response.isError) {
370                 completionHandler(CommandResult::fail(WTFMove(response.responseObject)));
371                 return;
372             }
373             switchToBrowsingContext(std::nullopt);
374             completionHandler(CommandResult::success());
375         });
376     });
377 }
378
379 void Session::forward(Function<void (CommandResult&&)>&& completionHandler)
380 {
381     if (!m_toplevelBrowsingContext) {
382         completionHandler(CommandResult::fail(CommandResult::ErrorCode::NoSuchWindow));
383         return;
384     }
385
386     handleUserPrompts([this, completionHandler = WTFMove(completionHandler)](CommandResult&& result) mutable {
387         if (result.isError()) {
388             completionHandler(WTFMove(result));
389             return;
390         }
391         RefPtr<JSON::Object> parameters = JSON::Object::create();
392         parameters->setString(ASCIILiteral("handle"), m_toplevelBrowsingContext.value());
393         parameters->setInteger(ASCIILiteral("pageLoadTimeout"), m_pageLoadTimeout.millisecondsAs<int>());
394         if (auto pageLoadStrategy = pageLoadStrategyString())
395             parameters->setString(ASCIILiteral("pageLoadStrategy"), pageLoadStrategy.value());
396         m_host->sendCommandToBackend(ASCIILiteral("goForwardInBrowsingContext"), WTFMove(parameters), [this, protectedThis = makeRef(*this), completionHandler = WTFMove(completionHandler)](SessionHost::CommandResponse&& response) {
397             if (response.isError) {
398                 completionHandler(CommandResult::fail(WTFMove(response.responseObject)));
399                 return;
400             }
401             switchToBrowsingContext(std::nullopt);
402             completionHandler(CommandResult::success());
403         });
404     });
405 }
406
407 void Session::refresh(Function<void (CommandResult&&)>&& completionHandler)
408 {
409     if (!m_toplevelBrowsingContext) {
410         completionHandler(CommandResult::fail(CommandResult::ErrorCode::NoSuchWindow));
411         return;
412     }
413
414     handleUserPrompts([this, completionHandler = WTFMove(completionHandler)](CommandResult&& result) mutable {
415         if (result.isError()) {
416             completionHandler(WTFMove(result));
417             return;
418         }
419         RefPtr<JSON::Object> parameters = JSON::Object::create();
420         parameters->setString(ASCIILiteral("handle"), m_toplevelBrowsingContext.value());
421         parameters->setInteger(ASCIILiteral("pageLoadTimeout"), m_pageLoadTimeout.millisecondsAs<int>());
422         if (auto pageLoadStrategy = pageLoadStrategyString())
423             parameters->setString(ASCIILiteral("pageLoadStrategy"), pageLoadStrategy.value());
424         m_host->sendCommandToBackend(ASCIILiteral("reloadBrowsingContext"), WTFMove(parameters), [this, protectedThis = makeRef(*this), completionHandler = WTFMove(completionHandler)](SessionHost::CommandResponse&& response) {
425             if (response.isError) {
426                 completionHandler(CommandResult::fail(WTFMove(response.responseObject)));
427                 return;
428             }
429             switchToBrowsingContext(std::nullopt);
430             completionHandler(CommandResult::success());
431         });
432     });
433 }
434
435 void Session::getTitle(Function<void (CommandResult&&)>&& completionHandler)
436 {
437     if (!m_toplevelBrowsingContext) {
438         completionHandler(CommandResult::fail(CommandResult::ErrorCode::NoSuchWindow));
439         return;
440     }
441
442     handleUserPrompts([this, completionHandler = WTFMove(completionHandler)](CommandResult&& result) mutable {
443         if (result.isError()) {
444             completionHandler(WTFMove(result));
445             return;
446         }
447         RefPtr<JSON::Object> parameters = JSON::Object::create();
448         parameters->setString(ASCIILiteral("browsingContextHandle"), m_toplevelBrowsingContext.value());
449         parameters->setString(ASCIILiteral("function"), ASCIILiteral("function() { return document.title; }"));
450         parameters->setArray(ASCIILiteral("arguments"), JSON::Array::create());
451         m_host->sendCommandToBackend(ASCIILiteral("evaluateJavaScriptFunction"), WTFMove(parameters), [this, protectedThis = makeRef(*this), completionHandler = WTFMove(completionHandler)](SessionHost::CommandResponse&& response) {
452             if (response.isError || !response.responseObject) {
453                 completionHandler(CommandResult::fail(WTFMove(response.responseObject)));
454                 return;
455             }
456             String valueString;
457             if (!response.responseObject->getString(ASCIILiteral("result"), valueString)) {
458                 completionHandler(CommandResult::fail(CommandResult::ErrorCode::UnknownError));
459                 return;
460             }
461             RefPtr<JSON::Value> resultValue;
462             if (!JSON::Value::parseJSON(valueString, resultValue)) {
463                 completionHandler(CommandResult::fail(CommandResult::ErrorCode::UnknownError));
464                 return;
465             }
466             completionHandler(CommandResult::success(WTFMove(resultValue)));
467         });
468     });
469 }
470
471 void Session::getWindowHandle(Function<void (CommandResult&&)>&& completionHandler)
472 {
473     if (!m_toplevelBrowsingContext) {
474         completionHandler(CommandResult::fail(CommandResult::ErrorCode::NoSuchWindow));
475         return;
476     }
477
478     RefPtr<JSON::Object> parameters = JSON::Object::create();
479     parameters->setString(ASCIILiteral("handle"), m_toplevelBrowsingContext.value());
480     m_host->sendCommandToBackend(ASCIILiteral("getBrowsingContext"), WTFMove(parameters), [this, protectedThis = makeRef(*this), completionHandler = WTFMove(completionHandler)](SessionHost::CommandResponse&& response) {
481         if (response.isError || !response.responseObject) {
482             completionHandler(CommandResult::fail(WTFMove(response.responseObject)));
483             return;
484         }
485         RefPtr<JSON::Object> browsingContext;
486         if (!response.responseObject->getObject(ASCIILiteral("context"), browsingContext)) {
487             completionHandler(CommandResult::fail(CommandResult::ErrorCode::UnknownError));
488             return;
489         }
490         String handle;
491         if (!browsingContext->getString(ASCIILiteral("handle"), handle)) {
492             completionHandler(CommandResult::fail(CommandResult::ErrorCode::UnknownError));
493             return;
494         }
495         completionHandler(CommandResult::success(JSON::Value::create(handle)));
496     });
497 }
498
499 void Session::closeTopLevelBrowsingContext(const String& toplevelBrowsingContext, Function<void (CommandResult&&)>&& completionHandler)
500 {
501     RefPtr<JSON::Object> parameters = JSON::Object::create();
502     parameters->setString(ASCIILiteral("handle"), toplevelBrowsingContext);
503     m_host->sendCommandToBackend(ASCIILiteral("closeBrowsingContext"), WTFMove(parameters), [this, protectedThis = makeRef(*this), completionHandler = WTFMove(completionHandler)](SessionHost::CommandResponse&& response) mutable {
504         if (!m_host->isConnected()) {
505             // Closing the browsing context made the browser quit.
506             completionHandler(CommandResult::success(JSON::Array::create()));
507             return;
508         }
509         if (response.isError) {
510             completionHandler(CommandResult::fail(WTFMove(response.responseObject)));
511             return;
512         }
513
514         getWindowHandles([this, completionHandler = WTFMove(completionHandler)](CommandResult&& result) {
515             if (!m_host->isConnected()) {
516                 // Closing the browsing context made the browser quit.
517                 completionHandler(CommandResult::success(JSON::Array::create()));
518                 return;
519             }
520             completionHandler(WTFMove(result));
521         });
522     });
523 }
524
525 void Session::closeWindow(Function<void (CommandResult&&)>&& completionHandler)
526 {
527     if (!m_toplevelBrowsingContext) {
528         completionHandler(CommandResult::fail(CommandResult::ErrorCode::NoSuchWindow));
529         return;
530     }
531
532     handleUserPrompts([this, completionHandler = WTFMove(completionHandler)](CommandResult&& result) mutable {
533         if (result.isError()) {
534             completionHandler(WTFMove(result));
535             return;
536         }
537         auto toplevelBrowsingContext = std::exchange(m_toplevelBrowsingContext, std::nullopt);
538         closeTopLevelBrowsingContext(toplevelBrowsingContext.value(), WTFMove(completionHandler));
539     });
540 }
541
542 void Session::switchToWindow(const String& windowHandle, Function<void (CommandResult&&)>&& completionHandler)
543 {
544     RefPtr<JSON::Object> parameters = JSON::Object::create();
545     parameters->setString(ASCIILiteral("browsingContextHandle"), windowHandle);
546     m_host->sendCommandToBackend(ASCIILiteral("switchToBrowsingContext"), WTFMove(parameters), [this, protectedThis = makeRef(*this), windowHandle, completionHandler = WTFMove(completionHandler)](SessionHost::CommandResponse&& response) {
547         if (response.isError) {
548             completionHandler(CommandResult::fail(WTFMove(response.responseObject)));
549             return;
550         }
551         switchToTopLevelBrowsingContext(windowHandle);
552         completionHandler(CommandResult::success());
553     });
554 }
555
556 void Session::getWindowHandles(Function<void (CommandResult&&)>&& completionHandler)
557 {
558     m_host->sendCommandToBackend(ASCIILiteral("getBrowsingContexts"), JSON::Object::create(), [this, protectedThis = makeRef(*this), completionHandler = WTFMove(completionHandler)](SessionHost::CommandResponse&& response) {
559         if (response.isError || !response.responseObject) {
560             completionHandler(CommandResult::fail(WTFMove(response.responseObject)));
561             return;
562         }
563         RefPtr<JSON::Array> browsingContextArray;
564         if (!response.responseObject->getArray(ASCIILiteral("contexts"), browsingContextArray)) {
565             completionHandler(CommandResult::fail(CommandResult::ErrorCode::UnknownError));
566             return;
567         }
568         RefPtr<JSON::Array> windowHandles = JSON::Array::create();
569         for (unsigned i = 0; i < browsingContextArray->length(); ++i) {
570             RefPtr<JSON::Value> browsingContextValue = browsingContextArray->get(i);
571             RefPtr<JSON::Object> browsingContext;
572             if (!browsingContextValue->asObject(browsingContext)) {
573                 completionHandler(CommandResult::fail(CommandResult::ErrorCode::UnknownError));
574                 return;
575             }
576
577             String handle;
578             if (!browsingContext->getString(ASCIILiteral("handle"), handle)) {
579                 completionHandler(CommandResult::fail(CommandResult::ErrorCode::UnknownError));
580                 return;
581             }
582
583             windowHandles->pushString(handle);
584         }
585         completionHandler(CommandResult::success(WTFMove(windowHandles)));
586     });
587 }
588
589 void Session::switchToFrame(RefPtr<JSON::Value>&& frameID, Function<void (CommandResult&&)>&& completionHandler)
590 {
591     if (!m_toplevelBrowsingContext) {
592         completionHandler(CommandResult::fail(CommandResult::ErrorCode::NoSuchWindow));
593         return;
594     }
595
596     if (frameID->isNull()) {
597         switchToBrowsingContext(std::nullopt);
598         completionHandler(CommandResult::success());
599         return;
600     }
601
602     handleUserPrompts([this, frameID = WTFMove(frameID), completionHandler = WTFMove(completionHandler)](CommandResult&& result) mutable {
603         if (result.isError()) {
604             completionHandler(WTFMove(result));
605             return;
606         }
607         RefPtr<JSON::Object> parameters = JSON::Object::create();
608         parameters->setString(ASCIILiteral("browsingContextHandle"), m_toplevelBrowsingContext.value());
609         if (m_currentBrowsingContext)
610             parameters->setString(ASCIILiteral("frameHandle"), m_currentBrowsingContext.value());
611
612         int frameIndex;
613         if (frameID->asInteger(frameIndex)) {
614             if (frameIndex < 0 || frameIndex > USHRT_MAX) {
615                 completionHandler(CommandResult::fail(CommandResult::ErrorCode::NoSuchFrame));
616                 return;
617             }
618             parameters->setInteger(ASCIILiteral("ordinal"), frameIndex);
619         } else {
620             String frameElementID = extractElementID(*frameID);
621             if (!frameElementID.isEmpty())
622                 parameters->setString(ASCIILiteral("nodeHandle"), frameElementID);
623             else {
624                 String frameName;
625                 if (!frameID->asString(frameName)) {
626                     completionHandler(CommandResult::fail(CommandResult::ErrorCode::NoSuchFrame));
627                     return;
628                 }
629                 parameters->setString(ASCIILiteral("name"), frameName);
630             }
631         }
632
633         m_host->sendCommandToBackend(ASCIILiteral("resolveChildFrameHandle"), WTFMove(parameters), [this, protectedThis = makeRef(*this), completionHandler = WTFMove(completionHandler)](SessionHost::CommandResponse&& response) {
634             if (response.isError || !response.responseObject) {
635                 completionHandler(CommandResult::fail(WTFMove(response.responseObject)));
636                 return;
637             }
638             String frameHandle;
639             if (!response.responseObject->getString(ASCIILiteral("result"), frameHandle)) {
640                 completionHandler(CommandResult::fail(CommandResult::ErrorCode::UnknownError));
641                 return;
642             }
643             switchToBrowsingContext(frameHandle);
644             completionHandler(CommandResult::success());
645         });
646     });
647 }
648
649 void Session::switchToParentFrame(Function<void (CommandResult&&)>&& completionHandler)
650 {
651     if (!m_toplevelBrowsingContext) {
652         completionHandler(CommandResult::fail(CommandResult::ErrorCode::NoSuchWindow));
653         return;
654     }
655
656     if (!m_currentBrowsingContext) {
657         completionHandler(CommandResult::success());
658         return;
659     }
660
661     handleUserPrompts([this, completionHandler = WTFMove(completionHandler)](CommandResult&& result) mutable {
662         if (result.isError()) {
663             completionHandler(WTFMove(result));
664             return;
665         }
666         RefPtr<JSON::Object> parameters = JSON::Object::create();
667         parameters->setString(ASCIILiteral("browsingContextHandle"), m_toplevelBrowsingContext.value());
668         parameters->setString(ASCIILiteral("frameHandle"), m_currentBrowsingContext.value());
669         m_host->sendCommandToBackend(ASCIILiteral("resolveParentFrameHandle"), WTFMove(parameters), [this, protectedThis = makeRef(*this), completionHandler = WTFMove(completionHandler)](SessionHost::CommandResponse&& response) {
670             if (response.isError || !response.responseObject) {
671                 completionHandler(CommandResult::fail(WTFMove(response.responseObject)));
672                 return;
673             }
674             String frameHandle;
675             if (!response.responseObject->getString(ASCIILiteral("result"), frameHandle)) {
676                 completionHandler(CommandResult::fail(CommandResult::ErrorCode::UnknownError));
677                 return;
678             }
679             switchToBrowsingContext(frameHandle);
680             completionHandler(CommandResult::success());
681         });
682     });
683 }
684
685 void Session::getToplevelBrowsingContextRect(Function<void (CommandResult&&)>&& completionHandler)
686 {
687     RefPtr<JSON::Object> parameters = JSON::Object::create();
688     parameters->setString(ASCIILiteral("handle"), m_toplevelBrowsingContext.value());
689     m_host->sendCommandToBackend(ASCIILiteral("getBrowsingContext"), WTFMove(parameters), [this, protectedThis = makeRef(*this), completionHandler = WTFMove(completionHandler)](SessionHost::CommandResponse&& response) {
690         if (response.isError || !response.responseObject) {
691             completionHandler(CommandResult::fail(WTFMove(response.responseObject)));
692             return;
693         }
694         RefPtr<JSON::Object> browsingContext;
695         if (!response.responseObject->getObject(ASCIILiteral("context"), browsingContext)) {
696             completionHandler(CommandResult::fail(CommandResult::ErrorCode::UnknownError));
697             return;
698         }
699         RefPtr<JSON::Object> windowOrigin;
700         double x, y;
701         if (!browsingContext->getObject(ASCIILiteral("windowOrigin"), windowOrigin)
702             || !windowOrigin->getDouble(ASCIILiteral("x"), x)
703             || !windowOrigin->getDouble(ASCIILiteral("y"), y)) {
704             completionHandler(CommandResult::fail(CommandResult::ErrorCode::UnknownError));
705             return;
706         }
707         RefPtr<JSON::Object> windowSize;
708         double width, height;
709         if (!browsingContext->getObject(ASCIILiteral("windowSize"), windowSize)
710             || !windowSize->getDouble(ASCIILiteral("width"), width)
711             || !windowSize->getDouble(ASCIILiteral("height"), height)) {
712             completionHandler(CommandResult::fail(CommandResult::ErrorCode::UnknownError));
713             return;
714         }
715         auto windowRect = JSON::Object::create();
716         windowRect->setDouble(ASCIILiteral("x"), x);
717         windowRect->setDouble(ASCIILiteral("y"), y);
718         windowRect->setDouble(ASCIILiteral("width"), width);
719         windowRect->setDouble(ASCIILiteral("height"), height);
720         completionHandler(CommandResult::success(WTFMove(windowRect)));
721     });
722 }
723
724 void Session::getWindowRect(Function<void (CommandResult&&)>&& completionHandler)
725 {
726     if (!m_toplevelBrowsingContext) {
727         completionHandler(CommandResult::fail(CommandResult::ErrorCode::NoSuchWindow));
728         return;
729     }
730
731     handleUserPrompts([this, completionHandler = WTFMove(completionHandler)](CommandResult&& result) mutable {
732         if (result.isError()) {
733             completionHandler(WTFMove(result));
734             return;
735         }
736         getToplevelBrowsingContextRect(WTFMove(completionHandler));
737     });
738 }
739
740 void Session::setWindowRect(std::optional<double> x, std::optional<double> y, std::optional<double> width, std::optional<double> height, Function<void (CommandResult&&)>&& completionHandler)
741 {
742     if (!m_toplevelBrowsingContext) {
743         completionHandler(CommandResult::fail(CommandResult::ErrorCode::NoSuchWindow));
744         return;
745     }
746
747     handleUserPrompts([this, x, y, width, height, completionHandler = WTFMove(completionHandler)](CommandResult&& result) mutable {
748         if (result.isError()) {
749             completionHandler(WTFMove(result));
750             return;
751         }
752
753         RefPtr<JSON::Object> parameters = JSON::Object::create();
754         parameters->setString(ASCIILiteral("handle"), m_toplevelBrowsingContext.value());
755         if (x && y) {
756             RefPtr<JSON::Object> windowOrigin = JSON::Object::create();
757             windowOrigin->setDouble("x", x.value());
758             windowOrigin->setDouble("y", y.value());
759             parameters->setObject(ASCIILiteral("origin"), WTFMove(windowOrigin));
760         }
761         if (width && height) {
762             RefPtr<JSON::Object> windowSize = JSON::Object::create();
763             windowSize->setDouble("width", width.value());
764             windowSize->setDouble("height", height.value());
765             parameters->setObject(ASCIILiteral("size"), WTFMove(windowSize));
766         }
767         m_host->sendCommandToBackend(ASCIILiteral("setWindowFrameOfBrowsingContext"), WTFMove(parameters), [this, protectedThis = makeRef(*this), completionHandler = WTFMove(completionHandler)] (SessionHost::CommandResponse&& response) mutable {
768             if (response.isError) {
769                 completionHandler(CommandResult::fail(WTFMove(response.responseObject)));
770                 return;
771             }
772             getToplevelBrowsingContextRect(WTFMove(completionHandler));
773         });
774     });
775 }
776
777 RefPtr<JSON::Object> Session::createElement(RefPtr<JSON::Value>&& value)
778 {
779     RefPtr<JSON::Object> valueObject;
780     if (!value->asObject(valueObject))
781         return nullptr;
782
783     String elementID;
784     if (!valueObject->getString("session-node-" + id(), elementID))
785         return nullptr;
786
787     RefPtr<JSON::Object> elementObject = JSON::Object::create();
788     elementObject->setString(webElementIdentifier(), elementID);
789     return elementObject;
790 }
791
792 RefPtr<JSON::Object> Session::createElement(const String& elementID)
793 {
794     RefPtr<JSON::Object> elementObject = JSON::Object::create();
795     elementObject->setString("session-node-" + id(), elementID);
796     return elementObject;
797 }
798
799 RefPtr<JSON::Object> Session::extractElement(JSON::Value& value)
800 {
801     String elementID = extractElementID(value);
802     return !elementID.isEmpty() ? createElement(elementID) : nullptr;
803 }
804
805 String Session::extractElementID(JSON::Value& value)
806 {
807     RefPtr<JSON::Object> valueObject;
808     if (!value.asObject(valueObject))
809         return emptyString();
810
811     String elementID;
812     if (!valueObject->getString(webElementIdentifier(), elementID))
813         return emptyString();
814
815     return elementID;
816 }
817
818 void Session::computeElementLayout(const String& elementID, OptionSet<ElementLayoutOption> options, Function<void (std::optional<Rect>&&, std::optional<Point>&&, bool, RefPtr<JSON::Object>&&)>&& completionHandler)
819 {
820     ASSERT(m_toplevelBrowsingContext.value());
821
822     RefPtr<JSON::Object> parameters = JSON::Object::create();
823     parameters->setString(ASCIILiteral("browsingContextHandle"), m_toplevelBrowsingContext.value());
824     parameters->setString(ASCIILiteral("frameHandle"), m_currentBrowsingContext.value_or(emptyString()));
825     parameters->setString(ASCIILiteral("nodeHandle"), elementID);
826     parameters->setBoolean(ASCIILiteral("scrollIntoViewIfNeeded"), options.contains(ElementLayoutOption::ScrollIntoViewIfNeeded));
827     parameters->setString(ASCIILiteral("coordinateSystem"), options.contains(ElementLayoutOption::UseViewportCoordinates) ? ASCIILiteral("LayoutViewport") : ASCIILiteral("Page"));
828     m_host->sendCommandToBackend(ASCIILiteral("computeElementLayout"), WTFMove(parameters), [this, protectedThis = makeRef(*this), completionHandler = WTFMove(completionHandler)](SessionHost::CommandResponse&& response) mutable {
829         if (response.isError || !response.responseObject) {
830             completionHandler(std::nullopt, std::nullopt, false, WTFMove(response.responseObject));
831             return;
832         }
833         RefPtr<JSON::Object> rectObject;
834         if (!response.responseObject->getObject(ASCIILiteral("rect"), rectObject)) {
835             completionHandler(std::nullopt, std::nullopt, false, nullptr);
836             return;
837         }
838         std::optional<int> elementX;
839         std::optional<int> elementY;
840         RefPtr<JSON::Object> elementPosition;
841         if (rectObject->getObject(ASCIILiteral("origin"), elementPosition)) {
842             int x, y;
843             if (elementPosition->getInteger(ASCIILiteral("x"), x) && elementPosition->getInteger(ASCIILiteral("y"), y)) {
844                 elementX = x;
845                 elementY = y;
846             }
847         }
848         if (!elementX || !elementY) {
849             completionHandler(std::nullopt, std::nullopt, false, nullptr);
850             return;
851         }
852         std::optional<int> elementWidth;
853         std::optional<int> elementHeight;
854         RefPtr<JSON::Object> elementSize;
855         if (rectObject->getObject(ASCIILiteral("size"), elementSize)) {
856             int width, height;
857             if (elementSize->getInteger(ASCIILiteral("width"), width) && elementSize->getInteger(ASCIILiteral("height"), height)) {
858                 elementWidth = width;
859                 elementHeight = height;
860             }
861         }
862         if (!elementWidth || !elementHeight) {
863             completionHandler(std::nullopt, std::nullopt, false, nullptr);
864             return;
865         }
866         Rect rect = { { elementX.value(), elementY.value() }, { elementWidth.value(), elementHeight.value() } };
867
868         bool isObscured;
869         if (!response.responseObject->getBoolean(ASCIILiteral("isObscured"), isObscured)) {
870             completionHandler(std::nullopt, std::nullopt, false, nullptr);
871             return;
872         }
873         RefPtr<JSON::Object> inViewCenterPointObject;
874         if (!response.responseObject->getObject(ASCIILiteral("inViewCenterPoint"), inViewCenterPointObject)) {
875             completionHandler(rect, std::nullopt, isObscured, nullptr);
876             return;
877         }
878         int inViewCenterPointX, inViewCenterPointY;
879         if (!inViewCenterPointObject->getInteger(ASCIILiteral("x"), inViewCenterPointX)
880             || !inViewCenterPointObject->getInteger(ASCIILiteral("y"), inViewCenterPointY)) {
881             completionHandler(std::nullopt, std::nullopt, isObscured, nullptr);
882             return;
883         }
884         Point inViewCenterPoint = { inViewCenterPointX, inViewCenterPointY };
885         completionHandler(rect, inViewCenterPoint, isObscured, nullptr);
886     });
887 }
888
889 void Session::findElements(const String& strategy, const String& selector, FindElementsMode mode, const String& rootElementID, Function<void (CommandResult&&)>&& completionHandler)
890 {
891     if (!m_toplevelBrowsingContext) {
892         completionHandler(CommandResult::fail(CommandResult::ErrorCode::NoSuchWindow));
893         return;
894     }
895
896     RefPtr<JSON::Array> arguments = JSON::Array::create();
897     arguments->pushString(JSON::Value::create(strategy)->toJSONString());
898     if (rootElementID.isEmpty())
899         arguments->pushString(JSON::Value::null()->toJSONString());
900     else
901         arguments->pushString(createElement(rootElementID)->toJSONString());
902     arguments->pushString(JSON::Value::create(selector)->toJSONString());
903     arguments->pushString(JSON::Value::create(mode == FindElementsMode::Single)->toJSONString());
904     arguments->pushString(JSON::Value::create(m_implicitWaitTimeout.milliseconds())->toJSONString());
905
906     RefPtr<JSON::Object> parameters = JSON::Object::create();
907     parameters->setString(ASCIILiteral("browsingContextHandle"), m_toplevelBrowsingContext.value());
908     if (m_currentBrowsingContext)
909         parameters->setString(ASCIILiteral("frameHandle"), m_currentBrowsingContext.value());
910     parameters->setString(ASCIILiteral("function"), FindNodesJavaScript);
911     parameters->setArray(ASCIILiteral("arguments"), WTFMove(arguments));
912     parameters->setBoolean(ASCIILiteral("expectsImplicitCallbackArgument"), true);
913     // If there's an implicit wait, use one second more as callback timeout.
914     if (m_implicitWaitTimeout)
915         parameters->setInteger(ASCIILiteral("callbackTimeout"), Seconds(m_implicitWaitTimeout + 1_s).millisecondsAs<int>());
916
917     m_host->sendCommandToBackend(ASCIILiteral("evaluateJavaScriptFunction"), WTFMove(parameters), [this, protectedThis = makeRef(*this), mode, completionHandler = WTFMove(completionHandler)](SessionHost::CommandResponse&& response) {
918         if (response.isError || !response.responseObject) {
919             completionHandler(CommandResult::fail(WTFMove(response.responseObject)));
920             return;
921         }
922         String valueString;
923         if (!response.responseObject->getString(ASCIILiteral("result"), valueString)) {
924             completionHandler(CommandResult::fail(CommandResult::ErrorCode::UnknownError));
925             return;
926         }
927         RefPtr<JSON::Value> resultValue;
928         if (!JSON::Value::parseJSON(valueString, resultValue)) {
929             completionHandler(CommandResult::fail(CommandResult::ErrorCode::UnknownError));
930             return;
931         }
932
933         switch (mode) {
934         case FindElementsMode::Single: {
935             RefPtr<JSON::Object> elementObject = createElement(WTFMove(resultValue));
936             if (!elementObject) {
937                 completionHandler(CommandResult::fail(CommandResult::ErrorCode::NoSuchElement));
938                 return;
939             }
940             completionHandler(CommandResult::success(WTFMove(elementObject)));
941             break;
942         }
943         case FindElementsMode::Multiple: {
944             RefPtr<JSON::Array> elementsArray;
945             if (!resultValue->asArray(elementsArray)) {
946                 completionHandler(CommandResult::fail(CommandResult::ErrorCode::NoSuchElement));
947                 return;
948             }
949             RefPtr<JSON::Array> elementObjectsArray = JSON::Array::create();
950             unsigned elementsArrayLength = elementsArray->length();
951             for (unsigned i = 0; i < elementsArrayLength; ++i) {
952                 if (auto elementObject = createElement(elementsArray->get(i)))
953                     elementObjectsArray->pushObject(WTFMove(elementObject));
954             }
955             completionHandler(CommandResult::success(WTFMove(elementObjectsArray)));
956             break;
957         }
958         }
959     });
960 }
961
962 void Session::getActiveElement(Function<void (CommandResult&&)>&& completionHandler)
963 {
964     if (!m_toplevelBrowsingContext) {
965         completionHandler(CommandResult::fail(CommandResult::ErrorCode::NoSuchWindow));
966         return;
967     }
968
969     handleUserPrompts([this, completionHandler = WTFMove(completionHandler)](CommandResult&& result) mutable {
970         if (result.isError()) {
971             completionHandler(WTFMove(result));
972             return;
973         }
974         RefPtr<JSON::Object> parameters = JSON::Object::create();
975         parameters->setString(ASCIILiteral("browsingContextHandle"), m_toplevelBrowsingContext.value());
976         parameters->setString(ASCIILiteral("function"), ASCIILiteral("function() { return document.activeElement; }"));
977         parameters->setArray(ASCIILiteral("arguments"), JSON::Array::create());
978         m_host->sendCommandToBackend(ASCIILiteral("evaluateJavaScriptFunction"), WTFMove(parameters), [this, protectedThis = makeRef(*this), completionHandler = WTFMove(completionHandler)](SessionHost::CommandResponse&& response) {
979             if (response.isError || !response.responseObject) {
980                 completionHandler(CommandResult::fail(WTFMove(response.responseObject)));
981                 return;
982             }
983             String valueString;
984             if (!response.responseObject->getString(ASCIILiteral("result"), valueString)) {
985                 completionHandler(CommandResult::fail(CommandResult::ErrorCode::UnknownError));
986                 return;
987             }
988             RefPtr<JSON::Value> resultValue;
989             if (!JSON::Value::parseJSON(valueString, resultValue)) {
990                 completionHandler(CommandResult::fail(CommandResult::ErrorCode::UnknownError));
991                 return;
992             }
993             RefPtr<JSON::Object> elementObject = createElement(WTFMove(resultValue));
994             if (!elementObject) {
995                 completionHandler(CommandResult::fail(CommandResult::ErrorCode::NoSuchElement));
996                 return;
997             }
998             completionHandler(CommandResult::success(WTFMove(elementObject)));
999         });
1000     });
1001 }
1002
1003 void Session::isElementSelected(const String& elementID, Function<void (CommandResult&&)>&& completionHandler)
1004 {
1005     if (!m_toplevelBrowsingContext) {
1006         completionHandler(CommandResult::fail(CommandResult::ErrorCode::NoSuchWindow));
1007         return;
1008     }
1009
1010     handleUserPrompts([this, elementID, completionHandler = WTFMove(completionHandler)](CommandResult&& result) mutable {
1011         if (result.isError()) {
1012             completionHandler(WTFMove(result));
1013             return;
1014         }
1015         RefPtr<JSON::Array> arguments = JSON::Array::create();
1016         arguments->pushString(createElement(elementID)->toJSONString());
1017         arguments->pushString(JSON::Value::create("selected")->toJSONString());
1018
1019         RefPtr<JSON::Object> parameters = JSON::Object::create();
1020         parameters->setString(ASCIILiteral("browsingContextHandle"), m_toplevelBrowsingContext.value());
1021         if (m_currentBrowsingContext)
1022             parameters->setString(ASCIILiteral("frameHandle"), m_currentBrowsingContext.value());
1023         parameters->setString(ASCIILiteral("function"), ElementAttributeJavaScript);
1024         parameters->setArray(ASCIILiteral("arguments"), WTFMove(arguments));
1025         m_host->sendCommandToBackend(ASCIILiteral("evaluateJavaScriptFunction"), WTFMove(parameters), [this, protectedThis = makeRef(*this), completionHandler = WTFMove(completionHandler)](SessionHost::CommandResponse&& response) {
1026             if (response.isError || !response.responseObject) {
1027                 completionHandler(CommandResult::fail(WTFMove(response.responseObject)));
1028                 return;
1029             }
1030             String valueString;
1031             if (!response.responseObject->getString(ASCIILiteral("result"), valueString)) {
1032                 completionHandler(CommandResult::fail(CommandResult::ErrorCode::UnknownError));
1033                 return;
1034             }
1035             RefPtr<JSON::Value> resultValue;
1036             if (!JSON::Value::parseJSON(valueString, resultValue)) {
1037                 completionHandler(CommandResult::fail(CommandResult::ErrorCode::UnknownError));
1038                 return;
1039             }
1040             if (resultValue->isNull()) {
1041                 completionHandler(CommandResult::success(JSON::Value::create(false)));
1042                 return;
1043             }
1044             String booleanResult;
1045             if (!resultValue->asString(booleanResult) || booleanResult != "true") {
1046                 completionHandler(CommandResult::fail(CommandResult::ErrorCode::UnknownError));
1047                 return;
1048             }
1049             completionHandler(CommandResult::success(JSON::Value::create(true)));
1050         });
1051     });
1052 }
1053
1054 void Session::getElementText(const String& elementID, Function<void (CommandResult&&)>&& completionHandler)
1055 {
1056     if (!m_toplevelBrowsingContext) {
1057         completionHandler(CommandResult::fail(CommandResult::ErrorCode::NoSuchWindow));
1058         return;
1059     }
1060
1061     handleUserPrompts([this, elementID, completionHandler = WTFMove(completionHandler)](CommandResult&& result) mutable {
1062         if (result.isError()) {
1063             completionHandler(WTFMove(result));
1064             return;
1065         }
1066         RefPtr<JSON::Array> arguments = JSON::Array::create();
1067         arguments->pushString(createElement(elementID)->toJSONString());
1068
1069         RefPtr<JSON::Object> parameters = JSON::Object::create();
1070         parameters->setString(ASCIILiteral("browsingContextHandle"), m_toplevelBrowsingContext.value());
1071         if (m_currentBrowsingContext)
1072             parameters->setString(ASCIILiteral("frameHandle"), m_currentBrowsingContext.value());
1073         // FIXME: Add an atom to properly implement this instead of just using innerText.
1074         parameters->setString(ASCIILiteral("function"), ASCIILiteral("function(element) { return element.innerText.replace(/^[^\\S\\xa0]+|[^\\S\\xa0]+$/g, '') }"));
1075         parameters->setArray(ASCIILiteral("arguments"), WTFMove(arguments));
1076         m_host->sendCommandToBackend(ASCIILiteral("evaluateJavaScriptFunction"), WTFMove(parameters), [this, protectedThis = makeRef(*this), completionHandler = WTFMove(completionHandler)](SessionHost::CommandResponse&& response) {
1077             if (response.isError || !response.responseObject) {
1078                 completionHandler(CommandResult::fail(WTFMove(response.responseObject)));
1079                 return;
1080             }
1081             String valueString;
1082             if (!response.responseObject->getString(ASCIILiteral("result"), valueString)) {
1083                 completionHandler(CommandResult::fail(CommandResult::ErrorCode::UnknownError));
1084                 return;
1085             }
1086             RefPtr<JSON::Value> resultValue;
1087             if (!JSON::Value::parseJSON(valueString, resultValue)) {
1088                 completionHandler(CommandResult::fail(CommandResult::ErrorCode::UnknownError));
1089                 return;
1090             }
1091             completionHandler(CommandResult::success(WTFMove(resultValue)));
1092         });
1093     });
1094 }
1095
1096 void Session::getElementTagName(const String& elementID, Function<void (CommandResult&&)>&& completionHandler)
1097 {
1098     if (!m_toplevelBrowsingContext) {
1099         completionHandler(CommandResult::fail(CommandResult::ErrorCode::NoSuchWindow));
1100         return;
1101     }
1102
1103     handleUserPrompts([this, elementID, completionHandler = WTFMove(completionHandler)](CommandResult&& result) mutable {
1104         if (result.isError()) {
1105             completionHandler(WTFMove(result));
1106             return;
1107         }
1108         RefPtr<JSON::Array> arguments = JSON::Array::create();
1109         arguments->pushString(createElement(elementID)->toJSONString());
1110
1111         RefPtr<JSON::Object> parameters = JSON::Object::create();
1112         parameters->setString(ASCIILiteral("browsingContextHandle"), m_toplevelBrowsingContext.value());
1113         if (m_currentBrowsingContext)
1114             parameters->setString(ASCIILiteral("frameHandle"), m_currentBrowsingContext.value());
1115         parameters->setString(ASCIILiteral("function"), ASCIILiteral("function(element) { return element.tagName.toLowerCase() }"));
1116         parameters->setArray(ASCIILiteral("arguments"), WTFMove(arguments));
1117         m_host->sendCommandToBackend(ASCIILiteral("evaluateJavaScriptFunction"), WTFMove(parameters), [this, protectedThis = makeRef(*this), completionHandler = WTFMove(completionHandler)](SessionHost::CommandResponse&& response) {
1118             if (response.isError || !response.responseObject) {
1119                 completionHandler(CommandResult::fail(WTFMove(response.responseObject)));
1120                 return;
1121             }
1122             String valueString;
1123             if (!response.responseObject->getString(ASCIILiteral("result"), valueString)) {
1124                 completionHandler(CommandResult::fail(CommandResult::ErrorCode::UnknownError));
1125                 return;
1126             }
1127             RefPtr<JSON::Value> resultValue;
1128             if (!JSON::Value::parseJSON(valueString, resultValue)) {
1129                 completionHandler(CommandResult::fail(CommandResult::ErrorCode::UnknownError));
1130                 return;
1131             }
1132             completionHandler(CommandResult::success(WTFMove(resultValue)));
1133         });
1134     });
1135 }
1136
1137 void Session::getElementRect(const String& elementID, Function<void (CommandResult&&)>&& completionHandler)
1138 {
1139     if (!m_toplevelBrowsingContext) {
1140         completionHandler(CommandResult::fail(CommandResult::ErrorCode::NoSuchWindow));
1141         return;
1142     }
1143
1144     handleUserPrompts([this, elementID, completionHandler = WTFMove(completionHandler)](CommandResult&& result) mutable {
1145         if (result.isError()) {
1146             completionHandler(WTFMove(result));
1147             return;
1148         }
1149         computeElementLayout(elementID, { }, [this, protectedThis = makeRef(*this), completionHandler = WTFMove(completionHandler)](std::optional<Rect>&& rect, std::optional<Point>&&, bool, RefPtr<JSON::Object>&& error) {
1150             if (!rect || error) {
1151                 completionHandler(CommandResult::fail(WTFMove(error)));
1152                 return;
1153             }
1154             RefPtr<JSON::Object> rectObject = JSON::Object::create();
1155             rectObject->setInteger(ASCIILiteral("x"), rect.value().origin.x);
1156             rectObject->setInteger(ASCIILiteral("y"), rect.value().origin.y);
1157             rectObject->setInteger(ASCIILiteral("width"), rect.value().size.width);
1158             rectObject->setInteger(ASCIILiteral("height"), rect.value().size.height);
1159             completionHandler(CommandResult::success(WTFMove(rectObject)));
1160         });
1161     });
1162 }
1163
1164 void Session::isElementEnabled(const String& elementID, Function<void (CommandResult&&)>&& completionHandler)
1165 {
1166     if (!m_toplevelBrowsingContext) {
1167         completionHandler(CommandResult::fail(CommandResult::ErrorCode::NoSuchWindow));
1168         return;
1169     }
1170
1171     handleUserPrompts([this, elementID, completionHandler = WTFMove(completionHandler)](CommandResult&& result) mutable {
1172         if (result.isError()) {
1173             completionHandler(WTFMove(result));
1174             return;
1175         }
1176         RefPtr<JSON::Array> arguments = JSON::Array::create();
1177         arguments->pushString(createElement(elementID)->toJSONString());
1178
1179         RefPtr<JSON::Object> parameters = JSON::Object::create();
1180         parameters->setString(ASCIILiteral("browsingContextHandle"), m_toplevelBrowsingContext.value());
1181         if (m_currentBrowsingContext)
1182             parameters->setString(ASCIILiteral("frameHandle"), m_currentBrowsingContext.value());
1183         parameters->setString(ASCIILiteral("function"), ASCIILiteral("function(element) { return element.disabled === undefined ? true : !element.disabled }"));
1184         parameters->setArray(ASCIILiteral("arguments"), WTFMove(arguments));
1185         m_host->sendCommandToBackend(ASCIILiteral("evaluateJavaScriptFunction"), WTFMove(parameters), [this, protectedThis = makeRef(*this), completionHandler = WTFMove(completionHandler)](SessionHost::CommandResponse&& response) {
1186             if (response.isError || !response.responseObject) {
1187                 completionHandler(CommandResult::fail(WTFMove(response.responseObject)));
1188                 return;
1189             }
1190             String valueString;
1191             if (!response.responseObject->getString(ASCIILiteral("result"), valueString)) {
1192                 completionHandler(CommandResult::fail(CommandResult::ErrorCode::UnknownError));
1193                 return;
1194             }
1195             RefPtr<JSON::Value> resultValue;
1196             if (!JSON::Value::parseJSON(valueString, resultValue)) {
1197                 completionHandler(CommandResult::fail(CommandResult::ErrorCode::UnknownError));
1198                 return;
1199             }
1200             completionHandler(CommandResult::success(WTFMove(resultValue)));
1201         });
1202     });
1203 }
1204
1205 void Session::isElementDisplayed(const String& elementID, Function<void (CommandResult&&)>&& completionHandler)
1206 {
1207     if (!m_toplevelBrowsingContext) {
1208         completionHandler(CommandResult::fail(CommandResult::ErrorCode::NoSuchWindow));
1209         return;
1210     }
1211
1212     handleUserPrompts([this, elementID, completionHandler = WTFMove(completionHandler)](CommandResult&& result) mutable {
1213         if (result.isError()) {
1214             completionHandler(WTFMove(result));
1215             return;
1216         }
1217         RefPtr<JSON::Array> arguments = JSON::Array::create();
1218         arguments->pushString(createElement(elementID)->toJSONString());
1219
1220         RefPtr<JSON::Object> parameters = JSON::Object::create();
1221         parameters->setString(ASCIILiteral("browsingContextHandle"), m_toplevelBrowsingContext.value());
1222         if (m_currentBrowsingContext)
1223             parameters->setString(ASCIILiteral("frameHandle"), m_currentBrowsingContext.value());
1224         parameters->setString(ASCIILiteral("function"), ElementDisplayedJavaScript);
1225         parameters->setArray(ASCIILiteral("arguments"), WTFMove(arguments));
1226         m_host->sendCommandToBackend(ASCIILiteral("evaluateJavaScriptFunction"), WTFMove(parameters), [this, protectedThis = makeRef(*this), completionHandler = WTFMove(completionHandler)](SessionHost::CommandResponse&& response) {
1227             if (response.isError || !response.responseObject) {
1228                 completionHandler(CommandResult::fail(WTFMove(response.responseObject)));
1229                 return;
1230             }
1231             String valueString;
1232             if (!response.responseObject->getString(ASCIILiteral("result"), valueString)) {
1233                 completionHandler(CommandResult::fail(CommandResult::ErrorCode::UnknownError));
1234                 return;
1235             }
1236             RefPtr<JSON::Value> resultValue;
1237             if (!JSON::Value::parseJSON(valueString, resultValue)) {
1238                 completionHandler(CommandResult::fail(CommandResult::ErrorCode::UnknownError));
1239                 return;
1240             }
1241             completionHandler(CommandResult::success(WTFMove(resultValue)));
1242         });
1243     });
1244 }
1245
1246 void Session::getElementAttribute(const String& elementID, const String& attribute, Function<void (CommandResult&&)>&& completionHandler)
1247 {
1248     if (!m_toplevelBrowsingContext) {
1249         completionHandler(CommandResult::fail(CommandResult::ErrorCode::NoSuchWindow));
1250         return;
1251     }
1252
1253     handleUserPrompts([this, elementID, attribute, completionHandler = WTFMove(completionHandler)](CommandResult&& result) mutable {
1254         if (result.isError()) {
1255             completionHandler(WTFMove(result));
1256             return;
1257         }
1258         RefPtr<JSON::Array> arguments = JSON::Array::create();
1259         arguments->pushString(createElement(elementID)->toJSONString());
1260         arguments->pushString(JSON::Value::create(attribute)->toJSONString());
1261
1262         RefPtr<JSON::Object> parameters = JSON::Object::create();
1263         parameters->setString(ASCIILiteral("browsingContextHandle"), m_toplevelBrowsingContext.value());
1264         if (m_currentBrowsingContext)
1265             parameters->setString(ASCIILiteral("frameHandle"), m_currentBrowsingContext.value());
1266         parameters->setString(ASCIILiteral("function"), ElementAttributeJavaScript);
1267         parameters->setArray(ASCIILiteral("arguments"), WTFMove(arguments));
1268         m_host->sendCommandToBackend(ASCIILiteral("evaluateJavaScriptFunction"), WTFMove(parameters), [this, protectedThis = makeRef(*this), completionHandler = WTFMove(completionHandler)](SessionHost::CommandResponse&& response) {
1269             if (response.isError || !response.responseObject) {
1270                 completionHandler(CommandResult::fail(WTFMove(response.responseObject)));
1271                 return;
1272             }
1273             String valueString;
1274             if (!response.responseObject->getString(ASCIILiteral("result"), valueString)) {
1275                 completionHandler(CommandResult::fail(CommandResult::ErrorCode::UnknownError));
1276                 return;
1277             }
1278             RefPtr<JSON::Value> resultValue;
1279             if (!JSON::Value::parseJSON(valueString, resultValue)) {
1280                 completionHandler(CommandResult::fail(CommandResult::ErrorCode::UnknownError));
1281                 return;
1282             }
1283             completionHandler(CommandResult::success(WTFMove(resultValue)));
1284         });
1285     });
1286 }
1287
1288 void Session::getElementProperty(const String& elementID, const String& property, Function<void (CommandResult&&)>&& completionHandler)
1289 {
1290     if (!m_toplevelBrowsingContext) {
1291         completionHandler(CommandResult::fail(CommandResult::ErrorCode::NoSuchWindow));
1292         return;
1293     }
1294
1295     handleUserPrompts([this, elementID, property, completionHandler = WTFMove(completionHandler)](CommandResult&& result) mutable {
1296         if (result.isError()) {
1297             completionHandler(WTFMove(result));
1298             return;
1299         }
1300         RefPtr<JSON::Array> arguments = JSON::Array::create();
1301         arguments->pushString(createElement(elementID)->toJSONString());
1302
1303         RefPtr<JSON::Object> parameters = JSON::Object::create();
1304         parameters->setString(ASCIILiteral("browsingContextHandle"), m_toplevelBrowsingContext.value());
1305         if (m_currentBrowsingContext)
1306             parameters->setString(ASCIILiteral("frameHandle"), m_currentBrowsingContext.value());
1307         parameters->setString(ASCIILiteral("function"), makeString("function(element) { return element.", property, "; }"));
1308         parameters->setArray(ASCIILiteral("arguments"), WTFMove(arguments));
1309         m_host->sendCommandToBackend(ASCIILiteral("evaluateJavaScriptFunction"), WTFMove(parameters), [this, protectedThis = makeRef(*this), completionHandler = WTFMove(completionHandler)](SessionHost::CommandResponse&& response) {
1310             if (response.isError || !response.responseObject) {
1311                 completionHandler(CommandResult::fail(WTFMove(response.responseObject)));
1312                 return;
1313             }
1314             String valueString;
1315             if (!response.responseObject->getString(ASCIILiteral("result"), valueString)) {
1316                 completionHandler(CommandResult::fail(CommandResult::ErrorCode::UnknownError));
1317                 return;
1318             }
1319             RefPtr<JSON::Value> resultValue;
1320             if (!JSON::Value::parseJSON(valueString, resultValue)) {
1321                 completionHandler(CommandResult::fail(CommandResult::ErrorCode::UnknownError));
1322                 return;
1323             }
1324             completionHandler(CommandResult::success(WTFMove(resultValue)));
1325         });
1326     });
1327 }
1328
1329 void Session::getElementCSSValue(const String& elementID, const String& cssProperty, Function<void (CommandResult&&)>&& completionHandler)
1330 {
1331     if (!m_toplevelBrowsingContext) {
1332         completionHandler(CommandResult::fail(CommandResult::ErrorCode::NoSuchWindow));
1333         return;
1334     }
1335
1336     handleUserPrompts([this, elementID, cssProperty, completionHandler = WTFMove(completionHandler)](CommandResult&& result) mutable {
1337         if (result.isError()) {
1338             completionHandler(WTFMove(result));
1339             return;
1340         }
1341         RefPtr<JSON::Array> arguments = JSON::Array::create();
1342         arguments->pushString(createElement(elementID)->toJSONString());
1343
1344         RefPtr<JSON::Object> parameters = JSON::Object::create();
1345         parameters->setString(ASCIILiteral("browsingContextHandle"), m_toplevelBrowsingContext.value());
1346         if (m_currentBrowsingContext)
1347             parameters->setString(ASCIILiteral("frameHandle"), m_currentBrowsingContext.value());
1348         parameters->setString(ASCIILiteral("function"), makeString("function(element) { return document.defaultView.getComputedStyle(element).getPropertyValue('", cssProperty, "'); }"));
1349         parameters->setArray(ASCIILiteral("arguments"), WTFMove(arguments));
1350         m_host->sendCommandToBackend(ASCIILiteral("evaluateJavaScriptFunction"), WTFMove(parameters), [this, protectedThis = makeRef(*this), completionHandler = WTFMove(completionHandler)](SessionHost::CommandResponse&& response) {
1351             if (response.isError || !response.responseObject) {
1352                 completionHandler(CommandResult::fail(WTFMove(response.responseObject)));
1353                 return;
1354             }
1355             String valueString;
1356             if (!response.responseObject->getString(ASCIILiteral("result"), valueString)) {
1357                 completionHandler(CommandResult::fail(CommandResult::ErrorCode::UnknownError));
1358                 return;
1359             }
1360             RefPtr<JSON::Value> resultValue;
1361             if (!JSON::Value::parseJSON(valueString, resultValue)) {
1362                 completionHandler(CommandResult::fail(CommandResult::ErrorCode::UnknownError));
1363                 return;
1364             }
1365             completionHandler(CommandResult::success(WTFMove(resultValue)));
1366         });
1367     });
1368 }
1369
1370 void Session::waitForNavigationToComplete(Function<void (CommandResult&&)>&& completionHandler)
1371 {
1372     if (!m_toplevelBrowsingContext) {
1373         completionHandler(CommandResult::fail(CommandResult::ErrorCode::NoSuchWindow));
1374         return;
1375     }
1376
1377     RefPtr<JSON::Object> parameters = JSON::Object::create();
1378     parameters->setString(ASCIILiteral("browsingContextHandle"), m_toplevelBrowsingContext.value());
1379     if (m_currentBrowsingContext)
1380         parameters->setString(ASCIILiteral("frameHandle"), m_currentBrowsingContext.value());
1381     parameters->setInteger(ASCIILiteral("pageLoadTimeout"), m_pageLoadTimeout.millisecondsAs<int>());
1382     if (auto pageLoadStrategy = pageLoadStrategyString())
1383         parameters->setString(ASCIILiteral("pageLoadStrategy"), pageLoadStrategy.value());
1384     m_host->sendCommandToBackend(ASCIILiteral("waitForNavigationToComplete"), WTFMove(parameters), [this, protectedThis = makeRef(*this), completionHandler = WTFMove(completionHandler)](SessionHost::CommandResponse&& response) {
1385         if (response.isError) {
1386             auto result = CommandResult::fail(WTFMove(response.responseObject));
1387             switch (result.errorCode()) {
1388             case CommandResult::ErrorCode::NoSuchWindow:
1389                 // Window was closed, reset the top level browsing context and ignore the error.
1390                 m_toplevelBrowsingContext = std::nullopt;
1391                 break;
1392             case CommandResult::ErrorCode::NoSuchFrame:
1393                 // Navigation destroyed the current frame, switch to top level browsing context and ignore the error.
1394                 switchToBrowsingContext(std::nullopt);
1395                 break;
1396             default:
1397                 completionHandler(WTFMove(result));
1398                 return;
1399             }
1400         }
1401         completionHandler(CommandResult::success());
1402     });
1403 }
1404
1405 void Session::selectOptionElement(const String& elementID, Function<void (CommandResult&&)>&& completionHandler)
1406 {
1407     RefPtr<JSON::Object> parameters = JSON::Object::create();
1408     parameters->setString(ASCIILiteral("browsingContextHandle"), m_toplevelBrowsingContext.value());
1409     parameters->setString(ASCIILiteral("frameHandle"), m_currentBrowsingContext.value_or(emptyString()));
1410     parameters->setString(ASCIILiteral("nodeHandle"), elementID);
1411     m_host->sendCommandToBackend(ASCIILiteral("selectOptionElement"), WTFMove(parameters), [this, protectedThis = makeRef(*this), completionHandler = WTFMove(completionHandler)](SessionHost::CommandResponse&& response) {
1412         if (response.isError) {
1413             completionHandler(CommandResult::fail(WTFMove(response.responseObject)));
1414             return;
1415         }
1416         completionHandler(CommandResult::success());
1417     });
1418 }
1419
1420 void Session::elementClick(const String& elementID, Function<void (CommandResult&&)>&& completionHandler)
1421 {
1422     if (!m_toplevelBrowsingContext) {
1423         completionHandler(CommandResult::fail(CommandResult::ErrorCode::NoSuchWindow));
1424         return;
1425     }
1426
1427     OptionSet<ElementLayoutOption> options = ElementLayoutOption::ScrollIntoViewIfNeeded;
1428     options |= ElementLayoutOption::UseViewportCoordinates;
1429     computeElementLayout(elementID, options, [this, protectedThis = makeRef(*this), elementID, completionHandler = WTFMove(completionHandler)](std::optional<Rect>&& rect, std::optional<Point>&& inViewCenter, bool isObscured, RefPtr<JSON::Object>&& error) mutable {
1430         if (!rect || error) {
1431             completionHandler(CommandResult::fail(WTFMove(error)));
1432             return;
1433         }
1434         if (isObscured) {
1435             completionHandler(CommandResult::fail(CommandResult::ErrorCode::ElementClickIntercepted));
1436             return;
1437         }
1438         if (!inViewCenter) {
1439             completionHandler(CommandResult::fail(CommandResult::ErrorCode::ElementNotInteractable));
1440             return;
1441         }
1442
1443         getElementTagName(elementID, [this, elementID, inViewCenter = WTFMove(inViewCenter), isObscured, completionHandler = WTFMove(completionHandler)](CommandResult&& result) mutable {
1444             bool isOptionElement = false;
1445             if (!result.isError()) {
1446                 String tagName;
1447                 if (result.result()->asString(tagName))
1448                     isOptionElement = tagName == "option";
1449             }
1450
1451             Function<void (CommandResult&&)> continueAfterClickFunction = [this, completionHandler = WTFMove(completionHandler)](CommandResult&& result) mutable {
1452                 if (result.isError()) {
1453                     completionHandler(WTFMove(result));
1454                     return;
1455                 }
1456
1457                 waitForNavigationToComplete(WTFMove(completionHandler));
1458             };
1459             if (isOptionElement)
1460                 selectOptionElement(elementID, WTFMove(continueAfterClickFunction));
1461             else
1462                 performMouseInteraction(inViewCenter.value().x, inViewCenter.value().y, MouseButton::Left, MouseInteraction::SingleClick, WTFMove(continueAfterClickFunction));
1463         });
1464     });
1465 }
1466
1467 void Session::elementClear(const String& elementID, Function<void (CommandResult&&)>&& completionHandler)
1468 {
1469     if (!m_toplevelBrowsingContext) {
1470         completionHandler(CommandResult::fail(CommandResult::ErrorCode::NoSuchWindow));
1471         return;
1472     }
1473
1474     RefPtr<JSON::Array> arguments = JSON::Array::create();
1475     arguments->pushString(createElement(elementID)->toJSONString());
1476
1477     RefPtr<JSON::Object> parameters = JSON::Object::create();
1478     parameters->setString(ASCIILiteral("browsingContextHandle"), m_toplevelBrowsingContext.value());
1479     if (m_currentBrowsingContext)
1480         parameters->setString(ASCIILiteral("frameHandle"), m_currentBrowsingContext.value());
1481     parameters->setString(ASCIILiteral("function"), FormElementClearJavaScript);
1482     parameters->setArray(ASCIILiteral("arguments"), WTFMove(arguments));
1483     m_host->sendCommandToBackend(ASCIILiteral("evaluateJavaScriptFunction"), WTFMove(parameters), [this, protectedThis = makeRef(*this), completionHandler = WTFMove(completionHandler)](SessionHost::CommandResponse&& response) {
1484         if (response.isError) {
1485             completionHandler(CommandResult::fail(WTFMove(response.responseObject)));
1486             return;
1487         }
1488         completionHandler(CommandResult::success());
1489     });
1490 }
1491
1492 String Session::virtualKeyForKeySequence(const String& keySequence, KeyModifier& modifier)
1493 {
1494     // ยง17.4.2 Keyboard Actions.
1495     // https://www.w3.org/TR/webdriver/#keyboard-actions
1496     modifier = KeyModifier::None;
1497     switch (keySequence[0]) {
1498     case 0xE001U:
1499         return ASCIILiteral("Cancel");
1500     case 0xE002U:
1501         return ASCIILiteral("Help");
1502     case 0xE003U:
1503         return ASCIILiteral("Backspace");
1504     case 0xE004U:
1505         return ASCIILiteral("Tab");
1506     case 0xE005U:
1507         return ASCIILiteral("Clear");
1508     case 0xE006U:
1509         return ASCIILiteral("Return");
1510     case 0xE007U:
1511         return ASCIILiteral("Enter");
1512     case 0xE008U:
1513     case 0xE050U:
1514         modifier = KeyModifier::Shift;
1515         return ASCIILiteral("Shift");
1516     case 0xE009U:
1517     case 0xE051U:
1518         modifier = KeyModifier::Control;
1519         return ASCIILiteral("Control");
1520     case 0xE00AU:
1521     case 0xE052U:
1522         modifier = KeyModifier::Alternate;
1523         return ASCIILiteral("Alternate");
1524     case 0xE00BU:
1525         return ASCIILiteral("Pause");
1526     case 0xE00CU:
1527         return ASCIILiteral("Escape");
1528     case 0xE00DU:
1529         return ASCIILiteral("Space");
1530     case 0xE00EU:
1531     case 0xE054U:
1532         return ASCIILiteral("PageUp");
1533     case 0xE00FU:
1534     case 0xE055U:
1535         return ASCIILiteral("PageDown");
1536     case 0xE010U:
1537     case 0xE056U:
1538         return ASCIILiteral("End");
1539     case 0xE011U:
1540     case 0xE057U:
1541         return ASCIILiteral("Home");
1542     case 0xE012U:
1543     case 0xE058U:
1544         return ASCIILiteral("LeftArrow");
1545     case 0xE013U:
1546     case 0xE059U:
1547         return ASCIILiteral("UpArrow");
1548     case 0xE014U:
1549     case 0xE05AU:
1550         return ASCIILiteral("RightArrow");
1551     case 0xE015U:
1552     case 0xE05BU:
1553         return ASCIILiteral("DownArrow");
1554     case 0xE016U:
1555     case 0xE05CU:
1556         return ASCIILiteral("Insert");
1557     case 0xE017U:
1558     case 0xE05DU:
1559         return ASCIILiteral("Delete");
1560     case 0xE018U:
1561         return ASCIILiteral("Semicolon");
1562     case 0xE019U:
1563         return ASCIILiteral("Equals");
1564     case 0xE01AU:
1565         return ASCIILiteral("NumberPad0");
1566     case 0xE01BU:
1567         return ASCIILiteral("NumberPad1");
1568     case 0xE01CU:
1569         return ASCIILiteral("NumberPad2");
1570     case 0xE01DU:
1571         return ASCIILiteral("NumberPad3");
1572     case 0xE01EU:
1573         return ASCIILiteral("NumberPad4");
1574     case 0xE01FU:
1575         return ASCIILiteral("NumberPad5");
1576     case 0xE020U:
1577         return ASCIILiteral("NumberPad6");
1578     case 0xE021U:
1579         return ASCIILiteral("NumberPad7");
1580     case 0xE022U:
1581         return ASCIILiteral("NumberPad8");
1582     case 0xE023U:
1583         return ASCIILiteral("NumberPad9");
1584     case 0xE024U:
1585         return ASCIILiteral("NumberPadMultiply");
1586     case 0xE025U:
1587         return ASCIILiteral("NumberPadAdd");
1588     case 0xE026U:
1589         return ASCIILiteral("NumberPadSeparator");
1590     case 0xE027U:
1591         return ASCIILiteral("NumberPadSubtract");
1592     case 0xE028U:
1593         return ASCIILiteral("NumberPadDecimal");
1594     case 0xE029U:
1595         return ASCIILiteral("NumberPadDivide");
1596     case 0xE031U:
1597         return ASCIILiteral("Function1");
1598     case 0xE032U:
1599         return ASCIILiteral("Function2");
1600     case 0xE033U:
1601         return ASCIILiteral("Function3");
1602     case 0xE034U:
1603         return ASCIILiteral("Function4");
1604     case 0xE035U:
1605         return ASCIILiteral("Function5");
1606     case 0xE036U:
1607         return ASCIILiteral("Function6");
1608     case 0xE037U:
1609         return ASCIILiteral("Function7");
1610     case 0xE038U:
1611         return ASCIILiteral("Function8");
1612     case 0xE039U:
1613         return ASCIILiteral("Function9");
1614     case 0xE03AU:
1615         return ASCIILiteral("Function10");
1616     case 0xE03BU:
1617         return ASCIILiteral("Function11");
1618     case 0xE03CU:
1619         return ASCIILiteral("Function12");
1620     case 0xE03DU:
1621     case 0xE053U:
1622         modifier = KeyModifier::Meta;
1623         return ASCIILiteral("Meta");
1624     default:
1625         break;
1626     }
1627     return String();
1628 }
1629
1630 void Session::elementSendKeys(const String& elementID, Vector<String>&& keys, Function<void (CommandResult&&)>&& completionHandler)
1631 {
1632     if (!m_toplevelBrowsingContext) {
1633         completionHandler(CommandResult::fail(CommandResult::ErrorCode::NoSuchWindow));
1634         return;
1635     }
1636
1637     handleUserPrompts([this, elementID, keys = WTFMove(keys), completionHandler = WTFMove(completionHandler)](CommandResult&& result) mutable {
1638         if (result.isError()) {
1639             completionHandler(WTFMove(result));
1640             return;
1641         }
1642         // FIXME: move this to an atom.
1643         static const char focusScript[] =
1644             "function focus(element) {"
1645             "    var doc = element.ownerDocument || element;"
1646             "    var prevActiveElement = doc.activeElement;"
1647             "    if (element != prevActiveElement && prevActiveElement)"
1648             "        prevActiveElement.blur();"
1649             "    element.focus();"
1650             "    if (element != prevActiveElement && element.value && element.value.length && element.setSelectionRange)"
1651             "        element.setSelectionRange(element.value.length, element.value.length);"
1652             "    if (element != doc.activeElement)"
1653             "        throw new Error('cannot focus element');"
1654             "}";
1655
1656         RefPtr<JSON::Array> arguments = JSON::Array::create();
1657         arguments->pushString(createElement(elementID)->toJSONString());
1658         RefPtr<JSON::Object> parameters = JSON::Object::create();
1659         parameters->setString(ASCIILiteral("browsingContextHandle"), m_toplevelBrowsingContext.value());
1660         if (m_currentBrowsingContext)
1661             parameters->setString(ASCIILiteral("frameHandle"), m_currentBrowsingContext.value());
1662         parameters->setString(ASCIILiteral("function"), focusScript);
1663         parameters->setArray(ASCIILiteral("arguments"), WTFMove(arguments));
1664         m_host->sendCommandToBackend(ASCIILiteral("evaluateJavaScriptFunction"), WTFMove(parameters), [this, protectedThis = makeRef(*this), keys = WTFMove(keys), completionHandler = WTFMove(completionHandler)](SessionHost::CommandResponse&& response) mutable {
1665             if (response.isError || !response.responseObject) {
1666                 completionHandler(CommandResult::fail(WTFMove(response.responseObject)));
1667                 return;
1668             }
1669
1670             unsigned stickyModifiers = 0;
1671             Vector<KeyboardInteraction> interactions;
1672             interactions.reserveInitialCapacity(keys.size());
1673             for (const auto& key : keys) {
1674                 KeyboardInteraction interaction;
1675                 KeyModifier modifier;
1676                 auto virtualKey = virtualKeyForKeySequence(key, modifier);
1677                 if (!virtualKey.isNull()) {
1678                     interaction.key = virtualKey;
1679                     if (modifier != KeyModifier::None) {
1680                         stickyModifiers ^= modifier;
1681                         if (stickyModifiers & modifier)
1682                             interaction.type = KeyboardInteractionType::KeyPress;
1683                         else
1684                             interaction.type = KeyboardInteractionType::KeyRelease;
1685                     }
1686                 } else
1687                     interaction.text = key;
1688                 interactions.uncheckedAppend(WTFMove(interaction));
1689             }
1690
1691             // Reset sticky modifiers if needed.
1692             if (stickyModifiers) {
1693                 if (stickyModifiers & KeyModifier::Shift)
1694                     interactions.append({ KeyboardInteractionType::KeyRelease, std::nullopt, std::optional<String>(ASCIILiteral("Shift")) });
1695                 if (stickyModifiers & KeyModifier::Control)
1696                     interactions.append({ KeyboardInteractionType::KeyRelease, std::nullopt, std::optional<String>(ASCIILiteral("Control")) });
1697                 if (stickyModifiers & KeyModifier::Alternate)
1698                     interactions.append({ KeyboardInteractionType::KeyRelease, std::nullopt, std::optional<String>(ASCIILiteral("Alternate")) });
1699                 if (stickyModifiers & KeyModifier::Meta)
1700                     interactions.append({ KeyboardInteractionType::KeyRelease, std::nullopt, std::optional<String>(ASCIILiteral("Meta")) });
1701             }
1702
1703             performKeyboardInteractions(WTFMove(interactions), WTFMove(completionHandler));
1704         });
1705     });
1706 }
1707
1708 RefPtr<JSON::Value> Session::handleScriptResult(RefPtr<JSON::Value>&& resultValue)
1709 {
1710     RefPtr<JSON::Array> resultArray;
1711     if (resultValue->asArray(resultArray)) {
1712         RefPtr<JSON::Array> returnValueArray = JSON::Array::create();
1713         unsigned resultArrayLength = resultArray->length();
1714         for (unsigned i = 0; i < resultArrayLength; ++i)
1715             returnValueArray->pushValue(handleScriptResult(resultArray->get(i)));
1716         return returnValueArray;
1717     }
1718
1719     if (auto element = createElement(RefPtr<JSON::Value>(resultValue)))
1720         return element;
1721
1722     RefPtr<JSON::Object> resultObject;
1723     if (resultValue->asObject(resultObject)) {
1724         RefPtr<JSON::Object> returnValueObject = JSON::Object::create();
1725         auto end = resultObject->end();
1726         for (auto it = resultObject->begin(); it != end; ++it)
1727             returnValueObject->setValue(it->key, handleScriptResult(WTFMove(it->value)));
1728         return returnValueObject;
1729     }
1730
1731     return resultValue;
1732 }
1733
1734 void Session::executeScript(const String& script, RefPtr<JSON::Array>&& argumentsArray, ExecuteScriptMode mode, Function<void (CommandResult&&)>&& completionHandler)
1735 {
1736     if (!m_toplevelBrowsingContext) {
1737         completionHandler(CommandResult::fail(CommandResult::ErrorCode::NoSuchWindow));
1738         return;
1739     }
1740
1741     handleUserPrompts([this, script, argumentsArray = WTFMove(argumentsArray), mode, completionHandler = WTFMove(completionHandler)](CommandResult&& result) mutable {
1742         if (result.isError()) {
1743             completionHandler(WTFMove(result));
1744             return;
1745         }
1746         RefPtr<JSON::Array> arguments = JSON::Array::create();
1747         unsigned argumentsLength = argumentsArray->length();
1748         for (unsigned i = 0; i < argumentsLength; ++i) {
1749             if (auto argument = argumentsArray->get(i)) {
1750                 if (auto element = extractElement(*argument))
1751                     arguments->pushString(element->toJSONString());
1752                 else
1753                     arguments->pushString(argument->toJSONString());
1754             }
1755         }
1756
1757         RefPtr<JSON::Object> parameters = JSON::Object::create();
1758         parameters->setString(ASCIILiteral("browsingContextHandle"), m_toplevelBrowsingContext.value());
1759         if (m_currentBrowsingContext)
1760             parameters->setString(ASCIILiteral("frameHandle"), m_currentBrowsingContext.value());
1761         parameters->setString(ASCIILiteral("function"), "function(){" + script + '}');
1762         parameters->setArray(ASCIILiteral("arguments"), WTFMove(arguments));
1763         if (mode == ExecuteScriptMode::Async) {
1764             parameters->setBoolean(ASCIILiteral("expectsImplicitCallbackArgument"), true);
1765             if (m_scriptTimeout)
1766                 parameters->setInteger(ASCIILiteral("callbackTimeout"), m_scriptTimeout.millisecondsAs<int>());
1767         }
1768         m_host->sendCommandToBackend(ASCIILiteral("evaluateJavaScriptFunction"), WTFMove(parameters), [this, protectedThis = makeRef(*this), completionHandler = WTFMove(completionHandler)](SessionHost::CommandResponse&& response) mutable {
1769             if (response.isError || !response.responseObject) {
1770                 auto result = CommandResult::fail(WTFMove(response.responseObject));
1771                 if (result.errorCode() == CommandResult::ErrorCode::UnexpectedAlertOpen)
1772                     handleUnexpectedAlertOpen(WTFMove(completionHandler));
1773                 else
1774                     completionHandler(WTFMove(result));
1775                 return;
1776             }
1777             String valueString;
1778             if (!response.responseObject->getString(ASCIILiteral("result"), valueString)) {
1779                 completionHandler(CommandResult::fail(CommandResult::ErrorCode::UnknownError));
1780                 return;
1781             }
1782             RefPtr<JSON::Value> resultValue;
1783             if (!JSON::Value::parseJSON(valueString, resultValue)) {
1784                 completionHandler(CommandResult::fail(CommandResult::ErrorCode::UnknownError));
1785                 return;
1786             }
1787             completionHandler(CommandResult::success(handleScriptResult(WTFMove(resultValue))));
1788         });
1789     });
1790 }
1791
1792 static String mouseButtonForAutomation(MouseButton button)
1793 {
1794     switch (button) {
1795     case MouseButton::None:
1796         return ASCIILiteral("None");
1797     case MouseButton::Left:
1798         return ASCIILiteral("Left");
1799     case MouseButton::Middle:
1800         return ASCIILiteral("Middle");
1801     case MouseButton::Right:
1802         return ASCIILiteral("Right");
1803     }
1804
1805     RELEASE_ASSERT_NOT_REACHED();
1806 }
1807
1808 void Session::performMouseInteraction(int x, int y, MouseButton button, MouseInteraction interaction, Function<void (CommandResult&&)>&& completionHandler)
1809 {
1810     RefPtr<JSON::Object> parameters = JSON::Object::create();
1811     parameters->setString(ASCIILiteral("handle"), m_toplevelBrowsingContext.value());
1812     RefPtr<JSON::Object> position = JSON::Object::create();
1813     position->setInteger(ASCIILiteral("x"), x);
1814     position->setInteger(ASCIILiteral("y"), y);
1815     parameters->setObject(ASCIILiteral("position"), WTFMove(position));
1816     parameters->setString(ASCIILiteral("button"), mouseButtonForAutomation(button));
1817     switch (interaction) {
1818     case MouseInteraction::Move:
1819         parameters->setString(ASCIILiteral("interaction"), ASCIILiteral("Move"));
1820         break;
1821     case MouseInteraction::Down:
1822         parameters->setString(ASCIILiteral("interaction"), ASCIILiteral("Down"));
1823         break;
1824     case MouseInteraction::Up:
1825         parameters->setString(ASCIILiteral("interaction"), ASCIILiteral("Up"));
1826         break;
1827     case MouseInteraction::SingleClick:
1828         parameters->setString(ASCIILiteral("interaction"), ASCIILiteral("SingleClick"));
1829         break;
1830     case MouseInteraction::DoubleClick:
1831         parameters->setString(ASCIILiteral("interaction"), ASCIILiteral("DoubleClick"));
1832         break;
1833     }
1834     parameters->setArray(ASCIILiteral("modifiers"), JSON::Array::create());
1835     m_host->sendCommandToBackend(ASCIILiteral("performMouseInteraction"), WTFMove(parameters), [this, protectedThis = makeRef(*this), completionHandler = WTFMove(completionHandler)](SessionHost::CommandResponse&& response) {
1836         if (response.isError) {
1837             completionHandler(CommandResult::fail(WTFMove(response.responseObject)));
1838             return;
1839         }
1840         completionHandler(CommandResult::success());
1841     });
1842 }
1843
1844 void Session::performKeyboardInteractions(Vector<KeyboardInteraction>&& interactions, Function<void (CommandResult&&)>&& completionHandler)
1845 {
1846     RefPtr<JSON::Object> parameters = JSON::Object::create();
1847     parameters->setString(ASCIILiteral("handle"), m_toplevelBrowsingContext.value());
1848     RefPtr<JSON::Array> interactionsArray = JSON::Array::create();
1849     for (const auto& interaction : interactions) {
1850         RefPtr<JSON::Object> interactionObject = JSON::Object::create();
1851         switch (interaction.type) {
1852         case KeyboardInteractionType::KeyPress:
1853             interactionObject->setString(ASCIILiteral("type"), ASCIILiteral("KeyPress"));
1854             break;
1855         case KeyboardInteractionType::KeyRelease:
1856             interactionObject->setString(ASCIILiteral("type"), ASCIILiteral("KeyRelease"));
1857             break;
1858         case KeyboardInteractionType::InsertByKey:
1859             interactionObject->setString(ASCIILiteral("type"), ASCIILiteral("InsertByKey"));
1860             break;
1861         }
1862         if (interaction.key)
1863             interactionObject->setString(ASCIILiteral("key"), interaction.key.value());
1864         if (interaction.text)
1865             interactionObject->setString(ASCIILiteral("text"), interaction.text.value());
1866         interactionsArray->pushObject(WTFMove(interactionObject));
1867     }
1868     parameters->setArray(ASCIILiteral("interactions"), WTFMove(interactionsArray));
1869     m_host->sendCommandToBackend(ASCIILiteral("performKeyboardInteractions"), WTFMove(parameters), [this, protectedThis = makeRef(*this), completionHandler = WTFMove(completionHandler)](SessionHost::CommandResponse&& response) {
1870         if (response.isError) {
1871             completionHandler(CommandResult::fail(WTFMove(response.responseObject)));
1872             return;
1873         }
1874         completionHandler(CommandResult::success());
1875     });
1876 }
1877
1878 static std::optional<Session::Cookie> parseAutomationCookie(const JSON::Object& cookieObject)
1879 {
1880     Session::Cookie cookie;
1881     if (!cookieObject.getString(ASCIILiteral("name"), cookie.name))
1882         return std::nullopt;
1883     if (!cookieObject.getString(ASCIILiteral("value"), cookie.value))
1884         return std::nullopt;
1885
1886     String path;
1887     if (cookieObject.getString(ASCIILiteral("path"), path))
1888         cookie.path = path;
1889     String domain;
1890     if (cookieObject.getString(ASCIILiteral("domain"), domain))
1891         cookie.domain = domain;
1892     bool secure;
1893     if (cookieObject.getBoolean(ASCIILiteral("secure"), secure))
1894         cookie.secure = secure;
1895     bool httpOnly;
1896     if (cookieObject.getBoolean(ASCIILiteral("httpOnly"), httpOnly))
1897         cookie.httpOnly = httpOnly;
1898     bool session = false;
1899     cookieObject.getBoolean(ASCIILiteral("session"), session);
1900     if (!session) {
1901         double expiry;
1902         if (cookieObject.getDouble(ASCIILiteral("expires"), expiry))
1903             cookie.expiry = expiry;
1904     }
1905
1906     return cookie;
1907 }
1908
1909 static RefPtr<JSON::Object> builtAutomationCookie(const Session::Cookie& cookie)
1910 {
1911     RefPtr<JSON::Object> cookieObject = JSON::Object::create();
1912     cookieObject->setString(ASCIILiteral("name"), cookie.name);
1913     cookieObject->setString(ASCIILiteral("value"), cookie.value);
1914     cookieObject->setString(ASCIILiteral("path"), cookie.path.value_or("/"));
1915     cookieObject->setString(ASCIILiteral("domain"), cookie.domain.value_or(emptyString()));
1916     cookieObject->setBoolean(ASCIILiteral("secure"), cookie.secure.value_or(false));
1917     cookieObject->setBoolean(ASCIILiteral("httpOnly"), cookie.httpOnly.value_or(false));
1918     cookieObject->setBoolean(ASCIILiteral("session"), !cookie.expiry);
1919     cookieObject->setDouble(ASCIILiteral("expires"), cookie.expiry.value_or(0));
1920     return cookieObject;
1921 }
1922
1923 static RefPtr<JSON::Object> serializeCookie(const Session::Cookie& cookie)
1924 {
1925     RefPtr<JSON::Object> cookieObject = JSON::Object::create();
1926     cookieObject->setString(ASCIILiteral("name"), cookie.name);
1927     cookieObject->setString(ASCIILiteral("value"), cookie.value);
1928     if (cookie.path)
1929         cookieObject->setString(ASCIILiteral("path"), cookie.path.value());
1930     if (cookie.domain)
1931         cookieObject->setString(ASCIILiteral("domain"), cookie.domain.value());
1932     if (cookie.secure)
1933         cookieObject->setBoolean(ASCIILiteral("secure"), cookie.secure.value());
1934     if (cookie.httpOnly)
1935         cookieObject->setBoolean(ASCIILiteral("httpOnly"), cookie.httpOnly.value());
1936     if (cookie.expiry)
1937         cookieObject->setInteger(ASCIILiteral("expiry"), cookie.expiry.value());
1938     return cookieObject;
1939 }
1940
1941 void Session::getAllCookies(Function<void (CommandResult&&)>&& completionHandler)
1942 {
1943     if (!m_toplevelBrowsingContext) {
1944         completionHandler(CommandResult::fail(CommandResult::ErrorCode::NoSuchWindow));
1945         return;
1946     }
1947
1948     handleUserPrompts([this, completionHandler = WTFMove(completionHandler)](CommandResult&& result) mutable {
1949         if (result.isError()) {
1950             completionHandler(WTFMove(result));
1951             return;
1952         }
1953
1954         RefPtr<JSON::Object> parameters = JSON::Object::create();
1955         parameters->setString(ASCIILiteral("browsingContextHandle"), m_toplevelBrowsingContext.value());
1956         m_host->sendCommandToBackend(ASCIILiteral("getAllCookies"), WTFMove(parameters), [this, protectedThis = makeRef(*this), completionHandler = WTFMove(completionHandler)](SessionHost::CommandResponse&& response) mutable {
1957             if (response.isError || !response.responseObject) {
1958                 completionHandler(CommandResult::fail(WTFMove(response.responseObject)));
1959                 return;
1960             }
1961             RefPtr<JSON::Array> cookiesArray;
1962             if (!response.responseObject->getArray(ASCIILiteral("cookies"), cookiesArray)) {
1963                 completionHandler(CommandResult::fail(CommandResult::ErrorCode::UnknownError));
1964                 return;
1965             }
1966             RefPtr<JSON::Array> cookies = JSON::Array::create();
1967             for (unsigned i = 0; i < cookiesArray->length(); ++i) {
1968                 RefPtr<JSON::Value> cookieValue = cookiesArray->get(i);
1969                 RefPtr<JSON::Object> cookieObject;
1970                 if (!cookieValue->asObject(cookieObject)) {
1971                     completionHandler(CommandResult::fail(CommandResult::ErrorCode::UnknownError));
1972                     return;
1973                 }
1974
1975                 auto cookie = parseAutomationCookie(*cookieObject);
1976                 if (!cookie) {
1977                     completionHandler(CommandResult::fail(CommandResult::ErrorCode::UnknownError));
1978                     return;
1979                 }
1980                 cookies->pushObject(serializeCookie(cookie.value()));
1981             }
1982             completionHandler(CommandResult::success(WTFMove(cookies)));
1983         });
1984     });
1985 }
1986
1987 void Session::getNamedCookie(const String& name, Function<void (CommandResult&&)>&& completionHandler)
1988 {
1989     getAllCookies([this, name, completionHandler = WTFMove(completionHandler)](CommandResult&& result) mutable {
1990         if (result.isError()) {
1991             completionHandler(WTFMove(result));
1992             return;
1993         }
1994         RefPtr<JSON::Array> cookiesArray;
1995         result.result()->asArray(cookiesArray);
1996         for (unsigned i = 0; i < cookiesArray->length(); ++i) {
1997             RefPtr<JSON::Value> cookieValue = cookiesArray->get(i);
1998             RefPtr<JSON::Object> cookieObject;
1999             cookieValue->asObject(cookieObject);
2000             String cookieName;
2001             cookieObject->getString(ASCIILiteral("name"), cookieName);
2002             if (cookieName == name) {
2003                 completionHandler(CommandResult::success(WTFMove(cookieObject)));
2004                 return;
2005             }
2006         }
2007         completionHandler(CommandResult::fail(CommandResult::ErrorCode::NoSuchCookie));
2008     });
2009 }
2010
2011 void Session::addCookie(const Cookie& cookie, Function<void (CommandResult&&)>&& completionHandler)
2012 {
2013     if (!m_toplevelBrowsingContext) {
2014         completionHandler(CommandResult::fail(CommandResult::ErrorCode::NoSuchWindow));
2015         return;
2016     }
2017
2018     handleUserPrompts([this, cookie = builtAutomationCookie(cookie), completionHandler = WTFMove(completionHandler)](CommandResult&& result) mutable {
2019         if (result.isError()) {
2020             completionHandler(WTFMove(result));
2021             return;
2022         }
2023         RefPtr<JSON::Object> parameters = JSON::Object::create();
2024         parameters->setString(ASCIILiteral("browsingContextHandle"), m_toplevelBrowsingContext.value());
2025         parameters->setObject(ASCIILiteral("cookie"), WTFMove(cookie));
2026         m_host->sendCommandToBackend(ASCIILiteral("addSingleCookie"), WTFMove(parameters), [this, protectedThis = makeRef(*this), completionHandler = WTFMove(completionHandler)](SessionHost::CommandResponse&& response) {
2027             if (response.isError) {
2028                 completionHandler(CommandResult::fail(WTFMove(response.responseObject)));
2029                 return;
2030             }
2031             completionHandler(CommandResult::success());
2032         });
2033     });
2034 }
2035
2036 void Session::deleteCookie(const String& name, Function<void (CommandResult&&)>&& completionHandler)
2037 {
2038     if (!m_toplevelBrowsingContext) {
2039         completionHandler(CommandResult::fail(CommandResult::ErrorCode::NoSuchWindow));
2040         return;
2041     }
2042
2043     handleUserPrompts([this, name, completionHandler = WTFMove(completionHandler)](CommandResult&& result) mutable {
2044         if (result.isError()) {
2045             completionHandler(WTFMove(result));
2046             return;
2047         }
2048         RefPtr<JSON::Object> parameters = JSON::Object::create();
2049         parameters->setString(ASCIILiteral("browsingContextHandle"), m_toplevelBrowsingContext.value());
2050         parameters->setString(ASCIILiteral("cookieName"), name);
2051         m_host->sendCommandToBackend(ASCIILiteral("deleteSingleCookie"), WTFMove(parameters), [this, protectedThis = makeRef(*this), completionHandler = WTFMove(completionHandler)](SessionHost::CommandResponse&& response) {
2052             if (response.isError) {
2053                 completionHandler(CommandResult::fail(WTFMove(response.responseObject)));
2054                 return;
2055             }
2056             completionHandler(CommandResult::success());
2057         });
2058     });
2059 }
2060
2061 void Session::deleteAllCookies(Function<void (CommandResult&&)>&& completionHandler)
2062 {
2063     if (!m_toplevelBrowsingContext) {
2064         completionHandler(CommandResult::fail(CommandResult::ErrorCode::NoSuchWindow));
2065         return;
2066     }
2067
2068     handleUserPrompts([this, completionHandler = WTFMove(completionHandler)](CommandResult&& result) mutable {
2069         if (result.isError()) {
2070             completionHandler(WTFMove(result));
2071             return;
2072         }
2073         RefPtr<JSON::Object> parameters = JSON::Object::create();
2074         parameters->setString(ASCIILiteral("browsingContextHandle"), m_toplevelBrowsingContext.value());
2075         m_host->sendCommandToBackend(ASCIILiteral("deleteAllCookies"), WTFMove(parameters), [this, protectedThis = makeRef(*this), completionHandler = WTFMove(completionHandler)](SessionHost::CommandResponse&& response) {
2076             if (response.isError) {
2077                 completionHandler(CommandResult::fail(WTFMove(response.responseObject)));
2078                 return;
2079             }
2080             completionHandler(CommandResult::success());
2081         });
2082     });
2083 }
2084
2085 InputSource& Session::getOrCreateInputSource(const String& id, InputSource::Type type, std::optional<PointerType> pointerType)
2086 {
2087     auto addResult = m_activeInputSources.add(id, InputSource());
2088     if (addResult.isNewEntry)
2089         addResult.iterator->value = { type, pointerType };
2090     return addResult.iterator->value;
2091 }
2092
2093 Session::InputSourceState& Session::inputSourceState(const String& id)
2094 {
2095     return m_inputStateTable.ensure(id, [] { return InputSourceState(); }).iterator->value;
2096 }
2097
2098 static const char* automationSourceType(InputSource::Type type)
2099 {
2100     switch (type) {
2101     case InputSource::Type::None:
2102         return "Null";
2103     case InputSource::Type::Pointer:
2104         return "Mouse";
2105     case InputSource::Type::Key:
2106         return "Keyboard";
2107     }
2108     RELEASE_ASSERT_NOT_REACHED();
2109 }
2110
2111 static const char* automationOriginType(PointerOrigin::Type type)
2112 {
2113     switch (type) {
2114     case PointerOrigin::Type::Viewport:
2115         return "Viewport";
2116     case PointerOrigin::Type::Pointer:
2117         return "Pointer";
2118     case PointerOrigin::Type::Element:
2119         return "Element";
2120     }
2121     RELEASE_ASSERT_NOT_REACHED();
2122 }
2123
2124 void Session::performActions(Vector<Vector<Action>>&& actionsByTick, Function<void (CommandResult&&)>&& completionHandler)
2125 {
2126     if (!m_toplevelBrowsingContext) {
2127         completionHandler(CommandResult::fail(CommandResult::ErrorCode::NoSuchWindow));
2128         return;
2129     }
2130
2131     handleUserPrompts([this, actionsByTick = WTFMove(actionsByTick), completionHandler = WTFMove(completionHandler)](CommandResult&& result) mutable {
2132         if (result.isError()) {
2133             completionHandler(WTFMove(result));
2134             return;
2135         }
2136
2137         // First check if we have actions and whether we need to resolve any pointer move element origin.
2138         unsigned actionsCount = 0;
2139         for (const auto& tick : actionsByTick)
2140             actionsCount += tick.size();
2141         if (!actionsCount) {
2142             completionHandler(CommandResult::success());
2143             return;
2144         }
2145
2146         RefPtr<JSON::Object> parameters = JSON::Object::create();
2147         parameters->setString(ASCIILiteral("handle"), m_toplevelBrowsingContext.value());
2148         if (m_currentBrowsingContext)
2149             parameters->setString(ASCIILiteral("frameHandle"), m_currentBrowsingContext.value());
2150         RefPtr<JSON::Array> inputSources = JSON::Array::create();
2151         for (const auto& inputSource : m_activeInputSources) {
2152             RefPtr<JSON::Object> inputSourceObject = JSON::Object::create();
2153             inputSourceObject->setString(ASCIILiteral("sourceId"), inputSource.key);
2154             inputSourceObject->setString(ASCIILiteral("sourceType"), automationSourceType(inputSource.value.type));
2155             inputSources->pushObject(WTFMove(inputSourceObject));
2156         }
2157         parameters->setArray(ASCIILiteral("inputSources"), WTFMove(inputSources));
2158         RefPtr<JSON::Array> steps = JSON::Array::create();
2159         for (const auto& tick : actionsByTick) {
2160             RefPtr<JSON::Array> states = JSON::Array::create();
2161             for (const auto& action : tick) {
2162                 RefPtr<JSON::Object> state = JSON::Object::create();
2163                 auto& currentState = inputSourceState(action.id);
2164                 state->setString(ASCIILiteral("sourceId"), action.id);
2165                 switch (action.type) {
2166                 case Action::Type::None:
2167                     state->setDouble(ASCIILiteral("duration"), action.duration.value());
2168                     break;
2169                 case Action::Type::Pointer: {
2170                     switch (action.subtype) {
2171                     case Action::Subtype::PointerUp:
2172                         currentState.pressedButton = std::nullopt;
2173                         break;
2174                     case Action::Subtype::PointerDown:
2175                         currentState.pressedButton = action.button.value();
2176                         break;
2177                     case Action::Subtype::PointerMove: {
2178                         state->setString(ASCIILiteral("origin"), automationOriginType(action.origin->type));
2179                         RefPtr<JSON::Object> location = JSON::Object::create();
2180                         location->setInteger(ASCIILiteral("x"), action.x.value());
2181                         location->setInteger(ASCIILiteral("y"), action.y.value());
2182                         state->setObject(ASCIILiteral("location"), WTFMove(location));
2183                         if (action.origin->type == PointerOrigin::Type::Element)
2184                             state->setString(ASCIILiteral("nodeHandle"), action.origin->elementID.value());
2185                         FALLTHROUGH;
2186                     }
2187                     case Action::Subtype::Pause:
2188                         if (action.duration)
2189                             state->setDouble(ASCIILiteral("duration"), action.duration.value());
2190                         break;
2191                     case Action::Subtype::PointerCancel:
2192                         currentState.pressedButton = std::nullopt;
2193                         break;
2194                     case Action::Subtype::KeyUp:
2195                     case Action::Subtype::KeyDown:
2196                         ASSERT_NOT_REACHED();
2197                     }
2198                     if (currentState.pressedButton)
2199                         state->setString(ASCIILiteral("pressedButton"), mouseButtonForAutomation(currentState.pressedButton.value()));
2200                     break;
2201                 }
2202                 case Action::Type::Key:
2203                     switch (action.subtype) {
2204                     case Action::Subtype::KeyUp:
2205                         if (currentState.pressedVirtualKey)
2206                             currentState.pressedVirtualKey = std::nullopt;
2207                         else
2208                             currentState.pressedKey = std::nullopt;
2209                         break;
2210                     case Action::Subtype::KeyDown: {
2211                         KeyModifier modifier;
2212                         auto virtualKey = virtualKeyForKeySequence(action.key.value(), modifier);
2213                         if (!virtualKey.isNull())
2214                             currentState.pressedVirtualKey = virtualKey;
2215                         else
2216                             currentState.pressedKey = action.key.value();
2217                         break;
2218                     }
2219                     case Action::Subtype::Pause:
2220                         if (action.duration)
2221                             state->setDouble(ASCIILiteral("duration"), action.duration.value());
2222                         break;
2223                     case Action::Subtype::PointerUp:
2224                     case Action::Subtype::PointerDown:
2225                     case Action::Subtype::PointerMove:
2226                     case Action::Subtype::PointerCancel:
2227                         ASSERT_NOT_REACHED();
2228                     }
2229                     if (currentState.pressedKey)
2230                         state->setString(ASCIILiteral("pressedCharKey"), currentState.pressedKey.value());
2231                     if (currentState.pressedVirtualKey)
2232                         state->setString(ASCIILiteral("pressedVirtualKey"), currentState.pressedVirtualKey.value());
2233                     break;
2234                 }
2235                 states->pushObject(WTFMove(state));
2236             }
2237             RefPtr<JSON::Object> stepStates = JSON::Object::create();
2238             stepStates->setArray(ASCIILiteral("states"), WTFMove(states));
2239             steps->pushObject(WTFMove(stepStates));
2240         }
2241
2242         parameters->setArray(ASCIILiteral("steps"), WTFMove(steps));
2243         m_host->sendCommandToBackend(ASCIILiteral("performInteractionSequence"), WTFMove(parameters), [this, protectedThis = makeRef(*this), completionHandler = WTFMove(completionHandler)] (SessionHost::CommandResponse&& response) {
2244             if (response.isError) {
2245                 completionHandler(CommandResult::fail(WTFMove(response.responseObject)));
2246                 return;
2247             }
2248             completionHandler(CommandResult::success());
2249         });
2250     });
2251 }
2252
2253 void Session::releaseActions(Function<void (CommandResult&&)>&& completionHandler)
2254 {
2255     if (!m_toplevelBrowsingContext) {
2256         completionHandler(CommandResult::fail(CommandResult::ErrorCode::NoSuchWindow));
2257         return;
2258     }
2259
2260     m_activeInputSources.clear();
2261     m_inputStateTable.clear();
2262
2263     RefPtr<JSON::Object> parameters = JSON::Object::create();
2264     parameters->setString(ASCIILiteral("handle"), m_toplevelBrowsingContext.value());
2265     m_host->sendCommandToBackend(ASCIILiteral("cancelInteractionSequence"), WTFMove(parameters), [this, protectedThis = makeRef(*this), completionHandler = WTFMove(completionHandler)](SessionHost::CommandResponse&& response) {
2266         if (response.isError) {
2267             completionHandler(CommandResult::fail(WTFMove(response.responseObject)));
2268             return;
2269         }
2270         completionHandler(CommandResult::success());
2271     });
2272 }
2273
2274 void Session::dismissAlert(Function<void (CommandResult&&)>&& completionHandler)
2275 {
2276     if (!m_toplevelBrowsingContext) {
2277         completionHandler(CommandResult::fail(CommandResult::ErrorCode::NoSuchWindow));
2278         return;
2279     }
2280
2281     RefPtr<JSON::Object> parameters = JSON::Object::create();
2282     parameters->setString(ASCIILiteral("browsingContextHandle"), m_toplevelBrowsingContext.value());
2283     m_host->sendCommandToBackend(ASCIILiteral("dismissCurrentJavaScriptDialog"), WTFMove(parameters), [this, protectedThis = makeRef(*this), completionHandler = WTFMove(completionHandler)](SessionHost::CommandResponse&& response) {
2284         if (response.isError) {
2285             completionHandler(CommandResult::fail(WTFMove(response.responseObject)));
2286             return;
2287         }
2288         completionHandler(CommandResult::success());
2289     });
2290 }
2291
2292 void Session::acceptAlert(Function<void (CommandResult&&)>&& completionHandler)
2293 {
2294     if (!m_toplevelBrowsingContext) {
2295         completionHandler(CommandResult::fail(CommandResult::ErrorCode::NoSuchWindow));
2296         return;
2297     }
2298
2299     RefPtr<JSON::Object> parameters = JSON::Object::create();
2300     parameters->setString(ASCIILiteral("browsingContextHandle"), m_toplevelBrowsingContext.value());
2301     m_host->sendCommandToBackend(ASCIILiteral("acceptCurrentJavaScriptDialog"), WTFMove(parameters), [this, protectedThis = makeRef(*this), completionHandler = WTFMove(completionHandler)](SessionHost::CommandResponse&& response) {
2302         if (response.isError) {
2303             completionHandler(CommandResult::fail(WTFMove(response.responseObject)));
2304             return;
2305         }
2306         completionHandler(CommandResult::success());
2307     });
2308 }
2309
2310 void Session::getAlertText(Function<void (CommandResult&&)>&& completionHandler)
2311 {
2312     if (!m_toplevelBrowsingContext) {
2313         completionHandler(CommandResult::fail(CommandResult::ErrorCode::NoSuchWindow));
2314         return;
2315     }
2316
2317     RefPtr<JSON::Object> parameters = JSON::Object::create();
2318     parameters->setString(ASCIILiteral("browsingContextHandle"), m_toplevelBrowsingContext.value());
2319     m_host->sendCommandToBackend(ASCIILiteral("messageOfCurrentJavaScriptDialog"), WTFMove(parameters), [this, protectedThis = makeRef(*this), completionHandler = WTFMove(completionHandler)](SessionHost::CommandResponse&& response) {
2320         if (response.isError || !response.responseObject) {
2321             completionHandler(CommandResult::fail(WTFMove(response.responseObject)));
2322             return;
2323         }
2324         String valueString;
2325         if (!response.responseObject->getString(ASCIILiteral("message"), valueString)) {
2326             completionHandler(CommandResult::fail(CommandResult::ErrorCode::UnknownError));
2327             return;
2328         }
2329         completionHandler(CommandResult::success(JSON::Value::create(valueString)));
2330     });
2331 }
2332
2333 void Session::sendAlertText(const String& text, Function<void (CommandResult&&)>&& completionHandler)
2334 {
2335     if (!m_toplevelBrowsingContext) {
2336         completionHandler(CommandResult::fail(CommandResult::ErrorCode::NoSuchWindow));
2337         return;
2338     }
2339
2340     RefPtr<JSON::Object> parameters = JSON::Object::create();
2341     parameters->setString(ASCIILiteral("browsingContextHandle"), m_toplevelBrowsingContext.value());
2342     parameters->setString(ASCIILiteral("userInput"), text);
2343     m_host->sendCommandToBackend(ASCIILiteral("setUserInputForCurrentJavaScriptPrompt"), WTFMove(parameters), [this, protectedThis = makeRef(*this), completionHandler = WTFMove(completionHandler)](SessionHost::CommandResponse&& response) {
2344         if (response.isError) {
2345             completionHandler(CommandResult::fail(WTFMove(response.responseObject)));
2346             return;
2347         }
2348         completionHandler(CommandResult::success());
2349     });
2350 }
2351
2352 void Session::takeScreenshot(std::optional<String> elementID, std::optional<bool> scrollIntoView, Function<void (CommandResult&&)>&& completionHandler)
2353 {
2354     if (!m_toplevelBrowsingContext) {
2355         completionHandler(CommandResult::fail(CommandResult::ErrorCode::NoSuchWindow));
2356         return;
2357     }
2358
2359     handleUserPrompts([this, elementID, scrollIntoView, completionHandler = WTFMove(completionHandler)](CommandResult&& result) mutable {
2360         if (result.isError()) {
2361             completionHandler(WTFMove(result));
2362             return;
2363         }
2364         RefPtr<JSON::Object> parameters = JSON::Object::create();
2365         parameters->setString(ASCIILiteral("handle"), m_toplevelBrowsingContext.value());
2366         if (m_currentBrowsingContext)
2367             parameters->setString(ASCIILiteral("frameHandle"), m_currentBrowsingContext.value());
2368         if (elementID)
2369             parameters->setString(ASCIILiteral("nodeHandle"), elementID.value());
2370         else
2371             parameters->setBoolean(ASCIILiteral("clipToViewport"), true);
2372         if (scrollIntoView.value_or(false))
2373             parameters->setBoolean(ASCIILiteral("scrollIntoViewIfNeeded"), true);
2374         m_host->sendCommandToBackend(ASCIILiteral("takeScreenshot"), WTFMove(parameters), [this, protectedThis = makeRef(*this), completionHandler = WTFMove(completionHandler)](SessionHost::CommandResponse&& response) mutable {
2375             if (response.isError || !response.responseObject) {
2376                 completionHandler(CommandResult::fail(WTFMove(response.responseObject)));
2377                 return;
2378             }
2379             String data;
2380             if (!response.responseObject->getString(ASCIILiteral("data"), data)) {
2381                 completionHandler(CommandResult::fail(CommandResult::ErrorCode::UnknownError));
2382                 return;
2383             }
2384             completionHandler(CommandResult::success(JSON::Value::create(data)));
2385         });
2386     });
2387 }
2388
2389 } // namespace WebDriver