7583535beda5731282762b372d797cdceb424188
[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::virtualKeyForKey(UChar key, KeyModifier& modifier)
1583 {
1584     // ยง17.4.2 Keyboard Actions.
1585     // https://www.w3.org/TR/webdriver/#keyboard-actions
1586     modifier = KeyModifier::None;
1587     switch (key) {
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, const String& text, Function<void (CommandResult&&)>&& completionHandler)
1721 {
1722     if (!m_toplevelBrowsingContext) {
1723         completionHandler(CommandResult::fail(CommandResult::ErrorCode::NoSuchWindow));
1724         return;
1725     }
1726
1727     handleUserPrompts([this, elementID, text, 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             "    let doc = element.ownerDocument || element;"
1736             "    let prevActiveElement = doc.activeElement;"
1737             "    if (element != prevActiveElement && prevActiveElement)"
1738             "        prevActiveElement.blur();"
1739             "    element.focus();"
1740             "    let tagName = element.tagName.toUpperCase();"
1741             "    let isTextElement = tagName === 'TEXTAREA' || (tagName === 'INPUT' && element.type === 'text');"
1742             "    if (isTextElement && element.selectionEnd == 0)"
1743             "        element.setSelectionRange(element.value.length, element.value.length);"
1744             "    if (element != doc.activeElement)"
1745             "        throw new Error('cannot focus element');"
1746             "}";
1747
1748         RefPtr<JSON::Array> arguments = JSON::Array::create();
1749         arguments->pushString(createElement(elementID)->toJSONString());
1750         RefPtr<JSON::Object> parameters = JSON::Object::create();
1751         parameters->setString("browsingContextHandle"_s, m_toplevelBrowsingContext.value());
1752         if (m_currentBrowsingContext)
1753             parameters->setString("frameHandle"_s, m_currentBrowsingContext.value());
1754         parameters->setString("function"_s, focusScript);
1755         parameters->setArray("arguments"_s, WTFMove(arguments));
1756         m_host->sendCommandToBackend("evaluateJavaScriptFunction"_s, WTFMove(parameters), [this, protectedThis = makeRef(*this), text, completionHandler = WTFMove(completionHandler)](SessionHost::CommandResponse&& response) mutable {
1757             if (response.isError || !response.responseObject) {
1758                 completionHandler(CommandResult::fail(WTFMove(response.responseObject)));
1759                 return;
1760             }
1761
1762             unsigned stickyModifiers = 0;
1763             auto textLength = text.length();
1764             Vector<KeyboardInteraction> interactions;
1765             interactions.reserveInitialCapacity(textLength);
1766             for (unsigned i = 0; i < textLength; ++i) {
1767                 auto key = text[i];
1768                 KeyboardInteraction interaction;
1769                 KeyModifier modifier;
1770                 auto virtualKey = virtualKeyForKey(key, modifier);
1771                 if (!virtualKey.isNull()) {
1772                     interaction.key = virtualKey;
1773                     if (modifier != KeyModifier::None) {
1774                         stickyModifiers ^= modifier;
1775                         if (stickyModifiers & modifier)
1776                             interaction.type = KeyboardInteractionType::KeyPress;
1777                         else
1778                             interaction.type = KeyboardInteractionType::KeyRelease;
1779                     }
1780                 } else
1781                     interaction.text = String(&key, 1);
1782                 interactions.uncheckedAppend(WTFMove(interaction));
1783             }
1784
1785             // Reset sticky modifiers if needed.
1786             if (stickyModifiers) {
1787                 if (stickyModifiers & KeyModifier::Shift)
1788                     interactions.append({ KeyboardInteractionType::KeyRelease, std::nullopt, std::optional<String>("Shift"_s) });
1789                 if (stickyModifiers & KeyModifier::Control)
1790                     interactions.append({ KeyboardInteractionType::KeyRelease, std::nullopt, std::optional<String>("Control"_s) });
1791                 if (stickyModifiers & KeyModifier::Alternate)
1792                     interactions.append({ KeyboardInteractionType::KeyRelease, std::nullopt, std::optional<String>("Alternate"_s) });
1793                 if (stickyModifiers & KeyModifier::Meta)
1794                     interactions.append({ KeyboardInteractionType::KeyRelease, std::nullopt, std::optional<String>("Meta"_s) });
1795             }
1796
1797             performKeyboardInteractions(WTFMove(interactions), WTFMove(completionHandler));
1798         });
1799     });
1800 }
1801
1802 RefPtr<JSON::Value> Session::handleScriptResult(RefPtr<JSON::Value>&& resultValue)
1803 {
1804     RefPtr<JSON::Array> resultArray;
1805     if (resultValue->asArray(resultArray)) {
1806         RefPtr<JSON::Array> returnValueArray = JSON::Array::create();
1807         unsigned resultArrayLength = resultArray->length();
1808         for (unsigned i = 0; i < resultArrayLength; ++i)
1809             returnValueArray->pushValue(handleScriptResult(resultArray->get(i)));
1810         return returnValueArray;
1811     }
1812
1813     if (auto element = createElement(RefPtr<JSON::Value>(resultValue)))
1814         return element;
1815
1816     RefPtr<JSON::Object> resultObject;
1817     if (resultValue->asObject(resultObject)) {
1818         RefPtr<JSON::Object> returnValueObject = JSON::Object::create();
1819         auto end = resultObject->end();
1820         for (auto it = resultObject->begin(); it != end; ++it)
1821             returnValueObject->setValue(it->key, handleScriptResult(WTFMove(it->value)));
1822         return returnValueObject;
1823     }
1824
1825     return resultValue;
1826 }
1827
1828 void Session::executeScript(const String& script, RefPtr<JSON::Array>&& argumentsArray, ExecuteScriptMode mode, Function<void (CommandResult&&)>&& completionHandler)
1829 {
1830     if (!m_toplevelBrowsingContext) {
1831         completionHandler(CommandResult::fail(CommandResult::ErrorCode::NoSuchWindow));
1832         return;
1833     }
1834
1835     handleUserPrompts([this, script, argumentsArray = WTFMove(argumentsArray), mode, completionHandler = WTFMove(completionHandler)](CommandResult&& result) mutable {
1836         if (result.isError()) {
1837             completionHandler(WTFMove(result));
1838             return;
1839         }
1840         RefPtr<JSON::Array> arguments = JSON::Array::create();
1841         unsigned argumentsLength = argumentsArray->length();
1842         for (unsigned i = 0; i < argumentsLength; ++i) {
1843             if (auto argument = argumentsArray->get(i)) {
1844                 if (auto element = extractElement(*argument))
1845                     arguments->pushString(element->toJSONString());
1846                 else
1847                     arguments->pushString(argument->toJSONString());
1848             }
1849         }
1850
1851         RefPtr<JSON::Object> parameters = JSON::Object::create();
1852         parameters->setString("browsingContextHandle"_s, m_toplevelBrowsingContext.value());
1853         if (m_currentBrowsingContext)
1854             parameters->setString("frameHandle"_s, m_currentBrowsingContext.value());
1855         parameters->setString("function"_s, "function(){" + script + '}');
1856         parameters->setArray("arguments"_s, WTFMove(arguments));
1857         if (mode == ExecuteScriptMode::Async) {
1858             parameters->setBoolean("expectsImplicitCallbackArgument"_s, true);
1859             if (m_scriptTimeout)
1860                 parameters->setInteger("callbackTimeout"_s, m_scriptTimeout.millisecondsAs<int>());
1861         }
1862         m_host->sendCommandToBackend("evaluateJavaScriptFunction"_s, WTFMove(parameters), [this, protectedThis = makeRef(*this), completionHandler = WTFMove(completionHandler)](SessionHost::CommandResponse&& response) mutable {
1863             if (response.isError || !response.responseObject) {
1864                 auto result = CommandResult::fail(WTFMove(response.responseObject));
1865                 if (result.errorCode() == CommandResult::ErrorCode::UnexpectedAlertOpen)
1866                     completionHandler(CommandResult::success());
1867                 else
1868                     completionHandler(WTFMove(result));
1869                 return;
1870             }
1871             String valueString;
1872             if (!response.responseObject->getString("result"_s, valueString)) {
1873                 completionHandler(CommandResult::fail(CommandResult::ErrorCode::UnknownError));
1874                 return;
1875             }
1876             RefPtr<JSON::Value> resultValue;
1877             if (!JSON::Value::parseJSON(valueString, resultValue)) {
1878                 completionHandler(CommandResult::fail(CommandResult::ErrorCode::UnknownError));
1879                 return;
1880             }
1881             completionHandler(CommandResult::success(handleScriptResult(WTFMove(resultValue))));
1882         });
1883     });
1884 }
1885
1886 static String mouseButtonForAutomation(MouseButton button)
1887 {
1888     switch (button) {
1889     case MouseButton::None:
1890         return "None"_s;
1891     case MouseButton::Left:
1892         return "Left"_s;
1893     case MouseButton::Middle:
1894         return "Middle"_s;
1895     case MouseButton::Right:
1896         return "Right"_s;
1897     }
1898
1899     RELEASE_ASSERT_NOT_REACHED();
1900 }
1901
1902 void Session::performMouseInteraction(int x, int y, MouseButton button, MouseInteraction interaction, Function<void (CommandResult&&)>&& completionHandler)
1903 {
1904     RefPtr<JSON::Object> parameters = JSON::Object::create();
1905     parameters->setString("handle"_s, m_toplevelBrowsingContext.value());
1906     RefPtr<JSON::Object> position = JSON::Object::create();
1907     position->setInteger("x"_s, x);
1908     position->setInteger("y"_s, y);
1909     parameters->setObject("position"_s, WTFMove(position));
1910     parameters->setString("button"_s, mouseButtonForAutomation(button));
1911     switch (interaction) {
1912     case MouseInteraction::Move:
1913         parameters->setString("interaction"_s, "Move"_s);
1914         break;
1915     case MouseInteraction::Down:
1916         parameters->setString("interaction"_s, "Down"_s);
1917         break;
1918     case MouseInteraction::Up:
1919         parameters->setString("interaction"_s, "Up"_s);
1920         break;
1921     case MouseInteraction::SingleClick:
1922         parameters->setString("interaction"_s, "SingleClick"_s);
1923         break;
1924     case MouseInteraction::DoubleClick:
1925         parameters->setString("interaction"_s, "DoubleClick"_s);
1926         break;
1927     }
1928     parameters->setArray("modifiers"_s, JSON::Array::create());
1929     m_host->sendCommandToBackend("performMouseInteraction"_s, WTFMove(parameters), [this, protectedThis = makeRef(*this), completionHandler = WTFMove(completionHandler)](SessionHost::CommandResponse&& response) {
1930         if (response.isError) {
1931             completionHandler(CommandResult::fail(WTFMove(response.responseObject)));
1932             return;
1933         }
1934         completionHandler(CommandResult::success());
1935     });
1936 }
1937
1938 void Session::performKeyboardInteractions(Vector<KeyboardInteraction>&& interactions, Function<void (CommandResult&&)>&& completionHandler)
1939 {
1940     RefPtr<JSON::Object> parameters = JSON::Object::create();
1941     parameters->setString("handle"_s, m_toplevelBrowsingContext.value());
1942     RefPtr<JSON::Array> interactionsArray = JSON::Array::create();
1943     for (const auto& interaction : interactions) {
1944         RefPtr<JSON::Object> interactionObject = JSON::Object::create();
1945         switch (interaction.type) {
1946         case KeyboardInteractionType::KeyPress:
1947             interactionObject->setString("type"_s, "KeyPress"_s);
1948             break;
1949         case KeyboardInteractionType::KeyRelease:
1950             interactionObject->setString("type"_s, "KeyRelease"_s);
1951             break;
1952         case KeyboardInteractionType::InsertByKey:
1953             interactionObject->setString("type"_s, "InsertByKey"_s);
1954             break;
1955         }
1956         if (interaction.key)
1957             interactionObject->setString("key"_s, interaction.key.value());
1958         if (interaction.text)
1959             interactionObject->setString("text"_s, interaction.text.value());
1960         interactionsArray->pushObject(WTFMove(interactionObject));
1961     }
1962     parameters->setArray("interactions"_s, WTFMove(interactionsArray));
1963     m_host->sendCommandToBackend("performKeyboardInteractions"_s, WTFMove(parameters), [this, protectedThis = makeRef(*this), completionHandler = WTFMove(completionHandler)](SessionHost::CommandResponse&& response) {
1964         if (response.isError) {
1965             completionHandler(CommandResult::fail(WTFMove(response.responseObject)));
1966             return;
1967         }
1968         completionHandler(CommandResult::success());
1969     });
1970 }
1971
1972 static std::optional<Session::Cookie> parseAutomationCookie(const JSON::Object& cookieObject)
1973 {
1974     Session::Cookie cookie;
1975     if (!cookieObject.getString("name"_s, cookie.name))
1976         return std::nullopt;
1977     if (!cookieObject.getString("value"_s, cookie.value))
1978         return std::nullopt;
1979
1980     String path;
1981     if (cookieObject.getString("path"_s, path))
1982         cookie.path = path;
1983     String domain;
1984     if (cookieObject.getString("domain"_s, domain))
1985         cookie.domain = domain;
1986     bool secure;
1987     if (cookieObject.getBoolean("secure"_s, secure))
1988         cookie.secure = secure;
1989     bool httpOnly;
1990     if (cookieObject.getBoolean("httpOnly"_s, httpOnly))
1991         cookie.httpOnly = httpOnly;
1992     bool session = false;
1993     cookieObject.getBoolean("session"_s, session);
1994     if (!session) {
1995         double expiry;
1996         if (cookieObject.getDouble("expires"_s, expiry))
1997             cookie.expiry = expiry;
1998     }
1999
2000     return cookie;
2001 }
2002
2003 static RefPtr<JSON::Object> builtAutomationCookie(const Session::Cookie& cookie)
2004 {
2005     RefPtr<JSON::Object> cookieObject = JSON::Object::create();
2006     cookieObject->setString("name"_s, cookie.name);
2007     cookieObject->setString("value"_s, cookie.value);
2008     cookieObject->setString("path"_s, cookie.path.value_or("/"));
2009     cookieObject->setString("domain"_s, cookie.domain.value_or(emptyString()));
2010     cookieObject->setBoolean("secure"_s, cookie.secure.value_or(false));
2011     cookieObject->setBoolean("httpOnly"_s, cookie.httpOnly.value_or(false));
2012     cookieObject->setBoolean("session"_s, !cookie.expiry);
2013     cookieObject->setDouble("expires"_s, cookie.expiry.value_or(0));
2014     return cookieObject;
2015 }
2016
2017 static RefPtr<JSON::Object> serializeCookie(const Session::Cookie& cookie)
2018 {
2019     RefPtr<JSON::Object> cookieObject = JSON::Object::create();
2020     cookieObject->setString("name"_s, cookie.name);
2021     cookieObject->setString("value"_s, cookie.value);
2022     if (cookie.path)
2023         cookieObject->setString("path"_s, cookie.path.value());
2024     if (cookie.domain)
2025         cookieObject->setString("domain"_s, cookie.domain.value());
2026     if (cookie.secure)
2027         cookieObject->setBoolean("secure"_s, cookie.secure.value());
2028     if (cookie.httpOnly)
2029         cookieObject->setBoolean("httpOnly"_s, cookie.httpOnly.value());
2030     if (cookie.expiry)
2031         cookieObject->setInteger("expiry"_s, cookie.expiry.value());
2032     return cookieObject;
2033 }
2034
2035 void Session::getAllCookies(Function<void (CommandResult&&)>&& completionHandler)
2036 {
2037     if (!m_toplevelBrowsingContext) {
2038         completionHandler(CommandResult::fail(CommandResult::ErrorCode::NoSuchWindow));
2039         return;
2040     }
2041
2042     handleUserPrompts([this, completionHandler = WTFMove(completionHandler)](CommandResult&& result) mutable {
2043         if (result.isError()) {
2044             completionHandler(WTFMove(result));
2045             return;
2046         }
2047
2048         RefPtr<JSON::Object> parameters = JSON::Object::create();
2049         parameters->setString("browsingContextHandle"_s, m_toplevelBrowsingContext.value());
2050         m_host->sendCommandToBackend("getAllCookies"_s, WTFMove(parameters), [this, protectedThis = makeRef(*this), completionHandler = WTFMove(completionHandler)](SessionHost::CommandResponse&& response) mutable {
2051             if (response.isError || !response.responseObject) {
2052                 completionHandler(CommandResult::fail(WTFMove(response.responseObject)));
2053                 return;
2054             }
2055             RefPtr<JSON::Array> cookiesArray;
2056             if (!response.responseObject->getArray("cookies"_s, cookiesArray)) {
2057                 completionHandler(CommandResult::fail(CommandResult::ErrorCode::UnknownError));
2058                 return;
2059             }
2060             RefPtr<JSON::Array> cookies = JSON::Array::create();
2061             for (unsigned i = 0; i < cookiesArray->length(); ++i) {
2062                 RefPtr<JSON::Value> cookieValue = cookiesArray->get(i);
2063                 RefPtr<JSON::Object> cookieObject;
2064                 if (!cookieValue->asObject(cookieObject)) {
2065                     completionHandler(CommandResult::fail(CommandResult::ErrorCode::UnknownError));
2066                     return;
2067                 }
2068
2069                 auto cookie = parseAutomationCookie(*cookieObject);
2070                 if (!cookie) {
2071                     completionHandler(CommandResult::fail(CommandResult::ErrorCode::UnknownError));
2072                     return;
2073                 }
2074                 cookies->pushObject(serializeCookie(cookie.value()));
2075             }
2076             completionHandler(CommandResult::success(WTFMove(cookies)));
2077         });
2078     });
2079 }
2080
2081 void Session::getNamedCookie(const String& name, Function<void (CommandResult&&)>&& completionHandler)
2082 {
2083     getAllCookies([this, name, completionHandler = WTFMove(completionHandler)](CommandResult&& result) mutable {
2084         if (result.isError()) {
2085             completionHandler(WTFMove(result));
2086             return;
2087         }
2088         RefPtr<JSON::Array> cookiesArray;
2089         result.result()->asArray(cookiesArray);
2090         for (unsigned i = 0; i < cookiesArray->length(); ++i) {
2091             RefPtr<JSON::Value> cookieValue = cookiesArray->get(i);
2092             RefPtr<JSON::Object> cookieObject;
2093             cookieValue->asObject(cookieObject);
2094             String cookieName;
2095             cookieObject->getString("name"_s, cookieName);
2096             if (cookieName == name) {
2097                 completionHandler(CommandResult::success(WTFMove(cookieObject)));
2098                 return;
2099             }
2100         }
2101         completionHandler(CommandResult::fail(CommandResult::ErrorCode::NoSuchCookie));
2102     });
2103 }
2104
2105 void Session::addCookie(const Cookie& cookie, Function<void (CommandResult&&)>&& completionHandler)
2106 {
2107     if (!m_toplevelBrowsingContext) {
2108         completionHandler(CommandResult::fail(CommandResult::ErrorCode::NoSuchWindow));
2109         return;
2110     }
2111
2112     handleUserPrompts([this, cookie = builtAutomationCookie(cookie), completionHandler = WTFMove(completionHandler)](CommandResult&& result) mutable {
2113         if (result.isError()) {
2114             completionHandler(WTFMove(result));
2115             return;
2116         }
2117         RefPtr<JSON::Object> parameters = JSON::Object::create();
2118         parameters->setString("browsingContextHandle"_s, m_toplevelBrowsingContext.value());
2119         parameters->setObject("cookie"_s, WTFMove(cookie));
2120         m_host->sendCommandToBackend("addSingleCookie"_s, WTFMove(parameters), [this, protectedThis = makeRef(*this), completionHandler = WTFMove(completionHandler)](SessionHost::CommandResponse&& response) {
2121             if (response.isError) {
2122                 completionHandler(CommandResult::fail(WTFMove(response.responseObject)));
2123                 return;
2124             }
2125             completionHandler(CommandResult::success());
2126         });
2127     });
2128 }
2129
2130 void Session::deleteCookie(const String& name, Function<void (CommandResult&&)>&& completionHandler)
2131 {
2132     if (!m_toplevelBrowsingContext) {
2133         completionHandler(CommandResult::fail(CommandResult::ErrorCode::NoSuchWindow));
2134         return;
2135     }
2136
2137     handleUserPrompts([this, name, completionHandler = WTFMove(completionHandler)](CommandResult&& result) mutable {
2138         if (result.isError()) {
2139             completionHandler(WTFMove(result));
2140             return;
2141         }
2142         RefPtr<JSON::Object> parameters = JSON::Object::create();
2143         parameters->setString("browsingContextHandle"_s, m_toplevelBrowsingContext.value());
2144         parameters->setString("cookieName"_s, name);
2145         m_host->sendCommandToBackend("deleteSingleCookie"_s, WTFMove(parameters), [this, protectedThis = makeRef(*this), completionHandler = WTFMove(completionHandler)](SessionHost::CommandResponse&& response) {
2146             if (response.isError) {
2147                 completionHandler(CommandResult::fail(WTFMove(response.responseObject)));
2148                 return;
2149             }
2150             completionHandler(CommandResult::success());
2151         });
2152     });
2153 }
2154
2155 void Session::deleteAllCookies(Function<void (CommandResult&&)>&& completionHandler)
2156 {
2157     if (!m_toplevelBrowsingContext) {
2158         completionHandler(CommandResult::fail(CommandResult::ErrorCode::NoSuchWindow));
2159         return;
2160     }
2161
2162     handleUserPrompts([this, completionHandler = WTFMove(completionHandler)](CommandResult&& result) mutable {
2163         if (result.isError()) {
2164             completionHandler(WTFMove(result));
2165             return;
2166         }
2167         RefPtr<JSON::Object> parameters = JSON::Object::create();
2168         parameters->setString("browsingContextHandle"_s, m_toplevelBrowsingContext.value());
2169         m_host->sendCommandToBackend("deleteAllCookies"_s, WTFMove(parameters), [this, protectedThis = makeRef(*this), completionHandler = WTFMove(completionHandler)](SessionHost::CommandResponse&& response) {
2170             if (response.isError) {
2171                 completionHandler(CommandResult::fail(WTFMove(response.responseObject)));
2172                 return;
2173             }
2174             completionHandler(CommandResult::success());
2175         });
2176     });
2177 }
2178
2179 InputSource& Session::getOrCreateInputSource(const String& id, InputSource::Type type, std::optional<PointerType> pointerType)
2180 {
2181     auto addResult = m_activeInputSources.add(id, InputSource());
2182     if (addResult.isNewEntry)
2183         addResult.iterator->value = { type, pointerType };
2184     return addResult.iterator->value;
2185 }
2186
2187 Session::InputSourceState& Session::inputSourceState(const String& id)
2188 {
2189     return m_inputStateTable.ensure(id, [] { return InputSourceState(); }).iterator->value;
2190 }
2191
2192 static const char* automationSourceType(InputSource::Type type)
2193 {
2194     switch (type) {
2195     case InputSource::Type::None:
2196         return "Null";
2197     case InputSource::Type::Pointer:
2198         return "Mouse";
2199     case InputSource::Type::Key:
2200         return "Keyboard";
2201     }
2202     RELEASE_ASSERT_NOT_REACHED();
2203 }
2204
2205 static const char* automationOriginType(PointerOrigin::Type type)
2206 {
2207     switch (type) {
2208     case PointerOrigin::Type::Viewport:
2209         return "Viewport";
2210     case PointerOrigin::Type::Pointer:
2211         return "Pointer";
2212     case PointerOrigin::Type::Element:
2213         return "Element";
2214     }
2215     RELEASE_ASSERT_NOT_REACHED();
2216 }
2217
2218 void Session::performActions(Vector<Vector<Action>>&& actionsByTick, Function<void (CommandResult&&)>&& completionHandler)
2219 {
2220     if (!m_toplevelBrowsingContext) {
2221         completionHandler(CommandResult::fail(CommandResult::ErrorCode::NoSuchWindow));
2222         return;
2223     }
2224
2225     handleUserPrompts([this, actionsByTick = WTFMove(actionsByTick), completionHandler = WTFMove(completionHandler)](CommandResult&& result) mutable {
2226         if (result.isError()) {
2227             completionHandler(WTFMove(result));
2228             return;
2229         }
2230
2231         // First check if we have actions and whether we need to resolve any pointer move element origin.
2232         unsigned actionsCount = 0;
2233         for (const auto& tick : actionsByTick)
2234             actionsCount += tick.size();
2235         if (!actionsCount) {
2236             completionHandler(CommandResult::success());
2237             return;
2238         }
2239
2240         RefPtr<JSON::Object> parameters = JSON::Object::create();
2241         parameters->setString("handle"_s, m_toplevelBrowsingContext.value());
2242         if (m_currentBrowsingContext)
2243             parameters->setString("frameHandle"_s, m_currentBrowsingContext.value());
2244         RefPtr<JSON::Array> inputSources = JSON::Array::create();
2245         for (const auto& inputSource : m_activeInputSources) {
2246             RefPtr<JSON::Object> inputSourceObject = JSON::Object::create();
2247             inputSourceObject->setString("sourceId"_s, inputSource.key);
2248             inputSourceObject->setString("sourceType"_s, automationSourceType(inputSource.value.type));
2249             inputSources->pushObject(WTFMove(inputSourceObject));
2250         }
2251         parameters->setArray("inputSources"_s, WTFMove(inputSources));
2252         RefPtr<JSON::Array> steps = JSON::Array::create();
2253         for (const auto& tick : actionsByTick) {
2254             RefPtr<JSON::Array> states = JSON::Array::create();
2255             for (const auto& action : tick) {
2256                 RefPtr<JSON::Object> state = JSON::Object::create();
2257                 auto& currentState = inputSourceState(action.id);
2258                 state->setString("sourceId"_s, action.id);
2259                 switch (action.type) {
2260                 case Action::Type::None:
2261                     state->setDouble("duration"_s, action.duration.value());
2262                     break;
2263                 case Action::Type::Pointer: {
2264                     switch (action.subtype) {
2265                     case Action::Subtype::PointerUp:
2266                         currentState.pressedButton = std::nullopt;
2267                         break;
2268                     case Action::Subtype::PointerDown:
2269                         currentState.pressedButton = action.button.value();
2270                         break;
2271                     case Action::Subtype::PointerMove: {
2272                         state->setString("origin"_s, automationOriginType(action.origin->type));
2273                         RefPtr<JSON::Object> location = JSON::Object::create();
2274                         location->setInteger("x"_s, action.x.value());
2275                         location->setInteger("y"_s, action.y.value());
2276                         state->setObject("location"_s, WTFMove(location));
2277                         if (action.origin->type == PointerOrigin::Type::Element)
2278                             state->setString("nodeHandle"_s, action.origin->elementID.value());
2279                         FALLTHROUGH;
2280                     }
2281                     case Action::Subtype::Pause:
2282                         if (action.duration)
2283                             state->setDouble("duration"_s, action.duration.value());
2284                         break;
2285                     case Action::Subtype::PointerCancel:
2286                         currentState.pressedButton = std::nullopt;
2287                         break;
2288                     case Action::Subtype::KeyUp:
2289                     case Action::Subtype::KeyDown:
2290                         ASSERT_NOT_REACHED();
2291                     }
2292                     if (currentState.pressedButton)
2293                         state->setString("pressedButton"_s, mouseButtonForAutomation(currentState.pressedButton.value()));
2294                     break;
2295                 }
2296                 case Action::Type::Key:
2297                     switch (action.subtype) {
2298                     case Action::Subtype::KeyUp:
2299                         if (currentState.pressedVirtualKey)
2300                             currentState.pressedVirtualKey = std::nullopt;
2301                         else
2302                             currentState.pressedKey = std::nullopt;
2303                         break;
2304                     case Action::Subtype::KeyDown: {
2305                         KeyModifier modifier;
2306                         auto virtualKey = virtualKeyForKey(action.key.value()[0], modifier);
2307                         if (!virtualKey.isNull())
2308                             currentState.pressedVirtualKey = virtualKey;
2309                         else
2310                             currentState.pressedKey = action.key.value();
2311                         break;
2312                     }
2313                     case Action::Subtype::Pause:
2314                         if (action.duration)
2315                             state->setDouble("duration"_s, action.duration.value());
2316                         break;
2317                     case Action::Subtype::PointerUp:
2318                     case Action::Subtype::PointerDown:
2319                     case Action::Subtype::PointerMove:
2320                     case Action::Subtype::PointerCancel:
2321                         ASSERT_NOT_REACHED();
2322                     }
2323                     if (currentState.pressedKey)
2324                         state->setString("pressedCharKey"_s, currentState.pressedKey.value());
2325                     if (currentState.pressedVirtualKey) {
2326                         // FIXME: support parsing and tracking multiple virtual keys.
2327                         Ref<JSON::Array> virtualKeys = JSON::Array::create();
2328                         virtualKeys->pushString(currentState.pressedVirtualKey.value());
2329                         state->setArray("pressedVirtualKeys"_s, WTFMove(virtualKeys));
2330                     }
2331                     break;
2332                 }
2333                 states->pushObject(WTFMove(state));
2334             }
2335             RefPtr<JSON::Object> stepStates = JSON::Object::create();
2336             stepStates->setArray("states"_s, WTFMove(states));
2337             steps->pushObject(WTFMove(stepStates));
2338         }
2339
2340         parameters->setArray("steps"_s, WTFMove(steps));
2341         m_host->sendCommandToBackend("performInteractionSequence"_s, WTFMove(parameters), [this, protectedThis = makeRef(*this), completionHandler = WTFMove(completionHandler)] (SessionHost::CommandResponse&& response) {
2342             if (response.isError) {
2343                 completionHandler(CommandResult::fail(WTFMove(response.responseObject)));
2344                 return;
2345             }
2346             completionHandler(CommandResult::success());
2347         });
2348     });
2349 }
2350
2351 void Session::releaseActions(Function<void (CommandResult&&)>&& completionHandler)
2352 {
2353     if (!m_toplevelBrowsingContext) {
2354         completionHandler(CommandResult::fail(CommandResult::ErrorCode::NoSuchWindow));
2355         return;
2356     }
2357
2358     m_activeInputSources.clear();
2359     m_inputStateTable.clear();
2360
2361     RefPtr<JSON::Object> parameters = JSON::Object::create();
2362     parameters->setString("handle"_s, m_toplevelBrowsingContext.value());
2363     m_host->sendCommandToBackend("cancelInteractionSequence"_s, WTFMove(parameters), [this, protectedThis = makeRef(*this), completionHandler = WTFMove(completionHandler)](SessionHost::CommandResponse&& response) {
2364         if (response.isError) {
2365             completionHandler(CommandResult::fail(WTFMove(response.responseObject)));
2366             return;
2367         }
2368         completionHandler(CommandResult::success());
2369     });
2370 }
2371
2372 void Session::dismissAlert(Function<void (CommandResult&&)>&& completionHandler)
2373 {
2374     if (!m_toplevelBrowsingContext) {
2375         completionHandler(CommandResult::fail(CommandResult::ErrorCode::NoSuchWindow));
2376         return;
2377     }
2378
2379     RefPtr<JSON::Object> parameters = JSON::Object::create();
2380     parameters->setString("browsingContextHandle"_s, m_toplevelBrowsingContext.value());
2381     m_host->sendCommandToBackend("dismissCurrentJavaScriptDialog"_s, WTFMove(parameters), [this, protectedThis = makeRef(*this), completionHandler = WTFMove(completionHandler)](SessionHost::CommandResponse&& response) {
2382         if (response.isError) {
2383             completionHandler(CommandResult::fail(WTFMove(response.responseObject)));
2384             return;
2385         }
2386         completionHandler(CommandResult::success());
2387     });
2388 }
2389
2390 void Session::acceptAlert(Function<void (CommandResult&&)>&& completionHandler)
2391 {
2392     if (!m_toplevelBrowsingContext) {
2393         completionHandler(CommandResult::fail(CommandResult::ErrorCode::NoSuchWindow));
2394         return;
2395     }
2396
2397     RefPtr<JSON::Object> parameters = JSON::Object::create();
2398     parameters->setString("browsingContextHandle"_s, m_toplevelBrowsingContext.value());
2399     m_host->sendCommandToBackend("acceptCurrentJavaScriptDialog"_s, WTFMove(parameters), [this, protectedThis = makeRef(*this), completionHandler = WTFMove(completionHandler)](SessionHost::CommandResponse&& response) {
2400         if (response.isError) {
2401             completionHandler(CommandResult::fail(WTFMove(response.responseObject)));
2402             return;
2403         }
2404         completionHandler(CommandResult::success());
2405     });
2406 }
2407
2408 void Session::getAlertText(Function<void (CommandResult&&)>&& completionHandler)
2409 {
2410     if (!m_toplevelBrowsingContext) {
2411         completionHandler(CommandResult::fail(CommandResult::ErrorCode::NoSuchWindow));
2412         return;
2413     }
2414
2415     RefPtr<JSON::Object> parameters = JSON::Object::create();
2416     parameters->setString("browsingContextHandle"_s, m_toplevelBrowsingContext.value());
2417     m_host->sendCommandToBackend("messageOfCurrentJavaScriptDialog"_s, WTFMove(parameters), [this, protectedThis = makeRef(*this), completionHandler = WTFMove(completionHandler)](SessionHost::CommandResponse&& response) {
2418         if (response.isError || !response.responseObject) {
2419             completionHandler(CommandResult::fail(WTFMove(response.responseObject)));
2420             return;
2421         }
2422         String valueString;
2423         if (!response.responseObject->getString("message"_s, valueString)) {
2424             completionHandler(CommandResult::fail(CommandResult::ErrorCode::UnknownError));
2425             return;
2426         }
2427         completionHandler(CommandResult::success(JSON::Value::create(valueString)));
2428     });
2429 }
2430
2431 void Session::sendAlertText(const String& text, Function<void (CommandResult&&)>&& completionHandler)
2432 {
2433     if (!m_toplevelBrowsingContext) {
2434         completionHandler(CommandResult::fail(CommandResult::ErrorCode::NoSuchWindow));
2435         return;
2436     }
2437
2438     RefPtr<JSON::Object> parameters = JSON::Object::create();
2439     parameters->setString("browsingContextHandle"_s, m_toplevelBrowsingContext.value());
2440     parameters->setString("userInput"_s, text);
2441     m_host->sendCommandToBackend("setUserInputForCurrentJavaScriptPrompt"_s, WTFMove(parameters), [this, protectedThis = makeRef(*this), completionHandler = WTFMove(completionHandler)](SessionHost::CommandResponse&& response) {
2442         if (response.isError) {
2443             completionHandler(CommandResult::fail(WTFMove(response.responseObject)));
2444             return;
2445         }
2446         completionHandler(CommandResult::success());
2447     });
2448 }
2449
2450 void Session::takeScreenshot(std::optional<String> elementID, std::optional<bool> scrollIntoView, Function<void (CommandResult&&)>&& completionHandler)
2451 {
2452     if (!m_toplevelBrowsingContext) {
2453         completionHandler(CommandResult::fail(CommandResult::ErrorCode::NoSuchWindow));
2454         return;
2455     }
2456
2457     handleUserPrompts([this, elementID, scrollIntoView, completionHandler = WTFMove(completionHandler)](CommandResult&& result) mutable {
2458         if (result.isError()) {
2459             completionHandler(WTFMove(result));
2460             return;
2461         }
2462         RefPtr<JSON::Object> parameters = JSON::Object::create();
2463         parameters->setString("handle"_s, m_toplevelBrowsingContext.value());
2464         if (m_currentBrowsingContext)
2465             parameters->setString("frameHandle"_s, m_currentBrowsingContext.value());
2466         if (elementID)
2467             parameters->setString("nodeHandle"_s, elementID.value());
2468         else
2469             parameters->setBoolean("clipToViewport"_s, true);
2470         if (scrollIntoView.value_or(false))
2471             parameters->setBoolean("scrollIntoViewIfNeeded"_s, true);
2472         m_host->sendCommandToBackend("takeScreenshot"_s, WTFMove(parameters), [this, protectedThis = makeRef(*this), completionHandler = WTFMove(completionHandler)](SessionHost::CommandResponse&& response) mutable {
2473             if (response.isError || !response.responseObject) {
2474                 completionHandler(CommandResult::fail(WTFMove(response.responseObject)));
2475                 return;
2476             }
2477             String data;
2478             if (!response.responseObject->getString("data"_s, data)) {
2479                 completionHandler(CommandResult::fail(CommandResult::ErrorCode::UnknownError));
2480                 return;
2481             }
2482             completionHandler(CommandResult::success(JSON::Value::create(data)));
2483         });
2484     });
2485 }
2486
2487 } // namespace WebDriver