[ATK] Expose accessibility objects for <dl>, <dt> and <dd>
[WebKit-https.git] / Tools / WebKitTestRunner / InjectedBundle / atk / AccessibilityUIElementAtk.cpp
1 /*
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.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
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.
14  *
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.
26  */
27
28 #include "config.h"
29 #include "AccessibilityUIElement.h"
30
31 #if HAVE(ACCESSIBILITY)
32
33 #include "InjectedBundle.h"
34 #include "InjectedBundlePage.h"
35 #include "NotImplemented.h"
36 #include <JavaScriptCore/JSStringRef.h>
37 #include <JavaScriptCore/OpaqueJSString.h>
38 #include <atk/atk.h>
39 #include <wtf/Assertions.h>
40 #include <wtf/gobject/GOwnPtr.h>
41 #include <wtf/gobject/GRefPtr.h>
42 #include <wtf/text/CString.h>
43 #include <wtf/text/StringBuilder.h>
44 #include <wtf/text/WTFString.h>
45 #include <wtf/unicode/CharacterNames.h>
46
47 namespace WTR {
48
49 namespace {
50
51 enum AtkAttributeType {
52     ObjectAttributeType,
53     TextAttributeType
54 };
55
56 enum AttributeDomain {
57     CoreDomain = 0,
58     AtkDomain
59 };
60
61 enum AttributesIndex {
62     // Attribute names.
63     InvalidNameIndex = 0,
64     PlaceholderNameIndex,
65     SortNameIndex,
66
67     // Attribute values.
68     SortAscendingValueIndex,
69     SortDescendingValueIndex,
70     SortUnknownValueIndex,
71
72     NumberOfAttributes
73 };
74
75 // Attribute names & Values (keep on sync with enum AttributesIndex).
76 const String attributesMap[][2] = {
77     // Attribute names.
78     { "AXInvalid", "invalid" },
79     { "AXPlaceholderValue", "placeholder-text" } ,
80     { "AXSortDirection", "sort" },
81
82     // Attribute values.
83     { "AXAscendingSortDirection", "ascending" },
84     { "AXDescendingSortDirection", "descending" },
85     { "AXUnknownSortDirection", "unknown" }
86 };
87
88 #if ATK_CHECK_VERSION(2, 11, 3)
89 const char* landmarkStringBanner = "AXLandmarkBanner";
90 const char* landmarkStringComplementary = "AXLandmarkComplementary";
91 const char* landmarkStringContentinfo = "AXLandmarkContentInfo";
92 const char* landmarkStringMain = "AXLandmarkMain";
93 const char* landmarkStringNavigation = "AXLandmarkNavigation";
94 const char* landmarkStringSearch = "AXLandmarkSearch";
95 #endif
96
97 String jsStringToWTFString(JSStringRef attribute)
98 {
99     size_t bufferSize = JSStringGetMaximumUTF8CStringSize(attribute);
100     GOwnPtr<gchar> buffer(static_cast<gchar*>(g_malloc(bufferSize)));
101     JSStringGetUTF8CString(attribute, buffer.get(), bufferSize);
102
103     return String::fromUTF8(buffer.get());
104 }
105
106 String coreAttributeToAtkAttribute(JSStringRef attribute)
107 {
108     String attributeString = jsStringToWTFString(attribute);
109     for (int i = 0; i < NumberOfAttributes; ++i) {
110         if (attributesMap[i][CoreDomain] == attributeString)
111             return attributesMap[i][AtkDomain];
112     }
113
114     return attributeString;
115 }
116
117 String atkAttributeValueToCoreAttributeValue(AtkAttributeType type, const String& id, const String& value)
118 {
119     if (type == ObjectAttributeType) {
120         // We need to translate ATK values exposed for 'aria-sort' (e.g. 'ascending')
121         // into those expected by the layout tests (e.g. 'AXAscendingSortDirection').
122         if (id == attributesMap[SortNameIndex][AtkDomain] && !value.isEmpty()) {
123             if (value == attributesMap[SortAscendingValueIndex][AtkDomain])
124                 return attributesMap[SortAscendingValueIndex][CoreDomain];
125             if (value == attributesMap[SortDescendingValueIndex][AtkDomain])
126                 return attributesMap[SortDescendingValueIndex][CoreDomain];
127
128             return attributesMap[SortUnknownValueIndex][CoreDomain];
129         }
130     } else if (type == TextAttributeType) {
131         // In case of 'aria-invalid' when the attribute empty or has "false" for ATK
132         // it should not be mapped at all, but layout tests will expect 'false'.
133         if (id == attributesMap[InvalidNameIndex][AtkDomain] && value.isEmpty())
134             return "false";
135     }
136
137     return value;
138 }
139
140 AtkAttributeSet* getAttributeSet(AtkObject* accessible, AtkAttributeType type)
141 {
142     if (type == ObjectAttributeType)
143         return atk_object_get_attributes(accessible);
144
145     if (type == TextAttributeType) {
146         if (!ATK_IS_TEXT(accessible))
147             return nullptr;
148
149         return atk_text_get_default_attributes(ATK_TEXT(accessible));
150     }
151
152     ASSERT_NOT_REACHED();
153     return nullptr;
154 }
155
156 String getAttributeSetValueForId(AtkObject* accessible, AtkAttributeType type, String id)
157 {
158     AtkAttributeSet* attributeSet = getAttributeSet(accessible, type);
159     if (!attributeSet)
160         return String();
161
162     String attributeValue;
163     for (AtkAttributeSet* attributes = attributeSet; attributes; attributes = attributes->next) {
164         AtkAttribute* atkAttribute = static_cast<AtkAttribute*>(attributes->data);
165         if (id == atkAttribute->name) {
166             attributeValue = String::fromUTF8(atkAttribute->value);
167             break;
168         }
169     }
170     atk_attribute_set_free(attributeSet);
171
172     return atkAttributeValueToCoreAttributeValue(type, id, attributeValue);
173 }
174
175 String getAtkAttributeSetAsString(AtkObject* accessible, AtkAttributeType type)
176 {
177     AtkAttributeSet* attributeSet = getAttributeSet(accessible, type);
178     if (!attributeSet)
179         return String();
180
181     StringBuilder builder;
182     for (AtkAttributeSet* attributes = attributeSet; attributes; attributes = attributes->next) {
183         AtkAttribute* attribute = static_cast<AtkAttribute*>(attributes->data);
184         GOwnPtr<gchar> attributeData(g_strconcat(attribute->name, ":", attribute->value, NULL));
185         builder.append(attributeData.get());
186         if (attributes->next)
187             builder.append(", ");
188     }
189     atk_attribute_set_free(attributeSet);
190
191     return builder.toString();
192 }
193
194 bool checkElementState(PlatformUIElement element, AtkStateType stateType)
195 {
196     if (!ATK_IS_OBJECT(element.get()))
197         return false;
198
199     GRefPtr<AtkStateSet> stateSet = adoptGRef(atk_object_ref_state_set(ATK_OBJECT(element.get())));
200     return atk_state_set_contains_state(stateSet.get(), stateType);
201 }
202
203 JSStringRef indexRangeInTable(PlatformUIElement element, bool isRowRange)
204 {
205     GOwnPtr<gchar> rangeString(g_strdup("{0, 0}"));
206
207     if (!ATK_IS_OBJECT(element.get()))
208         return JSStringCreateWithUTF8CString(rangeString.get());
209
210     AtkObject* axTable = atk_object_get_parent(ATK_OBJECT(element.get()));
211     if (!axTable || !ATK_IS_TABLE(axTable))
212         return JSStringCreateWithUTF8CString(rangeString.get());
213
214     // Look for the cell in the table.
215     gint indexInParent = atk_object_get_index_in_parent(ATK_OBJECT(element.get()));
216     if (indexInParent == -1)
217         return JSStringCreateWithUTF8CString(rangeString.get());
218
219     int row = -1;
220     int column = -1;
221     row = atk_table_get_row_at_index(ATK_TABLE(axTable), indexInParent);
222     column = atk_table_get_column_at_index(ATK_TABLE(axTable), indexInParent);
223
224     // Get the actual values, if row and columns are valid values.
225     if (row != -1 && column != -1) {
226         int base = 0;
227         int length = 0;
228         if (isRowRange) {
229             base = row;
230             length = atk_table_get_row_extent_at(ATK_TABLE(axTable), row, column);
231         } else {
232             base = column;
233             length = atk_table_get_column_extent_at(ATK_TABLE(axTable), row, column);
234         }
235         rangeString.set(g_strdup_printf("{%d, %d}", base, length));
236     }
237
238     return JSStringCreateWithUTF8CString(rangeString.get());
239 }
240
241 void alterCurrentValue(PlatformUIElement element, int factor)
242 {
243     if (!ATK_IS_VALUE(element.get()))
244         return;
245
246     GValue currentValue = G_VALUE_INIT;
247     atk_value_get_current_value(ATK_VALUE(element.get()), &currentValue);
248
249     GValue increment = G_VALUE_INIT;
250     atk_value_get_minimum_increment(ATK_VALUE(element.get()), &increment);
251
252     GValue newValue = G_VALUE_INIT;
253     g_value_init(&newValue, G_TYPE_FLOAT);
254
255     g_value_set_float(&newValue, g_value_get_float(&currentValue) + factor * g_value_get_float(&increment));
256     atk_value_set_current_value(ATK_VALUE(element.get()), &newValue);
257
258     g_value_unset(&newValue);
259     g_value_unset(&increment);
260     g_value_unset(&currentValue);
261 }
262
263 gchar* replaceCharactersForResults(gchar* str)
264 {
265     WTF::String uString = WTF::String::fromUTF8(str);
266
267     // The object replacement character is passed along to ATs so we need to be
268     // able to test for their presence and do so without causing test failures.
269     uString.replace(objectReplacementCharacter, "<obj>");
270
271     // The presence of newline characters in accessible text of a single object
272     // is appropriate, but it makes test results (especially the accessible tree)
273     // harder to read.
274     uString.replace("\n", "<\\n>");
275
276     return g_strdup(uString.utf8().data());
277 }
278
279 const gchar* roleToString(AtkObject* object)
280 {
281     AtkRole role = atk_object_get_role(object);
282
283 #if ATK_CHECK_VERSION(2, 11, 3)
284     if (role == ATK_ROLE_LANDMARK) {
285         String xmlRolesValue = getAttributeSetValueForId(object, ObjectAttributeType, "xml-roles");
286         if (equalIgnoringCase(xmlRolesValue, "banner"))
287             return landmarkStringBanner;
288         if (equalIgnoringCase(xmlRolesValue, "complementary"))
289             return landmarkStringComplementary;
290         if (equalIgnoringCase(xmlRolesValue, "contentinfo"))
291             return landmarkStringContentinfo;
292         if (equalIgnoringCase(xmlRolesValue, "main"))
293             return landmarkStringMain;
294         if (equalIgnoringCase(xmlRolesValue, "navigation"))
295             return landmarkStringNavigation;
296         if (equalIgnoringCase(xmlRolesValue, "search"))
297             return landmarkStringSearch;
298     }
299 #endif
300
301     switch (role) {
302     case ATK_ROLE_ALERT:
303         return "AXAlert";
304     case ATK_ROLE_DIALOG:
305         return "AXDialog";
306     case ATK_ROLE_CANVAS:
307         return "AXCanvas";
308     case ATK_ROLE_CHECK_BOX:
309         return "AXCheckBox";
310     case ATK_ROLE_COLOR_CHOOSER:
311         return "AXColorWell";
312     case ATK_ROLE_COLUMN_HEADER:
313         return "AXColumnHeader";
314     case ATK_ROLE_COMBO_BOX:
315         return "AXComboBox";
316     case ATK_ROLE_COMMENT:
317         return "AXComment";
318     case ATK_ROLE_DOCUMENT_FRAME:
319         return "AXDocument";
320     case ATK_ROLE_DOCUMENT_WEB:
321         return "AXWebArea";
322     case ATK_ROLE_EMBEDDED:
323         return "AXEmbedded";
324     case ATK_ROLE_ENTRY:
325         return "AXTextField";
326     case ATK_ROLE_FOOTER:
327         return "AXFooter";
328     case ATK_ROLE_FORM:
329         return "AXForm";
330     case ATK_ROLE_GROUPING:
331         return "AXGroup";
332     case ATK_ROLE_HEADING:
333         return "AXHeading";
334     case ATK_ROLE_IMAGE:
335         return "AXImage";
336     case ATK_ROLE_IMAGE_MAP:
337         return "AXImageMap";
338     case ATK_ROLE_LABEL:
339         return "AXLabel";
340     case ATK_ROLE_LINK:
341         return "AXLink";
342     case ATK_ROLE_LIST:
343         return "AXList";
344     case ATK_ROLE_LIST_BOX:
345         return "AXListBox";
346     case ATK_ROLE_LIST_ITEM:
347         return "AXListItem";
348     case ATK_ROLE_MENU:
349         return "AXMenu";
350     case ATK_ROLE_MENU_BAR:
351         return "AXMenuBar";
352     case ATK_ROLE_MENU_ITEM:
353         return "AXMenuItem";
354     case ATK_ROLE_PAGE_TAB:
355         return "AXTab";
356     case ATK_ROLE_PAGE_TAB_LIST:
357         return "AXTabGroup";
358     case ATK_ROLE_PANEL:
359         return "AXGroup";
360     case ATK_ROLE_PARAGRAPH:
361         return "AXParagraph";
362     case ATK_ROLE_PASSWORD_TEXT:
363         return "AXPasswordField";
364     case ATK_ROLE_PROGRESS_BAR:
365         return "AXProgressIndicator";
366     case ATK_ROLE_PUSH_BUTTON:
367         return "AXButton";
368     case ATK_ROLE_RADIO_BUTTON:
369         return "AXRadioButton";
370     case ATK_ROLE_RADIO_MENU_ITEM:
371         return "AXRadioMenuItem";
372     case ATK_ROLE_ROW_HEADER:
373         return "AXRowHeader";
374     case ATK_ROLE_CHECK_MENU_ITEM:
375         return "AXCheckMenuItem";
376     case ATK_ROLE_RULER:
377         return "AXRuler";
378     case ATK_ROLE_SCROLL_BAR:
379         return "AXScrollBar";
380     case ATK_ROLE_SCROLL_PANE:
381         return "AXScrollArea";
382     case ATK_ROLE_SECTION:
383         return "AXSection";
384     case ATK_ROLE_SEPARATOR:
385         return "AXSeparator";
386     case ATK_ROLE_SLIDER:
387         return "AXSlider";
388     case ATK_ROLE_SPIN_BUTTON:
389         return "AXSpinButton";
390     case ATK_ROLE_STATUSBAR:
391         return "AXStatusBar";
392     case ATK_ROLE_TABLE:
393         return "AXTable";
394     case ATK_ROLE_TABLE_CELL:
395         return "AXCell";
396     case ATK_ROLE_TABLE_COLUMN_HEADER:
397         return "AXColumnHeader";
398     case ATK_ROLE_TABLE_ROW:
399         return "AXRow";
400     case ATK_ROLE_TABLE_ROW_HEADER:
401         return "AXRowHeader";
402     case ATK_ROLE_TOGGLE_BUTTON:
403         return "AXToggleButton";
404     case ATK_ROLE_TOOL_BAR:
405         return "AXToolbar";
406     case ATK_ROLE_TOOL_TIP:
407         return "AXUserInterfaceTooltip";
408     case ATK_ROLE_TREE:
409         return "AXTree";
410     case ATK_ROLE_TREE_TABLE:
411         return "AXTreeGrid";
412     case ATK_ROLE_TREE_ITEM:
413         return "AXTreeItem";
414     case ATK_ROLE_WINDOW:
415         return "AXWindow";
416     case ATK_ROLE_UNKNOWN:
417         return "AXUnknown";
418 #if ATK_CHECK_VERSION(2, 11, 3)
419     case ATK_ROLE_ARTICLE:
420         return "AXArticle";
421     case ATK_ROLE_DEFINITION:
422         return "AXDefinition";
423     case ATK_ROLE_LOG:
424         return "AXLog";
425     case ATK_ROLE_MARQUEE:
426         return "AXMarquee";
427     case ATK_ROLE_MATH:
428         return "AXMath";
429     case ATK_ROLE_TIMER:
430         return "AXTimer";
431 #endif
432 #if ATK_CHECK_VERSION(2, 11, 4)
433     case ATK_ROLE_DESCRIPTION_LIST:
434         return "AXDescriptionList";
435     case ATK_ROLE_DESCRIPTION_TERM:
436         return "AXDescriptionTerm";
437     case ATK_ROLE_DESCRIPTION_VALUE:
438         return "AXDescriptionValue";
439 #endif
440     default:
441         // We want to distinguish ATK_ROLE_UNKNOWN from a known AtkRole which
442         // our DRT isn't properly handling.
443         return "FIXME not identified";
444     }
445 }
446
447 String attributesOfElement(AccessibilityUIElement* element)
448 {
449     StringBuilder builder;
450
451     builder.append(String::format("%s\n", element->role()->string().utf8().data()));
452
453     // For the parent we print its role and its name, if available.
454     builder.append("AXParent: ");
455     RefPtr<AccessibilityUIElement> parent = element->parentElement();
456     AtkObject* atkParent = parent ? parent->platformUIElement().get() : nullptr;
457     if (atkParent) {
458         builder.append(roleToString(atkParent));
459         const char* parentName = atk_object_get_name(atkParent);
460         if (parentName && g_utf8_strlen(parentName, -1))
461             builder.append(String::format(": %s", parentName));
462     } else
463         builder.append("(null)");
464     builder.append("\n");
465
466     builder.append(String::format("AXChildren: %d\n", element->childrenCount()));
467     builder.append(String::format("AXPosition: { %f, %f }\n", element->x(), element->y()));
468     builder.append(String::format("AXSize: { %f, %f }\n", element->width(), element->height()));
469
470     String title = element->title()->string();
471     if (!title.isEmpty())
472         builder.append(String::format("%s\n", title.utf8().data()));
473
474     String description = element->description()->string();
475     if (!description.isEmpty())
476         builder.append(String::format("%s\n", description.utf8().data()));
477
478     String value = element->stringValue()->string();
479     if (!value.isEmpty())
480         builder.append(String::format("%s\n", value.utf8().data()));
481
482     builder.append(String::format("AXFocusable: %d\n", element->isFocusable()));
483     builder.append(String::format("AXFocused: %d\n", element->isFocused()));
484     builder.append(String::format("AXSelectable: %d\n", element->isSelectable()));
485     builder.append(String::format("AXSelected: %d\n", element->isSelected()));
486     builder.append(String::format("AXMultiSelectable: %d\n", element->isMultiSelectable()));
487     builder.append(String::format("AXEnabled: %d\n", element->isEnabled()));
488     builder.append(String::format("AXExpanded: %d\n", element->isExpanded()));
489     builder.append(String::format("AXRequired: %d\n", element->isRequired()));
490     builder.append(String::format("AXChecked: %d\n", element->isChecked()));
491
492     String url = element->url()->string();
493     if (!url.isEmpty())
494         builder.append(String::format("%s\n", url.utf8().data()));
495
496     // We append the ATK specific attributes as a single line at the end.
497     builder.append("AXPlatformAttributes: ");
498     builder.append(getAtkAttributeSetAsString(element->platformUIElement().get(), ObjectAttributeType));
499
500     return builder.toString();
501 }
502
503 static JSRetainPtr<JSStringRef> createStringWithAttributes(const Vector<RefPtr<AccessibilityUIElement> >& elements)
504 {
505     StringBuilder builder;
506
507     for (Vector<RefPtr<AccessibilityUIElement> >::const_iterator it = elements.begin(); it != elements.end(); ++it) {
508         builder.append(attributesOfElement(const_cast<AccessibilityUIElement*>(it->get())));
509         builder.append("\n------------\n");
510     }
511
512     return JSStringCreateWithUTF8CString(builder.toString().utf8().data());
513 }
514
515 static Vector<RefPtr<AccessibilityUIElement> > getRowHeaders(AtkTable* accessible)
516 {
517     Vector<RefPtr<AccessibilityUIElement> > rowHeaders;
518
519     int rowsCount = atk_table_get_n_rows(accessible);
520     for (int row = 0; row < rowsCount; ++row)
521         rowHeaders.append(AccessibilityUIElement::create(atk_table_get_row_header(accessible, row)));
522
523     return rowHeaders;
524 }
525
526 static Vector<RefPtr<AccessibilityUIElement> > getColumnHeaders(AtkTable* accessible)
527 {
528     Vector<RefPtr<AccessibilityUIElement> > columnHeaders;
529
530     int columnsCount = atk_table_get_n_columns(accessible);
531     for (int column = 0; column < columnsCount; ++column)
532         columnHeaders.append(AccessibilityUIElement::create(atk_table_get_column_header(accessible, column)));
533
534     return columnHeaders;
535 }
536
537 static Vector<RefPtr<AccessibilityUIElement> > getVisibleCells(AccessibilityUIElement* element)
538 {
539     Vector<RefPtr<AccessibilityUIElement> > visibleCells;
540
541     AtkTable* accessible = ATK_TABLE(element->platformUIElement().get());
542     int rowsCount = atk_table_get_n_rows(accessible);
543     int columnsCount = atk_table_get_n_columns(accessible);
544
545     for (int row = 0; row < rowsCount; ++row) {
546         for (int column = 0; column < columnsCount; ++column)
547             visibleCells.append(element->cellForColumnAndRow(column, row));
548     }
549
550     return visibleCells;
551 }
552
553 } // namespace
554
555 AccessibilityUIElement::AccessibilityUIElement(PlatformUIElement element)
556     : m_element(element)
557 {
558 }
559
560 AccessibilityUIElement::AccessibilityUIElement(const AccessibilityUIElement& other)
561     : JSWrappable()
562     , m_element(other.m_element)
563 {
564 }
565
566 AccessibilityUIElement::~AccessibilityUIElement()
567 {
568 }
569
570 bool AccessibilityUIElement::isEqual(AccessibilityUIElement* otherElement)
571 {
572     return m_element == otherElement->platformUIElement();
573 }
574
575 void AccessibilityUIElement::getChildren(Vector<RefPtr<AccessibilityUIElement> >& children)
576 {
577     if (!ATK_IS_OBJECT(m_element.get()))
578         return;
579
580     int count = childrenCount();
581     for (int i = 0; i < count; i++) {
582         GRefPtr<AtkObject> child = adoptGRef(atk_object_ref_accessible_child(ATK_OBJECT(m_element.get()), i));
583         children.append(AccessibilityUIElement::create(child.get()));
584     }
585 }
586
587 void AccessibilityUIElement::getChildrenWithRange(Vector<RefPtr<AccessibilityUIElement> >& children, unsigned location, unsigned length)
588 {
589     if (!ATK_IS_OBJECT(m_element.get()))
590         return;
591     unsigned end = location + length;
592     for (unsigned i = location; i < end; i++) {
593         GRefPtr<AtkObject> child = adoptGRef(atk_object_ref_accessible_child(ATK_OBJECT(m_element.get()), i));
594         children.append(AccessibilityUIElement::create(child.get()));
595     }
596 }
597
598 int AccessibilityUIElement::childrenCount()
599 {
600     if (!ATK_IS_OBJECT(m_element.get()))
601         return 0;
602
603     return atk_object_get_n_accessible_children(ATK_OBJECT(m_element.get()));
604 }
605
606 PassRefPtr<AccessibilityUIElement> AccessibilityUIElement::elementAtPoint(int x, int y)
607 {
608     if (!ATK_IS_COMPONENT(m_element.get()))
609         return nullptr;
610
611     GRefPtr<AtkObject> objectAtPoint = adoptGRef(atk_component_ref_accessible_at_point(ATK_COMPONENT(m_element.get()), x, y, ATK_XY_WINDOW));
612     return AccessibilityUIElement::create(objectAtPoint ? objectAtPoint.get() : m_element.get());
613 }
614
615 unsigned AccessibilityUIElement::indexOfChild(AccessibilityUIElement* element)
616 {
617     if (!ATK_IS_OBJECT(m_element.get()))
618         return 0;
619
620     Vector<RefPtr<AccessibilityUIElement> > children;
621     getChildren(children);
622
623     unsigned count = children.size();
624     for (unsigned i = 0; i < count; i++)
625         if (children[i]->isEqual(element))
626             return i;
627
628     return 0;
629 }
630
631 PassRefPtr<AccessibilityUIElement> AccessibilityUIElement::childAtIndex(unsigned index)
632 {
633     if (!ATK_IS_OBJECT(m_element.get()))
634         return nullptr;
635
636     Vector<RefPtr<AccessibilityUIElement> > children;
637     getChildrenWithRange(children, index, 1);
638
639     if (children.size() == 1)
640         return children[0];
641
642     return nullptr;
643 }
644
645 PassRefPtr<AccessibilityUIElement> AccessibilityUIElement::linkedUIElementAtIndex(unsigned index)
646 {
647     // FIXME: implement
648     return nullptr;
649 }
650
651 PassRefPtr<AccessibilityUIElement> AccessibilityUIElement::ariaOwnsElementAtIndex(unsigned index)
652 {
653     // FIXME: implement
654     return nullptr;
655 }
656
657 PassRefPtr<AccessibilityUIElement> AccessibilityUIElement::ariaFlowToElementAtIndex(unsigned index)
658 {
659     // FIXME: implement
660     return nullptr;
661 }
662
663 PassRefPtr<AccessibilityUIElement> AccessibilityUIElement::disclosedRowAtIndex(unsigned index)
664 {
665     // FIXME: implement
666     return nullptr;
667 }
668
669 PassRefPtr<AccessibilityUIElement> AccessibilityUIElement::rowAtIndex(unsigned index)
670 {
671     // FIXME: implement
672     return nullptr;
673 }
674
675 PassRefPtr<AccessibilityUIElement> AccessibilityUIElement::selectedChildAtIndex(unsigned index) const
676 {
677     // FIXME: implement
678     return nullptr;
679 }
680
681 unsigned AccessibilityUIElement::selectedChildrenCount() const
682 {
683     // FIXME: implement
684     return 0;
685 }
686
687 PassRefPtr<AccessibilityUIElement> AccessibilityUIElement::selectedRowAtIndex(unsigned index)
688 {
689     // FIXME: implement
690     return nullptr;
691 }
692
693 PassRefPtr<AccessibilityUIElement> AccessibilityUIElement::titleUIElement()
694 {
695     if (!ATK_IS_OBJECT(m_element.get()))
696         return nullptr;
697
698     AtkRelationSet* set = atk_object_ref_relation_set(ATK_OBJECT(m_element.get()));
699     if (!set)
700         return nullptr;
701
702     AtkObject* target = nullptr;
703     int count = atk_relation_set_get_n_relations(set);
704     for (int i = 0; i < count; i++) {
705         AtkRelation* relation = atk_relation_set_get_relation(set, i);
706         if (atk_relation_get_relation_type(relation) == ATK_RELATION_LABELLED_BY) {
707             GPtrArray* targetList = atk_relation_get_target(relation);
708             if (targetList->len)
709                 target = static_cast<AtkObject*>(g_ptr_array_index(targetList, 0));
710         }
711     }
712
713     g_object_unref(set);
714     return target ? AccessibilityUIElement::create(target) : nullptr;
715 }
716
717 PassRefPtr<AccessibilityUIElement> AccessibilityUIElement::parentElement()
718 {
719     if (!ATK_IS_OBJECT(m_element.get()))
720         return nullptr;
721
722     AtkObject* parent = atk_object_get_parent(ATK_OBJECT(m_element.get()));
723     return parent ? AccessibilityUIElement::create(parent) : nullptr;
724 }
725
726 PassRefPtr<AccessibilityUIElement> AccessibilityUIElement::disclosedByRow()
727 {
728     // FIXME: implement
729     return nullptr;
730 }
731
732 JSRetainPtr<JSStringRef> AccessibilityUIElement::attributesOfLinkedUIElements()
733 {
734     // FIXME: implement
735     return JSStringCreateWithCharacters(0, 0);
736 }
737
738 JSRetainPtr<JSStringRef> AccessibilityUIElement::attributesOfDocumentLinks()
739 {
740     // FIXME: implement
741     return JSStringCreateWithCharacters(0, 0);
742 }
743
744 JSRetainPtr<JSStringRef> AccessibilityUIElement::attributesOfChildren()
745 {
746     if (!ATK_IS_OBJECT(m_element.get()))
747         return JSStringCreateWithCharacters(0, 0);
748
749     Vector<RefPtr<AccessibilityUIElement> > children;
750     getChildren(children);
751
752     return createStringWithAttributes(children);
753 }
754
755 JSRetainPtr<JSStringRef> AccessibilityUIElement::allAttributes()
756 {
757     if (!ATK_IS_OBJECT(m_element.get()))
758         return JSStringCreateWithCharacters(0, 0);
759
760     return JSStringCreateWithUTF8CString(attributesOfElement(this).utf8().data());
761 }
762
763 JSRetainPtr<JSStringRef> AccessibilityUIElement::stringAttributeValue(JSStringRef attribute)
764 {
765     if (!ATK_IS_OBJECT(m_element.get()))
766         return JSStringCreateWithCharacters(0, 0);
767
768     String atkAttributeName = coreAttributeToAtkAttribute(attribute);
769
770     // Try object attributes first.
771     String attributeValue = getAttributeSetValueForId(ATK_OBJECT(m_element.get()), ObjectAttributeType, atkAttributeName);
772
773     // Try text attributes if the requested one was not found and we have an AtkText object.
774     if (attributeValue.isEmpty() && ATK_IS_TEXT(m_element.get()))
775         attributeValue = getAttributeSetValueForId(ATK_OBJECT(m_element.get()), TextAttributeType, atkAttributeName);
776
777     // Additional check to make sure that the exposure of the state ATK_STATE_INVALID_ENTRY
778     // is consistent with the exposure of aria-invalid as a text attribute, if present.
779     if (atkAttributeName == attributesMap[InvalidNameIndex][AtkDomain]) {
780         bool isInvalidState = checkElementState(m_element.get(), ATK_STATE_INVALID_ENTRY);
781         if (attributeValue.isEmpty())
782             return JSStringCreateWithUTF8CString(isInvalidState ? "true" : "false");
783
784         // If the text attribute was there, check that it's consistent with
785         // what the state says or force the test to fail otherwise.
786         bool isAriaInvalid = attributeValue != "false";
787         if (isInvalidState != isAriaInvalid)
788             return JSStringCreateWithCharacters(0, 0);
789     }
790
791     return JSStringCreateWithUTF8CString(attributeValue.utf8().data());
792 }
793
794 double AccessibilityUIElement::numberAttributeValue(JSStringRef attribute)
795 {
796     // FIXME: implement
797     return 0;
798 }
799
800 JSValueRef AccessibilityUIElement::uiElementArrayAttributeValue(JSStringRef attribute) const
801 {
802     // FIXME: implement
803     return nullptr;
804 }
805
806 PassRefPtr<AccessibilityUIElement> AccessibilityUIElement::uiElementAttributeValue(JSStringRef attribute) const
807 {
808     // FIXME: implement
809     return nullptr;
810 }
811
812 bool AccessibilityUIElement::boolAttributeValue(JSStringRef attribute)
813 {
814     // FIXME: implement
815     return false;
816 }
817
818 bool AccessibilityUIElement::isAttributeSettable(JSStringRef attribute)
819 {
820     if (!ATK_IS_OBJECT(m_element.get()))
821         return false;
822
823     String attributeString = jsStringToWTFString(attribute);
824     if (attributeString == "AXValue")
825         return checkElementState(m_element.get(), ATK_STATE_EDITABLE);
826
827     return false;
828 }
829
830 bool AccessibilityUIElement::isAttributeSupported(JSStringRef attribute)
831 {
832     if (!ATK_IS_OBJECT(m_element.get()))
833         return false;
834
835     String atkAttributeName = coreAttributeToAtkAttribute(attribute);
836     if (atkAttributeName.isEmpty())
837         return false;
838
839     // For now, an attribute is supported whether it's exposed as a object or a text attribute.
840     String attributeValue = getAttributeSetValueForId(ATK_OBJECT(m_element.get()), ObjectAttributeType, atkAttributeName);
841     if (attributeValue.isEmpty())
842         attributeValue = getAttributeSetValueForId(ATK_OBJECT(m_element.get()), TextAttributeType, atkAttributeName);
843
844     return !attributeValue.isEmpty();
845 }
846
847 JSRetainPtr<JSStringRef> AccessibilityUIElement::parameterizedAttributeNames()
848 {
849     // FIXME: implement
850     return JSStringCreateWithCharacters(0, 0);
851 }
852
853 JSRetainPtr<JSStringRef> AccessibilityUIElement::role()
854 {
855     if (!ATK_IS_OBJECT(m_element.get()))
856         return JSStringCreateWithCharacters(0, 0);
857
858     if (!atk_object_get_role(ATK_OBJECT(m_element.get())))
859         return JSStringCreateWithCharacters(0, 0);
860
861     GOwnPtr<char> roleStringWithPrefix(g_strdup_printf("AXRole: %s", roleToString(ATK_OBJECT(m_element.get()))));
862     return JSStringCreateWithUTF8CString(roleStringWithPrefix.get());
863 }
864
865 JSRetainPtr<JSStringRef> AccessibilityUIElement::subrole()
866 {
867     // FIXME: implement
868     return JSStringCreateWithCharacters(0, 0);
869 }
870
871 JSRetainPtr<JSStringRef> AccessibilityUIElement::roleDescription()
872 {
873     // FIXME: implement
874     return JSStringCreateWithCharacters(0, 0);
875 }
876
877 JSRetainPtr<JSStringRef> AccessibilityUIElement::title()
878 {
879     if (!ATK_IS_OBJECT(m_element.get()))
880         return JSStringCreateWithCharacters(0, 0);
881
882     const gchar* name = atk_object_get_name(ATK_OBJECT(m_element.get()));
883     GOwnPtr<gchar> axTitle(g_strdup_printf("AXTitle: %s", name ? name : ""));
884
885     return JSStringCreateWithUTF8CString(axTitle.get());
886 }
887
888 JSRetainPtr<JSStringRef> AccessibilityUIElement::description()
889 {
890     if (!ATK_IS_OBJECT(m_element.get()))
891         return JSStringCreateWithCharacters(0, 0);
892
893     const gchar* description = atk_object_get_description(ATK_OBJECT(m_element.get()));
894     if (!description)
895         return JSStringCreateWithCharacters(0, 0);
896
897     GOwnPtr<gchar> axDesc(g_strdup_printf("AXDescription: %s", description));
898
899     return JSStringCreateWithUTF8CString(axDesc.get());
900 }
901
902 JSRetainPtr<JSStringRef> AccessibilityUIElement::orientation() const
903 {
904     if (!ATK_IS_OBJECT(m_element.get()))
905         return JSStringCreateWithCharacters(0, 0);
906
907     const gchar* axOrientation = nullptr;
908     if (checkElementState(m_element.get(), ATK_STATE_HORIZONTAL))
909         axOrientation = "AXOrientation: AXHorizontalOrientation";
910     else if (checkElementState(m_element.get(), ATK_STATE_VERTICAL))
911         axOrientation = "AXOrientation: AXVerticalOrientation";
912
913     if (!axOrientation)
914         return JSStringCreateWithCharacters(0, 0);
915
916     return JSStringCreateWithUTF8CString(axOrientation);
917 }
918
919 JSRetainPtr<JSStringRef> AccessibilityUIElement::stringValue()
920 {
921     if (!ATK_IS_TEXT(m_element.get()))
922         return JSStringCreateWithCharacters(0, 0);
923
924     GOwnPtr<gchar> text(atk_text_get_text(ATK_TEXT(m_element.get()), 0, -1));
925     GOwnPtr<gchar> textWithReplacedCharacters(replaceCharactersForResults(text.get()));
926     GOwnPtr<gchar> axValue(g_strdup_printf("AXValue: %s", textWithReplacedCharacters.get()));
927
928     return JSStringCreateWithUTF8CString(axValue.get());
929 }
930
931 JSRetainPtr<JSStringRef> AccessibilityUIElement::language()
932 {
933     if (!ATK_IS_OBJECT(m_element.get()))
934         return JSStringCreateWithCharacters(0, 0);
935
936     const gchar* locale = atk_object_get_object_locale(ATK_OBJECT(m_element.get()));
937     if (!locale)
938         return JSStringCreateWithCharacters(0, 0);
939
940     GOwnPtr<char> axValue(g_strdup_printf("AXLanguage: %s", locale));
941     return JSStringCreateWithUTF8CString(axValue.get());
942 }
943
944 JSRetainPtr<JSStringRef> AccessibilityUIElement::helpText() const
945 {
946     // FIXME: We need to provide a proper implementation for this that does
947     // not depend on Mac specific concepts such as ATK_RELATION_DESCRIBED_BY,
948     // once it's implemented (see http://webkit.org/b/121684).
949     return JSStringCreateWithCharacters(0, 0);
950 }
951
952 double AccessibilityUIElement::x()
953 {
954     if (!ATK_IS_COMPONENT(m_element.get()))
955         return 0;
956
957     int x, y;
958     atk_component_get_position(ATK_COMPONENT(m_element.get()), &x, &y, ATK_XY_SCREEN);
959     return x;
960 }
961
962 double AccessibilityUIElement::y()
963 {
964     if (!ATK_IS_COMPONENT(m_element.get()))
965         return 0;
966
967     int x, y;
968     atk_component_get_position(ATK_COMPONENT(m_element.get()), &x, &y, ATK_XY_SCREEN);
969     return y;
970 }
971
972 double AccessibilityUIElement::width()
973 {
974     if (!ATK_IS_COMPONENT(m_element.get()))
975         return 0;
976
977     int width, height;
978     atk_component_get_size(ATK_COMPONENT(m_element.get()), &width, &height);
979     return width;
980 }
981
982 double AccessibilityUIElement::height()
983 {
984     if (!ATK_IS_COMPONENT(m_element.get()))
985         return 0;
986
987     int width, height;
988     atk_component_get_size(ATK_COMPONENT(m_element.get()), &width, &height);
989     return height;
990 }
991
992 double AccessibilityUIElement::clickPointX()
993 {
994     if (!ATK_IS_COMPONENT(m_element.get()))
995         return 0;
996
997     int x, y;
998     atk_component_get_position(ATK_COMPONENT(m_element.get()), &x, &y, ATK_XY_WINDOW);
999
1000     int width, height;
1001     atk_component_get_size(ATK_COMPONENT(m_element.get()), &width, &height);
1002
1003     return x + width / 2.0;
1004 }
1005
1006 double AccessibilityUIElement::clickPointY()
1007 {
1008     if (!ATK_IS_COMPONENT(m_element.get()))
1009         return 0;
1010
1011     int x, y;
1012     atk_component_get_position(ATK_COMPONENT(m_element.get()), &x, &y, ATK_XY_WINDOW);
1013
1014     int width, height;
1015     atk_component_get_size(ATK_COMPONENT(m_element.get()), &width, &height);
1016
1017     return y + height / 2.0;
1018 }
1019
1020 double AccessibilityUIElement::intValue() const
1021 {
1022     if (!ATK_IS_OBJECT(m_element.get()))
1023         return 0;
1024
1025     if (ATK_IS_VALUE(m_element.get())) {
1026         GValue value = G_VALUE_INIT;
1027         atk_value_get_current_value(ATK_VALUE(m_element.get()), &value);
1028         if (!G_VALUE_HOLDS_FLOAT(&value))
1029             return 0;
1030         return g_value_get_float(&value);
1031     }
1032
1033     // Consider headings as an special case when returning the "int value" of
1034     // an AccessibilityUIElement, so we can reuse some tests to check the level
1035     // both for HTML headings and objects with the aria-level attribute.
1036     if (atk_object_get_role(ATK_OBJECT(m_element.get())) == ATK_ROLE_HEADING) {
1037         String headingLevel = getAttributeSetValueForId(ATK_OBJECT(m_element.get()), ObjectAttributeType, "level");
1038         bool ok;
1039         double headingLevelValue = headingLevel.toDouble(&ok);
1040         if (ok)
1041             return headingLevelValue;
1042     }
1043
1044     return 0;
1045 }
1046
1047 double AccessibilityUIElement::minValue()
1048 {
1049     if (!ATK_IS_VALUE(m_element.get()))
1050         return 0;
1051
1052     GValue value = G_VALUE_INIT;
1053     atk_value_get_minimum_value(ATK_VALUE(m_element.get()), &value);
1054     if (!G_VALUE_HOLDS_FLOAT(&value))
1055         return 0;
1056
1057     return g_value_get_float(&value);
1058 }
1059
1060 double AccessibilityUIElement::maxValue()
1061 {
1062     if (!ATK_IS_VALUE(m_element.get()))
1063         return 0;
1064
1065     GValue value = G_VALUE_INIT;
1066     atk_value_get_maximum_value(ATK_VALUE(m_element.get()), &value);
1067     if (!G_VALUE_HOLDS_FLOAT(&value))
1068         return 0;
1069
1070     return g_value_get_float(&value);
1071 }
1072
1073 JSRetainPtr<JSStringRef> AccessibilityUIElement::valueDescription()
1074 {
1075     // FIXME: implement
1076     return JSStringCreateWithCharacters(0, 0);
1077 }
1078
1079 int AccessibilityUIElement::insertionPointLineNumber()
1080 {
1081     // FIXME: implement
1082     return -1;
1083 }
1084
1085 bool AccessibilityUIElement::isPressActionSupported()
1086 {
1087     if (!ATK_IS_ACTION(m_element.get()))
1088         return false;
1089
1090     const gchar* actionName = atk_action_get_name(ATK_ACTION(m_element.get()), 0);
1091     return equalIgnoringCase(actionName, String("press")) || equalIgnoringCase(actionName, String("jump"));
1092 }
1093
1094 bool AccessibilityUIElement::isIncrementActionSupported()
1095 {
1096     // FIXME: implement
1097     return false;
1098 }
1099
1100 bool AccessibilityUIElement::isDecrementActionSupported()
1101 {
1102     // FIXME: implement
1103     return false;
1104 }
1105
1106 bool AccessibilityUIElement::isEnabled()
1107 {
1108     return checkElementState(m_element.get(), ATK_STATE_ENABLED);
1109 }
1110
1111 bool AccessibilityUIElement::isRequired() const
1112 {
1113     return checkElementState(m_element.get(), ATK_STATE_REQUIRED);
1114 }
1115
1116 bool AccessibilityUIElement::isFocused() const
1117 {
1118     return checkElementState(m_element.get(), ATK_STATE_FOCUSED);
1119 }
1120
1121 bool AccessibilityUIElement::isSelected() const
1122 {
1123     return checkElementState(m_element.get(), ATK_STATE_SELECTED);
1124 }
1125
1126 bool AccessibilityUIElement::isSelectedOptionActive() const
1127 {
1128     return checkElementState(m_element.get(), ATK_STATE_ACTIVE);
1129 }
1130
1131 bool AccessibilityUIElement::isExpanded() const
1132 {
1133     return checkElementState(m_element.get(), ATK_STATE_EXPANDED);
1134 }
1135
1136 bool AccessibilityUIElement::isChecked() const
1137 {
1138     return checkElementState(m_element.get(), ATK_STATE_CHECKED);
1139 }
1140
1141 int AccessibilityUIElement::hierarchicalLevel() const
1142 {
1143     // FIXME: implement
1144     return 0;
1145 }
1146
1147 JSRetainPtr<JSStringRef> AccessibilityUIElement::speak()
1148 {
1149     // FIXME: implement
1150     return JSStringCreateWithCharacters(0, 0);
1151 }
1152
1153 bool AccessibilityUIElement::ariaIsGrabbed() const
1154 {
1155     // FIXME: implement
1156     return false;
1157 }
1158
1159 JSRetainPtr<JSStringRef> AccessibilityUIElement::ariaDropEffects() const
1160 {
1161     // FIXME: implement
1162     return JSStringCreateWithCharacters(0, 0);
1163 }
1164
1165 // parameterized attributes
1166 int AccessibilityUIElement::lineForIndex(int index)
1167 {
1168     if (!ATK_IS_TEXT(m_element.get()))
1169         return -1;
1170
1171     if (index < 0 || index > atk_text_get_character_count(ATK_TEXT(m_element.get())))
1172         return -1;
1173
1174     GOwnPtr<gchar> text(atk_text_get_text(ATK_TEXT(m_element.get()), 0, index));
1175     int lineNo = 0;
1176     for (gchar* offset = text.get(); *offset; ++offset) {
1177         if (*offset == '\n')
1178             ++lineNo;
1179     }
1180
1181     return lineNo;
1182 }
1183
1184 JSRetainPtr<JSStringRef> AccessibilityUIElement::rangeForLine(int line)
1185 {
1186     // FIXME: implement
1187     return JSStringCreateWithCharacters(0, 0);
1188 }
1189
1190 JSRetainPtr<JSStringRef> AccessibilityUIElement::rangeForPosition(int x, int y)
1191 {
1192     // FIXME: implement
1193     return JSStringCreateWithCharacters(0, 0);
1194 }
1195
1196 JSRetainPtr<JSStringRef> AccessibilityUIElement::boundsForRange(unsigned location, unsigned length)
1197 {
1198     // FIXME: implement
1199     return JSStringCreateWithCharacters(0, 0);
1200 }
1201
1202 JSRetainPtr<JSStringRef> AccessibilityUIElement::stringForRange(unsigned location, unsigned length)
1203 {
1204     if (!ATK_IS_TEXT(m_element.get()))
1205         return JSStringCreateWithCharacters(0, 0);
1206
1207     String string = atk_text_get_text(ATK_TEXT(m_element.get()), location, location + length);
1208     return JSStringCreateWithUTF8CString(string.utf8().data());
1209 }
1210
1211 JSRetainPtr<JSStringRef> AccessibilityUIElement::attributedStringForRange(unsigned location, unsigned length)
1212 {
1213     // FIXME: implement
1214     return JSStringCreateWithCharacters(0, 0);
1215 }
1216
1217 bool AccessibilityUIElement::attributedStringRangeIsMisspelled(unsigned location, unsigned length)
1218 {
1219     // FIXME: implement
1220     return false;
1221 }
1222
1223 unsigned AccessibilityUIElement::uiElementCountForSearchPredicate(JSContextRef context, AccessibilityUIElement* startElement, bool isDirectionNext, JSValueRef searchKey, JSStringRef searchText, bool visibleOnly)
1224 {
1225     // FIXME: implement
1226     return 0;
1227 }
1228
1229 PassRefPtr<AccessibilityUIElement> AccessibilityUIElement::uiElementForSearchPredicate(JSContextRef context, AccessibilityUIElement* startElement, bool isDirectionNext, JSValueRef searchKey, JSStringRef searchText, bool visibleOnly)
1230 {
1231     // FIXME: implement
1232     return nullptr;
1233 }
1234
1235 JSRetainPtr<JSStringRef> AccessibilityUIElement::attributesOfColumnHeaders()
1236 {
1237     if (!ATK_IS_TABLE(m_element.get()))
1238         return JSStringCreateWithCharacters(0, 0);
1239
1240     Vector<RefPtr<AccessibilityUIElement> > columnHeaders = getColumnHeaders(ATK_TABLE(m_element.get()));
1241     return createStringWithAttributes(columnHeaders);
1242 }
1243
1244 JSRetainPtr<JSStringRef> AccessibilityUIElement::attributesOfRowHeaders()
1245 {
1246     if (!ATK_IS_TABLE(m_element.get()))
1247         return JSStringCreateWithCharacters(0, 0);
1248
1249     Vector<RefPtr<AccessibilityUIElement> > rowHeaders = getRowHeaders(ATK_TABLE(m_element.get()));
1250     return createStringWithAttributes(rowHeaders);
1251 }
1252
1253 JSRetainPtr<JSStringRef> AccessibilityUIElement::attributesOfColumns()
1254 {
1255     // FIXME: implement
1256     return JSStringCreateWithCharacters(0, 0);
1257 }
1258
1259 JSRetainPtr<JSStringRef> AccessibilityUIElement::attributesOfRows()
1260 {
1261     // FIXME: implement
1262     return JSStringCreateWithCharacters(0, 0);
1263 }
1264
1265 JSRetainPtr<JSStringRef> AccessibilityUIElement::attributesOfVisibleCells()
1266 {
1267     if (!ATK_IS_TABLE(m_element.get()))
1268         return JSStringCreateWithCharacters(0, 0);
1269
1270     Vector<RefPtr<AccessibilityUIElement> > visibleCells = getVisibleCells(this);
1271     return createStringWithAttributes(visibleCells);
1272 }
1273
1274 JSRetainPtr<JSStringRef> AccessibilityUIElement::attributesOfHeader()
1275 {
1276     // FIXME: implement
1277     return JSStringCreateWithCharacters(0, 0);
1278 }
1279
1280 int AccessibilityUIElement::rowCount()
1281 {
1282     if (!ATK_IS_TABLE(m_element.get()))
1283         return 0;
1284
1285     return atk_table_get_n_rows(ATK_TABLE(m_element.get()));
1286 }
1287
1288 int AccessibilityUIElement::columnCount()
1289 {
1290     if (!ATK_IS_TABLE(m_element.get()))
1291         return 0;
1292
1293     return atk_table_get_n_columns(ATK_TABLE(m_element.get()));
1294 }
1295
1296 int AccessibilityUIElement::indexInTable()
1297 {
1298     // FIXME: implement
1299     return -1;
1300 }
1301
1302 JSRetainPtr<JSStringRef> AccessibilityUIElement::rowIndexRange()
1303 {
1304     // Range in table for rows.
1305     return indexRangeInTable(m_element.get(), true);
1306 }
1307
1308 JSRetainPtr<JSStringRef> AccessibilityUIElement::columnIndexRange()
1309 {
1310     // Range in table for columns.
1311     return indexRangeInTable(m_element.get(), false);
1312 }
1313
1314 PassRefPtr<AccessibilityUIElement> AccessibilityUIElement::cellForColumnAndRow(unsigned col, unsigned row)
1315 {
1316     if (!ATK_IS_TABLE(m_element.get()))
1317         return nullptr;
1318
1319     // Adopt the AtkObject representing the cell because
1320     // at_table_ref_at() transfers full ownership.
1321     GRefPtr<AtkObject> foundCell = adoptGRef(atk_table_ref_at(ATK_TABLE(m_element.get()), row, col));
1322     return foundCell ? AccessibilityUIElement::create(foundCell.get()) : nullptr;
1323 }
1324
1325 PassRefPtr<AccessibilityUIElement> AccessibilityUIElement::horizontalScrollbar() const
1326 {
1327     // FIXME: implement
1328     return nullptr;
1329 }
1330
1331 PassRefPtr<AccessibilityUIElement> AccessibilityUIElement::verticalScrollbar() const
1332 {
1333     // FIXME: implement
1334     return nullptr;
1335 }
1336
1337 JSRetainPtr<JSStringRef> AccessibilityUIElement::selectedTextRange()
1338 {
1339     if (!ATK_IS_TEXT(m_element.get()))
1340         return JSStringCreateWithCharacters(0, 0);
1341
1342     gint start, end;
1343     g_free(atk_text_get_selection(ATK_TEXT(m_element.get()), 0, &start, &end));
1344
1345     GOwnPtr<gchar> selection(g_strdup_printf("{%d, %d}", start, end - start));
1346     return JSStringCreateWithUTF8CString(selection.get());
1347 }
1348
1349 void AccessibilityUIElement::setSelectedTextRange(unsigned location, unsigned length)
1350 {
1351     if (!ATK_IS_TEXT(m_element.get()))
1352         return;
1353
1354     atk_text_set_selection(ATK_TEXT(m_element.get()), 0, location, location + length);
1355 }
1356
1357 void AccessibilityUIElement::increment()
1358 {
1359     alterCurrentValue(m_element.get(), 1);
1360 }
1361
1362 void AccessibilityUIElement::decrement()
1363 {
1364     alterCurrentValue(m_element.get(), -1);
1365 }
1366
1367 void AccessibilityUIElement::showMenu()
1368 {
1369     // FIXME: implement
1370 }
1371
1372 void AccessibilityUIElement::press()
1373 {
1374     if (!ATK_IS_ACTION(m_element.get()))
1375         return;
1376
1377     // Only one action per object is supported so far.
1378     atk_action_do_action(ATK_ACTION(m_element.get()), 0);
1379 }
1380
1381 void AccessibilityUIElement::setSelectedChild(AccessibilityUIElement* element) const
1382 {
1383     // FIXME: implement
1384 }
1385
1386 JSRetainPtr<JSStringRef> AccessibilityUIElement::accessibilityValue() const
1387 {
1388     // FIXME: implement
1389     return JSStringCreateWithCharacters(0, 0);
1390 }
1391
1392 JSRetainPtr<JSStringRef> AccessibilityUIElement::documentEncoding()
1393 {
1394     if (!ATK_IS_DOCUMENT(m_element.get()))
1395         return JSStringCreateWithCharacters(0, 0);
1396
1397     AtkRole role = atk_object_get_role(ATK_OBJECT(m_element.get()));
1398     if (role != ATK_ROLE_DOCUMENT_FRAME)
1399         return JSStringCreateWithCharacters(0, 0);
1400
1401     return JSStringCreateWithUTF8CString(atk_document_get_attribute_value(ATK_DOCUMENT(m_element.get()), "Encoding"));
1402 }
1403
1404 JSRetainPtr<JSStringRef> AccessibilityUIElement::documentURI()
1405 {
1406     if (!ATK_IS_DOCUMENT(m_element.get()))
1407         return JSStringCreateWithCharacters(0, 0);
1408
1409     AtkRole role = atk_object_get_role(ATK_OBJECT(m_element.get()));
1410     if (role != ATK_ROLE_DOCUMENT_FRAME)
1411         return JSStringCreateWithCharacters(0, 0);
1412
1413     return JSStringCreateWithUTF8CString(atk_document_get_attribute_value(ATK_DOCUMENT(m_element.get()), "URI"));
1414 }
1415
1416 JSRetainPtr<JSStringRef> AccessibilityUIElement::url()
1417 {
1418     if (!ATK_IS_HYPERLINK_IMPL(m_element.get()))
1419         return JSStringCreateWithCharacters(0, 0);
1420
1421     AtkHyperlink* hyperlink = atk_hyperlink_impl_get_hyperlink(ATK_HYPERLINK_IMPL(m_element.get()));
1422     GOwnPtr<char> hyperlinkURI(atk_hyperlink_get_uri(hyperlink, 0));
1423
1424     // Build the result string, stripping the absolute URL paths if present.
1425     char* localURI = g_strstr_len(hyperlinkURI.get(), -1, "LayoutTests");
1426     String axURL = String::format("AXURL: %s", localURI ? localURI : hyperlinkURI.get());
1427     return JSStringCreateWithUTF8CString(axURL.utf8().data());
1428 }
1429
1430 bool AccessibilityUIElement::addNotificationListener(JSValueRef functionCallback)
1431 {
1432     if (!functionCallback)
1433         return false;
1434
1435     // Only one notification listener per element.
1436     if (m_notificationHandler)
1437         return false;
1438
1439     m_notificationHandler = AccessibilityNotificationHandler::create();
1440     m_notificationHandler->setPlatformElement(platformUIElement());
1441     m_notificationHandler->setNotificationFunctionCallback(functionCallback);
1442
1443     return true;
1444 }
1445
1446 bool AccessibilityUIElement::removeNotificationListener()
1447 {
1448     // Programmers should not be trying to remove a listener that's already removed.
1449     ASSERT(m_notificationHandler);
1450     m_notificationHandler = nullptr;
1451
1452     return true;
1453 }
1454
1455 bool AccessibilityUIElement::isFocusable() const
1456 {
1457     return checkElementState(m_element.get(), ATK_STATE_FOCUSABLE);
1458 }
1459
1460 bool AccessibilityUIElement::isSelectable() const
1461 {
1462     return checkElementState(m_element.get(), ATK_STATE_SELECTABLE);
1463 }
1464
1465 bool AccessibilityUIElement::isMultiSelectable() const
1466 {
1467     return checkElementState(m_element.get(), ATK_STATE_MULTISELECTABLE);
1468 }
1469
1470 bool AccessibilityUIElement::isVisible() const
1471 {
1472     return checkElementState(m_element.get(), ATK_STATE_VISIBLE);
1473 }
1474
1475 bool AccessibilityUIElement::isOffScreen() const
1476 {
1477     // FIXME: implement
1478     return false;
1479 }
1480
1481 bool AccessibilityUIElement::isCollapsed() const
1482 {
1483     // FIXME: implement
1484     return false;
1485 }
1486
1487 bool AccessibilityUIElement::isIgnored() const
1488 {
1489     // FIXME: implement
1490     return false;
1491 }
1492
1493 bool AccessibilityUIElement::hasPopup() const
1494 {
1495     if (!ATK_IS_OBJECT(m_element.get()))
1496         return false;
1497
1498     String hasPopupValue = getAttributeSetValueForId(ATK_OBJECT(m_element.get()), ObjectAttributeType, "haspopup");
1499     return equalIgnoringCase(hasPopupValue, "true");
1500 }
1501
1502 void AccessibilityUIElement::takeFocus()
1503 {
1504     // FIXME: implement
1505 }
1506
1507 void AccessibilityUIElement::takeSelection()
1508 {
1509     // FIXME: implement
1510 }
1511
1512 void AccessibilityUIElement::addSelection()
1513 {
1514     // FIXME: implement
1515 }
1516
1517 void AccessibilityUIElement::removeSelection()
1518 {
1519     // FIXME: implement
1520 }
1521
1522 // Text markers
1523 PassRefPtr<AccessibilityTextMarkerRange> AccessibilityUIElement::textMarkerRangeForElement(AccessibilityUIElement* element)
1524 {
1525     // FIXME: implement
1526     return nullptr;
1527 }
1528
1529 int AccessibilityUIElement::textMarkerRangeLength(AccessibilityTextMarkerRange* range)
1530 {
1531     // FIXME: implement
1532     return 0;
1533 }
1534
1535 PassRefPtr<AccessibilityTextMarker> AccessibilityUIElement::previousTextMarker(AccessibilityTextMarker* textMarker)
1536 {
1537     // FIXME: implement
1538     return nullptr;
1539 }
1540
1541 PassRefPtr<AccessibilityTextMarker> AccessibilityUIElement::nextTextMarker(AccessibilityTextMarker* textMarker)
1542 {
1543     // FIXME: implement
1544     return nullptr;
1545 }
1546
1547 JSRetainPtr<JSStringRef> AccessibilityUIElement::stringForTextMarkerRange(AccessibilityTextMarkerRange* markerRange)
1548 {
1549     // FIXME: implement
1550     return JSStringCreateWithCharacters(0, 0);
1551 }
1552
1553 PassRefPtr<AccessibilityTextMarkerRange> AccessibilityUIElement::textMarkerRangeForMarkers(AccessibilityTextMarker* startMarker, AccessibilityTextMarker* endMarker)
1554 {
1555     // FIXME: implement
1556     return nullptr;
1557 }
1558
1559 PassRefPtr<AccessibilityTextMarker> AccessibilityUIElement::startTextMarkerForTextMarkerRange(AccessibilityTextMarkerRange* range)
1560 {
1561     // FIXME: implement
1562     return nullptr;
1563 }
1564
1565 PassRefPtr<AccessibilityTextMarker> AccessibilityUIElement::endTextMarkerForTextMarkerRange(AccessibilityTextMarkerRange* range)
1566 {
1567     // FIXME: implement
1568     return nullptr;
1569 }
1570
1571 PassRefPtr<AccessibilityTextMarker> AccessibilityUIElement::endTextMarkerForBounds(int x, int y, int width, int height)
1572 {
1573     // FIXME: implement
1574     return nullptr;
1575 }
1576
1577 PassRefPtr<AccessibilityTextMarker> AccessibilityUIElement::startTextMarkerForBounds(int x, int y, int width, int height)
1578 {
1579     // FIXME: implement
1580     return nullptr;
1581 }
1582
1583 PassRefPtr<AccessibilityTextMarker> AccessibilityUIElement::textMarkerForPoint(int x, int y)
1584 {
1585     // FIXME: implement
1586     return nullptr;
1587 }
1588
1589 PassRefPtr<AccessibilityUIElement> AccessibilityUIElement::accessibilityElementForTextMarker(AccessibilityTextMarker* marker)
1590 {
1591     // FIXME: implement
1592     return nullptr;
1593 }
1594
1595 bool AccessibilityUIElement::attributedStringForTextMarkerRangeContainsAttribute(JSStringRef attribute, AccessibilityTextMarkerRange* range)
1596 {
1597     // FIXME: implement
1598     return false;
1599 }
1600
1601 int AccessibilityUIElement::indexForTextMarker(AccessibilityTextMarker* marker)
1602 {
1603     // FIXME: implement
1604     return -1;
1605 }
1606
1607 bool AccessibilityUIElement::isTextMarkerValid(AccessibilityTextMarker* textMarker)
1608 {
1609     // FIXME: implement
1610     return false;
1611 }
1612
1613 PassRefPtr<AccessibilityTextMarker> AccessibilityUIElement::textMarkerForIndex(int textIndex)
1614 {
1615     // FIXME: implement
1616     return nullptr;
1617 }
1618     
1619 PassRefPtr<AccessibilityTextMarker> AccessibilityUIElement::startTextMarker()
1620 {
1621     // FIXME: implement
1622     return nullptr;    
1623 }
1624
1625 PassRefPtr<AccessibilityTextMarker> AccessibilityUIElement::endTextMarker()
1626 {
1627     // FIXME: implement
1628     return nullptr;
1629 }
1630
1631 void AccessibilityUIElement::scrollToMakeVisible()
1632 {
1633     // FIXME: implement
1634 }
1635
1636 JSRetainPtr<JSStringRef> AccessibilityUIElement::supportedActions() const
1637 {
1638     // FIXME: implement
1639     return nullptr;
1640 }
1641
1642 JSRetainPtr<JSStringRef> AccessibilityUIElement::pathDescription() const
1643 {
1644     notImplemented();
1645     return nullptr;
1646 }
1647
1648 JSRetainPtr<JSStringRef> AccessibilityUIElement::mathPostscriptsDescription() const
1649 {
1650     notImplemented();
1651     return nullptr;
1652 }
1653
1654 JSRetainPtr<JSStringRef> AccessibilityUIElement::mathPrescriptsDescription() const
1655 {
1656     notImplemented();
1657     return nullptr;
1658 }
1659
1660 JSRetainPtr<JSStringRef> AccessibilityUIElement::classList() const
1661 {
1662     notImplemented();
1663     return nullptr;
1664 }
1665
1666 JSRetainPtr<JSStringRef> stringAtOffset(PlatformUIElement element, AtkTextBoundary boundary, int offset)
1667 {
1668     if (!ATK_IS_TEXT(element.get()))
1669         return JSStringCreateWithCharacters(0, 0);
1670
1671     gint startOffset, endOffset;
1672     StringBuilder builder;
1673
1674 #if ATK_CHECK_VERSION(2, 10, 0)
1675     AtkTextGranularity granularity;
1676     switch (boundary) {
1677     case ATK_TEXT_BOUNDARY_CHAR:
1678         granularity = ATK_TEXT_GRANULARITY_CHAR;
1679         break;
1680     case ATK_TEXT_BOUNDARY_WORD_START:
1681         granularity = ATK_TEXT_GRANULARITY_WORD;
1682         break;
1683     case ATK_TEXT_BOUNDARY_LINE_START:
1684         granularity = ATK_TEXT_GRANULARITY_LINE;
1685         break;
1686     case ATK_TEXT_BOUNDARY_SENTENCE_START:
1687         granularity = ATK_TEXT_GRANULARITY_SENTENCE;
1688         break;
1689     default:
1690         return JSStringCreateWithCharacters(0, 0);
1691     }
1692
1693     builder.append(atk_text_get_string_at_offset(ATK_TEXT(element.get()), offset, granularity, &startOffset, &endOffset));
1694 #else
1695     builder.append(atk_text_get_text_at_offset(ATK_TEXT(element.get()), offset, boundary, &startOffset, &endOffset));
1696 #endif
1697     builder.append(String::format(", %i, %i", startOffset, endOffset));
1698     return JSStringCreateWithUTF8CString(builder.toString().utf8().data());
1699 }
1700
1701 JSRetainPtr<JSStringRef> AccessibilityUIElement::characterAtOffset(int offset)
1702 {
1703     return stringAtOffset(m_element, ATK_TEXT_BOUNDARY_CHAR, offset);
1704 }
1705
1706 JSRetainPtr<JSStringRef> AccessibilityUIElement::wordAtOffset(int offset)
1707 {
1708     return stringAtOffset(m_element, ATK_TEXT_BOUNDARY_WORD_START, offset);
1709 }
1710
1711 JSRetainPtr<JSStringRef> AccessibilityUIElement::lineAtOffset(int offset)
1712 {
1713     return stringAtOffset(m_element, ATK_TEXT_BOUNDARY_LINE_START, offset);
1714 }
1715
1716 JSRetainPtr<JSStringRef> AccessibilityUIElement::sentenceAtOffset(int offset)
1717 {
1718     return stringAtOffset(m_element, ATK_TEXT_BOUNDARY_SENTENCE_START, offset);
1719 }
1720
1721 } // namespace WTR
1722
1723 #endif // HAVE(ACCESSIBILITY)