blob: b799bd54a5a66595e9877c56bb56491fbe4463aa [file] [log] [blame] [edit]
/*
* Copyright (C) 2026 Apple Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "config.h"
#include "AccessibilityUIElementClientMac.h"
#if PLATFORM(MAC)
#import "DictionaryFunctions.h"
#import "InjectedBundle.h"
#import <JavaScriptCore/JSRetainPtr.h>
#import <JavaScriptCore/JSStringRef.h>
#import <JavaScriptCore/OpaqueJSString.h>
#import <WebKit/WKBundle.h>
#import <WebKit/WKBundlePrivate.h>
#import <wtf/RetainPtr.h>
namespace WTR {
// IPC helper functions for client accessibility
static uint64_t axGetRoot()
{
ALLOW_DEPRECATED_DECLARATIONS_BEGIN
WKTypeRef returnData = nullptr;
WKBundlePostSynchronousMessage(InjectedBundle::singleton().bundle(), toWK("AXGetRoot").get(), nullptr, &returnData);
ALLOW_DEPRECATED_DECLARATIONS_END
if (!returnData || WKGetTypeID(returnData) != WKUInt64GetTypeID())
return 0;
uint64_t token = WKUInt64GetValue(static_cast<WKUInt64Ref>(returnData));
WKRelease(returnData);
return token;
}
static WKRetainPtr<WKStringRef> axCopyAttributeValueAsString(uint64_t elementToken, const char* attributeName)
{
WKRetainPtr dictionary = adoptWK(WKMutableDictionaryCreate());
setValue(dictionary, "elementToken", elementToken);
setValue(dictionary, "attributeName", attributeName);
ALLOW_DEPRECATED_DECLARATIONS_BEGIN
WKTypeRef returnData = nullptr;
WKBundlePostSynchronousMessage(InjectedBundle::singleton().bundle(), toWK("AXCopyAttributeValueAsString").get(), dictionary.get(), &returnData);
ALLOW_DEPRECATED_DECLARATIONS_END
if (!returnData || WKGetTypeID(returnData) != WKStringGetTypeID())
return nullptr;
return adoptWK(static_cast<WKStringRef>(returnData));
}
static WKRetainPtr<WKArrayRef> axCopyAttributeValueAsElementArray(uint64_t elementToken, const char* attributeName)
{
WKRetainPtr dictionary = adoptWK(WKMutableDictionaryCreate());
setValue(dictionary, "elementToken", elementToken);
setValue(dictionary, "attributeName", attributeName);
ALLOW_DEPRECATED_DECLARATIONS_BEGIN
WKTypeRef returnData = nullptr;
WKBundlePostSynchronousMessage(InjectedBundle::singleton().bundle(), toWK("AXCopyAttributeValueAsElementArray").get(), dictionary.get(), &returnData);
ALLOW_DEPRECATED_DECLARATIONS_END
if (!returnData || WKGetTypeID(returnData) != WKArrayGetTypeID())
return nullptr;
return adoptWK(static_cast<WKArrayRef>(returnData));
}
static std::optional<double> axCopyAttributeValueAsNumber(uint64_t elementToken, const char* attributeName)
{
WKRetainPtr dictionary = adoptWK(WKMutableDictionaryCreate());
setValue(dictionary, "elementToken", elementToken);
setValue(dictionary, "attributeName", attributeName);
ALLOW_DEPRECATED_DECLARATIONS_BEGIN
WKTypeRef returnData = nullptr;
WKBundlePostSynchronousMessage(InjectedBundle::singleton().bundle(), toWK("AXCopyAttributeValueAsNumber").get(), dictionary.get(), &returnData);
ALLOW_DEPRECATED_DECLARATIONS_END
if (!returnData || WKGetTypeID(returnData) != WKDoubleGetTypeID())
return std::nullopt;
double value = WKDoubleGetValue(static_cast<WKDoubleRef>(returnData));
WKRelease(returnData);
return value;
}
static std::pair<double, double> axCopyAttributeValueAsPoint(uint64_t elementToken, const char* attributeName)
{
WKRetainPtr dictionary = adoptWK(WKMutableDictionaryCreate());
setValue(dictionary, "elementToken", elementToken);
setValue(dictionary, "attributeName", attributeName);
ALLOW_DEPRECATED_DECLARATIONS_BEGIN
WKTypeRef returnData = nullptr;
WKBundlePostSynchronousMessage(InjectedBundle::singleton().bundle(), toWK("AXCopyAttributeValueAsPoint").get(), dictionary.get(), &returnData);
ALLOW_DEPRECATED_DECLARATIONS_END
if (!returnData || WKGetTypeID(returnData) != WKDictionaryGetTypeID())
return { 0, 0 };
auto resultDict = static_cast<WKDictionaryRef>(returnData);
double x = doubleValue(resultDict, "x");
double y = doubleValue(resultDict, "y");
WKRelease(returnData);
return { x, y };
}
static std::pair<double, double> axCopyAttributeValueAsSize(uint64_t elementToken, const char* attributeName)
{
WKRetainPtr dictionary = adoptWK(WKMutableDictionaryCreate());
setValue(dictionary, "elementToken", elementToken);
setValue(dictionary, "attributeName", attributeName);
ALLOW_DEPRECATED_DECLARATIONS_BEGIN
WKTypeRef returnData = nullptr;
WKBundlePostSynchronousMessage(InjectedBundle::singleton().bundle(), toWK("AXCopyAttributeValueAsSize").get(), dictionary.get(), &returnData);
ALLOW_DEPRECATED_DECLARATIONS_END
if (!returnData || WKGetTypeID(returnData) != WKDictionaryGetTypeID())
return { 0, 0 };
auto resultDict = static_cast<WKDictionaryRef>(returnData);
double width = doubleValue(resultDict, "width");
double height = doubleValue(resultDict, "height");
WKRelease(returnData);
return { width, height };
}
Ref<AccessibilityUIElementClientMac> AccessibilityUIElementClientMac::create(uint64_t elementToken)
{
return adoptRef(*new AccessibilityUIElementClientMac(elementToken));
}
Ref<AccessibilityUIElementClientMac> AccessibilityUIElementClientMac::create(const AccessibilityUIElementClientMac& other)
{
return adoptRef(*new AccessibilityUIElementClientMac(other));
}
Ref<AccessibilityUIElementClientMac> AccessibilityUIElementClientMac::createForUIProcess()
{
return create(axGetRoot());
}
AccessibilityUIElementClientMac::AccessibilityUIElementClientMac(uint64_t elementToken)
: AccessibilityUIElement(nullptr)
, m_elementToken(elementToken)
{
}
AccessibilityUIElementClientMac::AccessibilityUIElementClientMac(const AccessibilityUIElementClientMac& other)
: AccessibilityUIElement(other)
, m_elementToken(other.m_elementToken)
{
}
AccessibilityUIElementClientMac::~AccessibilityUIElementClientMac()
{
}
PlatformUIElement AccessibilityUIElementClientMac::platformUIElement()
{
// Client elements don't have a local platform element
return nullptr;
}
bool AccessibilityUIElementClientMac::isValid() const
{
return m_elementToken;
}
JSRetainPtr<JSStringRef> AccessibilityUIElementClientMac::getStringAttribute(const char* attributeName) const
{
if (!isValid())
return nullptr;
WKRetainPtr value = axCopyAttributeValueAsString(m_elementToken, attributeName);
if (!value)
return nullptr;
return JSRetainPtr<JSStringRef>(Adopt, OpaqueJSString::tryCreate(toWTFString(value.get())).leakRef());
}
JSRetainPtr<JSStringRef> AccessibilityUIElementClientMac::role()
{
if (!isValid())
return nullptr;
WKRetainPtr value = axCopyAttributeValueAsString(m_elementToken, "AXRole");
if (!value)
return nullptr;
String roleString = toWTFString(value.get());
String result = makeString("AXRole: "_s, roleString);
return JSRetainPtr<JSStringRef>(Adopt, OpaqueJSString::tryCreate(result).leakRef());
}
JSRetainPtr<JSStringRef> AccessibilityUIElementClientMac::title()
{
return getStringAttribute("AXTitle");
}
JSRetainPtr<JSStringRef> AccessibilityUIElementClientMac::description()
{
return getStringAttribute("AXDescription");
}
JSRetainPtr<JSStringRef> AccessibilityUIElementClientMac::stringValue()
{
return getStringAttribute("AXValue");
}
double AccessibilityUIElementClientMac::getNumberAttribute(const char* attributeName) const
{
if (!isValid())
return 0;
return axCopyAttributeValueAsNumber(m_elementToken, attributeName).value_or(0);
}
int AccessibilityUIElementClientMac::hierarchicalLevel() const
{
return static_cast<int>(getNumberAttribute("AXDisclosureLevel"));
}
double AccessibilityUIElementClientMac::minValue()
{
return getNumberAttribute("AXMinValue");
}
double AccessibilityUIElementClientMac::maxValue()
{
return getNumberAttribute("AXMaxValue");
}
double AccessibilityUIElementClientMac::x()
{
if (!isValid())
return 0;
auto [x, y] = axCopyAttributeValueAsPoint(m_elementToken, "AXPosition");
return x;
}
double AccessibilityUIElementClientMac::y()
{
if (!isValid())
return 0;
auto [x, y] = axCopyAttributeValueAsPoint(m_elementToken, "AXPosition");
return y;
}
double AccessibilityUIElementClientMac::width()
{
if (!isValid())
return 0;
auto [width, height] = axCopyAttributeValueAsSize(m_elementToken, "AXSize");
return width;
}
double AccessibilityUIElementClientMac::height()
{
if (!isValid())
return 0;
auto [width, height] = axCopyAttributeValueAsSize(m_elementToken, "AXSize");
return height;
}
Vector<RefPtr<AccessibilityUIElement>> AccessibilityUIElementClientMac::getChildren() const
{
Vector<RefPtr<AccessibilityUIElement>> children;
if (!isValid())
return children;
WKRetainPtr value = axCopyAttributeValueAsElementArray(m_elementToken, "AXChildren");
if (!value)
return children;
size_t count = WKArrayGetSize(value.get());
for (size_t i = 0; i < count; i++) {
WKTypeRef item = WKArrayGetItemAtIndex(value.get(), i);
if (WKGetTypeID(item) == WKUInt64GetTypeID()) {
uint64_t childToken = WKUInt64GetValue(static_cast<WKUInt64Ref>(item));
children.append(AccessibilityUIElementClientMac::create(childToken));
}
}
return children;
}
Vector<RefPtr<AccessibilityUIElement>> AccessibilityUIElementClientMac::getChildrenInRange(unsigned location, unsigned length) const
{
Vector allChildren = getChildren();
if (location >= allChildren.size())
return { };
unsigned end = std::min(location + length, static_cast<unsigned>(allChildren.size()));
Vector<RefPtr<AccessibilityUIElement>> result;
result.reserveInitialCapacity(end - location);
for (unsigned i = location; i < end; i++)
result.append(allChildren[i]);
return result;
}
unsigned AccessibilityUIElementClientMac::childrenCount()
{
return getChildren().size();
}
RefPtr<AccessibilityUIElement> AccessibilityUIElementClientMac::childAtIndex(unsigned index)
{
Vector children = getChildrenInRange(index, 1);
return children.size() == 1 ? children[0] : nullptr;
}
} // namespace WTR
#endif // PLATFORM(MAC)