Skip to content
Permalink
Browse files
WebDriver: Add support for Get Computed Role and Get Computed Label c…
…ommands

https://bugs.webkit.org/show_bug.cgi?id=212549
rdar://63774327

Reviewed by Devin Rousso.

Add support for getting the accessibility role and label for elements.

This change progresses `get_computed_role` and `get_computed_label` subtests when combined with a safaridriver change to
enable these commands and test fixes being reviewed in web-platform-tests/wpt#35581.

* Source/WebKit/UIProcess/Automation/Automation.json:
* Source/WebKit/UIProcess/Automation/WebAutomationSession.cpp:
(WebKit::WebAutomationSession::getComputedRole):
(WebKit::WebAutomationSession::getComputedLabel):
* Source/WebKit/UIProcess/Automation/WebAutomationSession.h:
* Source/WebKit/WebProcess/Automation/WebAutomationSessionProxy.cpp:
(WebKit::WebAutomationSessionProxy::getAccessibilityObjectForNode):
(WebKit::WebAutomationSessionProxy::getComputedRole):
(WebKit::WebAutomationSessionProxy::getComputedLabel):
* Source/WebKit/WebProcess/Automation/WebAutomationSessionProxy.h:
* Source/WebKit/WebProcess/Automation/WebAutomationSessionProxy.messages.in:

Canonical link: https://commits.webkit.org/253732@main
  • Loading branch information
