1/*
2 * Copyright (C) 2017 Igalia S.L.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 * 1. Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 *
13 * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
14 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
15 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
17 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
18 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
19 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
20 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
21 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
22 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
23 * THE POSSIBILITY OF SUCH DAMAGE.
24 */
25
26#include "config.h"
27#include "Session.h"
28
29#include "CommandResult.h"
30#include "SessionHost.h"
31#include "WebDriverAtoms.h"
32#include <wtf/CryptographicallyRandomNumber.h>
33#include <wtf/HashSet.h>
34#include <wtf/HexNumber.h>
35#include <wtf/NeverDestroyed.h>
36
37namespace WebDriver {
38
39// https://w3c.github.io/webdriver/webdriver-spec.html#dfn-session-script-timeout
40static const Seconds defaultScriptTimeout = 30_s;
41// https://w3c.github.io/webdriver/webdriver-spec.html#dfn-session-page-load-timeout
42static const Seconds defaultPageLoadTimeout = 300_s;
43// https://w3c.github.io/webdriver/webdriver-spec.html#dfn-session-implicit-wait-timeout
44static const Seconds defaultImplicitWaitTimeout = 0_s;
45
46const String& Session::webElementIdentifier()
47{
48 // The web element identifier is a constant defined by the spec in Section 11 Elements.
49 // https://www.w3.org/TR/webdriver/#elements
50 static NeverDestroyed<String> webElementID { "element-6066-11e4-a52e-4f735466cecf"_s };
51 return webElementID;
52}
53
54Session::Session(std::unique_ptr<SessionHost>&& host)
55 : m_host(WTFMove(host))
56 , m_scriptTimeout(defaultScriptTimeout)
57 , m_pageLoadTimeout(defaultPageLoadTimeout)
58 , m_implicitWaitTimeout(defaultImplicitWaitTimeout)
59{
60 if (capabilities().timeouts)
61 setTimeouts(capabilities().timeouts.value(), [](CommandResult&&) { });
62}
63
64Session::~Session()
65{
66}
67
68const String& Session::id() const
69{
70 return m_host->sessionID();
71}
72
73const Capabilities& Session::capabilities() const
74{
75 return m_host->capabilities();
76}
77
78bool Session::isConnected() const
79{
80 return m_host->isConnected();
81}
82
83static Optional<String> firstWindowHandleInResult(JSON::Value& result)
84{
85 RefPtr<JSON::Array> handles;
86 if (result.asArray(handles) && handles->length()) {
87 auto handleValue = handles->get(0);
88 String handle;
89 if (handleValue->asString(handle))
90 return handle;
91 }
92 return WTF::nullopt;
93}
94
95void Session::closeAllToplevelBrowsingContexts(const String& toplevelBrowsingContext, Function<void (CommandResult&&)>&& completionHandler)
96{
97 closeTopLevelBrowsingContext(toplevelBrowsingContext, [this, protectedThis = makeRef(*this), completionHandler = WTFMove(completionHandler)](CommandResult&& result) mutable {
98 if (result.isError()) {
99 completionHandler(WTFMove(result));
100 return;
101 }
102 if (auto handle = firstWindowHandleInResult(*result.result())) {
103 closeAllToplevelBrowsingContexts(handle.value(), WTFMove(completionHandler));
104 return;
105 }
106 completionHandler(CommandResult::success());
107 });
108}
109
110void Session::close(Function<void (CommandResult&&)>&& completionHandler)
111{
112 m_toplevelBrowsingContext = WTF::nullopt;
113 getWindowHandles([this, completionHandler = WTFMove(completionHandler)](CommandResult&& result) mutable {
114 if (result.isError()) {
115 completionHandler(WTFMove(result));
116 return;
117 }
118 if (auto handle = firstWindowHandleInResult(*result.result())) {
119 closeAllToplevelBrowsingContexts(handle.value(), WTFMove(completionHandler));
120 return;
121 }
122 completionHandler(CommandResult::success());
123 });
124}
125
126void Session::getTimeouts(Function<void (CommandResult&&)>&& completionHandler)
127{
128 RefPtr<JSON::Object> parameters = JSON::Object::create();
129 parameters->setInteger("script"_s, m_scriptTimeout.millisecondsAs<int>());
130 parameters->setInteger("pageLoad"_s, m_pageLoadTimeout.millisecondsAs<int>());
131 parameters->setInteger("implicit"_s, m_implicitWaitTimeout.millisecondsAs<int>());
132 completionHandler(CommandResult::success(WTFMove(parameters)));
133}
134
135void Session::setTimeouts(const Timeouts& timeouts, Function<void (CommandResult&&)>&& completionHandler)
136{
137 if (timeouts.script)
138 m_scriptTimeout = timeouts.script.value();
139 if (timeouts.pageLoad)
140 m_pageLoadTimeout = timeouts.pageLoad.value();
141 if (timeouts.implicit)
142 m_implicitWaitTimeout = timeouts.implicit.value();
143 completionHandler(CommandResult::success());
144}
145
146void Session::switchToTopLevelBrowsingContext(Optional<String> toplevelBrowsingContext)
147{
148 m_toplevelBrowsingContext = toplevelBrowsingContext;
149 m_currentBrowsingContext = WTF::nullopt;
150}
151
152void Session::switchToBrowsingContext(Optional<String> browsingContext)
153{
154 // Automation sends empty strings for main frame.
155 if (!browsingContext || browsingContext.value().isEmpty())
156 m_currentBrowsingContext = WTF::nullopt;
157 else
158 m_currentBrowsingContext = browsingContext;
159}
160
161Optional<String> Session::pageLoadStrategyString() const
162{
163 if (!capabilities().pageLoadStrategy)
164 return WTF::nullopt;
165
166 switch (capabilities().pageLoadStrategy.value()) {
167 case PageLoadStrategy::None:
168 return String("None");
169 case PageLoadStrategy::Normal:
170 return String("Normal");
171 case PageLoadStrategy::Eager:
172 return String("Eager");
173 }
174
175 return WTF::nullopt;
176}
177
178void Session::createTopLevelBrowsingContext(Function<void (CommandResult&&)>&& completionHandler)
179{
180 ASSERT(!m_toplevelBrowsingContext);
181 m_host->sendCommandToBackend("createBrowsingContext"_s, nullptr, [this, protectedThis = makeRef(*this), completionHandler = WTFMove(completionHandler)](SessionHost::CommandResponse&& response) mutable {
182 if (response.isError || !response.responseObject) {
183 completionHandler(CommandResult::fail(WTFMove(response.responseObject)));
184 return;
185 }
186 String handle;
187 if (!response.responseObject->getString("handle"_s, handle)) {
188 completionHandler(CommandResult::fail(CommandResult::ErrorCode::UnknownError));
189 return;
190 }
191 switchToTopLevelBrowsingContext(handle);
192 completionHandler(CommandResult::success());
193 });
194}
195
196void Session::handleUserPrompts(Function<void (CommandResult&&)>&& completionHandler)
197{
198 RefPtr<JSON::Object> parameters = JSON::Object::create();
199 parameters->setString("browsingContextHandle"_s, m_toplevelBrowsingContext.value());
200 m_host->sendCommandToBackend("isShowingJavaScriptDialog"_s, WTFMove(parameters), [this, protectedThis = makeRef(*this), completionHandler = WTFMove(completionHandler)](SessionHost::CommandResponse&& response) mutable {
201 if (response.isError || !response.responseObject) {
202 completionHandler(CommandResult::fail(WTFMove(response.responseObject)));
203 return;
204 }
205 bool isShowingJavaScriptDialog;
206 if (!response.responseObject->getBoolean("result", isShowingJavaScriptDialog)) {
207 completionHandler(CommandResult::fail(CommandResult::ErrorCode::UnknownError));
208 return;
209 }
210
211 if (!isShowingJavaScriptDialog) {
212 completionHandler(CommandResult::success());
213 return;
214 }
215
216 handleUnexpectedAlertOpen(WTFMove(completionHandler));
217 });
218}
219
220void Session::handleUnexpectedAlertOpen(Function<void (CommandResult&&)>&& completionHandler)
221{
222 switch (capabilities().unhandledPromptBehavior.valueOr(UnhandledPromptBehavior::DismissAndNotify)) {
223 case UnhandledPromptBehavior::Dismiss:
224 dismissAlert(WTFMove(completionHandler));
225 break;
226 case UnhandledPromptBehavior::Accept:
227 acceptAlert(WTFMove(completionHandler));
228 break;
229 case UnhandledPromptBehavior::DismissAndNotify:
230 dismissAndNotifyAlert(WTFMove(completionHandler));
231 break;
232 case UnhandledPromptBehavior::AcceptAndNotify:
233 acceptAndNotifyAlert(WTFMove(completionHandler));
234 break;
235 case UnhandledPromptBehavior::Ignore:
236 reportUnexpectedAlertOpen(WTFMove(completionHandler));
237 break;
238 }
239}
240
241void Session::dismissAndNotifyAlert(Function<void (CommandResult&&)>&& completionHandler)
242{
243 reportUnexpectedAlertOpen([this, completionHandler = WTFMove(completionHandler)](CommandResult&& result) mutable {
244 dismissAlert([errorResult = WTFMove(result), completionHandler = WTFMove(completionHandler)](CommandResult&& result) mutable {
245 if (result.isError()) {
246 completionHandler(WTFMove(result));
247 return;
248 }
249 completionHandler(WTFMove(errorResult));
250 });
251 });
252}
253
254void Session::acceptAndNotifyAlert(Function<void (CommandResult&&)>&& completionHandler)
255{
256 reportUnexpectedAlertOpen([this, completionHandler = WTFMove(completionHandler)](CommandResult&& result) mutable {
257 acceptAlert([errorResult = WTFMove(result), completionHandler = WTFMove(completionHandler)](CommandResult&& result) mutable {
258 if (result.isError()) {
259 completionHandler(WTFMove(result));
260 return;
261 }
262 completionHandler(WTFMove(errorResult));
263 });
264 });
265}
266
267void Session::reportUnexpectedAlertOpen(Function<void (CommandResult&&)>&& completionHandler)
268{
269 getAlertText([completionHandler = WTFMove(completionHandler)](CommandResult&& result) {
270 Optional<String> alertText;
271 if (!result.isError()) {
272 String valueString;
273 if (result.result()->asString(valueString))
274 alertText = valueString;
275 }
276 auto errorResult = CommandResult::fail(CommandResult::ErrorCode::UnexpectedAlertOpen);
277 if (alertText) {
278 RefPtr<JSON::Object> additonalData = JSON::Object::create();
279 additonalData->setString("text"_s, alertText.value());
280 errorResult.setAdditionalErrorData(WTFMove(additonalData));
281 }
282 completionHandler(WTFMove(errorResult));
283 });
284}
285
286void Session::go(const String& url, Function<void (CommandResult&&)>&& completionHandler)
287{
288 if (!m_toplevelBrowsingContext) {
289 completionHandler(CommandResult::fail(CommandResult::ErrorCode::NoSuchWindow));
290 return;
291 }
292
293 handleUserPrompts([this, protectedThis = makeRef(*this), url, completionHandler = WTFMove(completionHandler)](CommandResult&& result) mutable {
294 if (result.isError()) {
295 completionHandler(WTFMove(result));
296 return;
297 }
298
299 RefPtr<JSON::Object> parameters = JSON::Object::create();
300 parameters->setString("handle"_s, m_toplevelBrowsingContext.value());
301 parameters->setString("url"_s, url);
302 parameters->setInteger("pageLoadTimeout"_s, m_pageLoadTimeout.millisecondsAs<int>());
303 if (auto pageLoadStrategy = pageLoadStrategyString())
304 parameters->setString("pageLoadStrategy"_s, pageLoadStrategy.value());
305 m_host->sendCommandToBackend("navigateBrowsingContext"_s, WTFMove(parameters), [this, protectedThis = protectedThis.copyRef(), completionHandler = WTFMove(completionHandler)](SessionHost::CommandResponse&& response) {
306 if (response.isError) {
307 completionHandler(CommandResult::fail(WTFMove(response.responseObject)));
308 return;
309 }
310 switchToBrowsingContext(WTF::nullopt);
311 completionHandler(CommandResult::success());
312 });
313 });
314}
315
316void Session::getCurrentURL(Function<void (CommandResult&&)>&& completionHandler)
317{
318 if (!m_toplevelBrowsingContext) {
319 completionHandler(CommandResult::fail(CommandResult::ErrorCode::NoSuchWindow));
320 return;
321 }
322
323 handleUserPrompts([this, protectedThis = makeRef(*this), completionHandler = WTFMove(completionHandler)](CommandResult&& result) mutable {
324 if (result.isError()) {
325 completionHandler(WTFMove(result));
326 return;
327 }
328
329 RefPtr<JSON::Object> parameters = JSON::Object::create();
330 parameters->setString("handle"_s, m_toplevelBrowsingContext.value());
331 m_host->sendCommandToBackend("getBrowsingContext"_s, WTFMove(parameters), [protectedThis = protectedThis.copyRef(), completionHandler = WTFMove(completionHandler)](SessionHost::CommandResponse&& response) {
332 if (response.isError || !response.responseObject) {
333 completionHandler(CommandResult::fail(WTFMove(response.responseObject)));
334 return;
335 }
336 RefPtr<JSON::Object> browsingContext;
337 if (!response.responseObject->getObject("context", browsingContext)) {
338 completionHandler(CommandResult::fail(CommandResult::ErrorCode::UnknownError));
339 return;
340 }
341 String url;
342 if (!browsingContext->getString("url", url)) {
343 completionHandler(CommandResult::fail(CommandResult::ErrorCode::UnknownError));
344 return;
345 }
346 completionHandler(CommandResult::success(JSON::Value::create(url)));
347 });
348 });
349}
350
351void Session::back(Function<void (CommandResult&&)>&& completionHandler)
352{
353 if (!m_toplevelBrowsingContext) {
354 completionHandler(CommandResult::fail(CommandResult::ErrorCode::NoSuchWindow));
355 return;
356 }
357
358 handleUserPrompts([this, protectedThis = makeRef(*this), completionHandler = WTFMove(completionHandler)](CommandResult&& result) mutable {
359 if (result.isError()) {
360 completionHandler(WTFMove(result));
361 return;
362 }
363 RefPtr<JSON::Object> parameters = JSON::Object::create();
364 parameters->setString("handle"_s, m_toplevelBrowsingContext.value());
365 parameters->setInteger("pageLoadTimeout"_s, m_pageLoadTimeout.millisecondsAs<int>());
366 if (auto pageLoadStrategy = pageLoadStrategyString())
367 parameters->setString("pageLoadStrategy"_s, pageLoadStrategy.value());
368 m_host->sendCommandToBackend("goBackInBrowsingContext"_s, WTFMove(parameters), [this, protectedThis = protectedThis.copyRef(), completionHandler = WTFMove(completionHandler)](SessionHost::CommandResponse&& response) {
369 if (response.isError) {
370 completionHandler(CommandResult::fail(WTFMove(response.responseObject)));
371 return;
372 }
373 switchToBrowsingContext(WTF::nullopt);
374 completionHandler(CommandResult::success());
375 });
376 });
377}
378
379void Session::forward(Function<void (CommandResult&&)>&& completionHandler)
380{
381 if (!m_toplevelBrowsingContext) {
382 completionHandler(CommandResult::fail(CommandResult::ErrorCode::NoSuchWindow));
383 return;
384 }
385
386 handleUserPrompts([this, protectedThis = makeRef(*this), completionHandler = WTFMove(completionHandler)](CommandResult&& result) mutable {
387 if (result.isError()) {
388 completionHandler(WTFMove(result));
389 return;
390 }
391 RefPtr<JSON::Object> parameters = JSON::Object::create();
392 parameters->setString("handle"_s, m_toplevelBrowsingContext.value());
393 parameters->setInteger("pageLoadTimeout"_s, m_pageLoadTimeout.millisecondsAs<int>());
394 if (auto pageLoadStrategy = pageLoadStrategyString())
395 parameters->setString("pageLoadStrategy"_s, pageLoadStrategy.value());
396 m_host->sendCommandToBackend("goForwardInBrowsingContext"_s, WTFMove(parameters), [this, protectedThis = protectedThis.copyRef(), completionHandler = WTFMove(completionHandler)](SessionHost::CommandResponse&& response) {
397 if (response.isError) {
398 completionHandler(CommandResult::fail(WTFMove(response.responseObject)));
399 return;
400 }
401 switchToBrowsingContext(WTF::nullopt);
402 completionHandler(CommandResult::success());
403 });
404 });
405}
406
407void Session::refresh(Function<void (CommandResult&&)>&& completionHandler)
408{
409 if (!m_toplevelBrowsingContext) {
410 completionHandler(CommandResult::fail(CommandResult::ErrorCode::NoSuchWindow));
411 return;
412 }
413
414 handleUserPrompts([this, protectedThis = makeRef(*this), completionHandler = WTFMove(completionHandler)](CommandResult&& result) mutable {
415 if (result.isError()) {
416 completionHandler(WTFMove(result));
417 return;
418 }
419 RefPtr<JSON::Object> parameters = JSON::Object::create();
420 parameters->setString("handle"_s, m_toplevelBrowsingContext.value());
421 parameters->setInteger("pageLoadTimeout"_s, m_pageLoadTimeout.millisecondsAs<int>());
422 if (auto pageLoadStrategy = pageLoadStrategyString())
423 parameters->setString("pageLoadStrategy"_s, pageLoadStrategy.value());
424 m_host->sendCommandToBackend("reloadBrowsingContext"_s, WTFMove(parameters), [this, protectedThis = protectedThis.copyRef(), completionHandler = WTFMove(completionHandler)](SessionHost::CommandResponse&& response) {
425 if (response.isError) {
426 completionHandler(CommandResult::fail(WTFMove(response.responseObject)));
427 return;
428 }
429 switchToBrowsingContext(WTF::nullopt);
430 completionHandler(CommandResult::success());
431 });
432 });
433}
434
435void Session::getTitle(Function<void (CommandResult&&)>&& completionHandler)
436{
437 if (!m_toplevelBrowsingContext) {
438 completionHandler(CommandResult::fail(CommandResult::ErrorCode::NoSuchWindow));
439 return;
440 }
441
442 handleUserPrompts([this, protectedThis = makeRef(*this), completionHandler = WTFMove(completionHandler)](CommandResult&& result) mutable {
443 if (result.isError()) {
444 completionHandler(WTFMove(result));
445 return;
446 }
447 RefPtr<JSON::Object> parameters = JSON::Object::create();
448 parameters->setString("browsingContextHandle"_s, m_toplevelBrowsingContext.value());
449 parameters->setString("function"_s, "function() { return document.title; }"_s);
450 parameters->setArray("arguments"_s, JSON::Array::create());
451 m_host->sendCommandToBackend("evaluateJavaScriptFunction"_s, WTFMove(parameters), [protectedThis = protectedThis.copyRef(), completionHandler = WTFMove(completionHandler)](SessionHost::CommandResponse&& response) {
452 if (response.isError || !response.responseObject) {
453 completionHandler(CommandResult::fail(WTFMove(response.responseObject)));
454 return;
455 }
456 String valueString;
457 if (!response.responseObject->getString("result"_s, valueString)) {
458 completionHandler(CommandResult::fail(CommandResult::ErrorCode::UnknownError));
459 return;
460 }
461 RefPtr<JSON::Value> resultValue;
462 if (!JSON::Value::parseJSON(valueString, resultValue)) {
463 completionHandler(CommandResult::fail(CommandResult::ErrorCode::UnknownError));
464 return;
465 }
466 completionHandler(CommandResult::success(WTFMove(resultValue)));
467 });
468 });
469}
470
471void Session::getWindowHandle(Function<void (CommandResult&&)>&& completionHandler)
472{
473 if (!m_toplevelBrowsingContext) {
474 completionHandler(CommandResult::fail(CommandResult::ErrorCode::NoSuchWindow));
475 return;
476 }
477
478 RefPtr<JSON::Object> parameters = JSON::Object::create();
479 parameters->setString("handle"_s, m_toplevelBrowsingContext.value());
480 m_host->sendCommandToBackend("getBrowsingContext"_s, WTFMove(parameters), [protectedThis = makeRef(*this), completionHandler = WTFMove(completionHandler)](SessionHost::CommandResponse&& response) {
481 if (response.isError || !response.responseObject) {
482 completionHandler(CommandResult::fail(WTFMove(response.responseObject)));
483 return;
484 }
485 RefPtr<JSON::Object> browsingContext;
486 if (!response.responseObject->getObject("context"_s, browsingContext)) {
487 completionHandler(CommandResult::fail(CommandResult::ErrorCode::UnknownError));
488 return;
489 }
490 String handle;
491 if (!browsingContext->getString("handle"_s, handle)) {
492 completionHandler(CommandResult::fail(CommandResult::ErrorCode::UnknownError));
493 return;
494 }
495 completionHandler(CommandResult::success(JSON::Value::create(handle)));
496 });
497}
498
499void Session::closeTopLevelBrowsingContext(const String& toplevelBrowsingContext, Function<void (CommandResult&&)>&& completionHandler)
500{
501 RefPtr<JSON::Object> parameters = JSON::Object::create();
502 parameters->setString("handle"_s, toplevelBrowsingContext);
503 m_host->sendCommandToBackend("closeBrowsingContext"_s, WTFMove(parameters), [this, protectedThis = makeRef(*this), completionHandler = WTFMove(completionHandler)](SessionHost::CommandResponse&& response) mutable {
504 if (!m_host->isConnected()) {
505 // Closing the browsing context made the browser quit.
506 completionHandler(CommandResult::success(JSON::Array::create()));
507 return;
508 }
509 if (response.isError) {
510 completionHandler(CommandResult::fail(WTFMove(response.responseObject)));
511 return;
512 }
513
514 getWindowHandles([this, completionHandler = WTFMove(completionHandler)](CommandResult&& result) {
515 if (!m_host->isConnected()) {
516 // Closing the browsing context made the browser quit.
517 completionHandler(CommandResult::success(JSON::Array::create()));
518 return;
519 }
520 completionHandler(WTFMove(result));
521 });
522 });
523}
524
525void Session::closeWindow(Function<void (CommandResult&&)>&& completionHandler)
526{
527 if (!m_toplevelBrowsingContext) {
528 completionHandler(CommandResult::fail(CommandResult::ErrorCode::NoSuchWindow));
529 return;
530 }
531
532 handleUserPrompts([this, completionHandler = WTFMove(completionHandler)](CommandResult&& result) mutable {
533 if (result.isError()) {
534 completionHandler(WTFMove(result));
535 return;
536 }
537 auto toplevelBrowsingContext = std::exchange(m_toplevelBrowsingContext, WTF::nullopt);
538 closeTopLevelBrowsingContext(toplevelBrowsingContext.value(), WTFMove(completionHandler));
539 });
540}
541
542void Session::switchToWindow(const String& windowHandle, Function<void (CommandResult&&)>&& completionHandler)
543{
544 RefPtr<JSON::Object> parameters = JSON::Object::create();
545 parameters->setString("browsingContextHandle"_s, windowHandle);
546 m_host->sendCommandToBackend("switchToBrowsingContext"_s, WTFMove(parameters), [this, protectedThis = makeRef(*this), windowHandle, completionHandler = WTFMove(completionHandler)](SessionHost::CommandResponse&& response) {
547 if (response.isError) {
548 completionHandler(CommandResult::fail(WTFMove(response.responseObject)));
549 return;
550 }
551 switchToTopLevelBrowsingContext(windowHandle);
552 completionHandler(CommandResult::success());
553 });
554}
555
556void Session::getWindowHandles(Function<void (CommandResult&&)>&& completionHandler)
557{
558 m_host->sendCommandToBackend("getBrowsingContexts"_s, JSON::Object::create(), [protectedThis = makeRef(*this), completionHandler = WTFMove(completionHandler)](SessionHost::CommandResponse&& response) {
559 if (response.isError || !response.responseObject) {
560 completionHandler(CommandResult::fail(WTFMove(response.responseObject)));
561 return;
562 }
563 RefPtr<JSON::Array> browsingContextArray;
564 if (!response.responseObject->getArray("contexts"_s, browsingContextArray)) {
565 completionHandler(CommandResult::fail(CommandResult::ErrorCode::UnknownError));
566 return;
567 }
568 RefPtr<JSON::Array> windowHandles = JSON::Array::create();
569 for (unsigned i = 0; i < browsingContextArray->length(); ++i) {
570 RefPtr<JSON::Value> browsingContextValue = browsingContextArray->get(i);
571 RefPtr<JSON::Object> browsingContext;
572 if (!browsingContextValue->asObject(browsingContext)) {
573 completionHandler(CommandResult::fail(CommandResult::ErrorCode::UnknownError));
574 return;
575 }
576
577 String handle;
578 if (!browsingContext->getString("handle"_s, handle)) {
579 completionHandler(CommandResult::fail(CommandResult::ErrorCode::UnknownError));
580 return;
581 }
582
583 windowHandles->pushString(handle);
584 }
585 completionHandler(CommandResult::success(WTFMove(windowHandles)));
586 });
587}
588
589void Session::switchToFrame(RefPtr<JSON::Value>&& frameID, Function<void (CommandResult&&)>&& completionHandler)
590{
591 if (!m_toplevelBrowsingContext) {
592 completionHandler(CommandResult::fail(CommandResult::ErrorCode::NoSuchWindow));
593 return;
594 }
595
596 if (frameID->isNull()) {
597 switchToBrowsingContext(WTF::nullopt);
598 completionHandler(CommandResult::success());
599 return;
600 }
601
602 handleUserPrompts([this, protectedThis = makeRef(*this), frameID = WTFMove(frameID), completionHandler = WTFMove(completionHandler)](CommandResult&& result) mutable {
603 if (result.isError()) {
604 completionHandler(WTFMove(result));
605 return;
606 }
607 RefPtr<JSON::Object> parameters = JSON::Object::create();
608 parameters->setString("browsingContextHandle"_s, m_toplevelBrowsingContext.value());
609 if (m_currentBrowsingContext)
610 parameters->setString("frameHandle"_s, m_currentBrowsingContext.value());
611
612 int frameIndex;
613 if (frameID->asInteger(frameIndex)) {
614 if (frameIndex < 0 || frameIndex > USHRT_MAX) {
615 completionHandler(CommandResult::fail(CommandResult::ErrorCode::NoSuchFrame));
616 return;
617 }
618 parameters->setInteger("ordinal"_s, frameIndex);
619 } else {
620 String frameElementID = extractElementID(*frameID);
621 if (!frameElementID.isEmpty())
622 parameters->setString("nodeHandle"_s, frameElementID);
623 else {
624 String frameName;
625 if (!frameID->asString(frameName)) {
626 completionHandler(CommandResult::fail(CommandResult::ErrorCode::NoSuchFrame));
627 return;
628 }
629 parameters->setString("name"_s, frameName);
630 }
631 }
632
633 m_host->sendCommandToBackend("resolveChildFrameHandle"_s, WTFMove(parameters), [this, protectedThis = protectedThis.copyRef(), completionHandler = WTFMove(completionHandler)](SessionHost::CommandResponse&& response) {
634 if (response.isError || !response.responseObject) {
635 completionHandler(CommandResult::fail(WTFMove(response.responseObject)));
636 return;
637 }
638 String frameHandle;
639 if (!response.responseObject->getString("result"_s, frameHandle)) {
640 completionHandler(CommandResult::fail(CommandResult::ErrorCode::UnknownError));
641 return;
642 }
643 switchToBrowsingContext(frameHandle);
644 completionHandler(CommandResult::success());
645 });
646 });
647}
648
649void Session::switchToParentFrame(Function<void (CommandResult&&)>&& completionHandler)
650{
651 if (!m_toplevelBrowsingContext) {
652 completionHandler(CommandResult::fail(CommandResult::ErrorCode::NoSuchWindow));
653 return;
654 }
655
656 if (!m_currentBrowsingContext) {
657 completionHandler(CommandResult::success());
658 return;
659 }
660
661 handleUserPrompts([this, protectedThis = makeRef(*this), completionHandler = WTFMove(completionHandler)](CommandResult&& result) mutable {
662 if (result.isError()) {
663 completionHandler(WTFMove(result));
664 return;
665 }
666 RefPtr<JSON::Object> parameters = JSON::Object::create();
667 parameters->setString("browsingContextHandle"_s, m_toplevelBrowsingContext.value());
668 parameters->setString("frameHandle"_s, m_currentBrowsingContext.value());
669 m_host->sendCommandToBackend("resolveParentFrameHandle"_s, WTFMove(parameters), [this, protectedThis = protectedThis.copyRef(), completionHandler = WTFMove(completionHandler)](SessionHost::CommandResponse&& response) {
670 if (response.isError || !response.responseObject) {
671 completionHandler(CommandResult::fail(WTFMove(response.responseObject)));
672 return;
673 }
674 String frameHandle;
675 if (!response.responseObject->getString("result"_s, frameHandle)) {
676 completionHandler(CommandResult::fail(CommandResult::ErrorCode::UnknownError));
677 return;
678 }
679 switchToBrowsingContext(frameHandle);
680 completionHandler(CommandResult::success());
681 });
682 });
683}
684
685void Session::getToplevelBrowsingContextRect(Function<void (CommandResult&&)>&& completionHandler)
686{
687 RefPtr<JSON::Object> parameters = JSON::Object::create();
688 parameters->setString("handle"_s, m_toplevelBrowsingContext.value());
689 m_host->sendCommandToBackend("getBrowsingContext"_s, WTFMove(parameters), [protectedThis = makeRef(*this), completionHandler = WTFMove(completionHandler)](SessionHost::CommandResponse&& response) {
690 if (response.isError || !response.responseObject) {
691 completionHandler(CommandResult::fail(WTFMove(response.responseObject)));
692 return;
693 }
694 RefPtr<JSON::Object> browsingContext;
695 if (!response.responseObject->getObject("context"_s, browsingContext)) {
696 completionHandler(CommandResult::fail(CommandResult::ErrorCode::UnknownError));
697 return;
698 }
699 RefPtr<JSON::Object> windowOrigin;
700 double x, y;
701 if (!browsingContext->getObject("windowOrigin"_s, windowOrigin)
702 || !windowOrigin->getDouble("x"_s, x)
703 || !windowOrigin->getDouble("y"_s, y)) {
704 completionHandler(CommandResult::fail(CommandResult::ErrorCode::UnknownError));
705 return;
706 }
707 RefPtr<JSON::Object> windowSize;
708 double width, height;
709 if (!browsingContext->getObject("windowSize"_s, windowSize)
710 || !windowSize->getDouble("width"_s, width)
711 || !windowSize->getDouble("height"_s, height)) {
712 completionHandler(CommandResult::fail(CommandResult::ErrorCode::UnknownError));
713 return;
714 }
715 auto windowRect = JSON::Object::create();
716 windowRect->setDouble("x"_s, x);
717 windowRect->setDouble("y"_s, y);
718 windowRect->setDouble("width"_s, width);
719 windowRect->setDouble("height"_s, height);
720 completionHandler(CommandResult::success(WTFMove(windowRect)));
721 });
722}
723
724void Session::getWindowRect(Function<void (CommandResult&&)>&& completionHandler)
725{
726 if (!m_toplevelBrowsingContext) {
727 completionHandler(CommandResult::fail(CommandResult::ErrorCode::NoSuchWindow));
728 return;
729 }
730
731 handleUserPrompts([this, completionHandler = WTFMove(completionHandler)](CommandResult&& result) mutable {
732 if (result.isError()) {
733 completionHandler(WTFMove(result));
734 return;
735 }
736 getToplevelBrowsingContextRect(WTFMove(completionHandler));
737 });
738}
739
740void Session::setWindowRect(Optional<double> x, Optional<double> y, Optional<double> width, Optional<double> height, Function<void (CommandResult&&)>&& completionHandler)
741{
742 if (!m_toplevelBrowsingContext) {
743 completionHandler(CommandResult::fail(CommandResult::ErrorCode::NoSuchWindow));
744 return;
745 }
746
747 handleUserPrompts([this, protectedThis = makeRef(*this), x, y, width, height, completionHandler = WTFMove(completionHandler)](CommandResult&& result) mutable {
748 if (result.isError()) {
749 completionHandler(WTFMove(result));
750 return;
751 }
752
753 RefPtr<JSON::Object> parameters = JSON::Object::create();
754 parameters->setString("handle"_s, m_toplevelBrowsingContext.value());
755 if (x && y) {
756 RefPtr<JSON::Object> windowOrigin = JSON::Object::create();
757 windowOrigin->setDouble("x", x.value());
758 windowOrigin->setDouble("y", y.value());
759 parameters->setObject("origin"_s, WTFMove(windowOrigin));
760 }
761 if (width && height) {
762 RefPtr<JSON::Object> windowSize = JSON::Object::create();
763 windowSize->setDouble("width", width.value());
764 windowSize->setDouble("height", height.value());
765 parameters->setObject("size"_s, WTFMove(windowSize));
766 }
767 m_host->sendCommandToBackend("setWindowFrameOfBrowsingContext"_s, WTFMove(parameters), [this, protectedThis = protectedThis.copyRef(), completionHandler = WTFMove(completionHandler)] (SessionHost::CommandResponse&& response) mutable {
768 if (response.isError) {
769 completionHandler(CommandResult::fail(WTFMove(response.responseObject)));
770 return;
771 }
772 getToplevelBrowsingContextRect(WTFMove(completionHandler));
773 });
774 });
775}
776
777void Session::maximizeWindow(Function<void (CommandResult&&)>&& completionHandler)
778{
779 if (!m_toplevelBrowsingContext) {
780 completionHandler(CommandResult::fail(CommandResult::ErrorCode::NoSuchWindow));
781 return;
782 }
783
784 handleUserPrompts([this, protectedThis = makeRef(*this), completionHandler = WTFMove(completionHandler)](CommandResult&& result) mutable {
785 if (result.isError()) {
786 completionHandler(WTFMove(result));
787 return;
788 }
789
790 RefPtr<JSON::Object> parameters = JSON::Object::create();
791 parameters->setString("handle"_s, m_toplevelBrowsingContext.value());
792 m_host->sendCommandToBackend("maximizeWindowOfBrowsingContext"_s, WTFMove(parameters), [this, protectedThis = protectedThis.copyRef(), completionHandler = WTFMove(completionHandler)] (SessionHost::CommandResponse&& response) mutable {
793 if (response.isError) {
794 completionHandler(CommandResult::fail(WTFMove(response.responseObject)));
795 return;
796 }
797 getToplevelBrowsingContextRect(WTFMove(completionHandler));
798 });
799 });
800}
801
802void Session::minimizeWindow(Function<void (CommandResult&&)>&& completionHandler)
803{
804 if (!m_toplevelBrowsingContext) {
805 completionHandler(CommandResult::fail(CommandResult::ErrorCode::NoSuchWindow));
806 return;
807 }
808
809 handleUserPrompts([this, protectedThis = makeRef(*this), completionHandler = WTFMove(completionHandler)](CommandResult&& result) mutable {
810 if (result.isError()) {
811 completionHandler(WTFMove(result));
812 return;
813 }
814
815 RefPtr<JSON::Object> parameters = JSON::Object::create();
816 parameters->setString("handle"_s, m_toplevelBrowsingContext.value());
817 m_host->sendCommandToBackend("hideWindowOfBrowsingContext"_s, WTFMove(parameters), [this, protectedThis = protectedThis.copyRef(), completionHandler = WTFMove(completionHandler)] (SessionHost::CommandResponse&& response) mutable {
818 if (response.isError) {
819 completionHandler(CommandResult::fail(WTFMove(response.responseObject)));
820 return;
821 }
822 getToplevelBrowsingContextRect(WTFMove(completionHandler));
823 });
824 });
825}
826
827void Session::fullscreenWindow(Function<void (CommandResult&&)>&& completionHandler)
828{
829 if (!m_toplevelBrowsingContext) {
830 completionHandler(CommandResult::fail(CommandResult::ErrorCode::NoSuchWindow));
831 return;
832 }
833
834 handleUserPrompts([this, protectedThis = makeRef(*this), completionHandler = WTFMove(completionHandler)](CommandResult&& result) mutable {
835 if (result.isError()) {
836 completionHandler(WTFMove(result));
837 return;
838 }
839
840 RefPtr<JSON::Object> parameters = JSON::Object::create();
841 parameters->setString("browsingContextHandle"_s, m_toplevelBrowsingContext.value());
842 parameters->setString("function"_s, String(EnterFullscreenJavaScript, sizeof(EnterFullscreenJavaScript)));
843 parameters->setArray("arguments"_s, JSON::Array::create());
844 parameters->setBoolean("expectsImplicitCallbackArgument"_s, true);
845 m_host->sendCommandToBackend("evaluateJavaScriptFunction"_s, WTFMove(parameters), [this, protectedThis = protectedThis.copyRef(), completionHandler = WTFMove(completionHandler)](SessionHost::CommandResponse&& response) mutable {
846 if (response.isError || !response.responseObject) {
847 completionHandler(CommandResult::fail(WTFMove(response.responseObject)));
848 return;
849 }
850
851 String valueString;
852 if (!response.responseObject->getString("result"_s, valueString)) {
853 completionHandler(CommandResult::fail(CommandResult::ErrorCode::UnknownError));
854 return;
855 }
856 RefPtr<JSON::Value> resultValue;
857 if (!JSON::Value::parseJSON(valueString, resultValue)) {
858 completionHandler(CommandResult::fail(CommandResult::ErrorCode::UnknownError));
859 return;
860 }
861
862 getToplevelBrowsingContextRect(WTFMove(completionHandler));
863 });
864 });
865}
866
867RefPtr<JSON::Object> Session::createElement(RefPtr<JSON::Value>&& value)
868{
869 RefPtr<JSON::Object> valueObject;
870 if (!value->asObject(valueObject))
871 return nullptr;
872
873 String elementID;
874 if (!valueObject->getString("session-node-" + id(), elementID))
875 return nullptr;
876
877 RefPtr<JSON::Object> elementObject = JSON::Object::create();
878 elementObject->setString(webElementIdentifier(), elementID);
879 return elementObject;
880}
881
882RefPtr<JSON::Object> Session::createElement(const String& elementID)
883{
884 RefPtr<JSON::Object> elementObject = JSON::Object::create();
885 elementObject->setString("session-node-" + id(), elementID);
886 return elementObject;
887}
888
889RefPtr<JSON::Object> Session::extractElement(JSON::Value& value)
890{
891 String elementID = extractElementID(value);
892 return !elementID.isEmpty() ? createElement(elementID) : nullptr;
893}
894
895String Session::extractElementID(JSON::Value& value)
896{
897 RefPtr<JSON::Object> valueObject;
898 if (!value.asObject(valueObject))
899 return emptyString();
900
901 String elementID;
902 if (!valueObject->getString(webElementIdentifier(), elementID))
903 return emptyString();
904
905 return elementID;
906}
907
908void Session::computeElementLayout(const String& elementID, OptionSet<ElementLayoutOption> options, Function<void (Optional<Rect>&&, Optional<Point>&&, bool, RefPtr<JSON::Object>&&)>&& completionHandler)
909{
910 ASSERT(m_toplevelBrowsingContext.value());
911
912 RefPtr<JSON::Object> parameters = JSON::Object::create();
913 parameters->setString("browsingContextHandle"_s, m_toplevelBrowsingContext.value());
914 parameters->setString("frameHandle"_s, m_currentBrowsingContext.valueOr(emptyString()));
915 parameters->setString("nodeHandle"_s, elementID);
916 parameters->setBoolean("scrollIntoViewIfNeeded"_s, options.contains(ElementLayoutOption::ScrollIntoViewIfNeeded));
917 parameters->setString("coordinateSystem"_s, options.contains(ElementLayoutOption::UseViewportCoordinates) ? "LayoutViewport"_s : "Page"_s);
918 m_host->sendCommandToBackend("computeElementLayout"_s, WTFMove(parameters), [protectedThis = makeRef(*this), completionHandler = WTFMove(completionHandler)](SessionHost::CommandResponse&& response) mutable {
919 if (response.isError || !response.responseObject) {
920 completionHandler(WTF::nullopt, WTF::nullopt, false, WTFMove(response.responseObject));
921 return;
922 }
923 RefPtr<JSON::Object> rectObject;
924 if (!response.responseObject->getObject("rect"_s, rectObject)) {
925 completionHandler(WTF::nullopt, WTF::nullopt, false, nullptr);
926 return;
927 }
928 Optional<int> elementX;
929 Optional<int> elementY;
930 RefPtr<JSON::Object> elementPosition;
931 if (rectObject->getObject("origin"_s, elementPosition)) {
932 int x, y;
933 if (elementPosition->getInteger("x"_s, x) && elementPosition->getInteger("y"_s, y)) {
934 elementX = x;
935 elementY = y;
936 }
937 }
938 if (!elementX || !elementY) {
939 completionHandler(WTF::nullopt, WTF::nullopt, false, nullptr);
940 return;
941 }
942 Optional<int> elementWidth;
943 Optional<int> elementHeight;
944 RefPtr<JSON::Object> elementSize;
945 if (rectObject->getObject("size"_s, elementSize)) {
946 int width, height;
947 if (elementSize->getInteger("width"_s, width) && elementSize->getInteger("height"_s, height)) {
948 elementWidth = width;
949 elementHeight = height;
950 }
951 }
952 if (!elementWidth || !elementHeight) {
953 completionHandler(WTF::nullopt, WTF::nullopt, false, nullptr);
954 return;
955 }
956 Rect rect = { { elementX.value(), elementY.value() }, { elementWidth.value(), elementHeight.value() } };
957
958 bool isObscured;
959 if (!response.responseObject->getBoolean("isObscured"_s, isObscured)) {
960 completionHandler(WTF::nullopt, WTF::nullopt, false, nullptr);
961 return;
962 }
963 RefPtr<JSON::Object> inViewCenterPointObject;
964 if (!response.responseObject->getObject("inViewCenterPoint"_s, inViewCenterPointObject)) {
965 completionHandler(rect, WTF::nullopt, isObscured, nullptr);
966 return;
967 }
968 int inViewCenterPointX, inViewCenterPointY;
969 if (!inViewCenterPointObject->getInteger("x"_s, inViewCenterPointX)
970 || !inViewCenterPointObject->getInteger("y"_s, inViewCenterPointY)) {
971 completionHandler(WTF::nullopt, WTF::nullopt, isObscured, nullptr);
972 return;
973 }
974 Point inViewCenterPoint = { inViewCenterPointX, inViewCenterPointY };
975 completionHandler(rect, inViewCenterPoint, isObscured, nullptr);
976 });
977}
978
979void Session::findElements(const String& strategy, const String& selector, FindElementsMode mode, const String& rootElementID, Function<void (CommandResult&&)>&& completionHandler)
980{
981 if (!m_toplevelBrowsingContext) {
982 completionHandler(CommandResult::fail(CommandResult::ErrorCode::NoSuchWindow));
983 return;
984 }
985
986 handleUserPrompts([this, protectedThis = makeRef(*this), strategy, selector, mode, rootElementID, completionHandler = WTFMove(completionHandler)](CommandResult&& result) mutable {
987 if (result.isError()) {
988 completionHandler(WTFMove(result));
989 return;
990 }
991 RefPtr<JSON::Array> arguments = JSON::Array::create();
992 arguments->pushString(JSON::Value::create(strategy)->toJSONString());
993 if (rootElementID.isEmpty())
994 arguments->pushString(JSON::Value::null()->toJSONString());
995 else
996 arguments->pushString(createElement(rootElementID)->toJSONString());
997 arguments->pushString(JSON::Value::create(selector)->toJSONString());
998 arguments->pushString(JSON::Value::create(mode == FindElementsMode::Single)->toJSONString());
999 arguments->pushString(JSON::Value::create(m_implicitWaitTimeout.milliseconds())->toJSONString());
1000
1001 RefPtr<JSON::Object> parameters = JSON::Object::create();
1002 parameters->setString("browsingContextHandle"_s, m_toplevelBrowsingContext.value());
1003 if (m_currentBrowsingContext)
1004 parameters->setString("frameHandle"_s, m_currentBrowsingContext.value());
1005 parameters->setString("function"_s, String(FindNodesJavaScript, sizeof(FindNodesJavaScript)));
1006 parameters->setArray("arguments"_s, WTFMove(arguments));
1007 parameters->setBoolean("expectsImplicitCallbackArgument"_s, true);
1008 // If there's an implicit wait, use one second more as callback timeout.
1009 if (m_implicitWaitTimeout)
1010 parameters->setInteger("callbackTimeout"_s, Seconds(m_implicitWaitTimeout + 1_s).millisecondsAs<int>());
1011
1012 m_host->sendCommandToBackend("evaluateJavaScriptFunction"_s, WTFMove(parameters), [this, protectedThis = protectedThis.copyRef(), mode, completionHandler = WTFMove(completionHandler)](SessionHost::CommandResponse&& response) {
1013 if (response.isError || !response.responseObject) {
1014 completionHandler(CommandResult::fail(WTFMove(response.responseObject)));
1015 return;
1016 }
1017 String valueString;
1018 if (!response.responseObject->getString("result"_s, valueString)) {
1019 completionHandler(CommandResult::fail(CommandResult::ErrorCode::UnknownError));
1020 return;
1021 }
1022 RefPtr<JSON::Value> resultValue;
1023 if (!JSON::Value::parseJSON(valueString, resultValue)) {
1024 completionHandler(CommandResult::fail(CommandResult::ErrorCode::UnknownError));
1025 return;
1026 }
1027
1028 switch (mode) {
1029 case FindElementsMode::Single: {
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 break;
1037 }
1038 case FindElementsMode::Multiple: {
1039 RefPtr<JSON::Array> elementsArray;
1040 if (!resultValue->asArray(elementsArray)) {
1041 completionHandler(CommandResult::fail(CommandResult::ErrorCode::NoSuchElement));
1042 return;
1043 }
1044 RefPtr<JSON::Array> elementObjectsArray = JSON::Array::create();
1045 unsigned elementsArrayLength = elementsArray->length();
1046 for (unsigned i = 0; i < elementsArrayLength; ++i) {
1047 if (auto elementObject = createElement(elementsArray->get(i)))
1048 elementObjectsArray->pushObject(WTFMove(elementObject));
1049 }
1050 completionHandler(CommandResult::success(WTFMove(elementObjectsArray)));
1051 break;
1052 }
1053 }
1054 });
1055 });
1056}
1057
1058void Session::getActiveElement(Function<void (CommandResult&&)>&& completionHandler)
1059{
1060 if (!m_toplevelBrowsingContext) {
1061 completionHandler(CommandResult::fail(CommandResult::ErrorCode::NoSuchWindow));
1062 return;
1063 }
1064
1065 handleUserPrompts([this, protectedThis = makeRef(*this), completionHandler = WTFMove(completionHandler)](CommandResult&& result) mutable {
1066 if (result.isError()) {
1067 completionHandler(WTFMove(result));
1068 return;
1069 }
1070 RefPtr<JSON::Object> parameters = JSON::Object::create();
1071 parameters->setString("browsingContextHandle"_s, m_toplevelBrowsingContext.value());
1072 parameters->setString("function"_s, "function() { return document.activeElement; }"_s);
1073 parameters->setArray("arguments"_s, JSON::Array::create());
1074 m_host->sendCommandToBackend("evaluateJavaScriptFunction"_s, WTFMove(parameters), [this, protectedThis = protectedThis.copyRef(), completionHandler = WTFMove(completionHandler)](SessionHost::CommandResponse&& response) {
1075 if (response.isError || !response.responseObject) {
1076 completionHandler(CommandResult::fail(WTFMove(response.responseObject)));
1077 return;
1078 }
1079 String valueString;
1080 if (!response.responseObject->getString("result"_s, valueString)) {
1081 completionHandler(CommandResult::fail(CommandResult::ErrorCode::UnknownError));
1082 return;
1083 }
1084 RefPtr<JSON::Value> resultValue;
1085 if (!JSON::Value::parseJSON(valueString, resultValue)) {
1086 completionHandler(CommandResult::fail(CommandResult::ErrorCode::UnknownError));
1087 return;
1088 }
1089 RefPtr<JSON::Object> elementObject = createElement(WTFMove(resultValue));
1090 if (!elementObject) {
1091 completionHandler(CommandResult::fail(CommandResult::ErrorCode::NoSuchElement));
1092 return;
1093 }
1094 completionHandler(CommandResult::success(WTFMove(elementObject)));
1095 });
1096 });
1097}
1098
1099void Session::isElementSelected(const String& elementID, Function<void (CommandResult&&)>&& completionHandler)
1100{
1101 if (!m_toplevelBrowsingContext) {
1102 completionHandler(CommandResult::fail(CommandResult::ErrorCode::NoSuchWindow));
1103 return;
1104 }
1105
1106 handleUserPrompts([this, protectedThis = makeRef(*this), elementID, completionHandler = WTFMove(completionHandler)](CommandResult&& result) mutable {
1107 if (result.isError()) {
1108 completionHandler(WTFMove(result));
1109 return;
1110 }
1111 RefPtr<JSON::Array> arguments = JSON::Array::create();
1112 arguments->pushString(createElement(elementID)->toJSONString());
1113 arguments->pushString(JSON::Value::create("selected")->toJSONString());
1114
1115 RefPtr<JSON::Object> parameters = JSON::Object::create();
1116 parameters->setString("browsingContextHandle"_s, m_toplevelBrowsingContext.value());
1117 if (m_currentBrowsingContext)
1118 parameters->setString("frameHandle"_s, m_currentBrowsingContext.value());
1119 parameters->setString("function"_s, String(ElementAttributeJavaScript, sizeof(ElementAttributeJavaScript)));
1120 parameters->setArray("arguments"_s, WTFMove(arguments));
1121 m_host->sendCommandToBackend("evaluateJavaScriptFunction"_s, WTFMove(parameters), [protectedThis = protectedThis.copyRef(), completionHandler = WTFMove(completionHandler)](SessionHost::CommandResponse&& response) {
1122 if (response.isError || !response.responseObject) {
1123 completionHandler(CommandResult::fail(WTFMove(response.responseObject)));
1124 return;
1125 }
1126 String valueString;
1127 if (!response.responseObject->getString("result"_s, valueString)) {
1128 completionHandler(CommandResult::fail(CommandResult::ErrorCode::UnknownError));
1129 return;
1130 }
1131 RefPtr<JSON::Value> resultValue;
1132 if (!JSON::Value::parseJSON(valueString, resultValue)) {
1133 completionHandler(CommandResult::fail(CommandResult::ErrorCode::UnknownError));
1134 return;
1135 }
1136 if (resultValue->isNull()) {
1137 completionHandler(CommandResult::success(JSON::Value::create(false)));
1138 return;
1139 }
1140 String booleanResult;
1141 if (!resultValue->asString(booleanResult) || booleanResult != "true") {
1142 completionHandler(CommandResult::fail(CommandResult::ErrorCode::UnknownError));
1143 return;
1144 }
1145 completionHandler(CommandResult::success(JSON::Value::create(true)));
1146 });
1147 });
1148}
1149
1150void Session::getElementText(const String& elementID, Function<void (CommandResult&&)>&& completionHandler)
1151{
1152 if (!m_toplevelBrowsingContext) {
1153 completionHandler(CommandResult::fail(CommandResult::ErrorCode::NoSuchWindow));
1154 return;
1155 }
1156
1157 handleUserPrompts([this, protectedThis = makeRef(*this), elementID, completionHandler = WTFMove(completionHandler)](CommandResult&& result) mutable {
1158 if (result.isError()) {
1159 completionHandler(WTFMove(result));
1160 return;
1161 }
1162 RefPtr<JSON::Array> arguments = JSON::Array::create();
1163 arguments->pushString(createElement(elementID)->toJSONString());
1164
1165 RefPtr<JSON::Object> parameters = JSON::Object::create();
1166 parameters->setString("browsingContextHandle"_s, m_toplevelBrowsingContext.value());
1167 if (m_currentBrowsingContext)
1168 parameters->setString("frameHandle"_s, m_currentBrowsingContext.value());
1169 // FIXME: Add an atom to properly implement this instead of just using innerText.
1170 parameters->setString("function"_s, "function(element) { return element.innerText.replace(/^[^\\S\\xa0]+|[^\\S\\xa0]+$/g, '') }"_s);
1171 parameters->setArray("arguments"_s, WTFMove(arguments));
1172 m_host->sendCommandToBackend("evaluateJavaScriptFunction"_s, WTFMove(parameters), [protectedThis = protectedThis.copyRef(), completionHandler = WTFMove(completionHandler)](SessionHost::CommandResponse&& response) {
1173 if (response.isError || !response.responseObject) {
1174 completionHandler(CommandResult::fail(WTFMove(response.responseObject)));
1175 return;
1176 }
1177 String valueString;
1178 if (!response.responseObject->getString("result"_s, valueString)) {
1179 completionHandler(CommandResult::fail(CommandResult::ErrorCode::UnknownError));
1180 return;
1181 }
1182 RefPtr<JSON::Value> resultValue;
1183 if (!JSON::Value::parseJSON(valueString, resultValue)) {
1184 completionHandler(CommandResult::fail(CommandResult::ErrorCode::UnknownError));
1185 return;
1186 }
1187 completionHandler(CommandResult::success(WTFMove(resultValue)));
1188 });
1189 });
1190}
1191
1192void Session::getElementTagName(const String& elementID, Function<void (CommandResult&&)>&& completionHandler)
1193{
1194 if (!m_toplevelBrowsingContext) {
1195 completionHandler(CommandResult::fail(CommandResult::ErrorCode::NoSuchWindow));
1196 return;
1197 }
1198
1199 handleUserPrompts([this, protectedThis = makeRef(*this), elementID, completionHandler = WTFMove(completionHandler)](CommandResult&& result) mutable {
1200 if (result.isError()) {
1201 completionHandler(WTFMove(result));
1202 return;
1203 }
1204 RefPtr<JSON::Array> arguments = JSON::Array::create();
1205 arguments->pushString(createElement(elementID)->toJSONString());
1206
1207 RefPtr<JSON::Object> parameters = JSON::Object::create();
1208 parameters->setString("browsingContextHandle"_s, m_toplevelBrowsingContext.value());
1209 if (m_currentBrowsingContext)
1210 parameters->setString("frameHandle"_s, m_currentBrowsingContext.value());
1211 parameters->setString("function"_s, "function(element) { return element.tagName.toLowerCase() }"_s);
1212 parameters->setArray("arguments"_s, WTFMove(arguments));
1213 m_host->sendCommandToBackend("evaluateJavaScriptFunction"_s, WTFMove(parameters), [protectedThis = protectedThis.copyRef(), completionHandler = WTFMove(completionHandler)](SessionHost::CommandResponse&& response) {
1214 if (response.isError || !response.responseObject) {
1215 completionHandler(CommandResult::fail(WTFMove(response.responseObject)));
1216 return;
1217 }
1218 String valueString;
1219 if (!response.responseObject->getString("result"_s, valueString)) {
1220 completionHandler(CommandResult::fail(CommandResult::ErrorCode::UnknownError));
1221 return;
1222 }
1223 RefPtr<JSON::Value> resultValue;
1224 if (!JSON::Value::parseJSON(valueString, resultValue)) {
1225 completionHandler(CommandResult::fail(CommandResult::ErrorCode::UnknownError));
1226 return;
1227 }
1228 completionHandler(CommandResult::success(WTFMove(resultValue)));
1229 });
1230 });
1231}
1232
1233void Session::getElementRect(const String& elementID, Function<void (CommandResult&&)>&& completionHandler)
1234{
1235 if (!m_toplevelBrowsingContext) {
1236 completionHandler(CommandResult::fail(CommandResult::ErrorCode::NoSuchWindow));
1237 return;
1238 }
1239
1240 handleUserPrompts([this, protectedThis = makeRef(*this), elementID, completionHandler = WTFMove(completionHandler)](CommandResult&& result) mutable {
1241 if (result.isError()) {
1242 completionHandler(WTFMove(result));
1243 return;
1244 }
1245 computeElementLayout(elementID, { }, [protectedThis = protectedThis.copyRef(), completionHandler = WTFMove(completionHandler)](Optional<Rect>&& rect, Optional<Point>&&, bool, RefPtr<JSON::Object>&& error) {
1246 if (!rect || error) {
1247 completionHandler(CommandResult::fail(WTFMove(error)));
1248 return;
1249 }
1250 RefPtr<JSON::Object> rectObject = JSON::Object::create();
1251 rectObject->setInteger("x"_s, rect.value().origin.x);
1252 rectObject->setInteger("y"_s, rect.value().origin.y);
1253 rectObject->setInteger("width"_s, rect.value().size.width);
1254 rectObject->setInteger("height"_s, rect.value().size.height);
1255 completionHandler(CommandResult::success(WTFMove(rectObject)));
1256 });
1257 });
1258}
1259
1260void Session::isElementEnabled(const String& elementID, Function<void (CommandResult&&)>&& completionHandler)
1261{
1262 if (!m_toplevelBrowsingContext) {
1263 completionHandler(CommandResult::fail(CommandResult::ErrorCode::NoSuchWindow));
1264 return;
1265 }
1266
1267 handleUserPrompts([this, protectedThis = makeRef(*this), elementID, completionHandler = WTFMove(completionHandler)](CommandResult&& result) mutable {
1268 if (result.isError()) {
1269 completionHandler(WTFMove(result));
1270 return;
1271 }
1272 RefPtr<JSON::Array> arguments = JSON::Array::create();
1273 arguments->pushString(createElement(elementID)->toJSONString());
1274
1275 RefPtr<JSON::Object> parameters = JSON::Object::create();
1276 parameters->setString("browsingContextHandle"_s, m_toplevelBrowsingContext.value());
1277 if (m_currentBrowsingContext)
1278 parameters->setString("frameHandle"_s, m_currentBrowsingContext.value());
1279 parameters->setString("function"_s, "function(element) { return element.disabled === undefined ? true : !element.disabled }"_s);
1280 parameters->setArray("arguments"_s, WTFMove(arguments));
1281 m_host->sendCommandToBackend("evaluateJavaScriptFunction"_s, WTFMove(parameters), [protectedThis = protectedThis.copyRef(), completionHandler = WTFMove(completionHandler)](SessionHost::CommandResponse&& response) {
1282 if (response.isError || !response.responseObject) {
1283 completionHandler(CommandResult::fail(WTFMove(response.responseObject)));
1284 return;
1285 }
1286 String valueString;
1287 if (!response.responseObject->getString("result"_s, valueString)) {
1288 completionHandler(CommandResult::fail(CommandResult::ErrorCode::UnknownError));
1289 return;
1290 }
1291 RefPtr<JSON::Value> resultValue;
1292 if (!JSON::Value::parseJSON(valueString, resultValue)) {
1293 completionHandler(CommandResult::fail(CommandResult::ErrorCode::UnknownError));
1294 return;
1295 }
1296 completionHandler(CommandResult::success(WTFMove(resultValue)));
1297 });
1298 });
1299}
1300
1301void Session::isElementDisplayed(const String& elementID, Function<void (CommandResult&&)>&& completionHandler)
1302{
1303 if (!m_toplevelBrowsingContext) {
1304 completionHandler(CommandResult::fail(CommandResult::ErrorCode::NoSuchWindow));
1305 return;
1306 }
1307
1308 handleUserPrompts([this, protectedThis = makeRef(*this), elementID, completionHandler = WTFMove(completionHandler)](CommandResult&& result) mutable {
1309 if (result.isError()) {
1310 completionHandler(WTFMove(result));
1311 return;
1312 }
1313 RefPtr<JSON::Array> arguments = JSON::Array::create();
1314 arguments->pushString(createElement(elementID)->toJSONString());
1315
1316 RefPtr<JSON::Object> parameters = JSON::Object::create();
1317 parameters->setString("browsingContextHandle"_s, m_toplevelBrowsingContext.value());
1318 if (m_currentBrowsingContext)
1319 parameters->setString("frameHandle"_s, m_currentBrowsingContext.value());
1320 parameters->setString("function"_s, String(ElementDisplayedJavaScript, sizeof(ElementDisplayedJavaScript)));
1321 parameters->setArray("arguments"_s, WTFMove(arguments));
1322 m_host->sendCommandToBackend("evaluateJavaScriptFunction"_s, WTFMove(parameters), [protectedThis = protectedThis.copyRef(), completionHandler = WTFMove(completionHandler)](SessionHost::CommandResponse&& response) {
1323 if (response.isError || !response.responseObject) {
1324 completionHandler(CommandResult::fail(WTFMove(response.responseObject)));
1325 return;
1326 }
1327 String valueString;
1328 if (!response.responseObject->getString("result"_s, valueString)) {
1329 completionHandler(CommandResult::fail(CommandResult::ErrorCode::UnknownError));
1330 return;
1331 }
1332 RefPtr<JSON::Value> resultValue;
1333 if (!JSON::Value::parseJSON(valueString, resultValue)) {
1334 completionHandler(CommandResult::fail(CommandResult::ErrorCode::UnknownError));
1335 return;
1336 }
1337 completionHandler(CommandResult::success(WTFMove(resultValue)));
1338 });
1339 });
1340}
1341
1342void Session::getElementAttribute(const String& elementID, const String& attribute, Function<void (CommandResult&&)>&& completionHandler)
1343{
1344 if (!m_toplevelBrowsingContext) {
1345 completionHandler(CommandResult::fail(CommandResult::ErrorCode::NoSuchWindow));
1346 return;
1347 }
1348
1349 handleUserPrompts([this, protectedThis = makeRef(*this), elementID, attribute, completionHandler = WTFMove(completionHandler)](CommandResult&& result) mutable {
1350 if (result.isError()) {
1351 completionHandler(WTFMove(result));
1352 return;
1353 }
1354 RefPtr<JSON::Array> arguments = JSON::Array::create();
1355 arguments->pushString(createElement(elementID)->toJSONString());
1356 arguments->pushString(JSON::Value::create(attribute)->toJSONString());
1357
1358 RefPtr<JSON::Object> parameters = JSON::Object::create();
1359 parameters->setString("browsingContextHandle"_s, m_toplevelBrowsingContext.value());
1360 if (m_currentBrowsingContext)
1361 parameters->setString("frameHandle"_s, m_currentBrowsingContext.value());
1362 parameters->setString("function"_s, String(ElementAttributeJavaScript, sizeof(ElementAttributeJavaScript)));
1363 parameters->setArray("arguments"_s, WTFMove(arguments));
1364 m_host->sendCommandToBackend("evaluateJavaScriptFunction"_s, WTFMove(parameters), [protectedThis = protectedThis.copyRef(), completionHandler = WTFMove(completionHandler)](SessionHost::CommandResponse&& response) {
1365 if (response.isError || !response.responseObject) {
1366 completionHandler(CommandResult::fail(WTFMove(response.responseObject)));
1367 return;
1368 }
1369 String valueString;
1370 if (!response.responseObject->getString("result"_s, valueString)) {
1371 completionHandler(CommandResult::fail(CommandResult::ErrorCode::UnknownError));
1372 return;
1373 }
1374 RefPtr<JSON::Value> resultValue;
1375 if (!JSON::Value::parseJSON(valueString, resultValue)) {
1376 completionHandler(CommandResult::fail(CommandResult::ErrorCode::UnknownError));
1377 return;
1378 }
1379 completionHandler(CommandResult::success(WTFMove(resultValue)));
1380 });
1381 });
1382}
1383
1384void Session::getElementProperty(const String& elementID, const String& property, Function<void (CommandResult&&)>&& completionHandler)
1385{
1386 if (!m_toplevelBrowsingContext) {
1387 completionHandler(CommandResult::fail(CommandResult::ErrorCode::NoSuchWindow));
1388 return;
1389 }
1390
1391 handleUserPrompts([this, protectedThis = makeRef(*this), elementID, property, completionHandler = WTFMove(completionHandler)](CommandResult&& result) mutable {
1392 if (result.isError()) {
1393 completionHandler(WTFMove(result));
1394 return;
1395 }
1396 RefPtr<JSON::Array> arguments = JSON::Array::create();
1397 arguments->pushString(createElement(elementID)->toJSONString());
1398
1399 RefPtr<JSON::Object> parameters = JSON::Object::create();
1400 parameters->setString("browsingContextHandle"_s, m_toplevelBrowsingContext.value());
1401 if (m_currentBrowsingContext)
1402 parameters->setString("frameHandle"_s, m_currentBrowsingContext.value());
1403 parameters->setString("function"_s, makeString("function(element) { return element.", property, "; }"));
1404 parameters->setArray("arguments"_s, WTFMove(arguments));
1405 m_host->sendCommandToBackend("evaluateJavaScriptFunction"_s, WTFMove(parameters), [protectedThis = protectedThis.copyRef(), completionHandler = WTFMove(completionHandler)](SessionHost::CommandResponse&& response) {
1406 if (response.isError || !response.responseObject) {
1407 completionHandler(CommandResult::fail(WTFMove(response.responseObject)));
1408 return;
1409 }
1410 String valueString;
1411 if (!response.responseObject->getString("result"_s, valueString)) {
1412 completionHandler(CommandResult::fail(CommandResult::ErrorCode::UnknownError));
1413 return;
1414 }
1415 RefPtr<JSON::Value> resultValue;
1416 if (!JSON::Value::parseJSON(valueString, resultValue)) {
1417 completionHandler(CommandResult::fail(CommandResult::ErrorCode::UnknownError));
1418 return;
1419 }
1420 completionHandler(CommandResult::success(WTFMove(resultValue)));
1421 });
1422 });
1423}
1424
1425void Session::getElementCSSValue(const String& elementID, const String& cssProperty, Function<void (CommandResult&&)>&& completionHandler)
1426{
1427 if (!m_toplevelBrowsingContext) {
1428 completionHandler(CommandResult::fail(CommandResult::ErrorCode::NoSuchWindow));
1429 return;
1430 }
1431
1432 handleUserPrompts([this, protectedThis = makeRef(*this), elementID, cssProperty, completionHandler = WTFMove(completionHandler)](CommandResult&& result) mutable {
1433 if (result.isError()) {
1434 completionHandler(WTFMove(result));
1435 return;
1436 }
1437 RefPtr<JSON::Array> arguments = JSON::Array::create();
1438 arguments->pushString(createElement(elementID)->toJSONString());
1439
1440 RefPtr<JSON::Object> parameters = JSON::Object::create();
1441 parameters->setString("browsingContextHandle"_s, m_toplevelBrowsingContext.value());
1442 if (m_currentBrowsingContext)
1443 parameters->setString("frameHandle"_s, m_currentBrowsingContext.value());
1444 parameters->setString("function"_s, makeString("function(element) { return document.defaultView.getComputedStyle(element).getPropertyValue('", cssProperty, "'); }"));
1445 parameters->setArray("arguments"_s, WTFMove(arguments));
1446 m_host->sendCommandToBackend("evaluateJavaScriptFunction"_s, WTFMove(parameters), [protectedThis = protectedThis.copyRef(), completionHandler = WTFMove(completionHandler)](SessionHost::CommandResponse&& response) {
1447 if (response.isError || !response.responseObject) {
1448 completionHandler(CommandResult::fail(WTFMove(response.responseObject)));
1449 return;
1450 }
1451 String valueString;
1452 if (!response.responseObject->getString("result"_s, valueString)) {
1453 completionHandler(CommandResult::fail(CommandResult::ErrorCode::UnknownError));
1454 return;
1455 }
1456 RefPtr<JSON::Value> resultValue;
1457 if (!JSON::Value::parseJSON(valueString, resultValue)) {
1458 completionHandler(CommandResult::fail(CommandResult::ErrorCode::UnknownError));
1459 return;
1460 }
1461 completionHandler(CommandResult::success(WTFMove(resultValue)));
1462 });
1463 });
1464}
1465
1466void Session::waitForNavigationToComplete(Function<void (CommandResult&&)>&& completionHandler)
1467{
1468 if (!m_toplevelBrowsingContext) {
1469 completionHandler(CommandResult::fail(CommandResult::ErrorCode::NoSuchWindow));
1470 return;
1471 }
1472
1473 RefPtr<JSON::Object> parameters = JSON::Object::create();
1474 parameters->setString("browsingContextHandle"_s, m_toplevelBrowsingContext.value());
1475 if (m_currentBrowsingContext)
1476 parameters->setString("frameHandle"_s, m_currentBrowsingContext.value());
1477 parameters->setInteger("pageLoadTimeout"_s, m_pageLoadTimeout.millisecondsAs<int>());
1478 if (auto pageLoadStrategy = pageLoadStrategyString())
1479 parameters->setString("pageLoadStrategy"_s, pageLoadStrategy.value());
1480 m_host->sendCommandToBackend("waitForNavigationToComplete"_s, WTFMove(parameters), [this, protectedThis = makeRef(*this), completionHandler = WTFMove(completionHandler)](SessionHost::CommandResponse&& response) {
1481 if (response.isError) {
1482 auto result = CommandResult::fail(WTFMove(response.responseObject));
1483 switch (result.errorCode()) {
1484 case CommandResult::ErrorCode::NoSuchWindow:
1485 // Window was closed, reset the top level browsing context and ignore the error.
1486 m_toplevelBrowsingContext = WTF::nullopt;
1487 break;
1488 case CommandResult::ErrorCode::NoSuchFrame:
1489 // Navigation destroyed the current frame, switch to top level browsing context and ignore the error.
1490 switchToBrowsingContext(WTF::nullopt);
1491 break;
1492 default:
1493 completionHandler(WTFMove(result));
1494 return;
1495 }
1496 }
1497 completionHandler(CommandResult::success());
1498 });
1499}
1500
1501void Session::selectOptionElement(const String& elementID, Function<void (CommandResult&&)>&& completionHandler)
1502{
1503 RefPtr<JSON::Object> parameters = JSON::Object::create();
1504 parameters->setString("browsingContextHandle"_s, m_toplevelBrowsingContext.value());
1505 parameters->setString("frameHandle"_s, m_currentBrowsingContext.valueOr(emptyString()));
1506 parameters->setString("nodeHandle"_s, elementID);
1507 m_host->sendCommandToBackend("selectOptionElement"_s, WTFMove(parameters), [protectedThis = makeRef(*this), completionHandler = WTFMove(completionHandler)](SessionHost::CommandResponse&& response) {
1508 if (response.isError) {
1509 completionHandler(CommandResult::fail(WTFMove(response.responseObject)));
1510 return;
1511 }
1512 completionHandler(CommandResult::success());
1513 });
1514}
1515
1516void Session::elementClick(const String& elementID, Function<void (CommandResult&&)>&& completionHandler)
1517{
1518 if (!m_toplevelBrowsingContext) {
1519 completionHandler(CommandResult::fail(CommandResult::ErrorCode::NoSuchWindow));
1520 return;
1521 }
1522
1523 handleUserPrompts([this, protectedThis = makeRef(*this), elementID, completionHandler = WTFMove(completionHandler)](CommandResult&& result) mutable {
1524 if (result.isError()) {
1525 completionHandler(WTFMove(result));
1526 return;
1527 }
1528 OptionSet<ElementLayoutOption> options = { ElementLayoutOption::ScrollIntoViewIfNeeded, ElementLayoutOption::UseViewportCoordinates };
1529 computeElementLayout(elementID, options, [this, protectedThis = protectedThis.copyRef(), elementID, completionHandler = WTFMove(completionHandler)](Optional<Rect>&& rect, Optional<Point>&& inViewCenter, bool isObscured, RefPtr<JSON::Object>&& error) mutable {
1530 if (!rect || error) {
1531 completionHandler(CommandResult::fail(WTFMove(error)));
1532 return;
1533 }
1534 if (isObscured) {
1535 completionHandler(CommandResult::fail(CommandResult::ErrorCode::ElementClickIntercepted));
1536 return;
1537 }
1538 if (!inViewCenter) {
1539 completionHandler(CommandResult::fail(CommandResult::ErrorCode::ElementNotInteractable));
1540 return;
1541 }
1542
1543 getElementTagName(elementID, [this, elementID, inViewCenter = WTFMove(inViewCenter), completionHandler = WTFMove(completionHandler)](CommandResult&& result) mutable {
1544 bool isOptionElement = false;
1545 if (!result.isError()) {
1546 String tagName;
1547 if (result.result()->asString(tagName))
1548 isOptionElement = tagName == "option";
1549 }
1550
1551 Function<void (CommandResult&&)> continueAfterClickFunction = [this, completionHandler = WTFMove(completionHandler)](CommandResult&& result) mutable {
1552 if (result.isError()) {
1553 completionHandler(WTFMove(result));
1554 return;
1555 }
1556
1557 waitForNavigationToComplete(WTFMove(completionHandler));
1558 };
1559 if (isOptionElement)
1560 selectOptionElement(elementID, WTFMove(continueAfterClickFunction));
1561 else
1562 performMouseInteraction(inViewCenter.value().x, inViewCenter.value().y, MouseButton::Left, MouseInteraction::SingleClick, WTFMove(continueAfterClickFunction));
1563 });
1564 });
1565 });
1566}
1567
1568void Session::elementClear(const String& elementID, Function<void (CommandResult&&)>&& completionHandler)
1569{
1570 if (!m_toplevelBrowsingContext) {
1571 completionHandler(CommandResult::fail(CommandResult::