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