2 * Copyright (C) 2011 Apple Inc. All Rights Reserved.
3 * Copyright (C) 2012 Igalia S.L.
4 * Copyright (C) 2013 Samsung Electronics. All rights reserved.
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
15 * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
16 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
18 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
19 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
20 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
21 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
22 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
23 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
25 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 #include "AccessibilityUIElement.h"
31 #if HAVE(ACCESSIBILITY)
33 #include "InjectedBundle.h"
34 #include "InjectedBundlePage.h"
35 #include "NotImplemented.h"
36 #include <JavaScriptCore/JSStringRef.h>
37 #include <JavaScriptCore/OpaqueJSString.h>
38 #if ATK_CHECK_VERSION(2,11,90)
39 #include <WebKit/WKBundleFrame.h>
42 #include <wtf/Assertions.h>
43 #include <wtf/gobject/GRefPtr.h>
44 #include <wtf/gobject/GUniquePtr.h>
45 #include <wtf/text/CString.h>
46 #include <wtf/text/StringBuilder.h>
47 #include <wtf/text/WTFString.h>
48 #include <wtf/unicode/CharacterNames.h>
54 #if ATK_CHECK_VERSION(2,11,92)
61 enum AtkAttributeType {
66 enum AttributeDomain {
71 enum AttributesIndex {
80 SortAscendingValueIndex,
81 SortDescendingValueIndex,
82 SortUnknownValueIndex,
87 // Attribute names & Values (keep on sync with enum AttributesIndex).
88 const String attributesMap[][2] = {
90 { "AXInvalid", "invalid" },
91 { "AXARIAPosInSet", "posinset" },
92 { "AXARIASetSize", "setsize" },
93 { "AXPlaceholderValue", "placeholder-text" } ,
94 { "AXSortDirection", "sort" },
97 { "AXAscendingSortDirection", "ascending" },
98 { "AXDescendingSortDirection", "descending" },
99 { "AXUnknownSortDirection", "unknown" }
102 #if ATK_CHECK_VERSION(2, 11, 3)
103 const char* landmarkStringBanner = "AXLandmarkBanner";
104 const char* landmarkStringComplementary = "AXLandmarkComplementary";
105 const char* landmarkStringContentinfo = "AXLandmarkContentInfo";
106 const char* landmarkStringMain = "AXLandmarkMain";
107 const char* landmarkStringNavigation = "AXLandmarkNavigation";
108 const char* landmarkStringSearch = "AXLandmarkSearch";
111 String jsStringToWTFString(JSStringRef attribute)
113 size_t bufferSize = JSStringGetMaximumUTF8CStringSize(attribute);
114 GUniquePtr<gchar> buffer(static_cast<gchar*>(g_malloc(bufferSize)));
115 JSStringGetUTF8CString(attribute, buffer.get(), bufferSize);
117 return String::fromUTF8(buffer.get());
120 String coreAttributeToAtkAttribute(JSStringRef attribute)
122 String attributeString = jsStringToWTFString(attribute);
123 for (int i = 0; i < NumberOfAttributes; ++i) {
124 if (attributesMap[i][CoreDomain] == attributeString)
125 return attributesMap[i][AtkDomain];
128 return attributeString;
131 String atkAttributeValueToCoreAttributeValue(AtkAttributeType type, const String& id, const String& value)
133 if (type == ObjectAttributeType) {
134 // We need to translate ATK values exposed for 'aria-sort' (e.g. 'ascending')
135 // into those expected by the layout tests (e.g. 'AXAscendingSortDirection').
136 if (id == attributesMap[SortNameIndex][AtkDomain] && !value.isEmpty()) {
137 if (value == attributesMap[SortAscendingValueIndex][AtkDomain])
138 return attributesMap[SortAscendingValueIndex][CoreDomain];
139 if (value == attributesMap[SortDescendingValueIndex][AtkDomain])
140 return attributesMap[SortDescendingValueIndex][CoreDomain];
142 return attributesMap[SortUnknownValueIndex][CoreDomain];
144 } else if (type == TextAttributeType) {
145 // In case of 'aria-invalid' when the attribute empty or has "false" for ATK
146 // it should not be mapped at all, but layout tests will expect 'false'.
147 if (id == attributesMap[InvalidNameIndex][AtkDomain] && value.isEmpty())
154 AtkAttributeSet* getAttributeSet(AtkObject* accessible, AtkAttributeType type)
156 if (type == ObjectAttributeType)
157 return atk_object_get_attributes(accessible);
159 if (type == TextAttributeType) {
160 if (!ATK_IS_TEXT(accessible))
163 return atk_text_get_default_attributes(ATK_TEXT(accessible));
166 ASSERT_NOT_REACHED();
170 String getAttributeSetValueForId(AtkObject* accessible, AtkAttributeType type, String id)
172 AtkAttributeSet* attributeSet = getAttributeSet(accessible, type);
176 String attributeValue;
177 for (AtkAttributeSet* attributes = attributeSet; attributes; attributes = attributes->next) {
178 AtkAttribute* atkAttribute = static_cast<AtkAttribute*>(attributes->data);
179 if (id == atkAttribute->name) {
180 attributeValue = String::fromUTF8(atkAttribute->value);
184 atk_attribute_set_free(attributeSet);
186 return atkAttributeValueToCoreAttributeValue(type, id, attributeValue);
189 String getAtkAttributeSetAsString(AtkObject* accessible, AtkAttributeType type)
191 AtkAttributeSet* attributeSet = getAttributeSet(accessible, type);
195 StringBuilder builder;
196 for (AtkAttributeSet* attributes = attributeSet; attributes; attributes = attributes->next) {
197 AtkAttribute* attribute = static_cast<AtkAttribute*>(attributes->data);
198 GUniquePtr<gchar> attributeData(g_strconcat(attribute->name, ":", attribute->value, NULL));
199 builder.append(attributeData.get());
200 if (attributes->next)
201 builder.append(", ");
203 atk_attribute_set_free(attributeSet);
205 return builder.toString();
208 bool checkElementState(PlatformUIElement element, AtkStateType stateType)
210 if (!ATK_IS_OBJECT(element.get()))
213 GRefPtr<AtkStateSet> stateSet = adoptGRef(atk_object_ref_state_set(ATK_OBJECT(element.get())));
214 return atk_state_set_contains_state(stateSet.get(), stateType);
217 JSStringRef indexRangeInTable(PlatformUIElement element, bool isRowRange)
219 GUniquePtr<gchar> rangeString(g_strdup("{0, 0}"));
220 #if ATK_CHECK_VERSION(2,11,90)
221 if (!ATK_IS_TABLE_CELL(element.get()))
222 return JSStringCreateWithUTF8CString(rangeString.get());
224 if (!ATK_IS_OBJECT(element.get()))
225 return JSStringCreateWithUTF8CString(rangeString.get());
227 AtkObject* axTable = atk_object_get_parent(ATK_OBJECT(element.get()));
228 if (!axTable || !ATK_IS_TABLE(axTable))
229 return JSStringCreateWithUTF8CString(rangeString.get());
231 // Look for the cell in the table.
232 gint indexInParent = atk_object_get_index_in_parent(ATK_OBJECT(element.get()));
233 if (indexInParent == -1)
234 return JSStringCreateWithUTF8CString(rangeString.get());
240 gint columnSpan = -1;
241 #if ATK_CHECK_VERSION(2,11,90)
242 atk_table_cell_get_row_column_span(ATK_TABLE_CELL(element.get()), &row, &column, &rowSpan, &columnSpan);
244 row = atk_table_get_row_at_index(ATK_TABLE(axTable), indexInParent);
245 column = atk_table_get_column_at_index(ATK_TABLE(axTable), indexInParent);
246 rowSpan = atk_table_get_row_extent_at(ATK_TABLE(axTable), row, column);
247 columnSpan = atk_table_get_column_extent_at(ATK_TABLE(axTable), row, column);
250 // Get the actual values, if row and columns are valid values.
251 if (row != -1 && column != -1) {
261 rangeString.reset(g_strdup_printf("{%d, %d}", base, length));
264 return JSStringCreateWithUTF8CString(rangeString.get());
267 void alterCurrentValue(PlatformUIElement element, int factor)
269 if (!ATK_IS_VALUE(element.get()))
272 #if ATK_CHECK_VERSION(2,11,92)
274 atk_value_get_value_and_text(ATK_VALUE(element.get()), ¤tValue, nullptr);
276 double increment = atk_value_get_increment(ATK_VALUE(element.get()));
277 atk_value_set_value(ATK_VALUE(element.get()), currentValue + factor * increment);
279 GValue currentValue = G_VALUE_INIT;
280 atk_value_get_current_value(ATK_VALUE(element.get()), ¤tValue);
282 GValue increment = G_VALUE_INIT;
283 atk_value_get_minimum_increment(ATK_VALUE(element.get()), &increment);
285 GValue newValue = G_VALUE_INIT;
286 g_value_init(&newValue, G_TYPE_FLOAT);
288 g_value_set_float(&newValue, g_value_get_float(¤tValue) + factor * g_value_get_float(&increment));
289 atk_value_set_current_value(ATK_VALUE(element.get()), &newValue);
291 g_value_unset(&newValue);
292 g_value_unset(&increment);
293 g_value_unset(¤tValue);
297 gchar* replaceCharactersForResults(gchar* str)
299 WTF::String uString = WTF::String::fromUTF8(str);
301 // The object replacement character is passed along to ATs so we need to be
302 // able to test for their presence and do so without causing test failures.
303 uString.replace(objectReplacementCharacter, "<obj>");
305 // The presence of newline characters in accessible text of a single object
306 // is appropriate, but it makes test results (especially the accessible tree)
308 uString.replace("\n", "<\\n>");
310 return g_strdup(uString.utf8().data());
313 const gchar* roleToString(AtkObject* object)
315 AtkRole role = atk_object_get_role(object);
317 #if ATK_CHECK_VERSION(2, 11, 3)
318 if (role == ATK_ROLE_LANDMARK) {
319 String xmlRolesValue = getAttributeSetValueForId(object, ObjectAttributeType, "xml-roles");
320 if (equalIgnoringCase(xmlRolesValue, "banner"))
321 return landmarkStringBanner;
322 if (equalIgnoringCase(xmlRolesValue, "complementary"))
323 return landmarkStringComplementary;
324 if (equalIgnoringCase(xmlRolesValue, "contentinfo"))
325 return landmarkStringContentinfo;
326 if (equalIgnoringCase(xmlRolesValue, "main"))
327 return landmarkStringMain;
328 if (equalIgnoringCase(xmlRolesValue, "navigation"))
329 return landmarkStringNavigation;
330 if (equalIgnoringCase(xmlRolesValue, "search"))
331 return landmarkStringSearch;
338 case ATK_ROLE_DIALOG:
340 case ATK_ROLE_CANVAS:
342 case ATK_ROLE_CHECK_BOX:
344 case ATK_ROLE_COLOR_CHOOSER:
345 return "AXColorWell";
346 case ATK_ROLE_COLUMN_HEADER:
347 return "AXColumnHeader";
348 case ATK_ROLE_COMBO_BOX:
350 case ATK_ROLE_COMMENT:
352 case ATK_ROLE_DOCUMENT_FRAME:
354 case ATK_ROLE_DOCUMENT_WEB:
356 case ATK_ROLE_EMBEDDED:
359 return "AXTextField";
360 case ATK_ROLE_FOOTER:
364 case ATK_ROLE_GROUPING:
366 case ATK_ROLE_HEADING:
370 case ATK_ROLE_IMAGE_MAP:
372 case ATK_ROLE_INVALID:
380 case ATK_ROLE_LIST_BOX:
382 case ATK_ROLE_LIST_ITEM:
386 case ATK_ROLE_MENU_BAR:
388 case ATK_ROLE_MENU_ITEM:
390 case ATK_ROLE_PAGE_TAB:
392 case ATK_ROLE_PAGE_TAB_LIST:
396 case ATK_ROLE_PARAGRAPH:
397 return "AXParagraph";
398 case ATK_ROLE_PASSWORD_TEXT:
399 return "AXPasswordField";
400 case ATK_ROLE_PROGRESS_BAR:
401 return "AXProgressIndicator";
402 case ATK_ROLE_PUSH_BUTTON:
404 case ATK_ROLE_RADIO_BUTTON:
405 return "AXRadioButton";
406 case ATK_ROLE_RADIO_MENU_ITEM:
407 return "AXRadioMenuItem";
408 case ATK_ROLE_ROW_HEADER:
409 return "AXRowHeader";
410 case ATK_ROLE_CHECK_MENU_ITEM:
411 return "AXCheckMenuItem";
414 case ATK_ROLE_SCROLL_BAR:
415 return "AXScrollBar";
416 case ATK_ROLE_SCROLL_PANE:
417 return "AXScrollArea";
418 case ATK_ROLE_SECTION:
420 case ATK_ROLE_SEPARATOR:
421 return "AXSeparator";
422 case ATK_ROLE_SLIDER:
424 case ATK_ROLE_SPIN_BUTTON:
425 return "AXSpinButton";
426 case ATK_ROLE_STATUSBAR:
427 return "AXStatusBar";
430 case ATK_ROLE_TABLE_CELL:
432 case ATK_ROLE_TABLE_COLUMN_HEADER:
433 return "AXColumnHeader";
434 case ATK_ROLE_TABLE_ROW:
436 case ATK_ROLE_TABLE_ROW_HEADER:
437 return "AXRowHeader";
438 case ATK_ROLE_TOGGLE_BUTTON:
439 return "AXToggleButton";
440 case ATK_ROLE_TOOL_BAR:
442 case ATK_ROLE_TOOL_TIP:
443 return "AXUserInterfaceTooltip";
446 case ATK_ROLE_TREE_TABLE:
448 case ATK_ROLE_TREE_ITEM:
450 case ATK_ROLE_WINDOW:
452 case ATK_ROLE_UNKNOWN:
454 #if ATK_CHECK_VERSION(2, 11, 3)
455 case ATK_ROLE_ARTICLE:
459 case ATK_ROLE_BLOCK_QUOTE:
460 return "AXBlockquote";
461 case ATK_ROLE_DEFINITION:
462 return "AXDefinition";
465 case ATK_ROLE_MARQUEE:
474 #if ATK_CHECK_VERSION(2, 11, 4)
475 case ATK_ROLE_DESCRIPTION_LIST:
476 return "AXDescriptionList";
477 case ATK_ROLE_DESCRIPTION_TERM:
478 return "AXDescriptionTerm";
479 case ATK_ROLE_DESCRIPTION_VALUE:
480 return "AXDescriptionValue";
483 // We want to distinguish ATK_ROLE_UNKNOWN from a known AtkRole which
484 // our DRT isn't properly handling.
485 return "FIXME not identified";
489 String attributesOfElement(AccessibilityUIElement* element)
491 StringBuilder builder;
493 builder.append(String::format("%s\n", element->role()->string().utf8().data()));
495 // For the parent we print its role and its name, if available.
496 builder.append("AXParent: ");
497 RefPtr<AccessibilityUIElement> parent = element->parentElement();
498 AtkObject* atkParent = parent ? parent->platformUIElement().get() : nullptr;
500 builder.append(roleToString(atkParent));
501 const char* parentName = atk_object_get_name(atkParent);
502 if (parentName && g_utf8_strlen(parentName, -1))
503 builder.append(String::format(": %s", parentName));
505 builder.append("(null)");
506 builder.append("\n");
508 builder.append(String::format("AXChildren: %d\n", element->childrenCount()));
509 builder.append(String::format("AXPosition: { %f, %f }\n", element->x(), element->y()));
510 builder.append(String::format("AXSize: { %f, %f }\n", element->width(), element->height()));
512 String title = element->title()->string();
513 if (!title.isEmpty())
514 builder.append(String::format("%s\n", title.utf8().data()));
516 String description = element->description()->string();
517 if (!description.isEmpty())
518 builder.append(String::format("%s\n", description.utf8().data()));
520 String value = element->stringValue()->string();
521 if (!value.isEmpty())
522 builder.append(String::format("%s\n", value.utf8().data()));
524 builder.append(String::format("AXFocusable: %d\n", element->isFocusable()));
525 builder.append(String::format("AXFocused: %d\n", element->isFocused()));
526 builder.append(String::format("AXSelectable: %d\n", element->isSelectable()));
527 builder.append(String::format("AXSelected: %d\n", element->isSelected()));
528 builder.append(String::format("AXMultiSelectable: %d\n", element->isMultiSelectable()));
529 builder.append(String::format("AXEnabled: %d\n", element->isEnabled()));
530 builder.append(String::format("AXExpanded: %d\n", element->isExpanded()));
531 builder.append(String::format("AXRequired: %d\n", element->isRequired()));
532 builder.append(String::format("AXChecked: %d\n", element->isChecked()));
534 String url = element->url()->string();
536 builder.append(String::format("%s\n", url.utf8().data()));
538 // We append the ATK specific attributes as a single line at the end.
539 builder.append("AXPlatformAttributes: ");
540 builder.append(getAtkAttributeSetAsString(element->platformUIElement().get(), ObjectAttributeType));
542 return builder.toString();
545 static JSRetainPtr<JSStringRef> createStringWithAttributes(const Vector<RefPtr<AccessibilityUIElement> >& elements)
547 StringBuilder builder;
549 for (Vector<RefPtr<AccessibilityUIElement> >::const_iterator it = elements.begin(); it != elements.end(); ++it) {
550 builder.append(attributesOfElement(const_cast<AccessibilityUIElement*>(it->get())));
551 builder.append("\n------------\n");
554 return JSStringCreateWithUTF8CString(builder.toString().utf8().data());
557 static Vector<RefPtr<AccessibilityUIElement> > getRowHeaders(AtkTable* accessible)
559 Vector<RefPtr<AccessibilityUIElement> > rowHeaders;
561 int rowsCount = atk_table_get_n_rows(accessible);
562 for (int row = 0; row < rowsCount; ++row)
563 rowHeaders.append(AccessibilityUIElement::create(atk_table_get_row_header(accessible, row)));
568 static Vector<RefPtr<AccessibilityUIElement> > getColumnHeaders(AtkTable* accessible)
570 Vector<RefPtr<AccessibilityUIElement> > columnHeaders;
572 int columnsCount = atk_table_get_n_columns(accessible);
573 for (int column = 0; column < columnsCount; ++column)
574 columnHeaders.append(AccessibilityUIElement::create(atk_table_get_column_header(accessible, column)));
576 return columnHeaders;
579 static Vector<RefPtr<AccessibilityUIElement> > getVisibleCells(AccessibilityUIElement* element)
581 Vector<RefPtr<AccessibilityUIElement> > visibleCells;
583 AtkTable* accessible = ATK_TABLE(element->platformUIElement().get());
584 int rowsCount = atk_table_get_n_rows(accessible);
585 int columnsCount = atk_table_get_n_columns(accessible);
587 for (int row = 0; row < rowsCount; ++row) {
588 for (int column = 0; column < columnsCount; ++column)
589 visibleCells.append(element->cellForColumnAndRow(column, row));
595 #if ATK_CHECK_VERSION(2,11,90)
596 static Vector<RefPtr<AccessibilityUIElement>> convertGPtrArrayToVector(const GPtrArray* array)
598 Vector<RefPtr<AccessibilityUIElement>> cells;
599 for (guint i = 0; i < array->len; i++) {
600 if (AtkObject* atkObject = static_cast<AtkObject*>(g_ptr_array_index(array, i)))
601 cells.append(AccessibilityUIElement::create(atkObject));
606 static JSValueRef convertToJSObjectArray(const Vector<RefPtr<AccessibilityUIElement>>& children)
608 WKBundleFrameRef mainFrame = WKBundlePageGetMainFrame(InjectedBundle::shared().page()->page());
609 JSContextRef context = WKBundleFrameGetJavaScriptContext(mainFrame);
611 size_t elementCount = children.size();
612 auto valueElements = std::make_unique<JSValueRef[]>(elementCount);
613 for (size_t i = 0; i < elementCount; i++)
614 valueElements[i] = JSObjectMake(context, children[i]->wrapperClass(), children[i].get());
616 return JSObjectMakeArray(context, elementCount, valueElements.get(), nullptr);
620 #if ATK_CHECK_VERSION(2,11,92)
621 static double rangeMinMaxValue(AtkValue* atkValue, RangeLimit rangeLimit)
623 AtkRange* range = atk_value_get_range(atkValue);
627 double rangeValue = 0;
628 switch (rangeLimit) {
629 case RangeLimitMinimum:
630 rangeValue = atk_range_get_lower_limit(range);
632 case RangeLimitMaximum:
633 rangeValue = atk_range_get_upper_limit(range);
637 atk_range_free(range);
644 AccessibilityUIElement::AccessibilityUIElement(PlatformUIElement element)
649 AccessibilityUIElement::AccessibilityUIElement(const AccessibilityUIElement& other)
651 , m_element(other.m_element)
655 AccessibilityUIElement::~AccessibilityUIElement()
659 bool AccessibilityUIElement::isEqual(AccessibilityUIElement* otherElement)
661 return m_element == otherElement->platformUIElement();
664 void AccessibilityUIElement::getChildren(Vector<RefPtr<AccessibilityUIElement> >& children)
666 if (!ATK_IS_OBJECT(m_element.get()))
669 int count = childrenCount();
670 for (int i = 0; i < count; i++) {
671 GRefPtr<AtkObject> child = adoptGRef(atk_object_ref_accessible_child(ATK_OBJECT(m_element.get()), i));
672 children.append(AccessibilityUIElement::create(child.get()));
676 void AccessibilityUIElement::getChildrenWithRange(Vector<RefPtr<AccessibilityUIElement> >& children, unsigned location, unsigned length)
678 if (!ATK_IS_OBJECT(m_element.get()))
680 unsigned end = location + length;
681 for (unsigned i = location; i < end; i++) {
682 GRefPtr<AtkObject> child = adoptGRef(atk_object_ref_accessible_child(ATK_OBJECT(m_element.get()), i));
683 children.append(AccessibilityUIElement::create(child.get()));
687 int AccessibilityUIElement::childrenCount()
689 if (!ATK_IS_OBJECT(m_element.get()))
692 return atk_object_get_n_accessible_children(ATK_OBJECT(m_element.get()));
695 PassRefPtr<AccessibilityUIElement> AccessibilityUIElement::elementAtPoint(int x, int y)
697 if (!ATK_IS_COMPONENT(m_element.get()))
700 GRefPtr<AtkObject> objectAtPoint = adoptGRef(atk_component_ref_accessible_at_point(ATK_COMPONENT(m_element.get()), x, y, ATK_XY_WINDOW));
701 return AccessibilityUIElement::create(objectAtPoint ? objectAtPoint.get() : m_element.get());
704 unsigned AccessibilityUIElement::indexOfChild(AccessibilityUIElement* element)
706 if (!ATK_IS_OBJECT(m_element.get()))
709 Vector<RefPtr<AccessibilityUIElement> > children;
710 getChildren(children);
712 unsigned count = children.size();
713 for (unsigned i = 0; i < count; i++)
714 if (children[i]->isEqual(element))
720 PassRefPtr<AccessibilityUIElement> AccessibilityUIElement::childAtIndex(unsigned index)
722 if (!ATK_IS_OBJECT(m_element.get()))
725 Vector<RefPtr<AccessibilityUIElement> > children;
726 getChildrenWithRange(children, index, 1);
728 if (children.size() == 1)
734 static PassRefPtr<AccessibilityUIElement> accessibilityElementAtIndex(AtkObject* element, AtkRelationType relationType, unsigned index)
736 if (!ATK_IS_OBJECT(element))
739 AtkRelationSet* relationSet = atk_object_ref_relation_set(element);
743 AtkRelation* relation = atk_relation_set_get_relation_by_type(relationSet, relationType);
747 GPtrArray* targetList = atk_relation_get_target(relation);
748 if (!targetList || !targetList->len || index >= targetList->len)
751 AtkObject* target = static_cast<AtkObject*>(g_ptr_array_index(targetList, index));
752 g_object_unref(relationSet);
754 return target ? AccessibilityUIElement::create(target) : nullptr;
757 PassRefPtr<AccessibilityUIElement> AccessibilityUIElement::linkedUIElementAtIndex(unsigned index)
763 PassRefPtr<AccessibilityUIElement> AccessibilityUIElement::ariaOwnsElementAtIndex(unsigned index)
769 PassRefPtr<AccessibilityUIElement> AccessibilityUIElement::ariaFlowToElementAtIndex(unsigned index)
771 return accessibilityElementAtIndex(m_element.get(), ATK_RELATION_FLOWS_TO, index);
774 PassRefPtr<AccessibilityUIElement> AccessibilityUIElement::ariaControlsElementAtIndex(unsigned index)
776 return accessibilityElementAtIndex(m_element.get(), ATK_RELATION_CONTROLLER_FOR, index);
779 PassRefPtr<AccessibilityUIElement> AccessibilityUIElement::disclosedRowAtIndex(unsigned index)
785 PassRefPtr<AccessibilityUIElement> AccessibilityUIElement::rowAtIndex(unsigned index)
791 PassRefPtr<AccessibilityUIElement> AccessibilityUIElement::selectedChildAtIndex(unsigned index) const
793 if (!ATK_SELECTION(m_element.get()))
796 GRefPtr<AtkObject> child = adoptGRef(atk_selection_ref_selection(ATK_SELECTION(m_element.get()), index));
797 return child ? AccessibilityUIElement::create(child.get()) : nullptr;
800 unsigned AccessibilityUIElement::selectedChildrenCount() const
802 if (!ATK_IS_SELECTION(m_element.get()))
804 return atk_selection_get_selection_count(ATK_SELECTION(m_element.get()));
807 PassRefPtr<AccessibilityUIElement> AccessibilityUIElement::selectedRowAtIndex(unsigned index)
813 PassRefPtr<AccessibilityUIElement> AccessibilityUIElement::titleUIElement()
815 if (!ATK_IS_OBJECT(m_element.get()))
818 AtkRelationSet* set = atk_object_ref_relation_set(ATK_OBJECT(m_element.get()));
822 AtkObject* target = nullptr;
823 int count = atk_relation_set_get_n_relations(set);
824 for (int i = 0; i < count; i++) {
825 AtkRelation* relation = atk_relation_set_get_relation(set, i);
826 if (atk_relation_get_relation_type(relation) == ATK_RELATION_LABELLED_BY) {
827 GPtrArray* targetList = atk_relation_get_target(relation);
829 target = static_cast<AtkObject*>(g_ptr_array_index(targetList, 0));
834 return target ? AccessibilityUIElement::create(target) : nullptr;
837 PassRefPtr<AccessibilityUIElement> AccessibilityUIElement::parentElement()
839 if (!ATK_IS_OBJECT(m_element.get()))
842 AtkObject* parent = atk_object_get_parent(ATK_OBJECT(m_element.get()));
843 return parent ? AccessibilityUIElement::create(parent) : nullptr;
846 PassRefPtr<AccessibilityUIElement> AccessibilityUIElement::disclosedByRow()
852 JSRetainPtr<JSStringRef> AccessibilityUIElement::attributesOfLinkedUIElements()
855 return JSStringCreateWithCharacters(0, 0);
858 JSRetainPtr<JSStringRef> AccessibilityUIElement::attributesOfDocumentLinks()
861 return JSStringCreateWithCharacters(0, 0);
864 JSRetainPtr<JSStringRef> AccessibilityUIElement::attributesOfChildren()
866 if (!ATK_IS_OBJECT(m_element.get()))
867 return JSStringCreateWithCharacters(0, 0);
869 Vector<RefPtr<AccessibilityUIElement> > children;
870 getChildren(children);
872 return createStringWithAttributes(children);
875 JSRetainPtr<JSStringRef> AccessibilityUIElement::allAttributes()
877 if (!ATK_IS_OBJECT(m_element.get()))
878 return JSStringCreateWithCharacters(0, 0);
880 return JSStringCreateWithUTF8CString(attributesOfElement(this).utf8().data());
883 JSRetainPtr<JSStringRef> AccessibilityUIElement::stringAttributeValue(JSStringRef attribute)
885 if (!ATK_IS_OBJECT(m_element.get()))
886 return JSStringCreateWithCharacters(0, 0);
888 String atkAttributeName = coreAttributeToAtkAttribute(attribute);
890 // Try object attributes first.
891 String attributeValue = getAttributeSetValueForId(ATK_OBJECT(m_element.get()), ObjectAttributeType, atkAttributeName);
893 // Try text attributes if the requested one was not found and we have an AtkText object.
894 if (attributeValue.isEmpty() && ATK_IS_TEXT(m_element.get()))
895 attributeValue = getAttributeSetValueForId(ATK_OBJECT(m_element.get()), TextAttributeType, atkAttributeName);
897 // Additional check to make sure that the exposure of the state ATK_STATE_INVALID_ENTRY
898 // is consistent with the exposure of aria-invalid as a text attribute, if present.
899 if (atkAttributeName == attributesMap[InvalidNameIndex][AtkDomain]) {
900 bool isInvalidState = checkElementState(m_element.get(), ATK_STATE_INVALID_ENTRY);
901 if (attributeValue.isEmpty())
902 return JSStringCreateWithUTF8CString(isInvalidState ? "true" : "false");
904 // If the text attribute was there, check that it's consistent with
905 // what the state says or force the test to fail otherwise.
906 bool isAriaInvalid = attributeValue != "false";
907 if (isInvalidState != isAriaInvalid)
908 return JSStringCreateWithCharacters(0, 0);
911 return JSStringCreateWithUTF8CString(attributeValue.utf8().data());
914 double AccessibilityUIElement::numberAttributeValue(JSStringRef attribute)
916 if (!ATK_IS_OBJECT(m_element.get()))
919 String atkAttributeName = coreAttributeToAtkAttribute(attribute);
920 if (atkAttributeName.isEmpty())
923 if (atkAttributeName == "setsize" || atkAttributeName == "posinset") {
924 String attributeValue = getAttributeSetValueForId(ATK_OBJECT(m_element.get()), ObjectAttributeType, atkAttributeName);
925 if (!attributeValue.isEmpty())
926 return attributeValue.toDouble();
932 JSValueRef AccessibilityUIElement::uiElementArrayAttributeValue(JSStringRef attribute) const
938 JSValueRef AccessibilityUIElement::rowHeaders() const
940 #if ATK_CHECK_VERSION(2,11,90)
941 if (!ATK_IS_TABLE_CELL(m_element.get()))
944 GRefPtr<GPtrArray> array = adoptGRef(atk_table_cell_get_row_header_cells(ATK_TABLE_CELL(m_element.get())));
948 Vector<RefPtr<AccessibilityUIElement>> rows = convertGPtrArrayToVector(array.get());
949 return convertToJSObjectArray(rows);
955 JSValueRef AccessibilityUIElement::columnHeaders() const
957 #if ATK_CHECK_VERSION(2,11,90)
958 if (!ATK_IS_TABLE_CELL(m_element.get()))
961 GRefPtr<GPtrArray> array = adoptGRef(atk_table_cell_get_column_header_cells(ATK_TABLE_CELL(m_element.get())));
965 Vector<RefPtr<AccessibilityUIElement>> columns = convertGPtrArrayToVector(array.get());
966 return convertToJSObjectArray(columns);
972 PassRefPtr<AccessibilityUIElement> AccessibilityUIElement::uiElementAttributeValue(JSStringRef attribute) const
978 bool AccessibilityUIElement::boolAttributeValue(JSStringRef attribute)
984 bool AccessibilityUIElement::isAttributeSettable(JSStringRef attribute)
986 if (!ATK_IS_OBJECT(m_element.get()))
989 String attributeString = jsStringToWTFString(attribute);
990 if (attributeString == "AXValue")
991 return checkElementState(m_element.get(), ATK_STATE_EDITABLE);
996 bool AccessibilityUIElement::isAttributeSupported(JSStringRef attribute)
998 if (!ATK_IS_OBJECT(m_element.get()))
1001 String atkAttributeName = coreAttributeToAtkAttribute(attribute);
1002 if (atkAttributeName.isEmpty())
1005 // For now, an attribute is supported whether it's exposed as a object or a text attribute.
1006 String attributeValue = getAttributeSetValueForId(ATK_OBJECT(m_element.get()), ObjectAttributeType, atkAttributeName);
1007 if (attributeValue.isEmpty())
1008 attributeValue = getAttributeSetValueForId(ATK_OBJECT(m_element.get()), TextAttributeType, atkAttributeName);
1010 return !attributeValue.isEmpty();
1013 JSRetainPtr<JSStringRef> AccessibilityUIElement::parameterizedAttributeNames()
1016 return JSStringCreateWithCharacters(0, 0);
1019 JSRetainPtr<JSStringRef> AccessibilityUIElement::role()
1021 if (!ATK_IS_OBJECT(m_element.get()))
1022 return JSStringCreateWithCharacters(0, 0);
1024 GUniquePtr<char> roleStringWithPrefix(g_strdup_printf("AXRole: %s", roleToString(ATK_OBJECT(m_element.get()))));
1025 return JSStringCreateWithUTF8CString(roleStringWithPrefix.get());
1028 JSRetainPtr<JSStringRef> AccessibilityUIElement::subrole()
1031 return JSStringCreateWithCharacters(0, 0);
1034 JSRetainPtr<JSStringRef> AccessibilityUIElement::roleDescription()
1037 return JSStringCreateWithCharacters(0, 0);
1040 JSRetainPtr<JSStringRef> AccessibilityUIElement::computedRoleString()
1042 // FIXME: implement http://webkit.org/b/128420
1043 return JSStringCreateWithCharacters(0, 0);
1046 JSRetainPtr<JSStringRef> AccessibilityUIElement::title()
1048 if (!ATK_IS_OBJECT(m_element.get()))
1049 return JSStringCreateWithCharacters(0, 0);
1051 const gchar* name = atk_object_get_name(ATK_OBJECT(m_element.get()));
1052 GUniquePtr<gchar> axTitle(g_strdup_printf("AXTitle: %s", name ? name : ""));
1054 return JSStringCreateWithUTF8CString(axTitle.get());
1057 JSRetainPtr<JSStringRef> AccessibilityUIElement::description()
1059 if (!ATK_IS_OBJECT(m_element.get()))
1060 return JSStringCreateWithCharacters(0, 0);
1062 const gchar* description = atk_object_get_description(ATK_OBJECT(m_element.get()));
1064 return JSStringCreateWithCharacters(0, 0);
1066 GUniquePtr<gchar> axDesc(g_strdup_printf("AXDescription: %s", description));
1068 return JSStringCreateWithUTF8CString(axDesc.get());
1071 JSRetainPtr<JSStringRef> AccessibilityUIElement::orientation() const
1073 if (!ATK_IS_OBJECT(m_element.get()))
1074 return JSStringCreateWithCharacters(0, 0);
1076 const gchar* axOrientation = nullptr;
1077 if (checkElementState(m_element.get(), ATK_STATE_HORIZONTAL))
1078 axOrientation = "AXOrientation: AXHorizontalOrientation";
1079 else if (checkElementState(m_element.get(), ATK_STATE_VERTICAL))
1080 axOrientation = "AXOrientation: AXVerticalOrientation";
1083 return JSStringCreateWithCharacters(0, 0);
1085 return JSStringCreateWithUTF8CString(axOrientation);
1088 JSRetainPtr<JSStringRef> AccessibilityUIElement::stringValue()
1090 if (!ATK_IS_TEXT(m_element.get()))
1091 return JSStringCreateWithCharacters(0, 0);
1093 GUniquePtr<gchar> text(atk_text_get_text(ATK_TEXT(m_element.get()), 0, -1));
1094 GUniquePtr<gchar> textWithReplacedCharacters(replaceCharactersForResults(text.get()));
1095 GUniquePtr<gchar> axValue(g_strdup_printf("AXValue: %s", textWithReplacedCharacters.get()));
1097 return JSStringCreateWithUTF8CString(axValue.get());
1100 JSRetainPtr<JSStringRef> AccessibilityUIElement::language()
1102 if (!ATK_IS_OBJECT(m_element.get()))
1103 return JSStringCreateWithCharacters(0, 0);
1105 const gchar* locale = atk_object_get_object_locale(ATK_OBJECT(m_element.get()));
1107 return JSStringCreateWithCharacters(0, 0);
1109 GUniquePtr<char> axValue(g_strdup_printf("AXLanguage: %s", locale));
1110 return JSStringCreateWithUTF8CString(axValue.get());
1113 JSRetainPtr<JSStringRef> AccessibilityUIElement::helpText() const
1115 if (!ATK_IS_OBJECT(m_element.get()))
1116 return JSStringCreateWithCharacters(0, 0);
1118 AtkRelationSet* relationSet = atk_object_ref_relation_set(ATK_OBJECT(m_element.get()));
1122 AtkRelation* relation = atk_relation_set_get_relation_by_type(relationSet, ATK_RELATION_DESCRIBED_BY);
1126 GPtrArray* targetList = atk_relation_get_target(relation);
1127 if (!targetList || !targetList->len)
1130 StringBuilder builder;
1131 builder.append("AXHelp: ");
1133 for (int targetCount = 0; targetCount < targetList->len; targetCount++) {
1134 if (AtkObject* target = static_cast<AtkObject*>(g_ptr_array_index(targetList, targetCount))) {
1135 GUniquePtr<gchar> text(atk_text_get_text(ATK_TEXT(target), 0, -1));
1137 builder.append(" ");
1138 builder.append(text.get());
1142 g_object_unref(relationSet);
1144 return JSStringCreateWithUTF8CString(builder.toString().utf8().data());
1147 double AccessibilityUIElement::x()
1149 if (!ATK_IS_COMPONENT(m_element.get()))
1153 #if ATK_CHECK_VERSION(2,11,90)
1154 atk_component_get_extents(ATK_COMPONENT(m_element.get()), &x, nullptr, nullptr, nullptr, ATK_XY_SCREEN);
1156 atk_component_get_position(ATK_COMPONENT(m_element.get()), &x, nullptr, ATK_XY_SCREEN);
1161 double AccessibilityUIElement::y()
1163 if (!ATK_IS_COMPONENT(m_element.get()))
1167 #if ATK_CHECK_VERSION(2,11,90)
1168 atk_component_get_extents(ATK_COMPONENT(m_element.get()), nullptr, &y, nullptr, nullptr, ATK_XY_SCREEN);
1170 atk_component_get_position(ATK_COMPONENT(m_element.get()), nullptr, &y, ATK_XY_SCREEN);
1175 double AccessibilityUIElement::width()
1177 if (!ATK_IS_COMPONENT(m_element.get()))
1181 #if ATK_CHECK_VERSION(2,11,90)
1182 atk_component_get_extents(ATK_COMPONENT(m_element.get()), nullptr, nullptr, &width, nullptr, ATK_XY_WINDOW);
1184 atk_component_get_size(ATK_COMPONENT(m_element.get()), &width, nullptr);
1189 double AccessibilityUIElement::height()
1191 if (!ATK_IS_COMPONENT(m_element.get()))
1195 #if ATK_CHECK_VERSION(2,11,90)
1196 atk_component_get_extents(ATK_COMPONENT(m_element.get()), nullptr, nullptr, nullptr, &height, ATK_XY_WINDOW);
1198 atk_component_get_size(ATK_COMPONENT(m_element.get()), nullptr, &height);
1203 double AccessibilityUIElement::clickPointX()
1205 if (!ATK_IS_COMPONENT(m_element.get()))
1209 #if ATK_CHECK_VERSION(2,11,90)
1210 atk_component_get_extents(ATK_COMPONENT(m_element.get()), &x, nullptr, &width, nullptr, ATK_XY_WINDOW);
1212 atk_component_get_position(ATK_COMPONENT(m_element.get()), &x, nullptr, ATK_XY_WINDOW);
1213 atk_component_get_size(ATK_COMPONENT(m_element.get()), &width, nullptr);
1216 return x + width / 2.0;
1219 double AccessibilityUIElement::clickPointY()
1221 if (!ATK_IS_COMPONENT(m_element.get()))
1225 #if ATK_CHECK_VERSION(2,11,90)
1226 atk_component_get_extents(ATK_COMPONENT(m_element.get()), nullptr, &y, nullptr, &height, ATK_XY_WINDOW);
1228 atk_component_get_position(ATK_COMPONENT(m_element.get()), nullptr, &y, ATK_XY_WINDOW);
1229 atk_component_get_size(ATK_COMPONENT(m_element.get()), nullptr, &height);
1232 return y + height / 2.0;
1235 double AccessibilityUIElement::intValue() const
1237 if (!ATK_IS_OBJECT(m_element.get()))
1240 if (ATK_IS_VALUE(m_element.get())) {
1241 #if ATK_CHECK_VERSION(2,11,92)
1243 atk_value_get_value_and_text(ATK_VALUE(m_element.get()), &value, nullptr);
1246 GValue value = G_VALUE_INIT;
1247 atk_value_get_current_value(ATK_VALUE(m_element.get()), &value);
1248 if (!G_VALUE_HOLDS_FLOAT(&value))
1250 return g_value_get_float(&value);
1254 // Consider headings as an special case when returning the "int value" of
1255 // an AccessibilityUIElement, so we can reuse some tests to check the level
1256 // both for HTML headings and objects with the aria-level attribute.
1257 if (atk_object_get_role(ATK_OBJECT(m_element.get())) == ATK_ROLE_HEADING) {
1258 String headingLevel = getAttributeSetValueForId(ATK_OBJECT(m_element.get()), ObjectAttributeType, "level");
1260 double headingLevelValue = headingLevel.toDouble(&ok);
1262 return headingLevelValue;
1268 double AccessibilityUIElement::minValue()
1270 if (!ATK_IS_VALUE(m_element.get()))
1272 #if ATK_CHECK_VERSION(2,11,92)
1273 return rangeMinMaxValue(ATK_VALUE(m_element.get()), RangeLimitMinimum);
1275 GValue value = G_VALUE_INIT;
1276 atk_value_get_minimum_value(ATK_VALUE(m_element.get()), &value);
1277 if (!G_VALUE_HOLDS_FLOAT(&value))
1280 return g_value_get_float(&value);
1284 double AccessibilityUIElement::maxValue()
1286 if (!ATK_IS_VALUE(m_element.get()))
1289 #if ATK_CHECK_VERSION(2,11,92)
1290 return rangeMinMaxValue(ATK_VALUE(m_element.get()), RangeLimitMaximum);
1292 GValue value = G_VALUE_INIT;
1293 atk_value_get_maximum_value(ATK_VALUE(m_element.get()), &value);
1294 if (!G_VALUE_HOLDS_FLOAT(&value))
1297 return g_value_get_float(&value);
1301 JSRetainPtr<JSStringRef> AccessibilityUIElement::valueDescription()
1304 return JSStringCreateWithCharacters(0, 0);
1307 int AccessibilityUIElement::insertionPointLineNumber()
1313 bool AccessibilityUIElement::isPressActionSupported()
1315 if (!ATK_IS_ACTION(m_element.get()))
1318 const gchar* actionName = atk_action_get_name(ATK_ACTION(m_element.get()), 0);
1319 return equalIgnoringCase(actionName, String("press")) || equalIgnoringCase(actionName, String("jump"));
1322 bool AccessibilityUIElement::isIncrementActionSupported()
1328 bool AccessibilityUIElement::isDecrementActionSupported()
1334 bool AccessibilityUIElement::isEnabled()
1336 return checkElementState(m_element.get(), ATK_STATE_ENABLED);
1339 bool AccessibilityUIElement::isRequired() const
1341 return checkElementState(m_element.get(), ATK_STATE_REQUIRED);
1344 bool AccessibilityUIElement::isFocused() const
1346 return checkElementState(m_element.get(), ATK_STATE_FOCUSED);
1349 bool AccessibilityUIElement::isSelected() const
1351 return checkElementState(m_element.get(), ATK_STATE_SELECTED);
1354 bool AccessibilityUIElement::isSelectedOptionActive() const
1356 return checkElementState(m_element.get(), ATK_STATE_ACTIVE);
1359 bool AccessibilityUIElement::isExpanded() const
1361 return checkElementState(m_element.get(), ATK_STATE_EXPANDED);
1364 bool AccessibilityUIElement::isChecked() const
1366 return checkElementState(m_element.get(), ATK_STATE_CHECKED);
1369 bool AccessibilityUIElement::isIndeterminate() const
1371 return checkElementState(m_element.get(), ATK_STATE_INDETERMINATE);
1374 int AccessibilityUIElement::hierarchicalLevel() const
1380 JSRetainPtr<JSStringRef> AccessibilityUIElement::speak()
1383 return JSStringCreateWithCharacters(0, 0);
1386 bool AccessibilityUIElement::ariaIsGrabbed() const
1392 JSRetainPtr<JSStringRef> AccessibilityUIElement::ariaDropEffects() const
1395 return JSStringCreateWithCharacters(0, 0);
1398 // parameterized attributes
1399 int AccessibilityUIElement::lineForIndex(int index)
1401 if (!ATK_IS_TEXT(m_element.get()))
1404 if (index < 0 || index > atk_text_get_character_count(ATK_TEXT(m_element.get())))
1407 GUniquePtr<gchar> text(atk_text_get_text(ATK_TEXT(m_element.get()), 0, index));
1409 for (gchar* offset = text.get(); *offset; ++offset) {
1410 if (*offset == '\n')
1417 JSRetainPtr<JSStringRef> AccessibilityUIElement::rangeForLine(int line)
1420 return JSStringCreateWithCharacters(0, 0);
1423 JSRetainPtr<JSStringRef> AccessibilityUIElement::rangeForPosition(int x, int y)
1426 return JSStringCreateWithCharacters(0, 0);
1429 JSRetainPtr<JSStringRef> AccessibilityUIElement::boundsForRange(unsigned location, unsigned length)
1432 return JSStringCreateWithCharacters(0, 0);
1435 JSRetainPtr<JSStringRef> AccessibilityUIElement::stringForRange(unsigned location, unsigned length)
1437 if (!ATK_IS_TEXT(m_element.get()))
1438 return JSStringCreateWithCharacters(0, 0);
1440 String string = atk_text_get_text(ATK_TEXT(m_element.get()), location, location + length);
1441 return JSStringCreateWithUTF8CString(string.utf8().data());
1444 JSRetainPtr<JSStringRef> AccessibilityUIElement::attributedStringForRange(unsigned location, unsigned length)
1447 return JSStringCreateWithCharacters(0, 0);
1450 bool AccessibilityUIElement::attributedStringRangeIsMisspelled(unsigned location, unsigned length)
1456 unsigned AccessibilityUIElement::uiElementCountForSearchPredicate(JSContextRef context, AccessibilityUIElement* startElement, bool isDirectionNext, JSValueRef searchKey, JSStringRef searchText, bool visibleOnly, bool immediateDescendantsOnly)
1462 PassRefPtr<AccessibilityUIElement> AccessibilityUIElement::uiElementForSearchPredicate(JSContextRef context, AccessibilityUIElement* startElement, bool isDirectionNext, JSValueRef searchKey, JSStringRef searchText, bool visibleOnly, bool immediateDescendantsOnly)
1468 JSRetainPtr<JSStringRef> AccessibilityUIElement::selectTextWithCriteria(JSContextRef context, JSStringRef ambiguityResolution, JSValueRef searchStrings, JSStringRef replacementString, JSStringRef activity)
1474 JSRetainPtr<JSStringRef> AccessibilityUIElement::attributesOfColumnHeaders()
1476 if (!ATK_IS_TABLE(m_element.get()))
1477 return JSStringCreateWithCharacters(0, 0);
1479 Vector<RefPtr<AccessibilityUIElement> > columnHeaders = getColumnHeaders(ATK_TABLE(m_element.get()));
1480 return createStringWithAttributes(columnHeaders);
1483 JSRetainPtr<JSStringRef> AccessibilityUIElement::attributesOfRowHeaders()
1485 if (!ATK_IS_TABLE(m_element.get()))
1486 return JSStringCreateWithCharacters(0, 0);
1488 Vector<RefPtr<AccessibilityUIElement> > rowHeaders = getRowHeaders(ATK_TABLE(m_element.get()));
1489 return createStringWithAttributes(rowHeaders);
1492 JSRetainPtr<JSStringRef> AccessibilityUIElement::attributesOfColumns()
1495 return JSStringCreateWithCharacters(0, 0);
1498 JSRetainPtr<JSStringRef> AccessibilityUIElement::attributesOfRows()
1501 return JSStringCreateWithCharacters(0, 0);
1504 JSRetainPtr<JSStringRef> AccessibilityUIElement::attributesOfVisibleCells()
1506 if (!ATK_IS_TABLE(m_element.get()))
1507 return JSStringCreateWithCharacters(0, 0);
1509 Vector<RefPtr<AccessibilityUIElement> > visibleCells = getVisibleCells(this);
1510 return createStringWithAttributes(visibleCells);
1513 JSRetainPtr<JSStringRef> AccessibilityUIElement::attributesOfHeader()
1516 return JSStringCreateWithCharacters(0, 0);
1519 int AccessibilityUIElement::rowCount()
1521 if (!ATK_IS_TABLE(m_element.get()))
1524 return atk_table_get_n_rows(ATK_TABLE(m_element.get()));
1527 int AccessibilityUIElement::columnCount()
1529 if (!ATK_IS_TABLE(m_element.get()))
1532 return atk_table_get_n_columns(ATK_TABLE(m_element.get()));
1535 int AccessibilityUIElement::indexInTable()
1541 JSRetainPtr<JSStringRef> AccessibilityUIElement::rowIndexRange()
1543 // Range in table for rows.
1544 return indexRangeInTable(m_element.get(), true);
1547 JSRetainPtr<JSStringRef> AccessibilityUIElement::columnIndexRange()
1549 // Range in table for columns.
1550 return indexRangeInTable(m_element.get(), false);
1553 PassRefPtr<AccessibilityUIElement> AccessibilityUIElement::cellForColumnAndRow(unsigned col, unsigned row)
1555 if (!ATK_IS_TABLE(m_element.get()))
1558 // Adopt the AtkObject representing the cell because
1559 // at_table_ref_at() transfers full ownership.
1560 GRefPtr<AtkObject> foundCell = adoptGRef(atk_table_ref_at(ATK_TABLE(m_element.get()), row, col));
1561 return foundCell ? AccessibilityUIElement::create(foundCell.get()) : nullptr;
1564 PassRefPtr<AccessibilityUIElement> AccessibilityUIElement::horizontalScrollbar() const
1570 PassRefPtr<AccessibilityUIElement> AccessibilityUIElement::verticalScrollbar() const
1576 JSRetainPtr<JSStringRef> AccessibilityUIElement::selectedTextRange()
1578 if (!ATK_IS_TEXT(m_element.get()))
1579 return JSStringCreateWithCharacters(0, 0);
1582 g_free(atk_text_get_selection(ATK_TEXT(m_element.get()), 0, &start, &end));
1584 GUniquePtr<gchar> selection(g_strdup_printf("{%d, %d}", start, end - start));
1585 return JSStringCreateWithUTF8CString(selection.get());
1588 bool AccessibilityUIElement::setSelectedTextRange(unsigned location, unsigned length)
1590 if (!ATK_IS_TEXT(m_element.get()))
1594 return atk_text_set_caret_offset(ATK_TEXT(m_element.get()), location);
1596 return atk_text_set_selection(ATK_TEXT(m_element.get()), 0, location, location + length);
1599 void AccessibilityUIElement::increment()
1601 alterCurrentValue(m_element.get(), 1);
1604 void AccessibilityUIElement::decrement()
1606 alterCurrentValue(m_element.get(), -1);
1609 void AccessibilityUIElement::showMenu()
1614 void AccessibilityUIElement::press()
1616 if (!ATK_IS_ACTION(m_element.get()))
1619 // Only one action per object is supported so far.
1620 atk_action_do_action(ATK_ACTION(m_element.get()), 0);
1623 void AccessibilityUIElement::setSelectedChild(AccessibilityUIElement* element) const
1628 void AccessibilityUIElement::setSelectedChildAtIndex(unsigned index) const
1630 if (!ATK_IS_SELECTION(m_element.get()))
1633 atk_selection_add_selection(ATK_SELECTION(m_element.get()), index);
1636 void AccessibilityUIElement::removeSelectionAtIndex(unsigned index) const
1638 if (!ATK_IS_SELECTION(m_element.get()))
1641 atk_selection_remove_selection(ATK_SELECTION(m_element.get()), index);
1644 JSRetainPtr<JSStringRef> AccessibilityUIElement::accessibilityValue() const
1647 return JSStringCreateWithCharacters(0, 0);
1650 JSRetainPtr<JSStringRef> AccessibilityUIElement::documentEncoding()
1652 if (!ATK_IS_DOCUMENT(m_element.get()))
1653 return JSStringCreateWithCharacters(0, 0);
1655 AtkRole role = atk_object_get_role(ATK_OBJECT(m_element.get()));
1656 if (role != ATK_ROLE_DOCUMENT_FRAME)
1657 return JSStringCreateWithCharacters(0, 0);
1659 return JSStringCreateWithUTF8CString(atk_document_get_attribute_value(ATK_DOCUMENT(m_element.get()), "Encoding"));
1662 JSRetainPtr<JSStringRef> AccessibilityUIElement::documentURI()
1664 if (!ATK_IS_DOCUMENT(m_element.get()))
1665 return JSStringCreateWithCharacters(0, 0);
1667 AtkRole role = atk_object_get_role(ATK_OBJECT(m_element.get()));
1668 if (role != ATK_ROLE_DOCUMENT_FRAME)
1669 return JSStringCreateWithCharacters(0, 0);
1671 return JSStringCreateWithUTF8CString(atk_document_get_attribute_value(ATK_DOCUMENT(m_element.get()), "URI"));
1674 JSRetainPtr<JSStringRef> AccessibilityUIElement::url()
1676 if (!ATK_IS_HYPERLINK_IMPL(m_element.get()))
1677 return JSStringCreateWithCharacters(0, 0);
1679 AtkHyperlink* hyperlink = atk_hyperlink_impl_get_hyperlink(ATK_HYPERLINK_IMPL(m_element.get()));
1680 GUniquePtr<char> hyperlinkURI(atk_hyperlink_get_uri(hyperlink, 0));
1682 // Build the result string, stripping the absolute URL paths if present.
1683 char* localURI = g_strstr_len(hyperlinkURI.get(), -1, "LayoutTests");
1684 String axURL = String::format("AXURL: %s", localURI ? localURI : hyperlinkURI.get());
1685 return JSStringCreateWithUTF8CString(axURL.utf8().data());
1688 bool AccessibilityUIElement::addNotificationListener(JSValueRef functionCallback)
1690 if (!functionCallback)
1693 // Only one notification listener per element.
1694 if (m_notificationHandler)
1697 m_notificationHandler = AccessibilityNotificationHandler::create();
1698 m_notificationHandler->setPlatformElement(platformUIElement());
1699 m_notificationHandler->setNotificationFunctionCallback(functionCallback);
1704 bool AccessibilityUIElement::removeNotificationListener()
1706 // Programmers should not be trying to remove a listener that's already removed.
1707 ASSERT(m_notificationHandler);
1708 m_notificationHandler = nullptr;
1713 bool AccessibilityUIElement::isFocusable() const
1715 return checkElementState(m_element.get(), ATK_STATE_FOCUSABLE);
1718 bool AccessibilityUIElement::isSelectable() const
1720 return checkElementState(m_element.get(), ATK_STATE_SELECTABLE);
1723 bool AccessibilityUIElement::isMultiSelectable() const
1725 return checkElementState(m_element.get(), ATK_STATE_MULTISELECTABLE);
1728 bool AccessibilityUIElement::isVisible() const
1730 return checkElementState(m_element.get(), ATK_STATE_VISIBLE);
1733 bool AccessibilityUIElement::isOffScreen() const
1739 bool AccessibilityUIElement::isCollapsed() const
1745 bool AccessibilityUIElement::isIgnored() const
1751 bool AccessibilityUIElement::hasPopup() const
1753 if (!ATK_IS_OBJECT(m_element.get()))
1756 String hasPopupValue = getAttributeSetValueForId(ATK_OBJECT(m_element.get()), ObjectAttributeType, "haspopup");
1757 return equalIgnoringCase(hasPopupValue, "true");
1760 void AccessibilityUIElement::takeFocus()
1765 void AccessibilityUIElement::takeSelection()
1770 void AccessibilityUIElement::addSelection()
1775 void AccessibilityUIElement::removeSelection()
1781 PassRefPtr<AccessibilityTextMarkerRange> AccessibilityUIElement::lineTextMarkerRangeForTextMarker(AccessibilityTextMarker* textMarker)
1787 PassRefPtr<AccessibilityTextMarkerRange> AccessibilityUIElement::textMarkerRangeForElement(AccessibilityUIElement* element)
1793 int AccessibilityUIElement::textMarkerRangeLength(AccessibilityTextMarkerRange* range)
1799 PassRefPtr<AccessibilityTextMarker> AccessibilityUIElement::previousTextMarker(AccessibilityTextMarker* textMarker)
1805 PassRefPtr<AccessibilityTextMarker> AccessibilityUIElement::nextTextMarker(AccessibilityTextMarker* textMarker)
1811 JSRetainPtr<JSStringRef> AccessibilityUIElement::stringForTextMarkerRange(AccessibilityTextMarkerRange* markerRange)
1814 return JSStringCreateWithCharacters(0, 0);
1817 PassRefPtr<AccessibilityTextMarkerRange> AccessibilityUIElement::textMarkerRangeForMarkers(AccessibilityTextMarker* startMarker, AccessibilityTextMarker* endMarker)
1823 PassRefPtr<AccessibilityTextMarker> AccessibilityUIElement::startTextMarkerForTextMarkerRange(AccessibilityTextMarkerRange* range)
1829 PassRefPtr<AccessibilityTextMarker> AccessibilityUIElement::endTextMarkerForTextMarkerRange(AccessibilityTextMarkerRange* range)
1835 PassRefPtr<AccessibilityTextMarker> AccessibilityUIElement::endTextMarkerForBounds(int x, int y, int width, int height)
1841 PassRefPtr<AccessibilityTextMarker> AccessibilityUIElement::startTextMarkerForBounds(int x, int y, int width, int height)
1847 PassRefPtr<AccessibilityTextMarker> AccessibilityUIElement::textMarkerForPoint(int x, int y)
1853 PassRefPtr<AccessibilityUIElement> AccessibilityUIElement::accessibilityElementForTextMarker(AccessibilityTextMarker* marker)
1859 bool AccessibilityUIElement::attributedStringForTextMarkerRangeContainsAttribute(JSStringRef attribute, AccessibilityTextMarkerRange* range)
1865 int AccessibilityUIElement::indexForTextMarker(AccessibilityTextMarker* marker)
1871 bool AccessibilityUIElement::isTextMarkerValid(AccessibilityTextMarker* textMarker)
1877 PassRefPtr<AccessibilityTextMarker> AccessibilityUIElement::textMarkerForIndex(int textIndex)
1883 PassRefPtr<AccessibilityTextMarker> AccessibilityUIElement::startTextMarker()
1889 PassRefPtr<AccessibilityTextMarker> AccessibilityUIElement::endTextMarker()
1895 void AccessibilityUIElement::scrollToMakeVisible()
1900 JSRetainPtr<JSStringRef> AccessibilityUIElement::supportedActions() const
1906 JSRetainPtr<JSStringRef> AccessibilityUIElement::pathDescription() const
1912 JSRetainPtr<JSStringRef> AccessibilityUIElement::mathPostscriptsDescription() const
1918 JSRetainPtr<JSStringRef> AccessibilityUIElement::mathPrescriptsDescription() const
1924 JSRetainPtr<JSStringRef> AccessibilityUIElement::classList() const
1930 JSRetainPtr<JSStringRef> stringAtOffset(PlatformUIElement element, AtkTextBoundary boundary, int offset)
1932 if (!ATK_IS_TEXT(element.get()))
1933 return JSStringCreateWithCharacters(0, 0);
1935 gint startOffset, endOffset;
1936 StringBuilder builder;
1938 #if ATK_CHECK_VERSION(2, 10, 0)
1939 AtkTextGranularity granularity;
1941 case ATK_TEXT_BOUNDARY_CHAR:
1942 granularity = ATK_TEXT_GRANULARITY_CHAR;
1944 case ATK_TEXT_BOUNDARY_WORD_START:
1945 granularity = ATK_TEXT_GRANULARITY_WORD;
1947 case ATK_TEXT_BOUNDARY_LINE_START:
1948 granularity = ATK_TEXT_GRANULARITY_LINE;
1950 case ATK_TEXT_BOUNDARY_SENTENCE_START:
1951 granularity = ATK_TEXT_GRANULARITY_SENTENCE;
1954 return JSStringCreateWithCharacters(0, 0);
1957 builder.append(atk_text_get_string_at_offset(ATK_TEXT(element.get()), offset, granularity, &startOffset, &endOffset));
1959 builder.append(atk_text_get_text_at_offset(ATK_TEXT(element.get()), offset, boundary, &startOffset, &endOffset));
1961 builder.append(String::format(", %i, %i", startOffset, endOffset));
1962 return JSStringCreateWithUTF8CString(builder.toString().utf8().data());
1965 JSRetainPtr<JSStringRef> AccessibilityUIElement::characterAtOffset(int offset)
1967 return stringAtOffset(m_element, ATK_TEXT_BOUNDARY_CHAR, offset);
1970 JSRetainPtr<JSStringRef> AccessibilityUIElement::wordAtOffset(int offset)
1972 return stringAtOffset(m_element, ATK_TEXT_BOUNDARY_WORD_START, offset);
1975 JSRetainPtr<JSStringRef> AccessibilityUIElement::lineAtOffset(int offset)
1977 return stringAtOffset(m_element, ATK_TEXT_BOUNDARY_LINE_START, offset);
1980 JSRetainPtr<JSStringRef> AccessibilityUIElement::sentenceAtOffset(int offset)
1982 return stringAtOffset(m_element, ATK_TEXT_BOUNDARY_SENTENCE_START, offset);
1987 #endif // HAVE(ACCESSIBILITY)