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