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 switchToTopLevelBrowsingContext(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::switchToTopLevelBrowsingContext(std::optional<String> toplevelBrowsingContext)
92 m_toplevelBrowsingContext = toplevelBrowsingContext;
93 m_currentBrowsingContext = std::nullopt;
96 void Session::switchToBrowsingContext(std::optional<String> browsingContext)
98 // Automation sends empty strings for main frame.
99 if (!browsingContext || browsingContext.value().isEmpty())
100 m_currentBrowsingContext = std::nullopt;
102 m_currentBrowsingContext = browsingContext;
105 std::optional<String> Session::pageLoadStrategyString() const
107 if (!capabilities().pageLoadStrategy)
110 switch (capabilities().pageLoadStrategy.value()) {
111 case PageLoadStrategy::None:
112 return String("None");
113 case PageLoadStrategy::Normal:
114 return String("Normal");
115 case PageLoadStrategy::Eager:
116 return String("Eager");
122 void Session::createTopLevelBrowsingContext(Function<void (CommandResult&&)>&& completionHandler)
124 ASSERT(!m_toplevelBrowsingContext.value());
125 m_host->startAutomationSession(m_id, [this, protectedThis = makeRef(*this), completionHandler = WTFMove(completionHandler)](std::optional<String> errorMessage) mutable {
127 completionHandler(CommandResult::fail(CommandResult::ErrorCode::UnknownError, errorMessage.value()));
130 m_host->sendCommandToBackend(ASCIILiteral("createBrowsingContext"), nullptr, [this, protectedThis = makeRef(*this), completionHandler = WTFMove(completionHandler)](SessionHost::CommandResponse&& response) mutable {
131 if (response.isError || !response.responseObject) {
132 completionHandler(CommandResult::fail(WTFMove(response.responseObject)));
136 if (!response.responseObject->getString(ASCIILiteral("handle"), handle)) {
137 completionHandler(CommandResult::fail(CommandResult::ErrorCode::UnknownError));
140 switchToTopLevelBrowsingContext(handle);
141 completionHandler(CommandResult::success());
146 void Session::go(const String& url, Function<void (CommandResult&&)>&& completionHandler)
148 if (!m_toplevelBrowsingContext) {
149 completionHandler(CommandResult::fail(CommandResult::ErrorCode::NoSuchWindow));
153 RefPtr<InspectorObject> parameters = InspectorObject::create();
154 parameters->setString(ASCIILiteral("handle"), m_toplevelBrowsingContext.value());
155 parameters->setString(ASCIILiteral("url"), url);
156 if (m_timeouts.pageLoad)
157 parameters->setInteger(ASCIILiteral("pageLoadTimeout"), m_timeouts.pageLoad.value().millisecondsAs<int>());
158 if (auto pageLoadStrategy = pageLoadStrategyString())
159 parameters->setString(ASCIILiteral("pageLoadStrategy"), pageLoadStrategy.value());
160 m_host->sendCommandToBackend(ASCIILiteral("navigateBrowsingContext"), WTFMove(parameters), [this, protectedThis = makeRef(*this), completionHandler = WTFMove(completionHandler)](SessionHost::CommandResponse&& response) {
161 if (response.isError) {
162 completionHandler(CommandResult::fail(WTFMove(response.responseObject)));
165 switchToBrowsingContext(std::nullopt);
166 completionHandler(CommandResult::success());
170 void Session::getCurrentURL(Function<void (CommandResult&&)>&& completionHandler)
172 if (!m_toplevelBrowsingContext) {
173 completionHandler(CommandResult::fail(CommandResult::ErrorCode::NoSuchWindow));
177 RefPtr<InspectorObject> parameters = InspectorObject::create();
178 parameters->setString(ASCIILiteral("handle"), m_toplevelBrowsingContext.value());
179 m_host->sendCommandToBackend(ASCIILiteral("getBrowsingContext"), WTFMove(parameters), [this, protectedThis = makeRef(*this), completionHandler = WTFMove(completionHandler)](SessionHost::CommandResponse&& response) {
180 if (response.isError || !response.responseObject) {
181 completionHandler(CommandResult::fail(WTFMove(response.responseObject)));
184 RefPtr<InspectorObject> browsingContext;
185 if (!response.responseObject->getObject("context", browsingContext)) {
186 completionHandler(CommandResult::fail(CommandResult::ErrorCode::UnknownError));
190 if (!browsingContext->getString("url", url)) {
191 completionHandler(CommandResult::fail(CommandResult::ErrorCode::UnknownError));
194 completionHandler(CommandResult::success(InspectorValue::create(url)));
198 void Session::back(Function<void (CommandResult&&)>&& completionHandler)
200 if (!m_toplevelBrowsingContext) {
201 completionHandler(CommandResult::fail(CommandResult::ErrorCode::NoSuchWindow));
205 RefPtr<InspectorObject> parameters = InspectorObject::create();
206 parameters->setString(ASCIILiteral("handle"), m_toplevelBrowsingContext.value());
207 if (m_timeouts.pageLoad)
208 parameters->setInteger(ASCIILiteral("pageLoadTimeout"), m_timeouts.pageLoad.value().millisecondsAs<int>());
209 if (auto pageLoadStrategy = pageLoadStrategyString())
210 parameters->setString(ASCIILiteral("pageLoadStrategy"), pageLoadStrategy.value());
211 m_host->sendCommandToBackend(ASCIILiteral("goBackInBrowsingContext"), WTFMove(parameters), [this, protectedThis = makeRef(*this), completionHandler = WTFMove(completionHandler)](SessionHost::CommandResponse&& response) {
212 if (response.isError) {
213 completionHandler(CommandResult::fail(WTFMove(response.responseObject)));
216 switchToBrowsingContext(std::nullopt);
217 completionHandler(CommandResult::success());
221 void Session::forward(Function<void (CommandResult&&)>&& completionHandler)
223 if (!m_toplevelBrowsingContext) {
224 completionHandler(CommandResult::fail(CommandResult::ErrorCode::NoSuchWindow));
228 RefPtr<InspectorObject> parameters = InspectorObject::create();
229 parameters->setString(ASCIILiteral("handle"), m_toplevelBrowsingContext.value());
230 if (m_timeouts.pageLoad)
231 parameters->setInteger(ASCIILiteral("pageLoadTimeout"), m_timeouts.pageLoad.value().millisecondsAs<int>());
232 if (auto pageLoadStrategy = pageLoadStrategyString())
233 parameters->setString(ASCIILiteral("pageLoadStrategy"), pageLoadStrategy.value());
234 m_host->sendCommandToBackend(ASCIILiteral("goForwardInBrowsingContext"), WTFMove(parameters), [this, protectedThis = makeRef(*this), completionHandler = WTFMove(completionHandler)](SessionHost::CommandResponse&& response) {
235 if (response.isError) {
236 completionHandler(CommandResult::fail(WTFMove(response.responseObject)));
239 switchToBrowsingContext(std::nullopt);
240 completionHandler(CommandResult::success());
244 void Session::refresh(Function<void (CommandResult&&)>&& completionHandler)
246 if (!m_toplevelBrowsingContext) {
247 completionHandler(CommandResult::fail(CommandResult::ErrorCode::NoSuchWindow));
251 RefPtr<InspectorObject> parameters = InspectorObject::create();
252 parameters->setString(ASCIILiteral("handle"), m_toplevelBrowsingContext.value());
253 if (m_timeouts.pageLoad)
254 parameters->setInteger(ASCIILiteral("pageLoadTimeout"), m_timeouts.pageLoad.value().millisecondsAs<int>());
255 if (auto pageLoadStrategy = pageLoadStrategyString())
256 parameters->setString(ASCIILiteral("pageLoadStrategy"), pageLoadStrategy.value());
257 m_host->sendCommandToBackend(ASCIILiteral("reloadBrowsingContext"), WTFMove(parameters), [this, protectedThis = makeRef(*this), completionHandler = WTFMove(completionHandler)](SessionHost::CommandResponse&& response) {
258 if (response.isError) {
259 completionHandler(CommandResult::fail(WTFMove(response.responseObject)));
262 switchToBrowsingContext(std::nullopt);
263 completionHandler(CommandResult::success());
267 void Session::getTitle(Function<void (CommandResult&&)>&& completionHandler)
269 if (!m_toplevelBrowsingContext) {
270 completionHandler(CommandResult::fail(CommandResult::ErrorCode::NoSuchWindow));
274 RefPtr<InspectorObject> parameters = InspectorObject::create();
275 parameters->setString(ASCIILiteral("browsingContextHandle"), m_toplevelBrowsingContext.value());
276 parameters->setString(ASCIILiteral("function"), ASCIILiteral("function() { return document.title; }"));
277 parameters->setArray(ASCIILiteral("arguments"), InspectorArray::create());
278 m_host->sendCommandToBackend(ASCIILiteral("evaluateJavaScriptFunction"), WTFMove(parameters), [this, protectedThis = makeRef(*this), completionHandler = WTFMove(completionHandler)](SessionHost::CommandResponse&& response) {
279 if (response.isError || !response.responseObject) {
280 completionHandler(CommandResult::fail(WTFMove(response.responseObject)));
284 if (!response.responseObject->getString(ASCIILiteral("result"), valueString)) {
285 completionHandler(CommandResult::fail(CommandResult::ErrorCode::UnknownError));
288 RefPtr<InspectorValue> resultValue;
289 if (!InspectorValue::parseJSON(valueString, resultValue)) {
290 completionHandler(CommandResult::fail(CommandResult::ErrorCode::UnknownError));
293 completionHandler(CommandResult::success(WTFMove(resultValue)));
297 void Session::getWindowHandle(Function<void (CommandResult&&)>&& completionHandler)
299 if (!m_toplevelBrowsingContext) {
300 completionHandler(CommandResult::fail(CommandResult::ErrorCode::NoSuchWindow));
304 RefPtr<InspectorObject> parameters = InspectorObject::create();
305 parameters->setString(ASCIILiteral("handle"), m_toplevelBrowsingContext.value());
306 m_host->sendCommandToBackend(ASCIILiteral("getBrowsingContext"), WTFMove(parameters), [this, protectedThis = makeRef(*this), completionHandler = WTFMove(completionHandler)](SessionHost::CommandResponse&& response) {
307 if (response.isError || !response.responseObject) {
308 completionHandler(CommandResult::fail(WTFMove(response.responseObject)));
311 RefPtr<InspectorObject> browsingContext;
312 if (!response.responseObject->getObject(ASCIILiteral("context"), browsingContext)) {
313 completionHandler(CommandResult::fail(CommandResult::ErrorCode::UnknownError));
317 if (!browsingContext->getString(ASCIILiteral("handle"), handle)) {
318 completionHandler(CommandResult::fail(CommandResult::ErrorCode::UnknownError));
321 completionHandler(CommandResult::success(InspectorValue::create(handle)));
325 void Session::closeWindow(Function<void (CommandResult&&)>&& completionHandler)
327 if (!m_toplevelBrowsingContext) {
328 completionHandler(CommandResult::fail(CommandResult::ErrorCode::NoSuchWindow));
332 close(WTFMove(completionHandler));
335 void Session::switchToWindow(const String& windowHandle, Function<void (CommandResult&&)>&& completionHandler)
337 RefPtr<InspectorObject> parameters = InspectorObject::create();
338 parameters->setString(ASCIILiteral("browsingContextHandle"), windowHandle);
339 m_host->sendCommandToBackend(ASCIILiteral("switchToBrowsingContext"), WTFMove(parameters), [this, protectedThis = makeRef(*this), windowHandle, completionHandler = WTFMove(completionHandler)](SessionHost::CommandResponse&& response) {
340 if (response.isError) {
341 completionHandler(CommandResult::fail(WTFMove(response.responseObject)));
344 switchToTopLevelBrowsingContext(windowHandle);
345 completionHandler(CommandResult::success());
349 void Session::getWindowHandles(Function<void (CommandResult&&)>&& completionHandler)
351 if (!m_toplevelBrowsingContext) {
352 completionHandler(CommandResult::fail(CommandResult::ErrorCode::NoSuchWindow));
356 m_host->sendCommandToBackend(ASCIILiteral("getBrowsingContexts"), InspectorObject::create(), [this, protectedThis = makeRef(*this), completionHandler = WTFMove(completionHandler)](SessionHost::CommandResponse&& response) {
357 if (response.isError || !response.responseObject) {
358 completionHandler(CommandResult::fail(WTFMove(response.responseObject)));
361 RefPtr<InspectorArray> browsingContextArray;
362 if (!response.responseObject->getArray(ASCIILiteral("contexts"), browsingContextArray)) {
363 completionHandler(CommandResult::fail(CommandResult::ErrorCode::UnknownError));
366 RefPtr<InspectorArray> windowHandles = InspectorArray::create();
367 for (unsigned i = 0; i < browsingContextArray->length(); ++i) {
368 RefPtr<InspectorValue> browsingContextValue = browsingContextArray->get(i);
369 RefPtr<InspectorObject> browsingContext;
370 if (!browsingContextValue->asObject(browsingContext)) {
371 completionHandler(CommandResult::fail(CommandResult::ErrorCode::UnknownError));
376 if (!browsingContext->getString(ASCIILiteral("handle"), handle)) {
377 completionHandler(CommandResult::fail(CommandResult::ErrorCode::UnknownError));
381 windowHandles->pushString(handle);
383 completionHandler(CommandResult::success(WTFMove(windowHandles)));
387 void Session::switchToFrame(RefPtr<InspectorValue>&& frameID, Function<void (CommandResult&&)>&& completionHandler)
389 if (!m_toplevelBrowsingContext) {
390 completionHandler(CommandResult::fail(CommandResult::ErrorCode::NoSuchWindow));
394 if (frameID->isNull()) {
395 switchToBrowsingContext(std::nullopt);
396 completionHandler(CommandResult::success());
400 RefPtr<InspectorObject> parameters = InspectorObject::create();
401 parameters->setString(ASCIILiteral("browsingContextHandle"), m_toplevelBrowsingContext.value());
402 if (m_currentBrowsingContext)
403 parameters->setString(ASCIILiteral("frameHandle"), m_currentBrowsingContext.value());
406 if (frameID->asInteger(frameIndex)) {
407 if (frameIndex < 0 || frameIndex > USHRT_MAX) {
408 completionHandler(CommandResult::fail(CommandResult::ErrorCode::NoSuchFrame));
411 parameters->setInteger(ASCIILiteral("ordinal"), frameIndex);
413 String frameElementID = extractElementID(*frameID);
414 if (!frameElementID.isEmpty())
415 parameters->setString(ASCIILiteral("nodeHandle"), frameElementID);
418 if (!frameID->asString(frameName)) {
419 completionHandler(CommandResult::fail(CommandResult::ErrorCode::NoSuchFrame));
422 parameters->setString(ASCIILiteral("name"), frameName);
426 m_host->sendCommandToBackend(ASCIILiteral("resolveChildFrameHandle"), WTFMove(parameters), [this, protectedThis = makeRef(*this), completionHandler = WTFMove(completionHandler)](SessionHost::CommandResponse&& response) {
427 if (response.isError || !response.responseObject) {
428 completionHandler(CommandResult::fail(WTFMove(response.responseObject)));
432 if (!response.responseObject->getString(ASCIILiteral("result"), frameHandle)) {
433 completionHandler(CommandResult::fail(CommandResult::ErrorCode::UnknownError));
436 switchToBrowsingContext(frameHandle);
437 completionHandler(CommandResult::success());
441 void Session::switchToParentFrame(Function<void (CommandResult&&)>&& completionHandler)
443 if (!m_toplevelBrowsingContext) {
444 completionHandler(CommandResult::fail(CommandResult::ErrorCode::NoSuchWindow));
448 if (!m_currentBrowsingContext) {
449 completionHandler(CommandResult::success());
453 RefPtr<InspectorObject> parameters = InspectorObject::create();
454 parameters->setString(ASCIILiteral("browsingContextHandle"), m_toplevelBrowsingContext.value());
455 parameters->setString(ASCIILiteral("frameHandle"), m_currentBrowsingContext.value());
456 m_host->sendCommandToBackend(ASCIILiteral("resolveParentFrameHandle"), WTFMove(parameters), [this, protectedThis = makeRef(*this), completionHandler = WTFMove(completionHandler)](SessionHost::CommandResponse&& response) {
457 if (response.isError || !response.responseObject) {
458 completionHandler(CommandResult::fail(WTFMove(response.responseObject)));
462 if (!response.responseObject->getString(ASCIILiteral("result"), frameHandle)) {
463 completionHandler(CommandResult::fail(CommandResult::ErrorCode::UnknownError));
466 switchToBrowsingContext(frameHandle);
467 completionHandler(CommandResult::success());
471 void Session::getWindowPosition(Function<void (CommandResult&&)>&& completionHandler)
473 if (!m_toplevelBrowsingContext) {
474 completionHandler(CommandResult::fail(CommandResult::ErrorCode::NoSuchWindow));
478 RefPtr<InspectorObject> parameters = InspectorObject::create();
479 parameters->setString(ASCIILiteral("handle"), m_toplevelBrowsingContext.value());
480 m_host->sendCommandToBackend(ASCIILiteral("getBrowsingContext"), WTFMove(parameters), [this, protectedThis = makeRef(*this), completionHandler = WTFMove(completionHandler)](SessionHost::CommandResponse&& response) {
481 if (response.isError || !response.responseObject) {
482 completionHandler(CommandResult::fail(WTFMove(response.responseObject)));
485 RefPtr<InspectorObject> browsingContext;
486 if (!response.responseObject->getObject(ASCIILiteral("context"), browsingContext)) {
487 completionHandler(CommandResult::fail(CommandResult::ErrorCode::UnknownError));
490 RefPtr<InspectorObject> windowOrigin;
491 if (!browsingContext->getObject(ASCIILiteral("windowOrigin"), windowOrigin)) {
492 completionHandler(CommandResult::fail(CommandResult::ErrorCode::UnknownError));
495 completionHandler(CommandResult::success(WTFMove(windowOrigin)));
499 void Session::setWindowPosition(int windowX, int windowY, Function<void (CommandResult&&)>&& completionHandler)
501 if (!m_toplevelBrowsingContext) {
502 completionHandler(CommandResult::fail(CommandResult::ErrorCode::NoSuchWindow));
506 RefPtr<InspectorObject> parameters = InspectorObject::create();
507 parameters->setString(ASCIILiteral("handle"), m_toplevelBrowsingContext.value());
508 RefPtr<InspectorObject> windowOrigin = InspectorObject::create();
509 windowOrigin->setInteger("x", windowX);
510 windowOrigin->setInteger("y", windowY);
511 parameters->setObject(ASCIILiteral("origin"), WTFMove(windowOrigin));
512 m_host->sendCommandToBackend(ASCIILiteral("moveWindowOfBrowsingContext"), WTFMove(parameters), [this, protectedThis = makeRef(*this), completionHandler = WTFMove(completionHandler)](SessionHost::CommandResponse&& response) {
513 if (response.isError) {
514 completionHandler(CommandResult::fail(WTFMove(response.responseObject)));
517 completionHandler(CommandResult::success());
521 void Session::getWindowSize(Function<void (CommandResult&&)>&& completionHandler)
523 if (!m_toplevelBrowsingContext) {
524 completionHandler(CommandResult::fail(CommandResult::ErrorCode::NoSuchWindow));
528 RefPtr<InspectorObject> parameters = InspectorObject::create();
529 parameters->setString(ASCIILiteral("handle"), m_toplevelBrowsingContext.value());
530 m_host->sendCommandToBackend(ASCIILiteral("getBrowsingContext"), WTFMove(parameters), [this, protectedThis = makeRef(*this), completionHandler = WTFMove(completionHandler)](SessionHost::CommandResponse&& response) {
531 if (response.isError || !response.responseObject) {
532 completionHandler(CommandResult::fail(WTFMove(response.responseObject)));
535 RefPtr<InspectorObject> browsingContext;
536 if (!response.responseObject->getObject(ASCIILiteral("context"), browsingContext)) {
537 completionHandler(CommandResult::fail(CommandResult::ErrorCode::UnknownError));
540 RefPtr<InspectorObject> windowSize;
541 if (!browsingContext->getObject(ASCIILiteral("windowSize"), windowSize)) {
542 completionHandler(CommandResult::fail(CommandResult::ErrorCode::UnknownError));
545 completionHandler(CommandResult::success(WTFMove(windowSize)));
549 void Session::setWindowSize(int windowWidth, int windowHeight, Function<void (CommandResult&&)>&& completionHandler)
551 if (!m_toplevelBrowsingContext) {
552 completionHandler(CommandResult::fail(CommandResult::ErrorCode::NoSuchWindow));
556 RefPtr<InspectorObject> parameters = InspectorObject::create();
557 parameters->setString(ASCIILiteral("handle"), m_toplevelBrowsingContext.value());
558 RefPtr<InspectorObject> windowSize = InspectorObject::create();
559 windowSize->setInteger("width", windowWidth);
560 windowSize->setInteger("height", windowHeight);
561 parameters->setObject(ASCIILiteral("size"), WTFMove(windowSize));
562 m_host->sendCommandToBackend(ASCIILiteral("resizeWindowOfBrowsingContext"), WTFMove(parameters), [this, protectedThis = makeRef(*this), completionHandler = WTFMove(completionHandler)](SessionHost::CommandResponse&& response) {
563 if (response.isError) {
564 completionHandler(CommandResult::fail(WTFMove(response.responseObject)));
567 completionHandler(CommandResult::success());
571 RefPtr<InspectorObject> Session::createElement(RefPtr<InspectorValue>&& value)
573 RefPtr<InspectorObject> valueObject;
574 if (!value->asObject(valueObject))
578 if (!valueObject->getString("session-node-" + m_id, elementID))
581 RefPtr<InspectorObject> elementObject = InspectorObject::create();
582 elementObject->setString(webElementIdentifier, elementID);
583 return elementObject;
586 RefPtr<InspectorObject> Session::createElement(const String& elementID)
588 RefPtr<InspectorObject> elementObject = InspectorObject::create();
589 elementObject->setString("session-node-" + m_id, elementID);
590 return elementObject;
593 RefPtr<InspectorObject> Session::extractElement(InspectorValue& value)
595 String elementID = extractElementID(value);
596 return !elementID.isEmpty() ? createElement(elementID) : nullptr;
599 String Session::extractElementID(InspectorValue& value)
601 RefPtr<InspectorObject> valueObject;
602 if (!value.asObject(valueObject))
603 return emptyString();
606 if (!valueObject->getString(webElementIdentifier, elementID))
607 return emptyString();
612 void Session::computeElementLayout(const String& elementID, OptionSet<ElementLayoutOption> options, Function<void (std::optional<Rect>&&, std::optional<Point>&&, bool, RefPtr<InspectorObject>&&)>&& completionHandler)
614 ASSERT(m_toplevelBrowsingContext.value());
616 RefPtr<InspectorObject> parameters = InspectorObject::create();
617 parameters->setString(ASCIILiteral("browsingContextHandle"), m_toplevelBrowsingContext.value());
618 parameters->setString(ASCIILiteral("frameHandle"), m_currentBrowsingContext.value());
619 parameters->setString(ASCIILiteral("nodeHandle"), elementID);
620 parameters->setBoolean(ASCIILiteral("scrollIntoViewIfNeeded"), options.contains(ElementLayoutOption::ScrollIntoViewIfNeeded));
621 parameters->setBoolean(ASCIILiteral("useViewportCoordinates"), options.contains(ElementLayoutOption::UseViewportCoordinates));
622 m_host->sendCommandToBackend(ASCIILiteral("computeElementLayout"), WTFMove(parameters), [this, protectedThis = makeRef(*this), completionHandler = WTFMove(completionHandler)](SessionHost::CommandResponse&& response) mutable {
623 if (response.isError || !response.responseObject) {
624 completionHandler(std::nullopt, std::nullopt, false, WTFMove(response.responseObject));
627 RefPtr<InspectorObject> rectObject;
628 if (!response.responseObject->getObject(ASCIILiteral("rect"), rectObject)) {
629 completionHandler(std::nullopt, std::nullopt, false, nullptr);
632 std::optional<int> elementX;
633 std::optional<int> elementY;
634 RefPtr<InspectorObject> elementPosition;
635 if (rectObject->getObject(ASCIILiteral("origin"), elementPosition)) {
637 if (elementPosition->getInteger(ASCIILiteral("x"), x) && elementPosition->getInteger(ASCIILiteral("y"), y)) {
642 if (!elementX || !elementY) {
643 completionHandler(std::nullopt, std::nullopt, false, nullptr);
646 std::optional<int> elementWidth;
647 std::optional<int> elementHeight;
648 RefPtr<InspectorObject> elementSize;
649 if (rectObject->getObject(ASCIILiteral("size"), elementSize)) {
651 if (elementSize->getInteger(ASCIILiteral("width"), width) && elementSize->getInteger(ASCIILiteral("height"), height)) {
652 elementWidth = width;
653 elementHeight = height;
656 if (!elementWidth || !elementHeight) {
657 completionHandler(std::nullopt, std::nullopt, false, nullptr);
660 Rect rect = { { elementX.value(), elementY.value() }, { elementWidth.value(), elementHeight.value() } };
663 if (!response.responseObject->getBoolean(ASCIILiteral("isObscured"), isObscured)) {
664 completionHandler(std::nullopt, std::nullopt, false, nullptr);
667 RefPtr<InspectorObject> inViewCenterPointObject;
668 if (!response.responseObject->getObject(ASCIILiteral("inViewCenterPoint"), inViewCenterPointObject)) {
669 completionHandler(rect, std::nullopt, isObscured, nullptr);
672 int inViewCenterPointX, inViewCenterPointY;
673 if (!inViewCenterPointObject->getInteger(ASCIILiteral("x"), inViewCenterPointX)
674 || !inViewCenterPointObject->getInteger(ASCIILiteral("y"), inViewCenterPointY)) {
675 completionHandler(std::nullopt, std::nullopt, isObscured, nullptr);
678 Point inViewCenterPoint = { inViewCenterPointX, inViewCenterPointY };
679 completionHandler(rect, inViewCenterPoint, isObscured, nullptr);
683 void Session::findElements(const String& strategy, const String& selector, FindElementsMode mode, const String& rootElementID, Function<void (CommandResult&&)>&& completionHandler)
685 if (!m_toplevelBrowsingContext) {
686 completionHandler(CommandResult::fail(CommandResult::ErrorCode::NoSuchWindow));
690 auto implicitWait = m_timeouts.implicit.value_or(0_s);
691 RefPtr<InspectorArray> arguments = InspectorArray::create();
692 arguments->pushString(InspectorValue::create(strategy)->toJSONString());
693 if (rootElementID.isEmpty())
694 arguments->pushString(InspectorValue::null()->toJSONString());
696 arguments->pushString(createElement(rootElementID)->toJSONString());
697 arguments->pushString(InspectorValue::create(selector)->toJSONString());
698 arguments->pushString(InspectorValue::create(mode == FindElementsMode::Single)->toJSONString());
699 arguments->pushString(InspectorValue::create(implicitWait.milliseconds())->toJSONString());
701 RefPtr<InspectorObject> parameters = InspectorObject::create();
702 parameters->setString(ASCIILiteral("browsingContextHandle"), m_toplevelBrowsingContext.value());
703 if (m_currentBrowsingContext)
704 parameters->setString(ASCIILiteral("frameHandle"), m_currentBrowsingContext.value());
705 parameters->setString(ASCIILiteral("function"), FindNodesJavaScript);
706 parameters->setArray(ASCIILiteral("arguments"), WTFMove(arguments));
707 parameters->setBoolean(ASCIILiteral("expectsImplicitCallbackArgument"), true);
708 // If there's an implicit wait, use one second more as callback timeout.
710 parameters->setInteger(ASCIILiteral("callbackTimeout"), Seconds(implicitWait + 1_s).millisecondsAs<int>());
712 m_host->sendCommandToBackend(ASCIILiteral("evaluateJavaScriptFunction"), WTFMove(parameters), [this, protectedThis = makeRef(*this), mode, completionHandler = WTFMove(completionHandler)](SessionHost::CommandResponse&& response) {
713 if (response.isError || !response.responseObject) {
714 completionHandler(CommandResult::fail(WTFMove(response.responseObject)));
718 if (!response.responseObject->getString(ASCIILiteral("result"), valueString)) {
719 completionHandler(CommandResult::fail(CommandResult::ErrorCode::UnknownError));
722 RefPtr<InspectorValue> resultValue;
723 if (!InspectorValue::parseJSON(valueString, resultValue)) {
724 completionHandler(CommandResult::fail(CommandResult::ErrorCode::UnknownError));
729 case FindElementsMode::Single: {
730 RefPtr<InspectorObject> elementObject = createElement(WTFMove(resultValue));
731 if (!elementObject) {
732 completionHandler(CommandResult::fail(CommandResult::ErrorCode::NoSuchElement));
735 completionHandler(CommandResult::success(WTFMove(elementObject)));
738 case FindElementsMode::Multiple: {
739 RefPtr<InspectorArray> elementsArray;
740 if (!resultValue->asArray(elementsArray)) {
741 completionHandler(CommandResult::fail(CommandResult::ErrorCode::NoSuchElement));
744 RefPtr<InspectorArray> elementObjectsArray = InspectorArray::create();
745 unsigned elementsArrayLength = elementsArray->length();
746 for (unsigned i = 0; i < elementsArrayLength; ++i) {
747 if (auto elementObject = createElement(elementsArray->get(i)))
748 elementObjectsArray->pushObject(WTFMove(elementObject));
750 completionHandler(CommandResult::success(WTFMove(elementObjectsArray)));
757 void Session::isElementSelected(const String& elementID, Function<void (CommandResult&&)>&& completionHandler)
759 if (!m_toplevelBrowsingContext) {
760 completionHandler(CommandResult::fail(CommandResult::ErrorCode::NoSuchWindow));
764 RefPtr<InspectorArray> arguments = InspectorArray::create();
765 arguments->pushString(createElement(elementID)->toJSONString());
766 arguments->pushString(InspectorValue::create("selected")->toJSONString());
768 RefPtr<InspectorObject> parameters = InspectorObject::create();
769 parameters->setString(ASCIILiteral("browsingContextHandle"), m_toplevelBrowsingContext.value());
770 if (m_currentBrowsingContext)
771 parameters->setString(ASCIILiteral("frameHandle"), m_currentBrowsingContext.value());
772 parameters->setString(ASCIILiteral("function"), ElementAttributeJavaScript);
773 parameters->setArray(ASCIILiteral("arguments"), WTFMove(arguments));
774 m_host->sendCommandToBackend(ASCIILiteral("evaluateJavaScriptFunction"), WTFMove(parameters), [this, protectedThis = makeRef(*this), completionHandler = WTFMove(completionHandler)](SessionHost::CommandResponse&& response) {
775 if (response.isError || !response.responseObject) {
776 completionHandler(CommandResult::fail(WTFMove(response.responseObject)));
780 if (!response.responseObject->getString(ASCIILiteral("result"), valueString)) {
781 completionHandler(CommandResult::fail(CommandResult::ErrorCode::UnknownError));
784 RefPtr<InspectorValue> resultValue;
785 if (!InspectorValue::parseJSON(valueString, resultValue)) {
786 completionHandler(CommandResult::fail(CommandResult::ErrorCode::UnknownError));
789 if (resultValue->isNull()) {
790 completionHandler(CommandResult::success(InspectorValue::create(false)));
793 String booleanResult;
794 if (!resultValue->asString(booleanResult) || booleanResult != "true") {
795 completionHandler(CommandResult::fail(CommandResult::ErrorCode::UnknownError));
798 completionHandler(CommandResult::success(InspectorValue::create(true)));
802 void Session::getElementText(const String& elementID, Function<void (CommandResult&&)>&& completionHandler)
804 if (!m_toplevelBrowsingContext) {
805 completionHandler(CommandResult::fail(CommandResult::ErrorCode::NoSuchWindow));
809 RefPtr<InspectorArray> arguments = InspectorArray::create();
810 arguments->pushString(createElement(elementID)->toJSONString());
812 RefPtr<InspectorObject> parameters = InspectorObject::create();
813 parameters->setString(ASCIILiteral("browsingContextHandle"), m_toplevelBrowsingContext.value());
814 if (m_currentBrowsingContext)
815 parameters->setString(ASCIILiteral("frameHandle"), m_currentBrowsingContext.value());
816 // FIXME: Add an atom to properly implement this instead of just using innerText.
817 parameters->setString(ASCIILiteral("function"), ASCIILiteral("function(element) { return element.innerText.replace(/^[^\\S\\xa0]+|[^\\S\\xa0]+$/g, '') }"));
818 parameters->setArray(ASCIILiteral("arguments"), WTFMove(arguments));
819 m_host->sendCommandToBackend(ASCIILiteral("evaluateJavaScriptFunction"), WTFMove(parameters), [this, protectedThis = makeRef(*this), completionHandler = WTFMove(completionHandler)](SessionHost::CommandResponse&& response) {
820 if (response.isError || !response.responseObject) {
821 completionHandler(CommandResult::fail(WTFMove(response.responseObject)));
825 if (!response.responseObject->getString(ASCIILiteral("result"), valueString)) {
826 completionHandler(CommandResult::fail(CommandResult::ErrorCode::UnknownError));
829 RefPtr<InspectorValue> resultValue;
830 if (!InspectorValue::parseJSON(valueString, resultValue)) {
831 completionHandler(CommandResult::fail(CommandResult::ErrorCode::UnknownError));
834 completionHandler(CommandResult::success(WTFMove(resultValue)));
838 void Session::getElementTagName(const String& elementID, Function<void (CommandResult&&)>&& completionHandler)
840 if (!m_toplevelBrowsingContext) {
841 completionHandler(CommandResult::fail(CommandResult::ErrorCode::NoSuchWindow));
845 RefPtr<InspectorArray> arguments = InspectorArray::create();
846 arguments->pushString(createElement(elementID)->toJSONString());
848 RefPtr<InspectorObject> parameters = InspectorObject::create();
849 parameters->setString(ASCIILiteral("browsingContextHandle"), m_toplevelBrowsingContext.value());
850 if (m_currentBrowsingContext)
851 parameters->setString(ASCIILiteral("frameHandle"), m_currentBrowsingContext.value());
852 parameters->setString(ASCIILiteral("function"), ASCIILiteral("function(element) { return element.tagName.toLowerCase() }"));
853 parameters->setArray(ASCIILiteral("arguments"), WTFMove(arguments));
854 m_host->sendCommandToBackend(ASCIILiteral("evaluateJavaScriptFunction"), WTFMove(parameters), [this, protectedThis = makeRef(*this), completionHandler = WTFMove(completionHandler)](SessionHost::CommandResponse&& response) {
855 if (response.isError || !response.responseObject) {
856 completionHandler(CommandResult::fail(WTFMove(response.responseObject)));
860 if (!response.responseObject->getString(ASCIILiteral("result"), valueString)) {
861 completionHandler(CommandResult::fail(CommandResult::ErrorCode::UnknownError));
864 RefPtr<InspectorValue> resultValue;
865 if (!InspectorValue::parseJSON(valueString, resultValue)) {
866 completionHandler(CommandResult::fail(CommandResult::ErrorCode::UnknownError));
869 completionHandler(CommandResult::success(WTFMove(resultValue)));
873 void Session::getElementRect(const String& elementID, Function<void (CommandResult&&)>&& completionHandler)
875 if (!m_toplevelBrowsingContext) {
876 completionHandler(CommandResult::fail(CommandResult::ErrorCode::NoSuchWindow));
880 computeElementLayout(elementID, { }, [this, protectedThis = makeRef(*this), completionHandler = WTFMove(completionHandler)](std::optional<Rect>&& rect, std::optional<Point>&&, bool, RefPtr<InspectorObject>&& error) {
881 if (!rect || error) {
882 completionHandler(CommandResult::fail(WTFMove(error)));
885 RefPtr<InspectorObject> rectObject = InspectorObject::create();
886 rectObject->setInteger(ASCIILiteral("x"), rect.value().origin.x);
887 rectObject->setInteger(ASCIILiteral("y"), rect.value().origin.y);
888 rectObject->setInteger(ASCIILiteral("width"), rect.value().size.width);
889 rectObject->setInteger(ASCIILiteral("height"), rect.value().size.height);
890 completionHandler(CommandResult::success(WTFMove(rectObject)));
894 void Session::isElementEnabled(const String& elementID, 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());
904 RefPtr<InspectorObject> parameters = InspectorObject::create();
905 parameters->setString(ASCIILiteral("browsingContextHandle"), m_toplevelBrowsingContext.value());
906 if (m_currentBrowsingContext)
907 parameters->setString(ASCIILiteral("frameHandle"), m_currentBrowsingContext.value());
908 parameters->setString(ASCIILiteral("function"), ASCIILiteral("function(element) { return element.disabled === undefined ? true : !element.disabled }"));
909 parameters->setArray(ASCIILiteral("arguments"), WTFMove(arguments));
910 m_host->sendCommandToBackend(ASCIILiteral("evaluateJavaScriptFunction"), WTFMove(parameters), [this, protectedThis = makeRef(*this), completionHandler = WTFMove(completionHandler)](SessionHost::CommandResponse&& response) {
911 if (response.isError || !response.responseObject) {
912 completionHandler(CommandResult::fail(WTFMove(response.responseObject)));
916 if (!response.responseObject->getString(ASCIILiteral("result"), valueString)) {
917 completionHandler(CommandResult::fail(CommandResult::ErrorCode::UnknownError));
920 RefPtr<InspectorValue> resultValue;
921 if (!InspectorValue::parseJSON(valueString, resultValue)) {
922 completionHandler(CommandResult::fail(CommandResult::ErrorCode::UnknownError));
925 completionHandler(CommandResult::success(WTFMove(resultValue)));
929 void Session::isElementDisplayed(const String& elementID, Function<void (CommandResult&&)>&& completionHandler)
931 if (!m_toplevelBrowsingContext) {
932 completionHandler(CommandResult::fail(CommandResult::ErrorCode::NoSuchWindow));
936 RefPtr<InspectorArray> arguments = InspectorArray::create();
937 arguments->pushString(createElement(elementID)->toJSONString());
939 RefPtr<InspectorObject> parameters = InspectorObject::create();
940 parameters->setString(ASCIILiteral("browsingContextHandle"), m_toplevelBrowsingContext.value());
941 if (m_currentBrowsingContext)
942 parameters->setString(ASCIILiteral("frameHandle"), m_currentBrowsingContext.value());
943 parameters->setString(ASCIILiteral("function"), ElementDisplayedJavaScript);
944 parameters->setArray(ASCIILiteral("arguments"), WTFMove(arguments));
945 m_host->sendCommandToBackend(ASCIILiteral("evaluateJavaScriptFunction"), WTFMove(parameters), [this, protectedThis = makeRef(*this), completionHandler = WTFMove(completionHandler)](SessionHost::CommandResponse&& response) {
946 if (response.isError || !response.responseObject) {
947 completionHandler(CommandResult::fail(WTFMove(response.responseObject)));
951 if (!response.responseObject->getString(ASCIILiteral("result"), valueString)) {
952 completionHandler(CommandResult::fail(CommandResult::ErrorCode::UnknownError));
955 RefPtr<InspectorValue> resultValue;
956 if (!InspectorValue::parseJSON(valueString, resultValue)) {
957 completionHandler(CommandResult::fail(CommandResult::ErrorCode::UnknownError));
960 completionHandler(CommandResult::success(WTFMove(resultValue)));
964 void Session::getElementAttribute(const String& elementID, const String& attribute, Function<void (CommandResult&&)>&& completionHandler)
966 if (!m_toplevelBrowsingContext) {
967 completionHandler(CommandResult::fail(CommandResult::ErrorCode::NoSuchWindow));
971 RefPtr<InspectorArray> arguments = InspectorArray::create();
972 arguments->pushString(createElement(elementID)->toJSONString());
973 arguments->pushString(InspectorValue::create(attribute)->toJSONString());
975 RefPtr<InspectorObject> parameters = InspectorObject::create();
976 parameters->setString(ASCIILiteral("browsingContextHandle"), m_toplevelBrowsingContext.value());
977 if (m_currentBrowsingContext)
978 parameters->setString(ASCIILiteral("frameHandle"), m_currentBrowsingContext.value());
979 parameters->setString(ASCIILiteral("function"), ElementAttributeJavaScript);
980 parameters->setArray(ASCIILiteral("arguments"), WTFMove(arguments));
981 m_host->sendCommandToBackend(ASCIILiteral("evaluateJavaScriptFunction"), WTFMove(parameters), [this, protectedThis = makeRef(*this), completionHandler = WTFMove(completionHandler)](SessionHost::CommandResponse&& response) {
982 if (response.isError || !response.responseObject) {
983 completionHandler(CommandResult::fail(WTFMove(response.responseObject)));
987 if (!response.responseObject->getString(ASCIILiteral("result"), valueString)) {
988 completionHandler(CommandResult::fail(CommandResult::ErrorCode::UnknownError));
991 RefPtr<InspectorValue> resultValue;
992 if (!InspectorValue::parseJSON(valueString, resultValue)) {
993 completionHandler(CommandResult::fail(CommandResult::ErrorCode::UnknownError));
996 completionHandler(CommandResult::success(WTFMove(resultValue)));
1000 void Session::waitForNavigationToComplete(Function<void (CommandResult&&)>&& completionHandler)
1002 if (!m_toplevelBrowsingContext) {
1003 completionHandler(CommandResult::fail(CommandResult::ErrorCode::NoSuchWindow));
1007 RefPtr<InspectorObject> parameters = InspectorObject::create();
1008 parameters->setString(ASCIILiteral("browsingContextHandle"), m_toplevelBrowsingContext.value());
1009 if (m_currentBrowsingContext)
1010 parameters->setString(ASCIILiteral("frameHandle"), m_currentBrowsingContext.value());
1011 if (m_timeouts.pageLoad)
1012 parameters->setInteger(ASCIILiteral("pageLoadTimeout"), m_timeouts.pageLoad.value().millisecondsAs<int>());
1013 if (auto pageLoadStrategy = pageLoadStrategyString())
1014 parameters->setString(ASCIILiteral("pageLoadStrategy"), pageLoadStrategy.value());
1015 m_host->sendCommandToBackend(ASCIILiteral("waitForNavigationToComplete"), WTFMove(parameters), [this, protectedThis = makeRef(*this), completionHandler = WTFMove(completionHandler)](SessionHost::CommandResponse&& response) {
1016 if (response.isError) {
1017 auto result = CommandResult::fail(WTFMove(response.responseObject));
1018 if (result.errorCode() == CommandResult::ErrorCode::NoSuchFrame) {
1019 // Navigation destroyed the current frame, switch to top level browsing context and ignore the error.
1020 switchToBrowsingContext(std::nullopt);
1022 completionHandler(WTFMove(result));
1026 completionHandler(CommandResult::success());
1030 void Session::elementClick(const String& elementID, Function<void (CommandResult&&)>&& completionHandler)
1032 if (!m_toplevelBrowsingContext) {
1033 completionHandler(CommandResult::fail(CommandResult::ErrorCode::NoSuchWindow));
1037 OptionSet<ElementLayoutOption> options = ElementLayoutOption::ScrollIntoViewIfNeeded;
1038 options |= ElementLayoutOption::UseViewportCoordinates;
1039 computeElementLayout(elementID, options, [this, protectedThis = makeRef(*this), completionHandler = WTFMove(completionHandler)](std::optional<Rect>&& rect, std::optional<Point>&& inViewCenter, bool isObscured, RefPtr<InspectorObject>&& error) mutable {
1040 if (!rect || error) {
1041 completionHandler(CommandResult::fail(WTFMove(error)));
1045 completionHandler(CommandResult::fail(CommandResult::ErrorCode::ElementClickIntercepted));
1048 if (!inViewCenter) {
1049 completionHandler(CommandResult::fail(CommandResult::ErrorCode::ElementNotInteractable));
1053 performMouseInteraction(inViewCenter.value().x, inViewCenter.value().y, MouseButton::Left, MouseInteraction::SingleClick, WTFMove(completionHandler));
1055 waitForNavigationToComplete(WTFMove(completionHandler));
1059 void Session::elementClear(const String& elementID, Function<void (CommandResult&&)>&& completionHandler)
1061 if (!m_toplevelBrowsingContext) {
1062 completionHandler(CommandResult::fail(CommandResult::ErrorCode::NoSuchWindow));
1066 RefPtr<InspectorArray> arguments = InspectorArray::create();
1067 arguments->pushString(createElement(elementID)->toJSONString());
1069 RefPtr<InspectorObject> parameters = InspectorObject::create();
1070 parameters->setString(ASCIILiteral("browsingContextHandle"), m_toplevelBrowsingContext.value());
1071 if (m_currentBrowsingContext)
1072 parameters->setString(ASCIILiteral("frameHandle"), m_currentBrowsingContext.value());
1073 parameters->setString(ASCIILiteral("function"), FormElementClearJavaScript);
1074 parameters->setArray(ASCIILiteral("arguments"), WTFMove(arguments));
1075 m_host->sendCommandToBackend(ASCIILiteral("evaluateJavaScriptFunction"), WTFMove(parameters), [this, protectedThis = makeRef(*this), completionHandler = WTFMove(completionHandler)](SessionHost::CommandResponse&& response) {
1076 if (response.isError) {
1077 completionHandler(CommandResult::fail(WTFMove(response.responseObject)));
1080 completionHandler(CommandResult::success());
1084 String Session::virtualKeyForKeySequence(const String& keySequence, KeyModifier& modifier)
1086 // §17.4.2 Keyboard Actions.
1087 // https://www.w3.org/TR/webdriver/#keyboard-actions
1088 modifier = KeyModifier::None;
1089 switch (keySequence[0]) {
1091 return ASCIILiteral("Cancel");
1093 return ASCIILiteral("Help");
1095 return ASCIILiteral("Backspace");
1097 return ASCIILiteral("Tab");
1099 return ASCIILiteral("Clear");
1101 return ASCIILiteral("Return");
1103 return ASCIILiteral("Enter");
1105 modifier = KeyModifier::Shift;
1106 return ASCIILiteral("Shift");
1108 modifier = KeyModifier::Control;
1109 return ASCIILiteral("Control");
1111 modifier = KeyModifier::Alternate;
1112 return ASCIILiteral("Alternate");
1114 return ASCIILiteral("Pause");
1116 return ASCIILiteral("Escape");
1118 return ASCIILiteral("Space");
1120 return ASCIILiteral("PageUp");
1122 return ASCIILiteral("PageDown");
1124 return ASCIILiteral("End");
1126 return ASCIILiteral("Home");
1128 return ASCIILiteral("LeftArrow");
1130 return ASCIILiteral("UpArrow");
1132 return ASCIILiteral("RightArrow");
1134 return ASCIILiteral("DownArrow");
1136 return ASCIILiteral("Insert");
1138 return ASCIILiteral("Delete");
1140 return ASCIILiteral("Semicolon");
1142 return ASCIILiteral("Equals");
1144 return ASCIILiteral("NumberPad0");
1146 return ASCIILiteral("NumberPad1");
1148 return ASCIILiteral("NumberPad2");
1150 return ASCIILiteral("NumberPad3");
1152 return ASCIILiteral("NumberPad4");
1154 return ASCIILiteral("NumberPad5");
1156 return ASCIILiteral("NumberPad6");
1158 return ASCIILiteral("NumberPad7");
1160 return ASCIILiteral("NumberPad8");
1162 return ASCIILiteral("NumberPad9");
1164 return ASCIILiteral("NumberPadMultiply");
1166 return ASCIILiteral("NumberPadAdd");
1168 return ASCIILiteral("NumberPadSeparator");
1170 return ASCIILiteral("NumberPadSubtract");
1172 return ASCIILiteral("NumberPadDecimal");
1174 return ASCIILiteral("NumberPadDivide");
1176 return ASCIILiteral("Function1");
1178 return ASCIILiteral("Function2");
1180 return ASCIILiteral("Function3");
1182 return ASCIILiteral("Function4");
1184 return ASCIILiteral("Function5");
1186 return ASCIILiteral("Function6");
1188 return ASCIILiteral("Function7");
1190 return ASCIILiteral("Function8");
1192 return ASCIILiteral("Function9");
1194 return ASCIILiteral("Function10");
1196 return ASCIILiteral("Function11");
1198 return ASCIILiteral("Function12");
1200 modifier = KeyModifier::Meta;
1201 return ASCIILiteral("Meta");
1208 void Session::elementSendKeys(const String& elementID, Vector<String>&& keys, Function<void (CommandResult&&)>&& completionHandler)
1210 if (!m_toplevelBrowsingContext) {
1211 completionHandler(CommandResult::fail(CommandResult::ErrorCode::NoSuchWindow));
1215 // FIXME: move this to an atom.
1216 static const char focusScript[] =
1217 "function focus(element) {"
1218 " var doc = element.ownerDocument || element;"
1219 " var prevActiveElement = doc.activeElement;"
1220 " if (element != prevActiveElement && prevActiveElement)"
1221 " prevActiveElement.blur();"
1223 " if (element != prevActiveElement && element.value && element.value.length && element.setSelectionRange)"
1224 " element.setSelectionRange(element.value.length, element.value.length);"
1225 " if (element != doc.activeElement)"
1226 " throw new Error('cannot focus element');"
1229 RefPtr<InspectorArray> arguments = InspectorArray::create();
1230 arguments->pushString(createElement(elementID)->toJSONString());
1231 RefPtr<InspectorObject> parameters = InspectorObject::create();
1232 parameters->setString(ASCIILiteral("browsingContextHandle"), m_toplevelBrowsingContext.value());
1233 if (m_currentBrowsingContext)
1234 parameters->setString(ASCIILiteral("frameHandle"), m_currentBrowsingContext.value());
1235 parameters->setString(ASCIILiteral("function"), focusScript);
1236 parameters->setArray(ASCIILiteral("arguments"), WTFMove(arguments));
1237 m_host->sendCommandToBackend(ASCIILiteral("evaluateJavaScriptFunction"), WTFMove(parameters), [this, protectedThis = makeRef(*this), keys = WTFMove(keys), completionHandler = WTFMove(completionHandler)](SessionHost::CommandResponse&& response) mutable {
1238 if (response.isError || !response.responseObject) {
1239 completionHandler(CommandResult::fail(WTFMove(response.responseObject)));
1243 unsigned stickyModifiers = 0;
1244 Vector<KeyboardInteraction> interactions;
1245 interactions.reserveInitialCapacity(keys.size());
1246 for (const auto& key : keys) {
1247 KeyboardInteraction interaction;
1248 KeyModifier modifier;
1249 auto virtualKey = virtualKeyForKeySequence(key, modifier);
1250 if (!virtualKey.isNull()) {
1251 interaction.key = virtualKey;
1252 if (modifier != KeyModifier::None) {
1253 stickyModifiers ^= modifier;
1254 if (stickyModifiers & modifier)
1255 interaction.type = KeyboardInteractionType::KeyPress;
1257 interaction.type = KeyboardInteractionType::KeyRelease;
1260 interaction.text = key;
1261 interactions.uncheckedAppend(WTFMove(interaction));
1264 // Reset sticky modifiers if needed.
1265 if (stickyModifiers) {
1266 if (stickyModifiers & KeyModifier::Shift)
1267 interactions.append({ KeyboardInteractionType::KeyRelease, std::nullopt, std::optional<String>(ASCIILiteral("Shift")) });
1268 if (stickyModifiers & KeyModifier::Control)
1269 interactions.append({ KeyboardInteractionType::KeyRelease, std::nullopt, std::optional<String>(ASCIILiteral("Control")) });
1270 if (stickyModifiers & KeyModifier::Alternate)
1271 interactions.append({ KeyboardInteractionType::KeyRelease, std::nullopt, std::optional<String>(ASCIILiteral("Alternate")) });
1272 if (stickyModifiers & KeyModifier::Meta)
1273 interactions.append({ KeyboardInteractionType::KeyRelease, std::nullopt, std::optional<String>(ASCIILiteral("Meta")) });
1276 performKeyboardInteractions(WTFMove(interactions), WTFMove(completionHandler));
1280 void Session::elementSubmit(const String& elementID, Function<void (CommandResult&&)>&& completionHandler)
1282 if (!m_toplevelBrowsingContext) {
1283 completionHandler(CommandResult::fail(CommandResult::ErrorCode::NoSuchWindow));
1287 RefPtr<InspectorArray> arguments = InspectorArray::create();
1288 arguments->pushString(createElement(elementID)->toJSONString());
1290 RefPtr<InspectorObject> parameters = InspectorObject::create();
1291 parameters->setString(ASCIILiteral("browsingContextHandle"), m_toplevelBrowsingContext.value());
1292 if (m_currentBrowsingContext)
1293 parameters->setString(ASCIILiteral("frameHandle"), m_currentBrowsingContext.value());
1294 parameters->setString(ASCIILiteral("function"), FormSubmitJavaScript);
1295 parameters->setArray(ASCIILiteral("arguments"), WTFMove(arguments));
1296 m_host->sendCommandToBackend(ASCIILiteral("evaluateJavaScriptFunction"), WTFMove(parameters), [this, protectedThis = makeRef(*this), completionHandler = WTFMove(completionHandler)](SessionHost::CommandResponse&& response) {
1297 if (response.isError) {
1298 completionHandler(CommandResult::fail(WTFMove(response.responseObject)));
1301 completionHandler(CommandResult::success());
1305 RefPtr<InspectorValue> Session::handleScriptResult(RefPtr<InspectorValue>&& resultValue)
1307 RefPtr<InspectorArray> resultArray;
1308 if (resultValue->asArray(resultArray)) {
1309 RefPtr<InspectorArray> returnValueArray = InspectorArray::create();
1310 unsigned resultArrayLength = resultArray->length();
1311 for (unsigned i = 0; i < resultArrayLength; ++i)
1312 returnValueArray->pushValue(handleScriptResult(resultArray->get(i)));
1313 return returnValueArray;
1316 if (auto element = createElement(RefPtr<InspectorValue>(resultValue)))
1319 RefPtr<InspectorObject> resultObject;
1320 if (resultValue->asObject(resultObject)) {
1321 RefPtr<InspectorObject> returnValueObject = InspectorObject::create();
1322 auto end = resultObject->end();
1323 for (auto it = resultObject->begin(); it != end; ++it)
1324 returnValueObject->setValue(it->key, handleScriptResult(WTFMove(it->value)));
1325 return returnValueObject;
1331 void Session::executeScript(const String& script, RefPtr<InspectorArray>&& argumentsArray, ExecuteScriptMode mode, Function<void (CommandResult&&)>&& completionHandler)
1333 if (!m_toplevelBrowsingContext) {
1334 completionHandler(CommandResult::fail(CommandResult::ErrorCode::NoSuchWindow));
1338 RefPtr<InspectorArray> arguments = InspectorArray::create();
1339 unsigned argumentsLength = argumentsArray->length();
1340 for (unsigned i = 0; i < argumentsLength; ++i) {
1341 if (auto argument = argumentsArray->get(i)) {
1342 if (auto element = extractElement(*argument))
1343 arguments->pushString(element->toJSONString());
1345 arguments->pushString(argument->toJSONString());
1349 RefPtr<InspectorObject> parameters = InspectorObject::create();
1350 parameters->setString(ASCIILiteral("browsingContextHandle"), m_toplevelBrowsingContext.value());
1351 if (m_currentBrowsingContext)
1352 parameters->setString(ASCIILiteral("frameHandle"), m_currentBrowsingContext.value());
1353 parameters->setString(ASCIILiteral("function"), "function(){" + script + '}');
1354 parameters->setArray(ASCIILiteral("arguments"), WTFMove(arguments));
1355 if (mode == ExecuteScriptMode::Async) {
1356 parameters->setBoolean(ASCIILiteral("expectsImplicitCallbackArgument"), true);
1357 if (m_timeouts.script)
1358 parameters->setInteger(ASCIILiteral("callbackTimeout"), m_timeouts.script.value().millisecondsAs<int>());
1360 m_host->sendCommandToBackend(ASCIILiteral("evaluateJavaScriptFunction"), WTFMove(parameters), [this, protectedThis = makeRef(*this), completionHandler = WTFMove(completionHandler)](SessionHost::CommandResponse&& response) {
1361 if (response.isError || !response.responseObject) {
1362 completionHandler(CommandResult::fail(WTFMove(response.responseObject)));
1366 if (!response.responseObject->getString(ASCIILiteral("result"), valueString)) {
1367 completionHandler(CommandResult::fail(CommandResult::ErrorCode::UnknownError));
1370 if (valueString.isEmpty()) {
1371 completionHandler(CommandResult::success());
1374 RefPtr<InspectorValue> resultValue;
1375 if (!InspectorValue::parseJSON(valueString, resultValue)) {
1376 completionHandler(CommandResult::fail(CommandResult::ErrorCode::UnknownError));
1379 completionHandler(CommandResult::success(handleScriptResult(WTFMove(resultValue))));
1383 void Session::performMouseInteraction(int x, int y, MouseButton button, MouseInteraction interaction, Function<void (CommandResult&&)>&& completionHandler)
1385 RefPtr<InspectorObject> parameters = InspectorObject::create();
1386 parameters->setString(ASCIILiteral("handle"), m_toplevelBrowsingContext.value());
1387 RefPtr<InspectorObject> position = InspectorObject::create();
1388 position->setInteger(ASCIILiteral("x"), x);
1389 position->setInteger(ASCIILiteral("y"), y);
1390 parameters->setObject(ASCIILiteral("position"), WTFMove(position));
1392 case MouseButton::None:
1393 parameters->setString(ASCIILiteral("button"), ASCIILiteral("None"));
1395 case MouseButton::Left:
1396 parameters->setString(ASCIILiteral("button"), ASCIILiteral("Left"));
1398 case MouseButton::Middle:
1399 parameters->setString(ASCIILiteral("button"), ASCIILiteral("Middle"));
1401 case MouseButton::Right:
1402 parameters->setString(ASCIILiteral("button"), ASCIILiteral("Right"));
1405 switch (interaction) {
1406 case MouseInteraction::Move:
1407 parameters->setString(ASCIILiteral("interaction"), ASCIILiteral("Move"));
1409 case MouseInteraction::Down:
1410 parameters->setString(ASCIILiteral("interaction"), ASCIILiteral("Down"));
1412 case MouseInteraction::Up:
1413 parameters->setString(ASCIILiteral("interaction"), ASCIILiteral("Up"));
1415 case MouseInteraction::SingleClick:
1416 parameters->setString(ASCIILiteral("interaction"), ASCIILiteral("SingleClick"));
1418 case MouseInteraction::DoubleClick:
1419 parameters->setString(ASCIILiteral("interaction"), ASCIILiteral("DoubleClick"));
1422 parameters->setArray(ASCIILiteral("modifiers"), InspectorArray::create());
1423 m_host->sendCommandToBackend(ASCIILiteral("performMouseInteraction"), WTFMove(parameters), [this, protectedThis = makeRef(*this), completionHandler = WTFMove(completionHandler)](SessionHost::CommandResponse&& response) {
1424 if (response.isError) {
1425 completionHandler(CommandResult::fail(WTFMove(response.responseObject)));
1428 completionHandler(CommandResult::success());
1432 void Session::performKeyboardInteractions(Vector<KeyboardInteraction>&& interactions, Function<void (CommandResult&&)>&& completionHandler)
1434 RefPtr<InspectorObject> parameters = InspectorObject::create();
1435 parameters->setString(ASCIILiteral("handle"), m_toplevelBrowsingContext.value());
1436 RefPtr<InspectorArray> interactionsArray = InspectorArray::create();
1437 for (const auto& interaction : interactions) {
1438 RefPtr<InspectorObject> interactionObject = InspectorObject::create();
1439 switch (interaction.type) {
1440 case KeyboardInteractionType::KeyPress:
1441 interactionObject->setString(ASCIILiteral("type"), ASCIILiteral("KeyPress"));
1443 case KeyboardInteractionType::KeyRelease:
1444 interactionObject->setString(ASCIILiteral("type"), ASCIILiteral("KeyRelease"));
1446 case KeyboardInteractionType::InsertByKey:
1447 interactionObject->setString(ASCIILiteral("type"), ASCIILiteral("InsertByKey"));
1450 if (interaction.key)
1451 interactionObject->setString(ASCIILiteral("key"), interaction.key.value());
1452 if (interaction.text)
1453 interactionObject->setString(ASCIILiteral("text"), interaction.text.value());
1454 interactionsArray->pushObject(WTFMove(interactionObject));
1456 parameters->setArray(ASCIILiteral("interactions"), WTFMove(interactionsArray));
1457 m_host->sendCommandToBackend(ASCIILiteral("performKeyboardInteractions"), WTFMove(parameters), [this, protectedThis = makeRef(*this), completionHandler = WTFMove(completionHandler)](SessionHost::CommandResponse&& response) {
1458 if (response.isError) {
1459 completionHandler(CommandResult::fail(WTFMove(response.responseObject)));
1462 completionHandler(CommandResult::success());
1466 void Session::dismissAlert(Function<void (CommandResult&&)>&& completionHandler)
1468 if (!m_toplevelBrowsingContext) {
1469 completionHandler(CommandResult::fail(CommandResult::ErrorCode::NoSuchWindow));
1473 RefPtr<InspectorObject> parameters = InspectorObject::create();
1474 parameters->setString(ASCIILiteral("browsingContextHandle"), m_toplevelBrowsingContext.value());
1475 m_host->sendCommandToBackend(ASCIILiteral("dismissCurrentJavaScriptDialog"), WTFMove(parameters), [this, protectedThis = makeRef(*this), completionHandler = WTFMove(completionHandler)](SessionHost::CommandResponse&& response) {
1476 if (response.isError) {
1477 completionHandler(CommandResult::fail(WTFMove(response.responseObject)));
1480 completionHandler(CommandResult::success());
1484 void Session::acceptAlert(Function<void (CommandResult&&)>&& completionHandler)
1486 if (!m_toplevelBrowsingContext) {
1487 completionHandler(CommandResult::fail(CommandResult::ErrorCode::NoSuchWindow));
1491 RefPtr<InspectorObject> parameters = InspectorObject::create();
1492 parameters->setString(ASCIILiteral("browsingContextHandle"), m_toplevelBrowsingContext.value());
1493 m_host->sendCommandToBackend(ASCIILiteral("acceptCurrentJavaScriptDialog"), WTFMove(parameters), [this, protectedThis = makeRef(*this), completionHandler = WTFMove(completionHandler)](SessionHost::CommandResponse&& response) {
1494 if (response.isError) {
1495 completionHandler(CommandResult::fail(WTFMove(response.responseObject)));
1498 completionHandler(CommandResult::success());
1502 void Session::getAlertText(Function<void (CommandResult&&)>&& completionHandler)
1504 if (!m_toplevelBrowsingContext) {
1505 completionHandler(CommandResult::fail(CommandResult::ErrorCode::NoSuchWindow));
1509 RefPtr<InspectorObject> parameters = InspectorObject::create();
1510 parameters->setString(ASCIILiteral("browsingContextHandle"), m_toplevelBrowsingContext.value());
1511 m_host->sendCommandToBackend(ASCIILiteral("messageOfCurrentJavaScriptDialog"), WTFMove(parameters), [this, protectedThis = makeRef(*this), completionHandler = WTFMove(completionHandler)](SessionHost::CommandResponse&& response) {
1512 if (response.isError || !response.responseObject) {
1513 completionHandler(CommandResult::fail(WTFMove(response.responseObject)));
1517 if (!response.responseObject->getString(ASCIILiteral("message"), valueString)) {
1518 completionHandler(CommandResult::fail(CommandResult::ErrorCode::UnknownError));
1521 completionHandler(CommandResult::success(InspectorValue::create(valueString)));
1525 void Session::sendAlertText(const String& text, Function<void (CommandResult&&)>&& completionHandler)
1527 if (!m_toplevelBrowsingContext) {
1528 completionHandler(CommandResult::fail(CommandResult::ErrorCode::NoSuchWindow));
1532 RefPtr<InspectorObject> parameters = InspectorObject::create();
1533 parameters->setString(ASCIILiteral("browsingContextHandle"), m_toplevelBrowsingContext.value());
1534 parameters->setString(ASCIILiteral("userInput"), text);
1535 m_host->sendCommandToBackend(ASCIILiteral("setUserInputForCurrentJavaScriptPrompt"), WTFMove(parameters), [this, protectedThis = makeRef(*this), completionHandler = WTFMove(completionHandler)](SessionHost::CommandResponse&& response) {
1536 if (response.isError) {
1537 completionHandler(CommandResult::fail(WTFMove(response.responseObject)));
1540 completionHandler(CommandResult::success());
1544 } // namespace WebDriver