2 * Copyright (C) 2017 Igalia S.L.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
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.
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.
29 #include "CommandResult.h"
30 #include "SessionHost.h"
31 #include "WebDriverAtoms.h"
32 #include <wtf/CryptographicallyRandomNumber.h>
33 #include <wtf/HexNumber.h>
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");
42 Session::Session(std::unique_ptr<SessionHost>&& host)
43 : m_host(WTFMove(host))
44 , m_id(createCanonicalUUIDString())
52 const Capabilities& Session::capabilities() const
54 return m_host->capabilities();
57 static std::optional<String> firstWindowHandleInResult(JSON::Value& result)
59 RefPtr<JSON::Array> handles;
60 if (result.asArray(handles) && handles->length()) {
61 auto handleValue = handles->get(0);
63 if (handleValue->asString(handle))
69 void Session::closeAllToplevelBrowsingContexts(const String& toplevelBrowsingContext, Function<void (CommandResult&&)>&& completionHandler)
71 closeTopLevelBrowsingContext(toplevelBrowsingContext, [this, protectedThis = makeRef(*this), completionHandler = WTFMove(completionHandler)](CommandResult&& result) mutable {
72 if (result.isError()) {
73 completionHandler(WTFMove(result));
76 if (auto handle = firstWindowHandleInResult(*result.result())) {
77 closeAllToplevelBrowsingContexts(handle.value(), WTFMove(completionHandler));
80 completionHandler(CommandResult::success());
84 void Session::close(Function<void (CommandResult&&)>&& completionHandler)
86 m_toplevelBrowsingContext = std::nullopt;
87 getWindowHandles([this, completionHandler = WTFMove(completionHandler)](CommandResult&& result) mutable {
88 if (result.isError()) {
89 completionHandler(WTFMove(result));
92 if (auto handle = firstWindowHandleInResult(*result.result())) {
93 closeAllToplevelBrowsingContexts(handle.value(), WTFMove(completionHandler));
96 completionHandler(CommandResult::success());
100 void Session::setTimeouts(const Timeouts& timeouts, Function<void (CommandResult&&)>&& completionHandler)
103 m_timeouts.script = timeouts.script;
104 if (timeouts.pageLoad)
105 m_timeouts.pageLoad = timeouts.pageLoad;
106 if (timeouts.implicit)
107 m_timeouts.implicit = timeouts.implicit;
108 completionHandler(CommandResult::success());
111 void Session::switchToTopLevelBrowsingContext(std::optional<String> toplevelBrowsingContext)
113 m_toplevelBrowsingContext = toplevelBrowsingContext;
114 m_currentBrowsingContext = std::nullopt;
117 void Session::switchToBrowsingContext(std::optional<String> browsingContext)
119 // Automation sends empty strings for main frame.
120 if (!browsingContext || browsingContext.value().isEmpty())
121 m_currentBrowsingContext = std::nullopt;
123 m_currentBrowsingContext = browsingContext;
126 std::optional<String> Session::pageLoadStrategyString() const
128 if (!capabilities().pageLoadStrategy)
131 switch (capabilities().pageLoadStrategy.value()) {
132 case PageLoadStrategy::None:
133 return String("None");
134 case PageLoadStrategy::Normal:
135 return String("Normal");
136 case PageLoadStrategy::Eager:
137 return String("Eager");
143 void Session::createTopLevelBrowsingContext(Function<void (CommandResult&&)>&& completionHandler)
145 ASSERT(!m_toplevelBrowsingContext.value());
146 m_host->startAutomationSession(m_id, [this, protectedThis = makeRef(*this), completionHandler = WTFMove(completionHandler)](std::optional<String> errorMessage) mutable {
148 completionHandler(CommandResult::fail(CommandResult::ErrorCode::UnknownError, errorMessage.value()));
151 m_host->sendCommandToBackend(ASCIILiteral("createBrowsingContext"), nullptr, [this, protectedThis = makeRef(*this), completionHandler = WTFMove(completionHandler)](SessionHost::CommandResponse&& response) mutable {
152 if (response.isError || !response.responseObject) {
153 completionHandler(CommandResult::fail(WTFMove(response.responseObject)));
157 if (!response.responseObject->getString(ASCIILiteral("handle"), handle)) {
158 completionHandler(CommandResult::fail(CommandResult::ErrorCode::UnknownError));
161 switchToTopLevelBrowsingContext(handle);
162 completionHandler(CommandResult::success());
167 void Session::handleUserPrompts(Function<void (CommandResult&&)>&& completionHandler)
169 RefPtr<JSON::Object> parameters = JSON::Object::create();
170 parameters->setString(ASCIILiteral("browsingContextHandle"), m_toplevelBrowsingContext.value());
171 m_host->sendCommandToBackend(ASCIILiteral("isShowingJavaScriptDialog"), WTFMove(parameters), [this, protectedThis = makeRef(*this), completionHandler = WTFMove(completionHandler)](SessionHost::CommandResponse&& response) mutable {
172 if (response.isError || !response.responseObject) {
173 completionHandler(CommandResult::fail(WTFMove(response.responseObject)));
176 bool isShowingJavaScriptDialog;
177 if (!response.responseObject->getBoolean("result", isShowingJavaScriptDialog)) {
178 completionHandler(CommandResult::fail(CommandResult::ErrorCode::UnknownError));
182 if (!isShowingJavaScriptDialog) {
183 completionHandler(CommandResult::success());
187 handleUnexpectedAlertOpen(WTFMove(completionHandler));
191 void Session::handleUnexpectedAlertOpen(Function<void (CommandResult&&)>&& completionHandler)
193 if (!capabilities().unhandledPromptBehavior) {
194 reportUnexpectedAlertOpen([this, completionHandler = WTFMove(completionHandler)](CommandResult&& result) mutable {
195 dismissAlert([this, errorResult = WTFMove(result), completionHandler = WTFMove(completionHandler)](CommandResult&& result) mutable {
196 if (result.isError()) {
197 completionHandler(WTFMove(result));
200 completionHandler(WTFMove(errorResult));
206 switch (capabilities().unhandledPromptBehavior.value()) {
207 case UnhandledPromptBehavior::Dismiss:
208 dismissAlert(WTFMove(completionHandler));
210 case UnhandledPromptBehavior::Accept:
211 acceptAlert(WTFMove(completionHandler));
213 case UnhandledPromptBehavior::Ignore:
214 reportUnexpectedAlertOpen(WTFMove(completionHandler));
219 void Session::reportUnexpectedAlertOpen(Function<void (CommandResult&&)>&& completionHandler)
221 getAlertText([this, completionHandler = WTFMove(completionHandler)](CommandResult&& result) {
222 std::optional<String> alertText;
223 if (!result.isError()) {
225 if (result.result()->asString(valueString))
226 alertText = valueString;
228 auto errorResult = CommandResult::fail(CommandResult::ErrorCode::UnexpectedAlertOpen);
230 RefPtr<JSON::Object> additonalData = JSON::Object::create();
231 additonalData->setString(ASCIILiteral("text"), alertText.value());
232 errorResult.setAdditionalErrorData(WTFMove(additonalData));
234 completionHandler(WTFMove(errorResult));
238 void Session::go(const String& url, Function<void (CommandResult&&)>&& completionHandler)
240 if (!m_toplevelBrowsingContext) {
241 completionHandler(CommandResult::fail(CommandResult::ErrorCode::NoSuchWindow));
245 handleUserPrompts([this, url, completionHandler = WTFMove(completionHandler)](CommandResult&& result) mutable {
246 if (result.isError()) {
247 completionHandler(WTFMove(result));
251 RefPtr<JSON::Object> parameters = JSON::Object::create();
252 parameters->setString(ASCIILiteral("handle"), m_toplevelBrowsingContext.value());
253 parameters->setString(ASCIILiteral("url"), url);
254 if (m_timeouts.pageLoad)
255 parameters->setInteger(ASCIILiteral("pageLoadTimeout"), m_timeouts.pageLoad.value().millisecondsAs<int>());
256 if (auto pageLoadStrategy = pageLoadStrategyString())
257 parameters->setString(ASCIILiteral("pageLoadStrategy"), pageLoadStrategy.value());
258 m_host->sendCommandToBackend(ASCIILiteral("navigateBrowsingContext"), WTFMove(parameters), [this, protectedThis = makeRef(*this), completionHandler = WTFMove(completionHandler)](SessionHost::CommandResponse&& response) {
259 if (response.isError) {
260 completionHandler(CommandResult::fail(WTFMove(response.responseObject)));
263 switchToBrowsingContext(std::nullopt);
264 completionHandler(CommandResult::success());
269 void Session::getCurrentURL(Function<void (CommandResult&&)>&& completionHandler)
271 if (!m_toplevelBrowsingContext) {
272 completionHandler(CommandResult::fail(CommandResult::ErrorCode::NoSuchWindow));
276 handleUserPrompts([this, completionHandler = WTFMove(completionHandler)](CommandResult&& result) mutable {
277 if (result.isError()) {
278 completionHandler(WTFMove(result));
282 RefPtr<JSON::Object> parameters = JSON::Object::create();
283 parameters->setString(ASCIILiteral("handle"), m_toplevelBrowsingContext.value());
284 m_host->sendCommandToBackend(ASCIILiteral("getBrowsingContext"), WTFMove(parameters), [this, protectedThis = makeRef(*this), completionHandler = WTFMove(completionHandler)](SessionHost::CommandResponse&& response) {
285 if (response.isError || !response.responseObject) {
286 completionHandler(CommandResult::fail(WTFMove(response.responseObject)));
289 RefPtr<JSON::Object> browsingContext;
290 if (!response.responseObject->getObject("context", browsingContext)) {
291 completionHandler(CommandResult::fail(CommandResult::ErrorCode::UnknownError));
295 if (!browsingContext->getString("url", url)) {
296 completionHandler(CommandResult::fail(CommandResult::ErrorCode::UnknownError));
299 completionHandler(CommandResult::success(JSON::Value::create(url)));
304 void Session::back(Function<void (CommandResult&&)>&& completionHandler)
306 if (!m_toplevelBrowsingContext) {
307 completionHandler(CommandResult::fail(CommandResult::ErrorCode::NoSuchWindow));
311 handleUserPrompts([this, completionHandler = WTFMove(completionHandler)](CommandResult&& result) mutable {
312 if (result.isError()) {
313 completionHandler(WTFMove(result));
316 RefPtr<JSON::Object> parameters = JSON::Object::create();
317 parameters->setString(ASCIILiteral("handle"), m_toplevelBrowsingContext.value());
318 if (m_timeouts.pageLoad)
319 parameters->setInteger(ASCIILiteral("pageLoadTimeout"), m_timeouts.pageLoad.value().millisecondsAs<int>());
320 if (auto pageLoadStrategy = pageLoadStrategyString())
321 parameters->setString(ASCIILiteral("pageLoadStrategy"), pageLoadStrategy.value());
322 m_host->sendCommandToBackend(ASCIILiteral("goBackInBrowsingContext"), WTFMove(parameters), [this, protectedThis = makeRef(*this), completionHandler = WTFMove(completionHandler)](SessionHost::CommandResponse&& response) {
323 if (response.isError) {
324 completionHandler(CommandResult::fail(WTFMove(response.responseObject)));
327 switchToBrowsingContext(std::nullopt);
328 completionHandler(CommandResult::success());
333 void Session::forward(Function<void (CommandResult&&)>&& completionHandler)
335 if (!m_toplevelBrowsingContext) {
336 completionHandler(CommandResult::fail(CommandResult::ErrorCode::NoSuchWindow));
340 handleUserPrompts([this, completionHandler = WTFMove(completionHandler)](CommandResult&& result) mutable {
341 if (result.isError()) {
342 completionHandler(WTFMove(result));
345 RefPtr<JSON::Object> parameters = JSON::Object::create();
346 parameters->setString(ASCIILiteral("handle"), m_toplevelBrowsingContext.value());
347 if (m_timeouts.pageLoad)
348 parameters->setInteger(ASCIILiteral("pageLoadTimeout"), m_timeouts.pageLoad.value().millisecondsAs<int>());
349 if (auto pageLoadStrategy = pageLoadStrategyString())
350 parameters->setString(ASCIILiteral("pageLoadStrategy"), pageLoadStrategy.value());
351 m_host->sendCommandToBackend(ASCIILiteral("goForwardInBrowsingContext"), WTFMove(parameters), [this, protectedThis = makeRef(*this), completionHandler = WTFMove(completionHandler)](SessionHost::CommandResponse&& response) {
352 if (response.isError) {
353 completionHandler(CommandResult::fail(WTFMove(response.responseObject)));
356 switchToBrowsingContext(std::nullopt);
357 completionHandler(CommandResult::success());
362 void Session::refresh(Function<void (CommandResult&&)>&& completionHandler)
364 if (!m_toplevelBrowsingContext) {
365 completionHandler(CommandResult::fail(CommandResult::ErrorCode::NoSuchWindow));
369 handleUserPrompts([this, completionHandler = WTFMove(completionHandler)](CommandResult&& result) mutable {
370 if (result.isError()) {
371 completionHandler(WTFMove(result));
374 RefPtr<JSON::Object> parameters = JSON::Object::create();
375 parameters->setString(ASCIILiteral("handle"), m_toplevelBrowsingContext.value());
376 if (m_timeouts.pageLoad)
377 parameters->setInteger(ASCIILiteral("pageLoadTimeout"), m_timeouts.pageLoad.value().millisecondsAs<int>());
378 if (auto pageLoadStrategy = pageLoadStrategyString())
379 parameters->setString(ASCIILiteral("pageLoadStrategy"), pageLoadStrategy.value());
380 m_host->sendCommandToBackend(ASCIILiteral("reloadBrowsingContext"), WTFMove(parameters), [this, protectedThis = makeRef(*this), completionHandler = WTFMove(completionHandler)](SessionHost::CommandResponse&& response) {
381 if (response.isError) {
382 completionHandler(CommandResult::fail(WTFMove(response.responseObject)));
385 switchToBrowsingContext(std::nullopt);
386 completionHandler(CommandResult::success());
391 void Session::getTitle(Function<void (CommandResult&&)>&& completionHandler)
393 if (!m_toplevelBrowsingContext) {
394 completionHandler(CommandResult::fail(CommandResult::ErrorCode::NoSuchWindow));
398 handleUserPrompts([this, completionHandler = WTFMove(completionHandler)](CommandResult&& result) mutable {
399 if (result.isError()) {
400 completionHandler(WTFMove(result));
403 RefPtr<JSON::Object> parameters = JSON::Object::create();
404 parameters->setString(ASCIILiteral("browsingContextHandle"), m_toplevelBrowsingContext.value());
405 parameters->setString(ASCIILiteral("function"), ASCIILiteral("function() { return document.title; }"));
406 parameters->setArray(ASCIILiteral("arguments"), JSON::Array::create());
407 m_host->sendCommandToBackend(ASCIILiteral("evaluateJavaScriptFunction"), WTFMove(parameters), [this, protectedThis = makeRef(*this), completionHandler = WTFMove(completionHandler)](SessionHost::CommandResponse&& response) {
408 if (response.isError || !response.responseObject) {
409 completionHandler(CommandResult::fail(WTFMove(response.responseObject)));
413 if (!response.responseObject->getString(ASCIILiteral("result"), valueString)) {
414 completionHandler(CommandResult::fail(CommandResult::ErrorCode::UnknownError));
417 RefPtr<JSON::Value> resultValue;
418 if (!JSON::Value::parseJSON(valueString, resultValue)) {
419 completionHandler(CommandResult::fail(CommandResult::ErrorCode::UnknownError));
422 completionHandler(CommandResult::success(WTFMove(resultValue)));
427 void Session::getWindowHandle(Function<void (CommandResult&&)>&& completionHandler)
429 if (!m_toplevelBrowsingContext) {
430 completionHandler(CommandResult::fail(CommandResult::ErrorCode::NoSuchWindow));
434 RefPtr<JSON::Object> parameters = JSON::Object::create();
435 parameters->setString(ASCIILiteral("handle"), m_toplevelBrowsingContext.value());
436 m_host->sendCommandToBackend(ASCIILiteral("getBrowsingContext"), WTFMove(parameters), [this, protectedThis = makeRef(*this), completionHandler = WTFMove(completionHandler)](SessionHost::CommandResponse&& response) {
437 if (response.isError || !response.responseObject) {
438 completionHandler(CommandResult::fail(WTFMove(response.responseObject)));
441 RefPtr<JSON::Object> browsingContext;
442 if (!response.responseObject->getObject(ASCIILiteral("context"), browsingContext)) {
443 completionHandler(CommandResult::fail(CommandResult::ErrorCode::UnknownError));
447 if (!browsingContext->getString(ASCIILiteral("handle"), handle)) {
448 completionHandler(CommandResult::fail(CommandResult::ErrorCode::UnknownError));
451 completionHandler(CommandResult::success(JSON::Value::create(handle)));
455 void Session::closeTopLevelBrowsingContext(const String& toplevelBrowsingContext, Function<void (CommandResult&&)>&& completionHandler)
457 RefPtr<JSON::Object> parameters = JSON::Object::create();
458 parameters->setString(ASCIILiteral("handle"), toplevelBrowsingContext);
459 m_host->sendCommandToBackend(ASCIILiteral("closeBrowsingContext"), WTFMove(parameters), [this, protectedThis = makeRef(*this), completionHandler = WTFMove(completionHandler)](SessionHost::CommandResponse&& response) mutable {
460 if (!m_host->isConnected()) {
461 // Closing the browsing context made the browser quit.
462 completionHandler(CommandResult::success(JSON::Array::create()));
465 if (response.isError) {
466 completionHandler(CommandResult::fail(WTFMove(response.responseObject)));
470 getWindowHandles([this, completionHandler = WTFMove(completionHandler)](CommandResult&& result) {
471 if (!m_host->isConnected()) {
472 // Closing the browsing context made the browser quit.
473 completionHandler(CommandResult::success(JSON::Array::create()));
476 completionHandler(WTFMove(result));
481 void Session::closeWindow(Function<void (CommandResult&&)>&& completionHandler)
483 if (!m_toplevelBrowsingContext) {
484 completionHandler(CommandResult::fail(CommandResult::ErrorCode::NoSuchWindow));
488 handleUserPrompts([this, completionHandler = WTFMove(completionHandler)](CommandResult&& result) mutable {
489 if (result.isError()) {
490 completionHandler(WTFMove(result));
493 auto toplevelBrowsingContext = std::exchange(m_toplevelBrowsingContext, std::nullopt);
494 closeTopLevelBrowsingContext(toplevelBrowsingContext.value(), WTFMove(completionHandler));
498 void Session::switchToWindow(const String& windowHandle, Function<void (CommandResult&&)>&& completionHandler)
500 RefPtr<JSON::Object> parameters = JSON::Object::create();
501 parameters->setString(ASCIILiteral("browsingContextHandle"), windowHandle);
502 m_host->sendCommandToBackend(ASCIILiteral("switchToBrowsingContext"), WTFMove(parameters), [this, protectedThis = makeRef(*this), windowHandle, completionHandler = WTFMove(completionHandler)](SessionHost::CommandResponse&& response) {
503 if (response.isError) {
504 completionHandler(CommandResult::fail(WTFMove(response.responseObject)));
507 switchToTopLevelBrowsingContext(windowHandle);
508 completionHandler(CommandResult::success());
512 void Session::getWindowHandles(Function<void (CommandResult&&)>&& completionHandler)
514 m_host->sendCommandToBackend(ASCIILiteral("getBrowsingContexts"), JSON::Object::create(), [this, protectedThis = makeRef(*this), completionHandler = WTFMove(completionHandler)](SessionHost::CommandResponse&& response) {
515 if (response.isError || !response.responseObject) {
516 completionHandler(CommandResult::fail(WTFMove(response.responseObject)));
519 RefPtr<JSON::Array> browsingContextArray;
520 if (!response.responseObject->getArray(ASCIILiteral("contexts"), browsingContextArray)) {
521 completionHandler(CommandResult::fail(CommandResult::ErrorCode::UnknownError));
524 RefPtr<JSON::Array> windowHandles = JSON::Array::create();
525 for (unsigned i = 0; i < browsingContextArray->length(); ++i) {
526 RefPtr<JSON::Value> browsingContextValue = browsingContextArray->get(i);
527 RefPtr<JSON::Object> browsingContext;
528 if (!browsingContextValue->asObject(browsingContext)) {
529 completionHandler(CommandResult::fail(CommandResult::ErrorCode::UnknownError));
534 if (!browsingContext->getString(ASCIILiteral("handle"), handle)) {
535 completionHandler(CommandResult::fail(CommandResult::ErrorCode::UnknownError));
539 windowHandles->pushString(handle);
541 completionHandler(CommandResult::success(WTFMove(windowHandles)));
545 void Session::switchToFrame(RefPtr<JSON::Value>&& frameID, Function<void (CommandResult&&)>&& completionHandler)
547 if (!m_toplevelBrowsingContext) {
548 completionHandler(CommandResult::fail(CommandResult::ErrorCode::NoSuchWindow));
552 if (frameID->isNull()) {
553 switchToBrowsingContext(std::nullopt);
554 completionHandler(CommandResult::success());
558 handleUserPrompts([this, frameID = WTFMove(frameID), completionHandler = WTFMove(completionHandler)](CommandResult&& result) mutable {
559 if (result.isError()) {
560 completionHandler(WTFMove(result));
563 RefPtr<JSON::Object> parameters = JSON::Object::create();
564 parameters->setString(ASCIILiteral("browsingContextHandle"), m_toplevelBrowsingContext.value());
565 if (m_currentBrowsingContext)
566 parameters->setString(ASCIILiteral("frameHandle"), m_currentBrowsingContext.value());
569 if (frameID->asInteger(frameIndex)) {
570 if (frameIndex < 0 || frameIndex > USHRT_MAX) {
571 completionHandler(CommandResult::fail(CommandResult::ErrorCode::NoSuchFrame));
574 parameters->setInteger(ASCIILiteral("ordinal"), frameIndex);
576 String frameElementID = extractElementID(*frameID);
577 if (!frameElementID.isEmpty())
578 parameters->setString(ASCIILiteral("nodeHandle"), frameElementID);
581 if (!frameID->asString(frameName)) {
582 completionHandler(CommandResult::fail(CommandResult::ErrorCode::NoSuchFrame));
585 parameters->setString(ASCIILiteral("name"), frameName);
589 m_host->sendCommandToBackend(ASCIILiteral("resolveChildFrameHandle"), WTFMove(parameters), [this, protectedThis = makeRef(*this), completionHandler = WTFMove(completionHandler)](SessionHost::CommandResponse&& response) {
590 if (response.isError || !response.responseObject) {
591 completionHandler(CommandResult::fail(WTFMove(response.responseObject)));
595 if (!response.responseObject->getString(ASCIILiteral("result"), frameHandle)) {
596 completionHandler(CommandResult::fail(CommandResult::ErrorCode::UnknownError));
599 switchToBrowsingContext(frameHandle);
600 completionHandler(CommandResult::success());
605 void Session::switchToParentFrame(Function<void (CommandResult&&)>&& completionHandler)
607 if (!m_toplevelBrowsingContext) {
608 completionHandler(CommandResult::fail(CommandResult::ErrorCode::NoSuchWindow));
612 if (!m_currentBrowsingContext) {
613 completionHandler(CommandResult::success());
617 handleUserPrompts([this, completionHandler = WTFMove(completionHandler)](CommandResult&& result) mutable {
618 if (result.isError()) {
619 completionHandler(WTFMove(result));
622 RefPtr<JSON::Object> parameters = JSON::Object::create();
623 parameters->setString(ASCIILiteral("browsingContextHandle"), m_toplevelBrowsingContext.value());
624 parameters->setString(ASCIILiteral("frameHandle"), m_currentBrowsingContext.value());
625 m_host->sendCommandToBackend(ASCIILiteral("resolveParentFrameHandle"), WTFMove(parameters), [this, protectedThis = makeRef(*this), completionHandler = WTFMove(completionHandler)](SessionHost::CommandResponse&& response) {
626 if (response.isError || !response.responseObject) {
627 completionHandler(CommandResult::fail(WTFMove(response.responseObject)));
631 if (!response.responseObject->getString(ASCIILiteral("result"), frameHandle)) {
632 completionHandler(CommandResult::fail(CommandResult::ErrorCode::UnknownError));
635 switchToBrowsingContext(frameHandle);
636 completionHandler(CommandResult::success());
641 void Session::getToplevelBrowsingContextRect(Function<void (CommandResult&&)>&& completionHandler)
643 RefPtr<JSON::Object> parameters = JSON::Object::create();
644 parameters->setString(ASCIILiteral("handle"), m_toplevelBrowsingContext.value());
645 m_host->sendCommandToBackend(ASCIILiteral("getBrowsingContext"), WTFMove(parameters), [this, protectedThis = makeRef(*this), completionHandler = WTFMove(completionHandler)](SessionHost::CommandResponse&& response) {
646 if (response.isError || !response.responseObject) {
647 completionHandler(CommandResult::fail(WTFMove(response.responseObject)));
650 RefPtr<JSON::Object> browsingContext;
651 if (!response.responseObject->getObject(ASCIILiteral("context"), browsingContext)) {
652 completionHandler(CommandResult::fail(CommandResult::ErrorCode::UnknownError));
655 RefPtr<JSON::Object> windowOrigin;
657 if (!browsingContext->getObject(ASCIILiteral("windowOrigin"), windowOrigin)
658 || !windowOrigin->getDouble(ASCIILiteral("x"), x)
659 || !windowOrigin->getDouble(ASCIILiteral("y"), y)) {
660 completionHandler(CommandResult::fail(CommandResult::ErrorCode::UnknownError));
663 RefPtr<JSON::Object> windowSize;
664 double width, height;
665 if (!browsingContext->getObject(ASCIILiteral("windowSize"), windowSize)
666 || !windowSize->getDouble(ASCIILiteral("width"), width)
667 || !windowSize->getDouble(ASCIILiteral("height"), width)) {
668 completionHandler(CommandResult::fail(CommandResult::ErrorCode::UnknownError));
671 auto windowRect = JSON::Object::create();
672 windowRect->setDouble(ASCIILiteral("x"), x);
673 windowRect->setDouble(ASCIILiteral("y"), y);
674 windowRect->setDouble(ASCIILiteral("width"), width);
675 windowRect->setDouble(ASCIILiteral("height"), height);
676 completionHandler(CommandResult::success(WTFMove(windowRect)));
680 void Session::moveToplevelBrowsingContextWindow(double x, double y, Function<void (CommandResult&&)>&& completionHandler)
682 RefPtr<JSON::Object> parameters = JSON::Object::create();
683 parameters->setString(ASCIILiteral("handle"), m_toplevelBrowsingContext.value());
684 RefPtr<JSON::Object> windowOrigin = JSON::Object::create();
685 windowOrigin->setDouble("x", x);
686 windowOrigin->setDouble("y", y);
687 parameters->setObject(ASCIILiteral("origin"), WTFMove(windowOrigin));
688 m_host->sendCommandToBackend(ASCIILiteral("moveWindowOfBrowsingContext"), WTFMove(parameters), [this, protectedThis = makeRef(*this), completionHandler = WTFMove(completionHandler)](SessionHost::CommandResponse&& response) {
689 if (response.isError) {
690 completionHandler(CommandResult::fail(WTFMove(response.responseObject)));
693 completionHandler(CommandResult::success());
697 void Session::resizeToplevelBrowsingContextWindow(double width, double height, Function<void (CommandResult&&)>&& completionHandler)
699 RefPtr<JSON::Object> parameters = JSON::Object::create();
700 parameters->setString(ASCIILiteral("handle"), m_toplevelBrowsingContext.value());
701 RefPtr<JSON::Object> windowSize = JSON::Object::create();
702 windowSize->setDouble("width", width);
703 windowSize->setDouble("height", height);
704 parameters->setObject(ASCIILiteral("size"), WTFMove(windowSize));
705 m_host->sendCommandToBackend(ASCIILiteral("resizeWindowOfBrowsingContext"), WTFMove(parameters), [this, protectedThis = makeRef(*this), completionHandler = WTFMove(completionHandler)](SessionHost::CommandResponse&& response) {
706 if (response.isError) {
707 completionHandler(CommandResult::fail(WTFMove(response.responseObject)));
710 completionHandler(CommandResult::success());
714 void Session::getWindowRect(Function<void (CommandResult&&)>&& completionHandler)
716 if (!m_toplevelBrowsingContext) {
717 completionHandler(CommandResult::fail(CommandResult::ErrorCode::NoSuchWindow));
721 handleUserPrompts([this, completionHandler = WTFMove(completionHandler)](CommandResult&& result) mutable {
722 if (result.isError()) {
723 completionHandler(WTFMove(result));
726 getToplevelBrowsingContextRect(WTFMove(completionHandler));
730 void Session::setWindowRect(std::optional<double> x, std::optional<double> y, std::optional<double> width, std::optional<double> height, Function<void (CommandResult&&)>&& completionHandler)
732 if (!m_toplevelBrowsingContext) {
733 completionHandler(CommandResult::fail(CommandResult::ErrorCode::NoSuchWindow));
737 handleUserPrompts([this, x, y, width, height, completionHandler = WTFMove(completionHandler)](CommandResult&& result) mutable {
738 if (result.isError()) {
739 completionHandler(WTFMove(result));
743 if (width && height) {
744 resizeToplevelBrowsingContextWindow(width.value(), height.value(), [this, x, y, completionHandler = WTFMove(completionHandler)](CommandResult&& result) mutable {
745 if (result.isError()) {
746 completionHandler(WTFMove(result));
750 getToplevelBrowsingContextRect(WTFMove(completionHandler));
754 moveToplevelBrowsingContextWindow(x.value(), y.value(), [this, completionHandler = WTFMove(completionHandler)](CommandResult&& result) mutable {
755 if (result.isError()) {
756 completionHandler(WTFMove(result));
759 getToplevelBrowsingContextRect(WTFMove(completionHandler));
766 moveToplevelBrowsingContextWindow(x.value(), y.value(), [this, completionHandler = WTFMove(completionHandler)](CommandResult&& result) mutable {
767 if (result.isError()) {
768 completionHandler(WTFMove(result));
771 getToplevelBrowsingContextRect(WTFMove(completionHandler));
776 getToplevelBrowsingContextRect(WTFMove(completionHandler));
780 RefPtr<JSON::Object> Session::createElement(RefPtr<JSON::Value>&& value)
782 RefPtr<JSON::Object> valueObject;
783 if (!value->asObject(valueObject))
787 if (!valueObject->getString("session-node-" + m_id, elementID))
790 RefPtr<JSON::Object> elementObject = JSON::Object::create();
791 elementObject->setString(webElementIdentifier, elementID);
792 return elementObject;
795 RefPtr<JSON::Object> Session::createElement(const String& elementID)
797 RefPtr<JSON::Object> elementObject = JSON::Object::create();
798 elementObject->setString("session-node-" + m_id, elementID);
799 return elementObject;
802 RefPtr<JSON::Object> Session::extractElement(JSON::Value& value)
804 String elementID = extractElementID(value);
805 return !elementID.isEmpty() ? createElement(elementID) : nullptr;
808 String Session::extractElementID(JSON::Value& value)
810 RefPtr<JSON::Object> valueObject;
811 if (!value.asObject(valueObject))
812 return emptyString();
815 if (!valueObject->getString(webElementIdentifier, elementID))
816 return emptyString();
821 void Session::computeElementLayout(const String& elementID, OptionSet<ElementLayoutOption> options, Function<void (std::optional<Rect>&&, std::optional<Point>&&, bool, RefPtr<JSON::Object>&&)>&& completionHandler)
823 ASSERT(m_toplevelBrowsingContext.value());
825 RefPtr<JSON::Object> parameters = JSON::Object::create();
826 parameters->setString(ASCIILiteral("browsingContextHandle"), m_toplevelBrowsingContext.value());
827 parameters->setString(ASCIILiteral("frameHandle"), m_currentBrowsingContext.value_or(emptyString()));
828 parameters->setString(ASCIILiteral("nodeHandle"), elementID);
829 parameters->setBoolean(ASCIILiteral("scrollIntoViewIfNeeded"), options.contains(ElementLayoutOption::ScrollIntoViewIfNeeded));
830 parameters->setString(ASCIILiteral("coordinateSystem"), options.contains(ElementLayoutOption::UseViewportCoordinates) ? ASCIILiteral("LayoutViewport") : ASCIILiteral("Page"));
831 m_host->sendCommandToBackend(ASCIILiteral("computeElementLayout"), WTFMove(parameters), [this, protectedThis = makeRef(*this), completionHandler = WTFMove(completionHandler)](SessionHost::CommandResponse&& response) mutable {
832 if (response.isError || !response.responseObject) {
833 completionHandler(std::nullopt, std::nullopt, false, WTFMove(response.responseObject));
836 RefPtr<JSON::Object> rectObject;
837 if (!response.responseObject->getObject(ASCIILiteral("rect"), rectObject)) {
838 completionHandler(std::nullopt, std::nullopt, false, nullptr);
841 std::optional<int> elementX;
842 std::optional<int> elementY;
843 RefPtr<JSON::Object> elementPosition;
844 if (rectObject->getObject(ASCIILiteral("origin"), elementPosition)) {
846 if (elementPosition->getInteger(ASCIILiteral("x"), x) && elementPosition->getInteger(ASCIILiteral("y"), y)) {
851 if (!elementX || !elementY) {
852 completionHandler(std::nullopt, std::nullopt, false, nullptr);
855 std::optional<int> elementWidth;
856 std::optional<int> elementHeight;
857 RefPtr<JSON::Object> elementSize;
858 if (rectObject->getObject(ASCIILiteral("size"), elementSize)) {
860 if (elementSize->getInteger(ASCIILiteral("width"), width) && elementSize->getInteger(ASCIILiteral("height"), height)) {
861 elementWidth = width;
862 elementHeight = height;
865 if (!elementWidth || !elementHeight) {
866 completionHandler(std::nullopt, std::nullopt, false, nullptr);
869 Rect rect = { { elementX.value(), elementY.value() }, { elementWidth.value(), elementHeight.value() } };
872 if (!response.responseObject->getBoolean(ASCIILiteral("isObscured"), isObscured)) {
873 completionHandler(std::nullopt, std::nullopt, false, nullptr);
876 RefPtr<JSON::Object> inViewCenterPointObject;
877 if (!response.responseObject->getObject(ASCIILiteral("inViewCenterPoint"), inViewCenterPointObject)) {
878 completionHandler(rect, std::nullopt, isObscured, nullptr);
881 int inViewCenterPointX, inViewCenterPointY;
882 if (!inViewCenterPointObject->getInteger(ASCIILiteral("x"), inViewCenterPointX)
883 || !inViewCenterPointObject->getInteger(ASCIILiteral("y"), inViewCenterPointY)) {
884 completionHandler(std::nullopt, std::nullopt, isObscured, nullptr);
887 Point inViewCenterPoint = { inViewCenterPointX, inViewCenterPointY };
888 completionHandler(rect, inViewCenterPoint, isObscured, nullptr);
892 void Session::findElements(const String& strategy, const String& selector, FindElementsMode mode, const String& rootElementID, Function<void (CommandResult&&)>&& completionHandler)
894 if (!m_toplevelBrowsingContext) {
895 completionHandler(CommandResult::fail(CommandResult::ErrorCode::NoSuchWindow));
899 auto implicitWait = m_timeouts.implicit.value_or(0_s);
900 RefPtr<JSON::Array> arguments = JSON::Array::create();
901 arguments->pushString(JSON::Value::create(strategy)->toJSONString());
902 if (rootElementID.isEmpty())
903 arguments->pushString(JSON::Value::null()->toJSONString());
905 arguments->pushString(createElement(rootElementID)->toJSONString());
906 arguments->pushString(JSON::Value::create(selector)->toJSONString());
907 arguments->pushString(JSON::Value::create(mode == FindElementsMode::Single)->toJSONString());
908 arguments->pushString(JSON::Value::create(implicitWait.milliseconds())->toJSONString());
910 RefPtr<JSON::Object> parameters = JSON::Object::create();
911 parameters->setString(ASCIILiteral("browsingContextHandle"), m_toplevelBrowsingContext.value());
912 if (m_currentBrowsingContext)
913 parameters->setString(ASCIILiteral("frameHandle"), m_currentBrowsingContext.value());
914 parameters->setString(ASCIILiteral("function"), FindNodesJavaScript);
915 parameters->setArray(ASCIILiteral("arguments"), WTFMove(arguments));
916 parameters->setBoolean(ASCIILiteral("expectsImplicitCallbackArgument"), true);
917 // If there's an implicit wait, use one second more as callback timeout.
919 parameters->setInteger(ASCIILiteral("callbackTimeout"), Seconds(implicitWait + 1_s).millisecondsAs<int>());
921 m_host->sendCommandToBackend(ASCIILiteral("evaluateJavaScriptFunction"), WTFMove(parameters), [this, protectedThis = makeRef(*this), mode, completionHandler = WTFMove(completionHandler)](SessionHost::CommandResponse&& response) {
922 if (response.isError || !response.responseObject) {
923 completionHandler(CommandResult::fail(WTFMove(response.responseObject)));
927 if (!response.responseObject->getString(ASCIILiteral("result"), valueString)) {
928 completionHandler(CommandResult::fail(CommandResult::ErrorCode::UnknownError));
931 RefPtr<JSON::Value> resultValue;
932 if (!JSON::Value::parseJSON(valueString, resultValue)) {
933 completionHandler(CommandResult::fail(CommandResult::ErrorCode::UnknownError));
938 case FindElementsMode::Single: {
939 RefPtr<JSON::Object> elementObject = createElement(WTFMove(resultValue));
940 if (!elementObject) {
941 completionHandler(CommandResult::fail(CommandResult::ErrorCode::NoSuchElement));
944 completionHandler(CommandResult::success(WTFMove(elementObject)));
947 case FindElementsMode::Multiple: {
948 RefPtr<JSON::Array> elementsArray;
949 if (!resultValue->asArray(elementsArray)) {
950 completionHandler(CommandResult::fail(CommandResult::ErrorCode::NoSuchElement));
953 RefPtr<JSON::Array> elementObjectsArray = JSON::Array::create();
954 unsigned elementsArrayLength = elementsArray->length();
955 for (unsigned i = 0; i < elementsArrayLength; ++i) {
956 if (auto elementObject = createElement(elementsArray->get(i)))
957 elementObjectsArray->pushObject(WTFMove(elementObject));
959 completionHandler(CommandResult::success(WTFMove(elementObjectsArray)));
966 void Session::getActiveElement(Function<void (CommandResult&&)>&& completionHandler)
968 if (!m_toplevelBrowsingContext) {
969 completionHandler(CommandResult::fail(CommandResult::ErrorCode::NoSuchWindow));
973 handleUserPrompts([this, completionHandler = WTFMove(completionHandler)](CommandResult&& result) mutable {
974 if (result.isError()) {
975 completionHandler(WTFMove(result));
978 RefPtr<JSON::Object> parameters = JSON::Object::create();
979 parameters->setString(ASCIILiteral("browsingContextHandle"), m_toplevelBrowsingContext.value());
980 parameters->setString(ASCIILiteral("function"), ASCIILiteral("function() { return document.activeElement; }"));
981 parameters->setArray(ASCIILiteral("arguments"), JSON::Array::create());
982 m_host->sendCommandToBackend(ASCIILiteral("evaluateJavaScriptFunction"), WTFMove(parameters), [this, protectedThis = makeRef(*this), completionHandler = WTFMove(completionHandler)](SessionHost::CommandResponse&& response) {
983 if (response.isError || !response.responseObject) {
984 completionHandler(CommandResult::fail(WTFMove(response.responseObject)));
988 if (!response.responseObject->getString(ASCIILiteral("result"), valueString)) {
989 completionHandler(CommandResult::fail(CommandResult::ErrorCode::UnknownError));
992 RefPtr<JSON::Value> resultValue;
993 if (!JSON::Value::parseJSON(valueString, resultValue)) {
994 completionHandler(CommandResult::fail(CommandResult::ErrorCode::UnknownError));
997 RefPtr<JSON::Object> elementObject = createElement(WTFMove(resultValue));
998 if (!elementObject) {
999 completionHandler(CommandResult::fail(CommandResult::ErrorCode::UnknownError));
1002 completionHandler(CommandResult::success(WTFMove(elementObject)));
1007 void Session::isElementSelected(const String& elementID, Function<void (CommandResult&&)>&& completionHandler)
1009 if (!m_toplevelBrowsingContext) {
1010 completionHandler(CommandResult::fail(CommandResult::ErrorCode::NoSuchWindow));
1014 handleUserPrompts([this, elementID, completionHandler = WTFMove(completionHandler)](CommandResult&& result) mutable {
1015 if (result.isError()) {
1016 completionHandler(WTFMove(result));
1019 RefPtr<JSON::Array> arguments = JSON::Array::create();
1020 arguments->pushString(createElement(elementID)->toJSONString());
1021 arguments->pushString(JSON::Value::create("selected")->toJSONString());
1023 RefPtr<JSON::Object> parameters = JSON::Object::create();
1024 parameters->setString(ASCIILiteral("browsingContextHandle"), m_toplevelBrowsingContext.value());
1025 if (m_currentBrowsingContext)
1026 parameters->setString(ASCIILiteral("frameHandle"), m_currentBrowsingContext.value());
1027 parameters->setString(ASCIILiteral("function"), ElementAttributeJavaScript);
1028 parameters->setArray(ASCIILiteral("arguments"), WTFMove(arguments));
1029 m_host->sendCommandToBackend(ASCIILiteral("evaluateJavaScriptFunction"), WTFMove(parameters), [this, protectedThis = makeRef(*this), completionHandler = WTFMove(completionHandler)](SessionHost::CommandResponse&& response) {
1030 if (response.isError || !response.responseObject) {
1031 completionHandler(CommandResult::fail(WTFMove(response.responseObject)));
1035 if (!response.responseObject->getString(ASCIILiteral("result"), valueString)) {
1036 completionHandler(CommandResult::fail(CommandResult::ErrorCode::UnknownError));
1039 RefPtr<JSON::Value> resultValue;
1040 if (!JSON::Value::parseJSON(valueString, resultValue)) {
1041 completionHandler(CommandResult::fail(CommandResult::ErrorCode::UnknownError));
1044 if (resultValue->isNull()) {
1045 completionHandler(CommandResult::success(JSON::Value::create(false)));
1048 String booleanResult;
1049 if (!resultValue->asString(booleanResult) || booleanResult != "true") {
1050 completionHandler(CommandResult::fail(CommandResult::ErrorCode::UnknownError));
1053 completionHandler(CommandResult::success(JSON::Value::create(true)));
1058 void Session::getElementText(const String& elementID, Function<void (CommandResult&&)>&& completionHandler)
1060 if (!m_toplevelBrowsingContext) {
1061 completionHandler(CommandResult::fail(CommandResult::ErrorCode::NoSuchWindow));
1065 handleUserPrompts([this, elementID, completionHandler = WTFMove(completionHandler)](CommandResult&& result) mutable {
1066 if (result.isError()) {
1067 completionHandler(WTFMove(result));
1070 RefPtr<JSON::Array> arguments = JSON::Array::create();
1071 arguments->pushString(createElement(elementID)->toJSONString());
1073 RefPtr<JSON::Object> parameters = JSON::Object::create();
1074 parameters->setString(ASCIILiteral("browsingContextHandle"), m_toplevelBrowsingContext.value());
1075 if (m_currentBrowsingContext)
1076 parameters->setString(ASCIILiteral("frameHandle"), m_currentBrowsingContext.value());
1077 // FIXME: Add an atom to properly implement this instead of just using innerText.
1078 parameters->setString(ASCIILiteral("function"), ASCIILiteral("function(element) { return element.innerText.replace(/^[^\\S\\xa0]+|[^\\S\\xa0]+$/g, '') }"));
1079 parameters->setArray(ASCIILiteral("arguments"), WTFMove(arguments));
1080 m_host->sendCommandToBackend(ASCIILiteral("evaluateJavaScriptFunction"), WTFMove(parameters), [this, protectedThis = makeRef(*this), completionHandler = WTFMove(completionHandler)](SessionHost::CommandResponse&& response) {
1081 if (response.isError || !response.responseObject) {
1082 completionHandler(CommandResult::fail(WTFMove(response.responseObject)));
1086 if (!response.responseObject->getString(ASCIILiteral("result"), valueString)) {
1087 completionHandler(CommandResult::fail(CommandResult::ErrorCode::UnknownError));
1090 RefPtr<JSON::Value> resultValue;
1091 if (!JSON::Value::parseJSON(valueString, resultValue)) {
1092 completionHandler(CommandResult::fail(CommandResult::ErrorCode::UnknownError));
1095 completionHandler(CommandResult::success(WTFMove(resultValue)));
1100 void Session::getElementTagName(const String& elementID, Function<void (CommandResult&&)>&& completionHandler)
1102 if (!m_toplevelBrowsingContext) {
1103 completionHandler(CommandResult::fail(CommandResult::ErrorCode::NoSuchWindow));
1107 handleUserPrompts([this, elementID, completionHandler = WTFMove(completionHandler)](CommandResult&& result) mutable {
1108 if (result.isError()) {
1109 completionHandler(WTFMove(result));
1112 RefPtr<JSON::Array> arguments = JSON::Array::create();
1113 arguments->pushString(createElement(elementID)->toJSONString());
1115 RefPtr<JSON::Object> parameters = JSON::Object::create();
1116 parameters->setString(ASCIILiteral("browsingContextHandle"), m_toplevelBrowsingContext.value());
1117 if (m_currentBrowsingContext)
1118 parameters->setString(ASCIILiteral("frameHandle"), m_currentBrowsingContext.value());
1119 parameters->setString(ASCIILiteral("function"), ASCIILiteral("function(element) { return element.tagName.toLowerCase() }"));
1120 parameters->setArray(ASCIILiteral("arguments"), WTFMove(arguments));
1121 m_host->sendCommandToBackend(ASCIILiteral("evaluateJavaScriptFunction"), WTFMove(parameters), [this, protectedThis = makeRef(*this), completionHandler = WTFMove(completionHandler)](SessionHost::CommandResponse&& response) {
1122 if (response.isError || !response.responseObject) {
1123 completionHandler(CommandResult::fail(WTFMove(response.responseObject)));
1127 if (!response.responseObject->getString(ASCIILiteral("result"), valueString)) {
1128 completionHandler(CommandResult::fail(CommandResult::ErrorCode::UnknownError));
1131 RefPtr<JSON::Value> resultValue;
1132 if (!JSON::Value::parseJSON(valueString, resultValue)) {
1133 completionHandler(CommandResult::fail(CommandResult::ErrorCode::UnknownError));
1136 completionHandler(CommandResult::success(WTFMove(resultValue)));
1141 void Session::getElementRect(const String& elementID, Function<void (CommandResult&&)>&& completionHandler)
1143 if (!m_toplevelBrowsingContext) {
1144 completionHandler(CommandResult::fail(CommandResult::ErrorCode::NoSuchWindow));
1148 handleUserPrompts([this, elementID, completionHandler = WTFMove(completionHandler)](CommandResult&& result) mutable {
1149 if (result.isError()) {
1150 completionHandler(WTFMove(result));
1153 computeElementLayout(elementID, { }, [this, protectedThis = makeRef(*this), completionHandler = WTFMove(completionHandler)](std::optional<Rect>&& rect, std::optional<Point>&&, bool, RefPtr<JSON::Object>&& error) {
1154 if (!rect || error) {
1155 completionHandler(CommandResult::fail(WTFMove(error)));
1158 RefPtr<JSON::Object> rectObject = JSON::Object::create();
1159 rectObject->setInteger(ASCIILiteral("x"), rect.value().origin.x);
1160 rectObject->setInteger(ASCIILiteral("y"), rect.value().origin.y);
1161 rectObject->setInteger(ASCIILiteral("width"), rect.value().size.width);
1162 rectObject->setInteger(ASCIILiteral("height"), rect.value().size.height);
1163 completionHandler(CommandResult::success(WTFMove(rectObject)));
1168 void Session::isElementEnabled(const String& elementID, Function<void (CommandResult&&)>&& completionHandler)
1170 if (!m_toplevelBrowsingContext) {
1171 completionHandler(CommandResult::fail(CommandResult::ErrorCode::NoSuchWindow));
1175 handleUserPrompts([this, elementID, completionHandler = WTFMove(completionHandler)](CommandResult&& result) mutable {
1176 if (result.isError()) {
1177 completionHandler(WTFMove(result));
1180 RefPtr<JSON::Array> arguments = JSON::Array::create();
1181 arguments->pushString(createElement(elementID)->toJSONString());
1183 RefPtr<JSON::Object> parameters = JSON::Object::create();
1184 parameters->setString(ASCIILiteral("browsingContextHandle"), m_toplevelBrowsingContext.value());
1185 if (m_currentBrowsingContext)
1186 parameters->setString(ASCIILiteral("frameHandle"), m_currentBrowsingContext.value());
1187 parameters->setString(ASCIILiteral("function"), ASCIILiteral("function(element) { return element.disabled === undefined ? true : !element.disabled }"));
1188 parameters->setArray(ASCIILiteral("arguments"), WTFMove(arguments));
1189 m_host->sendCommandToBackend(ASCIILiteral("evaluateJavaScriptFunction"), WTFMove(parameters), [this, protectedThis = makeRef(*this), completionHandler = WTFMove(completionHandler)](SessionHost::CommandResponse&& response) {
1190 if (response.isError || !response.responseObject) {
1191 completionHandler(CommandResult::fail(WTFMove(response.responseObject)));
1195 if (!response.responseObject->getString(ASCIILiteral("result"), valueString)) {
1196 completionHandler(CommandResult::fail(CommandResult::ErrorCode::UnknownError));
1199 RefPtr<JSON::Value> resultValue;
1200 if (!JSON::Value::parseJSON(valueString, resultValue)) {
1201 completionHandler(CommandResult::fail(CommandResult::ErrorCode::UnknownError));
1204 completionHandler(CommandResult::success(WTFMove(resultValue)));
1209 void Session::isElementDisplayed(const String& elementID, Function<void (CommandResult&&)>&& completionHandler)
1211 if (!m_toplevelBrowsingContext) {
1212 completionHandler(CommandResult::fail(CommandResult::ErrorCode::NoSuchWindow));
1216 handleUserPrompts([this, elementID, completionHandler = WTFMove(completionHandler)](CommandResult&& result) mutable {
1217 if (result.isError()) {
1218 completionHandler(WTFMove(result));
1221 RefPtr<JSON::Array> arguments = JSON::Array::create();
1222 arguments->pushString(createElement(elementID)->toJSONString());
1224 RefPtr<JSON::Object> parameters = JSON::Object::create();
1225 parameters->setString(ASCIILiteral("browsingContextHandle"), m_toplevelBrowsingContext.value());
1226 if (m_currentBrowsingContext)
1227 parameters->setString(ASCIILiteral("frameHandle"), m_currentBrowsingContext.value());
1228 parameters->setString(ASCIILiteral("function"), ElementDisplayedJavaScript);
1229 parameters->setArray(ASCIILiteral("arguments"), WTFMove(arguments));
1230 m_host->sendCommandToBackend(ASCIILiteral("evaluateJavaScriptFunction"), WTFMove(parameters), [this, protectedThis = makeRef(*this), completionHandler = WTFMove(completionHandler)](SessionHost::CommandResponse&& response) {
1231 if (response.isError || !response.responseObject) {
1232 completionHandler(CommandResult::fail(WTFMove(response.responseObject)));
1236 if (!response.responseObject->getString(ASCIILiteral("result"), valueString)) {
1237 completionHandler(CommandResult::fail(CommandResult::ErrorCode::UnknownError));
1240 RefPtr<JSON::Value> resultValue;
1241 if (!JSON::Value::parseJSON(valueString, resultValue)) {
1242 completionHandler(CommandResult::fail(CommandResult::ErrorCode::UnknownError));
1245 completionHandler(CommandResult::success(WTFMove(resultValue)));
1250 void Session::getElementAttribute(const String& elementID, const String& attribute, Function<void (CommandResult&&)>&& completionHandler)
1252 if (!m_toplevelBrowsingContext) {
1253 completionHandler(CommandResult::fail(CommandResult::ErrorCode::NoSuchWindow));
1257 handleUserPrompts([this, elementID, attribute, completionHandler = WTFMove(completionHandler)](CommandResult&& result) mutable {
1258 if (result.isError()) {
1259 completionHandler(WTFMove(result));
1262 RefPtr<JSON::Array> arguments = JSON::Array::create();
1263 arguments->pushString(createElement(elementID)->toJSONString());
1264 arguments->pushString(JSON::Value::create(attribute)->toJSONString());
1266 RefPtr<JSON::Object> parameters = JSON::Object::create();
1267 parameters->setString(ASCIILiteral("browsingContextHandle"), m_toplevelBrowsingContext.value());
1268 if (m_currentBrowsingContext)
1269 parameters->setString(ASCIILiteral("frameHandle"), m_currentBrowsingContext.value());
1270 parameters->setString(ASCIILiteral("function"), ElementAttributeJavaScript);
1271 parameters->setArray(ASCIILiteral("arguments"), WTFMove(arguments));
1272 m_host->sendCommandToBackend(ASCIILiteral("evaluateJavaScriptFunction"), WTFMove(parameters), [this, protectedThis = makeRef(*this), completionHandler = WTFMove(completionHandler)](SessionHost::CommandResponse&& response) {
1273 if (response.isError || !response.responseObject) {
1274 completionHandler(CommandResult::fail(WTFMove(response.responseObject)));
1278 if (!response.responseObject->getString(ASCIILiteral("result"), valueString)) {
1279 completionHandler(CommandResult::fail(CommandResult::ErrorCode::UnknownError));
1282 RefPtr<JSON::Value> resultValue;
1283 if (!JSON::Value::parseJSON(valueString, resultValue)) {
1284 completionHandler(CommandResult::fail(CommandResult::ErrorCode::UnknownError));
1287 completionHandler(CommandResult::success(WTFMove(resultValue)));
1292 void Session::waitForNavigationToComplete(Function<void (CommandResult&&)>&& completionHandler)
1294 if (!m_toplevelBrowsingContext) {
1295 completionHandler(CommandResult::fail(CommandResult::ErrorCode::NoSuchWindow));
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 if (m_timeouts.pageLoad)
1304 parameters->setInteger(ASCIILiteral("pageLoadTimeout"), m_timeouts.pageLoad.value().millisecondsAs<int>());
1305 if (auto pageLoadStrategy = pageLoadStrategyString())
1306 parameters->setString(ASCIILiteral("pageLoadStrategy"), pageLoadStrategy.value());
1307 m_host->sendCommandToBackend(ASCIILiteral("waitForNavigationToComplete"), WTFMove(parameters), [this, protectedThis = makeRef(*this), completionHandler = WTFMove(completionHandler)](SessionHost::CommandResponse&& response) {
1308 if (response.isError) {
1309 auto result = CommandResult::fail(WTFMove(response.responseObject));
1310 if (result.errorCode() == CommandResult::ErrorCode::NoSuchFrame) {
1311 // Navigation destroyed the current frame, switch to top level browsing context and ignore the error.
1312 switchToBrowsingContext(std::nullopt);
1314 completionHandler(WTFMove(result));
1318 completionHandler(CommandResult::success());
1322 void Session::selectOptionElement(const String& elementID, Function<void (CommandResult&&)>&& completionHandler)
1324 RefPtr<JSON::Object> parameters = JSON::Object::create();
1325 parameters->setString(ASCIILiteral("browsingContextHandle"), m_toplevelBrowsingContext.value());
1326 parameters->setString(ASCIILiteral("frameHandle"), m_currentBrowsingContext.value_or(emptyString()));
1327 parameters->setString(ASCIILiteral("nodeHandle"), elementID);
1328 m_host->sendCommandToBackend(ASCIILiteral("selectOptionElement"), WTFMove(parameters), [this, protectedThis = makeRef(*this), completionHandler = WTFMove(completionHandler)](SessionHost::CommandResponse&& response) {
1329 if (response.isError) {
1330 completionHandler(CommandResult::fail(WTFMove(response.responseObject)));
1333 completionHandler(CommandResult::success());
1337 void Session::elementClick(const String& elementID, Function<void (CommandResult&&)>&& completionHandler)
1339 if (!m_toplevelBrowsingContext) {
1340 completionHandler(CommandResult::fail(CommandResult::ErrorCode::NoSuchWindow));
1344 OptionSet<ElementLayoutOption> options = ElementLayoutOption::ScrollIntoViewIfNeeded;
1345 options |= ElementLayoutOption::UseViewportCoordinates;
1346 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 {
1347 if (!rect || error) {
1348 completionHandler(CommandResult::fail(WTFMove(error)));
1352 completionHandler(CommandResult::fail(CommandResult::ErrorCode::ElementClickIntercepted));
1355 if (!inViewCenter) {
1356 completionHandler(CommandResult::fail(CommandResult::ErrorCode::ElementNotInteractable));
1360 getElementTagName(elementID, [this, elementID, inViewCenter = WTFMove(inViewCenter), isObscured, completionHandler = WTFMove(completionHandler)](CommandResult&& result) mutable {
1361 bool isOptionElement = false;
1362 if (!result.isError()) {
1364 if (result.result()->asString(tagName))
1365 isOptionElement = tagName == "option";
1368 Function<void (CommandResult&&)> continueAfterClickFunction = [this, completionHandler = WTFMove(completionHandler)](CommandResult&& result) mutable {
1369 if (result.isError()) {
1370 completionHandler(WTFMove(result));
1374 waitForNavigationToComplete(WTFMove(completionHandler));
1376 if (isOptionElement)
1377 selectOptionElement(elementID, WTFMove(continueAfterClickFunction));
1379 performMouseInteraction(inViewCenter.value().x, inViewCenter.value().y, MouseButton::Left, MouseInteraction::SingleClick, WTFMove(continueAfterClickFunction));
1384 void Session::elementClear(const String& elementID, Function<void (CommandResult&&)>&& completionHandler)
1386 if (!m_toplevelBrowsingContext) {
1387 completionHandler(CommandResult::fail(CommandResult::ErrorCode::NoSuchWindow));
1391 RefPtr<JSON::Array> arguments = JSON::Array::create();
1392 arguments->pushString(createElement(elementID)->toJSONString());
1394 RefPtr<JSON::Object> parameters = JSON::Object::create();
1395 parameters->setString(ASCIILiteral("browsingContextHandle"), m_toplevelBrowsingContext.value());
1396 if (m_currentBrowsingContext)
1397 parameters->setString(ASCIILiteral("frameHandle"), m_currentBrowsingContext.value());
1398 parameters->setString(ASCIILiteral("function"), FormElementClearJavaScript);
1399 parameters->setArray(ASCIILiteral("arguments"), WTFMove(arguments));
1400 m_host->sendCommandToBackend(ASCIILiteral("evaluateJavaScriptFunction"), WTFMove(parameters), [this, protectedThis = makeRef(*this), completionHandler = WTFMove(completionHandler)](SessionHost::CommandResponse&& response) {
1401 if (response.isError) {
1402 completionHandler(CommandResult::fail(WTFMove(response.responseObject)));
1405 completionHandler(CommandResult::success());
1409 String Session::virtualKeyForKeySequence(const String& keySequence, KeyModifier& modifier)
1411 // §17.4.2 Keyboard Actions.
1412 // https://www.w3.org/TR/webdriver/#keyboard-actions
1413 modifier = KeyModifier::None;
1414 switch (keySequence[0]) {
1416 return ASCIILiteral("Cancel");
1418 return ASCIILiteral("Help");
1420 return ASCIILiteral("Backspace");
1422 return ASCIILiteral("Tab");
1424 return ASCIILiteral("Clear");
1426 return ASCIILiteral("Return");
1428 return ASCIILiteral("Enter");
1430 modifier = KeyModifier::Shift;
1431 return ASCIILiteral("Shift");
1433 modifier = KeyModifier::Control;
1434 return ASCIILiteral("Control");
1436 modifier = KeyModifier::Alternate;
1437 return ASCIILiteral("Alternate");
1439 return ASCIILiteral("Pause");
1441 return ASCIILiteral("Escape");
1443 return ASCIILiteral("Space");
1445 return ASCIILiteral("PageUp");
1447 return ASCIILiteral("PageDown");
1449 return ASCIILiteral("End");
1451 return ASCIILiteral("Home");
1453 return ASCIILiteral("LeftArrow");
1455 return ASCIILiteral("UpArrow");
1457 return ASCIILiteral("RightArrow");
1459 return ASCIILiteral("DownArrow");
1461 return ASCIILiteral("Insert");
1463 return ASCIILiteral("Delete");
1465 return ASCIILiteral("Semicolon");
1467 return ASCIILiteral("Equals");
1469 return ASCIILiteral("NumberPad0");
1471 return ASCIILiteral("NumberPad1");
1473 return ASCIILiteral("NumberPad2");
1475 return ASCIILiteral("NumberPad3");
1477 return ASCIILiteral("NumberPad4");
1479 return ASCIILiteral("NumberPad5");
1481 return ASCIILiteral("NumberPad6");
1483 return ASCIILiteral("NumberPad7");
1485 return ASCIILiteral("NumberPad8");
1487 return ASCIILiteral("NumberPad9");
1489 return ASCIILiteral("NumberPadMultiply");
1491 return ASCIILiteral("NumberPadAdd");
1493 return ASCIILiteral("NumberPadSeparator");
1495 return ASCIILiteral("NumberPadSubtract");
1497 return ASCIILiteral("NumberPadDecimal");
1499 return ASCIILiteral("NumberPadDivide");
1501 return ASCIILiteral("Function1");
1503 return ASCIILiteral("Function2");
1505 return ASCIILiteral("Function3");
1507 return ASCIILiteral("Function4");
1509 return ASCIILiteral("Function5");
1511 return ASCIILiteral("Function6");
1513 return ASCIILiteral("Function7");
1515 return ASCIILiteral("Function8");
1517 return ASCIILiteral("Function9");
1519 return ASCIILiteral("Function10");
1521 return ASCIILiteral("Function11");
1523 return ASCIILiteral("Function12");
1525 modifier = KeyModifier::Meta;
1526 return ASCIILiteral("Meta");
1533 void Session::elementSendKeys(const String& elementID, Vector<String>&& keys, Function<void (CommandResult&&)>&& completionHandler)
1535 if (!m_toplevelBrowsingContext) {
1536 completionHandler(CommandResult::fail(CommandResult::ErrorCode::NoSuchWindow));
1540 handleUserPrompts([this, elementID, keys = WTFMove(keys), completionHandler = WTFMove(completionHandler)](CommandResult&& result) mutable {
1541 if (result.isError()) {
1542 completionHandler(WTFMove(result));
1545 // FIXME: move this to an atom.
1546 static const char focusScript[] =
1547 "function focus(element) {"
1548 " var doc = element.ownerDocument || element;"
1549 " var prevActiveElement = doc.activeElement;"
1550 " if (element != prevActiveElement && prevActiveElement)"
1551 " prevActiveElement.blur();"
1553 " if (element != prevActiveElement && element.value && element.value.length && element.setSelectionRange)"
1554 " element.setSelectionRange(element.value.length, element.value.length);"
1555 " if (element != doc.activeElement)"
1556 " throw new Error('cannot focus element');"
1559 RefPtr<JSON::Array> arguments = JSON::Array::create();
1560 arguments->pushString(createElement(elementID)->toJSONString());
1561 RefPtr<JSON::Object> parameters = JSON::Object::create();
1562 parameters->setString(ASCIILiteral("browsingContextHandle"), m_toplevelBrowsingContext.value());
1563 if (m_currentBrowsingContext)
1564 parameters->setString(ASCIILiteral("frameHandle"), m_currentBrowsingContext.value());
1565 parameters->setString(ASCIILiteral("function"), focusScript);
1566 parameters->setArray(ASCIILiteral("arguments"), WTFMove(arguments));
1567 m_host->sendCommandToBackend(ASCIILiteral("evaluateJavaScriptFunction"), WTFMove(parameters), [this, protectedThis = makeRef(*this), keys = WTFMove(keys), completionHandler = WTFMove(completionHandler)](SessionHost::CommandResponse&& response) mutable {
1568 if (response.isError || !response.responseObject) {
1569 completionHandler(CommandResult::fail(WTFMove(response.responseObject)));
1573 unsigned stickyModifiers = 0;
1574 Vector<KeyboardInteraction> interactions;
1575 interactions.reserveInitialCapacity(keys.size());
1576 for (const auto& key : keys) {
1577 KeyboardInteraction interaction;
1578 KeyModifier modifier;
1579 auto virtualKey = virtualKeyForKeySequence(key, modifier);
1580 if (!virtualKey.isNull()) {
1581 interaction.key = virtualKey;
1582 if (modifier != KeyModifier::None) {
1583 stickyModifiers ^= modifier;
1584 if (stickyModifiers & modifier)
1585 interaction.type = KeyboardInteractionType::KeyPress;
1587 interaction.type = KeyboardInteractionType::KeyRelease;
1590 interaction.text = key;
1591 interactions.uncheckedAppend(WTFMove(interaction));
1594 // Reset sticky modifiers if needed.
1595 if (stickyModifiers) {
1596 if (stickyModifiers & KeyModifier::Shift)
1597 interactions.append({ KeyboardInteractionType::KeyRelease, std::nullopt, std::optional<String>(ASCIILiteral("Shift")) });
1598 if (stickyModifiers & KeyModifier::Control)
1599 interactions.append({ KeyboardInteractionType::KeyRelease, std::nullopt, std::optional<String>(ASCIILiteral("Control")) });
1600 if (stickyModifiers & KeyModifier::Alternate)
1601 interactions.append({ KeyboardInteractionType::KeyRelease, std::nullopt, std::optional<String>(ASCIILiteral("Alternate")) });
1602 if (stickyModifiers & KeyModifier::Meta)
1603 interactions.append({ KeyboardInteractionType::KeyRelease, std::nullopt, std::optional<String>(ASCIILiteral("Meta")) });
1606 performKeyboardInteractions(WTFMove(interactions), WTFMove(completionHandler));
1611 RefPtr<JSON::Value> Session::handleScriptResult(RefPtr<JSON::Value>&& resultValue)
1613 RefPtr<JSON::Array> resultArray;
1614 if (resultValue->asArray(resultArray)) {
1615 RefPtr<JSON::Array> returnValueArray = JSON::Array::create();
1616 unsigned resultArrayLength = resultArray->length();
1617 for (unsigned i = 0; i < resultArrayLength; ++i)
1618 returnValueArray->pushValue(handleScriptResult(resultArray->get(i)));
1619 return returnValueArray;
1622 if (auto element = createElement(RefPtr<JSON::Value>(resultValue)))
1625 RefPtr<JSON::Object> resultObject;
1626 if (resultValue->asObject(resultObject)) {
1627 RefPtr<JSON::Object> returnValueObject = JSON::Object::create();
1628 auto end = resultObject->end();
1629 for (auto it = resultObject->begin(); it != end; ++it)
1630 returnValueObject->setValue(it->key, handleScriptResult(WTFMove(it->value)));
1631 return returnValueObject;
1637 void Session::executeScript(const String& script, RefPtr<JSON::Array>&& argumentsArray, ExecuteScriptMode mode, Function<void (CommandResult&&)>&& completionHandler)
1639 if (!m_toplevelBrowsingContext) {
1640 completionHandler(CommandResult::fail(CommandResult::ErrorCode::NoSuchWindow));
1644 handleUserPrompts([this, script, argumentsArray = WTFMove(argumentsArray), mode, completionHandler = WTFMove(completionHandler)](CommandResult&& result) mutable {
1645 if (result.isError()) {
1646 completionHandler(WTFMove(result));
1649 RefPtr<JSON::Array> arguments = JSON::Array::create();
1650 unsigned argumentsLength = argumentsArray->length();
1651 for (unsigned i = 0; i < argumentsLength; ++i) {
1652 if (auto argument = argumentsArray->get(i)) {
1653 if (auto element = extractElement(*argument))
1654 arguments->pushString(element->toJSONString());
1656 arguments->pushString(argument->toJSONString());
1660 RefPtr<JSON::Object> parameters = JSON::Object::create();
1661 parameters->setString(ASCIILiteral("browsingContextHandle"), m_toplevelBrowsingContext.value());
1662 if (m_currentBrowsingContext)
1663 parameters->setString(ASCIILiteral("frameHandle"), m_currentBrowsingContext.value());
1664 parameters->setString(ASCIILiteral("function"), "function(){" + script + '}');
1665 parameters->setArray(ASCIILiteral("arguments"), WTFMove(arguments));
1666 if (mode == ExecuteScriptMode::Async) {
1667 parameters->setBoolean(ASCIILiteral("expectsImplicitCallbackArgument"), true);
1668 if (m_timeouts.script)
1669 parameters->setInteger(ASCIILiteral("callbackTimeout"), m_timeouts.script.value().millisecondsAs<int>());
1671 m_host->sendCommandToBackend(ASCIILiteral("evaluateJavaScriptFunction"), WTFMove(parameters), [this, protectedThis = makeRef(*this), completionHandler = WTFMove(completionHandler)](SessionHost::CommandResponse&& response) mutable {
1672 if (response.isError || !response.responseObject) {
1673 auto result = CommandResult::fail(WTFMove(response.responseObject));
1674 if (result.errorCode() == CommandResult::ErrorCode::UnexpectedAlertOpen)
1675 handleUnexpectedAlertOpen(WTFMove(completionHandler));
1677 completionHandler(WTFMove(result));
1681 if (!response.responseObject->getString(ASCIILiteral("result"), valueString)) {
1682 completionHandler(CommandResult::fail(CommandResult::ErrorCode::UnknownError));
1685 if (valueString.isEmpty()) {
1686 completionHandler(CommandResult::success());
1689 RefPtr<JSON::Value> resultValue;
1690 if (!JSON::Value::parseJSON(valueString, resultValue)) {
1691 completionHandler(CommandResult::fail(CommandResult::ErrorCode::UnknownError));
1694 completionHandler(CommandResult::success(handleScriptResult(WTFMove(resultValue))));
1699 void Session::performMouseInteraction(int x, int y, MouseButton button, MouseInteraction interaction, Function<void (CommandResult&&)>&& completionHandler)
1701 RefPtr<JSON::Object> parameters = JSON::Object::create();
1702 parameters->setString(ASCIILiteral("handle"), m_toplevelBrowsingContext.value());
1703 RefPtr<JSON::Object> position = JSON::Object::create();
1704 position->setInteger(ASCIILiteral("x"), x);
1705 position->setInteger(ASCIILiteral("y"), y);
1706 parameters->setObject(ASCIILiteral("position"), WTFMove(position));
1708 case MouseButton::None:
1709 parameters->setString(ASCIILiteral("button"), ASCIILiteral("None"));
1711 case MouseButton::Left:
1712 parameters->setString(ASCIILiteral("button"), ASCIILiteral("Left"));
1714 case MouseButton::Middle:
1715 parameters->setString(ASCIILiteral("button"), ASCIILiteral("Middle"));
1717 case MouseButton::Right:
1718 parameters->setString(ASCIILiteral("button"), ASCIILiteral("Right"));
1721 switch (interaction) {
1722 case MouseInteraction::Move:
1723 parameters->setString(ASCIILiteral("interaction"), ASCIILiteral("Move"));
1725 case MouseInteraction::Down:
1726 parameters->setString(ASCIILiteral("interaction"), ASCIILiteral("Down"));
1728 case MouseInteraction::Up:
1729 parameters->setString(ASCIILiteral("interaction"), ASCIILiteral("Up"));
1731 case MouseInteraction::SingleClick:
1732 parameters->setString(ASCIILiteral("interaction"), ASCIILiteral("SingleClick"));
1734 case MouseInteraction::DoubleClick:
1735 parameters->setString(ASCIILiteral("interaction"), ASCIILiteral("DoubleClick"));
1738 parameters->setArray(ASCIILiteral("modifiers"), JSON::Array::create());
1739 m_host->sendCommandToBackend(ASCIILiteral("performMouseInteraction"), WTFMove(parameters), [this, protectedThis = makeRef(*this), completionHandler = WTFMove(completionHandler)](SessionHost::CommandResponse&& response) {
1740 if (response.isError) {
1741 completionHandler(CommandResult::fail(WTFMove(response.responseObject)));
1744 completionHandler(CommandResult::success());
1748 void Session::performKeyboardInteractions(Vector<KeyboardInteraction>&& interactions, Function<void (CommandResult&&)>&& completionHandler)
1750 RefPtr<JSON::Object> parameters = JSON::Object::create();
1751 parameters->setString(ASCIILiteral("handle"), m_toplevelBrowsingContext.value());
1752 RefPtr<JSON::Array> interactionsArray = JSON::Array::create();
1753 for (const auto& interaction : interactions) {
1754 RefPtr<JSON::Object> interactionObject = JSON::Object::create();
1755 switch (interaction.type) {
1756 case KeyboardInteractionType::KeyPress:
1757 interactionObject->setString(ASCIILiteral("type"), ASCIILiteral("KeyPress"));
1759 case KeyboardInteractionType::KeyRelease:
1760 interactionObject->setString(ASCIILiteral("type"), ASCIILiteral("KeyRelease"));
1762 case KeyboardInteractionType::InsertByKey:
1763 interactionObject->setString(ASCIILiteral("type"), ASCIILiteral("InsertByKey"));
1766 if (interaction.key)
1767 interactionObject->setString(ASCIILiteral("key"), interaction.key.value());
1768 if (interaction.text)
1769 interactionObject->setString(ASCIILiteral("text"), interaction.text.value());
1770 interactionsArray->pushObject(WTFMove(interactionObject));
1772 parameters->setArray(ASCIILiteral("interactions"), WTFMove(interactionsArray));
1773 m_host->sendCommandToBackend(ASCIILiteral("performKeyboardInteractions"), WTFMove(parameters), [this, protectedThis = makeRef(*this), completionHandler = WTFMove(completionHandler)](SessionHost::CommandResponse&& response) {
1774 if (response.isError) {
1775 completionHandler(CommandResult::fail(WTFMove(response.responseObject)));
1778 completionHandler(CommandResult::success());
1782 static std::optional<Session::Cookie> parseAutomationCookie(const JSON::Object& cookieObject)
1784 Session::Cookie cookie;
1785 if (!cookieObject.getString(ASCIILiteral("name"), cookie.name))
1786 return std::nullopt;
1787 if (!cookieObject.getString(ASCIILiteral("value"), cookie.value))
1788 return std::nullopt;
1791 if (cookieObject.getString(ASCIILiteral("path"), path))
1794 if (cookieObject.getString(ASCIILiteral("domain"), domain))
1795 cookie.domain = domain;
1797 if (cookieObject.getBoolean(ASCIILiteral("secure"), secure))
1798 cookie.secure = secure;
1800 if (cookieObject.getBoolean(ASCIILiteral("httpOnly"), httpOnly))
1801 cookie.httpOnly = httpOnly;
1802 bool session = false;
1803 cookieObject.getBoolean(ASCIILiteral("session"), session);
1806 if (cookieObject.getDouble(ASCIILiteral("expires"), expiry))
1807 cookie.expiry = expiry;
1813 static RefPtr<JSON::Object> builtAutomationCookie(const Session::Cookie& cookie)
1815 RefPtr<JSON::Object> cookieObject = JSON::Object::create();
1816 cookieObject->setString(ASCIILiteral("name"), cookie.name);
1817 cookieObject->setString(ASCIILiteral("value"), cookie.value);
1818 cookieObject->setString(ASCIILiteral("path"), cookie.path.value_or("/"));
1819 cookieObject->setString(ASCIILiteral("domain"), cookie.domain.value_or(emptyString()));
1820 cookieObject->setBoolean(ASCIILiteral("secure"), cookie.secure.value_or(false));
1821 cookieObject->setBoolean(ASCIILiteral("httpOnly"), cookie.httpOnly.value_or(false));
1822 cookieObject->setBoolean(ASCIILiteral("session"), !cookie.expiry);
1823 cookieObject->setDouble(ASCIILiteral("expires"), cookie.expiry.value_or(0));
1824 return cookieObject;
1827 static RefPtr<JSON::Object> serializeCookie(const Session::Cookie& cookie)
1829 RefPtr<JSON::Object> cookieObject = JSON::Object::create();
1830 cookieObject->setString(ASCIILiteral("name"), cookie.name);
1831 cookieObject->setString(ASCIILiteral("value"), cookie.value);
1833 cookieObject->setString(ASCIILiteral("path"), cookie.path.value());
1835 cookieObject->setString(ASCIILiteral("domain"), cookie.domain.value());
1837 cookieObject->setBoolean(ASCIILiteral("secure"), cookie.secure.value());
1838 if (cookie.httpOnly)
1839 cookieObject->setBoolean(ASCIILiteral("httpOnly"), cookie.httpOnly.value());
1841 cookieObject->setInteger(ASCIILiteral("expiry"), cookie.expiry.value());
1842 return cookieObject;
1845 void Session::getAllCookies(Function<void (CommandResult&&)>&& completionHandler)
1847 if (!m_toplevelBrowsingContext) {
1848 completionHandler(CommandResult::fail(CommandResult::ErrorCode::NoSuchWindow));
1852 handleUserPrompts([this, completionHandler = WTFMove(completionHandler)](CommandResult&& result) mutable {
1853 if (result.isError()) {
1854 completionHandler(WTFMove(result));
1858 RefPtr<JSON::Object> parameters = JSON::Object::create();
1859 parameters->setString(ASCIILiteral("browsingContextHandle"), m_toplevelBrowsingContext.value());
1860 m_host->sendCommandToBackend(ASCIILiteral("getAllCookies"), WTFMove(parameters), [this, protectedThis = makeRef(*this), completionHandler = WTFMove(completionHandler)](SessionHost::CommandResponse&& response) mutable {
1861 if (response.isError || !response.responseObject) {
1862 completionHandler(CommandResult::fail(WTFMove(response.responseObject)));
1865 RefPtr<JSON::Array> cookiesArray;
1866 if (!response.responseObject->getArray(ASCIILiteral("cookies"), cookiesArray)) {
1867 completionHandler(CommandResult::fail(CommandResult::ErrorCode::UnknownError));
1870 RefPtr<JSON::Array> cookies = JSON::Array::create();
1871 for (unsigned i = 0; i < cookiesArray->length(); ++i) {
1872 RefPtr<JSON::Value> cookieValue = cookiesArray->get(i);
1873 RefPtr<JSON::Object> cookieObject;
1874 if (!cookieValue->asObject(cookieObject)) {
1875 completionHandler(CommandResult::fail(CommandResult::ErrorCode::UnknownError));
1879 auto cookie = parseAutomationCookie(*cookieObject);
1881 completionHandler(CommandResult::fail(CommandResult::ErrorCode::UnknownError));
1884 cookies->pushObject(serializeCookie(cookie.value()));
1886 completionHandler(CommandResult::success(WTFMove(cookies)));
1891 void Session::getNamedCookie(const String& name, Function<void (CommandResult&&)>&& completionHandler)
1893 getAllCookies([this, name, completionHandler = WTFMove(completionHandler)](CommandResult&& result) mutable {
1894 if (result.isError()) {
1895 completionHandler(WTFMove(result));
1898 RefPtr<JSON::Array> cookiesArray;
1899 result.result()->asArray(cookiesArray);
1900 for (unsigned i = 0; i < cookiesArray->length(); ++i) {
1901 RefPtr<JSON::Value> cookieValue = cookiesArray->get(i);
1902 RefPtr<JSON::Object> cookieObject;
1903 cookieValue->asObject(cookieObject);
1905 cookieObject->getString(ASCIILiteral("name"), cookieName);
1906 if (cookieName == name) {
1907 completionHandler(CommandResult::success(WTFMove(cookieObject)));
1911 completionHandler(CommandResult::fail(CommandResult::ErrorCode::NoSuchCookie));
1915 void Session::addCookie(const Cookie& cookie, Function<void (CommandResult&&)>&& completionHandler)
1917 if (!m_toplevelBrowsingContext) {
1918 completionHandler(CommandResult::fail(CommandResult::ErrorCode::NoSuchWindow));
1922 handleUserPrompts([this, cookie = builtAutomationCookie(cookie), completionHandler = WTFMove(completionHandler)](CommandResult&& result) mutable {
1923 if (result.isError()) {
1924 completionHandler(WTFMove(result));
1927 RefPtr<JSON::Object> parameters = JSON::Object::create();
1928 parameters->setString(ASCIILiteral("browsingContextHandle"), m_toplevelBrowsingContext.value());
1929 parameters->setObject(ASCIILiteral("cookie"), WTFMove(cookie));
1930 m_host->sendCommandToBackend(ASCIILiteral("addSingleCookie"), WTFMove(parameters), [this, protectedThis = makeRef(*this), completionHandler = WTFMove(completionHandler)](SessionHost::CommandResponse&& response) {
1931 if (response.isError) {
1932 completionHandler(CommandResult::fail(WTFMove(response.responseObject)));
1935 completionHandler(CommandResult::success());
1940 void Session::deleteCookie(const String& name, Function<void (CommandResult&&)>&& completionHandler)
1942 if (!m_toplevelBrowsingContext) {
1943 completionHandler(CommandResult::fail(CommandResult::ErrorCode::NoSuchWindow));
1947 handleUserPrompts([this, name, completionHandler = WTFMove(completionHandler)](CommandResult&& result) mutable {
1948 if (result.isError()) {
1949 completionHandler(WTFMove(result));
1952 RefPtr<JSON::Object> parameters = JSON::Object::create();
1953 parameters->setString(ASCIILiteral("browsingContextHandle"), m_toplevelBrowsingContext.value());
1954 parameters->setString(ASCIILiteral("cookieName"), name);
1955 m_host->sendCommandToBackend(ASCIILiteral("deleteSingleCookie"), WTFMove(parameters), [this, protectedThis = makeRef(*this), completionHandler = WTFMove(completionHandler)](SessionHost::CommandResponse&& response) {
1956 if (response.isError) {
1957 completionHandler(CommandResult::fail(WTFMove(response.responseObject)));
1960 completionHandler(CommandResult::success());
1965 void Session::deleteAllCookies(Function<void (CommandResult&&)>&& completionHandler)
1967 if (!m_toplevelBrowsingContext) {
1968 completionHandler(CommandResult::fail(CommandResult::ErrorCode::NoSuchWindow));
1972 handleUserPrompts([this, completionHandler = WTFMove(completionHandler)](CommandResult&& result) mutable {
1973 if (result.isError()) {
1974 completionHandler(WTFMove(result));
1977 RefPtr<JSON::Object> parameters = JSON::Object::create();
1978 parameters->setString(ASCIILiteral("browsingContextHandle"), m_toplevelBrowsingContext.value());
1979 m_host->sendCommandToBackend(ASCIILiteral("deleteAllCookies"), WTFMove(parameters), [this, protectedThis = makeRef(*this), completionHandler = WTFMove(completionHandler)](SessionHost::CommandResponse&& response) {
1980 if (response.isError) {
1981 completionHandler(CommandResult::fail(WTFMove(response.responseObject)));
1984 completionHandler(CommandResult::success());
1989 void Session::dismissAlert(Function<void (CommandResult&&)>&& completionHandler)
1991 if (!m_toplevelBrowsingContext) {
1992 completionHandler(CommandResult::fail(CommandResult::ErrorCode::NoSuchWindow));
1996 RefPtr<JSON::Object> parameters = JSON::Object::create();
1997 parameters->setString(ASCIILiteral("browsingContextHandle"), m_toplevelBrowsingContext.value());
1998 m_host->sendCommandToBackend(ASCIILiteral("dismissCurrentJavaScriptDialog"), WTFMove(parameters), [this, protectedThis = makeRef(*this), completionHandler = WTFMove(completionHandler)](SessionHost::CommandResponse&& response) {
1999 if (response.isError) {
2000 completionHandler(CommandResult::fail(WTFMove(response.responseObject)));
2003 completionHandler(CommandResult::success());
2007 void Session::acceptAlert(Function<void (CommandResult&&)>&& completionHandler)
2009 if (!m_toplevelBrowsingContext) {
2010 completionHandler(CommandResult::fail(CommandResult::ErrorCode::NoSuchWindow));
2014 RefPtr<JSON::Object> parameters = JSON::Object::create();
2015 parameters->setString(ASCIILiteral("browsingContextHandle"), m_toplevelBrowsingContext.value());
2016 m_host->sendCommandToBackend(ASCIILiteral("acceptCurrentJavaScriptDialog"), WTFMove(parameters), [this, protectedThis = makeRef(*this), completionHandler = WTFMove(completionHandler)](SessionHost::CommandResponse&& response) {
2017 if (response.isError) {
2018 completionHandler(CommandResult::fail(WTFMove(response.responseObject)));
2021 completionHandler(CommandResult::success());
2025 void Session::getAlertText(Function<void (CommandResult&&)>&& completionHandler)
2027 if (!m_toplevelBrowsingContext) {
2028 completionHandler(CommandResult::fail(CommandResult::ErrorCode::NoSuchWindow));
2032 RefPtr<JSON::Object> parameters = JSON::Object::create();
2033 parameters->setString(ASCIILiteral("browsingContextHandle"), m_toplevelBrowsingContext.value());
2034 m_host->sendCommandToBackend(ASCIILiteral("messageOfCurrentJavaScriptDialog"), WTFMove(parameters), [this, protectedThis = makeRef(*this), completionHandler = WTFMove(completionHandler)](SessionHost::CommandResponse&& response) {
2035 if (response.isError || !response.responseObject) {
2036 completionHandler(CommandResult::fail(WTFMove(response.responseObject)));
2040 if (!response.responseObject->getString(ASCIILiteral("message"), valueString)) {
2041 completionHandler(CommandResult::fail(CommandResult::ErrorCode::UnknownError));
2044 completionHandler(CommandResult::success(JSON::Value::create(valueString)));
2048 void Session::sendAlertText(const String& text, Function<void (CommandResult&&)>&& completionHandler)
2050 if (!m_toplevelBrowsingContext) {
2051 completionHandler(CommandResult::fail(CommandResult::ErrorCode::NoSuchWindow));
2055 RefPtr<JSON::Object> parameters = JSON::Object::create();
2056 parameters->setString(ASCIILiteral("browsingContextHandle"), m_toplevelBrowsingContext.value());
2057 parameters->setString(ASCIILiteral("userInput"), text);
2058 m_host->sendCommandToBackend(ASCIILiteral("setUserInputForCurrentJavaScriptPrompt"), WTFMove(parameters), [this, protectedThis = makeRef(*this), completionHandler = WTFMove(completionHandler)](SessionHost::CommandResponse&& response) {
2059 if (response.isError) {
2060 completionHandler(CommandResult::fail(WTFMove(response.responseObject)));
2063 completionHandler(CommandResult::success());
2067 void Session::takeScreenshot(std::optional<String> elementID, std::optional<bool> scrollIntoView, Function<void (CommandResult&&)>&& completionHandler)
2069 if (!m_toplevelBrowsingContext) {
2070 completionHandler(CommandResult::fail(CommandResult::ErrorCode::NoSuchWindow));
2074 handleUserPrompts([this, elementID, scrollIntoView, completionHandler = WTFMove(completionHandler)](CommandResult&& result) mutable {
2075 if (result.isError()) {
2076 completionHandler(WTFMove(result));
2079 RefPtr<JSON::Object> parameters = JSON::Object::create();
2080 parameters->setString(ASCIILiteral("handle"), m_toplevelBrowsingContext.value());
2081 if (m_currentBrowsingContext)
2082 parameters->setString(ASCIILiteral("frameHandle"), m_currentBrowsingContext.value());
2084 parameters->setString(ASCIILiteral("nodeHandle"), elementID.value());
2085 if (scrollIntoView.value_or(false))
2086 parameters->setBoolean(ASCIILiteral("scrollIntoViewIfNeeded"), true);
2087 m_host->sendCommandToBackend(ASCIILiteral("takeScreenshot"), WTFMove(parameters), [this, protectedThis = makeRef(*this), completionHandler = WTFMove(completionHandler)](SessionHost::CommandResponse&& response) mutable {
2088 if (response.isError || !response.responseObject) {
2089 completionHandler(CommandResult::fail(WTFMove(response.responseObject)));
2093 if (!response.responseObject->getString(ASCIILiteral("data"), data)) {
2094 completionHandler(CommandResult::fail(CommandResult::ErrorCode::UnknownError));
2097 completionHandler(CommandResult::success(JSON::Value::create(data)));
2102 } // namespace WebDriver