Sign Up
Log In
Log In
or
Sign Up
Places
All Projects
Status Monitor
Collapse sidebar
SUSE:SLE-12-SP2:Update
webkit2gtk3.26886
webkit2gtk3-restore-atk.patch
Overview
Repositories
Revisions
Requests
Users
Attributes
Meta
File webkit2gtk3-restore-atk.patch of Package webkit2gtk3.26886
diff -urpN webkitgtk-2.38.1.glib244/Source/cmake/OptionsGTK.cmake webkitgtk-2.38.1/Source/cmake/OptionsGTK.cmake --- webkitgtk-2.38.1.glib244/Source/cmake/OptionsGTK.cmake 2022-10-24 13:33:35.809052808 -0500 +++ webkitgtk-2.38.1/Source/cmake/OptionsGTK.cmake 2022-10-24 13:34:51.897454499 -0500 @@ -26,6 +26,7 @@ find_package(PNG REQUIRED) find_package(SQLite3 REQUIRED) find_package(Threads REQUIRED) find_package(ZLIB REQUIRED) +find_package(ATK 2.16.0 REQUIRED) find_package(WebP REQUIRED COMPONENTS demux) find_package(ATSPI 2.5.3) find_package(EGL) @@ -78,6 +79,7 @@ WEBKIT_OPTION_DEFINE(USE_WPE_RENDERER "W # Private options specific to the GTK port. Changing these options is # completely unsupported. They are intended for use only by WebKit developers. +WEBKIT_OPTION_DEFINE(USE_ATSPI "Whether to use the ATSPI a11y implementation instead of ATK." PRIVATE ON) WEBKIT_OPTION_DEPEND(ENABLE_DOCUMENTATION ENABLE_INTROSPECTION) WEBKIT_OPTION_DEPEND(ENABLE_3D_TRANSFORMS USE_OPENGL_OR_ES) @@ -278,7 +280,17 @@ if (NOT EXISTS "${TOOLS_DIR}/glib/apply- set(BUILD_REVISION "tarball") endif () -SET_AND_EXPOSE_TO_BUILD(USE_ATSPI ${ENABLE_ACCESSIBILITY}) +if (ENABLE_ACCESSIBILITY) + if (USE_ATSPI) + SET_AND_EXPOSE_TO_BUILD(USE_ATK FALSE) + else () + SET_AND_EXPOSE_TO_BUILD(USE_ATK TRUE) + endif () +else () + SET_AND_EXPOSE_TO_BUILD(USE_ATK FALSE) + SET_AND_EXPOSE_TO_BUILD(USE_ATSPI FALSE) +endif () + SET_AND_EXPOSE_TO_BUILD(HAVE_GTK_UNIX_PRINTING ${GTK_UNIX_PRINT_FOUND}) SET_AND_EXPOSE_TO_BUILD(HAVE_OS_DARK_MODE_SUPPORT 1) diff -urpN webkitgtk-2.38.1.glib244/Source/WebCore/accessibility/AccessibilityList.cpp webkitgtk-2.38.1/Source/WebCore/accessibility/AccessibilityList.cpp --- webkitgtk-2.38.1.glib244/Source/WebCore/accessibility/AccessibilityList.cpp 2022-08-31 02:59:55.682515900 -0500 +++ webkitgtk-2.38.1/Source/WebCore/accessibility/AccessibilityList.cpp 2022-10-24 13:34:51.897454499 -0500 @@ -117,7 +117,7 @@ bool AccessibilityList::childHasPseudoVi // Platforms which expose rendered text content through the parent element will treat // those renderers as "ignored" objects. -#if USE(ATSPI) +#if USE(ATK) || USE(ATSPI) String text = axObj->textUnderElement(); return !text.isEmpty() && !text.isAllSpecialCharacters<isHTMLSpace>(); #else diff -urpN webkitgtk-2.38.1.glib244/Source/WebCore/accessibility/AccessibilityNodeObject.cpp webkitgtk-2.38.1/Source/WebCore/accessibility/AccessibilityNodeObject.cpp --- webkitgtk-2.38.1.glib244/Source/WebCore/accessibility/AccessibilityNodeObject.cpp 2022-09-08 06:51:52.493481600 -0500 +++ webkitgtk-2.38.1/Source/WebCore/accessibility/AccessibilityNodeObject.cpp 2022-10-24 13:34:51.901454519 -0500 @@ -2484,8 +2484,8 @@ bool AccessibilityNodeObject::canSetValu if (isProgressIndicator() || isSlider() || isScrollbar()) return true; -#if USE(ATSPI) - // In ATSPI, input types which support aria-readonly are treated as having a +#if USE(ATK) || USE(ATSPI) + // In ATK, input types which support aria-readonly are treated as having a // settable value if the user can modify the widget's value or its state. if (supportsReadOnly()) return true; diff -urpN webkitgtk-2.38.1.glib244/Source/WebCore/accessibility/AccessibilityObject.cpp webkitgtk-2.38.1/Source/WebCore/accessibility/AccessibilityObject.cpp --- webkitgtk-2.38.1.glib244/Source/WebCore/accessibility/AccessibilityObject.cpp 2022-09-14 06:58:10.436862700 -0500 +++ webkitgtk-2.38.1/Source/WebCore/accessibility/AccessibilityObject.cpp 2022-10-24 13:34:51.905454540 -0500 @@ -3673,8 +3673,8 @@ TextIteratorBehaviors AccessibilityObjec { TextIteratorBehaviors behaviors { TextIteratorBehavior::IgnoresStyleVisibility }; -#if USE(ATSPI) - // We need to emit replaced elements for ATSPI, and present +#if USE(ATK) || USE(ATSPI) + // We need to emit replaced elements for GTK, and present // them with the 'object replacement character' (0xFFFC). behaviors.add(TextIteratorBehavior::EmitsObjectReplacementCharacters); #endif diff -urpN webkitgtk-2.38.1.glib244/Source/WebCore/accessibility/AccessibilityObject.h webkitgtk-2.38.1/Source/WebCore/accessibility/AccessibilityObject.h --- webkitgtk-2.38.1.glib244/Source/WebCore/accessibility/AccessibilityObject.h 2022-09-08 06:51:52.497481800 -0500 +++ webkitgtk-2.38.1/Source/WebCore/accessibility/AccessibilityObject.h 2022-10-24 13:34:51.905454540 -0500 @@ -42,6 +42,10 @@ #include <wtf/RetainPtr.h> #endif +#if USE(ATK) +#include <wtf/glib/GRefPtr.h> +#endif + #if PLATFORM(COCOA) OBJC_CLASS NSArray; @@ -878,7 +882,7 @@ inline void AccessibilityObject::updateB inline void AccessibilityObject::detachPlatformWrapper(AccessibilityDetachmentType) { } #endif -#if !(ENABLE(ACCESSIBILITY) && USE(ATSPI)) +#if !(ENABLE(ACCESSIBILITY) && (USE(ATK) || USE(ATSPI))) inline bool AccessibilityObject::allowsTextRanges() const { return true; } inline unsigned AccessibilityObject::getLengthForTextRange() const { return text().length(); } #endif diff -urpN webkitgtk-2.38.1.glib244/Source/WebCore/accessibility/AccessibilityObjectInterface.h webkitgtk-2.38.1/Source/WebCore/accessibility/AccessibilityObjectInterface.h --- webkitgtk-2.38.1.glib244/Source/WebCore/accessibility/AccessibilityObjectInterface.h 2022-09-08 06:51:52.497481800 -0500 +++ webkitgtk-2.38.1/Source/WebCore/accessibility/AccessibilityObjectInterface.h 2022-10-24 13:34:51.905454540 -0500 @@ -48,6 +48,10 @@ #include "AccessibilityObjectAtspi.h" #endif +#if USE(ATK) +#include <wtf/glib/GRefPtr.h> +#endif + #if PLATFORM(COCOA) OBJC_CLASS WebAccessibilityObjectWrapper; typedef WebAccessibilityObjectWrapper AccessibilityObjectWrapper; @@ -56,6 +60,9 @@ typedef const struct __AXTextMarker* AXT typedef const struct __AXTextMarkerRange* AXTextMarkerRangeRef; #elif USE(ATSPI) typedef WebCore::AccessibilityObjectAtspi AccessibilityObjectWrapper; +#elif USE(ATK) +typedef struct _WebKitAccessible WebKitAccessible; +typedef struct _WebKitAccessible AccessibilityObjectWrapper; #else class AccessibilityObjectWrapper; #endif @@ -1558,6 +1565,8 @@ private: COMPtr<AccessibilityObjectWrapper> m_wrapper; #elif USE(ATSPI) RefPtr<AccessibilityObjectAtspi> m_wrapper; +#elif USE(ATK) + GRefPtr<WebKitAccessible> m_wrapper; #endif virtual void detachPlatformWrapper(AccessibilityDetachmentType) = 0; }; diff -urpN webkitgtk-2.38.1.glib244/Source/WebCore/accessibility/AccessibilityRenderObject.cpp webkitgtk-2.38.1/Source/WebCore/accessibility/AccessibilityRenderObject.cpp --- webkitgtk-2.38.1.glib244/Source/WebCore/accessibility/AccessibilityRenderObject.cpp 2022-09-14 06:58:10.436862700 -0500 +++ webkitgtk-2.38.1/Source/WebCore/accessibility/AccessibilityRenderObject.cpp 2022-10-24 13:34:51.909454562 -0500 @@ -2138,6 +2138,8 @@ int AccessibilityRenderObject::indexForV TextIteratorBehaviors behaviors; #if USE(ATSPI) behaviors.add(TextIteratorBehavior::EmitsObjectReplacementCharacters); +#elif USE(ATK) + behaviors.add(TextIteratorBehavior::EmitsCharactersBetweenAllVisiblePositions); #endif return WebCore::indexForVisiblePosition(*node, position, behaviors); diff -urpN webkitgtk-2.38.1.glib244/Source/WebCore/accessibility/AccessibilityTableColumn.cpp webkitgtk-2.38.1/Source/WebCore/accessibility/AccessibilityTableColumn.cpp --- webkitgtk-2.38.1.glib244/Source/WebCore/accessibility/AccessibilityTableColumn.cpp 2022-08-31 02:59:55.686516000 -0500 +++ webkitgtk-2.38.1/Source/WebCore/accessibility/AccessibilityTableColumn.cpp 2022-10-24 13:34:51.909454562 -0500 @@ -172,7 +172,7 @@ bool AccessibilityTableColumn::computeAc if (!m_parent) return true; -#if PLATFORM(IOS_FAMILY) || USE(ATSPI) +#if PLATFORM(IOS_FAMILY) || USE(ATK) || USE(ATSPI) return true; #endif diff -urpN webkitgtk-2.38.1.glib244/Source/WebCore/accessibility/AccessibilityTableHeaderContainer.cpp webkitgtk-2.38.1/Source/WebCore/accessibility/AccessibilityTableHeaderContainer.cpp --- webkitgtk-2.38.1.glib244/Source/WebCore/accessibility/AccessibilityTableHeaderContainer.cpp 2022-08-31 02:59:55.686516000 -0500 +++ webkitgtk-2.38.1/Source/WebCore/accessibility/AccessibilityTableHeaderContainer.cpp 2022-10-24 13:34:51.909454562 -0500 @@ -54,7 +54,7 @@ bool AccessibilityTableHeaderContainer:: if (!m_parent) return true; -#if PLATFORM(IOS_FAMILY) || USE(ATSPI) +#if PLATFORM(IOS_FAMILY) || USE(ATK) || USE(ATSPI) return true; #endif diff -urpN webkitgtk-2.38.1.glib244/Source/WebCore/accessibility/atk/AccessibilityObjectAtk.cpp webkitgtk-2.38.1/Source/WebCore/accessibility/atk/AccessibilityObjectAtk.cpp --- webkitgtk-2.38.1.glib244/Source/WebCore/accessibility/atk/AccessibilityObjectAtk.cpp 1969-12-31 18:00:00.000000000 -0600 +++ webkitgtk-2.38.1/Source/WebCore/accessibility/atk/AccessibilityObjectAtk.cpp 2022-10-24 13:34:51.913454582 -0500 @@ -0,0 +1,209 @@ +/* + * Copyright (C) 2008 Apple Ltd. + * Copyright (C) 2008 Alp Toker <alp@atoker.com> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include "config.h" +#include "AccessibilityObject.h" + +#if ENABLE(ACCESSIBILITY) && USE(ATK) + +#include "AXObjectCache.h" +#include "HTMLSpanElement.h" +#include "RenderBlock.h" +#include "RenderInline.h" +#include "RenderIterator.h" +#include "RenderTableCell.h" +#include "RenderText.h" +#include "TextControlInnerElements.h" +#include "WebKitAccessible.h" +#include <glib-object.h> + +namespace WebCore { + +void AccessibilityObject::detachPlatformWrapper(AccessibilityDetachmentType detachmentType) +{ + if (detachmentType != AccessibilityDetachmentType::CacheDestroyed) { + if (auto* cache = axObjectCache()) { + cache->detachWrapper(this, detachmentType); + return; + } + } + + auto* wrapper = this->wrapper(); + ASSERT(wrapper); + webkitAccessibleDetach(WEBKIT_ACCESSIBLE(wrapper)); +} + +bool AccessibilityObject::accessibilityIgnoreAttachment() const +{ + return false; +} + +AccessibilityObjectInclusion AccessibilityObject::accessibilityPlatformIncludesObject() const +{ + AccessibilityObject* parent = parentObject(); + if (!parent) + return AccessibilityObjectInclusion::DefaultBehavior; + + // If the author has provided a role, platform-specific inclusion likely doesn't apply. + if (ariaRoleAttribute() != AccessibilityRole::Unknown) + return AccessibilityObjectInclusion::DefaultBehavior; + + AccessibilityRole role = roleValue(); + // We expose the slider as a whole but not its value indicator. + if (role == AccessibilityRole::SliderThumb) + return AccessibilityObjectInclusion::IgnoreObject; + + // When a list item is made up entirely of children (e.g. paragraphs) + // the list item gets ignored. We need it. + if (isGroup() && parent->isList()) + return AccessibilityObjectInclusion::IncludeObject; + + // Entries and password fields have extraneous children which we want to ignore. + if (parent->isPasswordField() || parent->isTextControl()) + return AccessibilityObjectInclusion::IgnoreObject; + + // Include all tables, even layout tables. The AT can decide what to do with each. + if (role == AccessibilityRole::Cell || role == AccessibilityRole::Table || role == AccessibilityRole::ColumnHeader || role == AccessibilityRole::RowHeader) + return AccessibilityObjectInclusion::IncludeObject; + + // The object containing the text should implement AtkText itself. + // However, WebCore also maps ARIA's "text" role to the StaticTextRole. + if (role == AccessibilityRole::StaticText) + return ariaRoleAttribute() != AccessibilityRole::Unknown ? AccessibilityObjectInclusion::DefaultBehavior : AccessibilityObjectInclusion::IgnoreObject; + + // Include all list items, regardless they have or not inline children + if (role == AccessibilityRole::ListItem) + return AccessibilityObjectInclusion::IncludeObject; + + // Bullets/numbers for list items shouldn't be exposed as AtkObjects. + if (role == AccessibilityRole::ListMarker) + return AccessibilityObjectInclusion::IgnoreObject; + + // Never expose an unknown object, since AT's won't know what to + // do with them. This is what is done on the Mac as well. + if (role == AccessibilityRole::Unknown) + return AccessibilityObjectInclusion::IgnoreObject; + + if (role == AccessibilityRole::Inline) + return AccessibilityObjectInclusion::IncludeObject; + + // Lines past this point only make sense for AccessibilityRenderObjects. + RenderObject* renderObject = renderer(); + if (!renderObject) + return AccessibilityObjectInclusion::DefaultBehavior; + + // We always want to include paragraphs that have rendered content. + // WebCore Accessibility does so unless there is a RenderBlock child. + if (role == AccessibilityRole::Paragraph) { + auto child = childrenOfType<RenderBlock>(downcast<RenderElement>(*renderObject)).first(); + return child ? AccessibilityObjectInclusion::IncludeObject : AccessibilityObjectInclusion::DefaultBehavior; + } + + // We always want to include table cells (layout and CSS) that have rendered text content. + if (is<RenderTableCell>(renderObject)) { + for (const auto& child : childrenOfType<RenderObject>(downcast<RenderElement>(*renderObject))) { + if (is<RenderInline>(child) || is<RenderText>(child) || is<HTMLSpanElement>(child.node())) + return AccessibilityObjectInclusion::IncludeObject; + } + return AccessibilityObjectInclusion::DefaultBehavior; + } + + if (renderObject->isAnonymousBlock()) { + // The text displayed by an ARIA menu item is exposed through the accessible name. + if (parent->isMenuItem()) + return AccessibilityObjectInclusion::IgnoreObject; + + // The text displayed in headings is typically exposed in the heading itself. + if (parent->isHeading()) + return AccessibilityObjectInclusion::IgnoreObject; + + // The text displayed in list items is typically exposed in the list item itself. + if (parent->isListItem()) + return AccessibilityObjectInclusion::IgnoreObject; + + // The text displayed in links is typically exposed in the link itself. + if (parent->isLink()) + return AccessibilityObjectInclusion::IgnoreObject; + + // FIXME: This next one needs some further consideration. But paragraphs are not + // typically huge (like divs). And ignoring anonymous block children of paragraphs + // will preserve existing behavior. + if (parent->roleValue() == AccessibilityRole::Paragraph) + return AccessibilityObjectInclusion::IgnoreObject; + + return AccessibilityObjectInclusion::DefaultBehavior; + } + + Node* node = renderObject->node(); + if (!node) + return AccessibilityObjectInclusion::DefaultBehavior; + + // We don't want <span> elements to show up in the accessibility hierarchy unless + // we have good reasons for that (e.g. focusable or visible because of containing + // a meaningful accessible name, maybe set through ARIA), so we can use + // atk_component_grab_focus() to set the focus to it. + if (is<HTMLSpanElement>(node) && !canSetFocusAttribute() && !hasAttributesRequiredForInclusion() && !supportsARIAAttributes()) + return AccessibilityObjectInclusion::IgnoreObject; + + // If we include TextControlInnerTextElement children, changes to those children + // will result in focus and text notifications that suggest the user is no longer + // in the control. This can be especially problematic for screen reader users with + // key echo enabled when typing in a password input. + if (is<TextControlInnerTextElement>(node)) + return AccessibilityObjectInclusion::IgnoreObject; + + return AccessibilityObjectInclusion::DefaultBehavior; +} + +bool AccessibilityObject::allowsTextRanges() const +{ + // Check type for the AccessibilityObject. + if (isTextControl() || isWebArea() || isGroup() || isLink() || isHeading() || isListItem() || isTableCell()) + return true; + + // Check roles as the last fallback mechanism. + AccessibilityRole role = roleValue(); + return role == AccessibilityRole::Paragraph || role == AccessibilityRole::Label || role == AccessibilityRole::Div || role == AccessibilityRole::Form || role == AccessibilityRole::Pre; +} + +unsigned AccessibilityObject::getLengthForTextRange() const +{ + unsigned textLength = text().length(); + + if (textLength) + return textLength; + + // Gtk ATs need this for all text objects; not just text controls. + Node* node = this->node(); + RenderObject* renderer = node ? node->renderer() : nullptr; + if (is<RenderText>(renderer)) + textLength = downcast<RenderText>(*renderer).text().length(); + + // Get the text length from the elements under the + // accessibility object if the value is still zero. + if (!textLength && allowsTextRanges()) + textLength = textUnderElement(AccessibilityTextUnderElementMode(AccessibilityTextUnderElementMode::TextUnderElementModeIncludeAllChildren)).length(); + + return textLength; +} + +} // namespace WebCore + +#endif // ENABLE(ACCESSIBILITY) && USE(ATK) diff -urpN webkitgtk-2.38.1.glib244/Source/WebCore/accessibility/atk/AXObjectCacheAtk.cpp webkitgtk-2.38.1/Source/WebCore/accessibility/atk/AXObjectCacheAtk.cpp --- webkitgtk-2.38.1.glib244/Source/WebCore/accessibility/atk/AXObjectCacheAtk.cpp 1969-12-31 18:00:00.000000000 -0600 +++ webkitgtk-2.38.1/Source/WebCore/accessibility/atk/AXObjectCacheAtk.cpp 2022-10-24 13:34:51.913454582 -0500 @@ -0,0 +1,413 @@ +/* + * Copyright (C) 2008 Nuanti Ltd. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include "config.h" +#include "AXObjectCache.h" + +#if ENABLE(ACCESSIBILITY) && USE(ATK) + +#include "AccessibilityObject.h" +#include "AccessibilityRenderObject.h" +#include "Document.h" +#include "Element.h" +#include "HTMLSelectElement.h" +#include "Range.h" +#include "TextIterator.h" +#include "WebKitAccessible.h" +#include <wtf/NeverDestroyed.h> +#include <wtf/glib/GRefPtr.h> +#include <wtf/text/CString.h> + +namespace WebCore { + +static AtkObject* wrapperParent(WebKitAccessible* wrapper) +{ + // Look for the right object to emit the signal from, but using the implementation + // of atk_object_get_parent from AtkObject class (which uses a cached pointer if set) + // since the accessibility hierarchy in WebCore will no longer be navigable. + gpointer webkitAccessibleClass = g_type_class_peek_parent(WEBKIT_ACCESSIBLE_GET_CLASS(wrapper)); + gpointer atkObjectClass = g_type_class_peek_parent(webkitAccessibleClass); + AtkObject* atkParent = ATK_OBJECT_CLASS(atkObjectClass)->get_parent(ATK_OBJECT(wrapper)); + // We don't want to emit any signal from an object outside WebKit's world. + return WEBKIT_IS_ACCESSIBLE(atkParent) ? atkParent : nullptr; +} + +void AXObjectCache::detachWrapper(AXCoreObject* obj, AccessibilityDetachmentType detachmentType) +{ + auto* wrapper = obj->wrapper(); + ASSERT(wrapper); + + // If an object is being detached NOT because of the AXObjectCache being destroyed, + // then it's being removed from the accessibility tree and we should emit a signal. + if (detachmentType != AccessibilityDetachmentType::CacheDestroyed && obj->document() && wrapperParent(wrapper)) + m_deferredDetachedWrapperList.add(wrapper); + + webkitAccessibleDetach(WEBKIT_ACCESSIBLE(wrapper)); +} + +void AXObjectCache::attachWrapper(AXCoreObject* obj) +{ + // FIXME: at the moment, only allow to attach AccessibilityObjects. + if (!is<AccessibilityObject>(obj)) + return; + AccessibilityObject* accessibilityObject = downcast<AccessibilityObject>(obj); + + GRefPtr<WebKitAccessible> wrapper = adoptGRef(webkitAccessibleNew(accessibilityObject)); + accessibilityObject->setWrapper(wrapper.get()); + + // If an object is being attached and we are not in the middle of a layout update, then + // we should report ATs by emitting the children-changed::add signal from the parent. + Document* document = accessibilityObject->document(); + if (!document || document->childNeedsStyleRecalc()) + return; + + // Don't emit the signal when the actual object being added is not going to be exposed. + if (accessibilityObject->accessibilityIsIgnoredByDefault()) + return; + + // Don't emit the signal if the object being added is not -- or not yet -- rendered, + // which can occur in nested iframes. In these instances we don't want to ignore the + // child. But if an assistive technology is listening, AT-SPI2 will attempt to create + // and cache the state set for the child upon emission of the signal. If the object + // has not yet been rendered, this will result in a crash. + if (!accessibilityObject->renderer()) + return; + + m_deferredAttachedWrapperObjectList.add(accessibilityObject); +} + +void AXObjectCache::platformPerformDeferredCacheUpdate() +{ + for (auto& coreObject : m_deferredAttachedWrapperObjectList) { + auto* wrapper = coreObject->wrapper(); + if (!wrapper) + continue; + + // Don't emit the signal for objects whose parents won't be exposed directly. + auto* coreParent = coreObject->parentObjectUnignored(); + if (!coreParent || coreParent->accessibilityIsIgnoredByDefault()) + continue; + + // Look for the right object to emit the signal from. + auto* atkParent = coreParent->wrapper(); + if (!atkParent) + continue; + + size_t index = coreParent->children(false).find(coreObject); + g_signal_emit_by_name(atkParent, "children-changed::add", index != notFound ? index : -1, wrapper); + } + m_deferredAttachedWrapperObjectList.clear(); + + for (auto& wrapper : m_deferredDetachedWrapperList) { + if (auto* atkParent = wrapperParent(wrapper.get())) { + // The accessibility hierarchy is already invalid, so the parent-children relationships + // in the AccessibilityObject tree are not there anymore, so we can't know the offset. + g_signal_emit_by_name(atkParent, "children-changed::remove", -1, wrapper.get()); + } + } + m_deferredDetachedWrapperList.clear(); +} + +static AXCoreObject* getListObject(AXCoreObject* object) +{ + // Only list boxes and menu lists supported so far. + if (!object->isListBox() && !object->isMenuList()) + return 0; + + // For list boxes the list object is just itself. + if (object->isListBox()) + return object; + + // For menu lists we need to return the first accessible child, + // with role MenuListPopupRole, since that's the one holding the list + // of items with role MenuListOptionRole. + const AccessibilityObject::AccessibilityChildrenVector& children = object->children(); + if (!children.size()) + return 0; + + AXCoreObject* listObject = children.at(0).get(); + if (!listObject->isMenuListPopup()) + return 0; + + return listObject; +} + +static void notifyChildrenSelectionChange(AXCoreObject* object) +{ + // This static variables are needed to keep track of the old + // focused object and its associated list object, as per previous + // calls to this function, in order to properly decide whether to + // emit some signals or not. + static NeverDestroyed<RefPtr<AXCoreObject>> oldListObject; + static NeverDestroyed<RefPtr<AXCoreObject>> oldFocusedObject; + + // Only list boxes and menu lists supported so far. + if (!object || !(object->isListBox() || object->isMenuList())) + return; + + // Only support HTML select elements so far (ARIA selectors not supported). + Node* node = object->node(); + if (!is<HTMLSelectElement>(node)) + return; + + // Emit signal from the listbox's point of view first. + g_signal_emit_by_name(object->wrapper(), "selection-changed"); + + // Find the item where the selection change was triggered from. + HTMLSelectElement& select = downcast<HTMLSelectElement>(*node); + int changedItemIndex = select.activeSelectionStartListIndex(); + + AXCoreObject* listObject = getListObject(object); + if (!listObject) { + oldListObject.get() = nullptr; + return; + } + + const AccessibilityObject::AccessibilityChildrenVector& items = listObject->children(); + if (changedItemIndex < 0 || changedItemIndex >= static_cast<int>(items.size())) + return; + AXCoreObject* item = items.at(changedItemIndex).get(); + + // Ensure the current list object is the same than the old one so + // further comparisons make sense. Otherwise, just reset + // oldFocusedObject so it won't be taken into account. + if (oldListObject.get() != listObject) + oldFocusedObject.get() = nullptr; + + WebKitAccessible* axItem = item ? item->wrapper() : nullptr; + WebKitAccessible* axOldFocusedObject = oldFocusedObject.get() ? oldFocusedObject.get()->wrapper() : nullptr; + + // Old focused object just lost focus, so emit the events. + if (axOldFocusedObject && axItem != axOldFocusedObject) { + g_signal_emit_by_name(axOldFocusedObject, "focus-event", false); + atk_object_notify_state_change(ATK_OBJECT(axOldFocusedObject), ATK_STATE_FOCUSED, false); + } + + // Emit needed events for the currently (un)selected item. + if (axItem) { + bool isSelected = item->isSelected(); + atk_object_notify_state_change(ATK_OBJECT(axItem), ATK_STATE_SELECTED, isSelected); + // When the selection changes in a collapsed widget such as a combo box + // whose child menu is not showing, that collapsed widget retains focus. + if (!object->isCollapsed()) { + g_signal_emit_by_name(axItem, "focus-event", isSelected); + atk_object_notify_state_change(ATK_OBJECT(axItem), ATK_STATE_FOCUSED, isSelected); + } + } + + // Update pointers to the previously involved objects. + oldListObject.get() = listObject; + oldFocusedObject.get() = item; +} + +void AXObjectCache::postPlatformNotification(AXCoreObject* coreObject, AXNotification notification) +{ + auto* axObject = ATK_OBJECT(coreObject->wrapper()); + if (!axObject) + return; + + switch (notification) { + case AXCheckedStateChanged: + if (!coreObject->isCheckboxOrRadio() && !coreObject->isSwitch()) + return; + atk_object_notify_state_change(axObject, ATK_STATE_CHECKED, coreObject->isChecked()); + break; + + case AXSelectedChildrenChanged: + case AXMenuListValueChanged: + // Accessible focus claims should not be made if the associated widget is not focused. + if (notification == AXMenuListValueChanged && coreObject->isMenuList() && coreObject->isFocused()) { + g_signal_emit_by_name(axObject, "focus-event", true); + atk_object_notify_state_change(axObject, ATK_STATE_FOCUSED, true); + } + notifyChildrenSelectionChange(coreObject); + break; + + case AXValueChanged: + if (ATK_IS_VALUE(axObject)) { + AtkPropertyValues propertyValues; + propertyValues.property_name = "accessible-value"; + + memset(&propertyValues.new_value, 0, sizeof(GValue)); + + double value; + atk_value_get_value_and_text(ATK_VALUE(axObject), &value, nullptr); + g_value_set_double(g_value_init(&propertyValues.new_value, G_TYPE_DOUBLE), value); + + g_signal_emit_by_name(axObject, "property-change::accessible-value", &propertyValues, NULL); + } + break; + + case AXInvalidStatusChanged: + atk_object_notify_state_change(axObject, ATK_STATE_INVALID_ENTRY, coreObject->invalidStatus() != "false"_s); + break; + + case AXElementBusyChanged: + atk_object_notify_state_change(axObject, ATK_STATE_BUSY, coreObject->isBusy()); + break; + + case AXCurrentStateChanged: + atk_object_notify_state_change(axObject, ATK_STATE_ACTIVE, coreObject->currentState() != AccessibilityCurrentState::False); + break; + + case AXRowExpanded: + atk_object_notify_state_change(axObject, ATK_STATE_EXPANDED, true); + break; + + case AXRowCollapsed: + atk_object_notify_state_change(axObject, ATK_STATE_EXPANDED, false); + break; + + case AXExpandedChanged: + atk_object_notify_state_change(axObject, ATK_STATE_EXPANDED, coreObject->isExpanded()); + break; + + case AXDisabledStateChanged: { + bool enabledState = coreObject->isEnabled(); + atk_object_notify_state_change(axObject, ATK_STATE_ENABLED, enabledState); + atk_object_notify_state_change(axObject, ATK_STATE_SENSITIVE, enabledState); + break; + } + + case AXPressedStateChanged: + atk_object_notify_state_change(axObject, ATK_STATE_PRESSED, coreObject->isPressed()); + break; + + case AXReadOnlyStatusChanged: + atk_object_notify_state_change(axObject, ATK_STATE_READ_ONLY, !coreObject->canSetValueAttribute()); + break; + + case AXRequiredStatusChanged: + atk_object_notify_state_change(axObject, ATK_STATE_REQUIRED, coreObject->isRequired()); + break; + + case AXActiveDescendantChanged: + if (AXCoreObject* descendant = coreObject->activeDescendant()) + platformHandleFocusedUIElementChanged(nullptr, descendant->node()); + break; + + default: + break; + } +} + +void AXObjectCache::nodeTextChangePlatformNotification(AccessibilityObject* object, AXTextChange textChange, unsigned offset, const String& text) +{ + if (!object || text.isEmpty()) + return; + + AXCoreObject* parentObject = object->isNonNativeTextControl() ? object : object->parentObjectUnignored(); + if (!parentObject) + return; + + auto* wrapper = parentObject->wrapper(); + if (!wrapper || !ATK_IS_TEXT(wrapper)) + return; + + Node* node = object->node(); + if (!node) + return; + + // Ensure document's layout is up-to-date before using TextIterator. + Document& document = node->document(); + document.updateLayout(); + + // Select the right signal to be emitted + CString detail; + switch (textChange) { + case AXTextInserted: + detail = "text-insert"; + break; + case AXTextDeleted: + detail = "text-remove"; + break; + case AXTextAttributesChanged: + detail = "text-attributes-changed"; + break; + } + + String textToEmit = text; + unsigned offsetToEmit = offset; + + // If the object we're emitting the signal from represents a + // password field, we will emit the masked text. + if (parentObject->isPasswordField()) { + String maskedText = parentObject->passwordFieldValue(); + textToEmit = maskedText.substring(offset, text.length()); + } else { + // Consider previous text objects that might be present for + // the current accessibility object to ensure we emit the + // right offset (e.g. multiline text areas). + offsetToEmit = offset + characterCount(SimpleRange { { *node->parentNode(), 0 }, { *node, 0 } }); + } + + g_signal_emit_by_name(wrapper, detail.data(), offsetToEmit, textToEmit.length(), textToEmit.utf8().data()); +} + +void AXObjectCache::frameLoadingEventPlatformNotification(AccessibilityObject* object, AXLoadingEvent loadingEvent) +{ + if (!object) + return; + + auto* axObject = ATK_OBJECT(object->wrapper()); + if (!axObject || !ATK_IS_DOCUMENT(axObject)) + return; + + switch (loadingEvent) { + case AXObjectCache::AXLoadingStarted: + atk_object_notify_state_change(axObject, ATK_STATE_BUSY, true); + break; + case AXObjectCache::AXLoadingReloaded: + atk_object_notify_state_change(axObject, ATK_STATE_BUSY, true); + g_signal_emit_by_name(axObject, "reload"); + break; + case AXObjectCache::AXLoadingFailed: + g_signal_emit_by_name(axObject, "load-stopped"); + atk_object_notify_state_change(axObject, ATK_STATE_BUSY, false); + break; + case AXObjectCache::AXLoadingFinished: + g_signal_emit_by_name(axObject, "load-complete"); + atk_object_notify_state_change(axObject, ATK_STATE_BUSY, false); + break; + } +} + +void AXObjectCache::platformHandleFocusedUIElementChanged(Node* oldFocusedNode, Node* newFocusedNode) +{ + RefPtr<AccessibilityObject> oldObject = getOrCreate(oldFocusedNode); + if (oldObject) { + auto* axObject = oldObject->wrapper(); + g_signal_emit_by_name(axObject, "focus-event", false); + atk_object_notify_state_change(ATK_OBJECT(axObject), ATK_STATE_FOCUSED, false); + } + RefPtr<AccessibilityObject> newObject = getOrCreate(newFocusedNode); + if (newObject) { + auto* axObject = newObject->wrapper(); + g_signal_emit_by_name(axObject, "focus-event", true); + atk_object_notify_state_change(ATK_OBJECT(axObject), ATK_STATE_FOCUSED, true); + } +} + +void AXObjectCache::handleScrolledToAnchor(const Node*) +{ +} + +} // namespace WebCore + +#endif diff -urpN webkitgtk-2.38.1.glib244/Source/WebCore/accessibility/atk/WebKitAccessible.cpp webkitgtk-2.38.1/Source/WebCore/accessibility/atk/WebKitAccessible.cpp --- webkitgtk-2.38.1.glib244/Source/WebCore/accessibility/atk/WebKitAccessible.cpp 1969-12-31 18:00:00.000000000 -0600 +++ webkitgtk-2.38.1/Source/WebCore/accessibility/atk/WebKitAccessible.cpp 2022-10-24 13:34:51.917454604 -0500 @@ -0,0 +1,1386 @@ +/* + * Copyright (C) 2008 Nuanti Ltd. + * Copyright (C) 2009 Jan Alonzo + * Copyright (C) 2009, 2010, 2011, 2012, 2019 Igalia S.L. + * Copyright (C) 2013 Samsung Electronics + * + * Portions from Mozilla a11y, copyright as follows: + * + * The Original Code is mozilla.org code. + * + * The Initial Developer of the Original Code is + * Sun Microsystems, Inc. + * Portions created by the Initial Developer are Copyright (C) 2002 + * the Initial Developer. All Rights Reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include "config.h" +#include "WebKitAccessible.h" + +#if ENABLE(ACCESSIBILITY) && USE(ATK) + +#include "AXObjectCache.h" +#include "AccessibilityList.h" +#include "AccessibilityListBoxOption.h" +#include "AccessibilityTable.h" +#include "AccessibilityTableCell.h" +#include "AccessibilityTableRow.h" +#include "Document.h" +#include "Editing.h" +#include "ElementInlines.h" +#include "Frame.h" +#include "FrameView.h" +#include "HTMLNames.h" +#include "HTMLTableElement.h" +#include "HostWindow.h" +#include "RenderAncestorIterator.h" +#include "RenderBlock.h" +#include "RenderObject.h" +#include "SVGElement.h" +#include "Settings.h" +#include "TextIterator.h" +#include "VisibleUnits.h" +#include "WebKitAccessibleHyperlink.h" +#include "WebKitAccessibleInterfaceAction.h" +#include "WebKitAccessibleInterfaceComponent.h" +#include "WebKitAccessibleInterfaceDocument.h" +#include "WebKitAccessibleInterfaceEditableText.h" +#include "WebKitAccessibleInterfaceHyperlinkImpl.h" +#include "WebKitAccessibleInterfaceHypertext.h" +#include "WebKitAccessibleInterfaceImage.h" +#include "WebKitAccessibleInterfaceSelection.h" +#include "WebKitAccessibleInterfaceTable.h" +#include "WebKitAccessibleInterfaceTableCell.h" +#include "WebKitAccessibleInterfaceText.h" +#include "WebKitAccessibleInterfaceValue.h" +#include "WebKitAccessibleUtil.h" +#include <glib/gprintf.h> +#include <wtf/glib/WTFGType.h> +#include <wtf/text/CString.h> + +using namespace WebCore; + +struct _WebKitAccessiblePrivate { + AccessibilityObject* object; + + // Cached data for AtkObject. + CString accessibleName; + CString accessibleDescription; + + // Cached data for AtkAction. + CString actionName; + CString actionKeyBinding; + + // Cached data for AtkDocument. + CString documentLocale; + CString documentType; + CString documentEncoding; + CString documentURI; + + // Cached data for AtkImage. + CString imageDescription; +}; + +WEBKIT_DEFINE_TYPE(WebKitAccessible, webkit_accessible, ATK_TYPE_OBJECT) + +static const gchar* webkitAccessibleGetName(AtkObject* object) +{ + auto* accessible = WEBKIT_ACCESSIBLE(object); + returnValIfWebKitAccessibleIsInvalid(accessible, nullptr); + + if (!accessible->priv->object) + return ""; + + Vector<AccessibilityText> textOrder; + accessible->priv->object->accessibilityText(textOrder); + + for (const auto& text : textOrder) { + // FIXME: This check is here because AccessibilityNodeObject::titleElementText() + // appends an empty String for the LabelByElementText source when there is a + // titleUIElement(). Removing this check makes some fieldsets lose their name. + if (text.text.isEmpty()) + continue; + + // WebCore Accessibility should provide us with the text alternative computation + // in the order defined by that spec. So take the first thing that our platform + // does not expose via the AtkObject description. + if (text.textSource != AccessibilityTextSource::Help && text.textSource != AccessibilityTextSource::Summary) + return webkitAccessibleCacheAndReturnAtkProperty(accessible, AtkCachedAccessibleName, text.text.utf8()); + } + + return webkitAccessibleCacheAndReturnAtkProperty(accessible, AtkCachedAccessibleName, ""); +} + +static const gchar* webkitAccessibleGetDescription(AtkObject* object) +{ + auto* accessible = WEBKIT_ACCESSIBLE(object); + returnValIfWebKitAccessibleIsInvalid(accessible, nullptr); + + if (!accessible->priv->object) + return ""; + + Vector<AccessibilityText> textOrder; + accessible->priv->object->accessibilityText(textOrder); + + bool nameTextAvailable = false; + for (const auto& text : textOrder) { + // WebCore Accessibility should provide us with the text alternative computation + // in the order defined by that spec. So take the first thing that our platform + // does not expose via the AtkObject name. + if (text.textSource == AccessibilityTextSource::Help || text.textSource == AccessibilityTextSource::Summary) + return webkitAccessibleCacheAndReturnAtkProperty(accessible, AtkCachedAccessibleDescription, text.text.utf8()); + + // If there is no other text alternative, the title tag contents will have been + // used for the AtkObject name. We don't want to duplicate it here. + if (text.textSource == AccessibilityTextSource::TitleTag && nameTextAvailable) + return webkitAccessibleCacheAndReturnAtkProperty(accessible, AtkCachedAccessibleDescription, text.text.utf8()); + + nameTextAvailable = true; + } + + return webkitAccessibleCacheAndReturnAtkProperty(accessible, AtkCachedAccessibleDescription, ""); +} + +static void removeAtkRelationByType(AtkRelationSet* relationSet, AtkRelationType relationType) +{ + int count = atk_relation_set_get_n_relations(relationSet); + for (int i = 0; i < count; i++) { + AtkRelation* relation = atk_relation_set_get_relation(relationSet, i); + if (atk_relation_get_relation_type(relation) == relationType) { + atk_relation_set_remove(relationSet, relation); + break; + } + } +} + +static void setAtkRelationSetFromCoreObject(AccessibilityObject* coreObject, AtkRelationSet* relationSet) +{ + // Elements with aria-labelledby should have the labelled-by relation as per the ARIA AAM spec. + // Controls with a label element and fieldsets with a legend element should also use this relation + // as per the HTML AAM spec. The reciprocal label-for relation should also be used. + removeAtkRelationByType(relationSet, ATK_RELATION_LABELLED_BY); + removeAtkRelationByType(relationSet, ATK_RELATION_LABEL_FOR); + if (coreObject->isControl()) { + if (AccessibilityObject* label = coreObject->correspondingLabelForControlElement()) + atk_relation_set_add_relation_by_type(relationSet, ATK_RELATION_LABELLED_BY, ATK_OBJECT(label->wrapper())); + } else if (coreObject->isFieldset()) { + if (AccessibilityObject* label = coreObject->titleUIElement()) + atk_relation_set_add_relation_by_type(relationSet, ATK_RELATION_LABELLED_BY, ATK_OBJECT(label->wrapper())); + } else if (coreObject->roleValue() == AccessibilityRole::Legend) { + if (RenderBlock* renderFieldset = ancestorsOfType<RenderBlock>(*coreObject->renderer()).first()) { + if (renderFieldset->isFieldset()) { + AccessibilityObject* fieldset = coreObject->axObjectCache()->getOrCreate(renderFieldset); + atk_relation_set_add_relation_by_type(relationSet, ATK_RELATION_LABEL_FOR, ATK_OBJECT(fieldset->wrapper())); + } + } + } else if (AccessibilityObject* control = coreObject->correspondingControlForLabelElement()) + atk_relation_set_add_relation_by_type(relationSet, ATK_RELATION_LABEL_FOR, ATK_OBJECT(control->wrapper())); + else { + AccessibilityObject::AccessibilityChildrenVector ariaLabelledByElements; + ariaLabelledByElements = coreObject->labelledByObjects(); + for (const auto& accessibilityObject : ariaLabelledByElements) + atk_relation_set_add_relation_by_type(relationSet, ATK_RELATION_LABELLED_BY, ATK_OBJECT(accessibilityObject->wrapper())); + } + + // Elements referenced by aria-labelledby should have the label-for relation as per the ARIA AAM spec. + AccessibilityObject::AccessibilityChildrenVector labels; + labels = coreObject->labelForObjects(); + for (const auto& accessibilityObject : labels) + atk_relation_set_add_relation_by_type(relationSet, ATK_RELATION_LABEL_FOR, ATK_OBJECT(accessibilityObject->wrapper())); + + // Elements with aria-flowto should have the flows-to relation as per the ARIA AAM spec. + removeAtkRelationByType(relationSet, ATK_RELATION_FLOWS_TO); + AccessibilityObject::AccessibilityChildrenVector ariaFlowToElements; + ariaFlowToElements = coreObject->flowToObjects(); + for (const auto& accessibilityObject : ariaFlowToElements) + atk_relation_set_add_relation_by_type(relationSet, ATK_RELATION_FLOWS_TO, ATK_OBJECT(accessibilityObject->wrapper())); + + // Elements referenced by aria-flowto should have the flows-from relation as per the ARIA AAM spec. + removeAtkRelationByType(relationSet, ATK_RELATION_FLOWS_FROM); + AccessibilityObject::AccessibilityChildrenVector flowFrom; + flowFrom = coreObject->flowFromObjects(); + for (const auto& accessibilityObject : flowFrom) + atk_relation_set_add_relation_by_type(relationSet, ATK_RELATION_FLOWS_FROM, ATK_OBJECT(accessibilityObject->wrapper())); + + // Elements with aria-describedby should have the described-by relation as per the ARIA AAM spec. + removeAtkRelationByType(relationSet, ATK_RELATION_DESCRIBED_BY); + AccessibilityObject::AccessibilityChildrenVector ariaDescribedByElements; + ariaDescribedByElements = coreObject->describedByObjects(); + for (const auto& accessibilityObject : ariaDescribedByElements) + atk_relation_set_add_relation_by_type(relationSet, ATK_RELATION_DESCRIBED_BY, ATK_OBJECT(accessibilityObject->wrapper())); + + // Elements referenced by aria-describedby should have the description-for relation as per the ARIA AAM spec. + removeAtkRelationByType(relationSet, ATK_RELATION_DESCRIPTION_FOR); + AccessibilityObject::AccessibilityChildrenVector describers; + describers = coreObject->descriptionForObjects(); + for (const auto& accessibilityObject : describers) + atk_relation_set_add_relation_by_type(relationSet, ATK_RELATION_DESCRIPTION_FOR, ATK_OBJECT(accessibilityObject->wrapper())); + + // Elements with aria-controls should have the controller-for relation as per the ARIA AAM spec. + removeAtkRelationByType(relationSet, ATK_RELATION_CONTROLLER_FOR); + AccessibilityObject::AccessibilityChildrenVector ariaControls; + ariaControls = coreObject->controlledObjects(); + for (const auto& accessibilityObject : ariaControls) + atk_relation_set_add_relation_by_type(relationSet, ATK_RELATION_CONTROLLER_FOR, ATK_OBJECT(accessibilityObject->wrapper())); + + // Elements referenced by aria-controls should have the controlled-by relation as per the ARIA AAM spec. + removeAtkRelationByType(relationSet, ATK_RELATION_CONTROLLED_BY); + AccessibilityObject::AccessibilityChildrenVector controllers; + controllers = coreObject->controllers(); + for (const auto& accessibilityObject : controllers) + atk_relation_set_add_relation_by_type(relationSet, ATK_RELATION_CONTROLLED_BY, ATK_OBJECT(accessibilityObject->wrapper())); + + // Elements with aria-owns should have the node-parent-of relation as per the ARIA AAM spec. + removeAtkRelationByType(relationSet, ATK_RELATION_NODE_PARENT_OF); + AccessibilityObject::AccessibilityChildrenVector ariaOwns; + ariaOwns = coreObject->ownedObjects(); + for (const auto& accessibilityObject : ariaOwns) + atk_relation_set_add_relation_by_type(relationSet, ATK_RELATION_NODE_PARENT_OF, ATK_OBJECT(accessibilityObject->wrapper())); + + // Elements referenced by aria-owns should have the node-child-of relation as per the ARIA AAM spec. + removeAtkRelationByType(relationSet, ATK_RELATION_NODE_CHILD_OF); + AccessibilityObject::AccessibilityChildrenVector owners; + owners = coreObject->owners(); + for (const auto& accessibilityObject : owners) + atk_relation_set_add_relation_by_type(relationSet, ATK_RELATION_NODE_CHILD_OF, ATK_OBJECT(accessibilityObject->wrapper())); + +#if ATK_CHECK_VERSION(2, 25, 2) + // Elements with aria-details should have the details relation as per the ARIA AAM spec. + removeAtkRelationByType(relationSet, ATK_RELATION_DETAILS); + AccessibilityObject::AccessibilityChildrenVector ariaDetails; + ariaDetails = coreObject->detailsForObjects(); + for (const auto& accessibilityObject : ariaDetails) + atk_relation_set_add_relation_by_type(relationSet, ATK_RELATION_DETAILS, ATK_OBJECT(accessibilityObject->wrapper())); + + // Elements referenced by aria-details should have the details-for relation as per the ARIA AAM spec. + removeAtkRelationByType(relationSet, ATK_RELATION_DETAILS_FOR); + AccessibilityObject::AccessibilityChildrenVector details; + details = coreObject->detailsForObjects(); + for (const auto& accessibilityObject : details) + atk_relation_set_add_relation_by_type(relationSet, ATK_RELATION_DETAILS_FOR, ATK_OBJECT(accessibilityObject->wrapper())); + + // Elements with aria-errormessage should have the error-message relation as per the ARIA AAM spec. + removeAtkRelationByType(relationSet, ATK_RELATION_ERROR_MESSAGE); + AccessibilityObject::AccessibilityChildrenVector ariaErrorMessage; + ariaErrorMessage = coreObject->errorMessageObjects(); + for (const auto& accessibilityObject : ariaErrorMessage) + atk_relation_set_add_relation_by_type(relationSet, ATK_RELATION_ERROR_MESSAGE, ATK_OBJECT(accessibilityObject->wrapper())); + + // Elements referenced by aria-errormessage should have the error-for relation as per the ARIA AAM spec. + removeAtkRelationByType(relationSet, ATK_RELATION_ERROR_FOR); + AccessibilityObject::AccessibilityChildrenVector errors; + errors = coreObject->errorMessageForObjects(); + for (const auto& accessibilityObject : errors) + atk_relation_set_add_relation_by_type(relationSet, ATK_RELATION_ERROR_FOR, ATK_OBJECT(accessibilityObject->wrapper())); +#endif +} + +static bool isRootObject(AccessibilityObject* coreObject) +{ + // The root accessible object in WebCore is always an object with + // the ScrolledArea role with one child with the WebArea role. + if (!coreObject || !coreObject->isScrollView()) + return false; + + AccessibilityObject* firstChild = coreObject->firstChild(); + return firstChild && firstChild->isWebArea(); +} + +static AtkObject* webkitAccessibleGetParent(AtkObject* object) +{ + auto* accessible = WEBKIT_ACCESSIBLE(object); + returnValIfWebKitAccessibleIsInvalid(accessible, nullptr); + + // Check first if the parent has been already set. + AtkObject* accessibleParent = ATK_OBJECT_CLASS(webkit_accessible_parent_class)->get_parent(object); + if (accessibleParent) + return accessibleParent; + + // Parent not set yet, so try to find it in the hierarchy. + auto* coreObject = accessible->priv->object; + if (!coreObject) + return nullptr; + auto* coreParent = coreObject->parentObjectUnignored(); + if (!coreParent && isRootObject(coreObject)) { + // The top level object claims to not have a parent. This makes it + // impossible for assistive technologies to ascend the accessible + // hierarchy all the way to the application. (Bug 30489) + if (!coreObject->document()) + return nullptr; + } + + return coreParent ? ATK_OBJECT(coreParent->wrapper()) : nullptr; +} + +static gint webkitAccessibleGetNChildren(AtkObject* object) +{ + auto* accessible = WEBKIT_ACCESSIBLE(object); + returnValIfWebKitAccessibleIsInvalid(accessible, 0); + + if (!accessible->priv->object) + return 0; + return accessible->priv->object->children().size(); +} + +static AtkObject* webkitAccessibleRefChild(AtkObject* object, gint index) +{ + auto* accessible = WEBKIT_ACCESSIBLE(object); + returnValIfWebKitAccessibleIsInvalid(accessible, nullptr); + + if (!accessible->priv->object || index < 0) + return nullptr; + + const auto& children = accessible->priv->object->children(); + if (static_cast<size_t>(index) >= children.size()) + return nullptr; + + auto& coreChild = children[index]; + if (!coreChild) + return nullptr; + + auto* child = coreChild->wrapper(); + if (!child) + return nullptr; + + atk_object_set_parent(ATK_OBJECT(child), object); + return ATK_OBJECT(g_object_ref(child)); +} + +static gint webkitAccessibleGetIndexInParent(AtkObject* object) +{ + auto* accessible = WEBKIT_ACCESSIBLE(object); + returnValIfWebKitAccessibleIsInvalid(accessible, -1); + + auto* coreObject = accessible->priv->object; + if (!coreObject) + return -1; + auto* parent = coreObject->parentObjectUnignored(); + if (!parent && isRootObject(coreObject)) { + if (!coreObject->document()) + return -1; + + auto* atkParent = parent ? ATK_OBJECT(parent->wrapper()) : nullptr; + if (!atkParent) + return -1; + + unsigned count = atk_object_get_n_accessible_children(atkParent); + for (unsigned i = 0; i < count; ++i) { + GRefPtr<AtkObject> child = adoptGRef(atk_object_ref_accessible_child(atkParent, i)); + if (child.get() == object) + return i; + } + } + + if (!parent) + return -1; + + size_t index = parent->children().find(coreObject); + return (index == notFound) ? -1 : index; +} + +static AtkAttributeSet* webkitAccessibleGetAttributes(AtkObject* object) +{ + auto* accessible = WEBKIT_ACCESSIBLE(object); + returnValIfWebKitAccessibleIsInvalid(accessible, nullptr); + + AtkAttributeSet* attributeSet = nullptr; +#if PLATFORM(GTK) + attributeSet = addToAtkAttributeSet(attributeSet, "toolkit", "WebKitGtk"); +#elif PLATFORM(WPE) + attributeSet = addToAtkAttributeSet(attributeSet, "toolkit", "WPEWebKit"); +#endif + + auto* coreObject = accessible->priv->object; + if (!coreObject) + return attributeSet; + + // Hack needed for WebKit2 tests because obtaining an element by its ID + // cannot be done from the UIProcess. Assistive technologies have no need + // for this information. + Element* element = coreObject->element() ? coreObject->element() : coreObject->actionElement(); + if (element) { + String tagName = element->tagName(); + if (!tagName.isEmpty()) + attributeSet = addToAtkAttributeSet(attributeSet, "tag", tagName.convertToASCIILowercase().utf8().data()); + String id = element->getIdAttribute().string(); + if (!id.isEmpty()) + attributeSet = addToAtkAttributeSet(attributeSet, "html-id", id.utf8().data()); + } + + int level = coreObject->isHeading() ? coreObject->headingLevel() : coreObject->hierarchicalLevel(); + if (level) { + String value = String::number(level); + attributeSet = addToAtkAttributeSet(attributeSet, "level", value.utf8().data()); + } + + if (coreObject->roleValue() == AccessibilityRole::MathElement) { + if (coreObject->isMathMultiscriptObject(AccessibilityMathMultiscriptObjectType::PreSuperscript) || coreObject->isMathMultiscriptObject(AccessibilityMathMultiscriptObjectType::PreSubscript)) + attributeSet = addToAtkAttributeSet(attributeSet, "multiscript-type", "pre"); + else if (coreObject->isMathMultiscriptObject(AccessibilityMathMultiscriptObjectType::PostSuperscript) || coreObject->isMathMultiscriptObject(AccessibilityMathMultiscriptObjectType::PostSubscript)) + attributeSet = addToAtkAttributeSet(attributeSet, "multiscript-type", "post"); + } + + if (is<AccessibilityTable>(*coreObject) && downcast<AccessibilityTable>(*coreObject).isExposable()) { + auto& table = downcast<AccessibilityTable>(*coreObject); + int rowCount = table.axRowCount(); + if (rowCount) + attributeSet = addToAtkAttributeSet(attributeSet, "rowcount", String::number(rowCount).utf8().data()); + + int columnCount = table.axColumnCount(); + if (columnCount) + attributeSet = addToAtkAttributeSet(attributeSet, "colcount", String::number(columnCount).utf8().data()); + } else if (is<AccessibilityTableRow>(*coreObject)) { + auto& row = downcast<AccessibilityTableRow>(*coreObject); + int rowIndex = row.axRowIndex(); + if (rowIndex != -1) + attributeSet = addToAtkAttributeSet(attributeSet, "rowindex", String::number(rowIndex).utf8().data()); + } else if (is<AccessibilityTableCell>(*coreObject)) { + auto& cell = downcast<AccessibilityTableCell>(*coreObject); + int rowIndex = cell.axRowIndex(); + if (rowIndex != -1) + attributeSet = addToAtkAttributeSet(attributeSet, "rowindex", String::number(rowIndex).utf8().data()); + + int columnIndex = cell.axColumnIndex(); + if (columnIndex != -1) + attributeSet = addToAtkAttributeSet(attributeSet, "colindex", String::number(columnIndex).utf8().data()); + + int rowSpan = cell.axRowSpan(); + if (rowSpan != -1) + attributeSet = addToAtkAttributeSet(attributeSet, "rowspan", String::number(rowSpan).utf8().data()); + + int columnSpan = cell.axColumnSpan(); + if (columnSpan != -1) + attributeSet = addToAtkAttributeSet(attributeSet, "colspan", String::number(columnSpan).utf8().data()); + } + + String placeholder = coreObject->placeholderValue(); + if (!placeholder.isEmpty()) + attributeSet = addToAtkAttributeSet(attributeSet, "placeholder-text", placeholder.utf8().data()); + + if (coreObject->supportsAutoComplete()) + attributeSet = addToAtkAttributeSet(attributeSet, "autocomplete", coreObject->autoCompleteValue().utf8().data()); + + if (coreObject->supportsHasPopup()) + attributeSet = addToAtkAttributeSet(attributeSet, "haspopup", coreObject->popupValue().utf8().data()); + + if (coreObject->supportsCurrent()) + attributeSet = addToAtkAttributeSet(attributeSet, "current", coreObject->currentValue().utf8().data()); + + // The Core AAM states that an explicitly-set value should be exposed, including "none". + if (coreObject->hasAttribute(HTMLNames::aria_sortAttr)) { + switch (coreObject->sortDirection()) { + case AccessibilitySortDirection::Invalid: + break; + case AccessibilitySortDirection::Ascending: + attributeSet = addToAtkAttributeSet(attributeSet, "sort", "ascending"); + break; + case AccessibilitySortDirection::Descending: + attributeSet = addToAtkAttributeSet(attributeSet, "sort", "descending"); + break; + case AccessibilitySortDirection::Other: + attributeSet = addToAtkAttributeSet(attributeSet, "sort", "other"); + break; + case AccessibilitySortDirection::None: + attributeSet = addToAtkAttributeSet(attributeSet, "sort", "none"); + } + } + + if (coreObject->supportsPosInSet()) + attributeSet = addToAtkAttributeSet(attributeSet, "posinset", String::number(coreObject->posInSet()).utf8().data()); + + if (coreObject->supportsSetSize()) + attributeSet = addToAtkAttributeSet(attributeSet, "setsize", String::number(coreObject->setSize()).utf8().data()); + + String isReadOnly = coreObject->readOnlyValue(); + if (!isReadOnly.isEmpty()) + attributeSet = addToAtkAttributeSet(attributeSet, "readonly", isReadOnly.utf8().data()); + + String valueDescription = coreObject->valueDescription(); + if (!valueDescription.isEmpty()) + attributeSet = addToAtkAttributeSet(attributeSet, "valuetext", valueDescription.utf8().data()); + + // According to the W3C Core Accessibility API Mappings 1.1, section 5.4.1 General Rules: + // "User agents must expose the WAI-ARIA role string if the API supports a mechanism to do so." + // In the case of ATK, the mechanism to do so is an object attribute pair (xml-roles:"string"). + // We cannot use the computedRoleString for this purpose because it is not limited to elements + // with ARIA roles, and it might not contain the actual ARIA role value (e.g. DPub ARIA). + String roleString = coreObject->getAttribute(HTMLNames::roleAttr); + if (!roleString.isEmpty()) + attributeSet = addToAtkAttributeSet(attributeSet, "xml-roles", roleString.utf8().data()); + + String computedRoleString = coreObject->computedRoleString(); + if (!computedRoleString.isEmpty()) { + attributeSet = addToAtkAttributeSet(attributeSet, "computed-role", computedRoleString.utf8().data()); + + // The HTML AAM maps several elements to ARIA landmark roles. In order for the type of landmark + // to be obtainable in the same fashion as an ARIA landmark, fall back on the computedRoleString. + // We also want to do this for the style-format-group element types so that the type of format + // group it is doesn't get lost to a generic platform role. + if (coreObject->ariaRoleAttribute() == AccessibilityRole::Unknown + && (coreObject->isLandmark() || coreObject->isStyleFormatGroup())) + attributeSet = addToAtkAttributeSet(attributeSet, "xml-roles", computedRoleString.utf8().data()); + } + + String roleDescription = coreObject->roleDescription(); + if (!roleDescription.isEmpty()) + attributeSet = addToAtkAttributeSet(attributeSet, "roledescription", roleDescription.utf8().data()); + + // We need to expose the live region attributes even if the live region is currently disabled/off. + if (auto liveContainer = coreObject->liveRegionAncestor(false)) { + String liveStatus = liveContainer->liveRegionStatus(); + String relevant = liveContainer->liveRegionRelevant(); + bool isAtom = liveContainer->liveRegionAtomic(); + String liveRole = roleString.isEmpty() ? computedRoleString : roleString; + + // According to the Core AAM, we need to expose the above properties with "container-" prefixed + // object attributes regardless of whether the container is this object, or an ancestor of it. + attributeSet = addToAtkAttributeSet(attributeSet, "container-live", liveStatus.utf8().data()); + attributeSet = addToAtkAttributeSet(attributeSet, "container-relevant", relevant.utf8().data()); + if (isAtom) + attributeSet = addToAtkAttributeSet(attributeSet, "container-atomic", "true"); + if (!liveRole.isEmpty()) + attributeSet = addToAtkAttributeSet(attributeSet, "container-live-role", liveRole.utf8().data()); + + // According to the Core AAM, if this object is the live region (rather than its descendant), + // we must expose the above properties on the object without a "container-" prefix. + if (liveContainer == coreObject) { + attributeSet = addToAtkAttributeSet(attributeSet, "live", liveStatus.utf8().data()); + attributeSet = addToAtkAttributeSet(attributeSet, "relevant", relevant.utf8().data()); + if (isAtom) + attributeSet = addToAtkAttributeSet(attributeSet, "atomic", "true"); + } else if (!isAtom && coreObject->liveRegionAtomic()) + attributeSet = addToAtkAttributeSet(attributeSet, "atomic", "true"); + } + + // The Core AAM states the author-provided value should be exposed as-is. + String dropEffect = coreObject->getAttribute(HTMLNames::aria_dropeffectAttr); + if (!dropEffect.isEmpty()) + attributeSet = addToAtkAttributeSet(attributeSet, "dropeffect", dropEffect.utf8().data()); + + if (coreObject->isGrabbed()) + attributeSet = addToAtkAttributeSet(attributeSet, "grabbed", "true"); + else if (coreObject->supportsDragging()) + attributeSet = addToAtkAttributeSet(attributeSet, "grabbed", "false"); + + // The Core AAM states the author-provided value should be exposed as-is. + const String& keyShortcuts = coreObject->keyShortcutsValue(); + if (!keyShortcuts.isEmpty()) + attributeSet = addToAtkAttributeSet(attributeSet, "keyshortcuts", keyShortcuts.utf8().data()); + + return attributeSet; +} + +static AtkRole atkRole(AccessibilityObject* coreObject) +{ + switch (coreObject->roleValue()) { + case AccessibilityRole::ApplicationAlert: + return ATK_ROLE_NOTIFICATION; + case AccessibilityRole::ApplicationAlertDialog: + return ATK_ROLE_ALERT; + case AccessibilityRole::ApplicationDialog: + return ATK_ROLE_DIALOG; + case AccessibilityRole::ApplicationStatus: + return ATK_ROLE_STATUSBAR; + case AccessibilityRole::Unknown: + return ATK_ROLE_UNKNOWN; + case AccessibilityRole::Audio: + return ATK_ROLE_AUDIO; + case AccessibilityRole::Video: + return ATK_ROLE_VIDEO; + case AccessibilityRole::Button: + return ATK_ROLE_PUSH_BUTTON; + case AccessibilityRole::Switch: + case AccessibilityRole::ToggleButton: + return ATK_ROLE_TOGGLE_BUTTON; + case AccessibilityRole::RadioButton: + return ATK_ROLE_RADIO_BUTTON; + case AccessibilityRole::CheckBox: + return ATK_ROLE_CHECK_BOX; + case AccessibilityRole::Slider: + return ATK_ROLE_SLIDER; + case AccessibilityRole::TabGroup: + case AccessibilityRole::TabList: + return ATK_ROLE_PAGE_TAB_LIST; + case AccessibilityRole::TextField: + case AccessibilityRole::TextArea: + case AccessibilityRole::SearchField: + return ATK_ROLE_ENTRY; + case AccessibilityRole::StaticText: + return ATK_ROLE_STATIC; + case AccessibilityRole::Outline: + case AccessibilityRole::Tree: + return ATK_ROLE_TREE; + case AccessibilityRole::TreeItem: + return ATK_ROLE_TREE_ITEM; + case AccessibilityRole::MenuBar: + return ATK_ROLE_MENU_BAR; + case AccessibilityRole::MenuListPopup: + case AccessibilityRole::Menu: + return ATK_ROLE_MENU; + case AccessibilityRole::MenuListOption: + case AccessibilityRole::MenuItem: + case AccessibilityRole::MenuButton: + return ATK_ROLE_MENU_ITEM; + case AccessibilityRole::MenuItemCheckbox: + return ATK_ROLE_CHECK_MENU_ITEM; + case AccessibilityRole::MenuItemRadio: + return ATK_ROLE_RADIO_MENU_ITEM; + case AccessibilityRole::Column: + // return ATK_ROLE_TABLE_COLUMN_HEADER; // Is this right? + return ATK_ROLE_UNKNOWN; // Matches Mozilla + case AccessibilityRole::Row: + return ATK_ROLE_TABLE_ROW; + case AccessibilityRole::Toolbar: + return ATK_ROLE_TOOL_BAR; + case AccessibilityRole::Meter: + return ATK_ROLE_LEVEL_BAR; + case AccessibilityRole::BusyIndicator: + case AccessibilityRole::ProgressIndicator: + return ATK_ROLE_PROGRESS_BAR; + case AccessibilityRole::Window: + return ATK_ROLE_WINDOW; + case AccessibilityRole::PopUpButton: + return coreObject->hasPopup() ? ATK_ROLE_PUSH_BUTTON : ATK_ROLE_COMBO_BOX; + case AccessibilityRole::ComboBox: + return ATK_ROLE_COMBO_BOX; + case AccessibilityRole::SplitGroup: + return ATK_ROLE_SPLIT_PANE; + case AccessibilityRole::Splitter: + return ATK_ROLE_SEPARATOR; +#if PLATFORM(GTK) + case AccessibilityRole::ColorWell: + // ATK_ROLE_COLOR_CHOOSER is defined as a dialog (i.e. it's what appears when you push the button). + return ATK_ROLE_PUSH_BUTTON; +#endif + case AccessibilityRole::List: + return ATK_ROLE_LIST; + case AccessibilityRole::ScrollBar: + return ATK_ROLE_SCROLL_BAR; + case AccessibilityRole::ScrollArea: + case AccessibilityRole::TabPanel: + return ATK_ROLE_SCROLL_PANE; + case AccessibilityRole::Grid: + case AccessibilityRole::Table: + return ATK_ROLE_TABLE; + case AccessibilityRole::TreeGrid: + return ATK_ROLE_TREE_TABLE; + case AccessibilityRole::Application: + return ATK_ROLE_APPLICATION; + case AccessibilityRole::ApplicationGroup: + case AccessibilityRole::Feed: + case AccessibilityRole::Figure: + case AccessibilityRole::GraphicsObject: + case AccessibilityRole::Group: + case AccessibilityRole::RadioGroup: + case AccessibilityRole::SVGRoot: + return ATK_ROLE_PANEL; + case AccessibilityRole::RowHeader: + return ATK_ROLE_ROW_HEADER; + case AccessibilityRole::ColumnHeader: + return ATK_ROLE_COLUMN_HEADER; + case AccessibilityRole::Caption: + return ATK_ROLE_CAPTION; + case AccessibilityRole::Cell: + case AccessibilityRole::GridCell: + return coreObject->inheritsPresentationalRole() ? ATK_ROLE_SECTION : ATK_ROLE_TABLE_CELL; + case AccessibilityRole::Link: + case AccessibilityRole::WebCoreLink: + case AccessibilityRole::ImageMapLink: + return ATK_ROLE_LINK; + case AccessibilityRole::ImageMap: + return ATK_ROLE_IMAGE_MAP; + case AccessibilityRole::GraphicsSymbol: + case AccessibilityRole::Image: + return ATK_ROLE_IMAGE; + case AccessibilityRole::ListMarker: + return ATK_ROLE_TEXT; + case AccessibilityRole::DocumentArticle: + return ATK_ROLE_ARTICLE; + case AccessibilityRole::Document: + case AccessibilityRole::GraphicsDocument: + return ATK_ROLE_DOCUMENT_FRAME; + case AccessibilityRole::DocumentNote: + return ATK_ROLE_COMMENT; + case AccessibilityRole::Heading: + return ATK_ROLE_HEADING; + case AccessibilityRole::ListBox: + // https://rawgit.com/w3c/aria/master/core-aam/core-aam.html#role-map-listbox + return coreObject->isDescendantOfRole(AccessibilityRole::ComboBox) ? ATK_ROLE_MENU : ATK_ROLE_LIST_BOX; + case AccessibilityRole::ListItem: + return coreObject->inheritsPresentationalRole() ? ATK_ROLE_SECTION : ATK_ROLE_LIST_ITEM; + case AccessibilityRole::ListBoxOption: + return coreObject->isDescendantOfRole(AccessibilityRole::ComboBox) ? ATK_ROLE_MENU_ITEM : ATK_ROLE_LIST_ITEM; + case AccessibilityRole::Paragraph: + return ATK_ROLE_PARAGRAPH; + case AccessibilityRole::Label: + case AccessibilityRole::Legend: + return ATK_ROLE_LABEL; + case AccessibilityRole::Blockquote: + return ATK_ROLE_BLOCK_QUOTE; +#if ATK_CHECK_VERSION(2, 25, 2) + case AccessibilityRole::Footnote: + return ATK_ROLE_FOOTNOTE; +#endif + case AccessibilityRole::ApplicationTextGroup: + case AccessibilityRole::Div: + case AccessibilityRole::Pre: + case AccessibilityRole::SVGText: + case AccessibilityRole::TextGroup: + return ATK_ROLE_SECTION; + case AccessibilityRole::Footer: + return ATK_ROLE_FOOTER; + case AccessibilityRole::Form: + if (coreObject->ariaRoleAttribute() != AccessibilityRole::Unknown) + return ATK_ROLE_LANDMARK; + return ATK_ROLE_FORM; + case AccessibilityRole::Canvas: + return ATK_ROLE_CANVAS; + case AccessibilityRole::HorizontalRule: + return ATK_ROLE_SEPARATOR; + case AccessibilityRole::SpinButton: + return ATK_ROLE_SPIN_BUTTON; + case AccessibilityRole::Tab: + return ATK_ROLE_PAGE_TAB; + case AccessibilityRole::UserInterfaceTooltip: + return ATK_ROLE_TOOL_TIP; + case AccessibilityRole::WebArea: + return ATK_ROLE_DOCUMENT_WEB; + case AccessibilityRole::WebApplication: + return ATK_ROLE_EMBEDDED; + case AccessibilityRole::ApplicationLog: + return ATK_ROLE_LOG; + case AccessibilityRole::ApplicationMarquee: + return ATK_ROLE_MARQUEE; + case AccessibilityRole::ApplicationTimer: + return ATK_ROLE_TIMER; + case AccessibilityRole::Definition: + return ATK_ROLE_DEFINITION; + case AccessibilityRole::DocumentMath: + return ATK_ROLE_MATH; + case AccessibilityRole::MathElement: + if (coreObject->isMathRow()) + return ATK_ROLE_PANEL; + if (coreObject->isMathTable()) + return ATK_ROLE_TABLE; + if (coreObject->isMathTableRow()) + return ATK_ROLE_TABLE_ROW; + if (coreObject->isMathTableCell()) + return ATK_ROLE_TABLE_CELL; + if (coreObject->isMathSubscriptSuperscript() || coreObject->isMathMultiscript()) + return ATK_ROLE_SECTION; + if (coreObject->isMathFraction()) + return ATK_ROLE_MATH_FRACTION; + if (coreObject->isMathSquareRoot() || coreObject->isMathRoot()) + return ATK_ROLE_MATH_ROOT; + if (coreObject->isMathScriptObject(AccessibilityMathScriptObjectType::Subscript) + || coreObject->isMathMultiscriptObject(AccessibilityMathMultiscriptObjectType::PreSubscript) || coreObject->isMathMultiscriptObject(AccessibilityMathMultiscriptObjectType::PostSubscript)) + return ATK_ROLE_SUBSCRIPT; + if (coreObject->isMathScriptObject(AccessibilityMathScriptObjectType::Superscript) + || coreObject->isMathMultiscriptObject(AccessibilityMathMultiscriptObjectType::PreSuperscript) || coreObject->isMathMultiscriptObject(AccessibilityMathMultiscriptObjectType::PostSuperscript)) + return ATK_ROLE_SUPERSCRIPT; + if (coreObject->isMathToken()) + return ATK_ROLE_STATIC; + return ATK_ROLE_UNKNOWN; + case AccessibilityRole::LandmarkBanner: + case AccessibilityRole::LandmarkComplementary: + case AccessibilityRole::LandmarkContentInfo: + case AccessibilityRole::LandmarkDocRegion: + case AccessibilityRole::LandmarkMain: + case AccessibilityRole::LandmarkNavigation: + case AccessibilityRole::LandmarkRegion: + case AccessibilityRole::LandmarkSearch: + return ATK_ROLE_LANDMARK; + case AccessibilityRole::DescriptionList: + return ATK_ROLE_DESCRIPTION_LIST; + case AccessibilityRole::Term: + case AccessibilityRole::DescriptionListTerm: + return ATK_ROLE_DESCRIPTION_TERM; + case AccessibilityRole::DescriptionListDetail: + return ATK_ROLE_DESCRIPTION_VALUE; + case AccessibilityRole::Deletion: +#if ATK_CHECK_VERSION(2, 33, 3) + return ATK_ROLE_CONTENT_DELETION; +#else + return ATK_ROLE_STATIC; +#endif + case AccessibilityRole::Insertion: +#if ATK_CHECK_VERSION(2, 33, 3) + return ATK_ROLE_CONTENT_INSERTION; +#else + return ATK_ROLE_STATIC; +#endif + case AccessibilityRole::Subscript: + return ATK_ROLE_SUBSCRIPT; + case AccessibilityRole::Superscript: + return ATK_ROLE_SUPERSCRIPT; + case AccessibilityRole::Inline: + case AccessibilityRole::SVGTextPath: + case AccessibilityRole::SVGTSpan: + case AccessibilityRole::Time: + return ATK_ROLE_STATIC; + default: + return ATK_ROLE_UNKNOWN; + } +} + +static AtkRole webkitAccessibleGetRole(AtkObject* object) +{ + // ATK_ROLE_UNKNOWN should only be applied in cases where there is a valid + // WebCore accessible object for which the platform role mapping is unknown. + auto* accessible = WEBKIT_ACCESSIBLE(object); + returnValIfWebKitAccessibleIsInvalid(accessible, ATK_ROLE_INVALID); + + if (!accessible->priv->object) + return ATK_ROLE_INVALID; + + // Note: Why doesn't WebCore have a password field for this + if (accessible->priv->object->isPasswordField()) + return ATK_ROLE_PASSWORD_TEXT; + + return atkRole(accessible->priv->object); +} + +static bool isTextWithCaret(AccessibilityObject* coreObject) +{ + if (!coreObject || !coreObject->isAccessibilityRenderObject()) + return false; + + Document* document = coreObject->document(); + if (!document) + return false; + + Frame* frame = document->frame(); + if (!frame) + return false; + + if (!frame->settings().caretBrowsingEnabled()) + return false; + + // Check text objects and paragraphs only. + auto* axObject = coreObject->wrapper(); + AtkRole role = axObject ? atk_object_get_role(ATK_OBJECT(axObject)) : ATK_ROLE_INVALID; + if (role != ATK_ROLE_TEXT && role != ATK_ROLE_PARAGRAPH) + return false; + + // Finally, check whether the caret is set in the current object. + VisibleSelection selection = coreObject->selection(); + if (!selection.isCaret()) + return false; + + return selectionBelongsToObject(coreObject, selection); +} + +static void setAtkStateSetFromCoreObject(AccessibilityObject* coreObject, AtkStateSet* stateSet) +{ + AccessibilityObject* parent = coreObject->parentObject(); + bool isListBoxOption = parent && parent->isListBox(); + + // Please keep the state list in alphabetical order + if ((isListBoxOption && coreObject->isSelectedOptionActive()) + || coreObject->currentState() != AccessibilityCurrentState::False) + atk_state_set_add_state(stateSet, ATK_STATE_ACTIVE); + + if (coreObject->isBusy()) + atk_state_set_add_state(stateSet, ATK_STATE_BUSY); + + if (coreObject->supportsChecked() && coreObject->canSetValueAttribute()) + atk_state_set_add_state(stateSet, ATK_STATE_CHECKABLE); + + if (coreObject->isChecked()) + atk_state_set_add_state(stateSet, ATK_STATE_CHECKED); + + if ((coreObject->isTextControl() || coreObject->isNonNativeTextControl()) && coreObject->canSetValueAttribute()) + atk_state_set_add_state(stateSet, ATK_STATE_EDITABLE); + + // FIXME: Put both ENABLED and SENSITIVE together here for now + if (coreObject->isEnabled()) { + atk_state_set_add_state(stateSet, ATK_STATE_ENABLED); + atk_state_set_add_state(stateSet, ATK_STATE_SENSITIVE); + } + + if (coreObject->canSetExpandedAttribute()) + atk_state_set_add_state(stateSet, ATK_STATE_EXPANDABLE); + + if (coreObject->isExpanded()) + atk_state_set_add_state(stateSet, ATK_STATE_EXPANDED); + + if (coreObject->canSetFocusAttribute()) + atk_state_set_add_state(stateSet, ATK_STATE_FOCUSABLE); + + // According to the Core AAM, if the element which is focused has a valid aria-activedescendant, + // we should not expose the focused state on the element which is actually focused, but instead + // on its active descendant. + if ((coreObject->isFocused() && !coreObject->activeDescendant()) || isTextWithCaret(coreObject)) + atk_state_set_add_state(stateSet, ATK_STATE_FOCUSED); + else if (coreObject->isActiveDescendantOfFocusedContainer()) { + atk_state_set_add_state(stateSet, ATK_STATE_FOCUSABLE); + atk_state_set_add_state(stateSet, ATK_STATE_FOCUSED); + } + + if (coreObject->orientation() == AccessibilityOrientation::Horizontal) + atk_state_set_add_state(stateSet, ATK_STATE_HORIZONTAL); + else if (coreObject->orientation() == AccessibilityOrientation::Vertical) + atk_state_set_add_state(stateSet, ATK_STATE_VERTICAL); + + if (coreObject->hasPopup()) + atk_state_set_add_state(stateSet, ATK_STATE_HAS_POPUP); + + if (coreObject->isIndeterminate()) + atk_state_set_add_state(stateSet, ATK_STATE_INDETERMINATE); + else if (coreObject->isCheckboxOrRadio() || coreObject->isMenuItem() || coreObject->isToggleButton()) { + if (coreObject->checkboxOrRadioValue() == AccessibilityButtonState::Mixed) + atk_state_set_add_state(stateSet, ATK_STATE_INDETERMINATE); + } + + if (coreObject->isModalNode()) + atk_state_set_add_state(stateSet, ATK_STATE_MODAL); + + if (coreObject->invalidStatus() != "false"_s) + atk_state_set_add_state(stateSet, ATK_STATE_INVALID_ENTRY); + + if (coreObject->isMultiSelectable()) + atk_state_set_add_state(stateSet, ATK_STATE_MULTISELECTABLE); + + // TODO: ATK_STATE_OPAQUE + + if (coreObject->isPressed()) + atk_state_set_add_state(stateSet, ATK_STATE_PRESSED); + + if (!coreObject->canSetValueAttribute() && (coreObject->supportsReadOnly())) + atk_state_set_add_state(stateSet, ATK_STATE_READ_ONLY); + + if (coreObject->isRequired()) + atk_state_set_add_state(stateSet, ATK_STATE_REQUIRED); + + // TODO: ATK_STATE_SELECTABLE_TEXT + + if (coreObject->canSetSelectedAttribute()) { + atk_state_set_add_state(stateSet, ATK_STATE_SELECTABLE); + // Items in focusable lists have both STATE_SELECT{ABLE,ED} + // and STATE_FOCUS{ABLE,ED}. We'll fake the latter based on + // the former. + if (isListBoxOption) + atk_state_set_add_state(stateSet, ATK_STATE_FOCUSABLE); + } + + if (coreObject->isSelected()) { + atk_state_set_add_state(stateSet, ATK_STATE_SELECTED); + // Items in focusable lists have both STATE_SELECT{ABLE,ED} + // and STATE_FOCUS{ABLE,ED}. We'll fake the latter based on the + // former. + if (isListBoxOption) + atk_state_set_add_state(stateSet, ATK_STATE_FOCUSED); + } + + // FIXME: Group both SHOWING and VISIBLE here for now + // Not sure how to handle this in WebKit, see bug + // http://bugzilla.gnome.org/show_bug.cgi?id=509650 for other + // issues with SHOWING vs VISIBLE. + if (!coreObject->isOffScreen()) { + atk_state_set_add_state(stateSet, ATK_STATE_SHOWING); + atk_state_set_add_state(stateSet, ATK_STATE_VISIBLE); + } + + // Mutually exclusive, so we group these two + if (coreObject->roleValue() == AccessibilityRole::TextArea || coreObject->ariaIsMultiline()) + atk_state_set_add_state(stateSet, ATK_STATE_MULTI_LINE); + else if (coreObject->roleValue() == AccessibilityRole::TextField || coreObject->roleValue() == AccessibilityRole::SearchField) + atk_state_set_add_state(stateSet, ATK_STATE_SINGLE_LINE); + + // TODO: ATK_STATE_SENSITIVE + + if (coreObject->supportsAutoComplete() && coreObject->autoCompleteValue() != "none"_s) + atk_state_set_add_state(stateSet, ATK_STATE_SUPPORTS_AUTOCOMPLETION); + + if (coreObject->isVisited()) + atk_state_set_add_state(stateSet, ATK_STATE_VISITED); +} + +static AtkStateSet* webkitAccessibleRefStateSet(AtkObject* object) +{ + auto* accessible = WEBKIT_ACCESSIBLE(object); + AtkStateSet* stateSet = ATK_OBJECT_CLASS(webkit_accessible_parent_class)->ref_state_set(object); + + // Make sure the layout is updated to really know whether the object + // is defunct or not, so we can return the proper state. + if (accessible->priv->object) + accessible->priv->object->updateBackingStore(); + if (!accessible->priv->object) { + atk_state_set_add_state(stateSet, ATK_STATE_DEFUNCT); + return stateSet; + } + + // Text objects must be focusable. + AtkRole role = atk_object_get_role(object); + if (role == ATK_ROLE_TEXT || role == ATK_ROLE_PARAGRAPH) + atk_state_set_add_state(stateSet, ATK_STATE_FOCUSABLE); + + setAtkStateSetFromCoreObject(accessible->priv->object, stateSet); + return stateSet; +} + +static AtkRelationSet* webkitAccessibleRefRelationSet(AtkObject* object) +{ + auto* accessible = WEBKIT_ACCESSIBLE(object); + returnValIfWebKitAccessibleIsInvalid(accessible, nullptr); + + AtkRelationSet* relationSet = ATK_OBJECT_CLASS(webkit_accessible_parent_class)->ref_relation_set(object); + if (accessible->priv->object) + setAtkRelationSetFromCoreObject(accessible->priv->object, relationSet); + return relationSet; +} + +static void webkitAccessibleInit(AtkObject* object, gpointer data) +{ + if (ATK_OBJECT_CLASS(webkit_accessible_parent_class)->initialize) + ATK_OBJECT_CLASS(webkit_accessible_parent_class)->initialize(object, data); + + WebKitAccessible* accessible = WEBKIT_ACCESSIBLE(object); + accessible->priv->object = reinterpret_cast<AccessibilityObject*>(data); +} + +static const gchar* webkitAccessibleGetObjectLocale(AtkObject* object) +{ + auto* accessible = WEBKIT_ACCESSIBLE(object); + returnValIfWebKitAccessibleIsInvalid(accessible, nullptr); + + if (!accessible->priv->object) + return nullptr; + + if (ATK_IS_DOCUMENT(object)) { + // TODO: Should we fall back on lang xml:lang when the following comes up empty? + String language = accessible->priv->object->language(); + if (!language.isEmpty()) + return webkitAccessibleCacheAndReturnAtkProperty(accessible, AtkCachedDocumentLocale, language.utf8()); + + } else if (ATK_IS_TEXT(object)) { + const gchar* locale = nullptr; + + AtkAttributeSet* textAttributes = atk_text_get_default_attributes(ATK_TEXT(object)); + for (auto* attributes = textAttributes; attributes; attributes = attributes->next) { + auto* atkAttribute = static_cast<AtkAttribute*>(attributes->data); + if (!strcmp(atkAttribute->name, atk_text_attribute_get_name(ATK_TEXT_ATTR_LANGUAGE))) { + locale = webkitAccessibleCacheAndReturnAtkProperty(accessible, AtkCachedDocumentLocale, atkAttribute->value); + break; + } + } + atk_attribute_set_free(textAttributes); + + return locale; + } + + return nullptr; +} + +static void webkit_accessible_class_init(WebKitAccessibleClass* klass) +{ + auto* atkObjectClass = ATK_OBJECT_CLASS(klass); + atkObjectClass->initialize = webkitAccessibleInit; + atkObjectClass->get_name = webkitAccessibleGetName; + atkObjectClass->get_description = webkitAccessibleGetDescription; + atkObjectClass->get_parent = webkitAccessibleGetParent; + atkObjectClass->get_n_children = webkitAccessibleGetNChildren; + atkObjectClass->ref_child = webkitAccessibleRefChild; + atkObjectClass->get_role = webkitAccessibleGetRole; + atkObjectClass->ref_state_set = webkitAccessibleRefStateSet; + atkObjectClass->get_index_in_parent = webkitAccessibleGetIndexInParent; + atkObjectClass->get_attributes = webkitAccessibleGetAttributes; + atkObjectClass->ref_relation_set = webkitAccessibleRefRelationSet; + atkObjectClass->get_object_locale = webkitAccessibleGetObjectLocale; +} + +static const GInterfaceInfo atkInterfacesInitFunctions[] = { + {reinterpret_cast<GInterfaceInitFunc>(reinterpret_cast<GCallback>(webkitAccessibleActionInterfaceInit)), nullptr, nullptr}, + {reinterpret_cast<GInterfaceInitFunc>(reinterpret_cast<GCallback>(webkitAccessibleSelectionInterfaceInit)), nullptr, nullptr}, + {reinterpret_cast<GInterfaceInitFunc>(reinterpret_cast<GCallback>(webkitAccessibleEditableTextInterfaceInit)), nullptr, nullptr}, + {reinterpret_cast<GInterfaceInitFunc>(reinterpret_cast<GCallback>(webkitAccessibleTextInterfaceInit)), nullptr, nullptr}, + {reinterpret_cast<GInterfaceInitFunc>(reinterpret_cast<GCallback>(webkitAccessibleComponentInterfaceInit)), nullptr, nullptr}, + {reinterpret_cast<GInterfaceInitFunc>(reinterpret_cast<GCallback>(webkitAccessibleImageInterfaceInit)), nullptr, nullptr}, + {reinterpret_cast<GInterfaceInitFunc>(reinterpret_cast<GCallback>(webkitAccessibleTableInterfaceInit)), nullptr, nullptr}, + {reinterpret_cast<GInterfaceInitFunc>(reinterpret_cast<GCallback>(webkitAccessibleTableCellInterfaceInit)), nullptr, nullptr}, + {reinterpret_cast<GInterfaceInitFunc>(reinterpret_cast<GCallback>(webkitAccessibleHypertextInterfaceInit)), nullptr, nullptr}, + {reinterpret_cast<GInterfaceInitFunc>(reinterpret_cast<GCallback>(webkitAccessibleHyperlinkImplInterfaceInit)), nullptr, nullptr}, + {reinterpret_cast<GInterfaceInitFunc>(reinterpret_cast<GCallback>(webkitAccessibleDocumentInterfaceInit)), nullptr, nullptr}, + {reinterpret_cast<GInterfaceInitFunc>(reinterpret_cast<GCallback>(webkitAccessibleValueInterfaceInit)), nullptr, nullptr} +}; + +enum WAIType { + WAIAction, + WAISelection, + WAIEditableText, + WAIText, + WAIComponent, + WAIImage, + WAITable, + WAITableCell, + WAIHypertext, + WAIHyperlink, + WAIDocument, + WAIValue, +}; + +static GType atkInterfaceTypeFromWAIType(WAIType type) +{ + switch (type) { + case WAIAction: + return ATK_TYPE_ACTION; + case WAISelection: + return ATK_TYPE_SELECTION; + case WAIEditableText: + return ATK_TYPE_EDITABLE_TEXT; + case WAIText: + return ATK_TYPE_TEXT; + case WAIComponent: + return ATK_TYPE_COMPONENT; + case WAIImage: + return ATK_TYPE_IMAGE; + case WAITable: + return ATK_TYPE_TABLE; + case WAITableCell: + return ATK_TYPE_TABLE_CELL; + case WAIHypertext: + return ATK_TYPE_HYPERTEXT; + case WAIHyperlink: + return ATK_TYPE_HYPERLINK_IMPL; + case WAIDocument: + return ATK_TYPE_DOCUMENT; + case WAIValue: + return ATK_TYPE_VALUE; + } + + return G_TYPE_INVALID; +} + +static bool roleIsTextType(AccessibilityRole role) +{ + return role == AccessibilityRole::Paragraph + || role == AccessibilityRole::Heading + || role == AccessibilityRole::Div + || role == AccessibilityRole::Cell + || role == AccessibilityRole::Link + || role == AccessibilityRole::WebCoreLink + || role == AccessibilityRole::ListItem + || role == AccessibilityRole::Pre + || role == AccessibilityRole::GridCell + || role == AccessibilityRole::TextGroup + || role == AccessibilityRole::ApplicationTextGroup + || role == AccessibilityRole::ApplicationGroup; +} + +static guint16 interfaceMaskFromObject(AXCoreObject* coreObject) +{ + guint16 interfaceMask = 0; + + // Component interface is always supported + interfaceMask |= 1 << WAIComponent; + + AccessibilityRole role = coreObject->roleValue(); + + // Action + // As the implementation of the AtkAction interface is a very + // basic one (just relays in executing the default action for each + // object, and only supports having one action per object), it is + // better just to implement this interface for every instance of + // the WebKitAccessible class and let WebCore decide what to do. + interfaceMask |= 1 << WAIAction; + + // Selection + if (coreObject->canHaveSelectedChildren() || coreObject->isMenuList()) + interfaceMask |= 1 << WAISelection; + + // Get renderer if available. + RenderObject* renderer = nullptr; + if (coreObject->isAccessibilityRenderObject()) + renderer = coreObject->renderer(); + + // Hyperlink (links and embedded objects). + if (coreObject->isLink() || (renderer && renderer->isReplacedOrInlineBlock())) + interfaceMask |= 1 << WAIHyperlink; + + // Text, Editable Text & Hypertext + if (role == AccessibilityRole::StaticText || coreObject->isMenuListOption()) + interfaceMask |= 1 << WAIText; + else if (coreObject->isTextControl() || coreObject->isNonNativeTextControl()) { + interfaceMask |= 1 << WAIText; + if (coreObject->canSetValueAttribute()) + interfaceMask |= 1 << WAIEditableText; + } else if (!coreObject->isWebArea()) { + if (role != AccessibilityRole::Table) { + interfaceMask |= 1 << WAIHypertext; + if ((renderer && renderer->childrenInline()) || roleIsTextType(role) || coreObject->isMathToken()) + interfaceMask |= 1 << WAIText; + } + + // Add the TEXT interface for list items whose + // first accessible child has a text renderer + if (role == AccessibilityRole::ListItem) { + const auto& children = coreObject->children(); + if (!children.isEmpty()) + interfaceMask |= interfaceMaskFromObject(children[0].get()); + } + } + + // Image + if (coreObject->isImage()) + interfaceMask |= 1 << WAIImage; + + // Table + if (coreObject->isTable()) + interfaceMask |= 1 << WAITable; + + if (role == AccessibilityRole::Cell || role == AccessibilityRole::GridCell || role == AccessibilityRole::ColumnHeader || role == AccessibilityRole::RowHeader) + interfaceMask |= 1 << WAITableCell; + + // Document + if (role == AccessibilityRole::WebArea) + interfaceMask |= 1 << WAIDocument; + + // Value + if (coreObject->supportsRangeValue()) + interfaceMask |= 1 << WAIValue; + +#if ENABLE(INPUT_TYPE_COLOR) + // Color type. + if (role == AccessibilityRole::ColorWell) + interfaceMask |= 1 << WAIText; +#endif + + return interfaceMask; +} + +static const char* uniqueAccessibilityTypeName(guint16 interfaceMask) +{ +#define WAI_TYPE_NAME_LEN (30) // Enough for prefix + 5 hex characters (max). + static char name[WAI_TYPE_NAME_LEN + 1]; + + g_sprintf(name, "WAIType%x", interfaceMask); + name[WAI_TYPE_NAME_LEN] = '\0'; + + return name; +} + +static GType accessibilityTypeFromObject(AccessibilityObject* coreObject) +{ + static const GTypeInfo typeInfo = { + sizeof(WebKitAccessibleClass), + nullptr, // GBaseInitFunc + nullptr, // GBaseFinalizeFunc + nullptr, // GClassInitFunc + nullptr, // GClassFinalizeFunc + nullptr, // class data + sizeof(WebKitAccessible), // instance size + 0, // nb preallocs + nullptr, // GInstanceInitFunc + nullptr // value table + }; + + guint16 interfaceMask = interfaceMaskFromObject(coreObject); + const char* atkTypeName = uniqueAccessibilityTypeName(interfaceMask); + if (GType type = g_type_from_name(atkTypeName)) + return type; + + GType type = g_type_register_static(WEBKIT_TYPE_ACCESSIBLE, atkTypeName, &typeInfo, static_cast<GTypeFlags>(0)); + for (unsigned i = 0; i < G_N_ELEMENTS(atkInterfacesInitFunctions); ++i) { + if (interfaceMask & (1 << i)) { + g_type_add_interface_static(type, + atkInterfaceTypeFromWAIType(static_cast<WAIType>(i)), + &atkInterfacesInitFunctions[i]); + } + } + + return type; +} + +WebKitAccessible* webkitAccessibleNew(AccessibilityObject* coreObject) +{ + auto* object = ATK_OBJECT(g_object_new(accessibilityTypeFromObject(coreObject), nullptr)); + atk_object_initialize(object, coreObject); + return WEBKIT_ACCESSIBLE(object); +} + +AccessibilityObject& webkitAccessibleGetAccessibilityObject(WebKitAccessible* accessible) +{ + ASSERT(WEBKIT_IS_ACCESSIBLE(accessible)); + ASSERT(accessible->priv->object); + return *accessible->priv->object; +} + +void webkitAccessibleDetach(WebKitAccessible* accessible) +{ + ASSERT(WEBKIT_IS_ACCESSIBLE(accessible)); + + if (accessible->priv->object && accessible->priv->object->roleValue() == AccessibilityRole::WebArea) + atk_object_notify_state_change(ATK_OBJECT(accessible), ATK_STATE_DEFUNCT, TRUE); + + accessible->priv->object = nullptr; +} + +bool webkitAccessibleIsDetached(WebKitAccessible* accessible) +{ + ASSERT(WEBKIT_IS_ACCESSIBLE(accessible)); + return !accessible->priv->object; +} + +const char* webkitAccessibleCacheAndReturnAtkProperty(WebKitAccessible* accessible, AtkCachedProperty property, CString&& value) +{ + ASSERT(WEBKIT_IS_ACCESSIBLE(accessible)); + + WebKitAccessiblePrivate* priv = accessible->priv; + CString* propertyPtr = nullptr; + + switch (property) { + case AtkCachedAccessibleName: + propertyPtr = &priv->accessibleName; + break; + case AtkCachedAccessibleDescription: + propertyPtr = &priv->accessibleDescription; + break; + case AtkCachedActionName: + propertyPtr = &priv->actionName; + break; + case AtkCachedActionKeyBinding: + propertyPtr = &priv->actionKeyBinding; + break; + case AtkCachedDocumentLocale: + propertyPtr = &priv->documentLocale; + break; + case AtkCachedDocumentType: + propertyPtr = &priv->documentType; + break; + case AtkCachedDocumentEncoding: + propertyPtr = &priv->documentEncoding; + break; + case AtkCachedDocumentURI: + propertyPtr = &priv->documentURI; + break; + case AtkCachedImageDescription: + propertyPtr = &priv->imageDescription; + break; + default: + ASSERT_NOT_REACHED(); + } + + // Don't invalidate old memory if not stricly needed, since other + // callers might be still holding on to it. + if (*propertyPtr != value) + *propertyPtr = WTFMove(value); + + return (*propertyPtr).data(); +} + +#endif // ENABLE(ACCESSIBILITY) && USE(ATK) diff -urpN webkitgtk-2.38.1.glib244/Source/WebCore/accessibility/atk/WebKitAccessible.h webkitgtk-2.38.1/Source/WebCore/accessibility/atk/WebKitAccessible.h --- webkitgtk-2.38.1.glib244/Source/WebCore/accessibility/atk/WebKitAccessible.h 1969-12-31 18:00:00.000000000 -0600 +++ webkitgtk-2.38.1/Source/WebCore/accessibility/atk/WebKitAccessible.h 2022-10-24 13:34:51.917454604 -0500 @@ -0,0 +1,88 @@ +/* + * Copyright (C) 2008 Nuanti Ltd. + * Copyright (C) 2009 Jan Alonzo + * Copyright (C) 2009, 2010, 2011, 2012, 2019 Igalia S.L. + * Copyright (C) 2013 Samsung Electronics + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#pragma once + +#if ENABLE(ACCESSIBILITY) && USE(ATK) + +#include <atk/atk.h> +#include <wtf/text/WTFString.h> + +namespace WebCore { +class AccessibilityObject; +} + +G_BEGIN_DECLS + +#define WEBKIT_TYPE_ACCESSIBLE (webkit_accessible_get_type()) +#define WEBKIT_ACCESSIBLE(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), WEBKIT_TYPE_ACCESSIBLE, WebKitAccessible)) +#define WEBKIT_ACCESSIBLE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), WEBKIT_TYPE_ACCESSIBLE, WebKitAccessibleClass)) +#define WEBKIT_IS_ACCESSIBLE(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), WEBKIT_TYPE_ACCESSIBLE)) +#define WEBKIT_IS_ACCESSIBLE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), WEBKIT_TYPE_ACCESSIBLE)) +#define WEBKIT_ACCESSIBLE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), WEBKIT_TYPE_ACCESSIBLE, WebKitAccessibleClass)) + +typedef struct _WebKitAccessible WebKitAccessible; +typedef struct _WebKitAccessibleClass WebKitAccessibleClass; +typedef struct _WebKitAccessiblePrivate WebKitAccessiblePrivate; + + +struct _WebKitAccessible { + AtkObject parent; + + WebKitAccessiblePrivate *priv; +}; + +struct _WebKitAccessibleClass { + AtkObjectClass parentClass; +}; + +GType webkit_accessible_get_type(void); + +G_END_DECLS + +// The definitions below are only used in C++ code, and some use C++ types, +// therefore they should be outside of the G_BEGIN_DECLS/G_END_DECLS block +// to make them use the C++ ABI. + +enum AtkCachedProperty { + AtkCachedAccessibleName, + AtkCachedAccessibleDescription, + AtkCachedActionName, + AtkCachedActionKeyBinding, + AtkCachedDocumentLocale, + AtkCachedDocumentType, + AtkCachedDocumentEncoding, + AtkCachedDocumentURI, + AtkCachedImageDescription +}; + +WebKitAccessible* webkitAccessibleNew(WebCore::AccessibilityObject*); + +WebCore::AccessibilityObject& webkitAccessibleGetAccessibilityObject(WebKitAccessible*); + +void webkitAccessibleDetach(WebKitAccessible*); + +bool webkitAccessibleIsDetached(WebKitAccessible*); + +const char* webkitAccessibleCacheAndReturnAtkProperty(WebKitAccessible*, AtkCachedProperty, CString&&); + +#endif // ENABLE(ACCESSIBILITY) && USE(ATK) diff -urpN webkitgtk-2.38.1.glib244/Source/WebCore/accessibility/atk/WebKitAccessibleHyperlink.cpp webkitgtk-2.38.1/Source/WebCore/accessibility/atk/WebKitAccessibleHyperlink.cpp --- webkitgtk-2.38.1.glib244/Source/WebCore/accessibility/atk/WebKitAccessibleHyperlink.cpp 1969-12-31 18:00:00.000000000 -0600 +++ webkitgtk-2.38.1/Source/WebCore/accessibility/atk/WebKitAccessibleHyperlink.cpp 2022-10-24 13:34:51.917454604 -0500 @@ -0,0 +1,316 @@ +/* + * Copyright (C) 2010, 2011, 2012 Igalia S.L. + * Copyright (C) 2013 Samsung Electronics + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include "config.h" +#include "WebKitAccessibleHyperlink.h" + +#if ENABLE(ACCESSIBILITY) && USE(ATK) + +#include "AXObjectCache.h" +#include "AccessibilityObject.h" +#include "Editing.h" +#include "NotImplemented.h" +#include "Position.h" +#include "Range.h" +#include "RenderListMarker.h" +#include "RenderObject.h" +#include "TextIterator.h" +#include "WebKitAccessible.h" +#include "WebKitAccessibleUtil.h" +#include <wtf/glib/WTFGType.h> +#include <wtf/text/CString.h> + +using namespace WebCore; + +static void webkit_accessible_hyperlink_atk_action_interface_init(AtkActionIface*); + +struct _WebKitAccessibleHyperlinkPrivate { + WebKitAccessible* hyperlinkImpl; + + // We cache these values so we can return them as const values. + CString actionName; + CString actionKeyBinding; +}; + +WEBKIT_DEFINE_TYPE_WITH_CODE( + WebKitAccessibleHyperlink, webkit_accessible_hyperlink, ATK_TYPE_HYPERLINK, + G_IMPLEMENT_INTERFACE(ATK_TYPE_ACTION, webkit_accessible_hyperlink_atk_action_interface_init)) + +enum { + PROP_0, + + PROP_HYPERLINK_IMPL +}; + +static gboolean webkitAccessibleHyperlinkActionDoAction(AtkAction* action, gint) +{ + auto* accessibleHyperlink = WEBKIT_ACCESSIBLE_HYPERLINK(action); + returnValIfWebKitAccessibleIsInvalid(accessibleHyperlink->priv->hyperlinkImpl, FALSE); + + if (!ATK_IS_ACTION(accessibleHyperlink->priv->hyperlinkImpl)) + return FALSE; + + auto& coreObject = webkitAccessibleGetAccessibilityObject(accessibleHyperlink->priv->hyperlinkImpl); + return coreObject.performDefaultAction(); +} + +static gint webkitAccessibleHyperlinkActionGetNActions(AtkAction* action) +{ + auto* accessibleHyperlink = WEBKIT_ACCESSIBLE_HYPERLINK(action); + returnValIfWebKitAccessibleIsInvalid(accessibleHyperlink->priv->hyperlinkImpl, 0); + + return ATK_IS_ACTION(accessibleHyperlink->priv->hyperlinkImpl) ? 1 : 0; +} + +static const gchar* webkitAccessibleHyperlinkActionGetDescription(AtkAction* action, gint) +{ + auto* accessibleHyperlink = WEBKIT_ACCESSIBLE_HYPERLINK(action); + returnValIfWebKitAccessibleIsInvalid(accessibleHyperlink->priv->hyperlinkImpl, nullptr); + + // TODO: Need a way to provide/localize action descriptions. + notImplemented(); + return ""; +} + +static const gchar* webkitAccessibleHyperlinkActionGetKeybinding(AtkAction* action, gint) +{ + auto* accessibleHyperlink = WEBKIT_ACCESSIBLE_HYPERLINK(action); + returnValIfWebKitAccessibleIsInvalid(accessibleHyperlink->priv->hyperlinkImpl, nullptr); + + if (!ATK_IS_ACTION(accessibleHyperlink->priv->hyperlinkImpl)) + return nullptr; + + auto& coreObject = webkitAccessibleGetAccessibilityObject(accessibleHyperlink->priv->hyperlinkImpl); + accessibleHyperlink->priv->actionKeyBinding = coreObject.accessKey().utf8(); + return accessibleHyperlink->priv->actionKeyBinding.data(); +} + +static const gchar* webkitAccessibleHyperlinkActionGetName(AtkAction* action, gint) +{ + auto* accessibleHyperlink = WEBKIT_ACCESSIBLE_HYPERLINK(action); + returnValIfWebKitAccessibleIsInvalid(accessibleHyperlink->priv->hyperlinkImpl, nullptr); + + if (!ATK_IS_ACTION(accessibleHyperlink->priv->hyperlinkImpl)) + return nullptr; + + auto& coreObject = webkitAccessibleGetAccessibilityObject(accessibleHyperlink->priv->hyperlinkImpl); + accessibleHyperlink->priv->actionName = coreObject.localizedActionVerb().utf8(); + return accessibleHyperlink->priv->actionName.data(); +} + +static void webkit_accessible_hyperlink_atk_action_interface_init(AtkActionIface* iface) +{ + iface->do_action = webkitAccessibleHyperlinkActionDoAction; + iface->get_n_actions = webkitAccessibleHyperlinkActionGetNActions; + iface->get_description = webkitAccessibleHyperlinkActionGetDescription; + iface->get_keybinding = webkitAccessibleHyperlinkActionGetKeybinding; + iface->get_name = webkitAccessibleHyperlinkActionGetName; +} + +static gchar* webkitAccessibleHyperlinkGetURI(AtkHyperlink* link, gint index) +{ + // FIXME: Do NOT support more than one instance of an AtkObject + // implementing AtkHyperlinkImpl in every instance of AtkHyperLink + if (index) + return nullptr; + + auto* accessibleHyperlink = WEBKIT_ACCESSIBLE_HYPERLINK(link); + returnValIfWebKitAccessibleIsInvalid(accessibleHyperlink->priv->hyperlinkImpl, nullptr); + + auto& coreObject = webkitAccessibleGetAccessibilityObject(accessibleHyperlink->priv->hyperlinkImpl); + return !coreObject.url().isNull() ? g_strdup(coreObject.url().string().utf8().data()) : nullptr; +} + +static AtkObject* webkitAccessibleHyperlinkGetObject(AtkHyperlink* link, gint index) +{ + // FIXME: Do NOT support more than one instance of an AtkObject + // implementing AtkHyperlinkImpl in every instance of AtkHyperLink + if (index) + return nullptr; + + auto* accessibleHyperlink = WEBKIT_ACCESSIBLE_HYPERLINK(link); + returnValIfWebKitAccessibleIsInvalid(accessibleHyperlink->priv->hyperlinkImpl, 0); + + return ATK_OBJECT(accessibleHyperlink->priv->hyperlinkImpl); +} + +static gint rangeLengthForObject(AccessibilityObject& obj, const std::optional<SimpleRange>& range) +{ + if (!range) + return 0; + + // This is going to be the actual length in most of the cases + int baseLength = characterCount(*range, TextIteratorBehavior::EmitsCharactersBetweenAllVisiblePositions); + + // Check whether the current hyperlink belongs to a list item. + // If so, we need to consider the length of the item's marker + AXCoreObject* parent = obj.parentObjectUnignored(); + if (!parent || !parent->isAccessibilityRenderObject() || !parent->isListItem()) + return baseLength; + + // Even if we don't expose list markers to Assistive + // Technologies, we need to have a way to measure their length + // for those cases when it's needed to take it into account + // separately (as in getAccessibilityObjectForOffset) + AXCoreObject* markerObj = reinterpret_cast<AccessibilityObject *>(parent)->firstChild(); + if (!markerObj) + return baseLength; + + RenderObject* renderer = markerObj->renderer(); + if (!is<RenderListMarker>(renderer)) + return baseLength; + + return baseLength + downcast<RenderListMarker>(*renderer).textWithSuffix().length(); +} + +static gint webkitAccessibleHyperlinkGetStartIndex(AtkHyperlink* link) +{ + auto* accessibleHyperlink = WEBKIT_ACCESSIBLE_HYPERLINK(link); + returnValIfWebKitAccessibleIsInvalid(accessibleHyperlink->priv->hyperlinkImpl, 0); + + auto& coreObject = webkitAccessibleGetAccessibilityObject(accessibleHyperlink->priv->hyperlinkImpl); + AXCoreObject* parentUnignored = coreObject.parentObjectUnignored(); + if (!parentUnignored) + return 0; + + Node* node = coreObject.node(); + if (!node) + return 0; + + Node* parentNode = parentUnignored->node(); + if (!parentNode) + return 0; + + return rangeLengthForObject(coreObject, makeSimpleRange(firstPositionInOrBeforeNode(parentNode), firstPositionInOrBeforeNode(node))); +} + +static gint webkitAccessibleHyperlinkGetEndIndex(AtkHyperlink* link) +{ + auto* accessibleHyperlink = WEBKIT_ACCESSIBLE_HYPERLINK(link); + returnValIfWebKitAccessibleIsInvalid(accessibleHyperlink->priv->hyperlinkImpl, 0); + + auto& coreObject = webkitAccessibleGetAccessibilityObject(accessibleHyperlink->priv->hyperlinkImpl); + AXCoreObject* parentUnignored = coreObject.parentObjectUnignored(); + if (!parentUnignored) + return 0; + + Node* node = coreObject.node(); + if (!node) + return 0; + + Node* parentNode = parentUnignored->node(); + if (!parentNode) + return 0; + + return rangeLengthForObject(coreObject, makeSimpleRange(firstPositionInOrBeforeNode(parentNode), lastPositionInOrAfterNode(node))); +} + +static gboolean webkitAccessibleHyperlinkIsValid(AtkHyperlink* link) +{ + auto* accessibleHyperlink = WEBKIT_ACCESSIBLE_HYPERLINK(link); + returnValIfWebKitAccessibleIsInvalid(accessibleHyperlink->priv->hyperlinkImpl, FALSE); + + // Link is valid for the whole object's lifetime + return TRUE; +} + +static gint webkitAccessibleHyperlinkGetNAnchors(AtkHyperlink* link) +{ + // FIXME Do NOT support more than one instance of an AtkObject + // implementing AtkHyperlinkImpl in every instance of AtkHyperLink + auto* accessibleHyperlink = WEBKIT_ACCESSIBLE_HYPERLINK(link); + returnValIfWebKitAccessibleIsInvalid(accessibleHyperlink->priv->hyperlinkImpl, 0); + + return 1; +} + +static gboolean webkitAccessibleHyperlinkIsSelectedLink(AtkHyperlink* link) +{ + auto* accessibleHyperlink = WEBKIT_ACCESSIBLE_HYPERLINK(link); + returnValIfWebKitAccessibleIsInvalid(accessibleHyperlink->priv->hyperlinkImpl, FALSE); + + // Not implemented: this function is deprecated in ATK now + notImplemented(); + return FALSE; +} + +static void webkitAccessibleHyperlinkGetProperty(GObject* object, guint propId, GValue* value, GParamSpec* pspec) +{ + auto* accessibleHyperlink = WEBKIT_ACCESSIBLE_HYPERLINK(object); + + switch (propId) { + case PROP_HYPERLINK_IMPL: + g_value_set_object(value, accessibleHyperlink->priv->hyperlinkImpl); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(object, propId, pspec); + } +} + +static void webkitAccessibleHyperlinkSetProperty(GObject* object, guint propId, const GValue* value, GParamSpec* pspec) +{ + auto* accessibleHyperlink = WEBKIT_ACCESSIBLE_HYPERLINK(object); + + switch (propId) { + case PROP_HYPERLINK_IMPL: + accessibleHyperlink->priv->hyperlinkImpl = WEBKIT_ACCESSIBLE(g_value_get_object(value)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(object, propId, pspec); + } +} + +static void webkit_accessible_hyperlink_class_init(WebKitAccessibleHyperlinkClass* klass) +{ + GObjectClass* gobjectClass = G_OBJECT_CLASS(klass); + gobjectClass->set_property = webkitAccessibleHyperlinkSetProperty; + gobjectClass->get_property = webkitAccessibleHyperlinkGetProperty; + + AtkHyperlinkClass* atkHyperlinkClass = ATK_HYPERLINK_CLASS(klass); + atkHyperlinkClass->get_uri = webkitAccessibleHyperlinkGetURI; + atkHyperlinkClass->get_object = webkitAccessibleHyperlinkGetObject; + atkHyperlinkClass->get_start_index = webkitAccessibleHyperlinkGetStartIndex; + atkHyperlinkClass->get_end_index = webkitAccessibleHyperlinkGetEndIndex; + atkHyperlinkClass->is_valid = webkitAccessibleHyperlinkIsValid; + atkHyperlinkClass->get_n_anchors = webkitAccessibleHyperlinkGetNAnchors; + atkHyperlinkClass->is_selected_link = webkitAccessibleHyperlinkIsSelectedLink; + + g_object_class_install_property(gobjectClass, PROP_HYPERLINK_IMPL, + g_param_spec_object("hyperlink-impl", + "Hyperlink implementation", + "The associated WebKitAccessible instance.", + WEBKIT_TYPE_ACCESSIBLE, + static_cast<GParamFlags>(WEBKIT_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY))); +} + +WebKitAccessibleHyperlink* webkitAccessibleHyperlinkGetOrCreate(AtkHyperlinkImpl* hyperlinkImpl) +{ + g_return_val_if_fail(ATK_IS_HYPERLINK_IMPL(hyperlinkImpl), nullptr); + g_return_val_if_fail(WEBKIT_IS_ACCESSIBLE(hyperlinkImpl), nullptr); + + if (auto* currentHyperLink = g_object_get_data(G_OBJECT(hyperlinkImpl), "webkit-accessible-hyperlink-object")) + return WEBKIT_ACCESSIBLE_HYPERLINK(g_object_ref(currentHyperLink)); + + auto* hyperlink = WEBKIT_ACCESSIBLE_HYPERLINK(g_object_new(WEBKIT_TYPE_ACCESSIBLE_HYPERLINK, "hyperlink-impl", hyperlinkImpl, nullptr)); + g_object_set_data_full(G_OBJECT(hyperlinkImpl), "webkit-accessible-hyperlink-object", hyperlink, g_object_unref); + return hyperlink; +} + +#endif // ENABLE(ACCESSIBILITY) && USE(ATK) diff -urpN webkitgtk-2.38.1.glib244/Source/WebCore/accessibility/atk/WebKitAccessibleHyperlink.h webkitgtk-2.38.1/Source/WebCore/accessibility/atk/WebKitAccessibleHyperlink.h --- webkitgtk-2.38.1.glib244/Source/WebCore/accessibility/atk/WebKitAccessibleHyperlink.h 1969-12-31 18:00:00.000000000 -0600 +++ webkitgtk-2.38.1/Source/WebCore/accessibility/atk/WebKitAccessibleHyperlink.h 2022-10-24 13:34:51.917454604 -0500 @@ -0,0 +1,55 @@ +/* + * Copyright (C) 2010, 2011, 2012 Igalia S.L. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#pragma once + +#if ENABLE(ACCESSIBILITY) && USE(ATK) + +#include <atk/atk.h> + +G_BEGIN_DECLS + +#define WEBKIT_TYPE_ACCESSIBLE_HYPERLINK (webkit_accessible_hyperlink_get_type()) +#define WEBKIT_ACCESSIBLE_HYPERLINK(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), WEBKIT_TYPE_ACCESSIBLE_HYPERLINK, WebKitAccessibleHyperlink)) +#define WEBKIT_ACCESSIBLE_HYPERLINK_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), WEBKIT_TYPE_ACCESSIBLE_HYPERLINK, WebKitAccessibleHyperlinkClass)) +#define WEBKIT_IS_ACCESSIBLE_HYPERLINK(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), WEBKIT_TYPE_ACCESSIBLE_HYPERLINK)) +#define WEBKIT_IS_ACCESSIBLE_HYPERLINK_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), WEBKIT_TYPE_ACCESSIBLE_HYPERLINK)) +#define WEBKIT_ACCESSIBLE_HYPERLINK_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), WEBKIT_TYPE_ACCESSIBLE_HYPERLINK, WebKitAccessibleHyperlinkClass)) + +typedef struct _WebKitAccessibleHyperlink WebKitAccessibleHyperlink; +typedef struct _WebKitAccessibleHyperlinkClass WebKitAccessibleHyperlinkClass; +typedef struct _WebKitAccessibleHyperlinkPrivate WebKitAccessibleHyperlinkPrivate; + +struct _WebKitAccessibleHyperlink { + AtkHyperlink parent; + + WebKitAccessibleHyperlinkPrivate *priv; +}; + +struct _WebKitAccessibleHyperlinkClass { + AtkHyperlinkClass parentClass; +}; + +GType webkit_accessible_hyperlink_get_type(void); + +WebKitAccessibleHyperlink* webkitAccessibleHyperlinkGetOrCreate(AtkHyperlinkImpl*); + +G_END_DECLS + +#endif // ENABLE(ACCESSIBILITY) && USE(ATK) diff -urpN webkitgtk-2.38.1.glib244/Source/WebCore/accessibility/atk/WebKitAccessibleInterfaceAction.cpp webkitgtk-2.38.1/Source/WebCore/accessibility/atk/WebKitAccessibleInterfaceAction.cpp --- webkitgtk-2.38.1.glib244/Source/WebCore/accessibility/atk/WebKitAccessibleInterfaceAction.cpp 1969-12-31 18:00:00.000000000 -0600 +++ webkitgtk-2.38.1/Source/WebCore/accessibility/atk/WebKitAccessibleInterfaceAction.cpp 2022-10-24 13:34:51.917454604 -0500 @@ -0,0 +1,106 @@ +/* + * Copyright (C) 2008 Nuanti Ltd. + * Copyright (C) 2009 Jan Alonzo + * Copyright (C) 2012 Igalia S.L. + * Copyright (C) 2013 Samsung Electronics + * + * Portions from Mozilla a11y, copyright as follows: + * + * The Original Code is mozilla.org code. + * + * The Initial Developer of the Original Code is + * Sun Microsystems, Inc. + * Portions created by the Initial Developer are Copyright (C) 2002 + * the Initial Developer. All Rights Reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include "config.h" +#include "WebKitAccessibleInterfaceAction.h" + +#if ENABLE(ACCESSIBILITY) && USE(ATK) + +#include "AccessibilityObject.h" +#include "NotImplemented.h" +#include "WebKitAccessible.h" +#include "WebKitAccessibleUtil.h" + +using namespace WebCore; + +static AccessibilityObject* core(AtkAction* action) +{ + if (!WEBKIT_IS_ACCESSIBLE(action)) + return 0; + + return &webkitAccessibleGetAccessibilityObject(WEBKIT_ACCESSIBLE(action)); +} + +static gboolean webkitAccessibleActionDoAction(AtkAction* action, gint index) +{ + g_return_val_if_fail(ATK_IS_ACTION(action), FALSE); + g_return_val_if_fail(!index, FALSE); + + returnValIfWebKitAccessibleIsInvalid(WEBKIT_ACCESSIBLE(action), FALSE); + + return core(action)->performDefaultAction(); +} + +static gint webkitAccessibleActionGetNActions(AtkAction* action) +{ + g_return_val_if_fail(ATK_IS_ACTION(action), 0); + returnValIfWebKitAccessibleIsInvalid(WEBKIT_ACCESSIBLE(action), 0); + + return 1; +} + +static const gchar* webkitAccessibleActionGetDescription(AtkAction* action, gint) +{ + g_return_val_if_fail(ATK_IS_ACTION(action), 0); + returnValIfWebKitAccessibleIsInvalid(WEBKIT_ACCESSIBLE(action), 0); + + // TODO: Need a way to provide/localize action descriptions. + notImplemented(); + return ""; +} + +static const gchar* webkitAccessibleActionGetKeybinding(AtkAction* action, gint) +{ + auto* accessible = WEBKIT_ACCESSIBLE(action); + returnValIfWebKitAccessibleIsInvalid(accessible, nullptr); + + // FIXME: Construct a proper keybinding string. + return webkitAccessibleCacheAndReturnAtkProperty(accessible, AtkCachedActionKeyBinding, core(action)->accessKey().utf8()); +} + +static const gchar* webkitAccessibleActionGetName(AtkAction* action, gint) +{ + auto* accessible = WEBKIT_ACCESSIBLE(action); + returnValIfWebKitAccessibleIsInvalid(accessible, nullptr); + + return webkitAccessibleCacheAndReturnAtkProperty(accessible, AtkCachedActionName, core(action)->localizedActionVerb().utf8()); +} + +void webkitAccessibleActionInterfaceInit(AtkActionIface* iface) +{ + iface->do_action = webkitAccessibleActionDoAction; + iface->get_n_actions = webkitAccessibleActionGetNActions; + iface->get_description = webkitAccessibleActionGetDescription; + iface->get_keybinding = webkitAccessibleActionGetKeybinding; + iface->get_name = webkitAccessibleActionGetName; +} + +#endif diff -urpN webkitgtk-2.38.1.glib244/Source/WebCore/accessibility/atk/WebKitAccessibleInterfaceAction.h webkitgtk-2.38.1/Source/WebCore/accessibility/atk/WebKitAccessibleInterfaceAction.h --- webkitgtk-2.38.1.glib244/Source/WebCore/accessibility/atk/WebKitAccessibleInterfaceAction.h 1969-12-31 18:00:00.000000000 -0600 +++ webkitgtk-2.38.1/Source/WebCore/accessibility/atk/WebKitAccessibleInterfaceAction.h 2022-10-24 13:34:51.917454604 -0500 @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2008 Nuanti Ltd. + * Copyright (C) 2009 Jan Alonzo + * Copyright (C) 2012 Igalia S.L. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#pragma once + +#if ENABLE(ACCESSIBILITY) && USE(ATK) + +#include <atk/atk.h> + +void webkitAccessibleActionInterfaceInit(AtkActionIface*); + +#endif // ENABLE(ACCESSIBILITY) && USE(ATK) diff -urpN webkitgtk-2.38.1.glib244/Source/WebCore/accessibility/atk/WebKitAccessibleInterfaceComponent.cpp webkitgtk-2.38.1/Source/WebCore/accessibility/atk/WebKitAccessibleInterfaceComponent.cpp --- webkitgtk-2.38.1.glib244/Source/WebCore/accessibility/atk/WebKitAccessibleInterfaceComponent.cpp 1969-12-31 18:00:00.000000000 -0600 +++ webkitgtk-2.38.1/Source/WebCore/accessibility/atk/WebKitAccessibleInterfaceComponent.cpp 2022-10-24 13:34:51.917454604 -0500 @@ -0,0 +1,172 @@ +/* + * Copyright (C) 2008 Nuanti Ltd. + * Copyright (C) 2009 Jan Alonzo + * Copyright (C) 2009, 2012 Igalia S.L. + * + * Portions from Mozilla a11y, copyright as follows: + * + * The Original Code is mozilla.org code. + * + * The Initial Developer of the Original Code is + * Sun Microsystems, Inc. + * Portions created by the Initial Developer are Copyright (C) 2002 + * the Initial Developer. All Rights Reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include "config.h" +#include "WebKitAccessibleInterfaceComponent.h" + +#if ENABLE(ACCESSIBILITY) && USE(ATK) + +#include "AccessibilityObject.h" +#include "FrameView.h" +#include "IntRect.h" +#include "RenderLayer.h" +#include "WebKitAccessible.h" +#include "WebKitAccessibleUtil.h" + +using namespace WebCore; + +static IntPoint atkToContents(const AccessibilityObject& coreObject, AtkCoordType coordType, gint x, gint y) +{ + auto* frameView = coreObject.documentFrameView(); + if (!frameView) + return { x, y }; + + switch (coordType) { + case ATK_XY_SCREEN: + return frameView->screenToContents({ x, y }); + case ATK_XY_WINDOW: + return frameView->windowToContents({ x, y }); +#if ATK_CHECK_VERSION(2, 30, 0) + case ATK_XY_PARENT: + RELEASE_ASSERT_NOT_REACHED(); +#endif + } + + return { x, y }; +} + +static AtkObject* webkitAccessibleComponentRefAccessibleAtPoint(AtkComponent* component, gint x, gint y, AtkCoordType coordType) +{ + auto* accessible = WEBKIT_ACCESSIBLE(component); + returnValIfWebKitAccessibleIsInvalid(accessible, nullptr); + + auto& coreObject = webkitAccessibleGetAccessibilityObject(accessible); + auto* target = downcast<AccessibilityObject>(coreObject.accessibilityHitTest(atkToContents(coreObject, coordType, x, y))); + if (!target) + return nullptr; + + if (auto* wrapper = target->wrapper()) + return ATK_OBJECT(g_object_ref(wrapper)); + + return nullptr; +} + +static void webkitAccessibleComponentGetExtents(AtkComponent* component, gint* x, gint* y, gint* width, gint* height, AtkCoordType coordType) +{ + auto* accessible = WEBKIT_ACCESSIBLE(component); + returnIfWebKitAccessibleIsInvalid(accessible); + + auto& coreObject = webkitAccessibleGetAccessibilityObject(accessible); + contentsRelativeToAtkCoordinateType(&coreObject, coordType, snappedIntRect(coreObject.elementRect()), x, y, width, height); +} + +static gboolean webkitAccessibleComponentGrabFocus(AtkComponent* component) +{ + auto* accessible = WEBKIT_ACCESSIBLE(component); + returnValIfWebKitAccessibleIsInvalid(accessible, FALSE); + + auto& coreObject = webkitAccessibleGetAccessibilityObject(accessible); + coreObject.setFocused(true); + return coreObject.isFocused(); +} + +#if ATK_CHECK_VERSION(2, 30, 0) +static gboolean webkitAccessibleComponentScrollTo(AtkComponent* component, AtkScrollType scrollType) +{ + auto* accessible = WEBKIT_ACCESSIBLE(component); + returnValIfWebKitAccessibleIsInvalid(accessible, FALSE); + + ScrollAlignment alignX; + ScrollAlignment alignY; + + switch (scrollType) { + case ATK_SCROLL_TOP_LEFT: + alignX = ScrollAlignment::alignLeftAlways; + alignY = ScrollAlignment::alignTopAlways; + break; + case ATK_SCROLL_BOTTOM_RIGHT: + alignX = ScrollAlignment::alignRightAlways; + alignY = ScrollAlignment::alignBottomAlways; + break; + case ATK_SCROLL_TOP_EDGE: + case ATK_SCROLL_BOTTOM_EDGE: + // Align to a particular edge is not supported, it's always the closest edge. + alignX = ScrollAlignment::alignCenterIfNeeded; + alignY = ScrollAlignment::alignToEdgeIfNeeded; + break; + case ATK_SCROLL_LEFT_EDGE: + case ATK_SCROLL_RIGHT_EDGE: + // Align to a particular edge is not supported, it's always the closest edge. + alignX = ScrollAlignment::alignToEdgeIfNeeded; + alignY = ScrollAlignment::alignCenterIfNeeded; + break; + case ATK_SCROLL_ANYWHERE: + alignX = ScrollAlignment::alignCenterIfNeeded; + alignY = ScrollAlignment::alignCenterIfNeeded; + break; + } + + auto& coreObject = webkitAccessibleGetAccessibilityObject(accessible); + coreObject.scrollToMakeVisible({ SelectionRevealMode::Reveal, alignX, alignY, ShouldAllowCrossOriginScrolling::Yes }); + + return TRUE; +} + +static gboolean webkitAccessibleComponentScrollToPoint(AtkComponent* component, AtkCoordType coordType, gint x, gint y) +{ + auto* accessible = WEBKIT_ACCESSIBLE(component); + returnValIfWebKitAccessibleIsInvalid(accessible, FALSE); + + auto& coreObject = webkitAccessibleGetAccessibilityObject(accessible); + + IntPoint point(x, y); + if (coordType == ATK_XY_SCREEN) { + if (auto* frameView = coreObject.documentFrameView()) + point = frameView->contentsToWindow(frameView->screenToContents(point)); + } + + coreObject.scrollToGlobalPoint(point); + + return TRUE; +} +#endif + +void webkitAccessibleComponentInterfaceInit(AtkComponentIface* iface) +{ + iface->ref_accessible_at_point = webkitAccessibleComponentRefAccessibleAtPoint; + iface->get_extents = webkitAccessibleComponentGetExtents; + iface->grab_focus = webkitAccessibleComponentGrabFocus; +#if ATK_CHECK_VERSION(2, 30, 0) + iface->scroll_to = webkitAccessibleComponentScrollTo; + iface->scroll_to_point = webkitAccessibleComponentScrollToPoint; +#endif +} + +#endif // ENABLE(ACCESSIBILITY) && USE(ATK) diff -urpN webkitgtk-2.38.1.glib244/Source/WebCore/accessibility/atk/WebKitAccessibleInterfaceComponent.h webkitgtk-2.38.1/Source/WebCore/accessibility/atk/WebKitAccessibleInterfaceComponent.h --- webkitgtk-2.38.1.glib244/Source/WebCore/accessibility/atk/WebKitAccessibleInterfaceComponent.h 1969-12-31 18:00:00.000000000 -0600 +++ webkitgtk-2.38.1/Source/WebCore/accessibility/atk/WebKitAccessibleInterfaceComponent.h 2022-10-24 13:34:51.921454626 -0500 @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2008 Nuanti Ltd. + * Copyright (C) 2009 Jan Alonzo + * Copyright (C) 2009, 2012 Igalia S.L. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#pragma once + +#if ENABLE(ACCESSIBILITY) && USE(ATK) + +#include <atk/atk.h> + +void webkitAccessibleComponentInterfaceInit(AtkComponentIface*); + +#endif // ENABLE(ACCESSIBILITY) && USE(ATK) diff -urpN webkitgtk-2.38.1.glib244/Source/WebCore/accessibility/atk/WebKitAccessibleInterfaceDocument.cpp webkitgtk-2.38.1/Source/WebCore/accessibility/atk/WebKitAccessibleInterfaceDocument.cpp --- webkitgtk-2.38.1.glib244/Source/WebCore/accessibility/atk/WebKitAccessibleInterfaceDocument.cpp 1969-12-31 18:00:00.000000000 -0600 +++ webkitgtk-2.38.1/Source/WebCore/accessibility/atk/WebKitAccessibleInterfaceDocument.cpp 2022-10-24 13:34:51.921454626 -0500 @@ -0,0 +1,124 @@ +/* + * Copyright (C) 2008 Nuanti Ltd. + * Copyright (C) 2009 Jan Alonzo + * Copyright (C) 2012 Igalia S.L. + * Copyright (C) 2013 Samsung Electronics + * + * Portions from Mozilla a11y, copyright as follows: + * + * The Original Code is mozilla.org code. + * + * The Initial Developer of the Original Code is + * Sun Microsystems, Inc. + * Portions created by the Initial Developer are Copyright (C) 2002 + * the Initial Developer. All Rights Reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include "config.h" +#include "WebKitAccessibleInterfaceDocument.h" + +#if ENABLE(ACCESSIBILITY) && USE(ATK) + +#include "AccessibilityObject.h" +#include "DocumentInlines.h" +#include "DocumentType.h" +#include "WebKitAccessible.h" +#include "WebKitAccessibleUtil.h" + +using namespace WebCore; + +static AccessibilityObject* core(AtkDocument* document) +{ + if (!WEBKIT_IS_ACCESSIBLE(document)) + return 0; + + return &webkitAccessibleGetAccessibilityObject(WEBKIT_ACCESSIBLE(document)); +} + +static const gchar* documentAttributeValue(AtkDocument* document, const gchar* attribute) +{ + Document* coreDocument = core(document)->document(); + if (!coreDocument) + return 0; + + String value; + AtkCachedProperty atkCachedProperty; + + if (!g_ascii_strcasecmp(attribute, "DocType") && coreDocument->doctype()) { + value = coreDocument->doctype()->name(); + atkCachedProperty = AtkCachedDocumentType; + } else if (!g_ascii_strcasecmp(attribute, "Encoding")) { + value = coreDocument->charset(); + atkCachedProperty = AtkCachedDocumentEncoding; + } else if (!g_ascii_strcasecmp(attribute, "URI")) { + value = coreDocument->documentURI(); + atkCachedProperty = AtkCachedDocumentURI; + } + + if (!value.isEmpty()) + return webkitAccessibleCacheAndReturnAtkProperty(WEBKIT_ACCESSIBLE(document), atkCachedProperty, value.utf8()); + + return 0; +} + +static const gchar* webkitAccessibleDocumentGetAttributeValue(AtkDocument* document, const gchar* attribute) +{ + g_return_val_if_fail(ATK_IS_DOCUMENT(document), 0); + returnValIfWebKitAccessibleIsInvalid(WEBKIT_ACCESSIBLE(document), 0); + + return documentAttributeValue(document, attribute); +} + +static AtkAttributeSet* webkitAccessibleDocumentGetAttributes(AtkDocument* document) +{ + g_return_val_if_fail(ATK_IS_DOCUMENT(document), 0); + returnValIfWebKitAccessibleIsInvalid(WEBKIT_ACCESSIBLE(document), 0); + + AtkAttributeSet* attributeSet = nullptr; + const gchar* attributes[] = { "DocType", "Encoding", "URI" }; + + for (unsigned i = 0; i < G_N_ELEMENTS(attributes); i++) { + const gchar* value = documentAttributeValue(document, attributes[i]); + if (value) + attributeSet = addToAtkAttributeSet(attributeSet, attributes[i], value); + } + + return attributeSet; +} + +static const gchar* webkitAccessibleDocumentGetLocale(AtkDocument* document) +{ + g_return_val_if_fail(ATK_IS_DOCUMENT(document), 0); + returnValIfWebKitAccessibleIsInvalid(WEBKIT_ACCESSIBLE(document), 0); + + // The logic to resolve locale has been moved to + // AtkObject::get_object_locale() virtual method. However, to avoid breaking + // clients expecting the deprecated AtkDocumentIface::get_document_locale() + // to be overriden, method is kept and chained up to + // AtkObject::get_object_locale(). <https://bugs.webkit.org/show_bug.cgi?id=115647> + return atk_object_get_object_locale(ATK_OBJECT(document)); +} + +void webkitAccessibleDocumentInterfaceInit(AtkDocumentIface* iface) +{ + iface->get_document_attribute_value = webkitAccessibleDocumentGetAttributeValue; + iface->get_document_attributes = webkitAccessibleDocumentGetAttributes; + iface->get_document_locale = webkitAccessibleDocumentGetLocale; +} + +#endif diff -urpN webkitgtk-2.38.1.glib244/Source/WebCore/accessibility/atk/WebKitAccessibleInterfaceDocument.h webkitgtk-2.38.1/Source/WebCore/accessibility/atk/WebKitAccessibleInterfaceDocument.h --- webkitgtk-2.38.1.glib244/Source/WebCore/accessibility/atk/WebKitAccessibleInterfaceDocument.h 1969-12-31 18:00:00.000000000 -0600 +++ webkitgtk-2.38.1/Source/WebCore/accessibility/atk/WebKitAccessibleInterfaceDocument.h 2022-10-24 13:34:51.921454626 -0500 @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2008 Nuanti Ltd. + * Copyright (C) 2009 Jan Alonzo + * Copyright (C) 2012 Igalia S.L. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#pragma once + +#if ENABLE(ACCESSIBILITY) && USE(ATK) + +#include <atk/atk.h> + +void webkitAccessibleDocumentInterfaceInit(AtkDocumentIface*); + +#endif // ENABLE(ACCESSIBILITY) && USE(ATK) diff -urpN webkitgtk-2.38.1.glib244/Source/WebCore/accessibility/atk/WebKitAccessibleInterfaceEditableText.cpp webkitgtk-2.38.1/Source/WebCore/accessibility/atk/WebKitAccessibleInterfaceEditableText.cpp --- webkitgtk-2.38.1.glib244/Source/WebCore/accessibility/atk/WebKitAccessibleInterfaceEditableText.cpp 1969-12-31 18:00:00.000000000 -0600 +++ webkitgtk-2.38.1/Source/WebCore/accessibility/atk/WebKitAccessibleInterfaceEditableText.cpp 2022-10-24 13:34:51.921454626 -0500 @@ -0,0 +1,150 @@ +/* + * Copyright (C) 2008 Nuanti Ltd. + * Copyright (C) 2009 Jan Alonzo + * Copyright (C) 2011, 2012 Igalia S.L. + * + * Portions from Mozilla a11y, copyright as follows: + * + * The Original Code is mozilla.org code. + * + * The Initial Developer of the Original Code is + * Sun Microsystems, Inc. + * Portions created by the Initial Developer are Copyright (C) 2002 + * the Initial Developer. All Rights Reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include "config.h" +#include "WebKitAccessibleInterfaceEditableText.h" + +#if ENABLE(ACCESSIBILITY) && USE(ATK) + +#include "AccessibilityObject.h" +#include "Document.h" +#include "Editor.h" +#include "Frame.h" +#include "NotImplemented.h" +#include "WebKitAccessible.h" +#include "WebKitAccessibleUtil.h" + +using namespace WebCore; + +static AccessibilityObject* core(AtkEditableText* text) +{ + if (!WEBKIT_IS_ACCESSIBLE(text)) + return 0; + + return &webkitAccessibleGetAccessibilityObject(WEBKIT_ACCESSIBLE(text)); +} + +static gboolean webkitAccessibleEditableTextSetRunAttributes(AtkEditableText* text, AtkAttributeSet*, gint, gint) +{ + g_return_val_if_fail(ATK_IS_EDITABLE_TEXT(text), FALSE); + returnValIfWebKitAccessibleIsInvalid(WEBKIT_ACCESSIBLE(text), FALSE); + + notImplemented(); + return FALSE; +} + +static void webkitAccessibleEditableTextSetTextContents(AtkEditableText* text, const gchar* string) +{ + g_return_if_fail(ATK_IS_EDITABLE_TEXT(text)); + returnIfWebKitAccessibleIsInvalid(WEBKIT_ACCESSIBLE(text)); + + // FIXME: string nullcheck? + core(text)->setValue(String::fromUTF8(string)); +} + +static void webkitAccessibleEditableTextInsertText(AtkEditableText* text, const gchar* string, gint length, gint* position) +{ + g_return_if_fail(ATK_IS_EDITABLE_TEXT(text)); + returnIfWebKitAccessibleIsInvalid(WEBKIT_ACCESSIBLE(text)); + + if (!string) + return; + + AccessibilityObject* coreObject = core(text); + // FIXME: Not implemented in WebCore + // coreObject->setSelectedTextRange(PlainTextRange(*position, 0)); + // coreObject->setSelectedText(String::fromUTF8(string)); + + Document* document = coreObject->document(); + if (!document || !document->frame()) + return; + + coreObject->setSelectedVisiblePositionRange(coreObject->visiblePositionRangeForRange(PlainTextRange(*position, 0))); + coreObject->setFocused(true); + // FIXME: We should set position to the actual inserted text length, which may be less than that requested. + if (document->frame()->editor().insertTextWithoutSendingTextEvent(String::fromUTF8(string).substring(0, length), false, 0)) + *position += length; +} + +static void webkitAccessibleEditableTextCopyText(AtkEditableText* text, gint, gint) +{ + g_return_if_fail(ATK_IS_EDITABLE_TEXT(text)); + returnIfWebKitAccessibleIsInvalid(WEBKIT_ACCESSIBLE(text)); + + notImplemented(); +} + +static void webkitAccessibleEditableTextCutText(AtkEditableText* text, gint, gint) +{ + g_return_if_fail(ATK_IS_EDITABLE_TEXT(text)); + returnIfWebKitAccessibleIsInvalid(WEBKIT_ACCESSIBLE(text)); + + notImplemented(); +} + +static void webkitAccessibleEditableTextDeleteText(AtkEditableText* text, gint startPos, gint endPos) +{ + g_return_if_fail(ATK_IS_EDITABLE_TEXT(text)); + returnIfWebKitAccessibleIsInvalid(WEBKIT_ACCESSIBLE(text)); + + AccessibilityObject* coreObject = core(text); + // FIXME: Not implemented in WebCore + // coreObject->setSelectedTextRange(PlainTextRange(startPos, endPos - startPos)); + // coreObject->setSelectedText(String()); + + Document* document = coreObject->document(); + if (!document || !document->frame()) + return; + + coreObject->setSelectedVisiblePositionRange(coreObject->visiblePositionRangeForRange(PlainTextRange(startPos, endPos - startPos))); + coreObject->setFocused(true); + document->frame()->editor().performDelete(); +} + +static void webkitAccessibleEditableTextPasteText(AtkEditableText* text, gint) +{ + g_return_if_fail(ATK_IS_EDITABLE_TEXT(text)); + returnIfWebKitAccessibleIsInvalid(WEBKIT_ACCESSIBLE(text)); + + notImplemented(); +} + +void webkitAccessibleEditableTextInterfaceInit(AtkEditableTextIface* iface) +{ + iface->set_run_attributes = webkitAccessibleEditableTextSetRunAttributes; + iface->set_text_contents = webkitAccessibleEditableTextSetTextContents; + iface->insert_text = webkitAccessibleEditableTextInsertText; + iface->copy_text = webkitAccessibleEditableTextCopyText; + iface->cut_text = webkitAccessibleEditableTextCutText; + iface->delete_text = webkitAccessibleEditableTextDeleteText; + iface->paste_text = webkitAccessibleEditableTextPasteText; +} + +#endif diff -urpN webkitgtk-2.38.1.glib244/Source/WebCore/accessibility/atk/WebKitAccessibleInterfaceEditableText.h webkitgtk-2.38.1/Source/WebCore/accessibility/atk/WebKitAccessibleInterfaceEditableText.h --- webkitgtk-2.38.1.glib244/Source/WebCore/accessibility/atk/WebKitAccessibleInterfaceEditableText.h 1969-12-31 18:00:00.000000000 -0600 +++ webkitgtk-2.38.1/Source/WebCore/accessibility/atk/WebKitAccessibleInterfaceEditableText.h 2022-10-24 13:34:51.921454626 -0500 @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2008 Nuanti Ltd. + * Copyright (C) 2009 Jan Alonzo + * Copyright (C) 2011, 2012 Igalia S.L. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#pragma once + +#if ENABLE(ACCESSIBILITY) && USE(ATK) + +#include <atk/atk.h> + +void webkitAccessibleEditableTextInterfaceInit(AtkEditableTextIface*); + +#endif // ENABLE(ACCESSIBILITY) && USE(ATK) diff -urpN webkitgtk-2.38.1.glib244/Source/WebCore/accessibility/atk/WebKitAccessibleInterfaceHyperlinkImpl.cpp webkitgtk-2.38.1/Source/WebCore/accessibility/atk/WebKitAccessibleInterfaceHyperlinkImpl.cpp --- webkitgtk-2.38.1.glib244/Source/WebCore/accessibility/atk/WebKitAccessibleInterfaceHyperlinkImpl.cpp 1969-12-31 18:00:00.000000000 -0600 +++ webkitgtk-2.38.1/Source/WebCore/accessibility/atk/WebKitAccessibleInterfaceHyperlinkImpl.cpp 2022-10-24 13:34:51.921454626 -0500 @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2010, 2011, 2012 Igalia S.L. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include "config.h" +#include "WebKitAccessibleInterfaceHyperlinkImpl.h" + +#if ENABLE(ACCESSIBILITY) && USE(ATK) + +#include "WebKitAccessibleHyperlink.h" + +static AtkHyperlink* webkitAccessibleHyperlinkImplGetHyperlink(AtkHyperlinkImpl* hyperlink) +{ + return ATK_HYPERLINK(webkitAccessibleHyperlinkGetOrCreate(hyperlink)); +} + +void webkitAccessibleHyperlinkImplInterfaceInit(AtkHyperlinkImplIface* iface) +{ + iface->get_hyperlink = webkitAccessibleHyperlinkImplGetHyperlink; +} + +#endif // ENABLE(ACCESSIBILITY) && USE(ATK) diff -urpN webkitgtk-2.38.1.glib244/Source/WebCore/accessibility/atk/WebKitAccessibleInterfaceHyperlinkImpl.h webkitgtk-2.38.1/Source/WebCore/accessibility/atk/WebKitAccessibleInterfaceHyperlinkImpl.h --- webkitgtk-2.38.1.glib244/Source/WebCore/accessibility/atk/WebKitAccessibleInterfaceHyperlinkImpl.h 1969-12-31 18:00:00.000000000 -0600 +++ webkitgtk-2.38.1/Source/WebCore/accessibility/atk/WebKitAccessibleInterfaceHyperlinkImpl.h 2022-10-24 13:34:51.921454626 -0500 @@ -0,0 +1,28 @@ +/* + * Copyright (C) 2010, 2011, 2012 Igalia S.L. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#pragma once + +#if ENABLE(ACCESSIBILITY) && USE(ATK) + +#include <atk/atk.h> + +void webkitAccessibleHyperlinkImplInterfaceInit(AtkHyperlinkImplIface*); + +#endif // ENABLE(ACCESSIBILITY) && USE(ATK) diff -urpN webkitgtk-2.38.1.glib244/Source/WebCore/accessibility/atk/WebKitAccessibleInterfaceHypertext.cpp webkitgtk-2.38.1/Source/WebCore/accessibility/atk/WebKitAccessibleInterfaceHypertext.cpp --- webkitgtk-2.38.1.glib244/Source/WebCore/accessibility/atk/WebKitAccessibleInterfaceHypertext.cpp 1969-12-31 18:00:00.000000000 -0600 +++ webkitgtk-2.38.1/Source/WebCore/accessibility/atk/WebKitAccessibleInterfaceHypertext.cpp 2022-10-24 13:34:51.921454626 -0500 @@ -0,0 +1,116 @@ +/* + * Copyright (C) 2010, 2011, 2012 Igalia S.L. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include "config.h" +#include "WebKitAccessibleInterfaceHypertext.h" + +#if ENABLE(ACCESSIBILITY) && USE(ATK) + +#include "AccessibilityObject.h" +#include "WebKitAccessible.h" +#include "WebKitAccessibleUtil.h" + +using namespace WebCore; + +static AccessibilityObject* core(AtkHypertext* hypertext) +{ + if (!WEBKIT_IS_ACCESSIBLE(hypertext)) + return 0; + + return &webkitAccessibleGetAccessibilityObject(WEBKIT_ACCESSIBLE(hypertext)); +} + +static AtkHyperlink* webkitAccessibleHypertextGetLink(AtkHypertext* hypertext, gint index) +{ + g_return_val_if_fail(ATK_HYPERTEXT(hypertext), 0); + returnValIfWebKitAccessibleIsInvalid(WEBKIT_ACCESSIBLE(hypertext), 0); + + const AccessibilityObject::AccessibilityChildrenVector& children = core(hypertext)->children(); + if (index < 0 || static_cast<unsigned>(index) >= children.size()) + return 0; + + gint currentLink = -1; + for (const auto& child : children) { + AXCoreObject* coreChild = child.get(); + if (!coreChild->accessibilityIsIgnored()) { + auto* axObject = coreChild->wrapper(); + if (!axObject || !ATK_IS_HYPERLINK_IMPL(axObject)) + continue; + + currentLink++; + if (index != currentLink) + continue; + + return atk_hyperlink_impl_get_hyperlink(ATK_HYPERLINK_IMPL(axObject)); + } + } + + return 0; +} + +static gint webkitAccessibleHypertextGetNLinks(AtkHypertext* hypertext) +{ + g_return_val_if_fail(ATK_HYPERTEXT(hypertext), 0); + returnValIfWebKitAccessibleIsInvalid(WEBKIT_ACCESSIBLE(hypertext), 0); + + const AccessibilityObject::AccessibilityChildrenVector& children = core(hypertext)->children(); + gint linksFound = 0; + for (const auto& child : children) { + AXCoreObject* coreChild = child.get(); + if (!coreChild->accessibilityIsIgnored()) { + auto* axObject = coreChild->wrapper(); + if (axObject && ATK_IS_HYPERLINK_IMPL(axObject)) + linksFound++; + } + } + + return linksFound; +} + +static gint webkitAccessibleHypertextGetLinkIndex(AtkHypertext* hypertext, gint charIndex) +{ + g_return_val_if_fail(ATK_HYPERTEXT(hypertext), -1); + returnValIfWebKitAccessibleIsInvalid(WEBKIT_ACCESSIBLE(hypertext), -1); + + size_t linksCount = webkitAccessibleHypertextGetNLinks(hypertext); + if (!linksCount) + return -1; + + for (size_t i = 0; i < linksCount; i++) { + AtkHyperlink* hyperlink = ATK_HYPERLINK(webkitAccessibleHypertextGetLink(hypertext, i)); + gint startIndex = atk_hyperlink_get_start_index(hyperlink); + gint endIndex = atk_hyperlink_get_end_index(hyperlink); + + // Check if the char index in the link's offset range + if (startIndex <= charIndex && charIndex < endIndex) + return i; + } + + // Not found if reached + return -1; +} + +void webkitAccessibleHypertextInterfaceInit(AtkHypertextIface* iface) +{ + iface->get_link = webkitAccessibleHypertextGetLink; + iface->get_n_links = webkitAccessibleHypertextGetNLinks; + iface->get_link_index = webkitAccessibleHypertextGetLinkIndex; +} + +#endif diff -urpN webkitgtk-2.38.1.glib244/Source/WebCore/accessibility/atk/WebKitAccessibleInterfaceHypertext.h webkitgtk-2.38.1/Source/WebCore/accessibility/atk/WebKitAccessibleInterfaceHypertext.h --- webkitgtk-2.38.1.glib244/Source/WebCore/accessibility/atk/WebKitAccessibleInterfaceHypertext.h 1969-12-31 18:00:00.000000000 -0600 +++ webkitgtk-2.38.1/Source/WebCore/accessibility/atk/WebKitAccessibleInterfaceHypertext.h 2022-10-24 13:34:51.921454626 -0500 @@ -0,0 +1,28 @@ +/* + * Copyright (C) 2010, 2011, 2012 Igalia S.L. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#pragma once + +#if ENABLE(ACCESSIBILITY) && USE(ATK) + +#include <atk/atk.h> + +void webkitAccessibleHypertextInterfaceInit(AtkHypertextIface*); + +#endif // ENABLE(ACCESSIBILITY) && USE(ATK) diff -urpN webkitgtk-2.38.1.glib244/Source/WebCore/accessibility/atk/WebKitAccessibleInterfaceImage.cpp webkitgtk-2.38.1/Source/WebCore/accessibility/atk/WebKitAccessibleInterfaceImage.cpp --- webkitgtk-2.38.1.glib244/Source/WebCore/accessibility/atk/WebKitAccessibleInterfaceImage.cpp 1969-12-31 18:00:00.000000000 -0600 +++ webkitgtk-2.38.1/Source/WebCore/accessibility/atk/WebKitAccessibleInterfaceImage.cpp 2022-10-24 13:34:51.921454626 -0500 @@ -0,0 +1,89 @@ +/* + * Copyright (C) 2008 Nuanti Ltd. + * Copyright (C) 2009 Jan Alonzo + * Copyright (C) 2009, 2012 Igalia S.L. + * Copyright (C) 2013 Samsung Electronics + * + * Portions from Mozilla a11y, copyright as follows: + * + * The Original Code is mozilla.org code. + * + * The Initial Developer of the Original Code is + * Sun Microsystems, Inc. + * Portions created by the Initial Developer are Copyright (C) 2002 + * the Initial Developer. All Rights Reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include "config.h" +#include "WebKitAccessibleInterfaceImage.h" + +#if ENABLE(ACCESSIBILITY) && USE(ATK) + +#include "AccessibilityObject.h" +#include "IntRect.h" +#include "WebKitAccessible.h" +#include "WebKitAccessibleUtil.h" + +using namespace WebCore; + +static AccessibilityObject* core(AtkImage* image) +{ + if (!WEBKIT_IS_ACCESSIBLE(image)) + return 0; + + return &webkitAccessibleGetAccessibilityObject(WEBKIT_ACCESSIBLE(image)); +} + +static void webkitAccessibleImageGetImagePosition(AtkImage* image, gint* x, gint* y, AtkCoordType coordType) +{ + g_return_if_fail(ATK_IMAGE(image)); + returnIfWebKitAccessibleIsInvalid(WEBKIT_ACCESSIBLE(image)); + + IntRect rect = snappedIntRect(core(image)->elementRect()); + contentsRelativeToAtkCoordinateType(core(image), coordType, rect, x, y); +} + +static const gchar* webkitAccessibleImageGetImageDescription(AtkImage* image) +{ + auto* accessible = WEBKIT_ACCESSIBLE(image); + returnValIfWebKitAccessibleIsInvalid(accessible, nullptr); + + return webkitAccessibleCacheAndReturnAtkProperty(accessible, AtkCachedImageDescription, accessibilityDescription(core(image)).utf8()); +} + +static void webkitAccessibleImageGetImageSize(AtkImage* image, gint* width, gint* height) +{ + g_return_if_fail(ATK_IMAGE(image)); + returnIfWebKitAccessibleIsInvalid(WEBKIT_ACCESSIBLE(image)); + + IntSize size = snappedIntRect(core(image)->elementRect()).size(); + + if (width) + *width = size.width(); + if (height) + *height = size.height(); +} + +void webkitAccessibleImageInterfaceInit(AtkImageIface* iface) +{ + iface->get_image_position = webkitAccessibleImageGetImagePosition; + iface->get_image_description = webkitAccessibleImageGetImageDescription; + iface->get_image_size = webkitAccessibleImageGetImageSize; +} + +#endif diff -urpN webkitgtk-2.38.1.glib244/Source/WebCore/accessibility/atk/WebKitAccessibleInterfaceImage.h webkitgtk-2.38.1/Source/WebCore/accessibility/atk/WebKitAccessibleInterfaceImage.h --- webkitgtk-2.38.1.glib244/Source/WebCore/accessibility/atk/WebKitAccessibleInterfaceImage.h 1969-12-31 18:00:00.000000000 -0600 +++ webkitgtk-2.38.1/Source/WebCore/accessibility/atk/WebKitAccessibleInterfaceImage.h 2022-10-24 13:34:51.921454626 -0500 @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2008 Nuanti Ltd. + * Copyright (C) 2009 Jan Alonzo + * Copyright (C) 2009, 2012 Igalia S.L. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#pragma once + +#if ENABLE(ACCESSIBILITY) && USE(ATK) + +#include <atk/atk.h> + +void webkitAccessibleImageInterfaceInit(AtkImageIface*); + +#endif // ENABLE(ACCESSIBILITY) && USE(ATK) diff -urpN webkitgtk-2.38.1.glib244/Source/WebCore/accessibility/atk/WebKitAccessibleInterfaceSelection.cpp webkitgtk-2.38.1/Source/WebCore/accessibility/atk/WebKitAccessibleInterfaceSelection.cpp --- webkitgtk-2.38.1.glib244/Source/WebCore/accessibility/atk/WebKitAccessibleInterfaceSelection.cpp 1969-12-31 18:00:00.000000000 -0600 +++ webkitgtk-2.38.1/Source/WebCore/accessibility/atk/WebKitAccessibleInterfaceSelection.cpp 2022-10-24 13:34:51.921454626 -0500 @@ -0,0 +1,256 @@ +/* + * Copyright (C) 2008 Nuanti Ltd. + * Copyright (C) 2009 Jan Alonzo + * Copyright (C) 2010, 2011, 2012 Igalia S.L. + * + * Portions from Mozilla a11y, copyright as follows: + * + * The Original Code is mozilla.org code. + * + * The Initial Developer of the Original Code is + * Sun Microsystems, Inc. + * Portions created by the Initial Developer are Copyright (C) 2002 + * the Initial Developer. All Rights Reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include "config.h" +#include "WebKitAccessibleInterfaceSelection.h" + +#if ENABLE(ACCESSIBILITY) && USE(ATK) + +#include "AccessibilityListBox.h" +#include "AccessibilityObject.h" +#include "HTMLSelectElement.h" +#include "RenderObject.h" +#include "WebKitAccessible.h" +#include "WebKitAccessibleUtil.h" + +using namespace WebCore; + +static AccessibilityObject* core(AtkSelection* selection) +{ + if (!WEBKIT_IS_ACCESSIBLE(selection)) + return nullptr; + + return &webkitAccessibleGetAccessibilityObject(WEBKIT_ACCESSIBLE(selection)); +} + +static AXCoreObject* listObjectForCoreSelection(AccessibilityObject* coreSelection) +{ + // Only list boxes and menu lists supported so far. + if (!coreSelection->isListBox() && !coreSelection->isMenuList()) + return nullptr; + + // For list boxes the list object is just itself. + if (coreSelection->isListBox()) + return coreSelection; + + // For menu lists we need to return the first accessible child, + // with role MenuListPopupRole, since that's the one holding the list + // of items with role MenuListOptionRole. + const AccessibilityObject::AccessibilityChildrenVector& children = coreSelection->children(); + if (!children.size()) + return nullptr; + + AXCoreObject* listObject = children.at(0).get(); + if (!listObject->isMenuListPopup()) + return nullptr; + + return listObject; +} + +static AXCoreObject* optionFromList(AtkSelection* selection, gint index) +{ + AccessibilityObject* coreSelection = core(selection); + if (!coreSelection || index < 0) + return nullptr; + + // Need to select the proper list object depending on the type. + AXCoreObject* listObject = listObjectForCoreSelection(coreSelection); + if (!listObject) + return nullptr; + + const AccessibilityObject::AccessibilityChildrenVector& options = listObject->children(); + if (index < static_cast<gint>(options.size())) + return options.at(index).get(); + + return nullptr; +} + +static AXCoreObject* optionFromSelection(AtkSelection* selection, gint index) +{ + AccessibilityObject* coreSelection = core(selection); + if (!coreSelection || !coreSelection->isAccessibilityRenderObject() || index < 0) + return nullptr; + + // This method provides the functionality expected by atk_selection_ref_selection(). + // According to the ATK documentation for this method, the index is "a gint specifying + // the index in the selection set. (e.g. the ith selection as opposed to the ith child)." + // There is different API, namely atk_object_ref_accessible_child(), when the ith child + // from the set of all children is sought. + AccessibilityObject::AccessibilityChildrenVector options; + coreSelection->selectedChildren(options); + if (index < static_cast<gint>(options.size())) + return options.at(index).get(); + + return nullptr; +} + +static gboolean webkitAccessibleSelectionAddSelection(AtkSelection* selection, gint index) +{ + g_return_val_if_fail(ATK_SELECTION(selection), FALSE); + returnValIfWebKitAccessibleIsInvalid(WEBKIT_ACCESSIBLE(selection), FALSE); + + AccessibilityObject* coreSelection = core(selection); + if (!coreSelection) + return FALSE; + + AXCoreObject* option = optionFromList(selection, index); + if (option && (coreSelection->isListBox() || coreSelection->isMenuList())) { + option->setSelected(true); + return option->isSelected(); + } + + return FALSE; +} + +static gboolean webkitAccessibleSelectionClearSelection(AtkSelection* selection) +{ + g_return_val_if_fail(ATK_SELECTION(selection), FALSE); + returnValIfWebKitAccessibleIsInvalid(WEBKIT_ACCESSIBLE(selection), FALSE); + + AccessibilityObject* coreSelection = core(selection); + if (!coreSelection) + return FALSE; + + AccessibilityObject::AccessibilityChildrenVector selectedItems; + if (is<AccessibilityListBox>(*coreSelection)) { + // Set the list of selected items to an empty list; then verify that it worked. + auto& listBox = downcast<AccessibilityListBox>(*coreSelection); + listBox.setSelectedChildren(selectedItems); + listBox.selectedChildren(selectedItems); + return selectedItems.isEmpty(); + } + return FALSE; +} + +static AtkObject* webkitAccessibleSelectionRefSelection(AtkSelection* selection, gint index) +{ + g_return_val_if_fail(ATK_SELECTION(selection), nullptr); + returnValIfWebKitAccessibleIsInvalid(WEBKIT_ACCESSIBLE(selection), nullptr); + + AXCoreObject* option = optionFromSelection(selection, index); + if (option) { + auto* child = option->wrapper(); + g_object_ref(child); + return ATK_OBJECT(child); + } + + return nullptr; +} + +static gint webkitAccessibleSelectionGetSelectionCount(AtkSelection* selection) +{ + g_return_val_if_fail(ATK_SELECTION(selection), 0); + returnValIfWebKitAccessibleIsInvalid(WEBKIT_ACCESSIBLE(selection), 0); + + AccessibilityObject* coreSelection = core(selection); + if (!coreSelection || !coreSelection->isAccessibilityRenderObject()) + return 0; + + if (coreSelection->isMenuList()) { + RenderObject* renderer = coreSelection->renderer(); + if (!renderer) + return 0; + + int selectedIndex = downcast<HTMLSelectElement>(renderer->node())->selectedIndex(); + return selectedIndex >= 0 && selectedIndex < static_cast<int>(downcast<HTMLSelectElement>(renderer->node())->listItems().size()); + } + + AccessibilityObject::AccessibilityChildrenVector selectedItems; + coreSelection->selectedChildren(selectedItems); + return static_cast<gint>(selectedItems.size()); +} + +static gboolean webkitAccessibleSelectionIsChildSelected(AtkSelection* selection, gint index) +{ + g_return_val_if_fail(ATK_SELECTION(selection), FALSE); + returnValIfWebKitAccessibleIsInvalid(WEBKIT_ACCESSIBLE(selection), FALSE); + + AccessibilityObject* coreSelection = core(selection); + if (!coreSelection) + return FALSE; + + AXCoreObject* option = optionFromList(selection, index); + if (option && (coreSelection->isListBox() || coreSelection->isMenuList())) + return option->isSelected(); + + return FALSE; +} + +static gboolean webkitAccessibleSelectionRemoveSelection(AtkSelection* selection, gint index) +{ + g_return_val_if_fail(ATK_SELECTION(selection), FALSE); + returnValIfWebKitAccessibleIsInvalid(WEBKIT_ACCESSIBLE(selection), FALSE); + + AccessibilityObject* coreSelection = core(selection); + if (!coreSelection) + return FALSE; + + AXCoreObject* option = optionFromSelection(selection, index); + if (option && (coreSelection->isListBox() || coreSelection->isMenuList())) { + option->setSelected(false); + return !option->isSelected(); + } + + return FALSE; +} + +static gboolean webkitAccessibleSelectionSelectAllSelection(AtkSelection* selection) +{ + g_return_val_if_fail(ATK_SELECTION(selection), FALSE); + returnValIfWebKitAccessibleIsInvalid(WEBKIT_ACCESSIBLE(selection), FALSE); + + AccessibilityObject* coreSelection = core(selection); + if (!coreSelection || !coreSelection->isMultiSelectable()) + return FALSE; + + if (is<AccessibilityListBox>(*coreSelection)) { + const AccessibilityObject::AccessibilityChildrenVector& children = coreSelection->children(); + AccessibilityListBox& listBox = downcast<AccessibilityListBox>(*coreSelection); + listBox.setSelectedChildren(children); + AccessibilityObject::AccessibilityChildrenVector selectedItems; + listBox.selectedChildren(selectedItems); + return selectedItems.size() == children.size(); + } + + return FALSE; +} + +void webkitAccessibleSelectionInterfaceInit(AtkSelectionIface* iface) +{ + iface->add_selection = webkitAccessibleSelectionAddSelection; + iface->clear_selection = webkitAccessibleSelectionClearSelection; + iface->ref_selection = webkitAccessibleSelectionRefSelection; + iface->get_selection_count = webkitAccessibleSelectionGetSelectionCount; + iface->is_child_selected = webkitAccessibleSelectionIsChildSelected; + iface->remove_selection = webkitAccessibleSelectionRemoveSelection; + iface->select_all_selection = webkitAccessibleSelectionSelectAllSelection; +} + +#endif diff -urpN webkitgtk-2.38.1.glib244/Source/WebCore/accessibility/atk/WebKitAccessibleInterfaceSelection.h webkitgtk-2.38.1/Source/WebCore/accessibility/atk/WebKitAccessibleInterfaceSelection.h --- webkitgtk-2.38.1.glib244/Source/WebCore/accessibility/atk/WebKitAccessibleInterfaceSelection.h 1969-12-31 18:00:00.000000000 -0600 +++ webkitgtk-2.38.1/Source/WebCore/accessibility/atk/WebKitAccessibleInterfaceSelection.h 2022-10-24 13:34:51.921454626 -0500 @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2008 Nuanti Ltd. + * Copyright (C) 2009 Jan Alonzo + * Copyright (C) 2010, 2011, 2012 Igalia S.L. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#pragma once + +#if ENABLE(ACCESSIBILITY) && USE(ATK) + +#include <atk/atk.h> + +void webkitAccessibleSelectionInterfaceInit(AtkSelectionIface*); + +#endif // ENABLE(ACCESSIBILITY) && USE(ATK) diff -urpN webkitgtk-2.38.1.glib244/Source/WebCore/accessibility/atk/WebKitAccessibleInterfaceTableCell.cpp webkitgtk-2.38.1/Source/WebCore/accessibility/atk/WebKitAccessibleInterfaceTableCell.cpp --- webkitgtk-2.38.1.glib244/Source/WebCore/accessibility/atk/WebKitAccessibleInterfaceTableCell.cpp 1969-12-31 18:00:00.000000000 -0600 +++ webkitgtk-2.38.1/Source/WebCore/accessibility/atk/WebKitAccessibleInterfaceTableCell.cpp 2022-10-24 13:34:51.921454626 -0500 @@ -0,0 +1,156 @@ +/* + * Copyright (C) 2014 Samsung Electronics. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include "config.h" +#include "WebKitAccessibleInterfaceTableCell.h" + +#if ENABLE(ACCESSIBILITY) && USE(ATK) + +#include "AccessibilityObject.h" +#include "AccessibilityTable.h" +#include "AccessibilityTableCell.h" +#include "WebKitAccessible.h" +#include "WebKitAccessibleUtil.h" + +using namespace WebCore; + +static GPtrArray* convertToGPtrArray(const AccessibilityObject::AccessibilityChildrenVector& children) +{ + GPtrArray* array = g_ptr_array_new(); + for (const auto& child : children) { + if (auto* atkObject = child->wrapper()) + g_ptr_array_add(array, atkObject); + } + return array; +} + +static AccessibilityObject* core(AtkTableCell* cell) +{ + if (!WEBKIT_IS_ACCESSIBLE(cell)) + return nullptr; + + return &webkitAccessibleGetAccessibilityObject(WEBKIT_ACCESSIBLE(cell)); +} + +GPtrArray* webkitAccessibleTableCellGetColumnHeaderCells(AtkTableCell* cell) +{ + g_return_val_if_fail(ATK_TABLE_CELL(cell), nullptr); + returnValIfWebKitAccessibleIsInvalid(WEBKIT_ACCESSIBLE(cell), nullptr); + + AccessibilityObject* axObject = core(cell); + if (!is<AccessibilityTableCell>(axObject)) + return nullptr; + + auto columnHeaders = downcast<AccessibilityTableCell>(*axObject).columnHeaders(); + + return convertToGPtrArray(columnHeaders); +} + +GPtrArray* webkitAccessibleTableCellGetRowHeaderCells(AtkTableCell* cell) +{ + g_return_val_if_fail(ATK_TABLE_CELL(cell), nullptr); + returnValIfWebKitAccessibleIsInvalid(WEBKIT_ACCESSIBLE(cell), nullptr); + + AccessibilityObject* axObject = core(cell); + if (!is<AccessibilityTableCell>(axObject)) + return nullptr; + + auto rowHeaders = downcast<AccessibilityTableCell>(*axObject).rowHeaders(); + + return convertToGPtrArray(rowHeaders); +} + +gint webkitAccessibleTableCellGetColumnSpan(AtkTableCell* cell) +{ + g_return_val_if_fail(ATK_TABLE_CELL(cell), 0); + returnValIfWebKitAccessibleIsInvalid(WEBKIT_ACCESSIBLE(cell), 0); + + AccessibilityObject* axObject = core(cell); + if (!is<AccessibilityTableCell>(axObject)) + return 0; + + return axObject->columnIndexRange().second; +} + +gint webkitAccessibleTableCellGetRowSpan(AtkTableCell* cell) +{ + g_return_val_if_fail(ATK_TABLE_CELL(cell), 0); + returnValIfWebKitAccessibleIsInvalid(WEBKIT_ACCESSIBLE(cell), 0); + + AccessibilityObject* axObject = core(cell); + if (!is<AccessibilityTableCell>(axObject)) + return 0; + + return axObject->rowIndexRange().second; +} + +gboolean webkitAccessibleTableCellGetPosition(AtkTableCell* cell, gint* row, gint* column) +{ + g_return_val_if_fail(ATK_TABLE_CELL(cell), false); + returnValIfWebKitAccessibleIsInvalid(WEBKIT_ACCESSIBLE(cell), false); + + AccessibilityObject* axObject = core(cell); + if (!is<AccessibilityTableCell>(axObject)) + return false; + + if (row) { + // aria-rowindex is 1-based. + int rowIndex = axObject->axRowIndex() - 1; + if (rowIndex <= -1) + rowIndex = axObject->rowIndexRange().first; + *row = rowIndex; + } + if (column) { + // aria-colindex is 1-based. + int columnIndex = axObject->axColumnIndex() - 1; + if (columnIndex <= -1) + columnIndex = axObject->columnIndexRange().first; + *column = columnIndex; + } + + return true; +} + +AtkObject* webkitAccessibleTableCellGetTable(AtkTableCell* cell) +{ + g_return_val_if_fail(ATK_TABLE_CELL(cell), nullptr); + returnValIfWebKitAccessibleIsInvalid(WEBKIT_ACCESSIBLE(cell), nullptr); + + AccessibilityObject* axObject = core(cell); + if (!axObject || !axObject->isTableCell()) + return nullptr; + + auto* table = atk_object_get_parent(ATK_OBJECT(axObject->wrapper())); + if (!table || !ATK_IS_TABLE(table)) + return nullptr; + + return ATK_OBJECT(g_object_ref(table)); +} + +void webkitAccessibleTableCellInterfaceInit(AtkTableCellIface* iface) +{ + iface->get_column_header_cells = webkitAccessibleTableCellGetColumnHeaderCells; + iface->get_row_header_cells = webkitAccessibleTableCellGetRowHeaderCells; + iface->get_column_span = webkitAccessibleTableCellGetColumnSpan; + iface->get_row_span = webkitAccessibleTableCellGetRowSpan; + iface->get_position = webkitAccessibleTableCellGetPosition; + iface->get_table = webkitAccessibleTableCellGetTable; +} + +#endif // ENABLE(ACCESSIBILITY) && USE(ATK) diff -urpN webkitgtk-2.38.1.glib244/Source/WebCore/accessibility/atk/WebKitAccessibleInterfaceTableCell.h webkitgtk-2.38.1/Source/WebCore/accessibility/atk/WebKitAccessibleInterfaceTableCell.h --- webkitgtk-2.38.1.glib244/Source/WebCore/accessibility/atk/WebKitAccessibleInterfaceTableCell.h 1969-12-31 18:00:00.000000000 -0600 +++ webkitgtk-2.38.1/Source/WebCore/accessibility/atk/WebKitAccessibleInterfaceTableCell.h 2022-10-24 13:34:51.925454646 -0500 @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2008 Nuanti Ltd. + * Copyright (C) 2009 Jan Alonzo + * Copyright (C) 2009, 2010, 2011, 2012 Igalia S.L. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#pragma once + +#if ENABLE(ACCESSIBILITY) && USE(ATK) + +#include <atk/atk.h> + +void webkitAccessibleTableCellInterfaceInit(AtkTableCellIface*); + +#endif // ENABLE(ACCESSIBILITY) && USE(ATK) diff -urpN webkitgtk-2.38.1.glib244/Source/WebCore/accessibility/atk/WebKitAccessibleInterfaceTable.cpp webkitgtk-2.38.1/Source/WebCore/accessibility/atk/WebKitAccessibleInterfaceTable.cpp --- webkitgtk-2.38.1.glib244/Source/WebCore/accessibility/atk/WebKitAccessibleInterfaceTable.cpp 1969-12-31 18:00:00.000000000 -0600 +++ webkitgtk-2.38.1/Source/WebCore/accessibility/atk/WebKitAccessibleInterfaceTable.cpp 2022-10-24 13:34:51.925454646 -0500 @@ -0,0 +1,290 @@ +/* + * Copyright (C) 2008 Nuanti Ltd. + * Copyright (C) 2009 Jan Alonzo + * Copyright (C) 2009, 2010, 2011, 2012 Igalia S.L. + * + * Portions from Mozilla a11y, copyright as follows: + * + * The Original Code is mozilla.org code. + * + * The Initial Developer of the Original Code is + * Sun Microsystems, Inc. + * Portions created by the Initial Developer are Copyright (C) 2002 + * the Initial Developer. All Rights Reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include "config.h" +#include "WebKitAccessibleInterfaceTable.h" + +#if ENABLE(ACCESSIBILITY) && USE(ATK) + +#include "AccessibilityListBox.h" +#include "AccessibilityObject.h" +#include "AccessibilityTable.h" +#include "AccessibilityTableCell.h" +#include "HTMLTableCaptionElement.h" +#include "HTMLTableElement.h" +#include "RenderElement.h" +#include "WebKitAccessible.h" +#include "WebKitAccessibleInterfaceText.h" +#include "WebKitAccessibleUtil.h" + +using namespace WebCore; + +static AccessibilityObject* core(AtkTable* table) +{ + if (!WEBKIT_IS_ACCESSIBLE(table)) + return nullptr; + + return &webkitAccessibleGetAccessibilityObject(WEBKIT_ACCESSIBLE(table)); +} + +static AXCoreObject* cell(AtkTable* table, guint row, guint column) +{ + auto* accTable = core(table); + return accTable ? accTable->cellForColumnAndRow(column, row) : nullptr; +} + +static gint cellIndex(AXCoreObject* axCell, AccessibilityTable* axTable) +{ + // Calculate the cell's index as if we had a traditional Gtk+ table in + // which cells are all direct children of the table, arranged row-first. + auto allCells = axTable->cells(); + AXCoreObject::AccessibilityChildrenVector::iterator position; + position = std::find(allCells.begin(), allCells.end(), axCell); + if (position == allCells.end()) + return -1; + return position - allCells.begin(); +} + +static AccessibilityTableCell* cellAtIndex(AtkTable* table, gint index) +{ + AccessibilityObject* accTable = core(table); + if (is<AccessibilityTable>(*accTable)) { + auto allCells = downcast<AccessibilityTable>(*accTable).cells(); + if (0 <= index && static_cast<unsigned>(index) < allCells.size()) + return downcast<AccessibilityTableCell>(allCells[index].get()); + } + return nullptr; +} + +static AtkObject* webkitAccessibleTableRefAt(AtkTable* table, gint row, gint column) +{ + g_return_val_if_fail(ATK_TABLE(table), 0); + returnValIfWebKitAccessibleIsInvalid(WEBKIT_ACCESSIBLE(table), 0); + + auto* axCell = cell(table, row, column); + if (!axCell) + return 0; + + auto* cell = axCell->wrapper(); + if (!cell) + return 0; + + // This method transfers full ownership over the returned + // AtkObject, so an extra reference is needed here. + return ATK_OBJECT(g_object_ref(cell)); +} + +static gint webkitAccessibleTableGetIndexAt(AtkTable* table, gint row, gint column) +{ + g_return_val_if_fail(ATK_TABLE(table), -1); + returnValIfWebKitAccessibleIsInvalid(WEBKIT_ACCESSIBLE(table), -1); + + auto* axCell = cell(table, row, column); + AccessibilityTable* axTable = downcast<AccessibilityTable>(core(table)); + return cellIndex(axCell, axTable); +} + +static gint webkitAccessibleTableGetColumnAtIndex(AtkTable* table, gint index) +{ + g_return_val_if_fail(ATK_TABLE(table), -1); + returnValIfWebKitAccessibleIsInvalid(WEBKIT_ACCESSIBLE(table), -1); + + AccessibilityTableCell* axCell = cellAtIndex(table, index); + if (axCell) { + auto columnRange = axCell->columnIndexRange(); + return columnRange.first; + } + return -1; +} + +static gint webkitAccessibleTableGetRowAtIndex(AtkTable* table, gint index) +{ + g_return_val_if_fail(ATK_TABLE(table), -1); + returnValIfWebKitAccessibleIsInvalid(WEBKIT_ACCESSIBLE(table), -1); + + AccessibilityTableCell* axCell = cellAtIndex(table, index); + if (axCell) { + auto rowRange = axCell->rowIndexRange(); + return rowRange.first; + } + return -1; +} + +static gint webkitAccessibleTableGetNColumns(AtkTable* table) +{ + g_return_val_if_fail(ATK_TABLE(table), 0); + returnValIfWebKitAccessibleIsInvalid(WEBKIT_ACCESSIBLE(table), 0); + + AccessibilityObject* accTable = core(table); + if (!is<AccessibilityTable>(*accTable)) + return 0; + + if (int columnCount = downcast<AccessibilityTable>(*accTable).axColumnCount()) + return columnCount; + + return downcast<AccessibilityTable>(*accTable).columnCount(); +} + +static gint webkitAccessibleTableGetNRows(AtkTable* table) +{ + g_return_val_if_fail(ATK_TABLE(table), 0); + returnValIfWebKitAccessibleIsInvalid(WEBKIT_ACCESSIBLE(table), 0); + + AccessibilityObject* accTable = core(table); + if (!is<AccessibilityTable>(*accTable)) + return 0; + + if (int rowCount = downcast<AccessibilityTable>(*accTable).axRowCount()) + return rowCount; + + return downcast<AccessibilityTable>(*accTable).rowCount(); +} + +static gint webkitAccessibleTableGetColumnExtentAt(AtkTable* table, gint row, gint column) +{ + g_return_val_if_fail(ATK_TABLE(table), 0); + returnValIfWebKitAccessibleIsInvalid(WEBKIT_ACCESSIBLE(table), 0); + + auto* axCell = cell(table, row, column); + if (axCell) { + auto columnRange = axCell->columnIndexRange(); + return columnRange.second; + } + return 0; +} + +static gint webkitAccessibleTableGetRowExtentAt(AtkTable* table, gint row, gint column) +{ + g_return_val_if_fail(ATK_TABLE(table), 0); + returnValIfWebKitAccessibleIsInvalid(WEBKIT_ACCESSIBLE(table), 0); + + auto* axCell = cell(table, row, column); + if (axCell) { + auto rowRange = axCell->rowIndexRange(); + return rowRange.second; + } + return 0; +} + +static AtkObject* webkitAccessibleTableGetColumnHeader(AtkTable* table, gint column) +{ + g_return_val_if_fail(ATK_TABLE(table), 0); + returnValIfWebKitAccessibleIsInvalid(WEBKIT_ACCESSIBLE(table), 0); + + AccessibilityObject* accTable = core(table); + if (is<AccessibilityTable>(*accTable)) { + auto columnHeaders = downcast<AccessibilityTable>(*accTable).columnHeaders(); + + for (const auto& columnHeader : columnHeaders) { + auto columnRange = columnHeader->columnIndexRange(); + if (columnRange.first <= static_cast<unsigned>(column) && static_cast<unsigned>(column) < columnRange.first + columnRange.second) + return ATK_OBJECT(columnHeader->wrapper()); + } + } + return nullptr; +} + +static AtkObject* webkitAccessibleTableGetRowHeader(AtkTable* table, gint row) +{ + g_return_val_if_fail(ATK_TABLE(table), 0); + returnValIfWebKitAccessibleIsInvalid(WEBKIT_ACCESSIBLE(table), 0); + + AccessibilityObject* accTable = core(table); + if (is<AccessibilityTable>(*accTable)) { + auto rowHeaders = downcast<AccessibilityTable>(*accTable).rowHeaders(); + + for (const auto& rowHeader : rowHeaders) { + auto rowRange = rowHeader->rowIndexRange(); + if (rowRange.first <= static_cast<unsigned>(row) && static_cast<unsigned>(row) < rowRange.first + rowRange.second) + return ATK_OBJECT(rowHeader->wrapper()); + } + } + return nullptr; +} + +static AtkObject* webkitAccessibleTableGetCaption(AtkTable* table) +{ + g_return_val_if_fail(ATK_TABLE(table), nullptr); + returnValIfWebKitAccessibleIsInvalid(WEBKIT_ACCESSIBLE(table), nullptr); + + AccessibilityObject* accTable = core(table); + if (accTable->isAccessibilityRenderObject()) { + Node* node = accTable->node(); + if (is<HTMLTableElement>(node)) { + auto caption = downcast<HTMLTableElement>(*node).caption(); + if (caption) + return ATK_OBJECT(AccessibilityObject::firstAccessibleObjectFromNode(caption->renderer()->element())->wrapper()); + } + } + return nullptr; +} + +static const gchar* webkitAccessibleTableGetColumnDescription(AtkTable* table, gint column) +{ + g_return_val_if_fail(ATK_TABLE(table), 0); + returnValIfWebKitAccessibleIsInvalid(WEBKIT_ACCESSIBLE(table), 0); + + AtkObject* columnHeader = atk_table_get_column_header(table, column); + if (columnHeader && ATK_IS_TEXT(columnHeader)) + return atk_text_get_text(ATK_TEXT(columnHeader), 0, -1); + + return 0; +} + +static const gchar* webkitAccessibleTableGetRowDescription(AtkTable* table, gint row) +{ + g_return_val_if_fail(ATK_TABLE(table), 0); + returnValIfWebKitAccessibleIsInvalid(WEBKIT_ACCESSIBLE(table), 0); + + AtkObject* rowHeader = atk_table_get_row_header(table, row); + if (rowHeader && ATK_IS_TEXT(rowHeader)) + return atk_text_get_text(ATK_TEXT(rowHeader), 0, -1); + + return 0; +} + +void webkitAccessibleTableInterfaceInit(AtkTableIface* iface) +{ + iface->ref_at = webkitAccessibleTableRefAt; + iface->get_index_at = webkitAccessibleTableGetIndexAt; + iface->get_column_at_index = webkitAccessibleTableGetColumnAtIndex; + iface->get_row_at_index = webkitAccessibleTableGetRowAtIndex; + iface->get_n_columns = webkitAccessibleTableGetNColumns; + iface->get_n_rows = webkitAccessibleTableGetNRows; + iface->get_column_extent_at = webkitAccessibleTableGetColumnExtentAt; + iface->get_row_extent_at = webkitAccessibleTableGetRowExtentAt; + iface->get_column_header = webkitAccessibleTableGetColumnHeader; + iface->get_row_header = webkitAccessibleTableGetRowHeader; + iface->get_caption = webkitAccessibleTableGetCaption; + iface->get_column_description = webkitAccessibleTableGetColumnDescription; + iface->get_row_description = webkitAccessibleTableGetRowDescription; +} + +#endif diff -urpN webkitgtk-2.38.1.glib244/Source/WebCore/accessibility/atk/WebKitAccessibleInterfaceTable.h webkitgtk-2.38.1/Source/WebCore/accessibility/atk/WebKitAccessibleInterfaceTable.h --- webkitgtk-2.38.1.glib244/Source/WebCore/accessibility/atk/WebKitAccessibleInterfaceTable.h 1969-12-31 18:00:00.000000000 -0600 +++ webkitgtk-2.38.1/Source/WebCore/accessibility/atk/WebKitAccessibleInterfaceTable.h 2022-10-24 13:34:51.925454646 -0500 @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2008 Nuanti Ltd. + * Copyright (C) 2009 Jan Alonzo + * Copyright (C) 2009, 2010, 2011, 2012 Igalia S.L. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#pragma once + +#if ENABLE(ACCESSIBILITY) && USE(ATK) + +#include <atk/atk.h> + +void webkitAccessibleTableInterfaceInit(AtkTableIface*); + +#endif // ENABLE(ACCESSIBILITY) && USE(ATK) diff -urpN webkitgtk-2.38.1.glib244/Source/WebCore/accessibility/atk/WebKitAccessibleInterfaceText.cpp webkitgtk-2.38.1/Source/WebCore/accessibility/atk/WebKitAccessibleInterfaceText.cpp --- webkitgtk-2.38.1.glib244/Source/WebCore/accessibility/atk/WebKitAccessibleInterfaceText.cpp 1969-12-31 18:00:00.000000000 -0600 +++ webkitgtk-2.38.1/Source/WebCore/accessibility/atk/WebKitAccessibleInterfaceText.cpp 2022-10-24 13:34:51.925454646 -0500 @@ -0,0 +1,1270 @@ +/* + * Copyright (C) 2008 Nuanti Ltd. + * Copyright (C) 2009 Jan Alonzo + * Copyright (C) 2009, 2010, 2011, 2012 Igalia S.L. + * Copyright (C) 2013 Samsung Electronics. All rights reserved. + * + * Portions from Mozilla a11y, copyright as follows: + * + * The Original Code is mozilla.org code. + * + * The Initial Developer of the Original Code is + * Sun Microsystems, Inc. + * Portions created by the Initial Developer are Copyright (C) 2002 + * the Initial Developer. All Rights Reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include "config.h" +#include "WebKitAccessibleInterfaceText.h" + +#if ENABLE(ACCESSIBILITY) && USE(ATK) + +#include "AccessibilityObject.h" +#include "Document.h" +#include "Editing.h" +#include "FontCascade.h" +#include "FrameView.h" +#include "HTMLParserIdioms.h" +#include "HostWindow.h" +#include "NotImplemented.h" +#include "Range.h" +#include "RenderListItem.h" +#include "RenderListMarker.h" +#include "RenderText.h" +#include "TextIterator.h" +#include "VisibleUnits.h" +#include "WebKitAccessible.h" +#include "WebKitAccessibleUtil.h" +#include <pal/text/TextEncoding.h> +#include <wtf/glib/GUniquePtr.h> +#include <wtf/text/CString.h> + +using namespace WebCore; + +// Text attribute to expose the ARIA 'aria-invalid' attribute. Initially initialized +// to ATK_TEXT_ATTR_INVALID (which means 'invalid' text attribute'), will later on +// hold a reference to the custom registered AtkTextAttribute that we will use. +static AtkTextAttribute atkTextAttributeInvalid = ATK_TEXT_ATTR_INVALID; + +static AccessibilityObject* core(AtkText* text) +{ + if (!WEBKIT_IS_ACCESSIBLE(text)) + return 0; + + return &webkitAccessibleGetAccessibilityObject(WEBKIT_ACCESSIBLE(text)); +} + +static int baselinePositionForRenderObject(RenderObject* renderObject) +{ + // FIXME: This implementation of baselinePosition originates from RenderObject.cpp and was + // removed in r70072. The implementation looks incorrect though, because this is not the + // baseline of the underlying RenderObject, but of the AccessibilityRenderObject. + const FontMetrics& fontMetrics = renderObject->firstLineStyle().metricsOfPrimaryFont(); + return fontMetrics.ascent() + (renderObject->firstLineStyle().computedLineHeight() - fontMetrics.height()) / 2; +} + +static AtkAttributeSet* getAttributeSetForAccessibilityObject(const AccessibilityObject* object) +{ + if (!object->isAccessibilityRenderObject()) + return 0; + + RenderObject* renderer = object->renderer(); + auto* style = &renderer->style(); + + AtkAttributeSet* result = nullptr; + GUniquePtr<gchar> buffer(g_strdup_printf("%i", style->computedFontPixelSize())); + result = addToAtkAttributeSet(result, atk_text_attribute_get_name(ATK_TEXT_ATTR_SIZE), buffer.get()); + + Color bgColor = style->visitedDependentColor(CSSPropertyBackgroundColor); + if (bgColor.isValid()) { + auto [r, g, b, a] = bgColor.toColorTypeLossy<SRGBA<uint8_t>>().resolved(); + buffer.reset(g_strdup_printf("%i,%i,%i", r, g, b)); + result = addToAtkAttributeSet(result, atk_text_attribute_get_name(ATK_TEXT_ATTR_BG_COLOR), buffer.get()); + } + + Color fgColor = style->visitedDependentColor(CSSPropertyColor); + if (fgColor.isValid()) { + auto [r, g, b, a] = fgColor.toColorTypeLossy<SRGBA<uint8_t>>().resolved(); + buffer.reset(g_strdup_printf("%i,%i,%i", r, g, b)); + result = addToAtkAttributeSet(result, atk_text_attribute_get_name(ATK_TEXT_ATTR_FG_COLOR), buffer.get()); + } + + int baselinePosition; + bool includeRise = true; + switch (style->verticalAlign()) { + case VerticalAlign::Sub: + baselinePosition = -1 * baselinePositionForRenderObject(renderer); + break; + case VerticalAlign::Super: + baselinePosition = baselinePositionForRenderObject(renderer); + break; + case VerticalAlign::Baseline: + baselinePosition = 0; + break; + default: + includeRise = false; + break; + } + + if (includeRise) { + buffer.reset(g_strdup_printf("%i", baselinePosition)); + result = addToAtkAttributeSet(result, atk_text_attribute_get_name(ATK_TEXT_ATTR_RISE), buffer.get()); + } + + if (!style->textIndent().isUndefined()) { + int indentation = valueForLength(style->textIndent(), object->size().width()); + buffer.reset(g_strdup_printf("%i", indentation)); + result = addToAtkAttributeSet(result, atk_text_attribute_get_name(ATK_TEXT_ATTR_INDENT), buffer.get()); + } + + String fontFamilyName = style->fontCascade().firstFamily(); + if (fontFamilyName.left(8) == "-webkit-"_s) + fontFamilyName = fontFamilyName.substring(8); + + result = addToAtkAttributeSet(result, atk_text_attribute_get_name(ATK_TEXT_ATTR_FAMILY_NAME), fontFamilyName.utf8().data()); + + int fontWeight = static_cast<float>(style->fontCascade().weight()); + if (fontWeight > 0) { + buffer.reset(g_strdup_printf("%i", fontWeight)); + result = addToAtkAttributeSet(result, atk_text_attribute_get_name(ATK_TEXT_ATTR_WEIGHT), buffer.get()); + } + + switch (style->textAlign()) { + case TextAlignMode::Start: + case TextAlignMode::End: + break; + case TextAlignMode::Left: + case TextAlignMode::WebKitLeft: + result = addToAtkAttributeSet(result, atk_text_attribute_get_name(ATK_TEXT_ATTR_JUSTIFICATION), "left"); + break; + case TextAlignMode::Right: + case TextAlignMode::WebKitRight: + result = addToAtkAttributeSet(result, atk_text_attribute_get_name(ATK_TEXT_ATTR_JUSTIFICATION), "right"); + break; + case TextAlignMode::Center: + case TextAlignMode::WebKitCenter: + result = addToAtkAttributeSet(result, atk_text_attribute_get_name(ATK_TEXT_ATTR_JUSTIFICATION), "center"); + break; + case TextAlignMode::Justify: + result = addToAtkAttributeSet(result, atk_text_attribute_get_name(ATK_TEXT_ATTR_JUSTIFICATION), "fill"); + } + + result = addToAtkAttributeSet(result, atk_text_attribute_get_name(ATK_TEXT_ATTR_UNDERLINE), (style->textDecorationLine() & TextDecorationLine::Underline) ? "single" : "none"); + + result = addToAtkAttributeSet(result, atk_text_attribute_get_name(ATK_TEXT_ATTR_STYLE), style->fontCascade().italic() ? "italic" : "normal"); + + result = addToAtkAttributeSet(result, atk_text_attribute_get_name(ATK_TEXT_ATTR_STRIKETHROUGH), (style->textDecorationLine() & TextDecorationLine::LineThrough) ? "true" : "false"); + + result = addToAtkAttributeSet(result, atk_text_attribute_get_name(ATK_TEXT_ATTR_INVISIBLE), (style->visibility() == Visibility::Hidden) ? "true" : "false"); + + result = addToAtkAttributeSet(result, atk_text_attribute_get_name(ATK_TEXT_ATTR_EDITABLE), object->canSetValueAttribute() ? "true" : "false"); + + String language = object->language(); + if (!language.isEmpty()) + result = addToAtkAttributeSet(result, atk_text_attribute_get_name(ATK_TEXT_ATTR_LANGUAGE), language.utf8().data()); + + String invalidStatus = object->invalidStatus(); + if (invalidStatus != "false"_s) { + // Register the custom attribute for 'aria-invalid' if not done yet. + if (atkTextAttributeInvalid == ATK_TEXT_ATTR_INVALID) + atkTextAttributeInvalid = atk_text_attribute_register("invalid"); + + result = addToAtkAttributeSet(result, atk_text_attribute_get_name(atkTextAttributeInvalid), invalidStatus.utf8().data()); + } + + return result; +} + +static gint compareAttribute(const AtkAttribute* a, const AtkAttribute* b) +{ + return g_strcmp0(a->name, b->name) || g_strcmp0(a->value, b->value); +} + +// Returns an AtkAttributeSet with the elements of attributeSet1 which +// are either not present or different in attributeSet2. Neither +// attributeSet1 nor attributeSet2 should be used after calling this. +static AtkAttributeSet* attributeSetDifference(AtkAttributeSet* attributeSet1, AtkAttributeSet* attributeSet2) +{ + if (!attributeSet2) + return attributeSet1; + + AtkAttributeSet* currentSet = attributeSet1; + AtkAttributeSet* found; + AtkAttributeSet* toDelete = nullptr; + + while (currentSet) { + found = g_slist_find_custom(attributeSet2, currentSet->data, (GCompareFunc)compareAttribute); + if (found) { + AtkAttributeSet* nextSet = currentSet->next; + toDelete = g_slist_prepend(toDelete, currentSet->data); + attributeSet1 = g_slist_delete_link(attributeSet1, currentSet); + currentSet = nextSet; + } else + currentSet = currentSet->next; + } + + atk_attribute_set_free(attributeSet2); + atk_attribute_set_free(toDelete); + return attributeSet1; +} + +static gchar* webkitAccessibleTextGetText(AtkText*, gint startOffset, gint endOffset); + +static guint accessibilityObjectLength(const AccessibilityObject* object) +{ + // Non render objects are not taken into account + if (!object->isAccessibilityRenderObject()) + return 0; + + // For those objects implementing the AtkText interface we use the + // well known API to always get the text in a consistent way + auto* atkObj = ATK_OBJECT(object->wrapper()); + if (ATK_IS_TEXT(atkObj)) { + GUniquePtr<gchar> text(webkitAccessibleTextGetText(ATK_TEXT(atkObj), 0, -1)); + return g_utf8_strlen(text.get(), -1); + } + + // Even if we don't expose list markers to Assistive + // Technologies, we need to have a way to measure their length + // for those cases when it's needed to take it into account + // separately (as in getAccessibilityObjectForOffset) + if (auto renderer = object->renderer(); is<RenderListMarker>(renderer)) + return downcast<RenderListMarker>(*renderer).textWithSuffix().length(); + + return 0; +} + +static const AccessibilityObject* getAccessibilityObjectForOffset(const AccessibilityObject* object, guint offset, gint* startOffset, gint* endOffset) +{ + const AccessibilityObject* result; + guint length = accessibilityObjectLength(object); + if (length > offset) { + *startOffset = 0; + *endOffset = length; + result = object; + } else { + *startOffset = -1; + *endOffset = -1; + result = 0; + } + + if (!object->firstChild()) + return result; + + AccessibilityObject* child = object->firstChild(); + guint currentOffset = 0; + guint childPosition = 0; + while (child && currentOffset <= offset) { + guint childLength = accessibilityObjectLength(child); + currentOffset = childLength + childPosition; + if (currentOffset > offset) { + gint childStartOffset; + gint childEndOffset; + const AccessibilityObject* grandChild = getAccessibilityObjectForOffset(child, offset-childPosition, &childStartOffset, &childEndOffset); + if (childStartOffset >= 0) { + *startOffset = childStartOffset + childPosition; + *endOffset = childEndOffset + childPosition; + result = grandChild; + } + } else { + childPosition += childLength; + child = child->nextSibling(); + } + } + return result; +} + +static AtkAttributeSet* getRunAttributesFromAccessibilityObject(const AccessibilityObject* element, gint offset, gint* startOffset, gint* endOffset) +{ + const AccessibilityObject* child = getAccessibilityObjectForOffset(element, offset, startOffset, endOffset); + if (!child) { + *startOffset = -1; + *endOffset = -1; + return 0; + } + + AtkAttributeSet* defaultAttributes = getAttributeSetForAccessibilityObject(element); + AtkAttributeSet* childAttributes = getAttributeSetForAccessibilityObject(child); + + return attributeSetDifference(childAttributes, defaultAttributes); +} + +static IntRect textExtents(AtkText* text, gint startOffset, gint length, AtkCoordType coords) +{ + GUniquePtr<char> textContent(webkitAccessibleTextGetText(text, startOffset, -1)); + gint textLength = g_utf8_strlen(textContent.get(), -1); + + // The first case (endOffset of -1) should work, but seems broken for all Gtk+ apps. + gint rangeLength = length; + if (rangeLength < 0 || rangeLength > textLength) + rangeLength = textLength; + AccessibilityObject* coreObject = core(text); + + IntRect extents = coreObject->doAXBoundsForRange(PlainTextRange(startOffset, rangeLength)); + switch (coords) { + case ATK_XY_SCREEN: + if (Document* document = coreObject->document()) + extents = document->view()->contentsToScreen(extents); + break; + case ATK_XY_WINDOW: + // No-op + break; +#if ATK_CHECK_VERSION(2, 30, 0) + case ATK_XY_PARENT: + RELEASE_ASSERT_NOT_REACHED(); +#endif + } + + return extents; +} + +static int offsetAdjustmentForListItem(const AXCoreObject* object) +{ + // We need to adjust the offsets for the list item marker in + // Left-To-Right text, since we expose it together with the text. + RenderObject* renderer = object->renderer(); + if (is<RenderListItem>(renderer) && renderer->style().direction() == TextDirection::LTR) + return downcast<RenderListItem>(*renderer).markerTextWithSuffix().length(); + + return 0; +} + +static int webCoreOffsetToAtkOffset(const AXCoreObject* object, int offset) +{ + if (!object->isListItem()) + return offset; + + return offset + offsetAdjustmentForListItem(object); +} + +static int atkOffsetToWebCoreOffset(AtkText* text, int offset) +{ + AccessibilityObject* coreObject = core(text); + if (!coreObject || !coreObject->isListItem()) + return offset; + + return offset - offsetAdjustmentForListItem(coreObject); +} + +static Node* getNodeForAccessibilityObject(AccessibilityObject* coreObject) +{ + if (!coreObject->isNativeTextControl()) + return coreObject->node(); + + // For text controls, we get the first visible position on it (which will + // belong to its inner element, unreachable from the DOM) and return its + // parent node, so we have a "bounding node" for the accessibility object. + VisiblePosition positionInTextControlInnerElement = coreObject->visiblePositionForIndex(0, true); + Node* innerMostNode = positionInTextControlInnerElement.deepEquivalent().anchorNode(); + if (!innerMostNode) + return 0; + + return innerMostNode->parentNode(); +} + +static void getSelectionOffsetsForObject(AccessibilityObject* coreObject, VisibleSelection& selection, gint& startOffset, gint& endOffset) +{ + // Default values, unless the contrary is proved. + startOffset = 0; + endOffset = 0; + + Node* node = getNodeForAccessibilityObject(coreObject); + if (!node) + return; + + if (selection.isNone()) + return; + + // We need to limit our search to positions that fall inside the domain of the current object. + Position firstValidPosition = firstPositionInOrBeforeNode(node->firstDescendant()); + Position lastValidPosition = lastPositionInOrAfterNode(node->lastDescendant()); + + // Find the proper range for the selection that falls inside the object. + auto nodeRangeStart = std::max(selection.start(), firstValidPosition); + auto nodeRangeEnd = std::min(selection.end(), lastValidPosition); + + // Calculate position of the selected range inside the object. + Position parentFirstPosition = firstPositionInOrBeforeNode(node); + auto rangeInParent = *makeSimpleRange(parentFirstPosition, nodeRangeStart); + + // Set values for start offsets and calculate initial range length. + // These values might be adjusted later to cover special cases. + startOffset = webCoreOffsetToAtkOffset(coreObject, characterCount(rangeInParent, TextIteratorBehavior::EmitsCharactersBetweenAllVisiblePositions)); + auto nodeRange = *makeSimpleRange(nodeRangeStart, nodeRangeEnd); + int rangeLength = characterCount(nodeRange, TextIteratorBehavior::EmitsCharactersBetweenAllVisiblePositions); + + // Special cases that are only relevant when working with *_END boundaries. + if (selection.affinity() == Affinity::Upstream) { + VisiblePosition visibleStart(nodeRangeStart, Affinity::Upstream); + VisiblePosition visibleEnd(nodeRangeEnd, Affinity::Upstream); + + // We need to adjust offsets when finding wrapped lines so the position at the end + // of the line is properly taking into account when calculating the offsets. + if (isEndOfLine(visibleStart) && !lineBreakExistsAtVisiblePosition(visibleStart)) { + if (isStartOfLine(visibleStart.next())) + rangeLength++; + + if (!isEndOfBlock(visibleStart)) + startOffset = std::max(startOffset - 1, 0); + } + + if (isEndOfLine(visibleEnd) && !lineBreakExistsAtVisiblePosition(visibleEnd) && !isEndOfBlock(visibleEnd)) + rangeLength--; + } + + endOffset = std::min(startOffset + rangeLength, static_cast<int>(accessibilityObjectLength(coreObject))); +} + +static gchar* webkitAccessibleTextGetText(AtkText* text, gint startOffset, gint endOffset) +{ + g_return_val_if_fail(ATK_TEXT(text), 0); + returnValIfWebKitAccessibleIsInvalid(WEBKIT_ACCESSIBLE(text), 0); + + AccessibilityObject* coreObject = core(text); + +#if ENABLE(INPUT_TYPE_COLOR) + if (coreObject->roleValue() == AccessibilityRole::ColorWell) { + auto color = convertColor<SRGBA<float>>(coreObject->colorValue()).resolved(); + return g_strdup_printf("rgb %7.5f %7.5f %7.5f 1", color.red, color.green, color.blue); + } +#endif + + String ret; + if (coreObject->isTextControl()) + ret = coreObject->doAXStringForRange(PlainTextRange(0, endOffset)); + else { + ret = coreObject->stringValue(); + if (!ret) + ret = coreObject->textUnderElement(AccessibilityTextUnderElementMode(AccessibilityTextUnderElementMode::TextUnderElementModeIncludeAllChildren)); + } + + // Prefix a item number/bullet if needed + int actualEndOffset = endOffset == -1 ? ret.length() : endOffset; + if (coreObject->roleValue() == AccessibilityRole::ListItem) { + RenderObject* objRenderer = coreObject->renderer(); + if (is<RenderListItem>(objRenderer)) { + String markerText = downcast<RenderListItem>(*objRenderer).markerTextWithSuffix().toString(); + ret = objRenderer->style().direction() == TextDirection::LTR ? markerText + ret : ret + markerText; + if (endOffset == -1) + actualEndOffset = ret.length() + markerText.length(); + } + } + + ret = ret.substring(startOffset, actualEndOffset - startOffset); + return g_strdup(ret.utf8().data()); +} + +enum GetTextRelativePosition { + GetTextPositionAt, + GetTextPositionBefore, + GetTextPositionAfter +}; + +// Convenience function to be used in early returns. +static char* emptyTextSelectionAtOffset(int offset, int* startOffset, int* endOffset) +{ + *startOffset = offset; + *endOffset = offset; + return g_strdup(""); +} + +static char* webkitAccessibleTextGetChar(AtkText* text, int offset, GetTextRelativePosition textPosition, int* startOffset, int* endOffset) +{ + int actualOffset = offset; + if (textPosition == GetTextPositionBefore) + actualOffset--; + else if (textPosition == GetTextPositionAfter) + actualOffset++; + + GUniquePtr<char> textData(webkitAccessibleTextGetText(text, 0, -1)); + int textLength = g_utf8_strlen(textData.get(), -1); + + *startOffset = std::max(0, actualOffset); + *startOffset = std::min(*startOffset, textLength); + + *endOffset = std::max(0, actualOffset + 1); + *endOffset = std::min(*endOffset, textLength); + + if (*startOffset == *endOffset) + return g_strdup(""); + + return g_utf8_substring(textData.get(), *startOffset, *endOffset); +} + +static VisiblePosition nextWordStartPosition(const VisiblePosition &position) +{ + VisiblePosition positionAfterCurrentWord = nextWordPosition(position); + + // In order to skip spaces when moving right, we advance one word further + // and then move one word back. This will put us at the beginning of the + // word following. + VisiblePosition positionAfterSpacingAndFollowingWord = nextWordPosition(positionAfterCurrentWord); + + if (positionAfterSpacingAndFollowingWord != positionAfterCurrentWord) + positionAfterCurrentWord = previousWordPosition(positionAfterSpacingAndFollowingWord); + + bool movingBackwardsMovedPositionToStartOfCurrentWord = positionAfterCurrentWord == previousWordPosition(nextWordPosition(position)); + if (movingBackwardsMovedPositionToStartOfCurrentWord) + positionAfterCurrentWord = positionAfterSpacingAndFollowingWord; + + return positionAfterCurrentWord; +} + +static VisiblePosition previousWordEndPosition(const VisiblePosition &position) +{ + // We move forward and then backward to position ourselves at the beginning + // of the current word for this boundary, making the most of the semantics + // of previousWordPosition() and nextWordPosition(). + VisiblePosition positionAtStartOfCurrentWord = previousWordPosition(nextWordPosition(position)); + VisiblePosition positionAtPreviousWord = previousWordPosition(position); + + // Need to consider special cases (punctuation) when we are in the last word of a sentence. + if (isStartOfWord(position) && positionAtPreviousWord != position && positionAtPreviousWord == positionAtStartOfCurrentWord) + return nextWordPosition(positionAtStartOfCurrentWord); + + // In order to skip spaces when moving left, we advance one word backwards + // and then move one word forward. This will put us at the beginning of + // the word following. + VisiblePosition positionBeforeSpacingAndPreceedingWord = previousWordPosition(positionAtStartOfCurrentWord); + + if (positionBeforeSpacingAndPreceedingWord != positionAtStartOfCurrentWord) + positionAtStartOfCurrentWord = nextWordPosition(positionBeforeSpacingAndPreceedingWord); + + bool movingForwardMovedPositionToEndOfCurrentWord = nextWordPosition(positionAtStartOfCurrentWord) == previousWordPosition(nextWordPosition(position)); + if (movingForwardMovedPositionToEndOfCurrentWord) + positionAtStartOfCurrentWord = positionBeforeSpacingAndPreceedingWord; + + return positionAtStartOfCurrentWord; +} + +static VisibleSelection wordAtPositionForAtkBoundary(const AccessibilityObject* /*coreObject*/, const VisiblePosition& position, AtkTextBoundary boundaryType) +{ + VisiblePosition startPosition; + VisiblePosition endPosition; + + switch (boundaryType) { + case ATK_TEXT_BOUNDARY_WORD_START: + // isStartOfWord() returns true both when at the beginning of a "real" word + // as when at the beginning of a whitespace range between two "real" words, + // since that whitespace is considered a "word" as well. And in case we are + // already at the beginning of a "real" word we do not need to look backwards. + if (isStartOfWord(position) && deprecatedIsEditingWhitespace(position.characterBefore())) + startPosition = position; + else + startPosition = previousWordPosition(position); + endPosition = nextWordStartPosition(startPosition); + + // We need to make sure that we look for the word in the current line when + // we are at the beginning of a new line, and not look into the previous one + // at all, which might happen when lines belong to different nodes. + if (isStartOfLine(position) && isStartOfLine(endPosition)) { + startPosition = endPosition; + endPosition = nextWordStartPosition(startPosition); + } + break; + + case ATK_TEXT_BOUNDARY_WORD_END: + startPosition = previousWordEndPosition(position); + endPosition = nextWordPosition(startPosition); + break; + + default: + ASSERT_NOT_REACHED(); + } + + VisibleSelection selectedWord(startPosition, endPosition); + + // We mark the selection as 'upstream' so we can use that information later, + // when finding the actual offsets in getSelectionOffsetsForObject(). + if (boundaryType == ATK_TEXT_BOUNDARY_WORD_END) + selectedWord.setAffinity(Affinity::Upstream); + + return selectedWord; +} + +static int numberOfReplacedElementsBeforeOffset(AtkText* text, unsigned offset) +{ + GUniquePtr<char> textForObject(webkitAccessibleTextGetText(text, 0, offset)); + String textBeforeOffset = String::fromUTF8(textForObject.get()); + + int count = 0; + size_t index = textBeforeOffset.find(objectReplacementCharacter, 0); + while (index < offset && index != notFound) { + index = textBeforeOffset.find(objectReplacementCharacter, index + 1); + count++; + } + return count; +} + +static char* webkitAccessibleTextWordForBoundary(AtkText* text, int offset, AtkTextBoundary boundaryType, GetTextRelativePosition textPosition, int* startOffset, int* endOffset) +{ + AccessibilityObject* coreObject = core(text); + Document* document = coreObject->document(); + if (!document) + return emptyTextSelectionAtOffset(0, startOffset, endOffset); + + Node* node = getNodeForAccessibilityObject(coreObject); + if (!node) + return emptyTextSelectionAtOffset(0, startOffset, endOffset); + + int actualOffset = atkOffsetToWebCoreOffset(text, offset); + + // Besides of the usual conversion from ATK offsets to WebCore offsets, + // we need to consider the potential embedded objects that might have been + // inserted in the text exposed through AtkText when calculating the offset. + actualOffset -= numberOfReplacedElementsBeforeOffset(text, actualOffset); + + VisiblePosition caretPosition = coreObject->visiblePositionForIndex(actualOffset); + VisibleSelection currentWord = wordAtPositionForAtkBoundary(coreObject, caretPosition, boundaryType); + + // Take into account other relative positions, if needed, by + // calculating the new position that we would need to consider. + VisiblePosition newPosition = caretPosition; + switch (textPosition) { + case GetTextPositionAt: + break; + + case GetTextPositionBefore: + // Early return if asking for the previous word while already at the beginning. + if (isFirstVisiblePositionInNode(currentWord.visibleStart(), node)) + return emptyTextSelectionAtOffset(0, startOffset, endOffset); + + if (isStartOfLine(currentWord.end())) + newPosition = currentWord.visibleStart().previous(); + else + newPosition = startOfWord(currentWord.start(), LeftWordIfOnBoundary); + break; + + case GetTextPositionAfter: + // Early return if asking for the following word while already at the end. + if (isLastVisiblePositionInNode(currentWord.visibleEnd(), node)) + return emptyTextSelectionAtOffset(accessibilityObjectLength(coreObject), startOffset, endOffset); + + if (isEndOfLine(currentWord.end())) + newPosition = currentWord.visibleEnd().next(); + else + newPosition = endOfWord(currentWord.end(), RightWordIfOnBoundary); + break; + + default: + ASSERT_NOT_REACHED(); + } + + // Determine the relevant word we are actually interested in + // and calculate the ATK offsets for it, then return everything. + VisibleSelection selectedWord = newPosition != caretPosition ? wordAtPositionForAtkBoundary(coreObject, newPosition, boundaryType) : currentWord; + getSelectionOffsetsForObject(coreObject, selectedWord, *startOffset, *endOffset); + return webkitAccessibleTextGetText(text, *startOffset, *endOffset); +} + +static bool isSentenceBoundary(const VisiblePosition &pos) +{ + if (pos.isNull()) + return false; + + // It's definitely a sentence boundary if there's nothing before. + if (pos.previous().isNull()) + return true; + + // We go backwards and forward to make sure about this. + VisiblePosition startOfPreviousSentence = startOfSentence(pos); + return startOfPreviousSentence.isNotNull() && pos == endOfSentence(startOfPreviousSentence); +} + +static bool isWhiteSpaceBetweenSentences(const VisiblePosition& position) +{ + if (position.isNull()) + return false; + + if (!deprecatedIsEditingWhitespace(position.characterAfter())) + return false; + + auto start = startOfWord(position, RightWordIfOnBoundary); + auto end = endOfWord(start, RightWordIfOnBoundary); + if (!isSentenceBoundary(start) && !isSentenceBoundary(end)) + return false; + + auto range = makeSimpleRange(start, end); + return range && contains<ComposedTree>(*range, makeBoundaryPoint(position)); +} + +static VisibleSelection sentenceAtPositionForAtkBoundary(const AccessibilityObject*, const VisiblePosition& position, AtkTextBoundary boundaryType) +{ + VisiblePosition startPosition; + VisiblePosition endPosition; + + bool isAtStartOfSentenceForEndBoundary = isWhiteSpaceBetweenSentences(position) || isSentenceBoundary(position); + if (boundaryType == ATK_TEXT_BOUNDARY_SENTENCE_START || !isAtStartOfSentenceForEndBoundary) { + startPosition = isSentenceBoundary(position) ? position : startOfSentence(position); + // startOfSentence might stop at a linebreak in the HTML source code, + // but we don't want to stop there yet, so keep going. + while (!isSentenceBoundary(startPosition) && isHTMLLineBreak(startPosition.characterBefore())) + startPosition = startOfSentence(startPosition); + + endPosition = endOfSentence(startPosition); + } + + if (boundaryType == ATK_TEXT_BOUNDARY_SENTENCE_END) { + if (isAtStartOfSentenceForEndBoundary) { + startPosition = position; + endPosition = endOfSentence(endOfWord(position, RightWordIfOnBoundary)); + } + + // startOfSentence returns a position after any white space previous to + // the sentence, so we might need to adjust that offset for this boundary. + if (deprecatedIsEditingWhitespace(startPosition.characterBefore())) + startPosition = startOfWord(startPosition, LeftWordIfOnBoundary); + + // endOfSentence returns a position after any white space after the + // sentence, so we might need to adjust that offset for this boundary. + if (deprecatedIsEditingWhitespace(endPosition.characterBefore())) + endPosition = startOfWord(endPosition, LeftWordIfOnBoundary); + + // Finally, do some additional adjustments that might be needed if + // positions are at the start or the end of a line. + if (isStartOfLine(startPosition) && !isStartOfBlock(startPosition)) + startPosition = startPosition.previous(); + if (isStartOfLine(endPosition) && !isStartOfBlock(endPosition)) + endPosition = endPosition.previous(); + } + + VisibleSelection selectedSentence(startPosition, endPosition); + + // We mark the selection as 'upstream' so we can use that information later, + // when finding the actual offsets in getSelectionOffsetsForObject(). + if (boundaryType == ATK_TEXT_BOUNDARY_SENTENCE_END) + selectedSentence.setAffinity(Affinity::Upstream); + + return selectedSentence; +} + +static char* webkitAccessibleTextSentenceForBoundary(AtkText* text, int offset, AtkTextBoundary boundaryType, GetTextRelativePosition textPosition, int* startOffset, int* endOffset) +{ + AccessibilityObject* coreObject = core(text); + Document* document = coreObject->document(); + if (!document) + return emptyTextSelectionAtOffset(0, startOffset, endOffset); + + Node* node = getNodeForAccessibilityObject(coreObject); + if (!node) + return emptyTextSelectionAtOffset(0, startOffset, endOffset); + + int actualOffset = atkOffsetToWebCoreOffset(text, offset); + + // Besides of the usual conversion from ATK offsets to WebCore offsets, + // we need to consider the potential embedded objects that might have been + // inserted in the text exposed through AtkText when calculating the offset. + actualOffset -= numberOfReplacedElementsBeforeOffset(text, actualOffset); + + VisiblePosition caretPosition = coreObject->visiblePositionForIndex(actualOffset); + VisibleSelection currentSentence = sentenceAtPositionForAtkBoundary(coreObject, caretPosition, boundaryType); + + // Take into account other relative positions, if needed, by + // calculating the new position that we would need to consider. + VisiblePosition newPosition = caretPosition; + switch (textPosition) { + case GetTextPositionAt: + break; + + case GetTextPositionBefore: + // Early return if asking for the previous sentence while already at the beginning. + if (isFirstVisiblePositionInNode(currentSentence.visibleStart(), node)) + return emptyTextSelectionAtOffset(0, startOffset, endOffset); + newPosition = currentSentence.visibleStart().previous(); + break; + + case GetTextPositionAfter: + // Early return if asking for the following word while already at the end. + if (isLastVisiblePositionInNode(currentSentence.visibleEnd(), node)) + return emptyTextSelectionAtOffset(accessibilityObjectLength(coreObject), startOffset, endOffset); + newPosition = currentSentence.visibleEnd().next(); + break; + + default: + ASSERT_NOT_REACHED(); + } + + // Determine the relevant sentence we are actually interested in + // and calculate the ATK offsets for it, then return everything. + VisibleSelection selectedSentence = newPosition != caretPosition ? sentenceAtPositionForAtkBoundary(coreObject, newPosition, boundaryType) : currentSentence; + getSelectionOffsetsForObject(coreObject, selectedSentence, *startOffset, *endOffset); + return webkitAccessibleTextGetText(text, *startOffset, *endOffset); +} + +static VisibleSelection lineAtPositionForAtkBoundary(const AccessibilityObject* coreObject, const VisiblePosition& position, AtkTextBoundary boundaryType) +{ + UNUSED_PARAM(coreObject); + VisiblePosition startPosition; + VisiblePosition endPosition; + + switch (boundaryType) { + case ATK_TEXT_BOUNDARY_LINE_START: + startPosition = isStartOfLine(position) ? position : logicalStartOfLine(position); + endPosition = logicalEndOfLine(position); + + // In addition to checking that we are not at the end of a block, we need + // to check that endPosition has not UPSTREAM affinity, since that would + // cause trouble inside of text controls (we would be advancing too much). + if (!isEndOfBlock(endPosition) && endPosition.affinity() != Affinity::Upstream) + endPosition = endPosition.next(); + break; + + case ATK_TEXT_BOUNDARY_LINE_END: + startPosition = isEndOfLine(position) ? position : logicalStartOfLine(position); + if (!isStartOfBlock(startPosition)) + startPosition = startPosition.previous(); + endPosition = logicalEndOfLine(position); + break; + + default: + ASSERT_NOT_REACHED(); + } + + VisibleSelection selectedLine(startPosition, endPosition); + + // We mark the selection as 'upstream' so we can use that information later, + // when finding the actual offsets in getSelectionOffsetsForObject(). + if (boundaryType == ATK_TEXT_BOUNDARY_LINE_END) + selectedLine.setAffinity(Affinity::Upstream); + + return selectedLine; +} + +static char* webkitAccessibleTextLineForBoundary(AtkText* text, int offset, AtkTextBoundary boundaryType, GetTextRelativePosition textPosition, int* startOffset, int* endOffset) +{ + AccessibilityObject* coreObject = core(text); + Document* document = coreObject->document(); + if (!document) + return emptyTextSelectionAtOffset(0, startOffset, endOffset); + + Node* node = getNodeForAccessibilityObject(coreObject); + if (!node) + return emptyTextSelectionAtOffset(0, startOffset, endOffset); + + int actualOffset = atkOffsetToWebCoreOffset(text, offset); + + // Besides the usual conversion from ATK offsets to WebCore offsets, + // we need to consider the potential embedded objects that might have been + // inserted in the text exposed through AtkText when calculating the offset. + actualOffset -= numberOfReplacedElementsBeforeOffset(text, actualOffset); + + VisiblePosition caretPosition = coreObject->visiblePositionForIndex(actualOffset); + VisibleSelection currentLine = lineAtPositionForAtkBoundary(coreObject, caretPosition, boundaryType); + + // Take into account other relative positions, if needed, by + // calculating the new position that we would need to consider. + VisiblePosition newPosition = caretPosition; + switch (textPosition) { + case GetTextPositionAt: + // No need to do additional work if we are using the "at" position, we just + // explicitly list this case option to catch invalid values in the default case. + break; + + case GetTextPositionBefore: + // Early return if asking for the previous line while already at the beginning. + if (isFirstVisiblePositionInNode(currentLine.visibleStart(), node)) + return emptyTextSelectionAtOffset(0, startOffset, endOffset); + newPosition = currentLine.visibleStart().previous(); + break; + + case GetTextPositionAfter: + // Early return if asking for the following word while already at the end. + if (isLastVisiblePositionInNode(currentLine.visibleEnd(), node)) + return emptyTextSelectionAtOffset(accessibilityObjectLength(coreObject), startOffset, endOffset); + newPosition = currentLine.visibleEnd().next(); + break; + + default: + ASSERT_NOT_REACHED(); + } + + // Determine the relevant line we are actually interested in + // and calculate the ATK offsets for it, then return everything. + VisibleSelection selectedLine = newPosition != caretPosition ? lineAtPositionForAtkBoundary(coreObject, newPosition, boundaryType) : currentLine; + getSelectionOffsetsForObject(coreObject, selectedLine, *startOffset, *endOffset); + + // We might need to adjust the start or end offset to include the list item marker, + // if present, when printing the first or the last full line for a list item. + RenderObject* renderer = coreObject->renderer(); + if (renderer->isListItem()) { + // For Left-to-Right, the list item marker is at the beginning of the exposed text. + if (renderer->style().direction() == TextDirection::LTR && isFirstVisiblePositionInNode(selectedLine.visibleStart(), node)) + *startOffset = 0; + + // For Right-to-Left, the list item marker is at the end of the exposed text. + if (renderer->style().direction() == TextDirection::RTL && isLastVisiblePositionInNode(selectedLine.visibleEnd(), node)) + *endOffset = accessibilityObjectLength(coreObject); + } + + return webkitAccessibleTextGetText(text, *startOffset, *endOffset); +} + +static gchar* webkitAccessibleTextGetTextForOffset(AtkText* text, gint offset, AtkTextBoundary boundaryType, GetTextRelativePosition textPosition, gint* startOffset, gint* endOffset) +{ + AccessibilityObject* coreObject = core(text); + if (!coreObject || !coreObject->isAccessibilityRenderObject()) + return emptyTextSelectionAtOffset(0, startOffset, endOffset); + + switch (boundaryType) { + case ATK_TEXT_BOUNDARY_CHAR: + return webkitAccessibleTextGetChar(text, offset, textPosition, startOffset, endOffset); + + case ATK_TEXT_BOUNDARY_WORD_START: + case ATK_TEXT_BOUNDARY_WORD_END: + return webkitAccessibleTextWordForBoundary(text, offset, boundaryType, textPosition, startOffset, endOffset); + + case ATK_TEXT_BOUNDARY_LINE_START: + case ATK_TEXT_BOUNDARY_LINE_END: + return webkitAccessibleTextLineForBoundary(text, offset, boundaryType, textPosition, startOffset, endOffset); + + case ATK_TEXT_BOUNDARY_SENTENCE_START: + case ATK_TEXT_BOUNDARY_SENTENCE_END: + return webkitAccessibleTextSentenceForBoundary(text, offset, boundaryType, textPosition, startOffset, endOffset); + + default: + ASSERT_NOT_REACHED(); + } + + // This should never be reached. + return 0; +} + +static gchar* webkitAccessibleTextGetTextAfterOffset(AtkText* text, gint offset, AtkTextBoundary boundaryType, gint* startOffset, gint* endOffset) +{ + g_return_val_if_fail(ATK_TEXT(text), 0); + returnValIfWebKitAccessibleIsInvalid(WEBKIT_ACCESSIBLE(text), 0); + + return webkitAccessibleTextGetTextForOffset(text, offset, boundaryType, GetTextPositionAfter, startOffset, endOffset); +} + +static gchar* webkitAccessibleTextGetTextAtOffset(AtkText* text, gint offset, AtkTextBoundary boundaryType, gint* startOffset, gint* endOffset) +{ + g_return_val_if_fail(ATK_TEXT(text), 0); + returnValIfWebKitAccessibleIsInvalid(WEBKIT_ACCESSIBLE(text), 0); + + return webkitAccessibleTextGetTextForOffset(text, offset, boundaryType, GetTextPositionAt, startOffset, endOffset); +} + +static gchar* webkitAccessibleTextGetTextBeforeOffset(AtkText* text, gint offset, AtkTextBoundary boundaryType, gint* startOffset, gint* endOffset) +{ + g_return_val_if_fail(ATK_TEXT(text), 0); + returnValIfWebKitAccessibleIsInvalid(WEBKIT_ACCESSIBLE(text), 0); + + return webkitAccessibleTextGetTextForOffset(text, offset, boundaryType, GetTextPositionBefore, startOffset, endOffset); +} + +static gunichar webkitAccessibleTextGetCharacterAtOffset(AtkText* text, gint) +{ + g_return_val_if_fail(ATK_TEXT(text), 0); + returnValIfWebKitAccessibleIsInvalid(WEBKIT_ACCESSIBLE(text), 0); + + notImplemented(); + return 0; +} + +static gint webkitAccessibleTextGetCaretOffset(AtkText* text) +{ + g_return_val_if_fail(ATK_TEXT(text), 0); + returnValIfWebKitAccessibleIsInvalid(WEBKIT_ACCESSIBLE(text), 0); + + // coreObject is the unignored object whose offset the caller is requesting. + // focusedObject is the object with the caret. It is likely ignored -- unless it's a link. + AXCoreObject* coreObject = core(text); + if (!coreObject->isAccessibilityRenderObject()) + return 0; + + // We need to make sure we pass a valid object as reference. + if (coreObject->accessibilityIsIgnored()) + coreObject = coreObject->parentObjectUnignored(); + if (!coreObject) + return 0; + + int offset; + if (!objectFocusedAndCaretOffsetUnignored(coreObject, offset)) + return 0; + + return webCoreOffsetToAtkOffset(coreObject, offset); +} + +static AtkAttributeSet* webkitAccessibleTextGetRunAttributes(AtkText* text, gint offset, gint* startOffset, gint* endOffset) +{ + g_return_val_if_fail(ATK_TEXT(text), 0); + returnValIfWebKitAccessibleIsInvalid(WEBKIT_ACCESSIBLE(text), 0); + + AccessibilityObject* coreObject = core(text); + AtkAttributeSet* result; + + if (!coreObject) { + *startOffset = 0; + *endOffset = atk_text_get_character_count(text); + return 0; + } + + if (offset == -1) + offset = atk_text_get_caret_offset(text); + + result = getRunAttributesFromAccessibilityObject(coreObject, offset, startOffset, endOffset); + + if (*startOffset < 0) { + *startOffset = offset; + *endOffset = offset; + } + + return result; +} + +static AtkAttributeSet* webkitAccessibleTextGetDefaultAttributes(AtkText* text) +{ + g_return_val_if_fail(ATK_TEXT(text), 0); + returnValIfWebKitAccessibleIsInvalid(WEBKIT_ACCESSIBLE(text), 0); + + AccessibilityObject* coreObject = core(text); + if (!coreObject || !coreObject->isAccessibilityRenderObject()) + return 0; + + return getAttributeSetForAccessibilityObject(coreObject); +} + +static void webkitAccessibleTextGetCharacterExtents(AtkText* text, gint offset, gint* x, gint* y, gint* width, gint* height, AtkCoordType coords) +{ + g_return_if_fail(ATK_TEXT(text)); + returnIfWebKitAccessibleIsInvalid(WEBKIT_ACCESSIBLE(text)); + + IntRect extents = textExtents(text, offset, 1, coords); + *x = extents.x(); + *y = extents.y(); + *width = extents.width(); + *height = extents.height(); +} + +static void webkitAccessibleTextGetRangeExtents(AtkText* text, gint startOffset, gint endOffset, AtkCoordType coords, AtkTextRectangle* rect) +{ + g_return_if_fail(ATK_TEXT(text)); + returnIfWebKitAccessibleIsInvalid(WEBKIT_ACCESSIBLE(text)); + + IntRect extents = textExtents(text, startOffset, endOffset - startOffset, coords); + rect->x = extents.x(); + rect->y = extents.y(); + rect->width = extents.width(); + rect->height = extents.height(); +} + +static gint webkitAccessibleTextGetCharacterCount(AtkText* text) +{ + g_return_val_if_fail(ATK_TEXT(text), 0); + returnValIfWebKitAccessibleIsInvalid(WEBKIT_ACCESSIBLE(text), 0); + + return accessibilityObjectLength(core(text)); +} + +static gint webkitAccessibleTextGetOffsetAtPoint(AtkText* text, gint x, gint y, AtkCoordType) +{ + g_return_val_if_fail(ATK_TEXT(text), 0); + returnValIfWebKitAccessibleIsInvalid(WEBKIT_ACCESSIBLE(text), 0); + + // FIXME: Use the AtkCoordType + // TODO: Is it correct to ignore range.length? + IntPoint pos(x, y); + PlainTextRange range = core(text)->doAXRangeForPosition(pos); + return range.start; +} + +static gint webkitAccessibleTextGetNSelections(AtkText* text) +{ + g_return_val_if_fail(ATK_TEXT(text), 0); + returnValIfWebKitAccessibleIsInvalid(WEBKIT_ACCESSIBLE(text), 0); + + AccessibilityObject* coreObject = core(text); + VisibleSelection selection = coreObject->selection(); + + // Only range selections are needed for the purpose of this method + if (!selection.isRange()) + return 0; + + // We don't support multiple selections for now, so there's only + // two possibilities + // Also, we don't want to do anything if the selection does not + // belong to the currently selected object. We have to check since + // there's no way to get the selection for a given object, only + // the global one (the API is a bit confusing) + return selectionBelongsToObject(coreObject, selection) ? 1 : 0; +} + +static gchar* webkitAccessibleTextGetSelection(AtkText* text, gint selectionNum, gint* startOffset, gint* endOffset) +{ + g_return_val_if_fail(ATK_TEXT(text), 0); + returnValIfWebKitAccessibleIsInvalid(WEBKIT_ACCESSIBLE(text), 0); + + // WebCore does not support multiple selection, so anything but 0 does not make sense for now. + if (selectionNum) + return 0; + + // Get the offsets of the selection for the selected object + AccessibilityObject* coreObject = core(text); + VisibleSelection selection = coreObject->selection(); + getSelectionOffsetsForObject(coreObject, selection, *startOffset, *endOffset); + + // Return 0 instead of "", as that's the expected result for + // this AtkText method when there's no selection + if (*startOffset == *endOffset) + return 0; + + return webkitAccessibleTextGetText(text, *startOffset, *endOffset); +} + +static gboolean webkitAccessibleTextAddSelection(AtkText* text, gint, gint) +{ + g_return_val_if_fail(ATK_TEXT(text), FALSE); + returnValIfWebKitAccessibleIsInvalid(WEBKIT_ACCESSIBLE(text), FALSE); + + notImplemented(); + return FALSE; +} + +static gboolean webkitAccessibleTextSetSelection(AtkText* text, gint selectionNum, gint startOffset, gint endOffset) +{ + g_return_val_if_fail(ATK_TEXT(text), FALSE); + returnValIfWebKitAccessibleIsInvalid(WEBKIT_ACCESSIBLE(text), FALSE); + + // WebCore does not support multiple selection, so anything but 0 does not make sense for now. + if (selectionNum) + return FALSE; + + AccessibilityObject* coreObject = core(text); + if (!coreObject->isAccessibilityRenderObject()) + return FALSE; + + // Consider -1 and out-of-bound values and correct them to length + gint textCount = webkitAccessibleTextGetCharacterCount(text); + if (startOffset < 0 || startOffset > textCount) + startOffset = textCount; + if (endOffset < 0 || endOffset > textCount) + endOffset = textCount; + + // We need to adjust the offsets for the list item marker. + int offsetAdjustment = offsetAdjustmentForListItem(coreObject); + if (offsetAdjustment) { + if (startOffset < offsetAdjustment || endOffset < offsetAdjustment) + return FALSE; + + startOffset = atkOffsetToWebCoreOffset(text, startOffset); + endOffset = atkOffsetToWebCoreOffset(text, endOffset); + } + + PlainTextRange textRange(startOffset, endOffset - startOffset); + VisiblePositionRange range = coreObject->visiblePositionRangeForRange(textRange); + if (range.isNull()) + return FALSE; + + coreObject->setSelectedVisiblePositionRange(range); + return TRUE; +} + +static gboolean webkitAccessibleTextRemoveSelection(AtkText* text, gint selectionNum) +{ + g_return_val_if_fail(ATK_TEXT(text), FALSE); + returnValIfWebKitAccessibleIsInvalid(WEBKIT_ACCESSIBLE(text), FALSE); + + // WebCore does not support multiple selection, so anything but 0 does not make sense for now. + if (selectionNum) + return FALSE; + + // Do nothing if current selection doesn't belong to the object + if (!webkitAccessibleTextGetNSelections(text)) + return FALSE; + + // Set a new 0-sized selection to the caret position, in order + // to simulate selection removal (GAIL style) + gint caretOffset = webkitAccessibleTextGetCaretOffset(text); + return webkitAccessibleTextSetSelection(text, selectionNum, caretOffset, caretOffset); +} + +static gboolean webkitAccessibleTextSetCaretOffset(AtkText* text, gint offset) +{ + // Internally, setting the caret offset is equivalent to set a zero-length + // selection, so delegate in that implementation and void duplicated code. + return webkitAccessibleTextSetSelection(text, 0, offset, offset); +} + +static gchar* webkitAccessibleTextGetStringAtOffset(AtkText* text, gint offset, AtkTextGranularity granularity, gint* startOffset, gint* endOffset) +{ + // This new API has been designed to simplify the AtkText interface and it has been + // designed to keep exactly the same behaviour the atk_text_get_text_at_text() for + // ATK_TEXT_BOUNDARY_*_START boundaries, so for now we just need to translate the + // granularity to the right old boundary and reuse the code for the old API. + // However, this should be simplified later on (and a lot of code removed) once + // WebKitGTK depends on ATK >= 2.9.4 *and* can safely assume that a version of + // AT-SPI2 new enough not to include the old APIs is being used. But until then, + // we will have to live with both the old and new APIs implemented here. + // FIXME: WebKit nowadays depends on much newer ATK and we can safely assume AT-SPI2 + // isn't ancient. But whoever wrote this code didn't use ATK_CHECK_VERSION() guards, + // so it's unclear what is supposed to be changed here. + AtkTextBoundary boundaryType = ATK_TEXT_BOUNDARY_CHAR; + switch (granularity) { + case ATK_TEXT_GRANULARITY_CHAR: + break; + + case ATK_TEXT_GRANULARITY_WORD: + boundaryType = ATK_TEXT_BOUNDARY_WORD_START; + break; + + case ATK_TEXT_GRANULARITY_SENTENCE: + boundaryType = ATK_TEXT_BOUNDARY_SENTENCE_START; + break; + + case ATK_TEXT_GRANULARITY_LINE: + boundaryType = ATK_TEXT_BOUNDARY_LINE_START; + break; + + case ATK_TEXT_GRANULARITY_PARAGRAPH: + // FIXME: This has not been a need with the old AtkText API, which means ATs won't + // need it yet for some time, so we can skip it for now. + notImplemented(); + return g_strdup(""); + + default: + ASSERT_NOT_REACHED(); + } + + return webkitAccessibleTextGetTextForOffset(text, offset, boundaryType, GetTextPositionAt, startOffset, endOffset); +} + +void webkitAccessibleTextInterfaceInit(AtkTextIface* iface) +{ + iface->get_text = webkitAccessibleTextGetText; + iface->get_text_after_offset = webkitAccessibleTextGetTextAfterOffset; + iface->get_text_at_offset = webkitAccessibleTextGetTextAtOffset; + iface->get_text_before_offset = webkitAccessibleTextGetTextBeforeOffset; + iface->get_character_at_offset = webkitAccessibleTextGetCharacterAtOffset; + iface->get_caret_offset = webkitAccessibleTextGetCaretOffset; + iface->get_run_attributes = webkitAccessibleTextGetRunAttributes; + iface->get_default_attributes = webkitAccessibleTextGetDefaultAttributes; + iface->get_character_extents = webkitAccessibleTextGetCharacterExtents; + iface->get_range_extents = webkitAccessibleTextGetRangeExtents; + iface->get_character_count = webkitAccessibleTextGetCharacterCount; + iface->get_offset_at_point = webkitAccessibleTextGetOffsetAtPoint; + iface->get_n_selections = webkitAccessibleTextGetNSelections; + iface->get_selection = webkitAccessibleTextGetSelection; + iface->add_selection = webkitAccessibleTextAddSelection; + iface->remove_selection = webkitAccessibleTextRemoveSelection; + iface->set_selection = webkitAccessibleTextSetSelection; + iface->set_caret_offset = webkitAccessibleTextSetCaretOffset; + iface->get_string_at_offset = webkitAccessibleTextGetStringAtOffset; +} + +#endif diff -urpN webkitgtk-2.38.1.glib244/Source/WebCore/accessibility/atk/WebKitAccessibleInterfaceText.h webkitgtk-2.38.1/Source/WebCore/accessibility/atk/WebKitAccessibleInterfaceText.h --- webkitgtk-2.38.1.glib244/Source/WebCore/accessibility/atk/WebKitAccessibleInterfaceText.h 1969-12-31 18:00:00.000000000 -0600 +++ webkitgtk-2.38.1/Source/WebCore/accessibility/atk/WebKitAccessibleInterfaceText.h 2022-10-24 13:34:51.925454646 -0500 @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2008 Nuanti Ltd. + * Copyright (C) 2009 Jan Alonzo + * Copyright (C) 2009, 2010, 2011, 2012 Igalia S.L. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#pragma once + +#if ENABLE(ACCESSIBILITY) && USE(ATK) + +#include <atk/atk.h> + +void webkitAccessibleTextInterfaceInit(AtkTextIface*); + +#endif // ENABLE(ACCESSIBILITY) && USE(ATK) diff -urpN webkitgtk-2.38.1.glib244/Source/WebCore/accessibility/atk/WebKitAccessibleInterfaceValue.cpp webkitgtk-2.38.1/Source/WebCore/accessibility/atk/WebKitAccessibleInterfaceValue.cpp --- webkitgtk-2.38.1.glib244/Source/WebCore/accessibility/atk/WebKitAccessibleInterfaceValue.cpp 1969-12-31 18:00:00.000000000 -0600 +++ webkitgtk-2.38.1/Source/WebCore/accessibility/atk/WebKitAccessibleInterfaceValue.cpp 2022-10-24 13:34:51.925454646 -0500 @@ -0,0 +1,200 @@ +/* + * Copyright (C) 2011, 2012 Igalia S.L. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include "config.h" +#include "WebKitAccessibleInterfaceValue.h" + +#if ENABLE(ACCESSIBILITY) && USE(ATK) + +#include "AccessibilityObject.h" +#include "HTMLNames.h" +#include "WebKitAccessible.h" +#include "WebKitAccessibleUtil.h" +#include <wtf/text/CString.h> + +using namespace WebCore; + +static AccessibilityObject* core(AtkValue* value) +{ + if (!WEBKIT_IS_ACCESSIBLE(value)) + return 0; + + return &webkitAccessibleGetAccessibilityObject(WEBKIT_ACCESSIBLE(value)); +} + +static bool webkitAccessibleSetNewValue(AtkValue* coreValue, const gdouble newValue) +{ + AccessibilityObject* coreObject = core(coreValue); + if (!coreObject->canSetValueAttribute()) + return FALSE; + + // Check value against range limits + double value; + value = std::max(static_cast<double>(coreObject->minValueForRange()), newValue); + value = std::min(static_cast<double>(coreObject->maxValueForRange()), newValue); + + coreObject->setValue(String::numberToStringFixedPrecision(value)); + return TRUE; +} + +static float webkitAccessibleGetIncrementValue(AccessibilityObject* coreObject) +{ + if (!coreObject->getAttribute(HTMLNames::stepAttr).isEmpty()) + return coreObject->stepValueForRange(); + + // If 'step' attribute is not defined, WebCore assumes a 5% of the + // range between minimum and maximum values. Implicit value of step should be one or larger. + float step = (coreObject->maxValueForRange() - coreObject->minValueForRange()) * 0.05; + return step < 1 ? 1 : step; +} + +static void webkitAccessibleGetValueAndText(AtkValue* value, gdouble* currentValue, gchar** alternativeText) +{ + g_return_if_fail(ATK_VALUE(value)); + returnIfWebKitAccessibleIsInvalid(WEBKIT_ACCESSIBLE(value)); + + AccessibilityObject* coreObject = core(value); + if (!coreObject) + return; + + if (currentValue) + *currentValue = coreObject->valueForRange(); + if (alternativeText) + *alternativeText = g_strdup_printf("%s", coreObject->valueDescription().utf8().data()); +} + +static double webkitAccessibleGetIncrement(AtkValue* value) +{ + g_return_val_if_fail(ATK_VALUE(value), 0); + returnValIfWebKitAccessibleIsInvalid(WEBKIT_ACCESSIBLE(value), 0); + + AccessibilityObject* coreObject = core(value); + if (!coreObject) + return 0; + + return webkitAccessibleGetIncrementValue(coreObject); +} + +static void webkitAccessibleSetValue(AtkValue* value, const gdouble newValue) +{ + g_return_if_fail(ATK_VALUE(value)); + returnIfWebKitAccessibleIsInvalid(WEBKIT_ACCESSIBLE(value)); + + webkitAccessibleSetNewValue(value, newValue); +} + +static AtkRange* webkitAccessibleGetRange(AtkValue* value) +{ + g_return_val_if_fail(ATK_VALUE(value), nullptr); + returnValIfWebKitAccessibleIsInvalid(WEBKIT_ACCESSIBLE(value), nullptr); + + AccessibilityObject* coreObject = core(value); + if (!coreObject) + return nullptr; + + gdouble minValue = coreObject->minValueForRange(); + gdouble maxValue = coreObject->maxValueForRange(); + gchar* valueDescription = g_strdup_printf("%s", coreObject->valueDescription().utf8().data()); + return atk_range_new(minValue, maxValue, valueDescription); +} + +static void webkitAccessibleValueGetCurrentValue(AtkValue* value, GValue* gValue) +{ + g_return_if_fail(ATK_VALUE(value)); + returnIfWebKitAccessibleIsInvalid(WEBKIT_ACCESSIBLE(value)); + + memset(gValue, 0, sizeof(GValue)); + g_value_init(gValue, G_TYPE_FLOAT); + g_value_set_float(gValue, core(value)->valueForRange()); +} + +static void webkitAccessibleValueGetMaximumValue(AtkValue* value, GValue* gValue) +{ + g_return_if_fail(ATK_VALUE(value)); + returnIfWebKitAccessibleIsInvalid(WEBKIT_ACCESSIBLE(value)); + + memset(gValue, 0, sizeof(GValue)); + g_value_init(gValue, G_TYPE_FLOAT); + g_value_set_float(gValue, core(value)->maxValueForRange()); +} + +static void webkitAccessibleValueGetMinimumValue(AtkValue* value, GValue* gValue) +{ + g_return_if_fail(ATK_VALUE(value)); + returnIfWebKitAccessibleIsInvalid(WEBKIT_ACCESSIBLE(value)); + + memset(gValue, 0, sizeof(GValue)); + g_value_init(gValue, G_TYPE_FLOAT); + g_value_set_float(gValue, core(value)->minValueForRange()); +} + +static gboolean webkitAccessibleValueSetCurrentValue(AtkValue* value, const GValue* gValue) +{ + g_return_val_if_fail(ATK_VALUE(value), FALSE); + returnValIfWebKitAccessibleIsInvalid(WEBKIT_ACCESSIBLE(value), FALSE); + + double newValue; + if (G_VALUE_HOLDS_DOUBLE(gValue)) + newValue = g_value_get_double(gValue); + else if (G_VALUE_HOLDS_FLOAT(gValue)) + newValue = g_value_get_float(gValue); + else if (G_VALUE_HOLDS_INT64(gValue)) + newValue = g_value_get_int64(gValue); + else if (G_VALUE_HOLDS_INT(gValue)) + newValue = g_value_get_int(gValue); + else if (G_VALUE_HOLDS_LONG(gValue)) + newValue = g_value_get_long(gValue); + else if (G_VALUE_HOLDS_ULONG(gValue)) + newValue = g_value_get_ulong(gValue); + else if (G_VALUE_HOLDS_UINT64(gValue)) + newValue = g_value_get_uint64(gValue); + else if (G_VALUE_HOLDS_UINT(gValue)) + newValue = g_value_get_uint(gValue); + else + return FALSE; + + return webkitAccessibleSetNewValue(value, newValue); +} + +static void webkitAccessibleValueGetMinimumIncrement(AtkValue* value, GValue* gValue) +{ + g_return_if_fail(ATK_VALUE(value)); + returnIfWebKitAccessibleIsInvalid(WEBKIT_ACCESSIBLE(value)); + + memset(gValue, 0, sizeof(GValue)); + g_value_init(gValue, G_TYPE_FLOAT); + + AccessibilityObject* coreObject = core(value); + g_value_set_float(gValue, webkitAccessibleGetIncrementValue(coreObject)); +} + +void webkitAccessibleValueInterfaceInit(AtkValueIface* iface) +{ + iface->get_value_and_text = webkitAccessibleGetValueAndText; + iface->get_increment = webkitAccessibleGetIncrement; + iface->set_value = webkitAccessibleSetValue; + iface->get_range = webkitAccessibleGetRange; + iface->get_current_value = webkitAccessibleValueGetCurrentValue; + iface->get_maximum_value = webkitAccessibleValueGetMaximumValue; + iface->get_minimum_value = webkitAccessibleValueGetMinimumValue; + iface->set_current_value = webkitAccessibleValueSetCurrentValue; + iface->get_minimum_increment = webkitAccessibleValueGetMinimumIncrement; +} + +#endif diff -urpN webkitgtk-2.38.1.glib244/Source/WebCore/accessibility/atk/WebKitAccessibleInterfaceValue.h webkitgtk-2.38.1/Source/WebCore/accessibility/atk/WebKitAccessibleInterfaceValue.h --- webkitgtk-2.38.1.glib244/Source/WebCore/accessibility/atk/WebKitAccessibleInterfaceValue.h 1969-12-31 18:00:00.000000000 -0600 +++ webkitgtk-2.38.1/Source/WebCore/accessibility/atk/WebKitAccessibleInterfaceValue.h 2022-10-24 13:34:51.925454646 -0500 @@ -0,0 +1,28 @@ +/* + * Copyright (C) 2011, 2012 Igalia S.L. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#pragma once + +#if ENABLE(ACCESSIBILITY) && USE(ATK) + +#include <atk/atk.h> + +void webkitAccessibleValueInterfaceInit(AtkValueIface*); + +#endif // ENABLE(ACCESSIBILITY) && USE(ATK) diff -urpN webkitgtk-2.38.1.glib244/Source/WebCore/accessibility/atk/WebKitAccessibleUtil.cpp webkitgtk-2.38.1/Source/WebCore/accessibility/atk/WebKitAccessibleUtil.cpp --- webkitgtk-2.38.1.glib244/Source/WebCore/accessibility/atk/WebKitAccessibleUtil.cpp 1969-12-31 18:00:00.000000000 -0600 +++ webkitgtk-2.38.1/Source/WebCore/accessibility/atk/WebKitAccessibleUtil.cpp 2022-10-24 13:34:51.925454646 -0500 @@ -0,0 +1,242 @@ +/* + * Copyright (C) 2008 Nuanti Ltd. + * Copyright (C) 2009 Jan Alonzo + * Copyright (C) 2009, 2010, 2012 Igalia S.L. + * + * Portions from Mozilla a11y, copyright as follows: + * + * The Original Code is mozilla.org code. + * + * The Initial Developer of the Original Code is + * Sun Microsystems, Inc. + * Portions created by the Initial Developer are Copyright (C) 2002 + * the Initial Developer. All Rights Reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include "config.h" +#include "WebKitAccessibleUtil.h" + +#if ENABLE(ACCESSIBILITY) && USE(ATK) + +#include "AXObjectCache.h" +#include "AccessibilityObject.h" +#include "FrameView.h" +#include "IntRect.h" +#include "Node.h" +#include "Range.h" +#include "RenderObject.h" +#include "TextIterator.h" +#include "VisibleSelection.h" + +#include <wtf/text/AtomString.h> +#include <wtf/text/CString.h> + +using namespace WebCore; + +AtkAttributeSet* addToAtkAttributeSet(AtkAttributeSet* attributeSet, const char* name, const char* value) +{ + AtkAttribute* attribute = static_cast<AtkAttribute*>(g_malloc(sizeof(AtkAttribute))); + attribute->name = g_strdup(name); + attribute->value = g_strdup(value); + attributeSet = g_slist_prepend(attributeSet, attribute); + return attributeSet; +} + +void contentsRelativeToAtkCoordinateType(AccessibilityObject* coreObject, AtkCoordType coordType, IntRect rect, gint* x, gint* y, gint* width, gint* height) +{ + FrameView* frameView = coreObject->documentFrameView(); + + if (frameView) { + switch (coordType) { + case ATK_XY_WINDOW: + rect = frameView->contentsToWindow(rect); + break; + case ATK_XY_SCREEN: + rect = frameView->contentsToScreen(rect); + break; +#if ATK_CHECK_VERSION(2, 30, 0) + case ATK_XY_PARENT: + RELEASE_ASSERT_NOT_REACHED(); +#endif + } + } + + if (x) + *x = rect.x(); + if (y) + *y = rect.y(); + if (width) + *width = rect.width(); + if (height) + *height = rect.height(); +} + +// FIXME: Different kinds of elements are putting the title tag to use +// in different AX fields. This might not be 100% correct but we will +// keep it now in order to achieve consistency with previous behavior. +static bool titleTagShouldBeUsedInDescriptionField(AccessibilityObject* coreObject) +{ + return (coreObject->isLink() && !coreObject->isImageMapLink()) || coreObject->isImage(); +} + +// This should be the "visible" text that's actually on the screen if possible. +// If there's alternative text, that can override the title. +String accessibilityTitle(AccessibilityObject* coreObject) +{ + Vector<AccessibilityText> textOrder; + coreObject->accessibilityText(textOrder); + + for (const AccessibilityText& text : textOrder) { + // Once we encounter visible text, or the text from our children that should be used foremost. + if (text.textSource == AccessibilityTextSource::Visible || text.textSource == AccessibilityTextSource::Children) + return text.text; + + // If there's an element that labels this object and it's not exposed, then we should use + // that text as our title. + if (text.textSource == AccessibilityTextSource::LabelByElement && !coreObject->titleUIElement()) + return text.text; + + // Elements of role AccessibilityRole::Toolbar will return its title as AccessibilityTextSource::Alternative. + if (coreObject->roleValue() == AccessibilityRole::Toolbar && text.textSource == AccessibilityTextSource::Alternative) + return text.text; + + // FIXME: The title tag is used in certain cases for the title. This usage should + // probably be in the description field since it's not "visible". + if (text.textSource == AccessibilityTextSource::TitleTag && !titleTagShouldBeUsedInDescriptionField(coreObject)) + return text.text; + } + + return String(); +} + +String accessibilityDescription(AccessibilityObject* coreObject) +{ + Vector<AccessibilityText> textOrder; + coreObject->accessibilityText(textOrder); + + bool visibleTextAvailable = false; + for (const AccessibilityText& text : textOrder) { + if (text.textSource == AccessibilityTextSource::Alternative) + return text.text; + + switch (text.textSource) { + case AccessibilityTextSource::Visible: + case AccessibilityTextSource::Children: + case AccessibilityTextSource::LabelByElement: + visibleTextAvailable = true; + default: + break; + } + + if (text.textSource == AccessibilityTextSource::TitleTag && !visibleTextAvailable) + return text.text; + } + + return String(); +} + +bool selectionBelongsToObject(AccessibilityObject* coreObject, VisibleSelection& selection) +{ + if (!coreObject || !coreObject->isAccessibilityRenderObject()) + return false; + + auto range = selection.firstRange(); + return range && intersects<ComposedTree>(*range, *coreObject->node()); +} + +AXCoreObject* objectFocusedAndCaretOffsetUnignored(AXCoreObject* referenceObject, int& offset) +{ + // Indication that something bogus has transpired. + offset = -1; + + Document* document = referenceObject->document(); + if (!document) + return nullptr; + + Node* focusedNode = referenceObject->selection().end().containerNode(); + if (!focusedNode) + return nullptr; + + RenderObject* focusedRenderer = focusedNode->renderer(); + if (!focusedRenderer) + return nullptr; + + AccessibilityObject* focusedObject = document->axObjectCache()->getOrCreate(focusedRenderer); + if (!focusedObject) + return nullptr; + + // Look for the actual (not ignoring accessibility) selected object. + AXCoreObject* firstUnignoredParent = focusedObject; + if (firstUnignoredParent->accessibilityIsIgnored()) + firstUnignoredParent = firstUnignoredParent->parentObjectUnignored(); + if (!firstUnignoredParent) + return nullptr; + + // Don't ignore links if the offset is being requested for a link + // or if the link is a block. + if (!referenceObject->isLink() && firstUnignoredParent->isLink() + && !(firstUnignoredParent->renderer() && !firstUnignoredParent->renderer()->isInline())) + firstUnignoredParent = firstUnignoredParent->parentObjectUnignored(); + if (!firstUnignoredParent) + return nullptr; + + // The reference object must either coincide with the focused + // object being considered, or be a descendant of it. + if (referenceObject->isDescendantOfObject(firstUnignoredParent)) + referenceObject = firstUnignoredParent; + + Node* startNode = nullptr; + if (firstUnignoredParent != referenceObject || firstUnignoredParent->isTextControl()) { + // We need to use the first child's node of the reference + // object as the start point to calculate the caret offset + // because we want it to be relative to the object of + // reference, not just to the focused object (which could have + // previous siblings which should be taken into account too). + AXCoreObject* axFirstChild = reinterpret_cast<AccessibilityObject *>(referenceObject)->firstChild(); + if (axFirstChild) + startNode = axFirstChild->node(); + } + // Getting the Position of a PseudoElement now triggers an assertion. + // This can occur when clicking on empty space in a render block. + if (!startNode || startNode->isPseudoElement()) + startNode = firstUnignoredParent->node(); + + // Check if the node for the first parent object not ignoring + // accessibility is null again before using it. This might happen + // with certain kind of accessibility objects, such as the root + // one (the scroller containing the webArea object). + if (!startNode) + return nullptr; + + VisiblePosition startPosition = VisiblePosition(positionBeforeNode(startNode)); + VisiblePosition endPosition = firstUnignoredParent->selection().visibleEnd(); + + if (startPosition == endPosition) + offset = 0; + else if (!isStartOfLine(endPosition)) { + auto range = makeSimpleRange(startPosition, endPosition.previous()); + offset = (range ? characterCount(*range, TextIteratorBehavior::EmitsCharactersBetweenAllVisiblePositions) : 0) + 1; + } else { + auto range = makeSimpleRange(startPosition, endPosition); + offset = range ? characterCount(*range, TextIteratorBehavior::EmitsCharactersBetweenAllVisiblePositions) : 0; + } + + return firstUnignoredParent; +} + +#endif diff -urpN webkitgtk-2.38.1.glib244/Source/WebCore/accessibility/atk/WebKitAccessibleUtil.h webkitgtk-2.38.1/Source/WebCore/accessibility/atk/WebKitAccessibleUtil.h --- webkitgtk-2.38.1.glib244/Source/WebCore/accessibility/atk/WebKitAccessibleUtil.h 1969-12-31 18:00:00.000000000 -0600 +++ webkitgtk-2.38.1/Source/WebCore/accessibility/atk/WebKitAccessibleUtil.h 2022-10-24 13:34:51.925454646 -0500 @@ -0,0 +1,72 @@ +/* + * Copyright (C) 2008 Nuanti Ltd. + * Copyright (C) 2009 Jan Alonzo + * Copyright (C) 2009, 2010, 2012 Igalia S.L. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#pragma once + +#if ENABLE(ACCESSIBILITY) && USE(ATK) + +#include <atk/atk.h> +#include <wtf/text/WTFString.h> + +namespace WebCore { +class AXCoreObject; +class AccessibilityObject; +class IntRect; +class VisibleSelection; +} + +// An existing accessibility object is considered to be invalid whether it's already +// detached or if it's not but just updating the layout will detach it anyway. +#define returnIfWebKitAccessibleIsInvalid(webkitAccessible) G_STMT_START { \ + if (!webkitAccessible || webkitAccessibleIsDetached(webkitAccessible)) \ + return; \ + auto& coreObject = webkitAccessibleGetAccessibilityObject(webkitAccessible); \ + if (!coreObject.document()) \ + return; \ + coreObject.updateBackingStore(); \ + if (webkitAccessibleIsDetached(webkitAccessible)) \ + return; \ + ; } G_STMT_END + +#define returnValIfWebKitAccessibleIsInvalid(webkitAccessible, val) G_STMT_START { \ + if (!webkitAccessible || webkitAccessibleIsDetached(webkitAccessible)) \ + return (val); \ + auto& coreObject = webkitAccessibleGetAccessibilityObject(webkitAccessible); \ + if (!coreObject.document()) \ + return (val); \ + coreObject.updateBackingStore(); \ + if (webkitAccessibleIsDetached(webkitAccessible)) \ + return (val); \ + ; } G_STMT_END + +AtkAttributeSet* addToAtkAttributeSet(AtkAttributeSet*, const char* name, const char* value); + +void contentsRelativeToAtkCoordinateType(WebCore::AccessibilityObject*, AtkCoordType, WebCore::IntRect, gint* x, gint* y, gint* width = nullptr, gint* height = nullptr); + +String accessibilityTitle(WebCore::AccessibilityObject*); + +String accessibilityDescription(WebCore::AccessibilityObject*); + +bool selectionBelongsToObject(WebCore::AccessibilityObject*, WebCore::VisibleSelection&); + +WebCore::AXCoreObject* objectFocusedAndCaretOffsetUnignored(WebCore::AXCoreObject*, int& offset); + +#endif // ENABLE(ACCESSIBILITY) && USE(ATK) diff -urpN webkitgtk-2.38.1.glib244/Source/WebCore/accessibility/atspi/AccessibilityObjectAtspi.cpp webkitgtk-2.38.1/Source/WebCore/accessibility/atspi/AccessibilityObjectAtspi.cpp --- webkitgtk-2.38.1.glib244/Source/WebCore/accessibility/atspi/AccessibilityObjectAtspi.cpp 2022-09-08 06:51:52.497481800 -0500 +++ webkitgtk-2.38.1/Source/WebCore/accessibility/atspi/AccessibilityObjectAtspi.cpp 2022-10-24 13:34:51.925454646 -0500 @@ -970,7 +970,7 @@ HashMap<String, String> AccessibilityObj // According to the W3C Core Accessibility API Mappings 1.1, section 5.4.1 General Rules: // "User agents must expose the WAI-ARIA role string if the API supports a mechanism to do so." - // In the case of ATSPI, the mechanism to do so is an object attribute pair (xml-roles:"string"). + // In the case of ATK, the mechanism to do so is an object attribute pair (xml-roles:"string"). // We cannot use the computedRoleString for this purpose because it is not limited to elements // with ARIA roles, and it might not contain the actual ARIA role value (e.g. DPub ARIA). String roleString = static_cast<AccessibilityObject*>(m_coreObject)->getAttribute(HTMLNames::roleAttr); diff -urpN webkitgtk-2.38.1.glib244/Source/WebCore/accessibility/AXObjectCache.h webkitgtk-2.38.1/Source/WebCore/accessibility/AXObjectCache.h --- webkitgtk-2.38.1.glib244/Source/WebCore/accessibility/AXObjectCache.h 2022-10-19 03:11:34.494284200 -0500 +++ webkitgtk-2.38.1/Source/WebCore/accessibility/AXObjectCache.h 2022-10-24 13:34:51.929454667 -0500 @@ -37,6 +37,10 @@ #include <wtf/ListHashSet.h> #include <wtf/WeakHashSet.h> +#if USE(ATK) +#include <wtf/glib/GRefPtr.h> +#endif + namespace WTF { class TextStream; } @@ -585,6 +589,10 @@ private: bool m_relationsNeedUpdate { true }; HashSet<AXID> m_relationTargets; +#if USE(ATK) + ListHashSet<RefPtr<AccessibilityObject>> m_deferredAttachedWrapperObjectList; + ListHashSet<GRefPtr<AccessibilityObjectWrapper>> m_deferredDetachedWrapperList; +#endif #if USE(ATSPI) ListHashSet<RefPtr<AXCoreObject>> m_deferredParentChangedList; #endif diff -urpN webkitgtk-2.38.1.glib244/Source/WebCore/editing/atk/FrameSelectionAtk.cpp webkitgtk-2.38.1/Source/WebCore/editing/atk/FrameSelectionAtk.cpp --- webkitgtk-2.38.1.glib244/Source/WebCore/editing/atk/FrameSelectionAtk.cpp 1969-12-31 18:00:00.000000000 -0600 +++ webkitgtk-2.38.1/Source/WebCore/editing/atk/FrameSelectionAtk.cpp 2022-10-24 13:34:51.929454667 -0500 @@ -0,0 +1,119 @@ +/* + * Copyright (C) 2009 Igalia S.L. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include "config.h" +#include "FrameSelection.h" + +#if ENABLE(ACCESSIBILITY) && USE(ATK) + +#include "AXObjectCache.h" +#include "DocumentInlines.h" +#include "Frame.h" +#include "RenderListItem.h" +#include "WebKitAccessible.h" +#include "WebKitAccessibleUtil.h" +#include <glib.h> +#include <wtf/NeverDestroyed.h> +#include <wtf/RefPtr.h> + +namespace WebCore { + +static void emitTextSelectionChange(AXCoreObject* object, VisibleSelection selection, int offset) +{ + auto* axObject = object->wrapper(); + if (!axObject || !ATK_IS_TEXT(axObject)) + return; + + // We need to adjust the offset for the list item marker in Left-To-Right text because + // the list item marker is exposed through the text of the accessible list item rather + // than through a separate accessible object. + RenderObject* renderer = object->renderer(); + if (is<RenderListItem>(renderer) && renderer->style().direction() == TextDirection::LTR) + offset += downcast<RenderListItem>(*renderer).markerTextWithSuffix().length(); + + g_signal_emit_by_name(axObject, "text-caret-moved", offset); + if (selection.isRange()) + g_signal_emit_by_name(axObject, "text-selection-changed"); +} + +static void maybeEmitTextFocusChange(RefPtr<AXCoreObject>&& object) +{ + // This static variable is needed to keep track of the old object + // as per previous calls to this function, in order to properly + // decide whether to emit some signals or not. + static NeverDestroyed<RefPtr<AXCoreObject>> oldObject; + + // Ensure the oldObject belongs to the same document that the + // current object so further comparisons make sense. Otherwise, + // just reset oldObject to 0 so it won't be taken into account in + // the immediately following call to this function. + if (object && oldObject.get() && oldObject.get()->document() != object->document()) + oldObject.get() = nullptr; + + auto* axObject = object ? object->wrapper() : nullptr; + auto* oldAxObject = oldObject.get() ? oldObject.get()->wrapper() : nullptr; + + if (axObject != oldAxObject) { + if (oldAxObject && ATK_IS_TEXT(oldAxObject)) { + g_signal_emit_by_name(oldAxObject, "focus-event", FALSE); + atk_object_notify_state_change(ATK_OBJECT(oldAxObject), ATK_STATE_FOCUSED, FALSE); + } + if (axObject && ATK_IS_TEXT(axObject)) { + g_signal_emit_by_name(axObject, "focus-event", TRUE); + atk_object_notify_state_change(ATK_OBJECT(axObject), ATK_STATE_FOCUSED, TRUE); + } + } + + // Update pointer to last focused object. + oldObject.get() = WTFMove(object); +} + + +void FrameSelection::notifyAccessibilityForSelectionChange(const AXTextStateChangeIntent&) +{ + if (!AXObjectCache::accessibilityEnabled()) + return; + + if (!m_selection.start().isNotNull() || !m_selection.end().isNotNull()) + return; + + Node* focusedNode = m_selection.end().containerNode(); + if (!focusedNode) + return; + + AXObjectCache* cache = m_document->existingAXObjectCache(); + if (!cache) + return; + + AccessibilityObject* accessibilityObject = cache->getOrCreate(focusedNode->renderer()); + if (!accessibilityObject) + return; + + int offset; + RefPtr<AXCoreObject> object = objectFocusedAndCaretOffsetUnignored(accessibilityObject, offset); + if (!object) + return; + + emitTextSelectionChange(object.get(), m_selection, offset); + maybeEmitTextFocusChange(WTFMove(object)); +} + +} // namespace WebCore + +#endif // ENABLE(ACCESSIBILITY) && USE(ATK) diff -urpN webkitgtk-2.38.1.glib244/Source/WebCore/editing/FrameSelection.h webkitgtk-2.38.1/Source/WebCore/editing/FrameSelection.h --- webkitgtk-2.38.1.glib244/Source/WebCore/editing/FrameSelection.h 2022-08-31 02:59:55.922517300 -0500 +++ webkitgtk-2.38.1/Source/WebCore/editing/FrameSelection.h 2022-10-24 13:34:51.929454667 -0500 @@ -380,7 +380,7 @@ inline void FrameSelection::clearTypingS m_typingStyle = nullptr; } -#if !(ENABLE(ACCESSIBILITY) && (PLATFORM(COCOA) || USE(ATSPI))) +#if !(ENABLE(ACCESSIBILITY) && (PLATFORM(COCOA) || USE(ATK) || USE(ATSPI))) inline void FrameSelection::notifyAccessibilityForSelectionChange(const AXTextStateChangeIntent&) { diff -urpN webkitgtk-2.38.1.glib244/Source/WebCore/page/Settings.yaml webkitgtk-2.38.1/Source/WebCore/page/Settings.yaml --- webkitgtk-2.38.1.glib244/Source/WebCore/page/Settings.yaml 2022-08-31 02:59:56.110518500 -0500 +++ webkitgtk-2.38.1/Source/WebCore/page/Settings.yaml 2022-10-24 13:34:51.929454667 -0500 @@ -263,6 +263,7 @@ IsAccessibilityIsolatedTreeEnabled: type: bool defaultValue: WebCore: + USE(ATSPI): true default: false IsPerActivityStateCPUUsageMeasurementEnabled: diff -urpN webkitgtk-2.38.1.glib244/Source/WebCore/platform/graphics/PlatformDisplay.cpp webkitgtk-2.38.1/Source/WebCore/platform/graphics/PlatformDisplay.cpp --- webkitgtk-2.38.1.glib244/Source/WebCore/platform/graphics/PlatformDisplay.cpp 2022-09-08 06:51:52.585482400 -0500 +++ webkitgtk-2.38.1/Source/WebCore/platform/graphics/PlatformDisplay.cpp 2022-10-24 13:34:51.929454667 -0500 @@ -80,7 +80,7 @@ #include <wtf/NeverDestroyed.h> #endif -#if USE(ATSPI) +#if USE(ATSPI) || USE(ATK) #include <wtf/glib/GUniquePtr.h> #endif @@ -335,7 +335,7 @@ cmsHPROFILE PlatformDisplay::colorProfil } #endif -#if USE(ATSPI) +#if USE(ATSPI) || USE(ATK) const String& PlatformDisplay::accessibilityBusAddress() const { if (m_accessibilityBusAddress) diff -urpN webkitgtk-2.38.1.glib244/Source/WebCore/platform/graphics/PlatformDisplay.h webkitgtk-2.38.1/Source/WebCore/platform/graphics/PlatformDisplay.h --- webkitgtk-2.38.1.glib244/Source/WebCore/platform/graphics/PlatformDisplay.h 2022-10-19 03:11:34.522284300 -0500 +++ webkitgtk-2.38.1/Source/WebCore/platform/graphics/PlatformDisplay.h 2022-10-24 13:36:25.001945941 -0500 @@ -104,7 +104,7 @@ public: virtual cmsHPROFILE colorProfile() const; #endif -#if USE(ATSPI) +#if USE(ATSPI) || USE(ATK) const String& accessibilityBusAddress() const; #endif @@ -136,7 +136,7 @@ protected: mutable LCMSProfilePtr m_iccProfile; #endif -#if USE(ATSPI) +#if USE(ATSPI) || USE(ATK) virtual String plartformAccessibilityBusAddress() const { return { }; } mutable std::optional<String> m_accessibilityBusAddress; diff -urpN webkitgtk-2.38.1.glib244/Source/WebCore/platform/graphics/x11/PlatformDisplayX11.cpp webkitgtk-2.38.1/Source/WebCore/platform/graphics/x11/PlatformDisplayX11.cpp --- webkitgtk-2.38.1.glib244/Source/WebCore/platform/graphics/x11/PlatformDisplayX11.cpp 2022-08-31 02:59:56.298519600 -0500 +++ webkitgtk-2.38.1/Source/WebCore/platform/graphics/x11/PlatformDisplayX11.cpp 2022-10-24 13:34:51.929454667 -0500 @@ -263,7 +263,7 @@ cmsHPROFILE PlatformDisplayX11::colorPro } #endif -#if USE(ATSPI) +#if USE(ATSPI) || USE(ATK) String PlatformDisplayX11::plartformAccessibilityBusAddress() const { Atom atspiBusAtom = XInternAtom(m_display, "AT_SPI_BUS", False); diff -urpN webkitgtk-2.38.1.glib244/Source/WebCore/platform/graphics/x11/PlatformDisplayX11.h webkitgtk-2.38.1/Source/WebCore/platform/graphics/x11/PlatformDisplayX11.h --- webkitgtk-2.38.1.glib244/Source/WebCore/platform/graphics/x11/PlatformDisplayX11.h 2022-08-31 02:59:56.298519600 -0500 +++ webkitgtk-2.38.1/Source/WebCore/platform/graphics/x11/PlatformDisplayX11.h 2022-10-24 13:34:51.929454667 -0500 @@ -71,7 +71,7 @@ private: cmsHPROFILE colorProfile() const override; #endif -#if USE(ATSPI) +#if USE(ATSPI) || USE(ATK) String plartformAccessibilityBusAddress() const override; #endif diff -urpN webkitgtk-2.38.1.glib244/Source/WebCore/PlatformGTK.cmake webkitgtk-2.38.1/Source/WebCore/PlatformGTK.cmake --- webkitgtk-2.38.1.glib244/Source/WebCore/PlatformGTK.cmake 2022-08-31 02:59:55.650515800 -0500 +++ webkitgtk-2.38.1/Source/WebCore/PlatformGTK.cmake 2022-10-24 13:34:51.929454667 -0500 @@ -14,7 +14,9 @@ list(APPEND WebCore_UNIFIED_SOURCE_LIST_ ) list(APPEND WebCore_PRIVATE_INCLUDE_DIRECTORIES + "${WEBCORE_DIR}/accessibility/atk" "${WEBCORE_DIR}/accessibility/atspi" + "${WEBCORE_DIR}/editing/atk" "${WEBCORE_DIR}/page/gtk" "${WEBCORE_DIR}/platform/adwaita" "${WEBCORE_DIR}/platform/audio/glib" @@ -85,6 +87,7 @@ set(WebCore_USER_AGENT_SCRIPTS set(WebCore_USER_AGENT_SCRIPTS_DEPENDENCIES ${WEBCORE_DIR}/rendering/RenderThemeAdwaita.cpp) list(APPEND WebCore_LIBRARIES + ${ATK_LIBRARIES} ${ENCHANT_LIBRARIES} ${GLIB_GIO_LIBRARIES} ${GLIB_GMODULE_LIBRARIES} @@ -109,6 +112,7 @@ if (USE_WPE_RENDERER) endif () list(APPEND WebCore_SYSTEM_INCLUDE_DIRECTORIES + ${ATK_INCLUDE_DIRS} ${ENCHANT_INCLUDE_DIRS} ${GIO_UNIX_INCLUDE_DIRS} ${GLIB_INCLUDE_DIRS} diff -urpN webkitgtk-2.38.1.glib244/Source/WebCore/SourcesGTK.txt webkitgtk-2.38.1/Source/WebCore/SourcesGTK.txt --- webkitgtk-2.38.1.glib244/Source/WebCore/SourcesGTK.txt 2022-10-24 12:59:02.574106957 -0500 +++ webkitgtk-2.38.1/Source/WebCore/SourcesGTK.txt 2022-10-24 13:34:51.929454667 -0500 @@ -21,6 +21,24 @@ // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF // THE POSSIBILITY OF SUCH DAMAGE. +accessibility/atk/AXObjectCacheAtk.cpp +accessibility/atk/AccessibilityObjectAtk.cpp +accessibility/atk/WebKitAccessible.cpp +accessibility/atk/WebKitAccessibleHyperlink.cpp +accessibility/atk/WebKitAccessibleInterfaceAction.cpp @no-unify +accessibility/atk/WebKitAccessibleInterfaceComponent.cpp +accessibility/atk/WebKitAccessibleInterfaceDocument.cpp +accessibility/atk/WebKitAccessibleInterfaceEditableText.cpp +accessibility/atk/WebKitAccessibleInterfaceHyperlinkImpl.cpp +accessibility/atk/WebKitAccessibleInterfaceHypertext.cpp +accessibility/atk/WebKitAccessibleInterfaceImage.cpp +accessibility/atk/WebKitAccessibleInterfaceSelection.cpp +accessibility/atk/WebKitAccessibleInterfaceTable.cpp +accessibility/atk/WebKitAccessibleInterfaceTableCell.cpp +accessibility/atk/WebKitAccessibleInterfaceText.cpp +accessibility/atk/WebKitAccessibleInterfaceValue.cpp +accessibility/atk/WebKitAccessibleUtil.cpp + accessibility/atspi/AccessibilityAtspi.cpp accessibility/atspi/AccessibilityObjectAtspi.cpp accessibility/atspi/AccessibilityObjectActionAtspi.cpp @@ -38,6 +56,8 @@ accessibility/atspi/AccessibilityObjectV accessibility/atspi/AccessibilityRootAtspi.cpp accessibility/atspi/AXObjectCacheAtspi.cpp +editing/atk/FrameSelectionAtk.cpp + editing/atspi/FrameSelectionAtspi.cpp editing/gtk/EditorGtk.cpp diff -urpN webkitgtk-2.38.1.glib244/Source/WebKit/PlatformGTK.cmake webkitgtk-2.38.1/Source/WebKit/PlatformGTK.cmake --- webkitgtk-2.38.1.glib244/Source/WebKit/PlatformGTK.cmake 2022-10-24 13:33:57.001164714 -0500 +++ webkitgtk-2.38.1/Source/WebKit/PlatformGTK.cmake 2022-10-24 13:34:51.929454667 -0500 @@ -483,10 +483,12 @@ list(APPEND WebKit_INCLUDE_DIRECTORIES "${WEBKIT_DIR}/WebProcess/WebCoreSupport/gtk" "${WEBKIT_DIR}/WebProcess/WebCoreSupport/soup" "${WEBKIT_DIR}/WebProcess/WebPage/CoordinatedGraphics" + "${WEBKIT_DIR}/WebProcess/WebPage/atk" "${WEBKIT_DIR}/WebProcess/WebPage/gtk" ) list(APPEND WebKit_SYSTEM_INCLUDE_DIRECTORIES + ${ATK_INCLUDE_DIRS} ${ENCHANT_INCLUDE_DIRS} ${GIO_UNIX_INCLUDE_DIRS} ${GLIB_INCLUDE_DIRS} diff -urpN webkitgtk-2.38.1.glib244/Source/WebKit/SourcesGTK.txt webkitgtk-2.38.1/Source/WebKit/SourcesGTK.txt --- webkitgtk-2.38.1.glib244/Source/WebKit/SourcesGTK.txt 2022-10-24 12:59:02.574106957 -0500 +++ webkitgtk-2.38.1/Source/WebKit/SourcesGTK.txt 2022-10-24 13:34:51.929454667 -0500 @@ -457,6 +457,8 @@ WebProcess/WebPage/CoordinatedGraphics/C WebProcess/WebPage/CoordinatedGraphics/DrawingAreaCoordinatedGraphics.cpp WebProcess/WebPage/CoordinatedGraphics/LayerTreeHost.cpp +WebProcess/WebPage/atk/WebKitWebPageAccessibilityObject.cpp + WebProcess/WebPage/glib/WebPageGLib.cpp WebProcess/WebPage/gstreamer/WebPageGStreamer.cpp diff -urpN webkitgtk-2.38.1.glib244/Source/WebKit/UIProcess/API/glib/WebKitWebViewAccessible.cpp webkitgtk-2.38.1/Source/WebKit/UIProcess/API/glib/WebKitWebViewAccessible.cpp --- webkitgtk-2.38.1.glib244/Source/WebKit/UIProcess/API/glib/WebKitWebViewAccessible.cpp 2022-08-31 02:59:56.950523400 -0500 +++ webkitgtk-2.38.1/Source/WebKit/UIProcess/API/glib/WebKitWebViewAccessible.cpp 2022-10-24 13:34:51.929454667 -0500 @@ -20,7 +20,7 @@ #include "config.h" #include "WebKitWebViewAccessible.h" -#if ENABLE(ACCESSIBILITY) && !USE(GTK4) +#if ENABLE(ACCESSIBILITY) #include <wtf/glib/WTFGType.h> diff -urpN webkitgtk-2.38.1.glib244/Source/WebKit/UIProcess/API/glib/WebKitWebViewAccessible.h webkitgtk-2.38.1/Source/WebKit/UIProcess/API/glib/WebKitWebViewAccessible.h --- webkitgtk-2.38.1.glib244/Source/WebKit/UIProcess/API/glib/WebKitWebViewAccessible.h 2022-08-31 02:59:56.950523400 -0500 +++ webkitgtk-2.38.1/Source/WebKit/UIProcess/API/glib/WebKitWebViewAccessible.h 2022-10-24 13:34:51.929454667 -0500 @@ -19,7 +19,7 @@ #pragma once -#if ENABLE(ACCESSIBILITY) && !USE(GTK4) +#if ENABLE(ACCESSIBILITY) #include <atk/atk.h> diff -urpN webkitgtk-2.38.1.glib244/Source/WebKit/UIProcess/API/gtk/WebKitWebViewBase.cpp webkitgtk-2.38.1/Source/WebKit/UIProcess/API/gtk/WebKitWebViewBase.cpp --- webkitgtk-2.38.1.glib244/Source/WebKit/UIProcess/API/gtk/WebKitWebViewBase.cpp 2022-10-24 12:59:02.582106998 -0500 +++ webkitgtk-2.38.1/Source/WebKit/UIProcess/API/gtk/WebKitWebViewBase.cpp 2022-10-24 13:34:51.933454689 -0500 @@ -273,9 +273,7 @@ struct _WebKitWebViewBasePrivate { CString tooltipText; IntRect tooltipArea; WebHitTestResultData::IsScrollbar mouseIsOverScrollbar; -#if !USE(GTK4) GRefPtr<AtkObject> accessible; -#endif GtkWidget* dialog { nullptr }; GtkWidget* inspectorView { nullptr }; AttachmentSide inspectorAttachmentSide { AttachmentSide::Bottom }; diff -urpN webkitgtk-2.38.1.glib244/Source/WebKit/WebProcess/WebPage/atk/WebKitWebPageAccessibilityObject.cpp webkitgtk-2.38.1/Source/WebKit/WebProcess/WebPage/atk/WebKitWebPageAccessibilityObject.cpp --- webkitgtk-2.38.1.glib244/Source/WebKit/WebProcess/WebPage/atk/WebKitWebPageAccessibilityObject.cpp 1969-12-31 18:00:00.000000000 -0600 +++ webkitgtk-2.38.1/Source/WebKit/WebProcess/WebPage/atk/WebKitWebPageAccessibilityObject.cpp 2022-10-24 13:34:51.933454689 -0500 @@ -0,0 +1,170 @@ +/* + * Copyright (C) 2012, 2019 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. AND ITS CONTRIBUTORS ``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 ITS 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 "WebKitWebPageAccessibilityObject.h" + +#if ENABLE(ACCESSIBILITY) + +#include "WebPage.h" +#include <WebCore/AXObjectCache.h> +#include <WebCore/AccessibilityScrollView.h> +#include <WebCore/Document.h> +#include <WebCore/Frame.h> +#include <WebCore/Page.h> +#include <WebCore/ScrollView.h> +#include <wtf/glib/WTFGType.h> + +using namespace WebKit; +using namespace WebCore; + +struct _WebKitWebPageAccessibilityObjectPrivate { + WebPage* page; +}; + +WEBKIT_DEFINE_TYPE(WebKitWebPageAccessibilityObject, webkit_web_page_accessibility_object, ATK_TYPE_PLUG) + +static void coreRootObjectWrapperDetachedCallback(AtkObject* wrapper, const char*, gboolean value, AtkObject* atkObject) +{ + if (!value) + return; + + g_signal_emit_by_name(atkObject, "children-changed::remove", 0, wrapper); +} + +static AccessibilityObjectWrapper* rootWebAreaWrapper(AXCoreObject& rootObject) +{ + if (!rootObject.isScrollView()) + return nullptr; + + if (auto* webAreaObject = rootObject.webAreaObject()) + return webAreaObject->wrapper(); + + return nullptr; +} + +static AtkObject* accessibilityRootObjectWrapper(AtkObject* atkObject) +{ + if (!AXObjectCache::accessibilityEnabled()) + AXObjectCache::enableAccessibility(); + + auto* accessible = WEBKIT_WEB_PAGE_ACCESSIBILITY_OBJECT(atkObject); + if (!accessible->priv->page) + return nullptr; + + Page* corePage = accessible->priv->page->corePage(); + if (!corePage) + return nullptr; + + Frame& coreFrame = corePage->mainFrame(); + if (!coreFrame.document()) + return nullptr; + + AXObjectCache* cache = coreFrame.document()->axObjectCache(); + if (!cache) + return nullptr; + + AXCoreObject* coreRootObject = cache->rootObject(); + if (!coreRootObject) + return nullptr; + + auto* wrapper = ATK_OBJECT(coreRootObject->wrapper()); + if (!wrapper) + return nullptr; + + if (atk_object_peek_parent(wrapper) != ATK_OBJECT(accessible)) { + atk_object_set_parent(wrapper, ATK_OBJECT(accessible)); + g_signal_emit_by_name(accessible, "children-changed::add", 0, wrapper); + + if (auto* webAreaWrapper = rootWebAreaWrapper(*coreRootObject)) { + g_signal_connect_object(webAreaWrapper, "state-change::defunct", + G_CALLBACK(coreRootObjectWrapperDetachedCallback), accessible, static_cast<GConnectFlags>(0)); + } + } + + return wrapper; +} + +static void webkitWebPageAccessibilityObjectInitialize(AtkObject* atkObject, gpointer data) +{ + if (ATK_OBJECT_CLASS(webkit_web_page_accessibility_object_parent_class)->initialize) + ATK_OBJECT_CLASS(webkit_web_page_accessibility_object_parent_class)->initialize(atkObject, data); + + WEBKIT_WEB_PAGE_ACCESSIBILITY_OBJECT(atkObject)->priv->page = reinterpret_cast<WebPage*>(data); + atk_object_set_role(atkObject, ATK_ROLE_FILLER); +} + +static gint webkitWebPageAccessibilityObjectGetIndexInParent(AtkObject*) +{ + // An AtkPlug is the only child an AtkSocket can have. + return 0; +} + +static gint webkitWebPageAccessibilityObjectGetNChildren(AtkObject* atkObject) +{ + return accessibilityRootObjectWrapper(atkObject) ? 1 : 0; +} + +static AtkObject* webkitWebPageAccessibilityObjectRefChild(AtkObject* atkObject, gint index) +{ + // It's supposed to have either one child or zero. + if (index && index != 1) + return nullptr; + + if (auto* rootObjectWrapper = accessibilityRootObjectWrapper(atkObject)) + return ATK_OBJECT(g_object_ref(rootObjectWrapper)); + + return nullptr; +} + +static AtkStateSet* webkitWebPageAccessibilityObjectRefStateSet(AtkObject* atkObject) +{ + if (auto* rootObjectWrapper = accessibilityRootObjectWrapper(atkObject)) + return atk_object_ref_state_set(rootObjectWrapper); + + return atk_state_set_new(); +} + +static void webkit_web_page_accessibility_object_class_init(WebKitWebPageAccessibilityObjectClass* klass) +{ + AtkObjectClass* atkObjectClass = ATK_OBJECT_CLASS(klass); + // No need to implement get_parent() here since this is a subclass + // of AtkPlug and all the logic related to that function will be + // implemented by the ATK bridge. + atkObjectClass->initialize = webkitWebPageAccessibilityObjectInitialize; + atkObjectClass->get_index_in_parent = webkitWebPageAccessibilityObjectGetIndexInParent; + atkObjectClass->get_n_children = webkitWebPageAccessibilityObjectGetNChildren; + atkObjectClass->ref_child = webkitWebPageAccessibilityObjectRefChild; + atkObjectClass->ref_state_set = webkitWebPageAccessibilityObjectRefStateSet; +} + +AtkObject* webkitWebPageAccessibilityObjectNew(WebPage* page) +{ + AtkObject* object = ATK_OBJECT(g_object_new(WEBKIT_TYPE_WEB_PAGE_ACCESSIBILITY_OBJECT, nullptr)); + atk_object_initialize(object, page); + return object; +} + +#endif // ENABLE(ACCESSIBILITY) diff -urpN webkitgtk-2.38.1.glib244/Source/WebKit/WebProcess/WebPage/atk/WebKitWebPageAccessibilityObject.h webkitgtk-2.38.1/Source/WebKit/WebProcess/WebPage/atk/WebKitWebPageAccessibilityObject.h --- webkitgtk-2.38.1.glib244/Source/WebKit/WebProcess/WebPage/atk/WebKitWebPageAccessibilityObject.h 1969-12-31 18:00:00.000000000 -0600 +++ webkitgtk-2.38.1/Source/WebKit/WebProcess/WebPage/atk/WebKitWebPageAccessibilityObject.h 2022-10-24 13:34:51.933454689 -0500 @@ -0,0 +1,65 @@ +/* + * Copyright (C) 2012, 2019 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. AND ITS CONTRIBUTORS ``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 ITS 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. + */ + +#pragma once + +#if ENABLE(ACCESSIBILITY) + +#include <atk/atk.h> + +namespace WebKit { +class WebPage; +} + +G_BEGIN_DECLS + +#define WEBKIT_TYPE_WEB_PAGE_ACCESSIBILITY_OBJECT (webkit_web_page_accessibility_object_get_type()) +#define WEBKIT_WEB_PAGE_ACCESSIBILITY_OBJECT(object) (G_TYPE_CHECK_INSTANCE_CAST((object), WEBKIT_TYPE_WEB_PAGE_ACCESSIBILITY_OBJECT, WebKitWebPageAccessibilityObject)) +#define WEBKIT_WEB_PAGE_ACCESSIBILITY_OBJECT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), WEBKIT_TYPE_WEB_PAGE_ACCESSIBILITY_OBJECT, WebKitWebPageAccessibilityObjectClass)) +#define WEBKIT_IS_WEB_PAGE_ACCESSIBILITY_OBJECT(object) (G_TYPE_CHECK_INSTANCE_TYPE((object), WEBKIT_TYPE_WEB_PAGE_ACCESSIBILITY_OBJECT)) +#define WEBKIT_IS_WEB_PAGE_ACCESSIBILITY_OBJECT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), WEBKIT_TYPE_WEB_PAGE_ACCESSIBILITY_OBJECT)) +#define WEBKIT_WEB_PAGE_ACCESSIBILITY_OBJECT_GET_CLASS(object) (G_TYPE_INSTANCE_GET_CLASS((object), WEBKIT_TYPE_WEB_PAGE_ACCESSIBILITY_OBJECT, WebKitWebPageAccessibilityObjectClass)) + +typedef struct _WebKitWebPageAccessibilityObject WebKitWebPageAccessibilityObject; +typedef struct _WebKitWebPageAccessibilityObjectClass WebKitWebPageAccessibilityObjectClass; +typedef struct _WebKitWebPageAccessibilityObjectPrivate WebKitWebPageAccessibilityObjectPrivate; + +struct _WebKitWebPageAccessibilityObject { + AtkPlug parent; + + WebKitWebPageAccessibilityObjectPrivate* priv; +}; + +struct _WebKitWebPageAccessibilityObjectClass { + AtkPlugClass parentClass; +}; + +GType webkit_web_page_accessibility_object_get_type(); + +AtkObject* webkitWebPageAccessibilityObjectNew(WebKit::WebPage*); + +G_END_DECLS + +#endif // ENABLE(ACCESSIBILITY) diff -urpN webkitgtk-2.38.1.glib244/Source/WebKit/WebProcess/WebPage/glib/WebPageGLib.cpp webkitgtk-2.38.1/Source/WebKit/WebProcess/WebPage/glib/WebPageGLib.cpp --- webkitgtk-2.38.1.glib244/Source/WebKit/WebProcess/WebPage/glib/WebPageGLib.cpp 2022-08-31 02:59:57.158524800 -0500 +++ webkitgtk-2.38.1/Source/WebKit/WebProcess/WebPage/glib/WebPageGLib.cpp 2022-10-24 13:34:51.933454689 -0500 @@ -32,6 +32,7 @@ #include "WebKitExtensionManager.h" #include "WebKitUserMessage.h" #include "WebKitWebExtension.h" +#include "WebKitWebPageAccessibilityObject.h" #include "WebKitWebPagePrivate.h" #include "WebPageProxyMessages.h" #include <WebCore/Editor.h> @@ -50,11 +51,35 @@ using namespace WebCore; void WebPage::platformInitialize(const WebPageCreationParameters&) { -#if USE(ATSPI) +#if ENABLE(ACCESSIBILITY) // Create the accessible object (the plug) that will serve as the // entry point to the Web process, and send a message to the UI // process to connect the two worlds through the accessibility // object there specifically placed for that purpose (the socket). +#if USE(ATK) + auto isValidPlugID = [](const char* plugID) -> bool { + if (!plugID || plugID[0] != ':') + return false; + + auto* p = g_strrstr(plugID, ":"); + if (!p) + return false; + + if (!g_variant_is_object_path(p + 1)) + return false; + + GUniquePtr<char> name(g_strndup(plugID, p - plugID)); + if (!g_dbus_is_unique_name(name.get())) + return false; + + return true; + }; + + m_accessibilityObject = adoptGRef(webkitWebPageAccessibilityObjectNew(this)); + GUniquePtr<gchar> plugID(atk_plug_get_id(ATK_PLUG(m_accessibilityObject.get()))); + if (isValidPlugID(plugID.get())) + send(Messages::WebPageProxy::BindAccessibilityTree(String::fromLatin1(plugID.get()))); +#elif USE(ATSPI) #if PLATFORM(GTK) && USE(GTK4) // FIXME: we need a way to connect DOM and app a11y tree in GTK4. #else @@ -67,6 +92,7 @@ void WebPage::platformInitialize(const W } #endif #endif +#endif } void WebPage::platformDetach() diff -urpN webkitgtk-2.38.1.glib244/Source/WebKit/WebProcess/WebPage/WebPage.h webkitgtk-2.38.1/Source/WebKit/WebProcess/WebPage/WebPage.h --- webkitgtk-2.38.1.glib244/Source/WebKit/WebProcess/WebPage/WebPage.h 2022-10-24 12:58:27.513921881 -0500 +++ webkitgtk-2.38.1/Source/WebKit/WebProcess/WebPage/WebPage.h 2022-10-24 13:34:51.933454689 -0500 @@ -110,9 +110,14 @@ #include <wtf/WeakHashSet.h> #include <wtf/text/WTFString.h> -#if USE(ATSPI) +#if ENABLE(ACCESSIBILITY) +#if USE(ATK) +typedef struct _AtkObject AtkObject; +#include <wtf/glib/GRefPtr.h> +#elif USE(ATSPI) #include <WebCore/AccessibilityRootAtspi.h> #endif +#endif #if PLATFORM(GTK) #include "ArgumentCodersGtk.h" @@ -2130,9 +2135,13 @@ private: RetainPtr<NSDictionary> m_dataDetectionContext; #endif -#if USE(ATSPI) +#if ENABLE(ACCESSIBILITY) +#if USE(ATK) + GRefPtr<AtkObject> m_accessibilityObject; +#elif USE(ATSPI) RefPtr<WebCore::AccessibilityRootAtspi> m_accessibilityRootObject; #endif +#endif #if PLATFORM(WIN) uint64_t m_nativeWindowHandle { 0 };
Locations
Projects
Search
Status Monitor
Help
OpenBuildService.org
Documentation
API Documentation
Code of Conduct
Contact
Support
@OBShq
Terms
openSUSE Build Service is sponsored by
The Open Build Service is an
openSUSE project
.
Sign Up
Log In
Places
Places
All Projects
Status Monitor