Fix invalid optional access in WebDriver Session::createTopLevelBrowsingContext.
[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 { "element-6066-11e4-a52e-4f735466cecf"_s };
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("script"_s, m_scriptTimeout.millisecondsAs<int>());
130     parameters->setInteger("pageLoad"_s, m_pageLoadTimeout.millisecondsAs<int>());
131     parameters->setInteger("implicit"_s, 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);
181     m_host->sendCommandToBackend("createBrowsingContext"_s, 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("handle"_s, 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("browsingContextHandle"_s, m_toplevelBrowsingContext.value());
200     m_host->sendCommandToBackend("isShowingJavaScriptDialog"_s, 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("text"_s, 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("handle"_s, m_toplevelBrowsingContext.value());
301         parameters->setString("url"_s, url);
302         parameters->setInteger("pageLoadTimeout"_s, m_pageLoadTimeout.millisecondsAs<int>());
303         if (auto pageLoadStrategy = pageLoadStrategyString())
304             parameters->setString("pageLoadStrategy"_s, pageLoadStrategy.value());
305         m_host->sendCommandToBackend("navigateBrowsingContext"_s, 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("handle"_s, m_toplevelBrowsingContext.value());
331         m_host->sendCommandToBackend("getBrowsingContext"_s, 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("handle"_s, m_toplevelBrowsingContext.value());
365         parameters->setInteger("pageLoadTimeout"_s, m_pageLoadTimeout.millisecondsAs<int>());
366         if (auto pageLoadStrategy = pageLoadStrategyString())
367             parameters->setString("pageLoadStrategy"_s, pageLoadStrategy.value());
368         m_host->sendCommandToBackend("goBackInBrowsingContext"_s, 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("handle"_s, m_toplevelBrowsingContext.value());
393         parameters->setInteger("pageLoadTimeout"_s, m_pageLoadTimeout.millisecondsAs<int>());
394         if (auto pageLoadStrategy = pageLoadStrategyString())
395             parameters->setString("pageLoadStrategy"_s, pageLoadStrategy.value());
396         m_host->sendCommandToBackend("goForwardInBrowsingContext"_s, 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("handle"_s, m_toplevelBrowsingContext.value());
421         parameters->setInteger("pageLoadTimeout"_s, m_pageLoadTimeout.millisecondsAs<int>());
422         if (auto pageLoadStrategy = pageLoadStrategyString())
423             parameters->setString("pageLoadStrategy"_s, pageLoadStrategy.value());
424         m_host->sendCommandToBackend("reloadBrowsingContext"_s, 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("browsingContextHandle"_s, m_toplevelBrowsingContext.value());
449         parameters->setString("function"_s, "function() { return document.title; }"_s);
450         parameters->setArray("arguments"_s, JSON::Array::create());
451         m_host->sendCommandToBackend("evaluateJavaScriptFunction"_s, 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("result"_s, 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("handle"_s, m_toplevelBrowsingContext.value());
480     m_host->sendCommandToBackend("getBrowsingContext"_s, 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("context"_s, browsingContext)) {
487             completionHandler(CommandResult::fail(CommandResult::ErrorCode::UnknownError));
488             return;
489         }
490         String handle;
491         if (!browsingContext->getString("handle"_s, 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("handle"_s, toplevelBrowsingContext);
503     m_host->sendCommandToBackend("closeBrowsingContext"_s, 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("browsingContextHandle"_s, windowHandle);
546     m_host->sendCommandToBackend("switchToBrowsingContext"_s, 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("getBrowsingContexts"_s, 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("contexts"_s, 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("handle"_s, 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("browsingContextHandle"_s, m_toplevelBrowsingContext.value());
609         if (m_currentBrowsingContext)
610             parameters->setString("frameHandle"_s, 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("ordinal"_s, frameIndex);
619         } else {
620             String frameElementID = extractElementID(*frameID);
621             if (!frameElementID.isEmpty())
622                 parameters->setString("nodeHandle"_s, frameElementID);
623             else {
624                 String frameName;
625                 if (!frameID->asString(frameName)) {
626                     completionHandler(CommandResult::fail(CommandResult::ErrorCode::NoSuchFrame));
627                     return;
628                 }
629                 parameters->setString("name"_s, frameName);
630             }
631         }
632
633         m_host->sendCommandToBackend("resolveChildFrameHandle"_s, 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("result"_s, 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("browsingContextHandle"_s, m_toplevelBrowsingContext.value());
668         parameters->setString("frameHandle"_s, m_currentBrowsingContext.value());
669         m_host->sendCommandToBackend("resolveParentFrameHandle"_s, 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("result"_s, 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("handle"_s, m_toplevelBrowsingContext.value());
689     m_host->sendCommandToBackend("getBrowsingContext"_s, 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("context"_s, browsingContext)) {
696             completionHandler(CommandResult::fail(CommandResult::ErrorCode::UnknownError));
697             return;
698         }
699         RefPtr<JSON::Object> windowOrigin;
700         double x, y;
701         if (!browsingContext->getObject("windowOrigin"_s, windowOrigin)
702             || !windowOrigin->getDouble("x"_s, x)
703             || !windowOrigin->getDouble("y"_s, y)) {
704             completionHandler(CommandResult::fail(CommandResult::ErrorCode::UnknownError));
705             return;
706         }
707         RefPtr<JSON::Object> windowSize;
708         double width, height;
709         if (!browsingContext->getObject("windowSize"_s, windowSize)
710             || !windowSize->getDouble("width"_s, width)
711             || !windowSize->getDouble("height"_s, height)) {
712             completionHandler(CommandResult::fail(CommandResult::ErrorCode::UnknownError));
713             return;
714         }
715         auto windowRect = JSON::Object::create();
716         windowRect->setDouble("x"_s, x);
717         windowRect->setDouble("y"_s, y);
718         windowRect->setDouble("width"_s, width);
719         windowRect->setDouble("height"_s, 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("handle"_s, 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("origin"_s, 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("size"_s, WTFMove(windowSize));
766         }
767         m_host->sendCommandToBackend("setWindowFrameOfBrowsingContext"_s, 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 void Session::maximizeWindow(Function<void (CommandResult&&)>&& completionHandler)
778 {
779     if (!m_toplevelBrowsingContext) {
780         completionHandler(CommandResult::fail(CommandResult::ErrorCode::NoSuchWindow));
781         return;
782     }
783
784     handleUserPrompts([this, completionHandler = WTFMove(completionHandler)](CommandResult&& result) mutable {
785         if (result.isError()) {
786             completionHandler(WTFMove(result));
787             return;
788         }
789
790         RefPtr<JSON::Object> parameters = JSON::Object::create();
791         parameters->setString("handle"_s, m_toplevelBrowsingContext.value());
792         m_host->sendCommandToBackend("maximizeWindowOfBrowsingContext"_s, WTFMove(parameters), [this, protectedThis = makeRef(*this), completionHandler = WTFMove(completionHandler)] (SessionHost::CommandResponse&& response) mutable {
793             if (response.isError) {
794                 completionHandler(CommandResult::fail(WTFMove(response.responseObject)));
795                 return;
796             }
797             getToplevelBrowsingContextRect(WTFMove(completionHandler));
798         });
799     });
800 }
801
802 void Session::minimizeWindow(Function<void (CommandResult&&)>&& completionHandler)
803 {
804     if (!m_toplevelBrowsingContext) {
805         completionHandler(CommandResult::fail(CommandResult::ErrorCode::NoSuchWindow));
806         return;
807     }
808
809     handleUserPrompts([this, completionHandler = WTFMove(completionHandler)](CommandResult&& result) mutable {
810         if (result.isError()) {
811             completionHandler(WTFMove(result));
812             return;
813         }
814
815         RefPtr<JSON::Object> parameters = JSON::Object::create();
816         parameters->setString("handle"_s, m_toplevelBrowsingContext.value());
817         m_host->sendCommandToBackend("hideWindowOfBrowsingContext"_s, WTFMove(parameters), [this, protectedThis = makeRef(*this), completionHandler = WTFMove(completionHandler)] (SessionHost::CommandResponse&& response) mutable {
818             if (response.isError) {
819                 completionHandler(CommandResult::fail(WTFMove(response.responseObject)));
820                 return;
821             }
822             getToplevelBrowsingContextRect(WTFMove(completionHandler));
823         });
824     });
825 }
826
827 void Session::fullscreenWindow(Function<void (CommandResult&&)>&& completionHandler)
828 {
829     if (!m_toplevelBrowsingContext) {
830         completionHandler(CommandResult::fail(CommandResult::ErrorCode::NoSuchWindow));
831         return;
832     }
833
834     handleUserPrompts([this, completionHandler = WTFMove(completionHandler)](CommandResult&& result) mutable {
835         if (result.isError()) {
836             completionHandler(WTFMove(result));
837             return;
838         }
839
840         RefPtr<JSON::Object> parameters = JSON::Object::create();
841         parameters->setString("browsingContextHandle"_s, m_toplevelBrowsingContext.value());
842         parameters->setString("function"_s, EnterFullscreenJavaScript);
843         parameters->setArray("arguments"_s, JSON::Array::create());
844         parameters->setBoolean("expectsImplicitCallbackArgument"_s, true);
845         m_host->sendCommandToBackend("evaluateJavaScriptFunction"_s, WTFMove(parameters), [this, protectedThis = makeRef(*this), completionHandler = WTFMove(completionHandler)](SessionHost::CommandResponse&& response) mutable {
846             if (response.isError || !response.responseObject) {
847                 completionHandler(CommandResult::fail(WTFMove(response.responseObject)));
848                 return;
849             }
850
851             String valueString;
852             if (!response.responseObject->getString("result"_s, valueString)) {
853                 completionHandler(CommandResult::fail(CommandResult::ErrorCode::UnknownError));
854                 return;
855             }
856             RefPtr<JSON::Value> resultValue;
857             if (!JSON::Value::parseJSON(valueString, resultValue)) {
858                 completionHandler(CommandResult::fail(CommandResult::ErrorCode::UnknownError));
859                 return;
860             }
861
862             getToplevelBrowsingContextRect(WTFMove(completionHandler));
863         });
864     });
865 }
866
867 RefPtr<JSON::Object> Session::createElement(RefPtr<JSON::Value>&& value)
868 {
869     RefPtr<JSON::Object> valueObject;
870     if (!value->asObject(valueObject))
871         return nullptr;
872
873     String elementID;
874     if (!valueObject->getString("session-node-" + id(), elementID))
875         return nullptr;
876
877     RefPtr<JSON::Object> elementObject = JSON::Object::create();
878     elementObject->setString(webElementIdentifier(), elementID);
879     return elementObject;
880 }
881
882 RefPtr<JSON::Object> Session::createElement(const String& elementID)
883 {
884     RefPtr<JSON::Object> elementObject = JSON::Object::create();
885     elementObject->setString("session-node-" + id(), elementID);
886     return elementObject;
887 }
888
889 RefPtr<JSON::Object> Session::extractElement(JSON::Value& value)
890 {
891     String elementID = extractElementID(value);
892     return !elementID.isEmpty() ? createElement(elementID) : nullptr;
893 }
894
895 String Session::extractElementID(JSON::Value& value)
896 {
897     RefPtr<JSON::Object> valueObject;
898     if (!value.asObject(valueObject))
899         return emptyString();
900
901     String elementID;
902     if (!valueObject->getString(webElementIdentifier(), elementID))
903         return emptyString();
904
905     return elementID;
906 }
907
908 void Session::computeElementLayout(const String& elementID, OptionSet<ElementLayoutOption> options, Function<void (std::optional<Rect>&&, std::optional<Point>&&, bool, RefPtr<JSON::Object>&&)>&& completionHandler)
909 {
910     ASSERT(m_toplevelBrowsingContext.value());
911
912     RefPtr<JSON::Object> parameters = JSON::Object::create();
913     parameters->setString("browsingContextHandle"_s, m_toplevelBrowsingContext.value());
914     parameters->setString("frameHandle"_s, m_currentBrowsingContext.value_or(emptyString()));
915     parameters->setString("nodeHandle"_s, elementID);
916     parameters->setBoolean("scrollIntoViewIfNeeded"_s, options.contains(ElementLayoutOption::ScrollIntoViewIfNeeded));
917     parameters->setString("coordinateSystem"_s, options.contains(ElementLayoutOption::UseViewportCoordinates) ? "LayoutViewport"_s : "Page"_s);
918     m_host->sendCommandToBackend("computeElementLayout"_s, WTFMove(parameters), [this, protectedThis = makeRef(*this), completionHandler = WTFMove(completionHandler)](SessionHost::CommandResponse&& response) mutable {
919         if (response.isError || !response.responseObject) {
920             completionHandler(std::nullopt, std::nullopt, false, WTFMove(response.responseObject));
921             return;
922         }
923         RefPtr<JSON::Object> rectObject;
924         if (!response.responseObject->getObject("rect"_s, rectObject)) {
925             completionHandler(std::nullopt, std::nullopt, false, nullptr);
926             return;
927         }
928         std::optional<int> elementX;
929         std::optional<int> elementY;
930         RefPtr<JSON::Object> elementPosition;
931         if (rectObject->getObject("origin"_s, elementPosition)) {
932             int x, y;
933             if (elementPosition->getInteger("x"_s, x) && elementPosition->getInteger("y"_s, y)) {
934                 elementX = x;
935                 elementY = y;
936             }
937         }
938         if (!elementX || !elementY) {
939             completionHandler(std::nullopt, std::nullopt, false, nullptr);
940             return;
941         }
942         std::optional<int> elementWidth;
943         std::optional<int> elementHeight;
944         RefPtr<JSON::Object> elementSize;
945         if (rectObject->getObject("size"_s, elementSize)) {
946             int width, height;
947             if (elementSize->getInteger("width"_s, width) && elementSize->getInteger("height"_s, height)) {
948                 elementWidth = width;
949                 elementHeight = height;
950             }
951         }
952         if (!elementWidth || !elementHeight) {
953             completionHandler(std::nullopt, std::nullopt, false, nullptr);
954             return;
955         }
956         Rect rect = { { elementX.value(), elementY.value() }, { elementWidth.value(), elementHeight.value() } };
957
958         bool isObscured;
959         if (!response.responseObject->getBoolean("isObscured"_s, isObscured)) {
960             completionHandler(std::nullopt, std::nullopt, false, nullptr);
961             return;
962         }
963         RefPtr<JSON::Object> inViewCenterPointObject;
964         if (!response.responseObject->getObject("inViewCenterPoint"_s, inViewCenterPointObject)) {
965             completionHandler(rect, std::nullopt, isObscured, nullptr);
966             return;
967         }
968         int inViewCenterPointX, inViewCenterPointY;
969         if (!inViewCenterPointObject->getInteger("x"_s, inViewCenterPointX)
970             || !inViewCenterPointObject->getInteger("y"_s, inViewCenterPointY)) {
971             completionHandler(std::nullopt, std::nullopt, isObscured, nullptr);
972             return;
973         }
974         Point inViewCenterPoint = { inViewCenterPointX, inViewCenterPointY };
975         completionHandler(rect, inViewCenterPoint, isObscured, nullptr);
976     });
977 }
978
979 void Session::findElements(const String& strategy, const String& selector, FindElementsMode mode, const String& rootElementID, Function<void (CommandResult&&)>&& completionHandler)
980 {
981     if (!m_toplevelBrowsingContext) {
982         completionHandler(CommandResult::fail(CommandResult::ErrorCode::NoSuchWindow));
983         return;
984     }
985
986     RefPtr<JSON::Array> arguments = JSON::Array::create();
987     arguments->pushString(JSON::Value::create(strategy)->toJSONString());
988     if (rootElementID.isEmpty())
989         arguments->pushString(JSON::Value::null()->toJSONString());
990     else
991         arguments->pushString(createElement(rootElementID)->toJSONString());
992     arguments->pushString(JSON::Value::create(selector)->toJSONString());
993     arguments->pushString(JSON::Value::create(mode == FindElementsMode::Single)->toJSONString());
994     arguments->pushString(JSON::Value::create(m_implicitWaitTimeout.milliseconds())->toJSONString());
995
996     RefPtr<JSON::Object> parameters = JSON::Object::create();
997     parameters->setString("browsingContextHandle"_s, m_toplevelBrowsingContext.value());
998     if (m_currentBrowsingContext)
999         parameters->setString("frameHandle"_s, m_currentBrowsingContext.value());
1000     parameters->setString("function"_s, FindNodesJavaScript);
1001     parameters->setArray("arguments"_s, WTFMove(arguments));
1002     parameters->setBoolean("expectsImplicitCallbackArgument"_s, true);
1003     // If there's an implicit wait, use one second more as callback timeout.
1004     if (m_implicitWaitTimeout)
1005         parameters->setInteger("callbackTimeout"_s, Seconds(m_implicitWaitTimeout + 1_s).millisecondsAs<int>());
1006
1007     m_host->sendCommandToBackend("evaluateJavaScriptFunction"_s, WTFMove(parameters), [this, protectedThis = makeRef(*this), mode, completionHandler = WTFMove(completionHandler)](SessionHost::CommandResponse&& response) {
1008         if (response.isError || !response.responseObject) {
1009             completionHandler(CommandResult::fail(WTFMove(response.responseObject)));
1010             return;
1011         }
1012         String valueString;
1013         if (!response.responseObject->getString("result"_s, valueString)) {
1014             completionHandler(CommandResult::fail(CommandResult::ErrorCode::UnknownError));
1015             return;
1016         }
1017         RefPtr<JSON::Value> resultValue;
1018         if (!JSON::Value::parseJSON(valueString, resultValue)) {
1019             completionHandler(CommandResult::fail(CommandResult::ErrorCode::UnknownError));
1020             return;
1021         }
1022
1023         switch (mode) {
1024         case FindElementsMode::Single: {
1025             RefPtr<JSON::Object> elementObject = createElement(WTFMove(resultValue));
1026             if (!elementObject) {
1027                 completionHandler(CommandResult::fail(CommandResult::ErrorCode::NoSuchElement));
1028                 return;
1029             }
1030             completionHandler(CommandResult::success(WTFMove(elementObject)));
1031             break;
1032         }
1033         case FindElementsMode::Multiple: {
1034             RefPtr<JSON::Array> elementsArray;
1035             if (!resultValue->asArray(elementsArray)) {
1036                 completionHandler(CommandResult::fail(CommandResult::ErrorCode::NoSuchElement));
1037                 return;
1038             }
1039             RefPtr<JSON::Array> elementObjectsArray = JSON::Array::create();
1040             unsigned elementsArrayLength = elementsArray->length();
1041             for (unsigned i = 0; i < elementsArrayLength; ++i) {
1042                 if (auto elementObject = createElement(elementsArray->get(i)))
1043                     elementObjectsArray->pushObject(WTFMove(elementObject));
1044             }
1045             completionHandler(CommandResult::success(WTFMove(elementObjectsArray)));
1046             break;
1047         }
1048         }
1049     });
1050 }
1051
1052 void Session::getActiveElement(Function<void (CommandResult&&)>&& completionHandler)
1053 {
1054     if (!m_toplevelBrowsingContext) {
1055         completionHandler(CommandResult::fail(CommandResult::ErrorCode::NoSuchWindow));
1056         return;
1057     }
1058
1059     handleUserPrompts([this, completionHandler = WTFMove(completionHandler)](CommandResult&& result) mutable {
1060         if (result.isError()) {
1061             completionHandler(WTFMove(result));
1062             return;
1063         }
1064         RefPtr<JSON::Object> parameters = JSON::Object::create();
1065         parameters->setString("browsingContextHandle"_s, m_toplevelBrowsingContext.value());
1066         parameters->setString("function"_s, "function() { return document.activeElement; }"_s);
1067         parameters->setArray("arguments"_s, JSON::Array::create());
1068         m_host->sendCommandToBackend("evaluateJavaScriptFunction"_s, WTFMove(parameters), [this, protectedThis = makeRef(*this), completionHandler = WTFMove(completionHandler)](SessionHost::CommandResponse&& response) {
1069             if (response.isError || !response.responseObject) {
1070                 completionHandler(CommandResult::fail(WTFMove(response.responseObject)));
1071                 return;
1072             }
1073             String valueString;
1074             if (!response.responseObject->getString("result"_s, valueString)) {
1075                 completionHandler(CommandResult::fail(CommandResult::ErrorCode::UnknownError));
1076                 return;
1077             }
1078             RefPtr<JSON::Value> resultValue;
1079             if (!JSON::Value::parseJSON(valueString, resultValue)) {
1080                 completionHandler(CommandResult::fail(CommandResult::ErrorCode::UnknownError));
1081                 return;
1082             }
1083             RefPtr<JSON::Object> elementObject = createElement(WTFMove(resultValue));
1084             if (!elementObject) {
1085                 completionHandler(CommandResult::fail(CommandResult::ErrorCode::NoSuchElement));
1086                 return;
1087             }
1088             completionHandler(CommandResult::success(WTFMove(elementObject)));
1089         });
1090     });
1091 }
1092
1093 void Session::isElementSelected(const String& elementID, Function<void (CommandResult&&)>&& completionHandler)
1094 {
1095     if (!m_toplevelBrowsingContext) {
1096         completionHandler(CommandResult::fail(CommandResult::ErrorCode::NoSuchWindow));
1097         return;
1098     }
1099
1100     handleUserPrompts([this, elementID, completionHandler = WTFMove(completionHandler)](CommandResult&& result) mutable {
1101         if (result.isError()) {
1102             completionHandler(WTFMove(result));
1103             return;
1104         }
1105         RefPtr<JSON::Array> arguments = JSON::Array::create();
1106         arguments->pushString(createElement(elementID)->toJSONString());
1107         arguments->pushString(JSON::Value::create("selected")->toJSONString());
1108
1109         RefPtr<JSON::Object> parameters = JSON::Object::create();
1110         parameters->setString("browsingContextHandle"_s, m_toplevelBrowsingContext.value());
1111         if (m_currentBrowsingContext)
1112             parameters->setString("frameHandle"_s, m_currentBrowsingContext.value());
1113         parameters->setString("function"_s, ElementAttributeJavaScript);
1114         parameters->setArray("arguments"_s, WTFMove(arguments));
1115         m_host->sendCommandToBackend("evaluateJavaScriptFunction"_s, WTFMove(parameters), [this, protectedThis = makeRef(*this), completionHandler = WTFMove(completionHandler)](SessionHost::CommandResponse&& response) {
1116             if (response.isError || !response.responseObject) {
1117                 completionHandler(CommandResult::fail(WTFMove(response.responseObject)));
1118                 return;
1119             }
1120             String valueString;
1121             if (!response.responseObject->getString("result"_s, valueString)) {
1122                 completionHandler(CommandResult::fail(CommandResult::ErrorCode::UnknownError));
1123                 return;
1124             }
1125             RefPtr<JSON::Value> resultValue;
1126             if (!JSON::Value::parseJSON(valueString, resultValue)) {
1127                 completionHandler(CommandResult::fail(CommandResult::ErrorCode::UnknownError));
1128                 return;
1129             }
1130             if (resultValue->isNull()) {
1131                 completionHandler(CommandResult::success(JSON::Value::create(false)));
1132                 return;
1133             }
1134             String booleanResult;
1135             if (!resultValue->asString(booleanResult) || booleanResult != "true") {
1136                 completionHandler(CommandResult::fail(CommandResult::ErrorCode::UnknownError));
1137                 return;
1138             }
1139             completionHandler(CommandResult::success(JSON::Value::create(true)));
1140         });
1141     });
1142 }
1143
1144 void Session::getElementText(const String& elementID, Function<void (CommandResult&&)>&& completionHandler)
1145 {
1146     if (!m_toplevelBrowsingContext) {
1147         completionHandler(CommandResult::fail(CommandResult::ErrorCode::NoSuchWindow));
1148         return;
1149     }
1150
1151     handleUserPrompts([this, elementID, completionHandler = WTFMove(completionHandler)](CommandResult&& result) mutable {
1152         if (result.isError()) {
1153             completionHandler(WTFMove(result));
1154             return;
1155         }
1156         RefPtr<JSON::Array> arguments = JSON::Array::create();
1157         arguments->pushString(createElement(elementID)->toJSONString());
1158
1159         RefPtr<JSON::Object> parameters = JSON::Object::create();
1160         parameters->setString("browsingContextHandle"_s, m_toplevelBrowsingContext.value());
1161         if (m_currentBrowsingContext)
1162             parameters->setString("frameHandle"_s, m_currentBrowsingContext.value());
1163         // FIXME: Add an atom to properly implement this instead of just using innerText.
1164         parameters->setString("function"_s, "function(element) { return element.innerText.replace(/^[^\\S\\xa0]+|[^\\S\\xa0]+$/g, '') }"_s);
1165         parameters->setArray("arguments"_s, WTFMove(arguments));
1166         m_host->sendCommandToBackend("evaluateJavaScriptFunction"_s, WTFMove(parameters), [this, protectedThis = makeRef(*this), completionHandler = WTFMove(completionHandler)](SessionHost::CommandResponse&& response) {
1167             if (response.isError || !response.responseObject) {
1168                 completionHandler(CommandResult::fail(WTFMove(response.responseObject)));
1169                 return;
1170             }
1171             String valueString;
1172             if (!response.responseObject->getString("result"_s, valueString)) {
1173                 completionHandler(CommandResult::fail(CommandResult::ErrorCode::UnknownError));
1174                 return;
1175             }
1176             RefPtr<JSON::Value> resultValue;
1177             if (!JSON::Value::parseJSON(valueString, resultValue)) {
1178                 completionHandler(CommandResult::fail(CommandResult::ErrorCode::UnknownError));
1179                 return;
1180             }
1181             completionHandler(CommandResult::success(WTFMove(resultValue)));
1182         });
1183     });
1184 }
1185
1186 void Session::getElementTagName(const String& elementID, Function<void (CommandResult&&)>&& completionHandler)
1187 {
1188     if (!m_toplevelBrowsingContext) {
1189         completionHandler(CommandResult::fail(CommandResult::ErrorCode::NoSuchWindow));
1190         return;
1191     }
1192
1193     handleUserPrompts([this, elementID, completionHandler = WTFMove(completionHandler)](CommandResult&& result) mutable {
1194         if (result.isError()) {
1195             completionHandler(WTFMove(result));
1196             return;
1197         }
1198         RefPtr<JSON::Array> arguments = JSON::Array::create();
1199         arguments->pushString(createElement(elementID)->toJSONString());
1200
1201         RefPtr<JSON::Object> parameters = JSON::Object::create();
1202         parameters->setString("browsingContextHandle"_s, m_toplevelBrowsingContext.value());
1203         if (m_currentBrowsingContext)
1204             parameters->setString("frameHandle"_s, m_currentBrowsingContext.value());
1205         parameters->setString("function"_s, "function(element) { return element.tagName.toLowerCase() }"_s);
1206         parameters->setArray("arguments"_s, WTFMove(arguments));
1207         m_host->sendCommandToBackend("evaluateJavaScriptFunction"_s, WTFMove(parameters), [this, protectedThis = makeRef(*this), completionHandler = WTFMove(completionHandler)](SessionHost::CommandResponse&& response) {
1208             if (response.isError || !response.responseObject) {
1209                 completionHandler(CommandResult::fail(WTFMove(response.responseObject)));
1210                 return;
1211             }
1212             String valueString;
1213             if (!response.responseObject->getString("result"_s, valueString)) {
1214                 completionHandler(CommandResult::fail(CommandResult::ErrorCode::UnknownError));
1215                 return;
1216             }
1217             RefPtr<JSON::Value> resultValue;
1218             if (!JSON::Value::parseJSON(valueString, resultValue)) {
1219                 completionHandler(CommandResult::fail(CommandResult::ErrorCode::UnknownError));
1220                 return;
1221             }
1222             completionHandler(CommandResult::success(WTFMove(resultValue)));
1223         });
1224     });
1225 }
1226
1227 void Session::getElementRect(const String& elementID, Function<void (CommandResult&&)>&& completionHandler)
1228 {
1229     if (!m_toplevelBrowsingContext) {
1230         completionHandler(CommandResult::fail(CommandResult::ErrorCode::NoSuchWindow));
1231         return;
1232     }
1233
1234     handleUserPrompts([this, elementID, completionHandler = WTFMove(completionHandler)](CommandResult&& result) mutable {
1235         if (result.isError()) {
1236             completionHandler(WTFMove(result));
1237             return;
1238         }
1239         computeElementLayout(elementID, { }, [this, protectedThis = makeRef(*this), completionHandler = WTFMove(completionHandler)](std::optional<Rect>&& rect, std::optional<Point>&&, bool, RefPtr<JSON::Object>&& error) {
1240             if (!rect || error) {
1241                 completionHandler(CommandResult::fail(WTFMove(error)));
1242                 return;
1243             }
1244             RefPtr<JSON::Object> rectObject = JSON::Object::create();
1245             rectObject->setInteger("x"_s, rect.value().origin.x);
1246             rectObject->setInteger("y"_s, rect.value().origin.y);
1247             rectObject->setInteger("width"_s, rect.value().size.width);
1248             rectObject->setInteger("height"_s, rect.value().size.height);
1249             completionHandler(CommandResult::success(WTFMove(rectObject)));
1250         });
1251     });
1252 }
1253
1254 void Session::isElementEnabled(const String& elementID, Function<void (CommandResult&&)>&& completionHandler)
1255 {
1256     if (!m_toplevelBrowsingContext) {
1257         completionHandler(CommandResult::fail(CommandResult::ErrorCode::NoSuchWindow));
1258         return;
1259     }
1260
1261     handleUserPrompts([this, elementID, completionHandler = WTFMove(completionHandler)](CommandResult&& result) mutable {
1262         if (result.isError()) {
1263             completionHandler(WTFMove(result));
1264             return;
1265         }
1266         RefPtr<JSON::Array> arguments = JSON::Array::create();
1267         arguments->pushString(createElement(elementID)->toJSONString());
1268
1269         RefPtr<JSON::Object> parameters = JSON::Object::create();
1270         parameters->setString("browsingContextHandle"_s, m_toplevelBrowsingContext.value());
1271         if (m_currentBrowsingContext)
1272             parameters->setString("frameHandle"_s, m_currentBrowsingContext.value());
1273         parameters->setString("function"_s, "function(element) { return element.disabled === undefined ? true : !element.disabled }"_s);
1274         parameters->setArray("arguments"_s, WTFMove(arguments));
1275         m_host->sendCommandToBackend("evaluateJavaScriptFunction"_s, WTFMove(parameters), [this, protectedThis = makeRef(*this), completionHandler = WTFMove(completionHandler)](SessionHost::CommandResponse&& response) {
1276             if (response.isError || !response.responseObject) {
1277                 completionHandler(CommandResult::fail(WTFMove(response.responseObject)));
1278                 return;
1279             }
1280             String valueString;
1281             if (!response.responseObject->getString("result"_s, valueString)) {
1282                 completionHandler(CommandResult::fail(CommandResult::ErrorCode::UnknownError));
1283                 return;
1284             }
1285             RefPtr<JSON::Value> resultValue;
1286             if (!JSON::Value::parseJSON(valueString, resultValue)) {
1287                 completionHandler(CommandResult::fail(CommandResult::ErrorCode::UnknownError));
1288                 return;
1289             }
1290             completionHandler(CommandResult::success(WTFMove(resultValue)));
1291         });
1292     });
1293 }
1294
1295 void Session::isElementDisplayed(const String& elementID, Function<void (CommandResult&&)>&& completionHandler)
1296 {
1297     if (!m_toplevelBrowsingContext) {
1298         completionHandler(CommandResult::fail(CommandResult::ErrorCode::NoSuchWindow));
1299         return;
1300     }
1301
1302     handleUserPrompts([this, elementID, completionHandler = WTFMove(completionHandler)](CommandResult&& result) mutable {
1303         if (result.isError()) {
1304             completionHandler(WTFMove(result));
1305             return;
1306         }
1307         RefPtr<JSON::Array> arguments = JSON::Array::create();
1308         arguments->pushString(createElement(elementID)->toJSONString());
1309
1310         RefPtr<JSON::Object> parameters = JSON::Object::create();
1311         parameters->setString("browsingContextHandle"_s, m_toplevelBrowsingContext.value());
1312         if (m_currentBrowsingContext)
1313             parameters->setString("frameHandle"_s, m_currentBrowsingContext.value());
1314         parameters->setString("function"_s, ElementDisplayedJavaScript);
1315         parameters->setArray("arguments"_s, WTFMove(arguments));
1316         m_host->sendCommandToBackend("evaluateJavaScriptFunction"_s, WTFMove(parameters), [this, protectedThis = makeRef(*this), completionHandler = WTFMove(completionHandler)](SessionHost::CommandResponse&& response) {
1317             if (response.isError || !response.responseObject) {
1318                 completionHandler(CommandResult::fail(WTFMove(response.responseObject)));
1319                 return;
1320             }
1321             String valueString;
1322             if (!response.responseObject->getString("result"_s, valueString)) {
1323                 completionHandler(CommandResult::fail(CommandResult::ErrorCode::UnknownError));
1324                 return;
1325             }
1326             RefPtr<JSON::Value> resultValue;
1327             if (!JSON::Value::parseJSON(valueString, resultValue)) {
1328                 completionHandler(CommandResult::fail(CommandResult::ErrorCode::UnknownError));
1329                 return;
1330             }
1331             completionHandler(CommandResult::success(WTFMove(resultValue)));
1332         });
1333     });
1334 }
1335
1336 void Session::getElementAttribute(const String& elementID, const String& attribute, Function<void (CommandResult&&)>&& completionHandler)
1337 {
1338     if (!m_toplevelBrowsingContext) {
1339         completionHandler(CommandResult::fail(CommandResult::ErrorCode::NoSuchWindow));
1340         return;
1341     }
1342
1343     handleUserPrompts([this, elementID, attribute, completionHandler = WTFMove(completionHandler)](CommandResult&& result) mutable {
1344         if (result.isError()) {
1345             completionHandler(WTFMove(result));
1346             return;
1347         }
1348         RefPtr<JSON::Array> arguments = JSON::Array::create();
1349         arguments->pushString(createElement(elementID)->toJSONString());
1350         arguments->pushString(JSON::Value::create(attribute)->toJSONString());
1351
1352         RefPtr<JSON::Object> parameters = JSON::Object::create();
1353         parameters->setString("browsingContextHandle"_s, m_toplevelBrowsingContext.value());
1354         if (m_currentBrowsingContext)
1355             parameters->setString("frameHandle"_s, m_currentBrowsingContext.value());
1356         parameters->setString("function"_s, ElementAttributeJavaScript);
1357         parameters->setArray("arguments"_s, WTFMove(arguments));
1358         m_host->sendCommandToBackend("evaluateJavaScriptFunction"_s, WTFMove(parameters), [this, protectedThis = makeRef(*this), completionHandler = WTFMove(completionHandler)](SessionHost::CommandResponse&& response) {
1359             if (response.isError || !response.responseObject) {
1360                 completionHandler(CommandResult::fail(WTFMove(response.responseObject)));
1361                 return;
1362             }
1363             String valueString;
1364             if (!response.responseObject->getString("result"_s, valueString)) {
1365                 completionHandler(CommandResult::fail(CommandResult::ErrorCode::UnknownError));
1366                 return;
1367             }
1368             RefPtr<JSON::Value> resultValue;
1369             if (!JSON::Value::parseJSON(valueString, resultValue)) {
1370                 completionHandler(CommandResult::fail(CommandResult::ErrorCode::UnknownError));
1371                 return;
1372             }
1373             completionHandler(CommandResult::success(WTFMove(resultValue)));
1374         });
1375     });
1376 }
1377
1378 void Session::getElementProperty(const String& elementID, const String& property, Function<void (CommandResult&&)>&& completionHandler)
1379 {
1380     if (!m_toplevelBrowsingContext) {
1381         completionHandler(CommandResult::fail(CommandResult::ErrorCode::NoSuchWindow));
1382         return;
1383     }
1384
1385     handleUserPrompts([this, elementID, property, completionHandler = WTFMove(completionHandler)](CommandResult&& result) mutable {
1386         if (result.isError()) {
1387             completionHandler(WTFMove(result));
1388             return;
1389         }
1390         RefPtr<JSON::Array> arguments = JSON::Array::create();
1391         arguments->pushString(createElement(elementID)->toJSONString());
1392
1393         RefPtr<JSON::Object> parameters = JSON::Object::create();
1394         parameters->setString("browsingContextHandle"_s, m_toplevelBrowsingContext.value());
1395         if (m_currentBrowsingContext)
1396             parameters->setString("frameHandle"_s, m_currentBrowsingContext.value());
1397         parameters->setString("function"_s, makeString("function(element) { return element.", property, "; }"));
1398         parameters->setArray("arguments"_s, WTFMove(arguments));
1399         m_host->sendCommandToBackend("evaluateJavaScriptFunction"_s, WTFMove(parameters), [this, protectedThis = makeRef(*this), completionHandler = WTFMove(completionHandler)](SessionHost::CommandResponse&& response) {
1400             if (response.isError || !response.responseObject) {
1401                 completionHandler(CommandResult::fail(WTFMove(response.responseObject)));
1402                 return;
1403             }
1404             String valueString;
1405             if (!response.responseObject->getString("result"_s, valueString)) {
1406                 completionHandler(CommandResult::fail(CommandResult::ErrorCode::UnknownError));
1407                 return;
1408             }
1409             RefPtr<JSON::Value> resultValue;
1410             if (!JSON::Value::parseJSON(valueString, resultValue)) {
1411                 completionHandler(CommandResult::fail(CommandResult::ErrorCode::UnknownError));
1412                 return;
1413             }
1414             completionHandler(CommandResult::success(WTFMove(resultValue)));
1415         });
1416     });
1417 }
1418
1419 void Session::getElementCSSValue(const String& elementID, const String& cssProperty, Function<void (CommandResult&&)>&& completionHandler)
1420 {
1421     if (!m_toplevelBrowsingContext) {
1422         completionHandler(CommandResult::fail(CommandResult::ErrorCode::NoSuchWindow));
1423         return;
1424     }
1425
1426     handleUserPrompts([this, elementID, cssProperty, completionHandler = WTFMove(completionHandler)](CommandResult&& result) mutable {
1427         if (result.isError()) {
1428             completionHandler(WTFMove(result));
1429             return;
1430         }
1431         RefPtr<JSON::Array> arguments = JSON::Array::create();
1432         arguments->pushString(createElement(elementID)->toJSONString());
1433
1434         RefPtr<JSON::Object> parameters = JSON::Object::create();
1435         parameters->setString("browsingContextHandle"_s, m_toplevelBrowsingContext.value());
1436         if (m_currentBrowsingContext)
1437             parameters->setString("frameHandle"_s, m_currentBrowsingContext.value());
1438         parameters->setString("function"_s, makeString("function(element) { return document.defaultView.getComputedStyle(element).getPropertyValue('", cssProperty, "'); }"));
1439         parameters->setArray("arguments"_s, WTFMove(arguments));
1440         m_host->sendCommandToBackend("evaluateJavaScriptFunction"_s, WTFMove(parameters), [this, protectedThis = makeRef(*this), completionHandler = WTFMove(completionHandler)](SessionHost::CommandResponse&& response) {
1441             if (response.isError || !response.responseObject) {
1442                 completionHandler(CommandResult::fail(WTFMove(response.responseObject)));
1443                 return;
1444             }
1445             String valueString;
1446             if (!response.responseObject->getString("result"_s, valueString)) {
1447                 completionHandler(CommandResult::fail(CommandResult::ErrorCode::UnknownError));
1448                 return;
1449             }
1450             RefPtr<JSON::Value> resultValue;
1451             if (!JSON::Value::parseJSON(valueString, resultValue)) {
1452                 completionHandler(CommandResult::fail(CommandResult::ErrorCode::UnknownError));
1453                 return;
1454             }
1455             completionHandler(CommandResult::success(WTFMove(resultValue)));
1456         });
1457     });
1458 }
1459
1460 void Session::waitForNavigationToComplete(Function<void (CommandResult&&)>&& completionHandler)
1461 {
1462     if (!m_toplevelBrowsingContext) {
1463         completionHandler(CommandResult::fail(CommandResult::ErrorCode::NoSuchWindow));
1464         return;
1465     }
1466
1467     RefPtr<JSON::Object> parameters = JSON::Object::create();
1468     parameters->setString("browsingContextHandle"_s, m_toplevelBrowsingContext.value());
1469     if (m_currentBrowsingContext)
1470         parameters->setString("frameHandle"_s, m_currentBrowsingContext.value());
1471     parameters->setInteger("pageLoadTimeout"_s, m_pageLoadTimeout.millisecondsAs<int>());
1472     if (auto pageLoadStrategy = pageLoadStrategyString())
1473         parameters->setString("pageLoadStrategy"_s, pageLoadStrategy.value());
1474     m_host->sendCommandToBackend("waitForNavigationToComplete"_s, WTFMove(parameters), [this, protectedThis = makeRef(*this), completionHandler = WTFMove(completionHandler)](SessionHost::CommandResponse&& response) {
1475         if (response.isError) {
1476             auto result = CommandResult::fail(WTFMove(response.responseObject));
1477             switch (result.errorCode()) {
1478             case CommandResult::ErrorCode::NoSuchWindow:
1479                 // Window was closed, reset the top level browsing context and ignore the error.
1480                 m_toplevelBrowsingContext = std::nullopt;
1481                 break;
1482             case CommandResult::ErrorCode::NoSuchFrame:
1483                 // Navigation destroyed the current frame, switch to top level browsing context and ignore the error.
1484                 switchToBrowsingContext(std::nullopt);
1485                 break;
1486             default:
1487                 completionHandler(WTFMove(result));
1488                 return;
1489             }
1490         }
1491         completionHandler(CommandResult::success());
1492     });
1493 }
1494
1495 void Session::selectOptionElement(const String& elementID, Function<void (CommandResult&&)>&& completionHandler)
1496 {
1497     RefPtr<JSON::Object> parameters = JSON::Object::create();
1498     parameters->setString("browsingContextHandle"_s, m_toplevelBrowsingContext.value());
1499     parameters->setString("frameHandle"_s, m_currentBrowsingContext.value_or(emptyString()));
1500     parameters->setString("nodeHandle"_s, elementID);
1501     m_host->sendCommandToBackend("selectOptionElement"_s, WTFMove(parameters), [this, protectedThis = makeRef(*this), completionHandler = WTFMove(completionHandler)](SessionHost::CommandResponse&& response) {
1502         if (response.isError) {
1503             completionHandler(CommandResult::fail(WTFMove(response.responseObject)));
1504             return;
1505         }
1506         completionHandler(CommandResult::success());
1507     });
1508 }
1509
1510 void Session::elementClick(const String& elementID, Function<void (CommandResult&&)>&& completionHandler)
1511 {
1512     if (!m_toplevelBrowsingContext) {
1513         completionHandler(CommandResult::fail(CommandResult::ErrorCode::NoSuchWindow));
1514         return;
1515     }
1516
1517     OptionSet<ElementLayoutOption> options = ElementLayoutOption::ScrollIntoViewIfNeeded;
1518     options |= ElementLayoutOption::UseViewportCoordinates;
1519     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 {
1520         if (!rect || error) {
1521             completionHandler(CommandResult::fail(WTFMove(error)));
1522             return;
1523         }
1524         if (isObscured) {
1525             completionHandler(CommandResult::fail(CommandResult::ErrorCode::ElementClickIntercepted));
1526             return;
1527         }
1528         if (!inViewCenter) {
1529             completionHandler(CommandResult::fail(CommandResult::ErrorCode::ElementNotInteractable));
1530             return;
1531         }
1532
1533         getElementTagName(elementID, [this, elementID, inViewCenter = WTFMove(inViewCenter), isObscured, completionHandler = WTFMove(completionHandler)](CommandResult&& result) mutable {
1534             bool isOptionElement = false;
1535             if (!result.isError()) {
1536                 String tagName;
1537                 if (result.result()->asString(tagName))
1538                     isOptionElement = tagName == "option";
1539             }
1540
1541             Function<void (CommandResult&&)> continueAfterClickFunction = [this, completionHandler = WTFMove(completionHandler)](CommandResult&& result) mutable {
1542                 if (result.isError()) {
1543                     completionHandler(WTFMove(result));
1544                     return;
1545                 }
1546
1547                 waitForNavigationToComplete(WTFMove(completionHandler));
1548             };
1549             if (isOptionElement)
1550                 selectOptionElement(elementID, WTFMove(continueAfterClickFunction));
1551             else
1552                 performMouseInteraction(inViewCenter.value().x, inViewCenter.value().y, MouseButton::Left, MouseInteraction::SingleClick, WTFMove(continueAfterClickFunction));
1553         });
1554     });
1555 }
1556
1557 void Session::elementClear(const String& elementID, Function<void (CommandResult&&)>&& completionHandler)
1558 {
1559     if (!m_toplevelBrowsingContext) {
1560         completionHandler(CommandResult::fail(CommandResult::ErrorCode::NoSuchWindow));
1561         return;
1562     }
1563
1564     RefPtr<JSON::Array> arguments = JSON::Array::create();
1565     arguments->pushString(createElement(elementID)->toJSONString());
1566
1567     RefPtr<JSON::Object> parameters = JSON::Object::create();
1568     parameters->setString("browsingContextHandle"_s, m_toplevelBrowsingContext.value());
1569     if (m_currentBrowsingContext)
1570         parameters->setString("frameHandle"_s, m_currentBrowsingContext.value());
1571     parameters->setString("function"_s, FormElementClearJavaScript);
1572     parameters->setArray("arguments"_s, WTFMove(arguments));
1573     m_host->sendCommandToBackend("evaluateJavaScriptFunction"_s, WTFMove(parameters), [this, protectedThis = makeRef(*this), completionHandler = WTFMove(completionHandler)](SessionHost::CommandResponse&& response) {
1574         if (response.isError) {
1575             completionHandler(CommandResult::fail(WTFMove(response.responseObject)));
1576             return;
1577         }
1578         completionHandler(CommandResult::success());
1579     });
1580 }
1581
1582 String Session::virtualKeyForKeySequence(const String& keySequence, KeyModifier& modifier)
1583 {
1584     // ยง17.4.2 Keyboard Actions.
1585     // https://www.w3.org/TR/webdriver/#keyboard-actions
1586     modifier = KeyModifier::None;
1587     switch (keySequence[0]) {
1588     case 0xE001U:
1589         return "Cancel"_s;
1590     case 0xE002U:
1591         return "Help"_s;
1592     case 0xE003U:
1593         return "Backspace"_s;
1594     case 0xE004U:
1595         return "Tab"_s;
1596     case 0xE005U:
1597         return "Clear"_s;
1598     case 0xE006U:
1599         return "Return"_s;
1600     case 0xE007U:
1601         return "Enter"_s;
1602     case 0xE008U:
1603     case 0xE050U:
1604         modifier = KeyModifier::Shift;
1605         return "Shift"_s;
1606     case 0xE009U:
1607     case 0xE051U:
1608         modifier = KeyModifier::Control;
1609         return "Control"_s;
1610     case 0xE00AU:
1611     case 0xE052U:
1612         modifier = KeyModifier::Alternate;
1613         return "Alternate"_s;
1614     case 0xE00BU:
1615         return "Pause"_s;
1616     case 0xE00CU:
1617         return "Escape"_s;
1618     case 0xE00DU:
1619         return "Space"_s;
1620     case 0xE00EU:
1621     case 0xE054U:
1622         return "PageUp"_s;
1623     case 0xE00FU:
1624     case 0xE055U:
1625         return "PageDown"_s;
1626     case 0xE010U:
1627     case 0xE056U:
1628         return "End"_s;
1629     case 0xE011U:
1630     case 0xE057U:
1631         return "Home"_s;
1632     case 0xE012U:
1633     case 0xE058U:
1634         return "LeftArrow"_s;
1635     case 0xE013U:
1636     case 0xE059U:
1637         return "UpArrow"_s;
1638     case 0xE014U:
1639     case 0xE05AU:
1640         return "RightArrow"_s;
1641     case 0xE015U:
1642     case 0xE05BU:
1643         return "DownArrow"_s;
1644     case 0xE016U:
1645     case 0xE05CU:
1646         return "Insert"_s;
1647     case 0xE017U:
1648     case 0xE05DU:
1649         return "Delete"_s;
1650     case 0xE018U:
1651         return "Semicolon"_s;
1652     case 0xE019U:
1653         return "Equals"_s;
1654     case 0xE01AU:
1655         return "NumberPad0"_s;
1656     case 0xE01BU:
1657         return "NumberPad1"_s;
1658     case 0xE01CU:
1659         return "NumberPad2"_s;
1660     case 0xE01DU:
1661         return "NumberPad3"_s;
1662     case 0xE01EU:
1663         return "NumberPad4"_s;
1664     case 0xE01FU:
1665         return "NumberPad5"_s;
1666     case 0xE020U:
1667         return "NumberPad6"_s;
1668     case 0xE021U:
1669         return "NumberPad7"_s;
1670     case 0xE022U:
1671         return "NumberPad8"_s;
1672     case 0xE023U:
1673         return "NumberPad9"_s;
1674     case 0xE024U:
1675         return "NumberPadMultiply"_s;
1676     case 0xE025U:
1677         return "NumberPadAdd"_s;
1678     case 0xE026U:
1679         return "NumberPadSeparator"_s;
1680     case 0xE027U:
1681         return "NumberPadSubtract"_s;
1682     case 0xE028U:
1683         return "NumberPadDecimal"_s;
1684     case 0xE029U:
1685         return "NumberPadDivide"_s;
1686     case 0xE031U:
1687         return "Function1"_s;
1688     case 0xE032U:
1689         return "Function2"_s;
1690     case 0xE033U:
1691         return "Function3"_s;
1692     case 0xE034U:
1693         return "Function4"_s;
1694     case 0xE035U:
1695         return "Function5"_s;
1696     case 0xE036U:
1697         return "Function6"_s;
1698     case 0xE037U:
1699         return "Function7"_s;
1700     case 0xE038U:
1701         return "Function8"_s;
1702     case 0xE039U:
1703         return "Function9"_s;
1704     case 0xE03AU:
1705         return "Function10"_s;
1706     case 0xE03BU:
1707         return "Function11"_s;
1708     case 0xE03CU:
1709         return "Function12"_s;
1710     case 0xE03DU:
1711     case 0xE053U:
1712         modifier = KeyModifier::Meta;
1713         return "Meta"_s;
1714     default:
1715         break;
1716     }
1717     return String();
1718 }
1719
1720 void Session::elementSendKeys(const String& elementID, Vector<String>&& keys, Function<void (CommandResult&&)>&& completionHandler)
1721 {
1722     if (!m_toplevelBrowsingContext) {
1723         completionHandler(CommandResult::fail(CommandResult::ErrorCode::NoSuchWindow));
1724         return;
1725     }
1726
1727     handleUserPrompts([this, elementID, keys = WTFMove(keys), completionHandler = WTFMove(completionHandler)](CommandResult&& result) mutable {
1728         if (result.isError()) {
1729             completionHandler(WTFMove(result));
1730             return;
1731         }
1732         // FIXME: move this to an atom.
1733         static const char focusScript[] =
1734             "function focus(element) {"
1735             "    var doc = element.ownerDocument || element;"
1736             "    var prevActiveElement = doc.activeElement;"
1737             "    if (element != prevActiveElement && prevActiveElement)"
1738             "        prevActiveElement.blur();"
1739             "    element.focus();"
1740             "    if (element != prevActiveElement && element.value && element.value.length && element.setSelectionRange)"
1741             "        element.setSelectionRange(element.value.length, element.value.length);"
1742             "    if (element != doc.activeElement)"
1743             "        throw new Error('cannot focus element');"
1744             "}";
1745
1746         RefPtr<JSON::Array> arguments = JSON::Array::create();
1747         arguments->pushString(createElement(elementID)->toJSONString());
1748         RefPtr<JSON::Object> parameters = JSON::Object::create();
1749         parameters->setString("browsingContextHandle"_s, m_toplevelBrowsingContext.value());
1750         if (m_currentBrowsingContext)
1751             parameters->setString("frameHandle"_s, m_currentBrowsingContext.value());
1752         parameters->setString("function"_s, focusScript);
1753         parameters->setArray("arguments"_s, WTFMove(arguments));
1754         m_host->sendCommandToBackend("evaluateJavaScriptFunction"_s, WTFMove(parameters), [this, protectedThis = makeRef(*this), keys = WTFMove(keys), completionHandler = WTFMove(completionHandler)](SessionHost::CommandResponse&& response) mutable {
1755             if (response.isError || !response.responseObject) {
1756                 completionHandler(CommandResult::fail(WTFMove(response.responseObject)));
1757                 return;
1758             }
1759
1760             unsigned stickyModifiers = 0;
1761             Vector<KeyboardInteraction> interactions;
1762             interactions.reserveInitialCapacity(keys.size());
1763             for (const auto& key : keys) {
1764                 KeyboardInteraction interaction;
1765                 KeyModifier modifier;
1766                 auto virtualKey = virtualKeyForKeySequence(key, modifier);
1767                 if (!virtualKey.isNull()) {
1768                     interaction.key = virtualKey;
1769                     if (modifier != KeyModifier::None) {
1770                         stickyModifiers ^= modifier;
1771                         if (stickyModifiers & modifier)
1772                             interaction.type = KeyboardInteractionType::KeyPress;
1773                         else
1774                             interaction.type = KeyboardInteractionType::KeyRelease;
1775                     }
1776                 } else
1777                     interaction.text = key;
1778                 interactions.uncheckedAppend(WTFMove(interaction));
1779             }
1780
1781             // Reset sticky modifiers if needed.
1782             if (stickyModifiers) {
1783                 if (stickyModifiers & KeyModifier::Shift)
1784                     interactions.append({ KeyboardInteractionType::KeyRelease, std::nullopt, std::optional<String>("Shift"_s) });
1785                 if (stickyModifiers & KeyModifier::Control)
1786                     interactions.append({ KeyboardInteractionType::KeyRelease, std::nullopt, std::optional<String>("Control"_s) });
1787                 if (stickyModifiers & KeyModifier::Alternate)
1788                     interactions.append({ KeyboardInteractionType::KeyRelease, std::nullopt, std::optional<String>("Alternate"_s) });
1789                 if (stickyModifiers & KeyModifier::Meta)
1790                     interactions.append({ KeyboardInteractionType::KeyRelease, std::nullopt, std::optional<String>("Meta"_s) });
1791             }
1792
1793             performKeyboardInteractions(WTFMove(interactions), WTFMove(completionHandler));
1794         });
1795     });
1796 }
1797
1798 RefPtr<JSON::Value> Session::handleScriptResult(RefPtr<JSON::Value>&& resultValue)
1799 {
1800     RefPtr<JSON::Array> resultArray;
1801     if (resultValue->asArray(resultArray)) {
1802         RefPtr<JSON::Array> returnValueArray = JSON::Array::create();
1803         unsigned resultArrayLength = resultArray->length();
1804         for (unsigned i = 0; i < resultArrayLength; ++i)
1805             returnValueArray->pushValue(handleScriptResult(resultArray->get(i)));
1806         return returnValueArray;
1807     }
1808
1809     if (auto element = createElement(RefPtr<JSON::Value>(resultValue)))
1810         return element;
1811
1812     RefPtr<JSON::Object> resultObject;
1813     if (resultValue->asObject(resultObject)) {
1814         RefPtr<JSON::Object> returnValueObject = JSON::Object::create();
1815         auto end = resultObject->end();
1816         for (auto it = resultObject->begin(); it != end; ++it)
1817             returnValueObject->setValue(it->key, handleScriptResult(WTFMove(it->value)));
1818         return returnValueObject;
1819     }
1820
1821     return resultValue;
1822 }
1823
1824 void Session::executeScript(const String& script, RefPtr<JSON::Array>&& argumentsArray, ExecuteScriptMode mode, Function<void (CommandResult&&)>&& completionHandler)
1825 {
1826     if (!m_toplevelBrowsingContext) {
1827         completionHandler(CommandResult::fail(CommandResult::ErrorCode::NoSuchWindow));
1828         return;
1829     }
1830
1831     handleUserPrompts([this, script, argumentsArray = WTFMove(argumentsArray), mode, completionHandler = WTFMove(completionHandler)](CommandResult&& result) mutable {
1832         if (result.isError()) {
1833             completionHandler(WTFMove(result));
1834             return;
1835         }
1836         RefPtr<JSON::Array> arguments = JSON::Array::create();
1837         unsigned argumentsLength = argumentsArray->length();
1838         for (unsigned i = 0; i < argumentsLength; ++i) {
1839             if (auto argument = argumentsArray->get(i)) {
1840                 if (auto element = extractElement(*argument))
1841                     arguments->pushString(element->toJSONString());
1842                 else
1843                     arguments->pushString(argument->toJSONString());
1844             }
1845         }
1846
1847         RefPtr<JSON::Object> parameters = JSON::Object::create();
1848         parameters->setString("browsingContextHandle"_s, m_toplevelBrowsingContext.value());
1849         if (m_currentBrowsingContext)
1850             parameters->setString("frameHandle"_s, m_currentBrowsingContext.value());
1851         parameters->setString("function"_s, "function(){" + script + '}');
1852         parameters->setArray("arguments"_s, WTFMove(arguments));
1853         if (mode == ExecuteScriptMode::Async) {
1854             parameters->setBoolean("expectsImplicitCallbackArgument"_s, true);
1855             if (m_scriptTimeout)
1856                 parameters->setInteger("callbackTimeout"_s, m_scriptTimeout.millisecondsAs<int>());
1857         }
1858         m_host->sendCommandToBackend("evaluateJavaScriptFunction"_s, WTFMove(parameters), [this, protectedThis = makeRef(*this), completionHandler = WTFMove(completionHandler)](SessionHost::CommandResponse&& response) mutable {
1859             if (response.isError || !response.responseObject) {
1860                 auto result = CommandResult::fail(WTFMove(response.responseObject));
1861                 if (result.errorCode() == CommandResult::ErrorCode::UnexpectedAlertOpen)
1862                     handleUnexpectedAlertOpen(WTFMove(completionHandler));
1863                 else
1864                     completionHandler(WTFMove(result));
1865                 return;
1866             }
1867             String valueString;
1868             if (!response.responseObject->getString("result"_s, valueString)) {
1869                 completionHandler(CommandResult::fail(CommandResult::ErrorCode::UnknownError));
1870                 return;
1871             }
1872             RefPtr<JSON::Value> resultValue;
1873             if (!JSON::Value::parseJSON(valueString, resultValue)) {
1874                 completionHandler(CommandResult::fail(CommandResult::ErrorCode::UnknownError));
1875                 return;
1876             }
1877             completionHandler(CommandResult::success(handleScriptResult(WTFMove(resultValue))));
1878         });
1879     });
1880 }
1881
1882 static String mouseButtonForAutomation(MouseButton button)
1883 {
1884     switch (button) {
1885     case MouseButton::None:
1886         return "None"_s;
1887     case MouseButton::Left:
1888         return "Left"_s;
1889     case MouseButton::Middle:
1890         return "Middle"_s;
1891     case MouseButton::Right:
1892         return "Right"_s;
1893     }
1894
1895     RELEASE_ASSERT_NOT_REACHED();
1896 }
1897
1898 void Session::performMouseInteraction(int x, int y, MouseButton button, MouseInteraction interaction, Function<void (CommandResult&&)>&& completionHandler)
1899 {
1900     RefPtr<JSON::Object> parameters = JSON::Object::create();
1901     parameters->setString("handle"_s, m_toplevelBrowsingContext.value());
1902     RefPtr<JSON::Object> position = JSON::Object::create();
1903     position->setInteger("x"_s, x);
1904     position->setInteger("y"_s, y);
1905     parameters->setObject("position"_s, WTFMove(position));
1906     parameters->setString("button"_s, mouseButtonForAutomation(button));
1907     switch (interaction) {
1908     case MouseInteraction::Move:
1909         parameters->setString("interaction"_s, "Move"_s);
1910         break;
1911     case MouseInteraction::Down:
1912         parameters->setString("interaction"_s, "Down"_s);
1913         break;
1914     case MouseInteraction::Up:
1915         parameters->setString("interaction"_s, "Up"_s);
1916         break;
1917     case MouseInteraction::SingleClick:
1918         parameters->setString("interaction"_s, "SingleClick"_s);
1919         break;
1920     case MouseInteraction::DoubleClick:
1921         parameters->setString("interaction"_s, "DoubleClick"_s);
1922         break;
1923     }
1924     parameters->setArray("modifiers"_s, JSON::Array::create());
1925     m_host->sendCommandToBackend("performMouseInteraction"_s, WTFMove(parameters), [this, protectedThis = makeRef(*this), completionHandler = WTFMove(completionHandler)](SessionHost::CommandResponse&& response) {
1926         if (response.isError) {
1927             completionHandler(CommandResult::fail(WTFMove(response.responseObject)));
1928             return;
1929         }
1930         completionHandler(CommandResult::success());
1931     });
1932 }
1933
1934 void Session::performKeyboardInteractions(Vector<KeyboardInteraction>&& interactions, Function<void (CommandResult&&)>&& completionHandler)
1935 {
1936     RefPtr<JSON::Object> parameters = JSON::Object::create();
1937     parameters->setString("handle"_s, m_toplevelBrowsingContext.value());
1938     RefPtr<JSON::Array> interactionsArray = JSON::Array::create();
1939     for (const auto& interaction : interactions) {
1940         RefPtr<JSON::Object> interactionObject = JSON::Object::create();
1941         switch (interaction.type) {
1942         case KeyboardInteractionType::KeyPress:
1943             interactionObject->setString("type"_s, "KeyPress"_s);
1944             break;
1945         case KeyboardInteractionType::KeyRelease:
1946             interactionObject->setString("type"_s, "KeyRelease"_s);
1947             break;
1948         case KeyboardInteractionType::InsertByKey:
1949             interactionObject->setString("type"_s, "InsertByKey"_s);
1950             break;
1951         }
1952         if (interaction.key)
1953             interactionObject->setString("key"_s, interaction.key.value());
1954         if (interaction.text)
1955             interactionObject->setString("text"_s, interaction.text.value());
1956         interactionsArray->pushObject(WTFMove(interactionObject));
1957     }
1958     parameters->setArray("interactions"_s, WTFMove(interactionsArray));
1959     m_host->sendCommandToBackend("performKeyboardInteractions"_s, WTFMove(parameters), [this, protectedThis = makeRef(*this), completionHandler = WTFMove(completionHandler)](SessionHost::CommandResponse&& response) {
1960         if (response.isError) {
1961             completionHandler(CommandResult::fail(WTFMove(response.responseObject)));
1962             return;
1963         }
1964         completionHandler(CommandResult::success());
1965     });
1966 }
1967
1968 static std::optional<Session::Cookie> parseAutomationCookie(const JSON::Object& cookieObject)
1969 {
1970     Session::Cookie cookie;
1971     if (!cookieObject.getString("name"_s, cookie.name))
1972         return std::nullopt;
1973     if (!cookieObject.getString("value"_s, cookie.value))
1974         return std::nullopt;
1975
1976     String path;
1977     if (cookieObject.getString("path"_s, path))
1978         cookie.path = path;
1979     String domain;
1980     if (cookieObject.getString("domain"_s, domain))
1981         cookie.domain = domain;
1982     bool secure;
1983     if (cookieObject.getBoolean("secure"_s, secure))
1984         cookie.secure = secure;
1985     bool httpOnly;
1986     if (cookieObject.getBoolean("httpOnly"_s, httpOnly))
1987         cookie.httpOnly = httpOnly;
1988     bool session = false;
1989     cookieObject.getBoolean("session"_s, session);
1990     if (!session) {
1991         double expiry;
1992         if (cookieObject.getDouble("expires"_s, expiry))
1993             cookie.expiry = expiry;
1994     }
1995
1996     return cookie;
1997 }
1998
1999 static RefPtr<JSON::Object> builtAutomationCookie(const Session::Cookie& cookie)
2000 {
2001     RefPtr<JSON::Object> cookieObject = JSON::Object::create();
2002     cookieObject->setString("name"_s, cookie.name);
2003     cookieObject->setString("value"_s, cookie.value);
2004     cookieObject->setString("path"_s, cookie.path.value_or("/"));
2005     cookieObject->setString("domain"_s, cookie.domain.value_or(emptyString()));
2006     cookieObject->setBoolean("secure"_s, cookie.secure.value_or(false));
2007     cookieObject->setBoolean("httpOnly"_s, cookie.httpOnly.value_or(false));
2008     cookieObject->setBoolean("session"_s, !cookie.expiry);
2009     cookieObject->setDouble("expires"_s, cookie.expiry.value_or(0));
2010     return cookieObject;
2011 }
2012
2013 static RefPtr<JSON::Object> serializeCookie(const Session::Cookie& cookie)
2014 {
2015     RefPtr<JSON::Object> cookieObject = JSON::Object::create();
2016     cookieObject->setString("name"_s, cookie.name);
2017     cookieObject->setString("value"_s, cookie.value);
2018     if (cookie.path)
2019         cookieObject->setString("path"_s, cookie.path.value());
2020     if (cookie.domain)
2021         cookieObject->setString("domain"_s, cookie.domain.value());
2022     if (cookie.secure)
2023         cookieObject->setBoolean("secure"_s, cookie.secure.value());
2024     if (cookie.httpOnly)
2025         cookieObject->setBoolean("httpOnly"_s, cookie.httpOnly.value());
2026     if (cookie.expiry)
2027         cookieObject->setInteger("expiry"_s, cookie.expiry.value());
2028     return cookieObject;
2029 }
2030
2031 void Session::getAllCookies(Function<void (CommandResult&&)>&& completionHandler)
2032 {
2033     if (!m_toplevelBrowsingContext) {
2034         completionHandler(CommandResult::fail(CommandResult::ErrorCode::NoSuchWindow));
2035         return;
2036     }
2037
2038     handleUserPrompts([this, completionHandler = WTFMove(completionHandler)](CommandResult&& result) mutable {
2039         if (result.isError()) {
2040             completionHandler(WTFMove(result));
2041             return;
2042         }
2043
2044         RefPtr<JSON::Object> parameters = JSON::Object::create();
2045         parameters->setString("browsingContextHandle"_s, m_toplevelBrowsingContext.value());
2046         m_host->sendCommandToBackend("getAllCookies"_s, WTFMove(parameters), [this, protectedThis = makeRef(*this), completionHandler = WTFMove(completionHandler)](SessionHost::CommandResponse&& response) mutable {
2047             if (response.isError || !response.responseObject) {
2048                 completionHandler(CommandResult::fail(WTFMove(response.responseObject)));
2049                 return;
2050             }
2051             RefPtr<JSON::Array> cookiesArray;
2052             if (!response.responseObject->getArray("cookies"_s, cookiesArray)) {
2053                 completionHandler(CommandResult::fail(CommandResult::ErrorCode::UnknownError));
2054                 return;
2055             }
2056             RefPtr<JSON::Array> cookies = JSON::Array::create();
2057             for (unsigned i = 0; i < cookiesArray->length(); ++i) {
2058                 RefPtr<JSON::Value> cookieValue = cookiesArray->get(i);
2059                 RefPtr<JSON::Object> cookieObject;
2060                 if (!cookieValue->asObject(cookieObject)) {
2061                     completionHandler(CommandResult::fail(CommandResult::ErrorCode::UnknownError));
2062                     return;
2063                 }
2064
2065                 auto cookie = parseAutomationCookie(*cookieObject);
2066                 if (!cookie) {
2067                     completionHandler(CommandResult::fail(CommandResult::ErrorCode::UnknownError));
2068                     return;
2069                 }
2070                 cookies->pushObject(serializeCookie(cookie.value()));
2071             }
2072             completionHandler(CommandResult::success(WTFMove(cookies)));
2073         });
2074     });
2075 }
2076
2077 void Session::getNamedCookie(const String& name, Function<void (CommandResult&&)>&& completionHandler)
2078 {
2079     getAllCookies([this, name, completionHandler = WTFMove(completionHandler)](CommandResult&& result) mutable {
2080         if (result.isError()) {
2081             completionHandler(WTFMove(result));
2082             return;
2083         }
2084         RefPtr<JSON::Array> cookiesArray;
2085         result.result()->asArray(cookiesArray);
2086         for (unsigned i = 0; i < cookiesArray->length(); ++i) {
2087             RefPtr<JSON::Value> cookieValue = cookiesArray->get(i);
2088             RefPtr<JSON::Object> cookieObject;
2089             cookieValue->asObject(cookieObject);
2090             String cookieName;
2091             cookieObject->getString("name"_s, cookieName);
2092             if (cookieName == name) {
2093                 completionHandler(CommandResult::success(WTFMove(cookieObject)));
2094                 return;
2095             }
2096         }
2097         completionHandler(CommandResult::fail(CommandResult::ErrorCode::NoSuchCookie));
2098     });
2099 }
2100
2101 void Session::addCookie(const Cookie& cookie, Function<void (CommandResult&&)>&& completionHandler)
2102 {
2103     if (!m_toplevelBrowsingContext) {
2104         completionHandler(CommandResult::fail(CommandResult::ErrorCode::NoSuchWindow));
2105         return;
2106     }
2107
2108     handleUserPrompts([this, cookie = builtAutomationCookie(cookie), completionHandler = WTFMove(completionHandler)](CommandResult&& result) mutable {
2109         if (result.isError()) {
2110             completionHandler(WTFMove(result));
2111             return;
2112         }
2113         RefPtr<JSON::Object> parameters = JSON::Object::create();
2114         parameters->setString("browsingContextHandle"_s, m_toplevelBrowsingContext.value());
2115         parameters->setObject("cookie"_s, WTFMove(cookie));
2116         m_host->sendCommandToBackend("addSingleCookie"_s, WTFMove(parameters), [this, protectedThis = makeRef(*this), completionHandler = WTFMove(completionHandler)](SessionHost::CommandResponse&& response) {
2117             if (response.isError) {
2118                 completionHandler(CommandResult::fail(WTFMove(response.responseObject)));
2119                 return;
2120             }
2121             completionHandler(CommandResult::success());
2122         });
2123     });
2124 }
2125
2126 void Session::deleteCookie(const String& name, Function<void (CommandResult&&)>&& completionHandler)
2127 {
2128     if (!m_toplevelBrowsingContext) {
2129         completionHandler(CommandResult::fail(CommandResult::ErrorCode::NoSuchWindow));
2130         return;
2131     }
2132
2133     handleUserPrompts([this, name, completionHandler = WTFMove(completionHandler)](CommandResult&& result) mutable {
2134         if (result.isError()) {
2135             completionHandler(WTFMove(result));
2136             return;
2137         }
2138         RefPtr<JSON::Object> parameters = JSON::Object::create();
2139         parameters->setString("browsingContextHandle"_s, m_toplevelBrowsingContext.value());
2140         parameters->setString("cookieName"_s, name);
2141         m_host->sendCommandToBackend("deleteSingleCookie"_s, WTFMove(parameters), [this, protectedThis = makeRef(*this), completionHandler = WTFMove(completionHandler)](SessionHost::CommandResponse&& response) {
2142             if (response.isError) {
2143                 completionHandler(CommandResult::fail(WTFMove(response.responseObject)));
2144                 return;
2145             }
2146             completionHandler(CommandResult::success());
2147         });
2148     });
2149 }
2150
2151 void Session::deleteAllCookies(Function<void (CommandResult&&)>&& completionHandler)
2152 {
2153     if (!m_toplevelBrowsingContext) {
2154         completionHandler(CommandResult::fail(CommandResult::ErrorCode::NoSuchWindow));
2155         return;
2156     }
2157
2158     handleUserPrompts([this, completionHandler = WTFMove(completionHandler)](CommandResult&& result) mutable {
2159         if (result.isError()) {
2160             completionHandler(WTFMove(result));
2161             return;
2162         }
2163         RefPtr<JSON::Object> parameters = JSON::Object::create();
2164         parameters->setString("browsingContextHandle"_s, m_toplevelBrowsingContext.value());
2165         m_host->sendCommandToBackend("deleteAllCookies"_s, WTFMove(parameters), [this, protectedThis = makeRef(*this), completionHandler = WTFMove(completionHandler)](SessionHost::CommandResponse&& response) {
2166             if (response.isError) {
2167                 completionHandler(CommandResult::fail(WTFMove(response.responseObject)));
2168                 return;
2169             }
2170             completionHandler(CommandResult::success());
2171         });
2172     });
2173 }
2174
2175 InputSource& Session::getOrCreateInputSource(const String& id, InputSource::Type type, std::optional<PointerType> pointerType)
2176 {
2177     auto addResult = m_activeInputSources.add(id, InputSource());
2178     if (addResult.isNewEntry)
2179         addResult.iterator->value = { type, pointerType };
2180     return addResult.iterator->value;
2181 }
2182
2183 Session::InputSourceState& Session::inputSourceState(const String& id)
2184 {
2185     return m_inputStateTable.ensure(id, [] { return InputSourceState(); }).iterator->value;
2186 }
2187
2188 static const char* automationSourceType(InputSource::Type type)
2189 {
2190     switch (type) {
2191     case InputSource::Type::None:
2192         return "Null";
2193     case InputSource::Type::Pointer:
2194         return "Mouse";
2195     case InputSource::Type::Key:
2196         return "Keyboard";
2197     }
2198     RELEASE_ASSERT_NOT_REACHED();
2199 }
2200
2201 static const char* automationOriginType(PointerOrigin::Type type)
2202 {
2203     switch (type) {
2204     case PointerOrigin::Type::Viewport:
2205         return "Viewport";
2206     case PointerOrigin::Type::Pointer:
2207         return "Pointer";
2208     case PointerOrigin::Type::Element:
2209         return "Element";
2210     }
2211     RELEASE_ASSERT_NOT_REACHED();
2212 }
2213
2214 void Session::performActions(Vector<Vector<Action>>&& actionsByTick, Function<void (CommandResult&&)>&& completionHandler)
2215 {
2216     if (!m_toplevelBrowsingContext) {
2217         completionHandler(CommandResult::fail(CommandResult::ErrorCode::NoSuchWindow));
2218         return;
2219     }
2220
2221     handleUserPrompts([this, actionsByTick = WTFMove(actionsByTick), completionHandler = WTFMove(completionHandler)](CommandResult&& result) mutable {
2222         if (result.isError()) {
2223             completionHandler(WTFMove(result));
2224             return;
2225         }
2226
2227         // First check if we have actions and whether we need to resolve any pointer move element origin.
2228         unsigned actionsCount = 0;
2229         for (const auto& tick : actionsByTick)
2230             actionsCount += tick.size();
2231         if (!actionsCount) {
2232             completionHandler(CommandResult::success());
2233             return;
2234         }
2235
2236         RefPtr<JSON::Object> parameters = JSON::Object::create();
2237         parameters->setString("handle"_s, m_toplevelBrowsingContext.value());
2238         if (m_currentBrowsingContext)
2239             parameters->setString("frameHandle"_s, m_currentBrowsingContext.value());
2240         RefPtr<JSON::Array> inputSources = JSON::Array::create();
2241         for (const auto& inputSource : m_activeInputSources) {
2242             RefPtr<JSON::Object> inputSourceObject = JSON::Object::create();
2243             inputSourceObject->setString("sourceId"_s, inputSource.key);
2244             inputSourceObject->setString("sourceType"_s, automationSourceType(inputSource.value.type));
2245             inputSources->pushObject(WTFMove(inputSourceObject));
2246         }
2247         parameters->setArray("inputSources"_s, WTFMove(inputSources));
2248         RefPtr<JSON::Array> steps = JSON::Array::create();
2249         for (const auto& tick : actionsByTick) {
2250             RefPtr<JSON::Array> states = JSON::Array::create();
2251             for (const auto& action : tick) {
2252                 RefPtr<JSON::Object> state = JSON::Object::create();
2253                 auto& currentState = inputSourceState(action.id);
2254                 state->setString("sourceId"_s, action.id);
2255                 switch (action.type) {
2256                 case Action::Type::None:
2257                     state->setDouble("duration"_s, action.duration.value());
2258                     break;
2259                 case Action::Type::Pointer: {
2260                     switch (action.subtype) {
2261                     case Action::Subtype::PointerUp:
2262                         currentState.pressedButton = std::nullopt;
2263                         break;
2264                     case Action::Subtype::PointerDown:
2265                         currentState.pressedButton = action.button.value();
2266                         break;
2267                     case Action::Subtype::PointerMove: {
2268                         state->setString("origin"_s, automationOriginType(action.origin->type));
2269                         RefPtr<JSON::Object> location = JSON::Object::create();
2270                         location->setInteger("x"_s, action.x.value());
2271                         location->setInteger("y"_s, action.y.value());
2272                         state->setObject("location"_s, WTFMove(location));
2273                         if (action.origin->type == PointerOrigin::Type::Element)
2274                             state->setString("nodeHandle"_s, action.origin->elementID.value());
2275                         FALLTHROUGH;
2276                     }
2277                     case Action::Subtype::Pause:
2278                         if (action.duration)
2279                             state->setDouble("duration"_s, action.duration.value());
2280                         break;
2281                     case Action::Subtype::PointerCancel:
2282                         currentState.pressedButton = std::nullopt;
2283                         break;
2284                     case Action::Subtype::KeyUp:
2285                     case Action::Subtype::KeyDown:
2286                         ASSERT_NOT_REACHED();
2287                     }
2288                     if (currentState.pressedButton)
2289                         state->setString("pressedButton"_s, mouseButtonForAutomation(currentState.pressedButton.value()));
2290                     break;
2291                 }
2292                 case Action::Type::Key:
2293                     switch (action.subtype) {
2294                     case Action::Subtype::KeyUp:
2295                         if (currentState.pressedVirtualKey)
2296                             currentState.pressedVirtualKey = std::nullopt;
2297                         else
2298                             currentState.pressedKey = std::nullopt;
2299                         break;
2300                     case Action::Subtype::KeyDown: {
2301                         KeyModifier modifier;
2302                         auto virtualKey = virtualKeyForKeySequence(action.key.value(), modifier);
2303                         if (!virtualKey.isNull())
2304                             currentState.pressedVirtualKey = virtualKey;
2305                         else
2306                             currentState.pressedKey = action.key.value();
2307                         break;
2308                     }
2309                     case Action::Subtype::Pause:
2310                         if (action.duration)
2311                             state->setDouble("duration"_s, action.duration.value());
2312                         break;
2313                     case Action::Subtype::PointerUp:
2314                     case Action::Subtype::PointerDown:
2315                     case Action::Subtype::PointerMove:
2316                     case Action::Subtype::PointerCancel:
2317                         ASSERT_NOT_REACHED();
2318                     }
2319                     if (currentState.pressedKey)
2320                         state->setString("pressedCharKey"_s, currentState.pressedKey.value());
2321                     if (currentState.pressedVirtualKey) {
2322                         // FIXME: support parsing and tracking multiple virtual keys.
2323                         Ref<JSON::Array> virtualKeys = JSON::Array::create();
2324                         virtualKeys->pushString(currentState.pressedVirtualKey.value());
2325                         state->setArray("pressedVirtualKeys"_s, WTFMove(virtualKeys));
2326                     }
2327                     break;
2328                 }
2329                 states->pushObject(WTFMove(state));
2330             }
2331             RefPtr<JSON::Object> stepStates = JSON::Object::create();
2332             stepStates->setArray("states"_s, WTFMove(states));
2333             steps->pushObject(WTFMove(stepStates));
2334         }
2335
2336         parameters->setArray("steps"_s, WTFMove(steps));
2337         m_host->sendCommandToBackend("performInteractionSequence"_s, WTFMove(parameters), [this, protectedThis = makeRef(*this), completionHandler = WTFMove(completionHandler)] (SessionHost::CommandResponse&& response) {
2338             if (response.isError) {
2339                 completionHandler(CommandResult::fail(WTFMove(response.responseObject)));
2340                 return;
2341             }
2342             completionHandler(CommandResult::success());
2343         });
2344     });
2345 }
2346
2347 void Session::releaseActions(Function<void (CommandResult&&)>&& completionHandler)
2348 {
2349     if (!m_toplevelBrowsingContext) {
2350         completionHandler(CommandResult::fail(CommandResult::ErrorCode::NoSuchWindow));
2351         return;
2352     }
2353
2354     m_activeInputSources.clear();
2355     m_inputStateTable.clear();
2356
2357     RefPtr<JSON::Object> parameters = JSON::Object::create();
2358     parameters->setString("handle"_s, m_toplevelBrowsingContext.value());
2359     m_host->sendCommandToBackend("cancelInteractionSequence"_s, WTFMove(parameters), [this, protectedThis = makeRef(*this), completionHandler = WTFMove(completionHandler)](SessionHost::CommandResponse&& response) {
2360         if (response.isError) {
2361             completionHandler(CommandResult::fail(WTFMove(response.responseObject)));
2362             return;
2363         }
2364         completionHandler(CommandResult::success());
2365     });
2366 }
2367
2368 void Session::dismissAlert(Function<void (CommandResult&&)>&& completionHandler)
2369 {
2370     if (!m_toplevelBrowsingContext) {
2371         completionHandler(CommandResult::fail(CommandResult::ErrorCode::NoSuchWindow));
2372         return;
2373     }
2374
2375     RefPtr<JSON::Object> parameters = JSON::Object::create();
2376     parameters->setString("browsingContextHandle"_s, m_toplevelBrowsingContext.value());
2377     m_host->sendCommandToBackend("dismissCurrentJavaScriptDialog"_s, WTFMove(parameters), [this, protectedThis = makeRef(*this), completionHandler = WTFMove(completionHandler)](SessionHost::CommandResponse&& response) {
2378         if (response.isError) {
2379             completionHandler(CommandResult::fail(WTFMove(response.responseObject)));
2380             return;
2381         }
2382         completionHandler(CommandResult::success());
2383     });
2384 }
2385
2386 void Session::acceptAlert(Function<void (CommandResult&&)>&& completionHandler)
2387 {
2388     if (!m_toplevelBrowsingContext) {
2389         completionHandler(CommandResult::fail(CommandResult::ErrorCode::NoSuchWindow));
2390         return;
2391     }
2392
2393     RefPtr<JSON::Object> parameters = JSON::Object::create();
2394     parameters->setString("browsingContextHandle"_s, m_toplevelBrowsingContext.value());
2395     m_host->sendCommandToBackend("acceptCurrentJavaScriptDialog"_s, WTFMove(parameters), [this, protectedThis = makeRef(*this), completionHandler = WTFMove(completionHandler)](SessionHost::CommandResponse&& response) {
2396         if (response.isError) {
2397             completionHandler(CommandResult::fail(WTFMove(response.responseObject)));
2398             return;
2399         }
2400         completionHandler(CommandResult::success());
2401     });
2402 }
2403
2404 void Session::getAlertText(Function<void (CommandResult&&)>&& completionHandler)
2405 {
2406     if (!m_toplevelBrowsingContext) {
2407         completionHandler(CommandResult::fail(CommandResult::ErrorCode::NoSuchWindow));
2408         return;
2409     }
2410
2411     RefPtr<JSON::Object> parameters = JSON::Object::create();
2412     parameters->setString("browsingContextHandle"_s, m_toplevelBrowsingContext.value());
2413     m_host->sendCommandToBackend("messageOfCurrentJavaScriptDialog"_s, WTFMove(parameters), [this, protectedThis = makeRef(*this), completionHandler = WTFMove(completionHandler)](SessionHost::CommandResponse&& response) {
2414         if (response.isError || !response.responseObject) {
2415             completionHandler(CommandResult::fail(WTFMove(response.responseObject)));
2416             return;
2417         }
2418         String valueString;
2419         if (!response.responseObject->getString("message"_s, valueString)) {
2420             completionHandler(CommandResult::fail(CommandResult::ErrorCode::UnknownError));
2421             return;
2422         }
2423         completionHandler(CommandResult::success(JSON::Value::create(valueString)));
2424     });
2425 }
2426
2427 void Session::sendAlertText(const String& text, Function<void (CommandResult&&)>&& completionHandler)
2428 {
2429     if (!m_toplevelBrowsingContext) {
2430         completionHandler(CommandResult::fail(CommandResult::ErrorCode::NoSuchWindow));
2431         return;
2432     }
2433
2434     RefPtr<JSON::Object> parameters = JSON::Object::create();
2435     parameters->setString("browsingContextHandle"_s, m_toplevelBrowsingContext.value());
2436     parameters->setString("userInput"_s, text);
2437     m_host->sendCommandToBackend("setUserInputForCurrentJavaScriptPrompt"_s, WTFMove(parameters), [this, protectedThis = makeRef(*this), completionHandler = WTFMove(completionHandler)](SessionHost::CommandResponse&& response) {
2438         if (response.isError) {
2439             completionHandler(CommandResult::fail(WTFMove(response.responseObject)));
2440             return;
2441         }
2442         completionHandler(CommandResult::success());
2443     });
2444 }
2445
2446 void Session::takeScreenshot(std::optional<String> elementID, std::optional<bool> scrollIntoView, Function<void (CommandResult&&)>&& completionHandler)
2447 {
2448     if (!m_toplevelBrowsingContext) {
2449         completionHandler(CommandResult::fail(CommandResult::ErrorCode::NoSuchWindow));
2450         return;
2451     }
2452
2453     handleUserPrompts([this, elementID, scrollIntoView, completionHandler = WTFMove(completionHandler)](CommandResult&& result) mutable {
2454         if (result.isError()) {
2455             completionHandler(WTFMove(result));
2456             return;
2457         }
2458         RefPtr<JSON::Object> parameters = JSON::Object::create();
2459         parameters->setString("handle"_s, m_toplevelBrowsingContext.value());
2460         if (m_currentBrowsingContext)
2461             parameters->setString("frameHandle"_s, m_currentBrowsingContext.value());
2462         if (elementID)
2463             parameters->setString("nodeHandle"_s, elementID.value());
2464         else
2465             parameters->setBoolean("clipToViewport"_s, true);
2466         if (scrollIntoView.value_or(false))
2467             parameters->setBoolean("scrollIntoViewIfNeeded"_s, true);
2468         m_host->sendCommandToBackend("takeScreenshot"_s, WTFMove(parameters), [this, protectedThis = makeRef(*this), completionHandler = WTFMove(completionHandler)](SessionHost::CommandResponse&& response) mutable {
2469             if (response.isError || !response.responseObject) {
2470                 completionHandler(CommandResult::fail(WTFMove(response.responseObject)));
2471                 return;
2472             }
2473             String data;
2474             if (!response.responseObject->getString("data"_s, data)) {
2475                 completionHandler(CommandResult::fail(CommandResult::ErrorCode::UnknownError));
2476                 return;
2477             }
2478             completionHandler(CommandResult::success(JSON::Value::create(data)));
2479         });
2480     });
2481 }
2482
2483 } // namespace WebDriver