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 <inspector/InspectorValues.h>
33 #include <wtf/CryptographicallyRandomNumber.h>
34 #include <wtf/HexNumber.h>
37 using namespace Inspector;
41 // The web element identifier is a constant defined by the spec in Section 11 Elements.
42 // https://www.w3.org/TR/webdriver/#elements
43 static const String webElementIdentifier = ASCIILiteral("element-6066-11e4-a52e-4f735466cecf");
45 Session::Session(std::unique_ptr<SessionHost>&& host)
46 : m_host(WTFMove(host))
47 , m_id(createCanonicalUUIDString())
55 const Capabilities& Session::capabilities() const
57 return m_host->capabilities();
60 void Session::close(Function<void (CommandResult&&)>&& completionHandler)
62 if (!m_toplevelBrowsingContext) {
63 completionHandler(CommandResult::success());
67 RefPtr<InspectorObject> parameters = InspectorObject::create();
68 parameters->setString(ASCIILiteral("handle"), m_toplevelBrowsingContext.value());
69 m_host->sendCommandToBackend(ASCIILiteral("closeBrowsingContext"), WTFMove(parameters), [this, protectedThis = makeRef(*this), completionHandler = WTFMove(completionHandler)](SessionHost::CommandResponse&& response) {
70 if (response.isError) {
71 completionHandler(CommandResult::fail(WTFMove(response.responseObject)));
74 m_toplevelBrowsingContext = std::nullopt;
75 completionHandler(CommandResult::success());
79 void Session::setTimeouts(const Timeouts& timeouts, Function<void (CommandResult&&)>&& completionHandler)
82 m_timeouts.script = timeouts.script;
83 if (timeouts.pageLoad)
84 m_timeouts.pageLoad = timeouts.pageLoad;
85 if (timeouts.implicit)
86 m_timeouts.implicit = timeouts.implicit;
87 completionHandler(CommandResult::success());
90 void Session::createTopLevelBrowsingContext(Function<void (CommandResult&&)>&& completionHandler)
92 ASSERT(!m_toplevelBrowsingContext.value());
93 m_host->startAutomationSession(m_id, [this, protectedThis = makeRef(*this), completionHandler = WTFMove(completionHandler)]() mutable {
94 m_host->sendCommandToBackend(ASCIILiteral("createBrowsingContext"), nullptr, [this, protectedThis = makeRef(*this), completionHandler = WTFMove(completionHandler)](SessionHost::CommandResponse&& response) mutable {
95 if (response.isError || !response.responseObject) {
96 completionHandler(CommandResult::fail(WTFMove(response.responseObject)));
100 if (!response.responseObject->getString(ASCIILiteral("handle"), handle)) {
101 completionHandler(CommandResult::fail(CommandResult::ErrorCode::UnknownError));
104 m_toplevelBrowsingContext = handle;
105 completionHandler(CommandResult::success());
110 void Session::go(const String& url, Function<void (CommandResult&&)>&& completionHandler)
112 if (!m_toplevelBrowsingContext) {
113 completionHandler(CommandResult::fail(CommandResult::ErrorCode::NoSuchWindow));
117 RefPtr<InspectorObject> parameters = InspectorObject::create();
118 parameters->setString(ASCIILiteral("handle"), m_toplevelBrowsingContext.value());
119 parameters->setString(ASCIILiteral("url"), url);
120 m_host->sendCommandToBackend(ASCIILiteral("navigateBrowsingContext"), WTFMove(parameters), [this, protectedThis = makeRef(*this), completionHandler = WTFMove(completionHandler)](SessionHost::CommandResponse&& response) {
121 if (response.isError) {
122 completionHandler(CommandResult::fail(WTFMove(response.responseObject)));
125 m_browsingContext = std::nullopt;
126 completionHandler(CommandResult::success());
130 void Session::getCurrentURL(Function<void (CommandResult&&)>&& completionHandler)
132 if (!m_toplevelBrowsingContext) {
133 completionHandler(CommandResult::fail(CommandResult::ErrorCode::NoSuchWindow));
137 RefPtr<InspectorObject> parameters = InspectorObject::create();
138 parameters->setString(ASCIILiteral("handle"), m_toplevelBrowsingContext.value());
139 m_host->sendCommandToBackend(ASCIILiteral("getBrowsingContext"), WTFMove(parameters), [this, protectedThis = makeRef(*this), completionHandler = WTFMove(completionHandler)](SessionHost::CommandResponse&& response) {
140 if (response.isError || !response.responseObject) {
141 completionHandler(CommandResult::fail(WTFMove(response.responseObject)));
144 RefPtr<InspectorObject> browsingContext;
145 if (!response.responseObject->getObject("context", browsingContext)) {
146 completionHandler(CommandResult::fail(CommandResult::ErrorCode::UnknownError));
150 if (!browsingContext->getString("url", url)) {
151 completionHandler(CommandResult::fail(CommandResult::ErrorCode::UnknownError));
154 completionHandler(CommandResult::success(InspectorValue::create(url)));
158 void Session::back(Function<void (CommandResult&&)>&& completionHandler)
160 if (!m_toplevelBrowsingContext) {
161 completionHandler(CommandResult::fail(CommandResult::ErrorCode::NoSuchWindow));
165 RefPtr<InspectorObject> parameters = InspectorObject::create();
166 parameters->setString(ASCIILiteral("handle"), m_toplevelBrowsingContext.value());
167 m_host->sendCommandToBackend(ASCIILiteral("goBackInBrowsingContext"), WTFMove(parameters), [this, protectedThis = makeRef(*this), completionHandler = WTFMove(completionHandler)](SessionHost::CommandResponse&& response) {
168 if (response.isError) {
169 completionHandler(CommandResult::fail(WTFMove(response.responseObject)));
172 m_browsingContext = std::nullopt;
173 completionHandler(CommandResult::success());
177 void Session::forward(Function<void (CommandResult&&)>&& completionHandler)
179 if (!m_toplevelBrowsingContext) {
180 completionHandler(CommandResult::fail(CommandResult::ErrorCode::NoSuchWindow));
184 RefPtr<InspectorObject> parameters = InspectorObject::create();
185 parameters->setString(ASCIILiteral("handle"), m_toplevelBrowsingContext.value());
186 m_host->sendCommandToBackend(ASCIILiteral("goForwardInBrowsingContext"), WTFMove(parameters), [this, protectedThis = makeRef(*this), completionHandler = WTFMove(completionHandler)](SessionHost::CommandResponse&& response) {
187 if (response.isError) {
188 completionHandler(CommandResult::fail(WTFMove(response.responseObject)));
191 m_browsingContext = std::nullopt;
192 completionHandler(CommandResult::success());
196 void Session::refresh(Function<void (CommandResult&&)>&& completionHandler)
198 if (!m_toplevelBrowsingContext) {
199 completionHandler(CommandResult::fail(CommandResult::ErrorCode::NoSuchWindow));
203 RefPtr<InspectorObject> parameters = InspectorObject::create();
204 parameters->setString(ASCIILiteral("handle"), m_toplevelBrowsingContext.value());
205 m_host->sendCommandToBackend(ASCIILiteral("reloadBrowsingContext"), WTFMove(parameters), [this, protectedThis = makeRef(*this), completionHandler = WTFMove(completionHandler)](SessionHost::CommandResponse&& response) {
206 if (response.isError) {
207 completionHandler(CommandResult::fail(WTFMove(response.responseObject)));
210 m_browsingContext = std::nullopt;
211 completionHandler(CommandResult::success());
215 void Session::getTitle(Function<void (CommandResult&&)>&& completionHandler)
217 if (!m_toplevelBrowsingContext) {
218 completionHandler(CommandResult::fail(CommandResult::ErrorCode::NoSuchWindow));
222 RefPtr<InspectorObject> parameters = InspectorObject::create();
223 parameters->setString(ASCIILiteral("browsingContextHandle"), m_toplevelBrowsingContext.value());
224 parameters->setString(ASCIILiteral("function"), ASCIILiteral("function() { return document.title; }"));
225 parameters->setArray(ASCIILiteral("arguments"), InspectorArray::create());
226 m_host->sendCommandToBackend(ASCIILiteral("evaluateJavaScriptFunction"), WTFMove(parameters), [this, protectedThis = makeRef(*this), completionHandler = WTFMove(completionHandler)](SessionHost::CommandResponse&& response) {
227 if (response.isError || !response.responseObject) {
228 completionHandler(CommandResult::fail(WTFMove(response.responseObject)));
232 if (!response.responseObject->getString(ASCIILiteral("result"), valueString)) {
233 completionHandler(CommandResult::fail(CommandResult::ErrorCode::UnknownError));
236 RefPtr<InspectorValue> resultValue;
237 if (!InspectorValue::parseJSON(valueString, resultValue)) {
238 completionHandler(CommandResult::fail(CommandResult::ErrorCode::UnknownError));
241 completionHandler(CommandResult::success(WTFMove(resultValue)));
245 void Session::getWindowHandle(Function<void (CommandResult&&)>&& completionHandler)
247 if (!m_toplevelBrowsingContext) {
248 completionHandler(CommandResult::fail(CommandResult::ErrorCode::NoSuchWindow));
252 RefPtr<InspectorObject> parameters = InspectorObject::create();
253 parameters->setString(ASCIILiteral("handle"), m_toplevelBrowsingContext.value());
254 m_host->sendCommandToBackend(ASCIILiteral("getBrowsingContext"), WTFMove(parameters), [this, protectedThis = makeRef(*this), completionHandler = WTFMove(completionHandler)](SessionHost::CommandResponse&& response) {
255 if (response.isError || !response.responseObject) {
256 completionHandler(CommandResult::fail(WTFMove(response.responseObject)));
259 RefPtr<InspectorObject> browsingContext;
260 if (!response.responseObject->getObject(ASCIILiteral("context"), browsingContext)) {
261 completionHandler(CommandResult::fail(CommandResult::ErrorCode::UnknownError));
265 if (!browsingContext->getString(ASCIILiteral("handle"), handle)) {
266 completionHandler(CommandResult::fail(CommandResult::ErrorCode::UnknownError));
269 completionHandler(CommandResult::success(InspectorValue::create(handle)));
273 void Session::closeWindow(Function<void (CommandResult&&)>&& completionHandler)
275 if (!m_toplevelBrowsingContext) {
276 completionHandler(CommandResult::fail(CommandResult::ErrorCode::NoSuchWindow));
280 close(WTFMove(completionHandler));
283 void Session::switchToWindow(const String& windowHandle, Function<void (CommandResult&&)>&& completionHandler)
285 RefPtr<InspectorObject> parameters = InspectorObject::create();
286 parameters->setString(ASCIILiteral("browsingContextHandle"), windowHandle);
287 m_host->sendCommandToBackend(ASCIILiteral("switchToBrowsingContext"), WTFMove(parameters), [this, protectedThis = makeRef(*this), windowHandle, completionHandler = WTFMove(completionHandler)](SessionHost::CommandResponse&& response) {
288 if (response.isError) {
289 completionHandler(CommandResult::fail(WTFMove(response.responseObject)));
292 m_toplevelBrowsingContext = windowHandle;
293 completionHandler(CommandResult::success());
297 void Session::getWindowHandles(Function<void (CommandResult&&)>&& completionHandler)
299 if (!m_toplevelBrowsingContext) {
300 completionHandler(CommandResult::fail(CommandResult::ErrorCode::NoSuchWindow));
304 m_host->sendCommandToBackend(ASCIILiteral("getBrowsingContexts"), InspectorObject::create(), [this, protectedThis = makeRef(*this), completionHandler = WTFMove(completionHandler)](SessionHost::CommandResponse&& response) {
305 if (response.isError || !response.responseObject) {
306 completionHandler(CommandResult::fail(WTFMove(response.responseObject)));
309 RefPtr<InspectorArray> browsingContextArray;
310 if (!response.responseObject->getArray(ASCIILiteral("contexts"), browsingContextArray)) {
311 completionHandler(CommandResult::fail(CommandResult::ErrorCode::UnknownError));
314 RefPtr<InspectorArray> windowHandles = InspectorArray::create();
315 for (unsigned i = 0; i < browsingContextArray->length(); ++i) {
316 RefPtr<InspectorValue> browsingContextValue = browsingContextArray->get(i);
317 RefPtr<InspectorObject> browsingContext;
318 if (!browsingContextValue->asObject(browsingContext)) {
319 completionHandler(CommandResult::fail(CommandResult::ErrorCode::UnknownError));
324 if (!browsingContext->getString(ASCIILiteral("handle"), handle)) {
325 completionHandler(CommandResult::fail(CommandResult::ErrorCode::UnknownError));
329 windowHandles->pushString(handle);
331 completionHandler(CommandResult::success(WTFMove(windowHandles)));
335 void Session::switchToFrame(RefPtr<InspectorValue>&& frameID, Function<void (CommandResult&&)>&& completionHandler)
337 if (!m_toplevelBrowsingContext) {
338 completionHandler(CommandResult::fail(CommandResult::ErrorCode::NoSuchWindow));
342 if (frameID->isNull()) {
343 m_browsingContext = std::nullopt;
344 completionHandler(CommandResult::success());
348 RefPtr<InspectorObject> parameters = InspectorObject::create();
349 parameters->setString(ASCIILiteral("browsingContextHandle"), m_toplevelBrowsingContext.value());
350 if (m_browsingContext)
351 parameters->setString(ASCIILiteral("frameHandle"), m_browsingContext.value());
354 if (frameID->asInteger(frameIndex)) {
355 if (frameIndex < 0 || frameIndex > USHRT_MAX) {
356 completionHandler(CommandResult::fail(CommandResult::ErrorCode::NoSuchFrame));
359 parameters->setInteger(ASCIILiteral("ordinal"), frameIndex);
361 String frameElementID = extractElementID(*frameID);
362 if (!frameElementID.isEmpty())
363 parameters->setString(ASCIILiteral("nodeHandle"), frameElementID);
366 if (!frameID->asString(frameName)) {
367 completionHandler(CommandResult::fail(CommandResult::ErrorCode::NoSuchFrame));
370 parameters->setString(ASCIILiteral("name"), frameName);
374 m_host->sendCommandToBackend(ASCIILiteral("resolveChildFrameHandle"), WTFMove(parameters), [this, protectedThis = makeRef(*this), completionHandler = WTFMove(completionHandler)](SessionHost::CommandResponse&& response) {
375 if (response.isError || !response.responseObject) {
376 completionHandler(CommandResult::fail(WTFMove(response.responseObject)));
380 if (!response.responseObject->getString(ASCIILiteral("result"), frameHandle)) {
381 completionHandler(CommandResult::fail(CommandResult::ErrorCode::UnknownError));
384 m_browsingContext = frameHandle;
385 completionHandler(CommandResult::success());
389 void Session::switchToParentFrame(Function<void (CommandResult&&)>&& completionHandler)
391 if (!m_toplevelBrowsingContext) {
392 completionHandler(CommandResult::fail(CommandResult::ErrorCode::NoSuchWindow));
396 if (!m_browsingContext) {
397 completionHandler(CommandResult::success());
401 RefPtr<InspectorObject> parameters = InspectorObject::create();
402 parameters->setString(ASCIILiteral("browsingContextHandle"), m_toplevelBrowsingContext.value());
403 parameters->setString(ASCIILiteral("frameHandle"), m_browsingContext.value());
404 m_host->sendCommandToBackend(ASCIILiteral("resolveParentFrameHandle"), WTFMove(parameters), [this, protectedThis = makeRef(*this), completionHandler = WTFMove(completionHandler)](SessionHost::CommandResponse&& response) {
405 if (response.isError || !response.responseObject) {
406 completionHandler(CommandResult::fail(WTFMove(response.responseObject)));
410 if (!response.responseObject->getString(ASCIILiteral("result"), frameHandle)) {
411 completionHandler(CommandResult::fail(CommandResult::ErrorCode::UnknownError));
414 m_browsingContext = frameHandle;
415 completionHandler(CommandResult::success());
419 void Session::getWindowPosition(Function<void (CommandResult&&)>&& completionHandler)
421 if (!m_toplevelBrowsingContext) {
422 completionHandler(CommandResult::fail(CommandResult::ErrorCode::NoSuchWindow));
426 RefPtr<InspectorObject> parameters = InspectorObject::create();
427 parameters->setString(ASCIILiteral("handle"), m_toplevelBrowsingContext.value());
428 m_host->sendCommandToBackend(ASCIILiteral("getBrowsingContext"), WTFMove(parameters), [this, protectedThis = makeRef(*this), completionHandler = WTFMove(completionHandler)](SessionHost::CommandResponse&& response) {
429 if (response.isError || !response.responseObject) {
430 completionHandler(CommandResult::fail(WTFMove(response.responseObject)));
433 RefPtr<InspectorObject> browsingContext;
434 if (!response.responseObject->getObject(ASCIILiteral("context"), browsingContext)) {
435 completionHandler(CommandResult::fail(CommandResult::ErrorCode::UnknownError));
438 RefPtr<InspectorObject> windowOrigin;
439 if (!browsingContext->getObject(ASCIILiteral("windowOrigin"), windowOrigin)) {
440 completionHandler(CommandResult::fail(CommandResult::ErrorCode::UnknownError));
443 completionHandler(CommandResult::success(WTFMove(windowOrigin)));
447 void Session::setWindowPosition(int windowX, int windowY, Function<void (CommandResult&&)>&& completionHandler)
449 if (!m_toplevelBrowsingContext) {
450 completionHandler(CommandResult::fail(CommandResult::ErrorCode::NoSuchWindow));
454 RefPtr<InspectorObject> parameters = InspectorObject::create();
455 parameters->setString(ASCIILiteral("handle"), m_toplevelBrowsingContext.value());
456 RefPtr<InspectorObject> windowOrigin = InspectorObject::create();
457 windowOrigin->setInteger("x", windowX);
458 windowOrigin->setInteger("y", windowY);
459 parameters->setObject(ASCIILiteral("origin"), WTFMove(windowOrigin));
460 m_host->sendCommandToBackend(ASCIILiteral("moveWindowOfBrowsingContext"), WTFMove(parameters), [this, protectedThis = makeRef(*this), completionHandler = WTFMove(completionHandler)](SessionHost::CommandResponse&& response) {
461 if (response.isError) {
462 completionHandler(CommandResult::fail(WTFMove(response.responseObject)));
465 completionHandler(CommandResult::success());
469 void Session::getWindowSize(Function<void (CommandResult&&)>&& completionHandler)
471 if (!m_toplevelBrowsingContext) {
472 completionHandler(CommandResult::fail(CommandResult::ErrorCode::NoSuchWindow));
476 RefPtr<InspectorObject> parameters = InspectorObject::create();
477 parameters->setString(ASCIILiteral("handle"), m_toplevelBrowsingContext.value());
478 m_host->sendCommandToBackend(ASCIILiteral("getBrowsingContext"), WTFMove(parameters), [this, protectedThis = makeRef(*this), completionHandler = WTFMove(completionHandler)](SessionHost::CommandResponse&& response) {
479 if (response.isError || !response.responseObject) {
480 completionHandler(CommandResult::fail(WTFMove(response.responseObject)));
483 RefPtr<InspectorObject> browsingContext;
484 if (!response.responseObject->getObject(ASCIILiteral("context"), browsingContext)) {
485 completionHandler(CommandResult::fail(CommandResult::ErrorCode::UnknownError));
488 RefPtr<InspectorObject> windowSize;
489 if (!browsingContext->getObject(ASCIILiteral("windowSize"), windowSize)) {
490 completionHandler(CommandResult::fail(CommandResult::ErrorCode::UnknownError));
493 completionHandler(CommandResult::success(WTFMove(windowSize)));
497 void Session::setWindowSize(int windowWidth, int windowHeight, Function<void (CommandResult&&)>&& completionHandler)
499 if (!m_toplevelBrowsingContext) {
500 completionHandler(CommandResult::fail(CommandResult::ErrorCode::NoSuchWindow));
504 RefPtr<InspectorObject> parameters = InspectorObject::create();
505 parameters->setString(ASCIILiteral("handle"), m_toplevelBrowsingContext.value());
506 RefPtr<InspectorObject> windowSize = InspectorObject::create();
507 windowSize->setInteger("width", windowWidth);
508 windowSize->setInteger("height", windowHeight);
509 parameters->setObject(ASCIILiteral("size"), WTFMove(windowSize));
510 m_host->sendCommandToBackend(ASCIILiteral("resizeWindowOfBrowsingContext"), WTFMove(parameters), [this, protectedThis = makeRef(*this), completionHandler = WTFMove(completionHandler)](SessionHost::CommandResponse&& response) {
511 if (response.isError) {
512 completionHandler(CommandResult::fail(WTFMove(response.responseObject)));
515 completionHandler(CommandResult::success());
519 RefPtr<InspectorObject> Session::createElement(RefPtr<InspectorValue>&& value)
521 RefPtr<InspectorObject> valueObject;
522 if (!value->asObject(valueObject))
526 if (!valueObject->getString("session-node-" + m_id, elementID))
529 RefPtr<InspectorObject> elementObject = InspectorObject::create();
530 elementObject->setString(webElementIdentifier, elementID);
531 return elementObject;
534 RefPtr<InspectorObject> Session::createElement(const String& elementID)
536 RefPtr<InspectorObject> elementObject = InspectorObject::create();
537 elementObject->setString("session-node-" + m_id, elementID);
538 return elementObject;
541 RefPtr<InspectorObject> Session::extractElement(InspectorValue& value)
543 String elementID = extractElementID(value);
544 return !elementID.isEmpty() ? createElement(elementID) : nullptr;
547 String Session::extractElementID(InspectorValue& value)
549 RefPtr<InspectorObject> valueObject;
550 if (!value.asObject(valueObject))
551 return emptyString();
554 if (!valueObject->getString(webElementIdentifier, elementID))
555 return emptyString();
560 void Session::computeElementLayout(const String& elementID, OptionSet<ElementLayoutOption> options, Function<void (std::optional<Rect>&&, RefPtr<InspectorObject>&&)>&& completionHandler)
562 ASSERT(m_toplevelBrowsingContext.value());
564 RefPtr<InspectorObject> parameters = InspectorObject::create();
565 parameters->setString(ASCIILiteral("browsingContextHandle"), m_toplevelBrowsingContext.value());
566 parameters->setString(ASCIILiteral("frameHandle"), m_browsingContext.value());
567 parameters->setString(ASCIILiteral("nodeHandle"), elementID);
568 parameters->setBoolean(ASCIILiteral("scrollIntoViewIfNeeded"), options.contains(ElementLayoutOption::ScrollIntoViewIfNeeded));
569 parameters->setBoolean(ASCIILiteral("useViewportCoordinates"), options.contains(ElementLayoutOption::UseViewportCoordinates));
570 m_host->sendCommandToBackend(ASCIILiteral("computeElementLayout"), WTFMove(parameters), [this, protectedThis = makeRef(*this), completionHandler = WTFMove(completionHandler)](SessionHost::CommandResponse&& response) mutable {
571 if (response.isError || !response.responseObject) {
572 completionHandler(std::nullopt, WTFMove(response.responseObject));
575 RefPtr<InspectorObject> rectObject;
576 if (!response.responseObject->getObject(ASCIILiteral("rect"), rectObject)) {
577 completionHandler(std::nullopt, nullptr);
580 std::optional<int> elementX;
581 std::optional<int> elementY;
582 RefPtr<InspectorObject> elementPosition;
583 if (rectObject->getObject(ASCIILiteral("origin"), elementPosition)) {
585 if (elementPosition->getInteger(ASCIILiteral("x"), x) && elementPosition->getInteger(ASCIILiteral("y"), y)) {
590 if (!elementX || !elementY) {
591 completionHandler(std::nullopt, nullptr);
594 std::optional<int> elementWidth;
595 std::optional<int> elementHeight;
596 RefPtr<InspectorObject> elementSize;
597 if (rectObject->getObject(ASCIILiteral("size"), elementSize)) {
599 if (elementSize->getInteger(ASCIILiteral("width"), width) && elementSize->getInteger(ASCIILiteral("height"), height)) {
600 elementWidth = width;
601 elementHeight = height;
604 if (!elementWidth || !elementHeight) {
605 completionHandler(std::nullopt, nullptr);
608 Rect rect = { { elementX.value(), elementY.value() }, { elementWidth.value(), elementHeight.value() } };
609 completionHandler(rect, nullptr);
613 void Session::findElements(const String& strategy, const String& selector, FindElementsMode mode, const String& rootElementID, Function<void (CommandResult&&)>&& completionHandler)
615 if (!m_toplevelBrowsingContext) {
616 completionHandler(CommandResult::fail(CommandResult::ErrorCode::NoSuchWindow));
620 auto implicitWait = m_timeouts.implicit.value_or(0_s);
621 RefPtr<InspectorArray> arguments = InspectorArray::create();
622 arguments->pushString(InspectorValue::create(strategy)->toJSONString());
623 if (rootElementID.isEmpty())
624 arguments->pushString(InspectorValue::null()->toJSONString());
626 arguments->pushString(createElement(rootElementID)->toJSONString());
627 arguments->pushString(InspectorValue::create(selector)->toJSONString());
628 arguments->pushString(InspectorValue::create(mode == FindElementsMode::Single)->toJSONString());
629 arguments->pushString(InspectorValue::create(implicitWait.milliseconds())->toJSONString());
631 RefPtr<InspectorObject> parameters = InspectorObject::create();
632 parameters->setString(ASCIILiteral("browsingContextHandle"), m_toplevelBrowsingContext.value());
633 if (m_browsingContext)
634 parameters->setString(ASCIILiteral("frameHandle"), m_browsingContext.value());
635 parameters->setString(ASCIILiteral("function"), FindNodesJavaScript);
636 parameters->setArray(ASCIILiteral("arguments"), WTFMove(arguments));
637 parameters->setBoolean(ASCIILiteral("expectsImplicitCallbackArgument"), true);
638 // If there's an implicit wait, use one second more as callback timeout.
640 parameters->setInteger(ASCIILiteral("callbackTimeout"), Seconds(implicitWait + 1_s).millisecondsAs<int>());
642 m_host->sendCommandToBackend(ASCIILiteral("evaluateJavaScriptFunction"), WTFMove(parameters), [this, protectedThis = makeRef(*this), mode, completionHandler = WTFMove(completionHandler)](SessionHost::CommandResponse&& response) {
643 if (response.isError || !response.responseObject) {
644 completionHandler(CommandResult::fail(WTFMove(response.responseObject)));
648 if (!response.responseObject->getString(ASCIILiteral("result"), valueString)) {
649 completionHandler(CommandResult::fail(CommandResult::ErrorCode::UnknownError));
652 RefPtr<InspectorValue> resultValue;
653 if (!InspectorValue::parseJSON(valueString, resultValue)) {
654 completionHandler(CommandResult::fail(CommandResult::ErrorCode::UnknownError));
659 case FindElementsMode::Single: {
660 RefPtr<InspectorObject> elementObject = createElement(WTFMove(resultValue));
661 if (!elementObject) {
662 completionHandler(CommandResult::fail(CommandResult::ErrorCode::NoSuchElement));
665 completionHandler(CommandResult::success(WTFMove(elementObject)));
668 case FindElementsMode::Multiple: {
669 RefPtr<InspectorArray> elementsArray;
670 if (!resultValue->asArray(elementsArray)) {
671 completionHandler(CommandResult::fail(CommandResult::ErrorCode::NoSuchElement));
674 RefPtr<InspectorArray> elementObjectsArray = InspectorArray::create();
675 unsigned elementsArrayLength = elementsArray->length();
676 for (unsigned i = 0; i < elementsArrayLength; ++i) {
677 if (auto elementObject = createElement(elementsArray->get(i)))
678 elementObjectsArray->pushObject(WTFMove(elementObject));
680 completionHandler(CommandResult::success(WTFMove(elementObjectsArray)));
687 void Session::isElementSelected(const String& elementID, Function<void (CommandResult&&)>&& completionHandler)
689 if (!m_toplevelBrowsingContext) {
690 completionHandler(CommandResult::fail(CommandResult::ErrorCode::NoSuchWindow));
694 RefPtr<InspectorArray> arguments = InspectorArray::create();
695 arguments->pushString(createElement(elementID)->toJSONString());
696 arguments->pushString(InspectorValue::create("selected")->toJSONString());
698 RefPtr<InspectorObject> parameters = InspectorObject::create();
699 parameters->setString(ASCIILiteral("browsingContextHandle"), m_toplevelBrowsingContext.value());
700 if (m_browsingContext)
701 parameters->setString(ASCIILiteral("frameHandle"), m_browsingContext.value());
702 parameters->setString(ASCIILiteral("function"), ElementAttributeJavaScript);
703 parameters->setArray(ASCIILiteral("arguments"), WTFMove(arguments));
704 m_host->sendCommandToBackend(ASCIILiteral("evaluateJavaScriptFunction"), WTFMove(parameters), [this, protectedThis = makeRef(*this), completionHandler = WTFMove(completionHandler)](SessionHost::CommandResponse&& response) {
705 if (response.isError || !response.responseObject) {
706 completionHandler(CommandResult::fail(WTFMove(response.responseObject)));
710 if (!response.responseObject->getString(ASCIILiteral("result"), valueString)) {
711 completionHandler(CommandResult::fail(CommandResult::ErrorCode::UnknownError));
714 RefPtr<InspectorValue> resultValue;
715 if (!InspectorValue::parseJSON(valueString, resultValue)) {
716 completionHandler(CommandResult::fail(CommandResult::ErrorCode::UnknownError));
719 if (resultValue->isNull()) {
720 completionHandler(CommandResult::success(InspectorValue::create(false)));
723 String booleanResult;
724 if (!resultValue->asString(booleanResult) || booleanResult != "true") {
725 completionHandler(CommandResult::fail(CommandResult::ErrorCode::UnknownError));
728 completionHandler(CommandResult::success(InspectorValue::create(true)));
732 void Session::getElementText(const String& elementID, Function<void (CommandResult&&)>&& completionHandler)
734 if (!m_toplevelBrowsingContext) {
735 completionHandler(CommandResult::fail(CommandResult::ErrorCode::NoSuchWindow));
739 RefPtr<InspectorArray> arguments = InspectorArray::create();
740 arguments->pushString(createElement(elementID)->toJSONString());
742 RefPtr<InspectorObject> parameters = InspectorObject::create();
743 parameters->setString(ASCIILiteral("browsingContextHandle"), m_toplevelBrowsingContext.value());
744 if (m_browsingContext)
745 parameters->setString(ASCIILiteral("frameHandle"), m_browsingContext.value());
746 // FIXME: Add an atom to properly implement this instead of just using innerText.
747 parameters->setString(ASCIILiteral("function"), ASCIILiteral("function(element) { return element.innerText.replace(/^[^\\S\\xa0]+|[^\\S\\xa0]+$/g, '') }"));
748 parameters->setArray(ASCIILiteral("arguments"), WTFMove(arguments));
749 m_host->sendCommandToBackend(ASCIILiteral("evaluateJavaScriptFunction"), WTFMove(parameters), [this, protectedThis = makeRef(*this), completionHandler = WTFMove(completionHandler)](SessionHost::CommandResponse&& response) {
750 if (response.isError || !response.responseObject) {
751 completionHandler(CommandResult::fail(WTFMove(response.responseObject)));
755 if (!response.responseObject->getString(ASCIILiteral("result"), valueString)) {
756 completionHandler(CommandResult::fail(CommandResult::ErrorCode::UnknownError));
759 RefPtr<InspectorValue> resultValue;
760 if (!InspectorValue::parseJSON(valueString, resultValue)) {
761 completionHandler(CommandResult::fail(CommandResult::ErrorCode::UnknownError));
764 completionHandler(CommandResult::success(WTFMove(resultValue)));
768 void Session::getElementTagName(const String& elementID, Function<void (CommandResult&&)>&& completionHandler)
770 if (!m_toplevelBrowsingContext) {
771 completionHandler(CommandResult::fail(CommandResult::ErrorCode::NoSuchWindow));
775 RefPtr<InspectorArray> arguments = InspectorArray::create();
776 arguments->pushString(createElement(elementID)->toJSONString());
778 RefPtr<InspectorObject> parameters = InspectorObject::create();
779 parameters->setString(ASCIILiteral("browsingContextHandle"), m_toplevelBrowsingContext.value());
780 if (m_browsingContext)
781 parameters->setString(ASCIILiteral("frameHandle"), m_browsingContext.value());
782 parameters->setString(ASCIILiteral("function"), ASCIILiteral("function(element) { return element.tagName.toLowerCase() }"));
783 parameters->setArray(ASCIILiteral("arguments"), WTFMove(arguments));
784 m_host->sendCommandToBackend(ASCIILiteral("evaluateJavaScriptFunction"), WTFMove(parameters), [this, protectedThis = makeRef(*this), completionHandler = WTFMove(completionHandler)](SessionHost::CommandResponse&& response) {
785 if (response.isError || !response.responseObject) {
786 completionHandler(CommandResult::fail(WTFMove(response.responseObject)));
790 if (!response.responseObject->getString(ASCIILiteral("result"), valueString)) {
791 completionHandler(CommandResult::fail(CommandResult::ErrorCode::UnknownError));
794 RefPtr<InspectorValue> resultValue;
795 if (!InspectorValue::parseJSON(valueString, resultValue)) {
796 completionHandler(CommandResult::fail(CommandResult::ErrorCode::UnknownError));
799 completionHandler(CommandResult::success(WTFMove(resultValue)));
803 void Session::getElementRect(const String& elementID, Function<void (CommandResult&&)>&& completionHandler)
805 if (!m_toplevelBrowsingContext) {
806 completionHandler(CommandResult::fail(CommandResult::ErrorCode::NoSuchWindow));
810 computeElementLayout(elementID, { }, [this, protectedThis = makeRef(*this), completionHandler = WTFMove(completionHandler)](std::optional<Rect>&& rect, RefPtr<InspectorObject>&& error) {
811 if (!rect || error) {
812 completionHandler(CommandResult::fail(WTFMove(error)));
815 RefPtr<InspectorObject> rectObject = InspectorObject::create();
816 rectObject->setInteger(ASCIILiteral("x"), rect.value().origin.x);
817 rectObject->setInteger(ASCIILiteral("y"), rect.value().origin.y);
818 rectObject->setInteger(ASCIILiteral("width"), rect.value().size.width);
819 rectObject->setInteger(ASCIILiteral("height"), rect.value().size.height);
820 completionHandler(CommandResult::success(WTFMove(rectObject)));
824 void Session::isElementEnabled(const String& elementID, Function<void (CommandResult&&)>&& completionHandler)
826 if (!m_toplevelBrowsingContext) {
827 completionHandler(CommandResult::fail(CommandResult::ErrorCode::NoSuchWindow));
831 RefPtr<InspectorArray> arguments = InspectorArray::create();
832 arguments->pushString(createElement(elementID)->toJSONString());
834 RefPtr<InspectorObject> parameters = InspectorObject::create();
835 parameters->setString(ASCIILiteral("browsingContextHandle"), m_toplevelBrowsingContext.value());
836 if (m_browsingContext)
837 parameters->setString(ASCIILiteral("frameHandle"), m_browsingContext.value());
838 parameters->setString(ASCIILiteral("function"), ASCIILiteral("function(element) { return element.disabled === undefined ? true : !element.disabled }"));
839 parameters->setArray(ASCIILiteral("arguments"), WTFMove(arguments));
840 m_host->sendCommandToBackend(ASCIILiteral("evaluateJavaScriptFunction"), WTFMove(parameters), [this, protectedThis = makeRef(*this), completionHandler = WTFMove(completionHandler)](SessionHost::CommandResponse&& response) {
841 if (response.isError || !response.responseObject) {
842 completionHandler(CommandResult::fail(WTFMove(response.responseObject)));
846 if (!response.responseObject->getString(ASCIILiteral("result"), valueString)) {
847 completionHandler(CommandResult::fail(CommandResult::ErrorCode::UnknownError));
850 RefPtr<InspectorValue> resultValue;
851 if (!InspectorValue::parseJSON(valueString, resultValue)) {
852 completionHandler(CommandResult::fail(CommandResult::ErrorCode::UnknownError));
855 completionHandler(CommandResult::success(WTFMove(resultValue)));
859 void Session::isElementDisplayed(const String& elementID, Function<void (CommandResult&&)>&& completionHandler)
861 if (!m_toplevelBrowsingContext) {
862 completionHandler(CommandResult::fail(CommandResult::ErrorCode::NoSuchWindow));
866 RefPtr<InspectorArray> arguments = InspectorArray::create();
867 arguments->pushString(createElement(elementID)->toJSONString());
869 RefPtr<InspectorObject> parameters = InspectorObject::create();
870 parameters->setString(ASCIILiteral("browsingContextHandle"), m_toplevelBrowsingContext.value());
871 if (m_browsingContext)
872 parameters->setString(ASCIILiteral("frameHandle"), m_browsingContext.value());
873 parameters->setString(ASCIILiteral("function"), ElementDisplayedJavaScript);
874 parameters->setArray(ASCIILiteral("arguments"), WTFMove(arguments));
875 m_host->sendCommandToBackend(ASCIILiteral("evaluateJavaScriptFunction"), WTFMove(parameters), [this, protectedThis = makeRef(*this), completionHandler = WTFMove(completionHandler)](SessionHost::CommandResponse&& response) {
876 if (response.isError || !response.responseObject) {
877 completionHandler(CommandResult::fail(WTFMove(response.responseObject)));
881 if (!response.responseObject->getString(ASCIILiteral("result"), valueString)) {
882 completionHandler(CommandResult::fail(CommandResult::ErrorCode::UnknownError));
885 RefPtr<InspectorValue> resultValue;
886 if (!InspectorValue::parseJSON(valueString, resultValue)) {
887 completionHandler(CommandResult::fail(CommandResult::ErrorCode::UnknownError));
890 completionHandler(CommandResult::success(WTFMove(resultValue)));
894 void Session::getElementAttribute(const String& elementID, const String& attribute, Function<void (CommandResult&&)>&& completionHandler)
896 if (!m_toplevelBrowsingContext) {
897 completionHandler(CommandResult::fail(CommandResult::ErrorCode::NoSuchWindow));
901 RefPtr<InspectorArray> arguments = InspectorArray::create();
902 arguments->pushString(createElement(elementID)->toJSONString());
903 arguments->pushString(InspectorValue::create(attribute)->toJSONString());
905 RefPtr<InspectorObject> parameters = InspectorObject::create();
906 parameters->setString(ASCIILiteral("browsingContextHandle"), m_toplevelBrowsingContext.value());
907 if (m_browsingContext)
908 parameters->setString(ASCIILiteral("frameHandle"), m_browsingContext.value());
909 parameters->setString(ASCIILiteral("function"), ElementAttributeJavaScript);
910 parameters->setArray(ASCIILiteral("arguments"), WTFMove(arguments));
911 m_host->sendCommandToBackend(ASCIILiteral("evaluateJavaScriptFunction"), WTFMove(parameters), [this, protectedThis = makeRef(*this), completionHandler = WTFMove(completionHandler)](SessionHost::CommandResponse&& response) {
912 if (response.isError || !response.responseObject) {
913 completionHandler(CommandResult::fail(WTFMove(response.responseObject)));
917 if (!response.responseObject->getString(ASCIILiteral("result"), valueString)) {
918 completionHandler(CommandResult::fail(CommandResult::ErrorCode::UnknownError));
921 RefPtr<InspectorValue> resultValue;
922 if (!InspectorValue::parseJSON(valueString, resultValue)) {
923 completionHandler(CommandResult::fail(CommandResult::ErrorCode::UnknownError));
926 completionHandler(CommandResult::success(WTFMove(resultValue)));
930 void Session::elementClick(const String& elementID, Function<void (CommandResult&&)>&& completionHandler)
932 if (!m_toplevelBrowsingContext) {
933 completionHandler(CommandResult::fail(CommandResult::ErrorCode::NoSuchWindow));
937 OptionSet<ElementLayoutOption> options = ElementLayoutOption::ScrollIntoViewIfNeeded;
938 options |= ElementLayoutOption::UseViewportCoordinates;
939 computeElementLayout(elementID, options, [this, protectedThis = makeRef(*this), completionHandler = WTFMove(completionHandler)](std::optional<Rect>&& rect, RefPtr<InspectorObject>&& error) mutable {
940 if (!rect || error) {
941 completionHandler(CommandResult::fail(WTFMove(error)));
945 // FIXME: the center of the bounding box is not always part of the element.
946 performMouseInteraction(rect.value().origin.x + rect.value().size.width / 2, rect.value().origin.y + rect.value().size.height / 2,
947 MouseButton::Left, MouseInteraction::SingleClick, WTFMove(completionHandler));
951 void Session::elementClear(const String& elementID, Function<void (CommandResult&&)>&& completionHandler)
953 if (!m_toplevelBrowsingContext) {
954 completionHandler(CommandResult::fail(CommandResult::ErrorCode::NoSuchWindow));
958 RefPtr<InspectorArray> arguments = InspectorArray::create();
959 arguments->pushString(createElement(elementID)->toJSONString());
961 RefPtr<InspectorObject> parameters = InspectorObject::create();
962 parameters->setString(ASCIILiteral("browsingContextHandle"), m_toplevelBrowsingContext.value());
963 if (m_browsingContext)
964 parameters->setString(ASCIILiteral("frameHandle"), m_browsingContext.value());
965 parameters->setString(ASCIILiteral("function"), FormElementClearJavaScript);
966 parameters->setArray(ASCIILiteral("arguments"), WTFMove(arguments));
967 m_host->sendCommandToBackend(ASCIILiteral("evaluateJavaScriptFunction"), WTFMove(parameters), [this, protectedThis = makeRef(*this), completionHandler = WTFMove(completionHandler)](SessionHost::CommandResponse&& response) {
968 if (response.isError) {
969 completionHandler(CommandResult::fail(WTFMove(response.responseObject)));
972 completionHandler(CommandResult::success());
976 String Session::virtualKeyForKeySequence(const String& keySequence, KeyModifier& modifier)
978 // §17.4.2 Keyboard Actions.
979 // https://www.w3.org/TR/webdriver/#keyboard-actions
980 modifier = KeyModifier::None;
981 switch (keySequence[0]) {
983 return ASCIILiteral("Cancel");
985 return ASCIILiteral("Help");
987 return ASCIILiteral("Backspace");
989 return ASCIILiteral("Tab");
991 return ASCIILiteral("Clear");
993 return ASCIILiteral("Return");
995 return ASCIILiteral("Enter");
997 modifier = KeyModifier::Shift;
998 return ASCIILiteral("Shift");
1000 modifier = KeyModifier::Control;
1001 return ASCIILiteral("Control");
1003 modifier = KeyModifier::Alternate;
1004 return ASCIILiteral("Alternate");
1006 return ASCIILiteral("Pause");
1008 return ASCIILiteral("Escape");
1010 return ASCIILiteral("Space");
1012 return ASCIILiteral("PageUp");
1014 return ASCIILiteral("PageDown");
1016 return ASCIILiteral("End");
1018 return ASCIILiteral("Home");
1020 return ASCIILiteral("LeftArrow");
1022 return ASCIILiteral("UpArrow");
1024 return ASCIILiteral("RightArrow");
1026 return ASCIILiteral("DownArrow");
1028 return ASCIILiteral("Insert");
1030 return ASCIILiteral("Delete");
1032 return ASCIILiteral("Semicolon");
1034 return ASCIILiteral("Equals");
1036 return ASCIILiteral("NumberPad0");
1038 return ASCIILiteral("NumberPad1");
1040 return ASCIILiteral("NumberPad2");
1042 return ASCIILiteral("NumberPad3");
1044 return ASCIILiteral("NumberPad4");
1046 return ASCIILiteral("NumberPad5");
1048 return ASCIILiteral("NumberPad6");
1050 return ASCIILiteral("NumberPad7");
1052 return ASCIILiteral("NumberPad8");
1054 return ASCIILiteral("NumberPad9");
1056 return ASCIILiteral("NumberPadMultiply");
1058 return ASCIILiteral("NumberPadAdd");
1060 return ASCIILiteral("NumberPadSeparator");
1062 return ASCIILiteral("NumberPadSubtract");
1064 return ASCIILiteral("NumberPadDecimal");
1066 return ASCIILiteral("NumberPadDivide");
1068 return ASCIILiteral("Function1");
1070 return ASCIILiteral("Function2");
1072 return ASCIILiteral("Function3");
1074 return ASCIILiteral("Function4");
1076 return ASCIILiteral("Function5");
1078 return ASCIILiteral("Function6");
1080 return ASCIILiteral("Function7");
1082 return ASCIILiteral("Function8");
1084 return ASCIILiteral("Function9");
1086 return ASCIILiteral("Function10");
1088 return ASCIILiteral("Function11");
1090 return ASCIILiteral("Function12");
1092 modifier = KeyModifier::Meta;
1093 return ASCIILiteral("Meta");
1100 void Session::elementSendKeys(const String& elementID, Vector<String>&& keys, Function<void (CommandResult&&)>&& completionHandler)
1102 if (!m_toplevelBrowsingContext) {
1103 completionHandler(CommandResult::fail(CommandResult::ErrorCode::NoSuchWindow));
1107 // FIXME: move this to an atom.
1108 static const char focusScript[] =
1109 "function focus(element) {"
1110 " var doc = element.ownerDocument || element;"
1111 " var prevActiveElement = doc.activeElement;"
1112 " if (element != prevActiveElement && prevActiveElement)"
1113 " prevActiveElement.blur();"
1115 " if (element != prevActiveElement && element.value && element.value.length && element.setSelectionRange)"
1116 " element.setSelectionRange(element.value.length, element.value.length);"
1117 " if (element != doc.activeElement)"
1118 " throw new Error('cannot focus element');"
1121 RefPtr<InspectorArray> arguments = InspectorArray::create();
1122 arguments->pushString(createElement(elementID)->toJSONString());
1123 RefPtr<InspectorObject> parameters = InspectorObject::create();
1124 parameters->setString(ASCIILiteral("browsingContextHandle"), m_toplevelBrowsingContext.value());
1125 if (m_browsingContext)
1126 parameters->setString(ASCIILiteral("frameHandle"), m_browsingContext.value());
1127 parameters->setString(ASCIILiteral("function"), focusScript);
1128 parameters->setArray(ASCIILiteral("arguments"), WTFMove(arguments));
1129 m_host->sendCommandToBackend(ASCIILiteral("evaluateJavaScriptFunction"), WTFMove(parameters), [this, protectedThis = makeRef(*this), keys = WTFMove(keys), completionHandler = WTFMove(completionHandler)](SessionHost::CommandResponse&& response) mutable {
1130 if (response.isError || !response.responseObject) {
1131 completionHandler(CommandResult::fail(WTFMove(response.responseObject)));
1135 unsigned stickyModifiers = 0;
1136 Vector<KeyboardInteraction> interactions;
1137 interactions.reserveInitialCapacity(keys.size());
1138 for (const auto& key : keys) {
1139 KeyboardInteraction interaction;
1140 KeyModifier modifier;
1141 auto virtualKey = virtualKeyForKeySequence(key, modifier);
1142 if (!virtualKey.isNull()) {
1143 interaction.key = virtualKey;
1144 if (modifier != KeyModifier::None) {
1145 stickyModifiers ^= modifier;
1146 if (stickyModifiers & modifier)
1147 interaction.type = KeyboardInteractionType::KeyPress;
1149 interaction.type = KeyboardInteractionType::KeyRelease;
1152 interaction.text = key;
1153 interactions.uncheckedAppend(WTFMove(interaction));
1156 // Reset sticky modifiers if needed.
1157 if (stickyModifiers) {
1158 if (stickyModifiers & KeyModifier::Shift)
1159 interactions.append({ KeyboardInteractionType::KeyRelease, std::nullopt, std::optional<String>(ASCIILiteral("Shift")) });
1160 if (stickyModifiers & KeyModifier::Control)
1161 interactions.append({ KeyboardInteractionType::KeyRelease, std::nullopt, std::optional<String>(ASCIILiteral("Control")) });
1162 if (stickyModifiers & KeyModifier::Alternate)
1163 interactions.append({ KeyboardInteractionType::KeyRelease, std::nullopt, std::optional<String>(ASCIILiteral("Alternate")) });
1164 if (stickyModifiers & KeyModifier::Meta)
1165 interactions.append({ KeyboardInteractionType::KeyRelease, std::nullopt, std::optional<String>(ASCIILiteral("Meta")) });
1168 performKeyboardInteractions(WTFMove(interactions), WTFMove(completionHandler));
1172 void Session::elementSubmit(const String& elementID, Function<void (CommandResult&&)>&& completionHandler)
1174 if (!m_toplevelBrowsingContext) {
1175 completionHandler(CommandResult::fail(CommandResult::ErrorCode::NoSuchWindow));
1179 RefPtr<InspectorArray> arguments = InspectorArray::create();
1180 arguments->pushString(createElement(elementID)->toJSONString());
1182 RefPtr<InspectorObject> parameters = InspectorObject::create();
1183 parameters->setString(ASCIILiteral("browsingContextHandle"), m_toplevelBrowsingContext.value());
1184 if (m_browsingContext)
1185 parameters->setString(ASCIILiteral("frameHandle"), m_browsingContext.value());
1186 parameters->setString(ASCIILiteral("function"), FormSubmitJavaScript);
1187 parameters->setArray(ASCIILiteral("arguments"), WTFMove(arguments));
1188 m_host->sendCommandToBackend(ASCIILiteral("evaluateJavaScriptFunction"), WTFMove(parameters), [this, protectedThis = makeRef(*this), completionHandler = WTFMove(completionHandler)](SessionHost::CommandResponse&& response) {
1189 if (response.isError) {
1190 completionHandler(CommandResult::fail(WTFMove(response.responseObject)));
1193 completionHandler(CommandResult::success());
1197 RefPtr<InspectorValue> Session::handleScriptResult(RefPtr<InspectorValue>&& resultValue)
1199 RefPtr<InspectorArray> resultArray;
1200 if (resultValue->asArray(resultArray)) {
1201 RefPtr<InspectorArray> returnValueArray = InspectorArray::create();
1202 unsigned resultArrayLength = resultArray->length();
1203 for (unsigned i = 0; i < resultArrayLength; ++i)
1204 returnValueArray->pushValue(handleScriptResult(resultArray->get(i)));
1205 return returnValueArray;
1208 if (auto element = createElement(RefPtr<InspectorValue>(resultValue)))
1211 RefPtr<InspectorObject> resultObject;
1212 if (resultValue->asObject(resultObject)) {
1213 RefPtr<InspectorObject> returnValueObject = InspectorObject::create();
1214 auto end = resultObject->end();
1215 for (auto it = resultObject->begin(); it != end; ++it)
1216 returnValueObject->setValue(it->key, handleScriptResult(WTFMove(it->value)));
1217 return returnValueObject;
1223 void Session::executeScript(const String& script, RefPtr<InspectorArray>&& argumentsArray, ExecuteScriptMode mode, Function<void (CommandResult&&)>&& completionHandler)
1225 if (!m_toplevelBrowsingContext) {
1226 completionHandler(CommandResult::fail(CommandResult::ErrorCode::NoSuchWindow));
1230 RefPtr<InspectorArray> arguments = InspectorArray::create();
1231 unsigned argumentsLength = argumentsArray->length();
1232 for (unsigned i = 0; i < argumentsLength; ++i) {
1233 if (auto argument = argumentsArray->get(i)) {
1234 if (auto element = extractElement(*argument))
1235 arguments->pushString(element->toJSONString());
1237 arguments->pushString(argument->toJSONString());
1241 RefPtr<InspectorObject> parameters = InspectorObject::create();
1242 parameters->setString(ASCIILiteral("browsingContextHandle"), m_toplevelBrowsingContext.value());
1243 if (m_browsingContext)
1244 parameters->setString(ASCIILiteral("frameHandle"), m_browsingContext.value());
1245 parameters->setString(ASCIILiteral("function"), "function(){" + script + '}');
1246 parameters->setArray(ASCIILiteral("arguments"), WTFMove(arguments));
1247 if (mode == ExecuteScriptMode::Async) {
1248 parameters->setBoolean(ASCIILiteral("expectsImplicitCallbackArgument"), true);
1249 if (m_timeouts.script)
1250 parameters->setInteger(ASCIILiteral("callbackTimeout"), m_timeouts.script.value().millisecondsAs<int>());
1252 m_host->sendCommandToBackend(ASCIILiteral("evaluateJavaScriptFunction"), WTFMove(parameters), [this, protectedThis = makeRef(*this), completionHandler = WTFMove(completionHandler)](SessionHost::CommandResponse&& response) {
1253 if (response.isError || !response.responseObject) {
1254 completionHandler(CommandResult::fail(WTFMove(response.responseObject)));
1258 if (!response.responseObject->getString(ASCIILiteral("result"), valueString)) {
1259 completionHandler(CommandResult::fail(CommandResult::ErrorCode::UnknownError));
1262 if (valueString.isEmpty()) {
1263 completionHandler(CommandResult::success());
1266 RefPtr<InspectorValue> resultValue;
1267 if (!InspectorValue::parseJSON(valueString, resultValue)) {
1268 completionHandler(CommandResult::fail(CommandResult::ErrorCode::UnknownError));
1271 completionHandler(CommandResult::success(handleScriptResult(WTFMove(resultValue))));
1275 void Session::performMouseInteraction(int x, int y, MouseButton button, MouseInteraction interaction, Function<void (CommandResult&&)>&& completionHandler)
1277 RefPtr<InspectorObject> parameters = InspectorObject::create();
1278 parameters->setString(ASCIILiteral("handle"), m_toplevelBrowsingContext.value());
1279 RefPtr<InspectorObject> position = InspectorObject::create();
1280 position->setInteger(ASCIILiteral("x"), x);
1281 position->setInteger(ASCIILiteral("y"), y);
1282 parameters->setObject(ASCIILiteral("position"), WTFMove(position));
1284 case MouseButton::None:
1285 parameters->setString(ASCIILiteral("button"), ASCIILiteral("None"));
1287 case MouseButton::Left:
1288 parameters->setString(ASCIILiteral("button"), ASCIILiteral("Left"));
1290 case MouseButton::Middle:
1291 parameters->setString(ASCIILiteral("button"), ASCIILiteral("Middle"));
1293 case MouseButton::Right:
1294 parameters->setString(ASCIILiteral("button"), ASCIILiteral("Right"));
1297 switch (interaction) {
1298 case MouseInteraction::Move:
1299 parameters->setString(ASCIILiteral("interaction"), ASCIILiteral("Move"));
1301 case MouseInteraction::Down:
1302 parameters->setString(ASCIILiteral("interaction"), ASCIILiteral("Down"));
1304 case MouseInteraction::Up:
1305 parameters->setString(ASCIILiteral("interaction"), ASCIILiteral("Up"));
1307 case MouseInteraction::SingleClick:
1308 parameters->setString(ASCIILiteral("interaction"), ASCIILiteral("SingleClick"));
1310 case MouseInteraction::DoubleClick:
1311 parameters->setString(ASCIILiteral("interaction"), ASCIILiteral("DoubleClick"));
1314 parameters->setArray(ASCIILiteral("modifiers"), InspectorArray::create());
1315 m_host->sendCommandToBackend(ASCIILiteral("performMouseInteraction"), WTFMove(parameters), [this, protectedThis = makeRef(*this), completionHandler = WTFMove(completionHandler)](SessionHost::CommandResponse&& response) {
1316 if (response.isError) {
1317 completionHandler(CommandResult::fail(WTFMove(response.responseObject)));
1320 completionHandler(CommandResult::success());
1324 void Session::performKeyboardInteractions(Vector<KeyboardInteraction>&& interactions, Function<void (CommandResult&&)>&& completionHandler)
1326 RefPtr<InspectorObject> parameters = InspectorObject::create();
1327 parameters->setString(ASCIILiteral("handle"), m_toplevelBrowsingContext.value());
1328 RefPtr<InspectorArray> interactionsArray = InspectorArray::create();
1329 for (const auto& interaction : interactions) {
1330 RefPtr<InspectorObject> interactionObject = InspectorObject::create();
1331 switch (interaction.type) {
1332 case KeyboardInteractionType::KeyPress:
1333 interactionObject->setString(ASCIILiteral("type"), ASCIILiteral("KeyPress"));
1335 case KeyboardInteractionType::KeyRelease:
1336 interactionObject->setString(ASCIILiteral("type"), ASCIILiteral("KeyRelease"));
1338 case KeyboardInteractionType::InsertByKey:
1339 interactionObject->setString(ASCIILiteral("type"), ASCIILiteral("InsertByKey"));
1342 if (interaction.key)
1343 interactionObject->setString(ASCIILiteral("key"), interaction.key.value());
1344 if (interaction.text)
1345 interactionObject->setString(ASCIILiteral("text"), interaction.text.value());
1346 interactionsArray->pushObject(WTFMove(interactionObject));
1348 parameters->setArray(ASCIILiteral("interactions"), WTFMove(interactionsArray));
1349 m_host->sendCommandToBackend(ASCIILiteral("performKeyboardInteractions"), WTFMove(parameters), [this, protectedThis = makeRef(*this), completionHandler = WTFMove(completionHandler)](SessionHost::CommandResponse&& response) {
1350 if (response.isError) {
1351 completionHandler(CommandResult::fail(WTFMove(response.responseObject)));
1354 completionHandler(CommandResult::success());
1358 } // namespace WebDriver