| /* |
| * Copyright (C) 2021 Igalia S.L. |
| * |
| * 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 "AccessibilityUIElement.h" |
| |
| #if HAVE(ACCESSIBILITY) && USE(ATSPI) |
| #include "InjectedBundle.h" |
| #include "InjectedBundlePage.h" |
| #include <JavaScriptCore/JSStringRef.h> |
| #include <JavaScriptCore/OpaqueJSString.h> |
| #include <WebCore/AccessibilityAtspiEnums.h> |
| #include <WebCore/AccessibilityObjectAtspi.h> |
| #include <WebKit/WKBundleFrame.h> |
| #include <wtf/URL.h> |
| #include <wtf/text/CString.h> |
| #include <wtf/text/StringBuilder.h> |
| #include <wtf/text/StringToIntegerConversion.h> |
| #include <wtf/unicode/CharacterNames.h> |
| |
| namespace WTR { |
| |
| RefPtr<AccessibilityController> AccessibilityUIElement::s_controller; |
| |
| AccessibilityUIElement::AccessibilityUIElement(PlatformUIElement element) |
| : m_element(element) |
| { |
| if (!s_controller) |
| s_controller = InjectedBundle::singleton().accessibilityController(); |
| } |
| |
| AccessibilityUIElement::AccessibilityUIElement(const AccessibilityUIElement& other) |
| : JSWrappable() |
| , m_element(other.m_element) |
| { |
| } |
| |
| AccessibilityUIElement::~AccessibilityUIElement() |
| { |
| } |
| |
| bool AccessibilityUIElement::isEqual(AccessibilityUIElement* otherElement) |
| { |
| return otherElement && m_element.get() == otherElement->platformUIElement(); |
| } |
| |
| bool AccessibilityUIElement::isIsolatedObject() const |
| { |
| return true; |
| } |
| |
| void AccessibilityUIElement::getChildren(Vector<RefPtr<AccessibilityUIElement> >& children) |
| { |
| } |
| |
| void AccessibilityUIElement::getChildrenWithRange(Vector<RefPtr<AccessibilityUIElement> >& children, unsigned location, unsigned length) |
| { |
| } |
| |
| int AccessibilityUIElement::childrenCount() |
| { |
| unsigned childCount; |
| s_controller->executeOnAXThreadAndWait([this, &childCount] { |
| m_element->updateBackingStore(); |
| childCount = m_element->childCount(); |
| }); |
| return childCount; |
| } |
| |
| RefPtr<AccessibilityUIElement> AccessibilityUIElement::elementAtPoint(int x, int y) |
| { |
| auto* element = m_element->hitTest({ x, y }, WebCore::Atspi::CoordinateType::WindowCoordinates); |
| return AccessibilityUIElement::create(element ? element : m_element.get()); |
| } |
| |
| unsigned AccessibilityUIElement::indexOfChild(AccessibilityUIElement* element) |
| { |
| return 0; |
| } |
| |
| RefPtr<AccessibilityUIElement> AccessibilityUIElement::childAtIndex(unsigned index) |
| { |
| m_element->updateBackingStore(); |
| |
| RefPtr<WebCore::AccessibilityObjectAtspi> child; |
| s_controller->executeOnAXThreadAndWait([this, &child, index] { |
| m_element->updateBackingStore(); |
| child = m_element->childAt(index); |
| }); |
| if (!child) |
| return nullptr; |
| |
| return AccessibilityUIElement::create(child.get()); |
| } |
| |
| static RefPtr<AccessibilityUIElement> elementForRelationAtIndex(WebCore::AccessibilityObjectAtspi* element, WebCore::Atspi::Relation relation, unsigned index) |
| { |
| auto relationMap = element->relationMap(); |
| auto targets = relationMap.get(relation); |
| if (targets.isEmpty() || index >= targets.size()) |
| return nullptr; |
| |
| auto target = targets[index]; |
| if (!target) |
| return nullptr; |
| |
| return AccessibilityUIElement::create(target.get()); |
| } |
| |
| RefPtr<AccessibilityUIElement> AccessibilityUIElement::linkedUIElementAtIndex(unsigned index) |
| { |
| return nullptr; |
| } |
| |
| RefPtr<AccessibilityUIElement> AccessibilityUIElement::ariaOwnsElementAtIndex(unsigned index) |
| { |
| return elementForRelationAtIndex(m_element.get(), WebCore::Atspi::Relation::NodeParentOf, index); |
| } |
| |
| RefPtr<AccessibilityUIElement> AccessibilityUIElement::ariaFlowToElementAtIndex(unsigned index) |
| { |
| return elementForRelationAtIndex(m_element.get(), WebCore::Atspi::Relation::FlowsTo, index); |
| } |
| |
| RefPtr<AccessibilityUIElement> AccessibilityUIElement::ariaControlsElementAtIndex(unsigned index) |
| { |
| return elementForRelationAtIndex(m_element.get(), WebCore::Atspi::Relation::ControllerFor, index); |
| } |
| |
| RefPtr<AccessibilityUIElement> AccessibilityUIElement::disclosedRowAtIndex(unsigned index) |
| { |
| return nullptr; |
| } |
| |
| RefPtr<AccessibilityUIElement> AccessibilityUIElement::rowAtIndex(unsigned index) |
| { |
| return nullptr; |
| } |
| |
| RefPtr<AccessibilityUIElement> AccessibilityUIElement::selectedChildAtIndex(unsigned index) const |
| { |
| return nullptr; |
| } |
| |
| unsigned AccessibilityUIElement::selectedChildrenCount() const |
| { |
| return 0; |
| } |
| |
| RefPtr<AccessibilityUIElement> AccessibilityUIElement::selectedRowAtIndex(unsigned index) |
| { |
| return nullptr; |
| } |
| |
| RefPtr<AccessibilityUIElement> AccessibilityUIElement::titleUIElement() |
| { |
| return elementForRelationAtIndex(m_element.get(), WebCore::Atspi::Relation::LabelledBy, 0); |
| } |
| |
| RefPtr<AccessibilityUIElement> AccessibilityUIElement::parentElement() |
| { |
| m_element->updateBackingStore(); |
| |
| RefPtr<WebCore::AccessibilityObjectAtspi> parent; |
| s_controller->executeOnAXThreadAndWait([this, &parent] { |
| m_element->updateBackingStore(); |
| auto parentWrapper = m_element->parent(); |
| if (parentWrapper) |
| parent = parentWrapper.value(); |
| }); |
| if (!parent) |
| return nullptr; |
| |
| return AccessibilityUIElement::create(parent.get()); |
| } |
| |
| RefPtr<AccessibilityUIElement> AccessibilityUIElement::disclosedByRow() |
| { |
| return nullptr; |
| } |
| |
| JSRetainPtr<JSStringRef> AccessibilityUIElement::attributesOfLinkedUIElements() |
| { |
| return JSStringCreateWithCharacters(0, 0); |
| } |
| |
| JSRetainPtr<JSStringRef> AccessibilityUIElement::attributesOfDocumentLinks() |
| { |
| return JSStringCreateWithCharacters(0, 0); |
| } |
| |
| static String attributesOfElement(AccessibilityUIElement& element) |
| { |
| StringBuilder builder; |
| |
| builder.append(element.role()->string(), '\n'); |
| |
| builder.append("AXParent: "); |
| if (auto parent = element.parentElement()) { |
| builder.append(parent->role()->string().substring(8)); |
| auto parentName = parent->title()->string().substring(9); |
| if (!parentName.isEmpty()) |
| builder.append(": ", parentName); |
| } else |
| builder.append("(null)"); |
| builder.append('\n'); |
| |
| builder.append("AXChildren: ", element.childrenCount(), '\n'); |
| |
| builder.append("AXPosition: { ", FormattedNumber::fixedPrecision(element.x(), 6, KeepTrailingZeros)); |
| builder.append(", ", FormattedNumber::fixedPrecision(element.y(), 6, KeepTrailingZeros)); |
| builder.append(" }\n"); |
| |
| builder.append("AXSize: { ", FormattedNumber::fixedPrecision(element.width(), 6, KeepTrailingZeros)); |
| builder.append(", ", FormattedNumber::fixedPrecision(element.height(), 6, KeepTrailingZeros)); |
| builder.append(" }\n"); |
| |
| String title = element.title()->string(); |
| if (!title.isEmpty()) { |
| builder.append(title); |
| builder.append('\n'); |
| } |
| |
| String description = element.description()->string(); |
| if (!description.isEmpty()) |
| builder.append(description.utf8().data(), '\n'); |
| |
| String value = element.stringValue()->string(); |
| if (!value.isEmpty()) |
| builder.append(value, '\n'); |
| |
| builder.append("AXFocusable: ", element.isFocusable(), '\n'); |
| builder.append("AXFocused: ", element.isFocused(), '\n'); |
| builder.append("AXSelectable: ", element.isSelectable(), '\n'); |
| builder.append("AXSelected: ", element.isSelected(), '\n'); |
| builder.append("AXMultiSelectable: ", element.isMultiSelectable(), '\n'); |
| builder.append("AXEnabled: ", element.isEnabled(), '\n'); |
| builder.append("AXExpanded: ", element.isExpanded(), '\n'); |
| builder.append("AXRequired: ", element.isRequired(), '\n'); |
| builder.append("AXChecked: ", element.isChecked(), '\n'); |
| |
| String url = element.url()->string(); |
| if (!url.isEmpty()) |
| builder.append(url, '\n'); |
| |
| // We append the platform attributes as a single line at the end. |
| builder.append("AXPlatformAttributes: "); |
| auto attributes = element.platformUIElement()->attributes(); |
| bool isFirst = true; |
| for (const auto& it : attributes) { |
| if (!isFirst) |
| builder.append(", "); |
| isFirst = false; |
| builder.append(it.key, ':', it.value); |
| } |
| builder.append('\n'); |
| |
| return builder.toString(); |
| } |
| |
| static String attributesOfElements(Vector<RefPtr<AccessibilityUIElement>>& elements) |
| { |
| StringBuilder builder; |
| for (auto& element : elements) |
| builder.append(attributesOfElement(*element), "\n------------\n"); |
| return builder.toString(); |
| } |
| |
| JSRetainPtr<JSStringRef> AccessibilityUIElement::attributesOfChildren() |
| { |
| m_element->updateBackingStore(); |
| |
| Vector<RefPtr<WebCore::AccessibilityObjectAtspi>> children; |
| s_controller->executeOnAXThreadAndWait([this, &children] { |
| m_element->updateBackingStore(); |
| children = m_element->children(); |
| }); |
| |
| Vector<RefPtr<AccessibilityUIElement>> elements; |
| elements.reserveInitialCapacity(children.size()); |
| for (auto& child : children) |
| elements.uncheckedAppend(AccessibilityUIElement::create(child.get())); |
| |
| return OpaqueJSString::tryCreate(attributesOfElements(elements)).leakRef(); |
| } |
| |
| JSRetainPtr<JSStringRef> AccessibilityUIElement::allAttributes() |
| { |
| return OpaqueJSString::tryCreate(attributesOfElement(*this)).leakRef(); |
| } |
| |
| JSRetainPtr<JSStringRef> AccessibilityUIElement::stringDescriptionOfAttributeValue(JSStringRef attribute) |
| { |
| return JSStringCreateWithCharacters(0, 0); |
| } |
| |
| JSRetainPtr<JSStringRef> AccessibilityUIElement::stringAttributeValue(JSStringRef attribute) |
| { |
| String attributeName = toWTFString(attribute); |
| if (attributeName == "AXSelectedText") { |
| if (!m_element->interfaces().contains(WebCore::AccessibilityObjectAtspi::Interface::Text)) |
| return JSStringCreateWithCharacters(0, 0); |
| |
| m_element->updateBackingStore(); |
| auto text = m_element->text(); |
| auto offset = m_element->selectedRange(); |
| return OpaqueJSString::tryCreate(text.substring(offset.x(), offset.y() - offset.x())).leakRef(); |
| } |
| |
| auto attributes = m_element->attributes(); |
| if (attributeName == "AXPlaceholderValue") |
| return OpaqueJSString::tryCreate(attributes.get("placeholder-text")).leakRef(); |
| |
| return JSStringCreateWithCharacters(0, 0); |
| } |
| |
| double AccessibilityUIElement::numberAttributeValue(JSStringRef attribute) |
| { |
| String attributeName = toWTFString(attribute); |
| auto attributes = m_element->attributes(); |
| if (attributeName == "AXARIASetSize") |
| return attributes.get("setsize").toDouble(); |
| if (attributeName == "AXARIAPosInSet") |
| return attributes.get("posinset").toDouble(); |
| |
| return 0; |
| } |
| |
| JSValueRef AccessibilityUIElement::uiElementArrayAttributeValue(JSStringRef attribute) const |
| { |
| return nullptr; |
| } |
| |
| JSValueRef AccessibilityUIElement::rowHeaders() const |
| { |
| return nullptr; |
| } |
| |
| JSValueRef AccessibilityUIElement::columnHeaders() const |
| { |
| return nullptr; |
| } |
| |
| RefPtr<AccessibilityUIElement> AccessibilityUIElement::uiElementAttributeValue(JSStringRef attribute) const |
| { |
| return nullptr; |
| } |
| |
| static bool checkElementState(WebCore::AccessibilityObjectAtspi* element, WebCore::Atspi::State state) |
| { |
| return element->state() & (G_GUINT64_CONSTANT(1) << state); |
| } |
| |
| bool AccessibilityUIElement::boolAttributeValue(JSStringRef attribute) |
| { |
| String attributeName = toWTFString(attribute); |
| if (attributeName == "AXElementBusy") |
| return checkElementState(m_element.get(), WebCore::Atspi::State::Busy); |
| |
| return false; |
| } |
| |
| bool AccessibilityUIElement::isAttributeSettable(JSStringRef attribute) |
| { |
| String attributeName = toWTFString(attribute); |
| if (attributeName != "AXValue") |
| return false; |
| |
| if (checkElementState(m_element.get(), WebCore::Atspi::State::ReadOnly)) |
| return false; |
| |
| if (checkElementState(m_element.get(), WebCore::Atspi::State::Editable)) |
| return true; |
| |
| if (checkElementState(m_element.get(), WebCore::Atspi::State::Checkable)) |
| return true; |
| |
| if (m_element->interfaces().contains(WebCore::AccessibilityObjectAtspi::Interface::Value) |
| && checkElementState(m_element.get(), WebCore::Atspi::State::Focusable)) { |
| double minimumValue, maximumValue; |
| s_controller->executeOnAXThreadAndWait([this, &minimumValue, &maximumValue] { |
| m_element->updateBackingStore(); |
| minimumValue = m_element->minimumValue(); |
| maximumValue = m_element->maximumValue(); |
| }); |
| if (minimumValue != maximumValue) |
| return true; |
| } |
| |
| return false; |
| } |
| |
| bool AccessibilityUIElement::isAttributeSupported(JSStringRef attribute) |
| { |
| String attributeName = toWTFString(attribute); |
| auto attributes = m_element->attributes(); |
| if (attributeName == "AXARIASetSize") |
| return attributes.contains("setsize"); |
| if (attributeName == "AXARIAPosInSet") |
| return attributes.contains("posinset"); |
| |
| return false; |
| } |
| |
| JSRetainPtr<JSStringRef> AccessibilityUIElement::parameterizedAttributeNames() |
| { |
| return JSStringCreateWithCharacters(0, 0); |
| } |
| |
| static String roleValueToString(unsigned roleValue) |
| { |
| switch (roleValue) { |
| case WebCore::Atspi::Role::Alert: |
| return "AXAlert"_s; |
| case WebCore::Atspi::Role::Article: |
| return "AXArticle"_s; |
| case WebCore::Atspi::Role::Audio: |
| return "AXAudio"_s; |
| case WebCore::Atspi::Role::BlockQuote: |
| return "AXBlockquote"_s; |
| case WebCore::Atspi::Role::Canvas: |
| return "AXCanvas"_s; |
| case WebCore::Atspi::Role::Caption: |
| return "AXCaption"_s; |
| case WebCore::Atspi::Role::CheckBox: |
| return "AXCheckBox"_s; |
| case WebCore::Atspi::Role::CheckMenuItem: |
| return "AXCheckMenuItem"_s; |
| case WebCore::Atspi::Role::ColorChooser: |
| return "AXColorWell"_s; |
| case WebCore::Atspi::Role::ColumnHeader: |
| case WebCore::Atspi::Role::TableColumnHeader: |
| return "AXColumnHeader"_s; |
| case WebCore::Atspi::Role::ComboBox: |
| return "AXComboBox"_s; |
| case WebCore::Atspi::Role::Comment: |
| return "AXComment"_s; |
| case WebCore::Atspi::Role::ContentDeletion: |
| return "AXDeletion"_s; |
| case WebCore::Atspi::Role::ContentInsertion: |
| return "AXInsertion"_s; |
| case WebCore::Atspi::Role::Definition: |
| return "AXDefinition"_s; |
| case WebCore::Atspi::Role::DescriptionList: |
| return "AXDescriptionList"_s; |
| case WebCore::Atspi::Role::DescriptionTerm: |
| return "AXDescriptionTerm"_s; |
| case WebCore::Atspi::Role::DescriptionValue: |
| return "AXDescriptionValue"_s; |
| case WebCore::Atspi::Role::Dialog: |
| return "AXDialog"_s; |
| case WebCore::Atspi::Role::DocumentFrame: |
| return "AXDocument"_s; |
| case WebCore::Atspi::Role::DocumentWeb: |
| return "AXWebArea"_s; |
| case WebCore::Atspi::Role::Embedded: |
| return "AXEmbedded"_s; |
| case WebCore::Atspi::Role::Entry: |
| return "AXTextField"_s; |
| case WebCore::Atspi::Role::Footer: |
| return "AXFooter"_s; |
| case WebCore::Atspi::Role::Footnote: |
| return "AXFootnote"_s; |
| case WebCore::Atspi::Role::Form: |
| return "AXForm"_s; |
| case WebCore::Atspi::Role::Grouping: |
| case WebCore::Atspi::Role::Panel: |
| return "AXGroup"_s; |
| case WebCore::Atspi::Role::Heading: |
| return "AXHeading"_s; |
| case WebCore::Atspi::Role::Image: |
| return "AXImage"_s; |
| case WebCore::Atspi::Role::ImageMap: |
| return "AXImageMap"_s; |
| case WebCore::Atspi::Role::InvalidRole: |
| return "AXInvalid"_s; |
| case WebCore::Atspi::Role::Label: |
| return "AXLabel"_s; |
| case WebCore::Atspi::Role::LevelBar: |
| return "AXLevelIndicator"_s; |
| case WebCore::Atspi::Role::Link: |
| return "AXLink"_s; |
| case WebCore::Atspi::Role::ListBox: |
| return "AXListBox"_s; |
| case WebCore::Atspi::Role::List: |
| return "AXList"_s; |
| case WebCore::Atspi::Role::ListItem: |
| return "AXListItem"_s; |
| case WebCore::Atspi::Role::Log: |
| return "AXLog"_s; |
| case WebCore::Atspi::Role::Marquee: |
| return "AXMarquee"_s; |
| case WebCore::Atspi::Role::Math: |
| return "AXMath"_s; |
| case WebCore::Atspi::Role::MathFraction: |
| return "AXMathFraction"_s; |
| case WebCore::Atspi::Role::MathRoot: |
| return "AXMathRoot"_s; |
| case WebCore::Atspi::Role::Menu: |
| return "AXMenu"_s; |
| case WebCore::Atspi::Role::MenuBar: |
| return "AXMenuBar"_s; |
| case WebCore::Atspi::Role::MenuItem: |
| return "AXMenuItem"_s; |
| case WebCore::Atspi::Role::Notification: |
| return "AXNotification"_s; |
| case WebCore::Atspi::Role::PageTab: |
| return "AXTab"_s; |
| case WebCore::Atspi::Role::PageTabList: |
| return "AXTabGroup"_s; |
| case WebCore::Atspi::Role::Paragraph: |
| return "AXParagraph"_s; |
| case WebCore::Atspi::Role::PasswordText: |
| return "AXPasswordField"_s; |
| case WebCore::Atspi::Role::ProgressBar: |
| return "AXProgressIndicator"_s; |
| case WebCore::Atspi::Role::PushButton: |
| return "AXButton"_s; |
| case WebCore::Atspi::Role::RadioButton: |
| return "AXRadioButton"_s; |
| case WebCore::Atspi::Role::RadioMenuItem: |
| return "AXRadioMenuItem"_s; |
| case WebCore::Atspi::Role::RowHeader: |
| case WebCore::Atspi::Role::TableRowHeader: |
| return "AXRowHeader"_s; |
| case WebCore::Atspi::Role::Ruler: |
| return "AXRuler"_s; |
| case WebCore::Atspi::Role::ScrollBar: |
| return "AXScrollBar"_s; |
| case WebCore::Atspi::Role::ScrollPane: |
| return "AXScrollArea"; |
| case WebCore::Atspi::Role::Section: |
| return "AXSection"_s; |
| case WebCore::Atspi::Role::Separator: |
| return "AXSeparator"_s; |
| case WebCore::Atspi::Role::Slider: |
| return "AXSlider"_s; |
| case WebCore::Atspi::Role::SpinButton: |
| return "AXSpinButton"_s; |
| case WebCore::Atspi::Role::Static: |
| return "AXStatic"_s; |
| case WebCore::Atspi::Role::StatusBar: |
| return "AXStatusBar"_s; |
| case WebCore::Atspi::Role::Subscript: |
| return "AXSubscript"_s; |
| case WebCore::Atspi::Role::Superscript: |
| return "AXSuperscript"_s; |
| case WebCore::Atspi::Role::Table: |
| return "AXTable"_s; |
| case WebCore::Atspi::Role::TableCell: |
| return "AXCell"_s; |
| case WebCore::Atspi::Role::TableRow: |
| return "AXRow"_s; |
| case WebCore::Atspi::Role::Timer: |
| return "AXTimer"_s; |
| case WebCore::Atspi::Role::ToggleButton: |
| return "AXToggleButton"_s; |
| case WebCore::Atspi::Role::ToolBar: |
| return "AXToolbar"_s; |
| case WebCore::Atspi::Role::ToolTip: |
| return "AXUserInterfaceTooltip"_s; |
| case WebCore::Atspi::Role::Tree: |
| return "AXTree"_s; |
| case WebCore::Atspi::Role::TreeTable: |
| return "AXTreeGrid"_s; |
| case WebCore::Atspi::Role::TreeItem: |
| return "AXTreeItem"_s; |
| case WebCore::Atspi::Role::Unknown: |
| return "AXUnknown"_s; |
| case WebCore::Atspi::Role::Video: |
| return "AXVideo"_s; |
| case WebCore::Atspi::Role::Window: |
| return "AXWindow"_s; |
| default: |
| break; |
| } |
| |
| return { }; |
| } |
| |
| JSRetainPtr<JSStringRef> AccessibilityUIElement::role() |
| { |
| unsigned roleValue; |
| s_controller->executeOnAXThreadAndWait([this, &roleValue] { |
| m_element->updateBackingStore(); |
| roleValue = m_element->role(); |
| }); |
| auto roleValueString = roleValueToString(roleValue); |
| if (roleValueString.isEmpty()) |
| return JSStringCreateWithCharacters(0, 0); |
| |
| return OpaqueJSString::tryCreate(makeString("AXRole: ", roleValueString)).leakRef(); |
| } |
| |
| JSRetainPtr<JSStringRef> AccessibilityUIElement::subrole() |
| { |
| return JSStringCreateWithCharacters(0, 0); |
| } |
| |
| JSRetainPtr<JSStringRef> AccessibilityUIElement::roleDescription() |
| { |
| auto roleDescription = m_element->attributes().get("roledescription"); |
| return OpaqueJSString::tryCreate(makeString("AXRoleDescription: ", roleDescription)).leakRef(); |
| } |
| |
| JSRetainPtr<JSStringRef> AccessibilityUIElement::computedRoleString() |
| { |
| auto computedRole = m_element->attributes().get("computed-role"); |
| if (computedRole.isEmpty()) |
| return JSStringCreateWithCharacters(0, 0); |
| |
| return OpaqueJSString::tryCreate(computedRole).leakRef(); |
| } |
| |
| JSRetainPtr<JSStringRef> AccessibilityUIElement::title() |
| { |
| String name; |
| s_controller->executeOnAXThreadAndWait([this, &name] { |
| m_element->updateBackingStore(); |
| name = String::fromUTF8(m_element->name().data()); |
| }); |
| |
| auto titleValue = makeString("AXTitle: ", name); |
| return OpaqueJSString::tryCreate(titleValue).leakRef(); |
| } |
| |
| JSRetainPtr<JSStringRef> AccessibilityUIElement::description() |
| { |
| String description; |
| s_controller->executeOnAXThreadAndWait([this, &description] { |
| m_element->updateBackingStore(); |
| description = String::fromUTF8(m_element->description().data()); |
| }); |
| |
| auto descriptionValue = makeString("AXDescription: ", description); |
| return OpaqueJSString::tryCreate(descriptionValue).leakRef(); |
| } |
| |
| JSRetainPtr<JSStringRef> AccessibilityUIElement::orientation() const |
| { |
| const char* orientation = nullptr; |
| if (checkElementState(m_element.get(), WebCore::Atspi::State::Horizontal)) |
| orientation = "AXHorizontalOrientation"; |
| else if (checkElementState(m_element.get(), WebCore::Atspi::State::Vertical)) |
| orientation = "AXVerticalOrientation"; |
| else |
| orientation = "AXUnknownOrientation"; |
| |
| auto orientationValue = makeString("AXOrientation: ", orientation); |
| return OpaqueJSString::tryCreate(orientationValue).leakRef(); |
| } |
| |
| JSRetainPtr<JSStringRef> AccessibilityUIElement::stringValue() |
| { |
| if (!m_element->interfaces().contains(WebCore::AccessibilityObjectAtspi::Interface::Text)) |
| return JSStringCreateWithCharacters(0, 0); |
| |
| m_element->updateBackingStore(); |
| auto value = makeString("AXValue: ", m_element->text().replace("\n", "<\\n>").replace(objectReplacementCharacter, "<obj>")); |
| return OpaqueJSString::tryCreate(value).leakRef(); |
| } |
| |
| JSRetainPtr<JSStringRef> AccessibilityUIElement::language() |
| { |
| m_element->updateBackingStore(); |
| auto locale = m_element->locale(); |
| if (locale.isEmpty()) |
| return JSStringCreateWithCharacters(0, 0); |
| |
| return OpaqueJSString::tryCreate(makeString("AXLanguage: ", locale)).leakRef(); |
| } |
| |
| JSRetainPtr<JSStringRef> AccessibilityUIElement::helpText() const |
| { |
| auto relationMap = m_element->relationMap(); |
| auto targets = relationMap.get(WebCore::Atspi::Relation::DescribedBy); |
| if (targets.isEmpty()) |
| return JSStringCreateWithCharacters(0, 0); |
| |
| StringBuilder builder; |
| builder.append("AXHelp: "); |
| |
| bool isFirst = true; |
| for (const auto& target : targets) { |
| if (!isFirst) |
| builder.append(' '); |
| isFirst = false; |
| target->updateBackingStore(); |
| builder.append(target->text()); |
| } |
| |
| return OpaqueJSString::tryCreate(builder.toString()).leakRef(); |
| } |
| |
| double AccessibilityUIElement::x() |
| { |
| return m_element->elementRect(WebCore::Atspi::CoordinateType::ScreenCoordinates).x(); |
| } |
| |
| double AccessibilityUIElement::y() |
| { |
| return m_element->elementRect(WebCore::Atspi::CoordinateType::ScreenCoordinates).y(); |
| } |
| |
| double AccessibilityUIElement::width() |
| { |
| return m_element->elementRect(WebCore::Atspi::CoordinateType::ScreenCoordinates).width(); |
| } |
| |
| double AccessibilityUIElement::height() |
| { |
| return m_element->elementRect(WebCore::Atspi::CoordinateType::ScreenCoordinates).height(); |
| } |
| |
| double AccessibilityUIElement::clickPointX() |
| { |
| auto rect = m_element->elementRect(WebCore::Atspi::CoordinateType::WindowCoordinates); |
| return rect.center().x(); |
| } |
| |
| double AccessibilityUIElement::clickPointY() |
| { |
| auto rect = m_element->elementRect(WebCore::Atspi::CoordinateType::WindowCoordinates); |
| return rect.center().y(); |
| } |
| |
| double AccessibilityUIElement::intValue() const |
| { |
| if (!m_element->interfaces().contains(WebCore::AccessibilityObjectAtspi::Interface::Value)) |
| return 0; |
| |
| double currentValue; |
| s_controller->executeOnAXThreadAndWait([this, ¤tValue] { |
| m_element->updateBackingStore(); |
| currentValue = m_element->currentValue(); |
| }); |
| return currentValue; |
| } |
| |
| double AccessibilityUIElement::minValue() |
| { |
| if (!m_element->interfaces().contains(WebCore::AccessibilityObjectAtspi::Interface::Value)) |
| return 0; |
| |
| double minimumValue; |
| s_controller->executeOnAXThreadAndWait([this, &minimumValue] { |
| m_element->updateBackingStore(); |
| minimumValue = m_element->minimumValue(); |
| }); |
| return minimumValue; |
| } |
| |
| double AccessibilityUIElement::maxValue() |
| { |
| if (!m_element->interfaces().contains(WebCore::AccessibilityObjectAtspi::Interface::Value)) |
| return 0; |
| |
| double maximumValue; |
| s_controller->executeOnAXThreadAndWait([this, &maximumValue] { |
| m_element->updateBackingStore(); |
| maximumValue = m_element->maximumValue(); |
| }); |
| return maximumValue; |
| } |
| |
| JSRetainPtr<JSStringRef> AccessibilityUIElement::valueDescription() |
| { |
| auto attributes = m_element->attributes(); |
| auto value = makeString("AXValueDescription: ", attributes.get("valuetext")); |
| return OpaqueJSString::tryCreate(value).leakRef(); |
| } |
| |
| int AccessibilityUIElement::insertionPointLineNumber() |
| { |
| return -1; |
| } |
| |
| bool AccessibilityUIElement::isPressActionSupported() |
| { |
| m_element->updateBackingStore(); |
| auto name = m_element->actionName(); |
| return name == "press" || name == "jump"; |
| } |
| |
| bool AccessibilityUIElement::isIncrementActionSupported() |
| { |
| return false; |
| } |
| |
| bool AccessibilityUIElement::isDecrementActionSupported() |
| { |
| return false; |
| } |
| |
| bool AccessibilityUIElement::isEnabled() |
| { |
| return checkElementState(m_element.get(), WebCore::Atspi::State::Enabled); |
| } |
| |
| bool AccessibilityUIElement::isRequired() const |
| { |
| return checkElementState(m_element.get(), WebCore::Atspi::State::Required); |
| } |
| |
| bool AccessibilityUIElement::isFocused() const |
| { |
| return checkElementState(m_element.get(), WebCore::Atspi::State::Focused); |
| } |
| |
| bool AccessibilityUIElement::isSelected() const |
| { |
| return checkElementState(m_element.get(), WebCore::Atspi::State::Selected); |
| } |
| |
| bool AccessibilityUIElement::isSelectedOptionActive() const |
| { |
| return checkElementState(m_element.get(), WebCore::Atspi::State::Active); |
| } |
| |
| bool AccessibilityUIElement::isExpanded() const |
| { |
| return checkElementState(m_element.get(), WebCore::Atspi::State::Expanded); |
| } |
| |
| bool AccessibilityUIElement::isChecked() const |
| { |
| return checkElementState(m_element.get(), WebCore::Atspi::State::Checked); |
| } |
| |
| bool AccessibilityUIElement::isIndeterminate() const |
| { |
| return checkElementState(m_element.get(), WebCore::Atspi::State::Indeterminate); |
| } |
| |
| int AccessibilityUIElement::hierarchicalLevel() const |
| { |
| auto level = m_element->attributes().get("level"); |
| if (level.isEmpty()) |
| return 0; |
| |
| return parseIntegerAllowingTrailingJunk<int>(level).value_or(0); |
| } |
| |
| JSRetainPtr<JSStringRef> AccessibilityUIElement::speakAs() |
| { |
| return JSStringCreateWithCharacters(0, 0); |
| } |
| |
| bool AccessibilityUIElement::ariaIsGrabbed() const |
| { |
| return m_element->attributes().get("grabbed") == "true"; |
| } |
| |
| JSRetainPtr<JSStringRef> AccessibilityUIElement::ariaDropEffects() const |
| { |
| auto dropEffects = m_element->attributes().get("dropeffect"); |
| if (dropEffects.isEmpty()) |
| return JSStringCreateWithCharacters(0, 0); |
| return OpaqueJSString::tryCreate(dropEffects).leakRef(); |
| } |
| |
| int AccessibilityUIElement::lineForIndex(int index) |
| { |
| if (!m_element->interfaces().contains(WebCore::AccessibilityObjectAtspi::Interface::Text)) |
| return -1; |
| |
| m_element->updateBackingStore(); |
| auto text = m_element->text(); |
| if (index < 0 || index > static_cast<int>(text.length())) |
| return -1; |
| |
| int lineNumber = 0; |
| for (int i = 0; i < index; ++i) { |
| if (text[i] == '\n') |
| lineNumber++; |
| } |
| |
| return lineNumber; |
| } |
| |
| JSRetainPtr<JSStringRef> AccessibilityUIElement::rangeForLine(int line) |
| { |
| if (!m_element->interfaces().contains(WebCore::AccessibilityObjectAtspi::Interface::Text)) |
| return JSStringCreateWithCharacters(0, 0); |
| |
| WebCore::IntPoint offset; |
| for (int i = 0; i <= line; ++i) |
| offset = m_element->boundaryOffset(offset.y(), WebCore::AccessibilityObjectAtspi::TextGranularity::LineStart); |
| |
| auto range = makeString('{', offset.x(), ", ", offset.y() - offset.x(), '}'); |
| return OpaqueJSString::tryCreate(range).leakRef(); |
| } |
| |
| JSRetainPtr<JSStringRef> AccessibilityUIElement::rangeForPosition(int x, int y) |
| { |
| return JSStringCreateWithCharacters(0, 0); |
| } |
| |
| JSRetainPtr<JSStringRef> AccessibilityUIElement::boundsForRange(unsigned location, unsigned length) |
| { |
| if (!m_element->interfaces().contains(WebCore::AccessibilityObjectAtspi::Interface::Text)) |
| return JSStringCreateWithCharacters(0, 0); |
| |
| auto rect = m_element->boundsForRange(location, length, WebCore::Atspi::CoordinateType::WindowCoordinates); |
| auto bounds = makeString('{', rect.x(), ", ", rect.y(), ", ", rect.width(), ", ", rect.height(), '}'); |
| return OpaqueJSString::tryCreate(bounds).leakRef(); |
| } |
| |
| JSRetainPtr<JSStringRef> AccessibilityUIElement::stringForRange(unsigned location, unsigned length) |
| { |
| if (!m_element->interfaces().contains(WebCore::AccessibilityObjectAtspi::Interface::Text)) |
| return JSStringCreateWithCharacters(0, 0); |
| |
| m_element->updateBackingStore(); |
| return OpaqueJSString::tryCreate(m_element->text().substring(location, length)).leakRef(); |
| } |
| |
| JSRetainPtr<JSStringRef> AccessibilityUIElement::attributedStringForRange(unsigned location, unsigned length) |
| { |
| if (!m_element->interfaces().contains(WebCore::AccessibilityObjectAtspi::Interface::Text)) |
| return JSStringCreateWithCharacters(0, 0); |
| |
| StringBuilder builder; |
| |
| auto buildAttributes = [&](const WebCore::AccessibilityObjectAtspi::TextAttributes& attributes) { |
| for (const auto& it : attributes.attributes) { |
| builder.append("\n\t\t"); |
| builder.append(it.key, ':', it.value); |
| } |
| }; |
| |
| builder.append("\n\tDefault text attributes:"); |
| buildAttributes(m_element->textAttributes()); |
| |
| auto text = m_element->text(); |
| int endOffset = 0; |
| for (unsigned i = location; i < location + length; i = endOffset) { |
| auto attributes = m_element->textAttributes(i); |
| auto rangeStart = std::max<int>(location, attributes.startOffset); |
| auto rangeEnd = std::min<int>(location + length, attributes.endOffset); |
| builder.append("\n\tRange attributes for '", text.substring(rangeStart, rangeEnd - rangeStart).replace("\n", "<\\n>").replace(objectReplacementCharacter, "<obj>"), "':"); |
| buildAttributes(attributes); |
| endOffset = attributes.endOffset; |
| } |
| |
| return OpaqueJSString::tryCreate(builder.toString()).leakRef(); |
| } |
| |
| bool AccessibilityUIElement::attributedStringRangeIsMisspelled(unsigned location, unsigned length) |
| { |
| return false; |
| } |
| |
| unsigned AccessibilityUIElement::uiElementCountForSearchPredicate(JSContextRef context, AccessibilityUIElement* startElement, bool isDirectionNext, JSValueRef searchKey, JSStringRef searchText, bool visibleOnly, bool immediateDescendantsOnly) |
| { |
| return 0; |
| } |
| |
| RefPtr<AccessibilityUIElement> AccessibilityUIElement::uiElementForSearchPredicate(JSContextRef context, AccessibilityUIElement* startElement, bool isDirectionNext, JSValueRef searchKey, JSStringRef searchText, bool visibleOnly, bool immediateDescendantsOnly) |
| { |
| return nullptr; |
| } |
| |
| JSRetainPtr<JSStringRef> AccessibilityUIElement::selectTextWithCriteria(JSContextRef context, JSStringRef ambiguityResolution, JSValueRef searchStrings, JSStringRef replacementString, JSStringRef activity) |
| { |
| return nullptr; |
| } |
| |
| JSRetainPtr<JSStringRef> AccessibilityUIElement::attributesOfColumnHeaders() |
| { |
| return JSStringCreateWithCharacters(0, 0); |
| } |
| |
| JSRetainPtr<JSStringRef> AccessibilityUIElement::attributesOfRowHeaders() |
| { |
| return JSStringCreateWithCharacters(0, 0); |
| } |
| |
| JSRetainPtr<JSStringRef> AccessibilityUIElement::attributesOfColumns() |
| { |
| return JSStringCreateWithCharacters(0, 0); |
| } |
| |
| JSRetainPtr<JSStringRef> AccessibilityUIElement::attributesOfRows() |
| { |
| return JSStringCreateWithCharacters(0, 0); |
| } |
| |
| JSRetainPtr<JSStringRef> AccessibilityUIElement::attributesOfVisibleCells() |
| { |
| return JSStringCreateWithCharacters(0, 0); |
| } |
| |
| JSRetainPtr<JSStringRef> AccessibilityUIElement::attributesOfHeader() |
| { |
| return JSStringCreateWithCharacters(0, 0); |
| } |
| |
| int AccessibilityUIElement::rowCount() |
| { |
| return 0; |
| } |
| |
| int AccessibilityUIElement::columnCount() |
| { |
| return 0; |
| } |
| |
| int AccessibilityUIElement::indexInTable() |
| { |
| return -1; |
| } |
| |
| JSRetainPtr<JSStringRef> AccessibilityUIElement::rowIndexRange() |
| { |
| return JSStringCreateWithCharacters(0, 0); |
| } |
| |
| JSRetainPtr<JSStringRef> AccessibilityUIElement::columnIndexRange() |
| { |
| return JSStringCreateWithCharacters(0, 0); |
| } |
| |
| RefPtr<AccessibilityUIElement> AccessibilityUIElement::cellForColumnAndRow(unsigned col, unsigned row) |
| { |
| return nullptr; |
| } |
| |
| RefPtr<AccessibilityUIElement> AccessibilityUIElement::horizontalScrollbar() const |
| { |
| return nullptr; |
| } |
| |
| RefPtr<AccessibilityUIElement> AccessibilityUIElement::verticalScrollbar() const |
| { |
| return nullptr; |
| } |
| |
| JSRetainPtr<JSStringRef> AccessibilityUIElement::selectedTextRange() |
| { |
| if (!m_element->interfaces().contains(WebCore::AccessibilityObjectAtspi::Interface::Text)) |
| return JSStringCreateWithCharacters(0, 0); |
| |
| auto offset = m_element->selectedRange(); |
| auto range = makeString('{', offset.x(), ", ", offset.y() - offset.x(), '}'); |
| return OpaqueJSString::tryCreate(range).leakRef(); |
| } |
| |
| bool AccessibilityUIElement::setSelectedTextRange(unsigned location, unsigned length) |
| { |
| if (!m_element->interfaces().contains(WebCore::AccessibilityObjectAtspi::Interface::Text)) |
| return false; |
| |
| m_element->updateBackingStore(); |
| auto textLength = m_element->text().length(); |
| m_element->setSelectedRange(std::min(location, textLength), std::min(length, textLength)); |
| return true; |
| } |
| |
| void AccessibilityUIElement::increment() |
| { |
| if (!m_element->interfaces().contains(WebCore::AccessibilityObjectAtspi::Interface::Value)) |
| return; |
| |
| m_element->setCurrentValue(intValue() + m_element->minimumIncrement()); |
| } |
| |
| void AccessibilityUIElement::decrement() |
| { |
| if (!m_element->interfaces().contains(WebCore::AccessibilityObjectAtspi::Interface::Value)) |
| return; |
| |
| m_element->setCurrentValue(intValue() - m_element->minimumIncrement()); |
| } |
| |
| void AccessibilityUIElement::showMenu() |
| { |
| } |
| |
| void AccessibilityUIElement::press() |
| { |
| m_element->doAction(); |
| } |
| |
| void AccessibilityUIElement::setSelectedChild(AccessibilityUIElement* element) const |
| { |
| } |
| |
| void AccessibilityUIElement::setSelectedChildAtIndex(unsigned index) const |
| { |
| } |
| |
| void AccessibilityUIElement::removeSelectionAtIndex(unsigned index) const |
| { |
| } |
| |
| void AccessibilityUIElement::clearSelectedChildren() const |
| { |
| } |
| |
| JSRetainPtr<JSStringRef> AccessibilityUIElement::accessibilityValue() const |
| { |
| return JSStringCreateWithCharacters(0, 0); |
| } |
| |
| JSRetainPtr<JSStringRef> AccessibilityUIElement::documentEncoding() |
| { |
| if (!m_element->interfaces().contains(WebCore::AccessibilityObjectAtspi::Interface::Document)) |
| return JSStringCreateWithCharacters(0, 0); |
| |
| return OpaqueJSString::tryCreate(m_element->documentAttribute("Encoding")).leakRef(); |
| } |
| |
| JSRetainPtr<JSStringRef> AccessibilityUIElement::documentURI() |
| { |
| if (!m_element->interfaces().contains(WebCore::AccessibilityObjectAtspi::Interface::Document)) |
| return JSStringCreateWithCharacters(0, 0); |
| |
| return OpaqueJSString::tryCreate(m_element->documentAttribute("URI")).leakRef(); |
| } |
| |
| JSRetainPtr<JSStringRef> AccessibilityUIElement::url() |
| { |
| if (!m_element->interfaces().contains(WebCore::AccessibilityObjectAtspi::Interface::Hyperlink)) |
| return JSStringCreateWithCharacters(0, 0); |
| |
| m_element->updateBackingStore(); |
| auto axURL = m_element->url(); |
| if (axURL.isNull()) |
| return JSStringCreateWithUTF8CString("AXURL: (null)"); |
| |
| if (axURL.isLocalFile()) { |
| // Do not expose absolute paths. |
| auto path = axURL.fileSystemPath(); |
| auto index = path.find("LayoutTests"); |
| if (index != notFound) |
| path = path.substring(index); |
| return OpaqueJSString::tryCreate(makeString("AXURL: ", path)).leakRef(); |
| } |
| |
| return OpaqueJSString::tryCreate(makeString("AXURL: ", axURL.string())).leakRef(); |
| } |
| |
| bool AccessibilityUIElement::addNotificationListener(JSValueRef functionCallback) |
| { |
| return false; |
| } |
| |
| bool AccessibilityUIElement::removeNotificationListener() |
| { |
| return true; |
| } |
| |
| bool AccessibilityUIElement::isFocusable() const |
| { |
| return checkElementState(m_element.get(), WebCore::Atspi::State::Focusable); |
| } |
| |
| bool AccessibilityUIElement::isSelectable() const |
| { |
| return checkElementState(m_element.get(), WebCore::Atspi::State::Selectable); |
| } |
| |
| bool AccessibilityUIElement::isMultiSelectable() const |
| { |
| return checkElementState(m_element.get(), WebCore::Atspi::State::Multiselectable); |
| } |
| |
| bool AccessibilityUIElement::isVisible() const |
| { |
| return checkElementState(m_element.get(), WebCore::Atspi::State::Visible); |
| } |
| |
| bool AccessibilityUIElement::isOffScreen() const |
| { |
| return !checkElementState(m_element.get(), WebCore::Atspi::State::Showing); |
| } |
| |
| bool AccessibilityUIElement::isCollapsed() const |
| { |
| return checkElementState(m_element.get(), WebCore::Atspi::State::Collapsed); |
| } |
| |
| bool AccessibilityUIElement::isIgnored() const |
| { |
| return false; |
| } |
| |
| bool AccessibilityUIElement::isSingleLine() const |
| { |
| return checkElementState(m_element.get(), WebCore::Atspi::State::SingleLine); |
| } |
| |
| bool AccessibilityUIElement::isMultiLine() const |
| { |
| return checkElementState(m_element.get(), WebCore::Atspi::State::MultiLine); |
| } |
| |
| bool AccessibilityUIElement::hasPopup() const |
| { |
| return checkElementState(m_element.get(), WebCore::Atspi::State::HasPopup); |
| } |
| |
| void AccessibilityUIElement::takeFocus() |
| { |
| } |
| |
| void AccessibilityUIElement::takeSelection() |
| { |
| } |
| |
| void AccessibilityUIElement::addSelection() |
| { |
| } |
| |
| void AccessibilityUIElement::removeSelection() |
| { |
| } |
| |
| // Text markers |
| RefPtr<AccessibilityTextMarkerRange> AccessibilityUIElement::lineTextMarkerRangeForTextMarker(AccessibilityTextMarker* textMarker) |
| { |
| return nullptr; |
| } |
| |
| RefPtr<AccessibilityTextMarkerRange> AccessibilityUIElement::textMarkerRangeForElement(AccessibilityUIElement* element) |
| { |
| return nullptr; |
| } |
| |
| int AccessibilityUIElement::textMarkerRangeLength(AccessibilityTextMarkerRange* range) |
| { |
| return 0; |
| } |
| |
| RefPtr<AccessibilityTextMarker> AccessibilityUIElement::previousTextMarker(AccessibilityTextMarker* textMarker) |
| { |
| return nullptr; |
| } |
| |
| RefPtr<AccessibilityTextMarker> AccessibilityUIElement::nextTextMarker(AccessibilityTextMarker* textMarker) |
| { |
| return nullptr; |
| } |
| |
| JSRetainPtr<JSStringRef> AccessibilityUIElement::stringForTextMarkerRange(AccessibilityTextMarkerRange* markerRange) |
| { |
| return JSStringCreateWithCharacters(0, 0); |
| } |
| |
| JSRetainPtr<JSStringRef> AccessibilityUIElement::rectsForTextMarkerRange(AccessibilityTextMarkerRange* markerRange, JSStringRef searchText) |
| { |
| return JSStringCreateWithCharacters(0, 0); |
| } |
| |
| RefPtr<AccessibilityTextMarkerRange> AccessibilityUIElement::textMarkerRangeForMarkers(AccessibilityTextMarker* startMarker, AccessibilityTextMarker* endMarker) |
| { |
| return nullptr; |
| } |
| |
| RefPtr<AccessibilityTextMarker> AccessibilityUIElement::startTextMarkerForTextMarkerRange(AccessibilityTextMarkerRange* range) |
| { |
| return nullptr; |
| } |
| |
| RefPtr<AccessibilityTextMarker> AccessibilityUIElement::endTextMarkerForTextMarkerRange(AccessibilityTextMarkerRange* range) |
| { |
| return nullptr; |
| } |
| |
| RefPtr<AccessibilityTextMarker> AccessibilityUIElement::endTextMarkerForBounds(int x, int y, int width, int height) |
| { |
| return nullptr; |
| } |
| |
| RefPtr<AccessibilityTextMarker> AccessibilityUIElement::startTextMarkerForBounds(int x, int y, int width, int height) |
| { |
| return nullptr; |
| } |
| |
| RefPtr<AccessibilityTextMarker> AccessibilityUIElement::textMarkerForPoint(int x, int y) |
| { |
| return nullptr; |
| } |
| |
| RefPtr<AccessibilityUIElement> AccessibilityUIElement::accessibilityElementForTextMarker(AccessibilityTextMarker* marker) |
| { |
| return nullptr; |
| } |
| |
| JSRetainPtr<JSStringRef> AccessibilityUIElement::attributedStringForTextMarkerRange(AccessibilityTextMarkerRange*) |
| { |
| return nullptr; |
| } |
| |
| JSRetainPtr<JSStringRef> AccessibilityUIElement::attributedStringForTextMarkerRangeWithOptions(AccessibilityTextMarkerRange*, bool) |
| { |
| return nullptr; |
| } |
| |
| bool AccessibilityUIElement::attributedStringForTextMarkerRangeContainsAttribute(JSStringRef attribute, AccessibilityTextMarkerRange* range) |
| { |
| return false; |
| } |
| |
| int AccessibilityUIElement::indexForTextMarker(AccessibilityTextMarker* marker) |
| { |
| return -1; |
| } |
| |
| bool AccessibilityUIElement::isTextMarkerValid(AccessibilityTextMarker* textMarker) |
| { |
| return false; |
| } |
| |
| RefPtr<AccessibilityTextMarker> AccessibilityUIElement::textMarkerForIndex(int textIndex) |
| { |
| return nullptr; |
| } |
| |
| RefPtr<AccessibilityTextMarker> AccessibilityUIElement::startTextMarker() |
| { |
| return nullptr; |
| } |
| |
| RefPtr<AccessibilityTextMarker> AccessibilityUIElement::endTextMarker() |
| { |
| return nullptr; |
| } |
| |
| bool AccessibilityUIElement::setSelectedTextMarkerRange(AccessibilityTextMarkerRange*) |
| { |
| return false; |
| } |
| |
| void AccessibilityUIElement::scrollToMakeVisible() |
| { |
| m_element->scrollToMakeVisible(WebCore::Atspi::ScrollType::Anywhere); |
| } |
| |
| void AccessibilityUIElement::scrollToGlobalPoint(int x, int y) |
| { |
| m_element->scrollToPoint({ x, y }, WebCore::Atspi::CoordinateType::WindowCoordinates); |
| } |
| |
| void AccessibilityUIElement::scrollToMakeVisibleWithSubFocus(int x, int y, int width, int height) |
| { |
| } |
| |
| JSRetainPtr<JSStringRef> AccessibilityUIElement::supportedActions() const |
| { |
| return nullptr; |
| } |
| |
| JSRetainPtr<JSStringRef> AccessibilityUIElement::pathDescription() const |
| { |
| return nullptr; |
| } |
| |
| JSRetainPtr<JSStringRef> AccessibilityUIElement::mathPostscriptsDescription() const |
| { |
| return nullptr; |
| } |
| |
| JSRetainPtr<JSStringRef> AccessibilityUIElement::mathPrescriptsDescription() const |
| { |
| return nullptr; |
| } |
| |
| JSRetainPtr<JSStringRef> AccessibilityUIElement::classList() const |
| { |
| return nullptr; |
| } |
| |
| static String stringAtOffset(WebCore::AccessibilityObjectAtspi* element, int offset, WebCore::AccessibilityObjectAtspi::TextGranularity granularity) |
| { |
| if (!element || !element->interfaces().contains(WebCore::AccessibilityObjectAtspi::Interface::Text)) |
| return { }; |
| |
| element->updateBackingStore(); |
| auto text = element->text(); |
| if (offset < 0 || offset > static_cast<int>(text.length())) |
| return { }; |
| |
| auto bounds = element->boundaryOffset(offset, granularity); |
| unsigned startOffset = std::max<int>(bounds.x(), 0); |
| unsigned endOffset = std::min<int>(bounds.y(), text.length()); |
| return makeString(text.substring(startOffset, endOffset - startOffset), ", ", startOffset, ", ", endOffset); |
| } |
| |
| JSRetainPtr<JSStringRef> AccessibilityUIElement::characterAtOffset(int offset) |
| { |
| if (!m_element->interfaces().contains(WebCore::AccessibilityObjectAtspi::Interface::Text)) |
| return JSStringCreateWithCharacters(0, 0); |
| |
| m_element->updateBackingStore(); |
| auto text = m_element->text(); |
| if (offset < 0 || offset > static_cast<int>(text.length())) |
| return JSStringCreateWithCharacters(0, 0); |
| |
| auto string = makeString(text.substring(offset, 1), ", ", offset, ", ", offset + 1); |
| return OpaqueJSString::tryCreate(string).leakRef(); |
| } |
| |
| JSRetainPtr<JSStringRef> AccessibilityUIElement::wordAtOffset(int offset) |
| { |
| return OpaqueJSString::tryCreate(stringAtOffset(m_element.get(), offset, WebCore::AccessibilityObjectAtspi::TextGranularity::WordStart)).leakRef(); |
| } |
| |
| JSRetainPtr<JSStringRef> AccessibilityUIElement::lineAtOffset(int offset) |
| { |
| return OpaqueJSString::tryCreate(stringAtOffset(m_element.get(), offset, WebCore::AccessibilityObjectAtspi::TextGranularity::LineStart)).leakRef(); |
| } |
| |
| JSRetainPtr<JSStringRef> AccessibilityUIElement::sentenceAtOffset(int offset) |
| { |
| return OpaqueJSString::tryCreate(stringAtOffset(m_element.get(), offset, WebCore::AccessibilityObjectAtspi::TextGranularity::SentenceStart)).leakRef(); |
| } |
| |
| bool AccessibilityUIElement::replaceTextInRange(JSStringRef, int, int) |
| { |
| return false; |
| } |
| |
| bool AccessibilityUIElement::insertText(JSStringRef) |
| { |
| return false; |
| } |
| |
| JSRetainPtr<JSStringRef> AccessibilityUIElement::popupValue() const |
| { |
| return nullptr; |
| } |
| |
| } // namespace WTR |
| |
| #endif // HAVE(ACCESSIBILITY) && USE(ATSPI) |