summaryrefslogtreecommitdiffstats
path: root/Tools/WebKitTestRunner/InjectedBundle/atk/AccessibilityUIElementAtk.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'Tools/WebKitTestRunner/InjectedBundle/atk/AccessibilityUIElementAtk.cpp')
-rw-r--r--Tools/WebKitTestRunner/InjectedBundle/atk/AccessibilityUIElementAtk.cpp1397
1 files changed, 1155 insertions, 242 deletions
diff --git a/Tools/WebKitTestRunner/InjectedBundle/atk/AccessibilityUIElementAtk.cpp b/Tools/WebKitTestRunner/InjectedBundle/atk/AccessibilityUIElementAtk.cpp
index a72f48612..ded3a329d 100644
--- a/Tools/WebKitTestRunner/InjectedBundle/atk/AccessibilityUIElementAtk.cpp
+++ b/Tools/WebKitTestRunner/InjectedBundle/atk/AccessibilityUIElementAtk.cpp
@@ -1,6 +1,7 @@
/*
* Copyright (C) 2011 Apple Inc. All Rights Reserved.
* Copyright (C) 2012 Igalia S.L.
+ * Copyright (C) 2013 Samsung Electronics. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -33,62 +34,182 @@
#include "InjectedBundlePage.h"
#include "NotImplemented.h"
#include <JavaScriptCore/JSStringRef.h>
+#include <JavaScriptCore/OpaqueJSString.h>
+#if ATK_CHECK_VERSION(2,11,90)
+#include <WebKit/WKBundleFrame.h>
+#endif
#include <atk/atk.h>
#include <wtf/Assertions.h>
-#include <wtf/gobject/GOwnPtr.h>
-#include <wtf/gobject/GRefPtr.h>
+#include <wtf/glib/GRefPtr.h>
+#include <wtf/glib/GUniquePtr.h>
#include <wtf/text/CString.h>
+#include <wtf/text/StringBuilder.h>
#include <wtf/text/WTFString.h>
#include <wtf/unicode/CharacterNames.h>
namespace WTR {
-static String coreAttributeToAtkAttribute(JSStringRef attribute)
+namespace {
+
+#if ATK_CHECK_VERSION(2,11,92)
+enum RangeLimit {
+ RangeLimitMinimum,
+ RangeLimitMaximum
+};
+#endif
+
+enum AtkAttributeType {
+ ObjectAttributeType,
+ TextAttributeType
+};
+
+enum AttributeDomain {
+ CoreDomain = 0,
+ AtkDomain
+};
+
+enum AttributesIndex {
+ // Attribute names.
+ InvalidNameIndex = 0,
+ PosInSetIndex,
+ SetSizeIndex,
+ PlaceholderNameIndex,
+ SortNameIndex,
+
+ // Attribute values.
+ SortAscendingValueIndex,
+ SortDescendingValueIndex,
+ SortUnknownValueIndex,
+
+ NumberOfAttributes
+};
+
+// Attribute names & Values (keep on sync with enum AttributesIndex).
+const String attributesMap[][2] = {
+ // Attribute names.
+ { "AXInvalid", "invalid" },
+ { "AXARIAPosInSet", "posinset" },
+ { "AXARIASetSize", "setsize" },
+ { "AXPlaceholderValue", "placeholder-text" } ,
+ { "AXSortDirection", "sort" },
+
+ // Attribute values.
+ { "AXAscendingSortDirection", "ascending" },
+ { "AXDescendingSortDirection", "descending" },
+ { "AXUnknownSortDirection", "unknown" }
+};
+
+#if ATK_CHECK_VERSION(2, 11, 3)
+const char* landmarkStringBanner = "AXLandmarkBanner";
+const char* landmarkStringComplementary = "AXLandmarkComplementary";
+const char* landmarkStringContentinfo = "AXLandmarkContentInfo";
+const char* landmarkStringMain = "AXLandmarkMain";
+const char* landmarkStringNavigation = "AXLandmarkNavigation";
+const char* landmarkStringSearch = "AXLandmarkSearch";
+#endif
+
+String jsStringToWTFString(JSStringRef attribute)
{
size_t bufferSize = JSStringGetMaximumUTF8CStringSize(attribute);
- GOwnPtr<gchar> buffer(static_cast<gchar*>(g_malloc(bufferSize)));
+ GUniquePtr<gchar> buffer(static_cast<gchar*>(g_malloc(bufferSize)));
JSStringGetUTF8CString(attribute, buffer.get(), bufferSize);
- String attributeString = String::fromUTF8(buffer.get());
- return attributeString == "AXPlaceholderValue" ? "placeholder-text" : String();
+ return String::fromUTF8(buffer.get());
+}
+
+String coreAttributeToAtkAttribute(JSStringRef attribute)
+{
+ String attributeString = jsStringToWTFString(attribute);
+ for (int i = 0; i < NumberOfAttributes; ++i) {
+ if (attributesMap[i][CoreDomain] == attributeString)
+ return attributesMap[i][AtkDomain];
+ }
+
+ return attributeString;
}
-static String getAttributeSetValueForId(AtkObject* accessible, const char* id)
+String atkAttributeValueToCoreAttributeValue(AtkAttributeType type, const String& id, const String& value)
{
- const char* attributeValue = 0;
- AtkAttributeSet* attributeSet = atk_object_get_attributes(accessible);
+ if (type == ObjectAttributeType) {
+ // We need to translate ATK values exposed for 'aria-sort' (e.g. 'ascending')
+ // into those expected by the layout tests (e.g. 'AXAscendingSortDirection').
+ if (id == attributesMap[SortNameIndex][AtkDomain] && !value.isEmpty()) {
+ if (value == attributesMap[SortAscendingValueIndex][AtkDomain])
+ return attributesMap[SortAscendingValueIndex][CoreDomain];
+ if (value == attributesMap[SortDescendingValueIndex][AtkDomain])
+ return attributesMap[SortDescendingValueIndex][CoreDomain];
+
+ return attributesMap[SortUnknownValueIndex][CoreDomain];
+ }
+ } else if (type == TextAttributeType) {
+ // In case of 'aria-invalid' when the attribute empty or has "false" for ATK
+ // it should not be mapped at all, but layout tests will expect 'false'.
+ if (id == attributesMap[InvalidNameIndex][AtkDomain] && value.isEmpty())
+ return "false";
+ }
+
+ return value;
+}
+
+AtkAttributeSet* getAttributeSet(AtkObject* accessible, AtkAttributeType type)
+{
+ if (type == ObjectAttributeType)
+ return atk_object_get_attributes(accessible);
+
+ if (type == TextAttributeType) {
+ if (!ATK_IS_TEXT(accessible))
+ return nullptr;
+
+ return atk_text_get_default_attributes(ATK_TEXT(accessible));
+ }
+
+ ASSERT_NOT_REACHED();
+ return nullptr;
+}
+
+String getAttributeSetValueForId(AtkObject* accessible, AtkAttributeType type, String id)
+{
+ AtkAttributeSet* attributeSet = getAttributeSet(accessible, type);
+ if (!attributeSet)
+ return String();
+
+ String attributeValue;
for (AtkAttributeSet* attributes = attributeSet; attributes; attributes = attributes->next) {
AtkAttribute* atkAttribute = static_cast<AtkAttribute*>(attributes->data);
- if (!strcmp(atkAttribute->name, id)) {
- attributeValue = atkAttribute->value;
+ if (id == atkAttribute->name) {
+ attributeValue = String::fromUTF8(atkAttribute->value);
break;
}
}
-
- String atkAttributeValue = String::fromUTF8(attributeValue);
atk_attribute_set_free(attributeSet);
- return atkAttributeValue;
+ return atkAttributeValueToCoreAttributeValue(type, id, attributeValue);
}
-static char* getAtkAttributeSetAsString(AtkObject* accessible)
+String attributeSetToString(AtkAttributeSet* attributeSet, String separator=", ")
{
- GString* str = g_string_new(0);
+ if (!attributeSet)
+ return String();
- AtkAttributeSet* attributeSet = atk_object_get_attributes(accessible);
+ StringBuilder builder;
for (AtkAttributeSet* attributes = attributeSet; attributes; attributes = attributes->next) {
AtkAttribute* attribute = static_cast<AtkAttribute*>(attributes->data);
- GOwnPtr<gchar> attributeData(g_strconcat(attribute->name, ":", attribute->value, NULL));
- g_string_append(str, attributeData.get());
+ GUniquePtr<gchar> attributeData(g_strconcat(attribute->name, ":", attribute->value, NULL));
+ builder.append(attributeData.get());
if (attributes->next)
- g_string_append(str, ", ");
+ builder.append(separator);
}
atk_attribute_set_free(attributeSet);
- return g_string_free(str, FALSE);
+ return builder.toString();
}
-static bool checkElementState(PlatformUIElement element, AtkStateType stateType)
+String getAtkAttributeSetAsString(AtkObject* accessible, AtkAttributeType type, String separator=", ")
+{
+ return attributeSetToString(getAttributeSet(accessible, type), separator);
+}
+
+bool checkElementState(PlatformUIElement element, AtkStateType stateType)
{
if (!ATK_IS_OBJECT(element.get()))
return false;
@@ -97,11 +218,14 @@ static bool checkElementState(PlatformUIElement element, AtkStateType stateType)
return atk_state_set_contains_state(stateSet.get(), stateType);
}
-static JSStringRef indexRangeInTable(PlatformUIElement element, bool isRowRange)
+JSStringRef indexRangeInTable(PlatformUIElement element, bool isRowRange)
{
- GOwnPtr<gchar> rangeString(g_strdup("{0, 0}"));
-
- if (!element || !ATK_IS_OBJECT(element.get()))
+ GUniquePtr<gchar> rangeString(g_strdup("{0, 0}"));
+#if ATK_CHECK_VERSION(2,11,90)
+ if (!ATK_IS_TABLE_CELL(element.get()))
+ return JSStringCreateWithUTF8CString(rangeString.get());
+#else
+ if (!ATK_IS_OBJECT(element.get()))
return JSStringCreateWithUTF8CString(rangeString.get());
AtkObject* axTable = atk_object_get_parent(ATK_OBJECT(element.get()));
@@ -112,11 +236,20 @@ static JSStringRef indexRangeInTable(PlatformUIElement element, bool isRowRange)
gint indexInParent = atk_object_get_index_in_parent(ATK_OBJECT(element.get()));
if (indexInParent == -1)
return JSStringCreateWithUTF8CString(rangeString.get());
+#endif
- int row = -1;
- int column = -1;
+ gint row = -1;
+ gint column = -1;
+ gint rowSpan = -1;
+ gint columnSpan = -1;
+#if ATK_CHECK_VERSION(2,11,90)
+ atk_table_cell_get_row_column_span(ATK_TABLE_CELL(element.get()), &row, &column, &rowSpan, &columnSpan);
+#else
row = atk_table_get_row_at_index(ATK_TABLE(axTable), indexInParent);
column = atk_table_get_column_at_index(ATK_TABLE(axTable), indexInParent);
+ rowSpan = atk_table_get_row_extent_at(ATK_TABLE(axTable), row, column);
+ columnSpan = atk_table_get_column_extent_at(ATK_TABLE(axTable), row, column);
+#endif
// Get the actual values, if row and columns are valid values.
if (row != -1 && column != -1) {
@@ -124,22 +257,29 @@ static JSStringRef indexRangeInTable(PlatformUIElement element, bool isRowRange)
int length = 0;
if (isRowRange) {
base = row;
- length = atk_table_get_row_extent_at(ATK_TABLE(axTable), row, column);
+ length = rowSpan;
} else {
base = column;
- length = atk_table_get_column_extent_at(ATK_TABLE(axTable), row, column);
+ length = columnSpan;
}
- rangeString.set(g_strdup_printf("{%d, %d}", base, length));
+ rangeString.reset(g_strdup_printf("{%d, %d}", base, length));
}
return JSStringCreateWithUTF8CString(rangeString.get());
}
-static void alterCurrentValue(PlatformUIElement element, int factor)
+void alterCurrentValue(PlatformUIElement element, int factor)
{
- if (!element || !ATK_IS_VALUE(element.get()))
+ if (!ATK_IS_VALUE(element.get()))
return;
+#if ATK_CHECK_VERSION(2,11,92)
+ double currentValue;
+ atk_value_get_value_and_text(ATK_VALUE(element.get()), &currentValue, nullptr);
+
+ double increment = atk_value_get_increment(ATK_VALUE(element.get()));
+ atk_value_set_value(ATK_VALUE(element.get()), currentValue + factor * increment);
+#else
GValue currentValue = G_VALUE_INIT;
atk_value_get_current_value(ATK_VALUE(element.get()), &currentValue);
@@ -147,7 +287,7 @@ static void alterCurrentValue(PlatformUIElement element, int factor)
atk_value_get_minimum_increment(ATK_VALUE(element.get()), &increment);
GValue newValue = G_VALUE_INIT;
- g_value_init(&newValue, G_TYPE_DOUBLE);
+ g_value_init(&newValue, G_TYPE_FLOAT);
g_value_set_float(&newValue, g_value_get_float(&currentValue) + factor * g_value_get_float(&increment));
atk_value_set_current_value(ATK_VALUE(element.get()), &newValue);
@@ -155,9 +295,10 @@ static void alterCurrentValue(PlatformUIElement element, int factor)
g_value_unset(&newValue);
g_value_unset(&increment);
g_value_unset(&currentValue);
+#endif
}
-static gchar* replaceCharactersForResults(gchar* str)
+gchar* replaceCharactersForResults(gchar* str)
{
WTF::String uString = WTF::String::fromUTF8(str);
@@ -173,114 +314,366 @@ static gchar* replaceCharactersForResults(gchar* str)
return g_strdup(uString.utf8().data());
}
-static const gchar* roleToString(AtkRole role)
-{
+const gchar* roleToString(AtkObject* object)
+{
+ AtkRole role = atk_object_get_role(object);
+
+#if ATK_CHECK_VERSION(2, 11, 3)
+ if (role == ATK_ROLE_LANDMARK) {
+ String xmlRolesValue = getAttributeSetValueForId(object, ObjectAttributeType, "xml-roles");
+ if (equalLettersIgnoringASCIICase(xmlRolesValue, "banner"))
+ return landmarkStringBanner;
+ if (equalLettersIgnoringASCIICase(xmlRolesValue, "complementary"))
+ return landmarkStringComplementary;
+ if (equalLettersIgnoringASCIICase(xmlRolesValue, "contentinfo"))
+ return landmarkStringContentinfo;
+ if (equalLettersIgnoringASCIICase(xmlRolesValue, "main"))
+ return landmarkStringMain;
+ if (equalLettersIgnoringASCIICase(xmlRolesValue, "navigation"))
+ return landmarkStringNavigation;
+ if (equalLettersIgnoringASCIICase(xmlRolesValue, "search"))
+ return landmarkStringSearch;
+ }
+#endif
+
switch (role) {
case ATK_ROLE_ALERT:
- return "AXRole: AXAlert";
+ return "AXAlert";
+ case ATK_ROLE_DIALOG:
+ return "AXDialog";
case ATK_ROLE_CANVAS:
- return "AXRole: AXCanvas";
+ return "AXCanvas";
+ case ATK_ROLE_CAPTION:
+ return "AXCaption";
case ATK_ROLE_CHECK_BOX:
- return "AXRole: AXCheckBox";
+ return "AXCheckBox";
+ case ATK_ROLE_COLOR_CHOOSER:
+ return "AXColorWell";
case ATK_ROLE_COLUMN_HEADER:
- return "AXRole: AXColumnHeader";
+ return "AXColumnHeader";
case ATK_ROLE_COMBO_BOX:
- return "AXRole: AXComboBox";
+ return "AXComboBox";
+ case ATK_ROLE_COMMENT:
+ return "AXComment";
case ATK_ROLE_DOCUMENT_FRAME:
- return "AXRole: AXWebArea";
+ return "AXDocument";
+ case ATK_ROLE_DOCUMENT_WEB:
+ return "AXWebArea";
+ case ATK_ROLE_EMBEDDED:
+ return "AXEmbedded";
case ATK_ROLE_ENTRY:
- return "AXRole: AXTextField";
+ return "AXTextField";
case ATK_ROLE_FOOTER:
- return "AXRole: AXFooter";
+ return "AXFooter";
case ATK_ROLE_FORM:
- return "AXRole: AXForm";
+ return "AXForm";
case ATK_ROLE_GROUPING:
- return "AXRole: AXGroup";
+ return "AXGroup";
case ATK_ROLE_HEADING:
- return "AXRole: AXHeading";
+ return "AXHeading";
case ATK_ROLE_IMAGE:
- return "AXRole: AXImage";
+ return "AXImage";
case ATK_ROLE_IMAGE_MAP:
- return "AXRole: AXImageMap";
+ return "AXImageMap";
+ case ATK_ROLE_INVALID:
+ return "AXInvalid";
case ATK_ROLE_LABEL:
- return "AXRole: AXLabel";
+ return "AXLabel";
case ATK_ROLE_LINK:
- return "AXRole: AXLink";
+ return "AXLink";
case ATK_ROLE_LIST:
- return "AXRole: AXList";
+ return "AXList";
case ATK_ROLE_LIST_BOX:
- return "AXRole: AXListBox";
+ return "AXListBox";
case ATK_ROLE_LIST_ITEM:
- return "AXRole: AXListItem";
+ return "AXListItem";
case ATK_ROLE_MENU:
- return "AXRole: AXMenu";
+ return "AXMenu";
case ATK_ROLE_MENU_BAR:
- return "AXRole: AXMenuBar";
+ return "AXMenuBar";
case ATK_ROLE_MENU_ITEM:
- return "AXRole: AXMenuItem";
+ return "AXMenuItem";
case ATK_ROLE_PAGE_TAB:
- return "AXRole: AXTab";
+ return "AXTab";
case ATK_ROLE_PAGE_TAB_LIST:
- return "AXRole: AXTabGroup";
+ return "AXTabGroup";
case ATK_ROLE_PANEL:
- return "AXRole: AXGroup";
+ return "AXGroup";
case ATK_ROLE_PARAGRAPH:
- return "AXRole: AXParagraph";
+ return "AXParagraph";
case ATK_ROLE_PASSWORD_TEXT:
- return "AXRole: AXPasswordField";
+ return "AXPasswordField";
+ case ATK_ROLE_PROGRESS_BAR:
+ return "AXProgressIndicator";
case ATK_ROLE_PUSH_BUTTON:
- return "AXRole: AXButton";
+ return "AXButton";
case ATK_ROLE_RADIO_BUTTON:
- return "AXRole: AXRadioButton";
+ return "AXRadioButton";
+ case ATK_ROLE_RADIO_MENU_ITEM:
+ return "AXRadioMenuItem";
case ATK_ROLE_ROW_HEADER:
- return "AXRole: AXRowHeader";
+ return "AXRowHeader";
+ case ATK_ROLE_CHECK_MENU_ITEM:
+ return "AXCheckMenuItem";
case ATK_ROLE_RULER:
- return "AXRole: AXRuler";
+ return "AXRuler";
case ATK_ROLE_SCROLL_BAR:
- return "AXRole: AXScrollBar";
+ return "AXScrollBar";
case ATK_ROLE_SCROLL_PANE:
- return "AXRole: AXScrollArea";
+ return "AXScrollArea";
case ATK_ROLE_SECTION:
- return "AXRole: AXDiv";
+ return "AXSection";
case ATK_ROLE_SEPARATOR:
- return "AXRole: AXHorizontalRule";
+ return "AXSeparator";
case ATK_ROLE_SLIDER:
- return "AXRole: AXSlider";
+ return "AXSlider";
case ATK_ROLE_SPIN_BUTTON:
- return "AXRole: AXSpinButton";
+ return "AXSpinButton";
+ case ATK_ROLE_STATUSBAR:
+ return "AXStatusBar";
case ATK_ROLE_TABLE:
- return "AXRole: AXTable";
+ return "AXTable";
case ATK_ROLE_TABLE_CELL:
- return "AXRole: AXCell";
+ return "AXCell";
case ATK_ROLE_TABLE_COLUMN_HEADER:
- return "AXRole: AXColumnHeader";
+ return "AXColumnHeader";
case ATK_ROLE_TABLE_ROW:
- return "AXRole: AXRow";
+ return "AXRow";
case ATK_ROLE_TABLE_ROW_HEADER:
- return "AXRole: AXRowHeader";
+ return "AXRowHeader";
case ATK_ROLE_TOGGLE_BUTTON:
- return "AXRole: AXToggleButton";
+ return "AXToggleButton";
case ATK_ROLE_TOOL_BAR:
- return "AXRole: AXToolbar";
+ return "AXToolbar";
case ATK_ROLE_TOOL_TIP:
- return "AXRole: AXUserInterfaceTooltip";
+ return "AXUserInterfaceTooltip";
case ATK_ROLE_TREE:
- return "AXRole: AXTree";
+ return "AXTree";
case ATK_ROLE_TREE_TABLE:
- return "AXRole: AXTreeGrid";
+ return "AXTreeGrid";
case ATK_ROLE_TREE_ITEM:
- return "AXRole: AXTreeItem";
+ return "AXTreeItem";
case ATK_ROLE_WINDOW:
- return "AXRole: AXWindow";
+ return "AXWindow";
case ATK_ROLE_UNKNOWN:
- return "AXRole: AXUnknown";
+ return "AXUnknown";
+#if ATK_CHECK_VERSION(2, 11, 3)
+ case ATK_ROLE_ARTICLE:
+ return "AXArticle";
+ case ATK_ROLE_AUDIO:
+ return "AXAudio";
+ case ATK_ROLE_BLOCK_QUOTE:
+ return "AXBlockquote";
+ case ATK_ROLE_DEFINITION:
+ return "AXDefinition";
+ case ATK_ROLE_LOG:
+ return "AXLog";
+ case ATK_ROLE_MARQUEE:
+ return "AXMarquee";
+ case ATK_ROLE_MATH:
+ return "AXMath";
+ case ATK_ROLE_TIMER:
+ return "AXTimer";
+ case ATK_ROLE_VIDEO:
+ return "AXVideo";
+#endif
+#if ATK_CHECK_VERSION(2, 11, 4)
+ case ATK_ROLE_DESCRIPTION_LIST:
+ return "AXDescriptionList";
+ case ATK_ROLE_DESCRIPTION_TERM:
+ return "AXDescriptionTerm";
+ case ATK_ROLE_DESCRIPTION_VALUE:
+ return "AXDescriptionValue";
+#endif
+#if ATK_CHECK_VERSION(2, 15, 2)
+ case ATK_ROLE_STATIC:
+ return "AXStatic";
+#endif
+#if ATK_CHECK_VERSION(2, 15, 4)
+ case ATK_ROLE_MATH_FRACTION:
+ return "AXMathFraction";
+ case ATK_ROLE_MATH_ROOT:
+ return "AXMathRoot";
+ case ATK_ROLE_SUBSCRIPT:
+ return "AXSubscript";
+ case ATK_ROLE_SUPERSCRIPT:
+ return "AXSuperscript";
+#endif
default:
// We want to distinguish ATK_ROLE_UNKNOWN from a known AtkRole which
// our DRT isn't properly handling.
- return "AXRole: FIXME not identified";
+ return "FIXME not identified";
+ }
+}
+
+String selectedText(AtkObject* accessible)
+{
+ if (!ATK_IS_TEXT(accessible))
+ return String();
+
+ AtkText* text = ATK_TEXT(accessible);
+
+ gint start, end;
+ g_free(atk_text_get_selection(text, 0, &start, &end));
+
+ return atk_text_get_text(text, start, end);
+}
+
+String attributesOfElement(AccessibilityUIElement* element)
+{
+ StringBuilder builder;
+
+ builder.append(String::format("%s\n", element->role()->string().utf8().data()));
+
+ // For the parent we print its role and its name, if available.
+ builder.append("AXParent: ");
+ RefPtr<AccessibilityUIElement> parent = element->parentElement();
+ AtkObject* atkParent = parent ? parent->platformUIElement().get() : nullptr;
+ if (atkParent) {
+ builder.append(roleToString(atkParent));
+ const char* parentName = atk_object_get_name(atkParent);
+ if (parentName && g_utf8_strlen(parentName, -1))
+ builder.append(String::format(": %s", parentName));
+ } else
+ builder.append("(null)");
+ builder.append("\n");
+
+ builder.append(String::format("AXChildren: %d\n", element->childrenCount()));
+ builder.append(String::format("AXPosition: { %f, %f }\n", element->x(), element->y()));
+ builder.append(String::format("AXSize: { %f, %f }\n", element->width(), element->height()));
+
+ String title = element->title()->string();
+ if (!title.isEmpty())
+ builder.append(String::format("%s\n", title.utf8().data()));
+
+ String description = element->description()->string();
+ if (!description.isEmpty())
+ builder.append(String::format("%s\n", description.utf8().data()));
+
+ String value = element->stringValue()->string();
+ if (!value.isEmpty())
+ builder.append(String::format("%s\n", value.utf8().data()));
+
+ builder.append(String::format("AXFocusable: %d\n", element->isFocusable()));
+ builder.append(String::format("AXFocused: %d\n", element->isFocused()));
+ builder.append(String::format("AXSelectable: %d\n", element->isSelectable()));
+ builder.append(String::format("AXSelected: %d\n", element->isSelected()));
+ builder.append(String::format("AXMultiSelectable: %d\n", element->isMultiSelectable()));
+ builder.append(String::format("AXEnabled: %d\n", element->isEnabled()));
+ builder.append(String::format("AXExpanded: %d\n", element->isExpanded()));
+ builder.append(String::format("AXRequired: %d\n", element->isRequired()));
+ builder.append(String::format("AXChecked: %d\n", element->isChecked()));
+
+ String url = element->url()->string();
+ if (!url.isEmpty())
+ builder.append(String::format("%s\n", url.utf8().data()));
+
+ // We append the ATK specific attributes as a single line at the end.
+ builder.append("AXPlatformAttributes: ");
+ builder.append(getAtkAttributeSetAsString(element->platformUIElement().get(), ObjectAttributeType));
+
+ return builder.toString();
+}
+
+static JSRetainPtr<JSStringRef> createStringWithAttributes(const Vector<RefPtr<AccessibilityUIElement> >& elements)
+{
+ StringBuilder builder;
+
+ for (Vector<RefPtr<AccessibilityUIElement> >::const_iterator it = elements.begin(); it != elements.end(); ++it) {
+ builder.append(attributesOfElement(const_cast<AccessibilityUIElement*>(it->get())));
+ builder.append("\n------------\n");
}
+
+ return JSStringCreateWithUTF8CString(builder.toString().utf8().data());
+}
+
+static Vector<RefPtr<AccessibilityUIElement> > getRowHeaders(AtkTable* accessible)
+{
+ Vector<RefPtr<AccessibilityUIElement> > rowHeaders;
+
+ int rowsCount = atk_table_get_n_rows(accessible);
+ for (int row = 0; row < rowsCount; ++row)
+ rowHeaders.append(AccessibilityUIElement::create(atk_table_get_row_header(accessible, row)));
+
+ return rowHeaders;
+}
+
+static Vector<RefPtr<AccessibilityUIElement> > getColumnHeaders(AtkTable* accessible)
+{
+ Vector<RefPtr<AccessibilityUIElement> > columnHeaders;
+
+ int columnsCount = atk_table_get_n_columns(accessible);
+ for (int column = 0; column < columnsCount; ++column)
+ columnHeaders.append(AccessibilityUIElement::create(atk_table_get_column_header(accessible, column)));
+
+ return columnHeaders;
}
+static Vector<RefPtr<AccessibilityUIElement> > getVisibleCells(AccessibilityUIElement* element)
+{
+ Vector<RefPtr<AccessibilityUIElement> > visibleCells;
+
+ AtkTable* accessible = ATK_TABLE(element->platformUIElement().get());
+ int rowsCount = atk_table_get_n_rows(accessible);
+ int columnsCount = atk_table_get_n_columns(accessible);
+
+ for (int row = 0; row < rowsCount; ++row) {
+ for (int column = 0; column < columnsCount; ++column)
+ visibleCells.append(element->cellForColumnAndRow(column, row));
+ }
+
+ return visibleCells;
+}
+
+#if ATK_CHECK_VERSION(2,11,90)
+static Vector<RefPtr<AccessibilityUIElement>> convertGPtrArrayToVector(const GPtrArray* array)
+{
+ Vector<RefPtr<AccessibilityUIElement>> cells;
+ for (guint i = 0; i < array->len; i++) {
+ if (AtkObject* atkObject = static_cast<AtkObject*>(g_ptr_array_index(array, i)))
+ cells.append(AccessibilityUIElement::create(atkObject));
+ }
+ return cells;
+}
+
+static JSValueRef convertToJSObjectArray(const Vector<RefPtr<AccessibilityUIElement>>& children)
+{
+ WKBundleFrameRef mainFrame = WKBundlePageGetMainFrame(InjectedBundle::singleton().page()->page());
+ JSContextRef context = WKBundleFrameGetJavaScriptContext(mainFrame);
+
+ size_t elementCount = children.size();
+ auto valueElements = std::make_unique<JSValueRef[]>(elementCount);
+ for (size_t i = 0; i < elementCount; i++)
+ valueElements[i] = JSObjectMake(context, children[i]->wrapperClass(), children[i].get());
+
+ return JSObjectMakeArray(context, elementCount, valueElements.get(), nullptr);
+}
+#endif
+
+#if ATK_CHECK_VERSION(2,11,92)
+static double rangeMinMaxValue(AtkValue* atkValue, RangeLimit rangeLimit)
+{
+ AtkRange* range = atk_value_get_range(atkValue);
+ if (!range)
+ return 0;
+
+ double rangeValue = 0;
+ switch (rangeLimit) {
+ case RangeLimitMinimum:
+ rangeValue = atk_range_get_lower_limit(range);
+ break;
+ case RangeLimitMaximum:
+ rangeValue = atk_range_get_upper_limit(range);
+ break;
+ };
+
+ atk_range_free(range);
+ return rangeValue;
+}
+#endif
+
+} // namespace
+
AccessibilityUIElement::AccessibilityUIElement(PlatformUIElement element)
: m_element(element)
{
@@ -303,7 +696,7 @@ bool AccessibilityUIElement::isEqual(AccessibilityUIElement* otherElement)
void AccessibilityUIElement::getChildren(Vector<RefPtr<AccessibilityUIElement> >& children)
{
- if (!m_element || !ATK_IS_OBJECT(m_element.get()))
+ if (!ATK_IS_OBJECT(m_element.get()))
return;
int count = childrenCount();
@@ -315,7 +708,7 @@ void AccessibilityUIElement::getChildren(Vector<RefPtr<AccessibilityUIElement> >
void AccessibilityUIElement::getChildrenWithRange(Vector<RefPtr<AccessibilityUIElement> >& children, unsigned location, unsigned length)
{
- if (!m_element || !ATK_IS_OBJECT(m_element.get()))
+ if (!ATK_IS_OBJECT(m_element.get()))
return;
unsigned end = location + length;
for (unsigned i = location; i < end; i++) {
@@ -326,7 +719,7 @@ void AccessibilityUIElement::getChildrenWithRange(Vector<RefPtr<AccessibilityUIE
int AccessibilityUIElement::childrenCount()
{
- if (!m_element)
+ if (!ATK_IS_OBJECT(m_element.get()))
return 0;
return atk_object_get_n_accessible_children(ATK_OBJECT(m_element.get()));
@@ -334,16 +727,16 @@ int AccessibilityUIElement::childrenCount()
PassRefPtr<AccessibilityUIElement> AccessibilityUIElement::elementAtPoint(int x, int y)
{
- if (!m_element || !ATK_IS_OBJECT(m_element.get()))
- return 0;
+ if (!ATK_IS_COMPONENT(m_element.get()))
+ return nullptr;
GRefPtr<AtkObject> objectAtPoint = adoptGRef(atk_component_ref_accessible_at_point(ATK_COMPONENT(m_element.get()), x, y, ATK_XY_WINDOW));
- return objectAtPoint ? AccessibilityUIElement::create(objectAtPoint.get()) : 0;
+ return AccessibilityUIElement::create(objectAtPoint ? objectAtPoint.get() : m_element.get());
}
unsigned AccessibilityUIElement::indexOfChild(AccessibilityUIElement* element)
{
- if (!m_element || !ATK_IS_OBJECT(m_element.get()))
+ if (!ATK_IS_OBJECT(m_element.get()))
return 0;
Vector<RefPtr<AccessibilityUIElement> > children;
@@ -359,73 +752,118 @@ unsigned AccessibilityUIElement::indexOfChild(AccessibilityUIElement* element)
PassRefPtr<AccessibilityUIElement> AccessibilityUIElement::childAtIndex(unsigned index)
{
+ if (!ATK_IS_OBJECT(m_element.get()))
+ return nullptr;
+
Vector<RefPtr<AccessibilityUIElement> > children;
getChildrenWithRange(children, index, 1);
if (children.size() == 1)
return children[0];
- return 0;
+ return nullptr;
+}
+
+static PassRefPtr<AccessibilityUIElement> accessibilityElementAtIndex(AtkObject* element, AtkRelationType relationType, unsigned index)
+{
+ if (!ATK_IS_OBJECT(element))
+ return nullptr;
+
+ AtkRelationSet* relationSet = atk_object_ref_relation_set(element);
+ if (!relationSet)
+ return nullptr;
+
+ AtkRelation* relation = atk_relation_set_get_relation_by_type(relationSet, relationType);
+ if (!relation)
+ return nullptr;
+
+ GPtrArray* targetList = atk_relation_get_target(relation);
+ if (!targetList || !targetList->len || index >= targetList->len)
+ return nullptr;
+
+ AtkObject* target = static_cast<AtkObject*>(g_ptr_array_index(targetList, index));
+ g_object_unref(relationSet);
+
+ return target ? AccessibilityUIElement::create(target) : nullptr;
}
PassRefPtr<AccessibilityUIElement> AccessibilityUIElement::linkedUIElementAtIndex(unsigned index)
{
// FIXME: implement
- return 0;
+ return nullptr;
}
PassRefPtr<AccessibilityUIElement> AccessibilityUIElement::ariaOwnsElementAtIndex(unsigned index)
{
// FIXME: implement
- return 0;
+ return nullptr;
}
PassRefPtr<AccessibilityUIElement> AccessibilityUIElement::ariaFlowToElementAtIndex(unsigned index)
{
- // FIXME: implement
- return 0;
+ return accessibilityElementAtIndex(m_element.get(), ATK_RELATION_FLOWS_TO, index);
+}
+
+PassRefPtr<AccessibilityUIElement> AccessibilityUIElement::ariaControlsElementAtIndex(unsigned index)
+{
+ return accessibilityElementAtIndex(m_element.get(), ATK_RELATION_CONTROLLER_FOR, index);
}
PassRefPtr<AccessibilityUIElement> AccessibilityUIElement::disclosedRowAtIndex(unsigned index)
{
// FIXME: implement
- return 0;
+ return nullptr;
}
PassRefPtr<AccessibilityUIElement> AccessibilityUIElement::rowAtIndex(unsigned index)
{
- // FIXME: implement
- return 0;
+ // ATK doesn't have API to get an accessible row by index directly. It does, however, have
+ // API to get cells in the row specified by index. The parent of a cell should be the row.
+ AtkTable* axTable = ATK_TABLE(m_element.get());
+ unsigned nColumns = columnCount();
+ for (unsigned col = 0; col < nColumns; col++) {
+ // Find the first cell in this row that only spans one row.
+ if (atk_table_get_row_extent_at(axTable, index, col) == 1) {
+ AtkObject* cell = atk_table_ref_at(axTable, index, col);
+ return cell ? AccessibilityUIElement::create(atk_object_get_parent(cell)) : nullptr;
+ }
+ }
+
+ return nullptr;
}
PassRefPtr<AccessibilityUIElement> AccessibilityUIElement::selectedChildAtIndex(unsigned index) const
{
- // FIXME: implement
- return 0;
+ if (!ATK_SELECTION(m_element.get()))
+ return nullptr;
+
+ GRefPtr<AtkObject> child = adoptGRef(atk_selection_ref_selection(ATK_SELECTION(m_element.get()), index));
+ return child ? AccessibilityUIElement::create(child.get()) : nullptr;
}
unsigned AccessibilityUIElement::selectedChildrenCount() const
{
- // FIXME: implement
- return 0;
+ if (!ATK_IS_SELECTION(m_element.get()))
+ return 0;
+ return atk_selection_get_selection_count(ATK_SELECTION(m_element.get()));
}
PassRefPtr<AccessibilityUIElement> AccessibilityUIElement::selectedRowAtIndex(unsigned index)
{
// FIXME: implement
- return 0;
+ return nullptr;
}
PassRefPtr<AccessibilityUIElement> AccessibilityUIElement::titleUIElement()
{
- if (!m_element)
- return 0;
+ if (!ATK_IS_OBJECT(m_element.get()))
+ return nullptr;
AtkRelationSet* set = atk_object_ref_relation_set(ATK_OBJECT(m_element.get()));
if (!set)
- return 0;
+ return nullptr;
- AtkObject* target = 0;
+ AtkObject* target = nullptr;
int count = atk_relation_set_get_n_relations(set);
for (int i = 0; i < count; i++) {
AtkRelation* relation = atk_relation_set_get_relation(set, i);
@@ -437,22 +875,22 @@ PassRefPtr<AccessibilityUIElement> AccessibilityUIElement::titleUIElement()
}
g_object_unref(set);
- return target ? AccessibilityUIElement::create(target) : 0;
+ return target ? AccessibilityUIElement::create(target) : nullptr;
}
PassRefPtr<AccessibilityUIElement> AccessibilityUIElement::parentElement()
{
- if (!m_element || !ATK_IS_OBJECT(m_element.get()))
- return 0;
+ if (!ATK_IS_OBJECT(m_element.get()))
+ return nullptr;
AtkObject* parent = atk_object_get_parent(ATK_OBJECT(m_element.get()));
- return parent ? AccessibilityUIElement::create(parent) : 0;
+ return parent ? AccessibilityUIElement::create(parent) : nullptr;
}
PassRefPtr<AccessibilityUIElement> AccessibilityUIElement::disclosedByRow()
{
// FIXME: implement
- return 0;
+ return nullptr;
}
JSRetainPtr<JSStringRef> AccessibilityUIElement::attributesOfLinkedUIElements()
@@ -469,42 +907,126 @@ JSRetainPtr<JSStringRef> AccessibilityUIElement::attributesOfDocumentLinks()
JSRetainPtr<JSStringRef> AccessibilityUIElement::attributesOfChildren()
{
- // FIXME: implement
- return JSStringCreateWithCharacters(0, 0);
+ if (!ATK_IS_OBJECT(m_element.get()))
+ return JSStringCreateWithCharacters(0, 0);
+
+ Vector<RefPtr<AccessibilityUIElement> > children;
+ getChildren(children);
+
+ return createStringWithAttributes(children);
}
JSRetainPtr<JSStringRef> AccessibilityUIElement::allAttributes()
{
- if (!m_element || !ATK_IS_OBJECT(m_element.get()))
+ if (!ATK_IS_OBJECT(m_element.get()))
return JSStringCreateWithCharacters(0, 0);
- GOwnPtr<char> attributeData(getAtkAttributeSetAsString(ATK_OBJECT(m_element.get())));
- return JSStringCreateWithUTF8CString(attributeData.get());
+ return JSStringCreateWithUTF8CString(attributesOfElement(this).utf8().data());
}
JSRetainPtr<JSStringRef> AccessibilityUIElement::stringAttributeValue(JSStringRef attribute)
{
- if (!m_element)
+ if (!ATK_IS_OBJECT(m_element.get()))
return JSStringCreateWithCharacters(0, 0);
String atkAttributeName = coreAttributeToAtkAttribute(attribute);
- if (atkAttributeName.isNull())
- return JSStringCreateWithCharacters(0, 0);
- String attributeValue = getAttributeSetValueForId(ATK_OBJECT(m_element.get()), atkAttributeName.utf8().data());
+ // The value of AXSelectedText is not exposed through any AtkAttribute.
+ if (atkAttributeName == "AXSelectedText") {
+ String string = selectedText(m_element.get());
+ return JSStringCreateWithUTF8CString(string.utf8().data());
+ }
+
+ // Try object attributes before text attributes.
+ String attributeValue = getAttributeSetValueForId(ATK_OBJECT(m_element.get()), ObjectAttributeType, atkAttributeName);
+
+ // Try text attributes if the requested one was not found and we have an AtkText object.
+ if (attributeValue.isEmpty() && ATK_IS_TEXT(m_element.get()))
+ attributeValue = getAttributeSetValueForId(ATK_OBJECT(m_element.get()), TextAttributeType, atkAttributeName);
+
+ // Additional check to make sure that the exposure of the state ATK_STATE_INVALID_ENTRY
+ // is consistent with the exposure of aria-invalid as a text attribute, if present.
+ if (atkAttributeName == attributesMap[InvalidNameIndex][AtkDomain]) {
+ bool isInvalidState = checkElementState(m_element.get(), ATK_STATE_INVALID_ENTRY);
+ if (attributeValue.isEmpty())
+ return JSStringCreateWithUTF8CString(isInvalidState ? "true" : "false");
+
+ // If the text attribute was there, check that it's consistent with
+ // what the state says or force the test to fail otherwise.
+ bool isAriaInvalid = attributeValue != "false";
+ if (isInvalidState != isAriaInvalid)
+ return JSStringCreateWithCharacters(0, 0);
+ }
+
return JSStringCreateWithUTF8CString(attributeValue.utf8().data());
}
double AccessibilityUIElement::numberAttributeValue(JSStringRef attribute)
{
+ if (!ATK_IS_OBJECT(m_element.get()))
+ return 0;
+
+ String atkAttributeName = coreAttributeToAtkAttribute(attribute);
+ if (atkAttributeName.isEmpty())
+ return 0;
+
+ if (atkAttributeName == "setsize" || atkAttributeName == "posinset") {
+ String attributeValue = getAttributeSetValueForId(ATK_OBJECT(m_element.get()), ObjectAttributeType, atkAttributeName);
+ if (!attributeValue.isEmpty())
+ return attributeValue.toDouble();
+ }
+
+ return 0;
+}
+
+JSValueRef AccessibilityUIElement::uiElementArrayAttributeValue(JSStringRef attribute) const
+{
// FIXME: implement
- return 0.0f;
+ return nullptr;
+}
+
+JSValueRef AccessibilityUIElement::rowHeaders() const
+{
+#if ATK_CHECK_VERSION(2,11,90)
+ if (!ATK_IS_TABLE_CELL(m_element.get()))
+ return nullptr;
+
+ GRefPtr<GPtrArray> array = adoptGRef(atk_table_cell_get_row_header_cells(ATK_TABLE_CELL(m_element.get())));
+ if (!array)
+ return nullptr;
+
+ Vector<RefPtr<AccessibilityUIElement>> rows = convertGPtrArrayToVector(array.get());
+ return convertToJSObjectArray(rows);
+#else
+ return nullptr;
+#endif
+}
+
+JSValueRef AccessibilityUIElement::columnHeaders() const
+{
+#if ATK_CHECK_VERSION(2,11,90)
+ if (!ATK_IS_TABLE_CELL(m_element.get()) && !ATK_IS_TABLE(m_element.get()))
+ return nullptr;
+
+ Vector<RefPtr<AccessibilityUIElement>> columns;
+ if (ATK_IS_TABLE_CELL(m_element.get())) {
+ GRefPtr<GPtrArray> array = adoptGRef(atk_table_cell_get_column_header_cells(ATK_TABLE_CELL(m_element.get())));
+ if (!array)
+ return nullptr;
+
+ columns = convertGPtrArrayToVector(array.get());
+ } else
+ columns = getColumnHeaders(ATK_TABLE(m_element.get()));
+ return convertToJSObjectArray(columns);
+#else
+ return nullptr;
+#endif
}
PassRefPtr<AccessibilityUIElement> AccessibilityUIElement::uiElementAttributeValue(JSStringRef attribute) const
{
// FIXME: implement
- return 0;
+ return nullptr;
}
bool AccessibilityUIElement::boolAttributeValue(JSStringRef attribute)
@@ -515,14 +1037,74 @@ bool AccessibilityUIElement::boolAttributeValue(JSStringRef attribute)
bool AccessibilityUIElement::isAttributeSettable(JSStringRef attribute)
{
- // FIXME: implement
+ if (!ATK_IS_OBJECT(m_element.get()))
+ return false;
+
+ String attributeString = jsStringToWTFString(attribute);
+ if (attributeString != "AXValue")
+ return false;
+
+ // ATK does not have a single state or property to indicate whether or not the value
+ // of an accessible object can be set. ATs look at several states and properties based
+ // on the type of object. If nothing explicitly indicates the value can or cannot be
+ // set, ATs make role- and interface-based decisions. We'll do something similar here.
+
+ // This state is expected to be present only for text widgets and contenteditable elements.
+ if (checkElementState(m_element.get(), ATK_STATE_EDITABLE))
+ return true;
+
+#if ATK_CHECK_VERSION(2,11,2)
+ // This state is applicable to checkboxes, radiobuttons, switches, etc.
+ if (checkElementState(m_element.get(), ATK_STATE_CHECKABLE))
+ return true;
+#endif
+
+#if ATK_CHECK_VERSION(2,15,3)
+ // This state is expected to be present only for controls and only if explicitly set.
+ if (checkElementState(m_element.get(), ATK_STATE_READ_ONLY))
+ return false;
+#endif
+
+ // We expose an object attribute to ATs when there is an author-provided ARIA property
+ // and also when there is a supported ARIA role but no author-provided value.
+ String isReadOnly = getAttributeSetValueForId(ATK_OBJECT(m_element.get()), ObjectAttributeType, "readonly");
+ if (!isReadOnly.isEmpty())
+ return isReadOnly == "true" ? false : true;
+
+ // If we have a native listbox or combobox and the value can be set, the options should
+ // have ATK_STATE_SELECTABLE.
+ AtkRole role = atk_object_get_role(ATK_OBJECT(m_element.get()));
+ if (role == ATK_ROLE_LIST_BOX || role == ATK_ROLE_COMBO_BOX) {
+ if (GRefPtr<AtkObject> child = adoptGRef(atk_object_ref_accessible_child(ATK_OBJECT(m_element.get()), 0))) {
+ if (atk_object_get_role(ATK_OBJECT(child.get())) == ATK_ROLE_MENU)
+ child = adoptGRef(atk_object_ref_accessible_child(ATK_OBJECT(child.get()), 0));
+ return child && checkElementState(child.get(), ATK_STATE_SELECTABLE);
+ }
+ }
+
+ // If we have a native element which exposes a range whose value can be set, it should
+ // be focusable and have a true range.
+ if (ATK_IS_VALUE(m_element.get()) && checkElementState(m_element.get(), ATK_STATE_FOCUSABLE))
+ return minValue() != maxValue();
+
return false;
}
bool AccessibilityUIElement::isAttributeSupported(JSStringRef attribute)
{
- // FIXME: implement
- return false;
+ if (!ATK_IS_OBJECT(m_element.get()))
+ return false;
+
+ String atkAttributeName = coreAttributeToAtkAttribute(attribute);
+ if (atkAttributeName.isEmpty())
+ return false;
+
+ // For now, an attribute is supported whether it's exposed as a object or a text attribute.
+ String attributeValue = getAttributeSetValueForId(ATK_OBJECT(m_element.get()), ObjectAttributeType, atkAttributeName);
+ if (attributeValue.isEmpty())
+ attributeValue = getAttributeSetValueForId(ATK_OBJECT(m_element.get()), TextAttributeType, atkAttributeName);
+
+ return !attributeValue.isEmpty();
}
JSRetainPtr<JSStringRef> AccessibilityUIElement::parameterizedAttributeNames()
@@ -533,15 +1115,11 @@ JSRetainPtr<JSStringRef> AccessibilityUIElement::parameterizedAttributeNames()
JSRetainPtr<JSStringRef> AccessibilityUIElement::role()
{
- if (!m_element || !ATK_IS_OBJECT(m_element.get()))
- return JSStringCreateWithCharacters(0, 0);
-
- AtkRole role = atk_object_get_role(ATK_OBJECT(m_element.get()));
- if (!role)
+ if (!ATK_IS_OBJECT(m_element.get()))
return JSStringCreateWithCharacters(0, 0);
- GOwnPtr<gchar> axRole(g_strdup(roleToString(role)));
- return JSStringCreateWithUTF8CString(axRole.get());
+ GUniquePtr<char> roleStringWithPrefix(g_strdup_printf("AXRole: %s", roleToString(ATK_OBJECT(m_element.get()))));
+ return JSStringCreateWithUTF8CString(roleStringWithPrefix.get());
}
JSRetainPtr<JSStringRef> AccessibilityUIElement::subrole()
@@ -556,37 +1134,46 @@ JSRetainPtr<JSStringRef> AccessibilityUIElement::roleDescription()
return JSStringCreateWithCharacters(0, 0);
}
+JSRetainPtr<JSStringRef> AccessibilityUIElement::computedRoleString()
+{
+ String role = getAttributeSetValueForId(ATK_OBJECT(m_element.get()), ObjectAttributeType, "computed-role");
+ if (!role.isEmpty())
+ return JSStringCreateWithUTF8CString(role.utf8().data());
+
+ return JSStringCreateWithCharacters(0, 0);
+}
+
JSRetainPtr<JSStringRef> AccessibilityUIElement::title()
{
- if (!m_element || !ATK_IS_OBJECT(m_element.get()))
+ if (!ATK_IS_OBJECT(m_element.get()))
return JSStringCreateWithCharacters(0, 0);
const gchar* name = atk_object_get_name(ATK_OBJECT(m_element.get()));
- GOwnPtr<gchar> axTitle(g_strdup_printf("AXTitle: %s", name ? name : ""));
+ GUniquePtr<gchar> axTitle(g_strdup_printf("AXTitle: %s", name ? name : ""));
return JSStringCreateWithUTF8CString(axTitle.get());
}
JSRetainPtr<JSStringRef> AccessibilityUIElement::description()
{
- if (!m_element || !ATK_IS_OBJECT(m_element.get()))
+ if (!ATK_IS_OBJECT(m_element.get()))
return JSStringCreateWithCharacters(0, 0);
const gchar* description = atk_object_get_description(ATK_OBJECT(m_element.get()));
if (!description)
return JSStringCreateWithCharacters(0, 0);
- GOwnPtr<gchar> axDesc(g_strdup_printf("AXDescription: %s", description));
+ GUniquePtr<gchar> axDesc(g_strdup_printf("AXDescription: %s", description));
return JSStringCreateWithUTF8CString(axDesc.get());
}
JSRetainPtr<JSStringRef> AccessibilityUIElement::orientation() const
{
- if (!m_element || !ATK_IS_OBJECT(m_element.get()))
+ if (!ATK_IS_OBJECT(m_element.get()))
return JSStringCreateWithCharacters(0, 0);
- const gchar* axOrientation = 0;
+ const gchar* axOrientation = nullptr;
if (checkElementState(m_element.get(), ATK_STATE_HORIZONTAL))
axOrientation = "AXOrientation: AXHorizontalOrientation";
else if (checkElementState(m_element.get(), ATK_STATE_VERTICAL))
@@ -600,126 +1187,215 @@ JSRetainPtr<JSStringRef> AccessibilityUIElement::orientation() const
JSRetainPtr<JSStringRef> AccessibilityUIElement::stringValue()
{
- if (!m_element || !ATK_IS_TEXT(m_element.get()))
+ if (!ATK_IS_TEXT(m_element.get()))
return JSStringCreateWithCharacters(0, 0);
- GOwnPtr<gchar> text(atk_text_get_text(ATK_TEXT(m_element.get()), 0, -1));
- GOwnPtr<gchar> textWithReplacedCharacters(replaceCharactersForResults(text.get()));
- GOwnPtr<gchar> axValue(g_strdup_printf("AXValue: %s", textWithReplacedCharacters.get()));
+ GUniquePtr<gchar> text(atk_text_get_text(ATK_TEXT(m_element.get()), 0, -1));
+ GUniquePtr<gchar> textWithReplacedCharacters(replaceCharactersForResults(text.get()));
+ GUniquePtr<gchar> axValue(g_strdup_printf("AXValue: %s", textWithReplacedCharacters.get()));
return JSStringCreateWithUTF8CString(axValue.get());
}
JSRetainPtr<JSStringRef> AccessibilityUIElement::language()
{
- if (!m_element || !ATK_IS_OBJECT(m_element.get()))
+ if (!ATK_IS_OBJECT(m_element.get()))
return JSStringCreateWithCharacters(0, 0);
const gchar* locale = atk_object_get_object_locale(ATK_OBJECT(m_element.get()));
if (!locale)
return JSStringCreateWithCharacters(0, 0);
- GOwnPtr<char> axValue(g_strdup_printf("AXLanguage: %s", locale));
+ GUniquePtr<char> axValue(g_strdup_printf("AXLanguage: %s", locale));
return JSStringCreateWithUTF8CString(axValue.get());
}
JSRetainPtr<JSStringRef> AccessibilityUIElement::helpText() const
{
- // FIXME: implement
- // We need a way to call WebCore::AccessibilityObject::helpText()
- // from here, probably a new helper class in WebProcess/WebCoreSupport.
- return JSStringCreateWithCharacters(0, 0);
+ if (!ATK_IS_OBJECT(m_element.get()))
+ return JSStringCreateWithCharacters(0, 0);
+
+ AtkRelationSet* relationSet = atk_object_ref_relation_set(ATK_OBJECT(m_element.get()));
+ if (!relationSet)
+ return nullptr;
+
+ AtkRelation* relation = atk_relation_set_get_relation_by_type(relationSet, ATK_RELATION_DESCRIBED_BY);
+ if (!relation)
+ return nullptr;
+
+ GPtrArray* targetList = atk_relation_get_target(relation);
+ if (!targetList || !targetList->len)
+ return nullptr;
+
+ StringBuilder builder;
+ builder.append("AXHelp: ");
+
+ for (int targetCount = 0; targetCount < targetList->len; targetCount++) {
+ if (AtkObject* target = static_cast<AtkObject*>(g_ptr_array_index(targetList, targetCount))) {
+ GUniquePtr<gchar> text(atk_text_get_text(ATK_TEXT(target), 0, -1));
+ if (targetCount)
+ builder.append(" ");
+ builder.append(text.get());
+ }
+ }
+
+ g_object_unref(relationSet);
+
+ return JSStringCreateWithUTF8CString(builder.toString().utf8().data());
}
double AccessibilityUIElement::x()
{
- if (!m_element || !ATK_IS_OBJECT(m_element.get()))
- return 0.0f;
+ if (!ATK_IS_COMPONENT(m_element.get()))
+ return 0;
- int x, y;
- atk_component_get_position(ATK_COMPONENT(m_element.get()), &x, &y, ATK_XY_SCREEN);
+ int x;
+#if ATK_CHECK_VERSION(2,11,90)
+ atk_component_get_extents(ATK_COMPONENT(m_element.get()), &x, nullptr, nullptr, nullptr, ATK_XY_SCREEN);
+#else
+ atk_component_get_position(ATK_COMPONENT(m_element.get()), &x, nullptr, ATK_XY_SCREEN);
+#endif
return x;
}
double AccessibilityUIElement::y()
{
- if (!m_element || !ATK_IS_OBJECT(m_element.get()))
- return 0.0f;
+ if (!ATK_IS_COMPONENT(m_element.get()))
+ return 0;
- int x, y;
- atk_component_get_position(ATK_COMPONENT(m_element.get()), &x, &y, ATK_XY_SCREEN);
+ int y;
+#if ATK_CHECK_VERSION(2,11,90)
+ atk_component_get_extents(ATK_COMPONENT(m_element.get()), nullptr, &y, nullptr, nullptr, ATK_XY_SCREEN);
+#else
+ atk_component_get_position(ATK_COMPONENT(m_element.get()), nullptr, &y, ATK_XY_SCREEN);
+#endif
return y;
}
double AccessibilityUIElement::width()
{
- if (!m_element || !ATK_IS_OBJECT(m_element.get()))
- return 0.0f;
+ if (!ATK_IS_COMPONENT(m_element.get()))
+ return 0;
- int width, height;
- atk_component_get_size(ATK_COMPONENT(m_element.get()), &width, &height);
+ int width;
+#if ATK_CHECK_VERSION(2,11,90)
+ atk_component_get_extents(ATK_COMPONENT(m_element.get()), nullptr, nullptr, &width, nullptr, ATK_XY_WINDOW);
+#else
+ atk_component_get_size(ATK_COMPONENT(m_element.get()), &width, nullptr);
+#endif
return width;
}
double AccessibilityUIElement::height()
{
- if (!m_element || !ATK_IS_OBJECT(m_element.get()))
- return 0.0f;
+ if (!ATK_IS_COMPONENT(m_element.get()))
+ return 0;
- int width, height;
- atk_component_get_size(ATK_COMPONENT(m_element.get()), &width, &height);
+ int height;
+#if ATK_CHECK_VERSION(2,11,90)
+ atk_component_get_extents(ATK_COMPONENT(m_element.get()), nullptr, nullptr, nullptr, &height, ATK_XY_WINDOW);
+#else
+ atk_component_get_size(ATK_COMPONENT(m_element.get()), nullptr, &height);
+#endif
return height;
}
double AccessibilityUIElement::clickPointX()
{
- // FIXME: implement
- return 0.0f;
+ if (!ATK_IS_COMPONENT(m_element.get()))
+ return 0;
+
+ int x, width;
+#if ATK_CHECK_VERSION(2,11,90)
+ atk_component_get_extents(ATK_COMPONENT(m_element.get()), &x, nullptr, &width, nullptr, ATK_XY_WINDOW);
+#else
+ atk_component_get_position(ATK_COMPONENT(m_element.get()), &x, nullptr, ATK_XY_WINDOW);
+ atk_component_get_size(ATK_COMPONENT(m_element.get()), &width, nullptr);
+#endif
+
+ return x + width / 2.0;
}
double AccessibilityUIElement::clickPointY()
{
- // FIXME: implement
- return 0.0f;
+ if (!ATK_IS_COMPONENT(m_element.get()))
+ return 0;
+
+ int y, height;
+#if ATK_CHECK_VERSION(2,11,90)
+ atk_component_get_extents(ATK_COMPONENT(m_element.get()), nullptr, &y, nullptr, &height, ATK_XY_WINDOW);
+#else
+ atk_component_get_position(ATK_COMPONENT(m_element.get()), nullptr, &y, ATK_XY_WINDOW);
+ atk_component_get_size(ATK_COMPONENT(m_element.get()), nullptr, &height);
+#endif
+
+ return y + height / 2.0;
}
double AccessibilityUIElement::intValue() const
{
- if (!m_element || !ATK_IS_OBJECT(m_element.get()))
- return 0.0f;
+ if (!ATK_IS_OBJECT(m_element.get()))
+ return 0;
- GValue value = G_VALUE_INIT;
- atk_value_get_current_value(ATK_VALUE(m_element.get()), &value);
- if (!G_VALUE_HOLDS_FLOAT(&value))
- return 0.0f;
+ if (ATK_IS_VALUE(m_element.get())) {
+#if ATK_CHECK_VERSION(2,11,92)
+ double value;
+ atk_value_get_value_and_text(ATK_VALUE(m_element.get()), &value, nullptr);
+ return value;
+#else
+ GValue value = G_VALUE_INIT;
+ atk_value_get_current_value(ATK_VALUE(m_element.get()), &value);
+ if (!G_VALUE_HOLDS_FLOAT(&value))
+ return 0;
+ return g_value_get_float(&value);
+#endif
+ }
- return g_value_get_float(&value);
+ // Consider headings as an special case when returning the "int value" of
+ // an AccessibilityUIElement, so we can reuse some tests to check the level
+ // both for HTML headings and objects with the aria-level attribute.
+ if (atk_object_get_role(ATK_OBJECT(m_element.get())) == ATK_ROLE_HEADING) {
+ String headingLevel = getAttributeSetValueForId(ATK_OBJECT(m_element.get()), ObjectAttributeType, "level");
+ bool ok;
+ double headingLevelValue = headingLevel.toDouble(&ok);
+ if (ok)
+ return headingLevelValue;
+ }
+
+ return 0;
}
double AccessibilityUIElement::minValue()
{
- if (!m_element || !ATK_IS_OBJECT(m_element.get()))
- return 0.0f;
-
+ if (!ATK_IS_VALUE(m_element.get()))
+ return 0;
+#if ATK_CHECK_VERSION(2,11,92)
+ return rangeMinMaxValue(ATK_VALUE(m_element.get()), RangeLimitMinimum);
+#else
GValue value = G_VALUE_INIT;
atk_value_get_minimum_value(ATK_VALUE(m_element.get()), &value);
if (!G_VALUE_HOLDS_FLOAT(&value))
- return 0.0f;
+ return 0;
return g_value_get_float(&value);
+#endif
}
double AccessibilityUIElement::maxValue()
{
- if (!m_element || !ATK_IS_OBJECT(m_element.get()))
- return 0.0f;
+ if (!ATK_IS_VALUE(m_element.get()))
+ return 0;
+#if ATK_CHECK_VERSION(2,11,92)
+ return rangeMinMaxValue(ATK_VALUE(m_element.get()), RangeLimitMaximum);
+#else
GValue value = G_VALUE_INIT;
atk_value_get_maximum_value(ATK_VALUE(m_element.get()), &value);
if (!G_VALUE_HOLDS_FLOAT(&value))
- return 0.0f;
+ return 0;
return g_value_get_float(&value);
+#endif
}
JSRetainPtr<JSStringRef> AccessibilityUIElement::valueDescription()
@@ -736,8 +1412,11 @@ int AccessibilityUIElement::insertionPointLineNumber()
bool AccessibilityUIElement::isPressActionSupported()
{
- // FIXME: implement
- return false;
+ if (!ATK_IS_ACTION(m_element.get()))
+ return false;
+
+ const gchar* actionName = atk_action_get_name(ATK_ACTION(m_element.get()), 0);
+ return equalLettersIgnoringASCIICase(String(actionName), "press") || equalLettersIgnoringASCIICase(String(actionName), "jump");
}
bool AccessibilityUIElement::isIncrementActionSupported()
@@ -772,6 +1451,11 @@ bool AccessibilityUIElement::isSelected() const
return checkElementState(m_element.get(), ATK_STATE_SELECTED);
}
+bool AccessibilityUIElement::isSelectedOptionActive() const
+{
+ return checkElementState(m_element.get(), ATK_STATE_ACTIVE);
+}
+
bool AccessibilityUIElement::isExpanded() const
{
return checkElementState(m_element.get(), ATK_STATE_EXPANDED);
@@ -782,6 +1466,11 @@ bool AccessibilityUIElement::isChecked() const
return checkElementState(m_element.get(), ATK_STATE_CHECKED);
}
+bool AccessibilityUIElement::isIndeterminate() const
+{
+ return checkElementState(m_element.get(), ATK_STATE_INDETERMINATE);
+}
+
int AccessibilityUIElement::hierarchicalLevel() const
{
// FIXME: implement
@@ -809,14 +1498,34 @@ JSRetainPtr<JSStringRef> AccessibilityUIElement::ariaDropEffects() const
// parameterized attributes
int AccessibilityUIElement::lineForIndex(int index)
{
- // FIXME: implement
- return 0;
+ if (!ATK_IS_TEXT(m_element.get()))
+ return -1;
+
+ if (index < 0 || index > atk_text_get_character_count(ATK_TEXT(m_element.get())))
+ return -1;
+
+ GUniquePtr<gchar> text(atk_text_get_text(ATK_TEXT(m_element.get()), 0, index));
+ int lineNo = 0;
+ for (gchar* offset = text.get(); *offset; ++offset) {
+ if (*offset == '\n')
+ ++lineNo;
+ }
+
+ return lineNo;
}
JSRetainPtr<JSStringRef> AccessibilityUIElement::rangeForLine(int line)
{
- // FIXME: implement
- return JSStringCreateWithCharacters(0, 0);
+ if (!ATK_IS_TEXT(m_element.get()))
+ return JSStringCreateWithCharacters(0, 0);
+
+ AtkText* text = ATK_TEXT(m_element.get());
+ gint startOffset = 0, endOffset = 0;
+ for (int i = 0; i <= line; ++i)
+ atk_text_get_string_at_offset(text, endOffset, ATK_TEXT_GRANULARITY_LINE, &startOffset, &endOffset);
+
+ GUniquePtr<gchar> range(g_strdup_printf("{%d, %d}", startOffset, endOffset - startOffset));
+ return JSStringCreateWithUTF8CString(range.get());
}
JSRetainPtr<JSStringRef> AccessibilityUIElement::rangeForPosition(int x, int y)
@@ -827,20 +1536,50 @@ JSRetainPtr<JSStringRef> AccessibilityUIElement::rangeForPosition(int x, int y)
JSRetainPtr<JSStringRef> AccessibilityUIElement::boundsForRange(unsigned location, unsigned length)
{
- // FIXME: implement
- return JSStringCreateWithCharacters(0, 0);
+ if (!ATK_IS_TEXT(m_element.get()))
+ return JSStringCreateWithCharacters(0, 0);
+
+ AtkTextRectangle rect;
+ atk_text_get_range_extents(ATK_TEXT(m_element.get()), location, location + length, ATK_XY_WINDOW, &rect);
+
+ GUniquePtr<gchar> bounds(g_strdup_printf("{%d, %d, %d, %d}", rect.x, rect.y, rect.width, rect.height));
+ return JSStringCreateWithUTF8CString(bounds.get());
}
JSRetainPtr<JSStringRef> AccessibilityUIElement::stringForRange(unsigned location, unsigned length)
{
- // FIXME: implement
- return JSStringCreateWithCharacters(0, 0);
+ if (!ATK_IS_TEXT(m_element.get()))
+ return JSStringCreateWithCharacters(0, 0);
+
+ String string = atk_text_get_text(ATK_TEXT(m_element.get()), location, location + length);
+ return JSStringCreateWithUTF8CString(string.utf8().data());
}
JSRetainPtr<JSStringRef> AccessibilityUIElement::attributedStringForRange(unsigned location, unsigned length)
{
- // FIXME: implement
- return JSStringCreateWithCharacters(0, 0);
+ if (!ATK_IS_TEXT(m_element.get()))
+ return JSStringCreateWithCharacters(0, 0);
+
+ StringBuilder builder;
+
+ // The default text attributes apply to the entire element.
+ builder.append("\n\tDefault text attributes:\n\t\t");
+ builder.append(attributeSetToString(getAttributeSet(m_element.get(), TextAttributeType), "\n\t\t"));
+
+ // The attribute run provides attributes specific to the range of text at the specified offset.
+ AtkAttributeSet* attributeSet;
+ AtkText* text = ATK_TEXT(m_element.get());
+ gint start = 0, end = 0;
+ for (int i = location; i < location + length; i = end) {
+ AtkAttributeSet* attributeSet = atk_text_get_run_attributes(text, i, &start, &end);
+ GUniquePtr<gchar> substring(replaceCharactersForResults(atk_text_get_text(text, start, end)));
+ builder.append(String::format("\n\tRange attributes for '%s':\n\t\t", substring.get()));
+ builder.append(attributeSetToString(attributeSet, "\n\t\t"));
+ }
+
+ atk_attribute_set_free(attributeSet);
+
+ return JSStringCreateWithUTF8CString(builder.toString().utf8().data());
}
bool AccessibilityUIElement::attributedStringRangeIsMisspelled(unsigned location, unsigned length)
@@ -849,22 +1588,40 @@ bool AccessibilityUIElement::attributedStringRangeIsMisspelled(unsigned location
return false;
}
-PassRefPtr<AccessibilityUIElement> AccessibilityUIElement::uiElementForSearchPredicate(JSContextRef context, AccessibilityUIElement* startElement, bool isDirectionNext, JSValueRef searchKey, JSStringRef searchText, bool visibleOnly)
+unsigned AccessibilityUIElement::uiElementCountForSearchPredicate(JSContextRef context, AccessibilityUIElement* startElement, bool isDirectionNext, JSValueRef searchKey, JSStringRef searchText, bool visibleOnly, bool immediateDescendantsOnly)
{
// FIXME: implement
return 0;
}
-JSRetainPtr<JSStringRef> AccessibilityUIElement::attributesOfColumnHeaders()
+PassRefPtr<AccessibilityUIElement> AccessibilityUIElement::uiElementForSearchPredicate(JSContextRef context, AccessibilityUIElement* startElement, bool isDirectionNext, JSValueRef searchKey, JSStringRef searchText, bool visibleOnly, bool immediateDescendantsOnly)
{
// FIXME: implement
- return JSStringCreateWithCharacters(0, 0);
+ return nullptr;
}
-JSRetainPtr<JSStringRef> AccessibilityUIElement::attributesOfRowHeaders()
+JSRetainPtr<JSStringRef> AccessibilityUIElement::selectTextWithCriteria(JSContextRef context, JSStringRef ambiguityResolution, JSValueRef searchStrings, JSStringRef replacementString, JSStringRef activity)
{
// FIXME: implement
- return JSStringCreateWithCharacters(0, 0);
+ return nullptr;
+}
+
+JSRetainPtr<JSStringRef> AccessibilityUIElement::attributesOfColumnHeaders()
+{
+ if (!ATK_IS_TABLE(m_element.get()))
+ return JSStringCreateWithCharacters(0, 0);
+
+ Vector<RefPtr<AccessibilityUIElement> > columnHeaders = getColumnHeaders(ATK_TABLE(m_element.get()));
+ return createStringWithAttributes(columnHeaders);
+}
+
+JSRetainPtr<JSStringRef> AccessibilityUIElement::attributesOfRowHeaders()
+{
+ if (!ATK_IS_TABLE(m_element.get()))
+ return JSStringCreateWithCharacters(0, 0);
+
+ Vector<RefPtr<AccessibilityUIElement> > rowHeaders = getRowHeaders(ATK_TABLE(m_element.get()));
+ return createStringWithAttributes(rowHeaders);
}
JSRetainPtr<JSStringRef> AccessibilityUIElement::attributesOfColumns()
@@ -881,8 +1638,11 @@ JSRetainPtr<JSStringRef> AccessibilityUIElement::attributesOfRows()
JSRetainPtr<JSStringRef> AccessibilityUIElement::attributesOfVisibleCells()
{
- // FIXME: implement
- return JSStringCreateWithCharacters(0, 0);
+ if (!ATK_IS_TABLE(m_element.get()))
+ return JSStringCreateWithCharacters(0, 0);
+
+ Vector<RefPtr<AccessibilityUIElement> > visibleCells = getVisibleCells(this);
+ return createStringWithAttributes(visibleCells);
}
JSRetainPtr<JSStringRef> AccessibilityUIElement::attributesOfHeader()
@@ -893,7 +1653,7 @@ JSRetainPtr<JSStringRef> AccessibilityUIElement::attributesOfHeader()
int AccessibilityUIElement::rowCount()
{
- if (!m_element || !ATK_IS_TABLE(m_element.get()))
+ if (!ATK_IS_TABLE(m_element.get()))
return 0;
return atk_table_get_n_rows(ATK_TABLE(m_element.get()));
@@ -901,7 +1661,7 @@ int AccessibilityUIElement::rowCount()
int AccessibilityUIElement::columnCount()
{
- if (!m_element || !ATK_IS_TABLE(m_element.get()))
+ if (!ATK_IS_TABLE(m_element.get()))
return 0;
return atk_table_get_n_columns(ATK_TABLE(m_element.get()));
@@ -927,36 +1687,48 @@ JSRetainPtr<JSStringRef> AccessibilityUIElement::columnIndexRange()
PassRefPtr<AccessibilityUIElement> AccessibilityUIElement::cellForColumnAndRow(unsigned col, unsigned row)
{
- if (!m_element || !ATK_IS_TABLE(m_element.get()))
- return 0;
+ if (!ATK_IS_TABLE(m_element.get()))
+ return nullptr;
// Adopt the AtkObject representing the cell because
// at_table_ref_at() transfers full ownership.
GRefPtr<AtkObject> foundCell = adoptGRef(atk_table_ref_at(ATK_TABLE(m_element.get()), row, col));
- return foundCell ? AccessibilityUIElement::create(foundCell.get()) : 0;
+ return foundCell ? AccessibilityUIElement::create(foundCell.get()) : nullptr;
}
PassRefPtr<AccessibilityUIElement> AccessibilityUIElement::horizontalScrollbar() const
{
// FIXME: implement
- return 0;
+ return nullptr;
}
PassRefPtr<AccessibilityUIElement> AccessibilityUIElement::verticalScrollbar() const
{
// FIXME: implement
- return 0;
+ return nullptr;
}
JSRetainPtr<JSStringRef> AccessibilityUIElement::selectedTextRange()
{
- // FIXME: implement
- return JSStringCreateWithCharacters(0, 0);
+ if (!ATK_IS_TEXT(m_element.get()))
+ return JSStringCreateWithCharacters(0, 0);
+
+ gint start, end;
+ g_free(atk_text_get_selection(ATK_TEXT(m_element.get()), 0, &start, &end));
+
+ GUniquePtr<gchar> selection(g_strdup_printf("{%d, %d}", start, end - start));
+ return JSStringCreateWithUTF8CString(selection.get());
}
-void AccessibilityUIElement::setSelectedTextRange(unsigned location, unsigned length)
+bool AccessibilityUIElement::setSelectedTextRange(unsigned location, unsigned length)
{
- // FIXME: implement
+ if (!ATK_IS_TEXT(m_element.get()))
+ return false;
+
+ if (!length)
+ return atk_text_set_caret_offset(ATK_TEXT(m_element.get()), location);
+
+ return atk_text_set_selection(ATK_TEXT(m_element.get()), 0, location, location + length);
}
void AccessibilityUIElement::increment()
@@ -976,9 +1748,6 @@ void AccessibilityUIElement::showMenu()
void AccessibilityUIElement::press()
{
- if (!m_element || !ATK_IS_OBJECT(m_element.get()))
- return;
-
if (!ATK_IS_ACTION(m_element.get()))
return;
@@ -991,6 +1760,22 @@ void AccessibilityUIElement::setSelectedChild(AccessibilityUIElement* element) c
// FIXME: implement
}
+void AccessibilityUIElement::setSelectedChildAtIndex(unsigned index) const
+{
+ if (!ATK_IS_SELECTION(m_element.get()))
+ return;
+
+ atk_selection_add_selection(ATK_SELECTION(m_element.get()), index);
+}
+
+void AccessibilityUIElement::removeSelectionAtIndex(unsigned index) const
+{
+ if (!ATK_IS_SELECTION(m_element.get()))
+ return;
+
+ atk_selection_remove_selection(ATK_SELECTION(m_element.get()), index);
+}
+
JSRetainPtr<JSStringRef> AccessibilityUIElement::accessibilityValue() const
{
// FIXME: implement
@@ -999,7 +1784,7 @@ JSRetainPtr<JSStringRef> AccessibilityUIElement::accessibilityValue() const
JSRetainPtr<JSStringRef> AccessibilityUIElement::documentEncoding()
{
- if (!m_element || !ATK_IS_OBJECT(m_element.get()))
+ if (!ATK_IS_DOCUMENT(m_element.get()))
return JSStringCreateWithCharacters(0, 0);
AtkRole role = atk_object_get_role(ATK_OBJECT(m_element.get()));
@@ -1011,7 +1796,7 @@ JSRetainPtr<JSStringRef> AccessibilityUIElement::documentEncoding()
JSRetainPtr<JSStringRef> AccessibilityUIElement::documentURI()
{
- if (!m_element || !ATK_IS_OBJECT(m_element.get()))
+ if (!ATK_IS_DOCUMENT(m_element.get()))
return JSStringCreateWithCharacters(0, 0);
AtkRole role = atk_object_get_role(ATK_OBJECT(m_element.get()));
@@ -1023,19 +1808,40 @@ JSRetainPtr<JSStringRef> AccessibilityUIElement::documentURI()
JSRetainPtr<JSStringRef> AccessibilityUIElement::url()
{
- // FIXME: implement
- return JSStringCreateWithCharacters(0, 0);
+ if (!ATK_IS_HYPERLINK_IMPL(m_element.get()))
+ return JSStringCreateWithCharacters(0, 0);
+
+ AtkHyperlink* hyperlink = atk_hyperlink_impl_get_hyperlink(ATK_HYPERLINK_IMPL(m_element.get()));
+ GUniquePtr<char> hyperlinkURI(atk_hyperlink_get_uri(hyperlink, 0));
+
+ // Build the result string, stripping the absolute URL paths if present.
+ char* localURI = g_strstr_len(hyperlinkURI.get(), -1, "LayoutTests");
+ String axURL = String::format("AXURL: %s", localURI ? localURI : hyperlinkURI.get());
+ return JSStringCreateWithUTF8CString(axURL.utf8().data());
}
bool AccessibilityUIElement::addNotificationListener(JSValueRef functionCallback)
{
- // FIXME: implement
+ if (!functionCallback)
+ return false;
+
+ // Only one notification listener per element.
+ if (m_notificationHandler)
+ return false;
+
+ m_notificationHandler = AccessibilityNotificationHandler::create();
+ m_notificationHandler->setPlatformElement(platformUIElement());
+ m_notificationHandler->setNotificationFunctionCallback(functionCallback);
+
return true;
}
bool AccessibilityUIElement::removeNotificationListener()
{
- // FIXME: implement
+ // Programmers should not be trying to remove a listener that's already removed.
+ ASSERT(m_notificationHandler);
+ m_notificationHandler = nullptr;
+
return true;
}
@@ -1079,10 +1885,11 @@ bool AccessibilityUIElement::isIgnored() const
bool AccessibilityUIElement::hasPopup() const
{
- if (!m_element || !ATK_IS_OBJECT(m_element.get()))
+ if (!ATK_IS_OBJECT(m_element.get()))
return false;
- return equalIgnoringCase(getAttributeSetValueForId(ATK_OBJECT(m_element.get()), "aria-haspopup"), "true");
+ String hasPopupValue = getAttributeSetValueForId(ATK_OBJECT(m_element.get()), ObjectAttributeType, "haspopup");
+ return equalLettersIgnoringASCIICase(hasPopupValue, "true");
}
void AccessibilityUIElement::takeFocus()
@@ -1106,10 +1913,16 @@ void AccessibilityUIElement::removeSelection()
}
// Text markers
+PassRefPtr<AccessibilityTextMarkerRange> AccessibilityUIElement::lineTextMarkerRangeForTextMarker(AccessibilityTextMarker* textMarker)
+{
+ // FIXME: implement
+ return nullptr;
+}
+
PassRefPtr<AccessibilityTextMarkerRange> AccessibilityUIElement::textMarkerRangeForElement(AccessibilityUIElement* element)
{
// FIXME: implement
- return 0;
+ return nullptr;
}
int AccessibilityUIElement::textMarkerRangeLength(AccessibilityTextMarkerRange* range)
@@ -1121,13 +1934,13 @@ int AccessibilityUIElement::textMarkerRangeLength(AccessibilityTextMarkerRange*
PassRefPtr<AccessibilityTextMarker> AccessibilityUIElement::previousTextMarker(AccessibilityTextMarker* textMarker)
{
// FIXME: implement
- return 0;
+ return nullptr;
}
PassRefPtr<AccessibilityTextMarker> AccessibilityUIElement::nextTextMarker(AccessibilityTextMarker* textMarker)
{
// FIXME: implement
- return 0;
+ return nullptr;
}
JSRetainPtr<JSStringRef> AccessibilityUIElement::stringForTextMarkerRange(AccessibilityTextMarkerRange* markerRange)
@@ -1139,31 +1952,43 @@ JSRetainPtr<JSStringRef> AccessibilityUIElement::stringForTextMarkerRange(Access
PassRefPtr<AccessibilityTextMarkerRange> AccessibilityUIElement::textMarkerRangeForMarkers(AccessibilityTextMarker* startMarker, AccessibilityTextMarker* endMarker)
{
// FIXME: implement
- return 0;
+ return nullptr;
}
PassRefPtr<AccessibilityTextMarker> AccessibilityUIElement::startTextMarkerForTextMarkerRange(AccessibilityTextMarkerRange* range)
{
// FIXME: implement
- return 0;
+ return nullptr;
}
PassRefPtr<AccessibilityTextMarker> AccessibilityUIElement::endTextMarkerForTextMarkerRange(AccessibilityTextMarkerRange* range)
{
// FIXME: implement
- return 0;
+ return nullptr;
+}
+
+PassRefPtr<AccessibilityTextMarker> AccessibilityUIElement::endTextMarkerForBounds(int x, int y, int width, int height)
+{
+ // FIXME: implement
+ return nullptr;
+}
+
+PassRefPtr<AccessibilityTextMarker> AccessibilityUIElement::startTextMarkerForBounds(int x, int y, int width, int height)
+{
+ // FIXME: implement
+ return nullptr;
}
PassRefPtr<AccessibilityTextMarker> AccessibilityUIElement::textMarkerForPoint(int x, int y)
{
// FIXME: implement
- return 0;
+ return nullptr;
}
PassRefPtr<AccessibilityUIElement> AccessibilityUIElement::accessibilityElementForTextMarker(AccessibilityTextMarker* marker)
{
// FIXME: implement
- return 0;
+ return nullptr;
}
bool AccessibilityUIElement::attributedStringForTextMarkerRangeContainsAttribute(JSStringRef attribute, AccessibilityTextMarkerRange* range)
@@ -1187,38 +2012,126 @@ bool AccessibilityUIElement::isTextMarkerValid(AccessibilityTextMarker* textMark
PassRefPtr<AccessibilityTextMarker> AccessibilityUIElement::textMarkerForIndex(int textIndex)
{
// FIXME: implement
- return 0;
+ return nullptr;
+}
+
+PassRefPtr<AccessibilityTextMarker> AccessibilityUIElement::startTextMarker()
+{
+ // FIXME: implement
+ return nullptr;
+}
+
+PassRefPtr<AccessibilityTextMarker> AccessibilityUIElement::endTextMarker()
+{
+ // FIXME: implement
+ return nullptr;
+}
+
+bool AccessibilityUIElement::setSelectedVisibleTextRange(AccessibilityTextMarkerRange*)
+{
+ return false;
}
void AccessibilityUIElement::scrollToMakeVisible()
{
// FIXME: implement
}
+
+void AccessibilityUIElement::scrollToGlobalPoint(int x, int y)
+{
+ // FIXME: implement
+}
+
+void AccessibilityUIElement::scrollToMakeVisibleWithSubFocus(int x, int y, int width, int height)
+{
+ // FIXME: implement
+}
JSRetainPtr<JSStringRef> AccessibilityUIElement::supportedActions() const
{
// FIXME: implement
- return 0;
+ return nullptr;
}
JSRetainPtr<JSStringRef> AccessibilityUIElement::pathDescription() const
{
notImplemented();
- return 0;
+ return nullptr;
}
JSRetainPtr<JSStringRef> AccessibilityUIElement::mathPostscriptsDescription() const
{
notImplemented();
- return 0;
+ return nullptr;
}
JSRetainPtr<JSStringRef> AccessibilityUIElement::mathPrescriptsDescription() const
{
notImplemented();
- return 0;
+ return nullptr;
}
-} // namespace WTR
+JSRetainPtr<JSStringRef> AccessibilityUIElement::classList() const
+{
+ notImplemented();
+ return nullptr;
+}
+JSRetainPtr<JSStringRef> stringAtOffset(PlatformUIElement element, AtkTextBoundary boundary, int offset)
+{
+ if (!ATK_IS_TEXT(element.get()))
+ return JSStringCreateWithCharacters(0, 0);
+
+ gint startOffset, endOffset;
+ StringBuilder builder;
+
+#if ATK_CHECK_VERSION(2, 10, 0)
+ AtkTextGranularity granularity;
+ switch (boundary) {
+ case ATK_TEXT_BOUNDARY_CHAR:
+ granularity = ATK_TEXT_GRANULARITY_CHAR;
+ break;
+ case ATK_TEXT_BOUNDARY_WORD_START:
+ granularity = ATK_TEXT_GRANULARITY_WORD;
+ break;
+ case ATK_TEXT_BOUNDARY_LINE_START:
+ granularity = ATK_TEXT_GRANULARITY_LINE;
+ break;
+ case ATK_TEXT_BOUNDARY_SENTENCE_START:
+ granularity = ATK_TEXT_GRANULARITY_SENTENCE;
+ break;
+ default:
+ return JSStringCreateWithCharacters(0, 0);
+ }
+
+ builder.append(atk_text_get_string_at_offset(ATK_TEXT(element.get()), offset, granularity, &startOffset, &endOffset));
+#else
+ builder.append(atk_text_get_text_at_offset(ATK_TEXT(element.get()), offset, boundary, &startOffset, &endOffset));
#endif
+ builder.append(String::format(", %i, %i", startOffset, endOffset));
+ return JSStringCreateWithUTF8CString(builder.toString().utf8().data());
+}
+
+JSRetainPtr<JSStringRef> AccessibilityUIElement::characterAtOffset(int offset)
+{
+ return stringAtOffset(m_element, ATK_TEXT_BOUNDARY_CHAR, offset);
+}
+
+JSRetainPtr<JSStringRef> AccessibilityUIElement::wordAtOffset(int offset)
+{
+ return stringAtOffset(m_element, ATK_TEXT_BOUNDARY_WORD_START, offset);
+}
+
+JSRetainPtr<JSStringRef> AccessibilityUIElement::lineAtOffset(int offset)
+{
+ return stringAtOffset(m_element, ATK_TEXT_BOUNDARY_LINE_START, offset);
+}
+
+JSRetainPtr<JSStringRef> AccessibilityUIElement::sentenceAtOffset(int offset)
+{
+ return stringAtOffset(m_element, ATK_TEXT_BOUNDARY_SENTENCE_START, offset);
+}
+
+} // namespace WTR
+
+#endif // HAVE(ACCESSIBILITY)