patrickangle committed Aug 24, 2022
1 parent d1256a0 commit dd64a1da3991691e2fa6873e15ff31b4506182f7
Show file tree
Hide file tree
Showing 6 changed files with 146 additions and 0 deletions.
@@ -642,6 +642,32 @@
],
"async": true
},
{
"name": "getComputedRole",
"description": "Get the computed WAI-ARIA role of the element.",
"parameters": [
{ "name": "browsingContextHandle", "$ref": "BrowsingContextHandle", "description": "The handle for the browsing context." },
{ "name": "frameHandle", "$ref": "FrameHandle", "description": "The handle for the frame that contains the element. The main frame is used if this parameter is empty string." },
{ "name": "nodeHandle", "$ref": "NodeHandle", "description": "The handle of the element to get the role of." }
],
"returns": [
{ "name": "role", "type": "string", "description": "The computed WAI-ARIA role of the element." }
],
"async": true
},
{
"name": "getComputedLabel",
"description": "Get the Accessible Name and Description Computation computed accessible name of the element.",
"parameters": [
{ "name": "browsingContextHandle", "$ref": "BrowsingContextHandle", "description": "The handle for the browsing context." },
{ "name": "frameHandle", "$ref": "FrameHandle", "description": "The handle for the frame that contains the element. The main frame is used if this parameter is empty string." },
{ "name": "nodeHandle", "$ref": "NodeHandle", "description": "The handle of the element to get the label of." }
],
"returns": [
{ "name": "label", "type": "string", "description": "The computed label for the element." }
],
"async": true
},
{
"name": "selectOptionElement",
"description": "Selects the given option element. In case of container with multiple options enabled, the element selectedness is toggled.",
@@ -1113,6 +1113,52 @@ void WebAutomationSession::computeElementLayout(const Inspector::Protocol::Autom
page->process().sendWithAsyncReply(Messages::WebAutomationSessionProxy::ComputeElementLayout(page->webPageID(), frameID, nodeHandle, scrollIntoViewIfNeeded, coordinateSystem.value()), WTFMove(completionHandler));
}

void WebAutomationSession::getComputedRole(const Inspector::Protocol::Automation::BrowsingContextHandle& browsingContextHandle, const Inspector::Protocol::Automation::FrameHandle& frameHandle, const Inspector::Protocol::Automation::NodeHandle& nodeHandle, Ref<GetComputedRoleCallback>&& callback)
{
WebPageProxy* page = webPageProxyForHandle(browsingContextHandle);
if (!page)
ASYNC_FAIL_WITH_PREDEFINED_ERROR(WindowNotFound);

bool frameNotFound = false;
auto frameID = webFrameIDForHandle(frameHandle, frameNotFound);
if (frameNotFound)
ASYNC_FAIL_WITH_PREDEFINED_ERROR(FrameNotFound);

WTF::CompletionHandler<void(std::optional<String>, std::optional<String>)> completionHandler = [callback](std::optional<String> errorType, std::optional<String> role) mutable {
if (errorType) {
callback->sendFailure(STRING_FOR_PREDEFINED_ERROR_MESSAGE(*errorType));
return;
}

callback->sendSuccess(*role);
};

page->process().sendWithAsyncReply(Messages::WebAutomationSessionProxy::GetComputedRole(page->webPageID(), frameID, nodeHandle), WTFMove(completionHandler));
}

void WebAutomationSession::getComputedLabel(const Inspector::Protocol::Automation::BrowsingContextHandle& browsingContextHandle, const Inspector::Protocol::Automation::FrameHandle& frameHandle, const Inspector::Protocol::Automation::NodeHandle& nodeHandle, Ref<GetComputedLabelCallback>&& callback)
{
WebPageProxy* page = webPageProxyForHandle(browsingContextHandle);
if (!page)
ASYNC_FAIL_WITH_PREDEFINED_ERROR(WindowNotFound);

bool frameNotFound = false;
auto frameID = webFrameIDForHandle(frameHandle, frameNotFound);
if (frameNotFound)
ASYNC_FAIL_WITH_PREDEFINED_ERROR(FrameNotFound);

WTF::CompletionHandler<void(std::optional<String>, std::optional<String>)> completionHandler = [callback](std::optional<String> errorType, std::optional<String> label) mutable {
if (errorType) {
callback->sendFailure(STRING_FOR_PREDEFINED_ERROR_MESSAGE(*errorType));
return;
}

callback->sendSuccess(*label);
};

page->process().sendWithAsyncReply(Messages::WebAutomationSessionProxy::GetComputedLabel(page->webPageID(), frameID, nodeHandle), WTFMove(completionHandler));
}

void WebAutomationSession::selectOptionElement(const Inspector::Protocol::Automation::BrowsingContextHandle& browsingContextHandle, const Inspector::Protocol::Automation::FrameHandle& frameHandle, const Inspector::Protocol::Automation::NodeHandle& nodeHandle, Ref<SelectOptionElementCallback>&& callback)
{
WebPageProxy* page = webPageProxyForHandle(browsingContextHandle);
@@ -189,6 +189,8 @@ class WebAutomationSession final : public API::ObjectImpl<API::Object::Type::Aut
void resolveChildFrameHandle(const Inspector::Protocol::Automation::BrowsingContextHandle&, const Inspector::Protocol::Automation::FrameHandle&, std::optional<int>&& ordinal, const String& name, const Inspector::Protocol::Automation::NodeHandle&, Ref<ResolveChildFrameHandleCallback>&&);
void resolveParentFrameHandle(const Inspector::Protocol::Automation::BrowsingContextHandle&, const Inspector::Protocol::Automation::FrameHandle&, Ref<ResolveParentFrameHandleCallback>&&);
void computeElementLayout(const Inspector::Protocol::Automation::BrowsingContextHandle&, const Inspector::Protocol::Automation::FrameHandle&, const Inspector::Protocol::Automation::NodeHandle&, std::optional<bool>&& scrollIntoViewIfNeeded, Inspector::Protocol::Automation::CoordinateSystem, Ref<ComputeElementLayoutCallback>&&);
void getComputedRole(const Inspector::Protocol::Automation::BrowsingContextHandle&, const Inspector::Protocol::Automation::FrameHandle&, const Inspector::Protocol::Automation::NodeHandle&, Ref<GetComputedRoleCallback>&&);
void getComputedLabel(const Inspector::Protocol::Automation::BrowsingContextHandle&, const Inspector::Protocol::Automation::FrameHandle&, const Inspector::Protocol::Automation::NodeHandle&, Ref<GetComputedLabelCallback>&&);
void selectOptionElement(const Inspector::Protocol::Automation::BrowsingContextHandle&, const Inspector::Protocol::Automation::FrameHandle&, const Inspector::Protocol::Automation::NodeHandle&, Ref<SelectOptionElementCallback>&&);
Inspector::Protocol::ErrorStringOr<bool> isShowingJavaScriptDialog(const Inspector::Protocol::Automation::BrowsingContextHandle&);
Inspector::Protocol::ErrorStringOr<void> dismissCurrentJavaScriptDialog(const Inspector::Protocol::Automation::BrowsingContextHandle&);
@@ -42,6 +42,8 @@
#include <JavaScriptCore/JSObject.h>
#include <JavaScriptCore/JSStringRefPrivate.h>
#include <JavaScriptCore/OpaqueJSString.h>
#include <WebCore/AXObjectCache.h>
#include <WebCore/AccessibilityObject.h>
#include <WebCore/CookieJar.h>
#include <WebCore/DOMRect.h>
#include <WebCore/DOMRectList.h>
@@ -303,6 +305,43 @@ WebCore::Element* WebAutomationSessionProxy::elementForNodeHandle(WebFrame& fram
return &elementWrapper->wrapped();
}

WebCore::AccessibilityObject* WebAutomationSessionProxy::getAccessibilityObjectForNode(WebCore::PageIdentifier pageID, std::optional<WebCore::FrameIdentifier> frameID, String nodeHandle, String& errorType)
{
WebPage* page = WebProcess::singleton().webPage(pageID);
if (!page) {
errorType = Inspector::Protocol::AutomationHelpers::getEnumConstantValue(Inspector::Protocol::Automation::ErrorMessage::WindowNotFound);
return nullptr;
}

auto* frame = frameID ? WebProcess::singleton().webFrame(*frameID) : &page->mainWebFrame();
if (!frame || !frame->coreFrame() || !frame->coreFrame()->view()) {
errorType = Inspector::Protocol::AutomationHelpers::getEnumConstantValue(Inspector::Protocol::Automation::ErrorMessage::FrameNotFound);
return nullptr;
}

if (!isValidNodeHandle(nodeHandle)) {
errorType = Inspector::Protocol::AutomationHelpers::getEnumConstantValue(Inspector::Protocol::Automation::ErrorMessage::InvalidNodeIdentifier);
return nullptr;
}

WebCore::Element* coreElement = elementForNodeHandle(*frame, nodeHandle);
if (!coreElement) {
errorType = Inspector::Protocol::AutomationHelpers::getEnumConstantValue(Inspector::Protocol::Automation::ErrorMessage::NodeNotFound);
return nullptr;
}

if (!WebCore::AXObjectCache::accessibilityEnabled())
WebCore::AXObjectCache::enableAccessibility();

if (WebCore::AXObjectCache* axObjectCache = coreElement->document().axObjectCache()) {
if (WebCore::AccessibilityObject* axObject = axObjectCache->getOrCreate(coreElement))
return axObject;
}

errorType = Inspector::Protocol::AutomationHelpers::getEnumConstantValue(Inspector::Protocol::Automation::ErrorMessage::InternalError);
return nullptr;
}

void WebAutomationSessionProxy::ensureObserverForFrame(WebFrame& frame)
{
// If the frame and DOMWindow have become disconnected, then frame is already being destroyed
@@ -729,6 +768,32 @@ void WebAutomationSessionProxy::computeElementLayout(WebCore::PageIdentifier pag
completionHandler(std::nullopt, resultElementBounds, resultInViewCenterPoint, isObscured);
}

void WebAutomationSessionProxy::getComputedRole(WebCore::PageIdentifier pageID, std::optional<WebCore::FrameIdentifier> frameID, String nodeHandle, CompletionHandler<void(std::optional<String>, std::optional<String>)>&& completionHandler)
{
String errorType;
auto* axObject = getAccessibilityObjectForNode(pageID, frameID, nodeHandle, errorType);

if (!errorType.isNull()) {
completionHandler(errorType, std::nullopt);
return;
}

completionHandler(std::nullopt, axObject->computedRoleString());
}

void WebAutomationSessionProxy::getComputedLabel(WebCore::PageIdentifier pageID, std::optional<WebCore::FrameIdentifier> frameID, String nodeHandle, CompletionHandler<void(std::optional<String>, std::optional<String>)>&& completionHandler)
{
String errorType;
auto* axObject = getAccessibilityObjectForNode(pageID, frameID, nodeHandle, errorType);

if (!errorType.isNull()) {
completionHandler(errorType, std::nullopt);
return;
}

completionHandler(std::nullopt, axObject->computedLabel());
}

void WebAutomationSessionProxy::selectOptionElement(WebCore::PageIdentifier pageID, std::optional<WebCore::FrameIdentifier> frameID, String nodeHandle, CompletionHandler<void(std::optional<String>)>&& completionHandler)
{
WebPage* page = WebProcess::singleton().webPage(pageID);
@@ -36,6 +36,7 @@

namespace WebCore {
struct Cookie;
class AccessibilityObject;
class Element;
}

@@ -63,6 +64,7 @@ class WebAutomationSessionProxy : public IPC::MessageReceiver {
void setScriptObject(JSGlobalContextRef, JSObjectRef);
JSObjectRef scriptObjectForFrame(WebFrame&);
WebCore::Element* elementForNodeHandle(WebFrame&, const String&);
WebCore::AccessibilityObject* getAccessibilityObjectForNode(WebCore::PageIdentifier, std::optional<WebCore::FrameIdentifier>, String nodeHandle, String& error);

void ensureObserverForFrame(WebFrame&);

@@ -76,6 +78,8 @@ class WebAutomationSessionProxy : public IPC::MessageReceiver {
void resolveChildFrameWithName(WebCore::PageIdentifier, std::optional<WebCore::FrameIdentifier>, const String& name, CompletionHandler<void(std::optional<String>, std::optional<WebCore::FrameIdentifier>)>&&);
void resolveParentFrame(WebCore::PageIdentifier, std::optional<WebCore::FrameIdentifier>, CompletionHandler<void(std::optional<String>, std::optional<WebCore::FrameIdentifier>)>&&);
void computeElementLayout(WebCore::PageIdentifier, std::optional<WebCore::FrameIdentifier>, String nodeHandle, bool scrollIntoViewIfNeeded, CoordinateSystem, CompletionHandler<void(std::optional<String>, WebCore::IntRect, std::optional<WebCore::IntPoint>, bool)>&&);
void getComputedRole(WebCore::PageIdentifier, std::optional<WebCore::FrameIdentifier>, String nodeHandle, CompletionHandler<void(std::optional<String>, std::optional<String>)>&&);
void getComputedLabel(WebCore::PageIdentifier, std::optional<WebCore::FrameIdentifier>, String nodeHandle, CompletionHandler<void(std::optional<String>, std::optional<String>)>&&);
void selectOptionElement(WebCore::PageIdentifier, std::optional<WebCore::FrameIdentifier>, String nodeHandle, CompletionHandler<void(std::optional<String>)>&&);
void setFilesForInputFileUpload(WebCore::PageIdentifier, std::optional<WebCore::FrameIdentifier>, String nodeHandle, Vector<String>&& filenames, CompletionHandler<void(std::optional<String>)>&&);
void takeScreenshot(WebCore::PageIdentifier, std::optional<WebCore::FrameIdentifier>, String nodeHandle, bool scrollIntoViewIfNeeded, bool clipToViewport, uint64_t callbackID);
@@ -30,6 +30,9 @@ messages -> WebAutomationSessionProxy NotRefCounted {

ComputeElementLayout(WebCore::PageIdentifier pageID, std::optional<WebCore::FrameIdentifier> frameID, String nodeHandle, bool scrollIntoViewIfNeeded, enum:uint8_t WebKit::CoordinateSystem coordinateSystem) -> (std::optional<String> errorType, WebCore::IntRect rect, std::optional<WebCore::IntPoint> inViewCenterPoint, bool isObscured)

GetComputedRole(WebCore::PageIdentifier pageID, std::optional<WebCore::FrameIdentifier> frameID, String nodeHandle) -> (std::optional<String> role, std::optional<String> errorType)
GetComputedLabel(WebCore::PageIdentifier pageID, std::optional<WebCore::FrameIdentifier> frameID, String nodeHandle) -> (std::optional<String> role, std::optional<String> errorType)

SelectOptionElement(WebCore::PageIdentifier pageID, std::optional<WebCore::FrameIdentifier> frameID, String nodeHandle) -> (std::optional<String> errorType)

SetFilesForInputFileUpload(WebCore::PageIdentifier pageID, std::optional<WebCore::FrameIdentifier> frameID, String nodeHandle, Vector<String> filenames) -> (std::optional<String> errorType)

0 comments on commit dd64a1d

Please sign in to comment.