|
47 | 47 | #include <JavaScriptCore/JSStringRefPrivate.h> |
48 | 48 | #include <JavaScriptCore/OpaqueJSString.h> |
49 | 49 | #include <JavaScriptCore/SourceTaintedOrigin.h> |
| 50 | +#include <WebCore/AXID.h> |
50 | 51 | #include <WebCore/AXObjectCache.h> |
51 | 52 | #include <WebCore/AccessibilityObject.h> |
52 | 53 | #include <WebCore/ContainerNodeInlines.h> |
|
76 | 77 | #include <WebCore/RenderElement.h> |
77 | 78 | #include <WebCore/ScriptController.h> |
78 | 79 | #include <wtf/StdLibExtras.h> |
| 80 | +#include <wtf/text/StringToIntegerConversion.h> |
79 | 81 | #include <wtf/TZoneMallocInlines.h> |
80 | 82 | #include <wtf/UUID.h> |
81 | 83 |
|
@@ -383,6 +385,42 @@ WebCore::AccessibilityObject* WebAutomationSessionProxy::getAccessibilityObjectF |
383 | 385 | return nullptr; |
384 | 386 | } |
385 | 387 |
|
| 388 | +Expected<WebCore::AccessibilityObject*, Inspector::ErrorString> WebAutomationSessionProxy::getAccessibilityObjectForAXNode(WebCore::PageIdentifier pageID, String accessibilityNodeHandle, String& errorType) |
| 389 | +{ |
| 390 | + RefPtr page = WebProcess::singleton().webPage(pageID); |
| 391 | + RefPtr frame = page ? &page->mainWebFrame() : nullptr; |
| 392 | + RefPtr localFrame = frame ? frame->coreLocalFrame() : nullptr; |
| 393 | + RefPtr document = localFrame ? localFrame->document() : nullptr; |
| 394 | + RefPtr frameView = localFrame ? localFrame->view() : nullptr; |
| 395 | + |
| 396 | + if (!localFrame || !localFrame->view() || !document) { |
| 397 | + errorType = Inspector::Protocol::AutomationHelpers::getEnumConstantValue(Inspector::Protocol::Automation::ErrorMessage::WindowNotFound); |
| 398 | + return nullptr; |
| 399 | + } |
| 400 | + |
| 401 | + WebCore::AXObjectCache::enableAccessibility(); |
| 402 | + |
| 403 | + if (CheckedPtr axObjectCache = frame->coreLocalFrame()->document()->axObjectCache()) { |
| 404 | + // Force a layout and cache update. If we don't, and this request has come in before the render tree was built, |
| 405 | + // the accessibility object for this element will not be created (because it doesn't yet have its renderer). |
| 406 | + axObjectCache->performDeferredCacheUpdate(ForceLayout::Yes); |
| 407 | + |
| 408 | + auto parsedAXID = parseInteger<uint64_t>(accessibilityNodeHandle); |
| 409 | + if (!parsedAXID) { |
| 410 | + errorType = Inspector::Protocol::AutomationHelpers::getEnumConstantValue( |
| 411 | + Inspector::Protocol::Automation::ErrorMessage::NodeNotFound); |
| 412 | + return nullptr; |
| 413 | + } |
| 414 | + |
| 415 | + auto axID = WebCore::AXID { *parsedAXID }; |
| 416 | + if (auto* axObject = axObjectCache->objectForID(axID)) |
| 417 | + return axObject; |
| 418 | + } |
| 419 | + |
| 420 | + errorType = Inspector::Protocol::AutomationHelpers::getEnumConstantValue(Inspector::Protocol::Automation::ErrorMessage::InternalError); |
| 421 | + return nullptr; |
| 422 | +} |
| 423 | + |
386 | 424 | void WebAutomationSessionProxy::ensureObserverForFrame(WebFrame& frame) |
387 | 425 | { |
388 | 426 | // If the frame and LocalDOMWindow have become disconnected, then frame is already being destroyed |
@@ -912,6 +950,59 @@ void WebAutomationSessionProxy::getComputedLabel(WebCore::PageIdentifier pageID, |
912 | 950 | completionHandler(std::nullopt, axObject->computedLabel()); |
913 | 951 | } |
914 | 952 |
|
| 953 | +static WebAutomationSessionProxy::ComputedAXProperties getComputedAccessibilityProperties(WebCore::AccessibilityObject& axObject) |
| 954 | +{ |
| 955 | + String accessibilityNodeId = String::number(axObject.objectID().toUInt64()); |
| 956 | + String role = axObject.computedRoleString(); |
| 957 | + String label = axObject.computedLabel(); |
| 958 | + |
| 959 | + String isChecked; |
| 960 | + if (axObject.supportsCheckedState()) |
| 961 | + isChecked = axObject.isChecked() ? "true"_s : "false"_s; |
| 962 | + |
| 963 | + String isPressed; |
| 964 | + if (axObject.isToggleButton()) |
| 965 | + isPressed = axObject.isPressed() ? "true"_s : "false"_s; |
| 966 | + |
| 967 | + String parentAXID; |
| 968 | + if (auto* parent = axObject.parentObject()) |
| 969 | + parentAXID = String::number(parent->objectID().toUInt64()); |
| 970 | + |
| 971 | + Vector<String> childrenAXIDs; |
| 972 | + for (auto& child : axObject.stitchedUnignoredChildren()) |
| 973 | + childrenAXIDs.append(String::number(child->objectID().toUInt64())); |
| 974 | + |
| 975 | + return { accessibilityNodeId, role, label, isChecked, isPressed, parentAXID, WTF::move(childrenAXIDs) }; |
| 976 | +} |
| 977 | + |
| 978 | +void WebAutomationSessionProxy::getAccessibilityPropertiesForElement(WebCore::PageIdentifier pageID, std::optional<WebCore::FrameIdentifier> frameID, String nodeHandle, CompletionHandler<void(std::optional<String>&&, std::optional<String>&&, std::optional<String>&&, std::optional<String>&&, std::optional<String>&&, std::optional<String>&&, std::optional<String>&&, Vector<String>&&)>&& completionHandler) |
| 979 | +{ |
| 980 | + String errorType; |
| 981 | + RefPtr axObject = getAccessibilityObjectForNode(pageID, frameID, nodeHandle, errorType); |
| 982 | + |
| 983 | + if (!errorType.isNull()) { |
| 984 | + completionHandler(errorType, std::nullopt, std::nullopt, std::nullopt, std::nullopt, std::nullopt, std::nullopt, Vector<String> { }); |
| 985 | + return; |
| 986 | + } |
| 987 | + |
| 988 | + auto properties = getComputedAccessibilityProperties(*axObject); |
| 989 | + completionHandler(std::nullopt, WTF::move(properties.accessibilityNodeId), WTF::move(properties.role), WTF::move(properties.label), WTF::move(properties.checked), WTF::move(properties.pressed), WTF::move(properties.parent), WTF::move(properties.children)); |
| 990 | +} |
| 991 | + |
| 992 | +void WebAutomationSessionProxy::getAccessibilityPropertiesForAccessibilityNode(WebCore::PageIdentifier pageID, String accessibilityNodeHandle, CompletionHandler<void(std::optional<String>&&, std::optional<String>&&, std::optional<String>&&, std::optional<String>&&, std::optional<String>&&, std::optional<String>&&, std::optional<String>&&, Vector<String>&&)>&& completionHandler) |
| 993 | +{ |
| 994 | + String errorType; |
| 995 | + auto axObjectForNode = getAccessibilityObjectForAXNode(pageID, accessibilityNodeHandle, errorType); |
| 996 | + if (!axObjectForNode) { |
| 997 | + completionHandler(axObjectForNode.error(), std::nullopt, std::nullopt, std::nullopt, std::nullopt, std::nullopt, std::nullopt, Vector<String> { }); |
| 998 | + return; |
| 999 | + } |
| 1000 | + |
| 1001 | + RefPtr axObject = axObjectForNode.value(); |
| 1002 | + auto properties = getComputedAccessibilityProperties(*axObject); |
| 1003 | + completionHandler(std::nullopt, WTF::move(properties.accessibilityNodeId), WTF::move(properties.role), WTF::move(properties.label), WTF::move(properties.checked), WTF::move(properties.pressed), WTF::move(properties.parent), WTF::move(properties.children)); |
| 1004 | +} |
| 1005 | + |
915 | 1006 | void WebAutomationSessionProxy::selectOptionElement(WebCore::PageIdentifier pageID, std::optional<WebCore::FrameIdentifier> frameID, String nodeHandle, CompletionHandler<void(std::optional<String>)>&& completionHandler) |
916 | 1007 | { |
917 | 1008 | RefPtr page = WebProcess::singleton().webPage(pageID); |
|
0 commit comments