b8931052be2edd24e2d0711d9b163aeb9462a896
[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/GRefPtr.h>
41 #include <wtf/gobject/GUniquePtr.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     GUniquePtr<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         GUniquePtr<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     GUniquePtr<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.reset(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 static PassRefPtr<AccessibilityUIElement> accessibilityElementAtIndex(AtkObject* element, AtkRelationType relationType, unsigned index)
646 {
647     if (!ATK_IS_OBJECT(element))
648         return nullptr;
649
650     AtkRelationSet* relationSet = atk_object_ref_relation_set(element);
651     if (!relationSet)
652         return nullptr;
653
654     AtkRelation* relation = atk_relation_set_get_relation_by_type(relationSet, relationType);
655     if (!relation)
656         return nullptr;
657
658     GPtrArray* targetList = atk_relation_get_target(relation);
659     if (!targetList || !targetList->len || index >= targetList->len)
660         return nullptr;
661
662     AtkObject* target = static_cast<AtkObject*>(g_ptr_array_index(targetList, index));
663     g_object_unref(relationSet);
664
665     return target ? AccessibilityUIElement::create(target) : nullptr;
666 }
667
668 PassRefPtr<AccessibilityUIElement> AccessibilityUIElement::linkedUIElementAtIndex(unsigned index)
669 {
670     // FIXME: implement
671     return nullptr;
672 }
673
674 PassRefPtr<AccessibilityUIElement> AccessibilityUIElement::ariaOwnsElementAtIndex(unsigned index)
675 {
676     // FIXME: implement
677     return nullptr;
678 }
679
680 PassRefPtr<AccessibilityUIElement> AccessibilityUIElement::ariaFlowToElementAtIndex(unsigned index)
681 {
682     return accessibilityElementAtIndex(m_element.get(), ATK_RELATION_FLOWS_TO, index);
683 }
684
685 PassRefPtr<AccessibilityUIElement> AccessibilityUIElement::ariaControlsElementAtIndex(unsigned index)
686 {
687     return accessibilityElementAtIndex(m_element.get(), ATK_RELATION_CONTROLLER_FOR, index);
688 }
689
690 PassRefPtr<AccessibilityUIElement> AccessibilityUIElement::disclosedRowAtIndex(unsigned index)
691 {
692     // FIXME: implement
693     return nullptr;
694 }
695
696 PassRefPtr<AccessibilityUIElement> AccessibilityUIElement::rowAtIndex(unsigned index)
697 {
698     // FIXME: implement
699     return nullptr;
700 }
701
702 PassRefPtr<AccessibilityUIElement> AccessibilityUIElement::selectedChildAtIndex(unsigned index) const
703 {
704     // FIXME: implement
705     return nullptr;
706 }
707
708 unsigned AccessibilityUIElement::selectedChildrenCount() const
709 {
710     // FIXME: implement
711     return 0;
712 }
713
714 PassRefPtr<AccessibilityUIElement> AccessibilityUIElement::selectedRowAtIndex(unsigned index)
715 {
716     // FIXME: implement
717     return nullptr;
718 }
719
720 PassRefPtr<AccessibilityUIElement> AccessibilityUIElement::titleUIElement()
721 {
722     if (!ATK_IS_OBJECT(m_element.get()))
723         return nullptr;
724
725     AtkRelationSet* set = atk_object_ref_relation_set(ATK_OBJECT(m_element.get()));
726     if (!set)
727         return nullptr;
728
729     AtkObject* target = nullptr;
730     int count = atk_relation_set_get_n_relations(set);
731     for (int i = 0; i < count; i++) {
732         AtkRelation* relation = atk_relation_set_get_relation(set, i);
733         if (atk_relation_get_relation_type(relation) == ATK_RELATION_LABELLED_BY) {
734             GPtrArray* targetList = atk_relation_get_target(relation);
735             if (targetList->len)
736                 target = static_cast<AtkObject*>(g_ptr_array_index(targetList, 0));
737         }
738     }
739
740     g_object_unref(set);
741     return target ? AccessibilityUIElement::create(target) : nullptr;
742 }
743
744 PassRefPtr<AccessibilityUIElement> AccessibilityUIElement::parentElement()
745 {
746     if (!ATK_IS_OBJECT(m_element.get()))
747         return nullptr;
748
749     AtkObject* parent = atk_object_get_parent(ATK_OBJECT(m_element.get()));
750     return parent ? AccessibilityUIElement::create(parent) : nullptr;
751 }
752
753 PassRefPtr<AccessibilityUIElement> AccessibilityUIElement::disclosedByRow()
754 {
755     // FIXME: implement
756     return nullptr;
757 }
758
759 JSRetainPtr<JSStringRef> AccessibilityUIElement::attributesOfLinkedUIElements()
760 {
761     // FIXME: implement
762     return JSStringCreateWithCharacters(0, 0);
763 }
764
765 JSRetainPtr<JSStringRef> AccessibilityUIElement::attributesOfDocumentLinks()
766 {
767     // FIXME: implement
768     return JSStringCreateWithCharacters(0, 0);
769 }
770
771 JSRetainPtr<JSStringRef> AccessibilityUIElement::attributesOfChildren()
772 {
773     if (!ATK_IS_OBJECT(m_element.get()))
774         return JSStringCreateWithCharacters(0, 0);
775
776     Vector<RefPtr<AccessibilityUIElement> > children;
777     getChildren(children);
778
779     return createStringWithAttributes(children);
780 }
781
782 JSRetainPtr<JSStringRef> AccessibilityUIElement::allAttributes()
783 {
784     if (!ATK_IS_OBJECT(m_element.get()))
785         return JSStringCreateWithCharacters(0, 0);
786
787     return JSStringCreateWithUTF8CString(attributesOfElement(this).utf8().data());
788 }
789
790 JSRetainPtr<JSStringRef> AccessibilityUIElement::stringAttributeValue(JSStringRef attribute)
791 {
792     if (!ATK_IS_OBJECT(m_element.get()))
793         return JSStringCreateWithCharacters(0, 0);
794
795     String atkAttributeName = coreAttributeToAtkAttribute(attribute);
796
797     // Try object attributes first.
798     String attributeValue = getAttributeSetValueForId(ATK_OBJECT(m_element.get()), ObjectAttributeType, atkAttributeName);
799
800     // Try text attributes if the requested one was not found and we have an AtkText object.
801     if (attributeValue.isEmpty() && ATK_IS_TEXT(m_element.get()))
802         attributeValue = getAttributeSetValueForId(ATK_OBJECT(m_element.get()), TextAttributeType, atkAttributeName);
803
804     // Additional check to make sure that the exposure of the state ATK_STATE_INVALID_ENTRY
805     // is consistent with the exposure of aria-invalid as a text attribute, if present.
806     if (atkAttributeName == attributesMap[InvalidNameIndex][AtkDomain]) {
807         bool isInvalidState = checkElementState(m_element.get(), ATK_STATE_INVALID_ENTRY);
808         if (attributeValue.isEmpty())
809             return JSStringCreateWithUTF8CString(isInvalidState ? "true" : "false");
810
811         // If the text attribute was there, check that it's consistent with
812         // what the state says or force the test to fail otherwise.
813         bool isAriaInvalid = attributeValue != "false";
814         if (isInvalidState != isAriaInvalid)
815             return JSStringCreateWithCharacters(0, 0);
816     }
817
818     return JSStringCreateWithUTF8CString(attributeValue.utf8().data());
819 }
820
821 double AccessibilityUIElement::numberAttributeValue(JSStringRef attribute)
822 {
823     // FIXME: implement
824     return 0;
825 }
826
827 JSValueRef AccessibilityUIElement::uiElementArrayAttributeValue(JSStringRef attribute) const
828 {
829     // FIXME: implement
830     return nullptr;
831 }
832
833 JSValueRef AccessibilityUIElement::rowHeaders() const
834 {
835     // FIXME: implement
836     return nullptr;
837 }
838
839 JSValueRef AccessibilityUIElement::columnHeaders() const
840 {
841     // FIXME: implement
842     return nullptr;
843 }
844
845 PassRefPtr<AccessibilityUIElement> AccessibilityUIElement::uiElementAttributeValue(JSStringRef attribute) const
846 {
847     // FIXME: implement
848     return nullptr;
849 }
850
851 bool AccessibilityUIElement::boolAttributeValue(JSStringRef attribute)
852 {
853     // FIXME: implement
854     return false;
855 }
856
857 bool AccessibilityUIElement::isAttributeSettable(JSStringRef attribute)
858 {
859     if (!ATK_IS_OBJECT(m_element.get()))
860         return false;
861
862     String attributeString = jsStringToWTFString(attribute);
863     if (attributeString == "AXValue")
864         return checkElementState(m_element.get(), ATK_STATE_EDITABLE);
865
866     return false;
867 }
868
869 bool AccessibilityUIElement::isAttributeSupported(JSStringRef attribute)
870 {
871     if (!ATK_IS_OBJECT(m_element.get()))
872         return false;
873
874     String atkAttributeName = coreAttributeToAtkAttribute(attribute);
875     if (atkAttributeName.isEmpty())
876         return false;
877
878     // For now, an attribute is supported whether it's exposed as a object or a text attribute.
879     String attributeValue = getAttributeSetValueForId(ATK_OBJECT(m_element.get()), ObjectAttributeType, atkAttributeName);
880     if (attributeValue.isEmpty())
881         attributeValue = getAttributeSetValueForId(ATK_OBJECT(m_element.get()), TextAttributeType, atkAttributeName);
882
883     return !attributeValue.isEmpty();
884 }
885
886 JSRetainPtr<JSStringRef> AccessibilityUIElement::parameterizedAttributeNames()
887 {
888     // FIXME: implement
889     return JSStringCreateWithCharacters(0, 0);
890 }
891
892 JSRetainPtr<JSStringRef> AccessibilityUIElement::role()
893 {
894     if (!ATK_IS_OBJECT(m_element.get()))
895         return JSStringCreateWithCharacters(0, 0);
896
897     if (!atk_object_get_role(ATK_OBJECT(m_element.get())))
898         return JSStringCreateWithCharacters(0, 0);
899
900     GUniquePtr<char> roleStringWithPrefix(g_strdup_printf("AXRole: %s", roleToString(ATK_OBJECT(m_element.get()))));
901     return JSStringCreateWithUTF8CString(roleStringWithPrefix.get());
902 }
903
904 JSRetainPtr<JSStringRef> AccessibilityUIElement::subrole()
905 {
906     // FIXME: implement
907     return JSStringCreateWithCharacters(0, 0);
908 }
909
910 JSRetainPtr<JSStringRef> AccessibilityUIElement::roleDescription()
911 {
912     // FIXME: implement
913     return JSStringCreateWithCharacters(0, 0);
914 }
915
916 JSRetainPtr<JSStringRef> AccessibilityUIElement::computedRoleString()
917 {
918     // FIXME: implement http://webkit.org/b/128420
919     return JSStringCreateWithCharacters(0, 0);
920 }
921
922 JSRetainPtr<JSStringRef> AccessibilityUIElement::title()
923 {
924     if (!ATK_IS_OBJECT(m_element.get()))
925         return JSStringCreateWithCharacters(0, 0);
926
927     const gchar* name = atk_object_get_name(ATK_OBJECT(m_element.get()));
928     GUniquePtr<gchar> axTitle(g_strdup_printf("AXTitle: %s", name ? name : ""));
929
930     return JSStringCreateWithUTF8CString(axTitle.get());
931 }
932
933 JSRetainPtr<JSStringRef> AccessibilityUIElement::description()
934 {
935     if (!ATK_IS_OBJECT(m_element.get()))
936         return JSStringCreateWithCharacters(0, 0);
937
938     const gchar* description = atk_object_get_description(ATK_OBJECT(m_element.get()));
939     if (!description)
940         return JSStringCreateWithCharacters(0, 0);
941
942     GUniquePtr<gchar> axDesc(g_strdup_printf("AXDescription: %s", description));
943
944     return JSStringCreateWithUTF8CString(axDesc.get());
945 }
946
947 JSRetainPtr<JSStringRef> AccessibilityUIElement::orientation() const
948 {
949     if (!ATK_IS_OBJECT(m_element.get()))
950         return JSStringCreateWithCharacters(0, 0);
951
952     const gchar* axOrientation = nullptr;
953     if (checkElementState(m_element.get(), ATK_STATE_HORIZONTAL))
954         axOrientation = "AXOrientation: AXHorizontalOrientation";
955     else if (checkElementState(m_element.get(), ATK_STATE_VERTICAL))
956         axOrientation = "AXOrientation: AXVerticalOrientation";
957
958     if (!axOrientation)
959         return JSStringCreateWithCharacters(0, 0);
960
961     return JSStringCreateWithUTF8CString(axOrientation);
962 }
963
964 JSRetainPtr<JSStringRef> AccessibilityUIElement::stringValue()
965 {
966     if (!ATK_IS_TEXT(m_element.get()))
967         return JSStringCreateWithCharacters(0, 0);
968
969     GUniquePtr<gchar> text(atk_text_get_text(ATK_TEXT(m_element.get()), 0, -1));
970     GUniquePtr<gchar> textWithReplacedCharacters(replaceCharactersForResults(text.get()));
971     GUniquePtr<gchar> axValue(g_strdup_printf("AXValue: %s", textWithReplacedCharacters.get()));
972
973     return JSStringCreateWithUTF8CString(axValue.get());
974 }
975
976 JSRetainPtr<JSStringRef> AccessibilityUIElement::language()
977 {
978     if (!ATK_IS_OBJECT(m_element.get()))
979         return JSStringCreateWithCharacters(0, 0);
980
981     const gchar* locale = atk_object_get_object_locale(ATK_OBJECT(m_element.get()));
982     if (!locale)
983         return JSStringCreateWithCharacters(0, 0);
984
985     GUniquePtr<char> axValue(g_strdup_printf("AXLanguage: %s", locale));
986     return JSStringCreateWithUTF8CString(axValue.get());
987 }
988
989 JSRetainPtr<JSStringRef> AccessibilityUIElement::helpText() const
990 {
991     if (!ATK_IS_OBJECT(m_element.get()))
992         return JSStringCreateWithCharacters(0, 0);
993
994     AtkRelationSet* relationSet = atk_object_ref_relation_set(ATK_OBJECT(m_element.get()));
995     if (!relationSet)
996         return nullptr;
997
998     AtkRelation* relation = atk_relation_set_get_relation_by_type(relationSet, ATK_RELATION_DESCRIBED_BY);
999     if (!relation)
1000         return nullptr;
1001
1002     GPtrArray* targetList = atk_relation_get_target(relation);
1003     if (!targetList || !targetList->len)
1004         return nullptr;
1005
1006     StringBuilder builder;
1007     builder.append("AXHelp: ");
1008
1009     for (int targetCount = 0; targetCount < targetList->len; targetCount++) {
1010         if (AtkObject* target = static_cast<AtkObject*>(g_ptr_array_index(targetList, targetCount))) {
1011             GUniquePtr<gchar> text(atk_text_get_text(ATK_TEXT(target), 0, -1));
1012             if (targetCount)
1013                 builder.append(" ");
1014             builder.append(text.get());
1015         }
1016     }
1017
1018     g_object_unref(relationSet);
1019
1020     return JSStringCreateWithUTF8CString(builder.toString().utf8().data());
1021 }
1022
1023 double AccessibilityUIElement::x()
1024 {
1025     if (!ATK_IS_COMPONENT(m_element.get()))
1026         return 0;
1027
1028     int x, y;
1029     atk_component_get_position(ATK_COMPONENT(m_element.get()), &x, &y, ATK_XY_SCREEN);
1030     return x;
1031 }
1032
1033 double AccessibilityUIElement::y()
1034 {
1035     if (!ATK_IS_COMPONENT(m_element.get()))
1036         return 0;
1037
1038     int x, y;
1039     atk_component_get_position(ATK_COMPONENT(m_element.get()), &x, &y, ATK_XY_SCREEN);
1040     return y;
1041 }
1042
1043 double AccessibilityUIElement::width()
1044 {
1045     if (!ATK_IS_COMPONENT(m_element.get()))
1046         return 0;
1047
1048     int width, height;
1049     atk_component_get_size(ATK_COMPONENT(m_element.get()), &width, &height);
1050     return width;
1051 }
1052
1053 double AccessibilityUIElement::height()
1054 {
1055     if (!ATK_IS_COMPONENT(m_element.get()))
1056         return 0;
1057
1058     int width, height;
1059     atk_component_get_size(ATK_COMPONENT(m_element.get()), &width, &height);
1060     return height;
1061 }
1062
1063 double AccessibilityUIElement::clickPointX()
1064 {
1065     if (!ATK_IS_COMPONENT(m_element.get()))
1066         return 0;
1067
1068     int x, y;
1069     atk_component_get_position(ATK_COMPONENT(m_element.get()), &x, &y, ATK_XY_WINDOW);
1070
1071     int width, height;
1072     atk_component_get_size(ATK_COMPONENT(m_element.get()), &width, &height);
1073
1074     return x + width / 2.0;
1075 }
1076
1077 double AccessibilityUIElement::clickPointY()
1078 {
1079     if (!ATK_IS_COMPONENT(m_element.get()))
1080         return 0;
1081
1082     int x, y;
1083     atk_component_get_position(ATK_COMPONENT(m_element.get()), &x, &y, ATK_XY_WINDOW);
1084
1085     int width, height;
1086     atk_component_get_size(ATK_COMPONENT(m_element.get()), &width, &height);
1087
1088     return y + height / 2.0;
1089 }
1090
1091 double AccessibilityUIElement::intValue() const
1092 {
1093     if (!ATK_IS_OBJECT(m_element.get()))
1094         return 0;
1095
1096     if (ATK_IS_VALUE(m_element.get())) {
1097         GValue value = G_VALUE_INIT;
1098         atk_value_get_current_value(ATK_VALUE(m_element.get()), &value);
1099         if (!G_VALUE_HOLDS_FLOAT(&value))
1100             return 0;
1101         return g_value_get_float(&value);
1102     }
1103
1104     // Consider headings as an special case when returning the "int value" of
1105     // an AccessibilityUIElement, so we can reuse some tests to check the level
1106     // both for HTML headings and objects with the aria-level attribute.
1107     if (atk_object_get_role(ATK_OBJECT(m_element.get())) == ATK_ROLE_HEADING) {
1108         String headingLevel = getAttributeSetValueForId(ATK_OBJECT(m_element.get()), ObjectAttributeType, "level");
1109         bool ok;
1110         double headingLevelValue = headingLevel.toDouble(&ok);
1111         if (ok)
1112             return headingLevelValue;
1113     }
1114
1115     return 0;
1116 }
1117
1118 double AccessibilityUIElement::minValue()
1119 {
1120     if (!ATK_IS_VALUE(m_element.get()))
1121         return 0;
1122
1123     GValue value = G_VALUE_INIT;
1124     atk_value_get_minimum_value(ATK_VALUE(m_element.get()), &value);
1125     if (!G_VALUE_HOLDS_FLOAT(&value))
1126         return 0;
1127
1128     return g_value_get_float(&value);
1129 }
1130
1131 double AccessibilityUIElement::maxValue()
1132 {
1133     if (!ATK_IS_VALUE(m_element.get()))
1134         return 0;
1135
1136     GValue value = G_VALUE_INIT;
1137     atk_value_get_maximum_value(ATK_VALUE(m_element.get()), &value);
1138     if (!G_VALUE_HOLDS_FLOAT(&value))
1139         return 0;
1140
1141     return g_value_get_float(&value);
1142 }
1143
1144 JSRetainPtr<JSStringRef> AccessibilityUIElement::valueDescription()
1145 {
1146     // FIXME: implement
1147     return JSStringCreateWithCharacters(0, 0);
1148 }
1149
1150 int AccessibilityUIElement::insertionPointLineNumber()
1151 {
1152     // FIXME: implement
1153     return -1;
1154 }
1155
1156 bool AccessibilityUIElement::isPressActionSupported()
1157 {
1158     if (!ATK_IS_ACTION(m_element.get()))
1159         return false;
1160
1161     const gchar* actionName = atk_action_get_name(ATK_ACTION(m_element.get()), 0);
1162     return equalIgnoringCase(actionName, String("press")) || equalIgnoringCase(actionName, String("jump"));
1163 }
1164
1165 bool AccessibilityUIElement::isIncrementActionSupported()
1166 {
1167     // FIXME: implement
1168     return false;
1169 }
1170
1171 bool AccessibilityUIElement::isDecrementActionSupported()
1172 {
1173     // FIXME: implement
1174     return false;
1175 }
1176
1177 bool AccessibilityUIElement::isEnabled()
1178 {
1179     return checkElementState(m_element.get(), ATK_STATE_ENABLED);
1180 }
1181
1182 bool AccessibilityUIElement::isRequired() const
1183 {
1184     return checkElementState(m_element.get(), ATK_STATE_REQUIRED);
1185 }
1186
1187 bool AccessibilityUIElement::isFocused() const
1188 {
1189     return checkElementState(m_element.get(), ATK_STATE_FOCUSED);
1190 }
1191
1192 bool AccessibilityUIElement::isSelected() const
1193 {
1194     return checkElementState(m_element.get(), ATK_STATE_SELECTED);
1195 }
1196
1197 bool AccessibilityUIElement::isSelectedOptionActive() const
1198 {
1199     return checkElementState(m_element.get(), ATK_STATE_ACTIVE);
1200 }
1201
1202 bool AccessibilityUIElement::isExpanded() const
1203 {
1204     return checkElementState(m_element.get(), ATK_STATE_EXPANDED);
1205 }
1206
1207 bool AccessibilityUIElement::isChecked() const
1208 {
1209     return checkElementState(m_element.get(), ATK_STATE_CHECKED);
1210 }
1211
1212 bool AccessibilityUIElement::isIndeterminate() const
1213 {
1214     return checkElementState(m_element.get(), ATK_STATE_INDETERMINATE);
1215 }
1216
1217 int AccessibilityUIElement::hierarchicalLevel() const
1218 {
1219     // FIXME: implement
1220     return 0;
1221 }
1222
1223 JSRetainPtr<JSStringRef> AccessibilityUIElement::speak()
1224 {
1225     // FIXME: implement
1226     return JSStringCreateWithCharacters(0, 0);
1227 }
1228
1229 bool AccessibilityUIElement::ariaIsGrabbed() const
1230 {
1231     // FIXME: implement
1232     return false;
1233 }
1234
1235 JSRetainPtr<JSStringRef> AccessibilityUIElement::ariaDropEffects() const
1236 {
1237     // FIXME: implement
1238     return JSStringCreateWithCharacters(0, 0);
1239 }
1240
1241 // parameterized attributes
1242 int AccessibilityUIElement::lineForIndex(int index)
1243 {
1244     if (!ATK_IS_TEXT(m_element.get()))
1245         return -1;
1246
1247     if (index < 0 || index > atk_text_get_character_count(ATK_TEXT(m_element.get())))
1248         return -1;
1249
1250     GUniquePtr<gchar> text(atk_text_get_text(ATK_TEXT(m_element.get()), 0, index));
1251     int lineNo = 0;
1252     for (gchar* offset = text.get(); *offset; ++offset) {
1253         if (*offset == '\n')
1254             ++lineNo;
1255     }
1256
1257     return lineNo;
1258 }
1259
1260 JSRetainPtr<JSStringRef> AccessibilityUIElement::rangeForLine(int line)
1261 {
1262     // FIXME: implement
1263     return JSStringCreateWithCharacters(0, 0);
1264 }
1265
1266 JSRetainPtr<JSStringRef> AccessibilityUIElement::rangeForPosition(int x, int y)
1267 {
1268     // FIXME: implement
1269     return JSStringCreateWithCharacters(0, 0);
1270 }
1271
1272 JSRetainPtr<JSStringRef> AccessibilityUIElement::boundsForRange(unsigned location, unsigned length)
1273 {
1274     // FIXME: implement
1275     return JSStringCreateWithCharacters(0, 0);
1276 }
1277
1278 JSRetainPtr<JSStringRef> AccessibilityUIElement::stringForRange(unsigned location, unsigned length)
1279 {
1280     if (!ATK_IS_TEXT(m_element.get()))
1281         return JSStringCreateWithCharacters(0, 0);
1282
1283     String string = atk_text_get_text(ATK_TEXT(m_element.get()), location, location + length);
1284     return JSStringCreateWithUTF8CString(string.utf8().data());
1285 }
1286
1287 JSRetainPtr<JSStringRef> AccessibilityUIElement::attributedStringForRange(unsigned location, unsigned length)
1288 {
1289     // FIXME: implement
1290     return JSStringCreateWithCharacters(0, 0);
1291 }
1292
1293 bool AccessibilityUIElement::attributedStringRangeIsMisspelled(unsigned location, unsigned length)
1294 {
1295     // FIXME: implement
1296     return false;
1297 }
1298
1299 unsigned AccessibilityUIElement::uiElementCountForSearchPredicate(JSContextRef context, AccessibilityUIElement* startElement, bool isDirectionNext, JSValueRef searchKey, JSStringRef searchText, bool visibleOnly)
1300 {
1301     // FIXME: implement
1302     return 0;
1303 }
1304
1305 PassRefPtr<AccessibilityUIElement> AccessibilityUIElement::uiElementForSearchPredicate(JSContextRef context, AccessibilityUIElement* startElement, bool isDirectionNext, JSValueRef searchKey, JSStringRef searchText, bool visibleOnly)
1306 {
1307     // FIXME: implement
1308     return nullptr;
1309 }
1310
1311 JSRetainPtr<JSStringRef> AccessibilityUIElement::attributesOfColumnHeaders()
1312 {
1313     if (!ATK_IS_TABLE(m_element.get()))
1314         return JSStringCreateWithCharacters(0, 0);
1315
1316     Vector<RefPtr<AccessibilityUIElement> > columnHeaders = getColumnHeaders(ATK_TABLE(m_element.get()));
1317     return createStringWithAttributes(columnHeaders);
1318 }
1319
1320 JSRetainPtr<JSStringRef> AccessibilityUIElement::attributesOfRowHeaders()
1321 {
1322     if (!ATK_IS_TABLE(m_element.get()))
1323         return JSStringCreateWithCharacters(0, 0);
1324
1325     Vector<RefPtr<AccessibilityUIElement> > rowHeaders = getRowHeaders(ATK_TABLE(m_element.get()));
1326     return createStringWithAttributes(rowHeaders);
1327 }
1328
1329 JSRetainPtr<JSStringRef> AccessibilityUIElement::attributesOfColumns()
1330 {
1331     // FIXME: implement
1332     return JSStringCreateWithCharacters(0, 0);
1333 }
1334
1335 JSRetainPtr<JSStringRef> AccessibilityUIElement::attributesOfRows()
1336 {
1337     // FIXME: implement
1338     return JSStringCreateWithCharacters(0, 0);
1339 }
1340
1341 JSRetainPtr<JSStringRef> AccessibilityUIElement::attributesOfVisibleCells()
1342 {
1343     if (!ATK_IS_TABLE(m_element.get()))
1344         return JSStringCreateWithCharacters(0, 0);
1345
1346     Vector<RefPtr<AccessibilityUIElement> > visibleCells = getVisibleCells(this);
1347     return createStringWithAttributes(visibleCells);
1348 }
1349
1350 JSRetainPtr<JSStringRef> AccessibilityUIElement::attributesOfHeader()
1351 {
1352     // FIXME: implement
1353     return JSStringCreateWithCharacters(0, 0);
1354 }
1355
1356 int AccessibilityUIElement::rowCount()
1357 {
1358     if (!ATK_IS_TABLE(m_element.get()))
1359         return 0;
1360
1361     return atk_table_get_n_rows(ATK_TABLE(m_element.get()));
1362 }
1363
1364 int AccessibilityUIElement::columnCount()
1365 {
1366     if (!ATK_IS_TABLE(m_element.get()))
1367         return 0;
1368
1369     return atk_table_get_n_columns(ATK_TABLE(m_element.get()));
1370 }
1371
1372 int AccessibilityUIElement::indexInTable()
1373 {
1374     // FIXME: implement
1375     return -1;
1376 }
1377
1378 JSRetainPtr<JSStringRef> AccessibilityUIElement::rowIndexRange()
1379 {
1380     // Range in table for rows.
1381     return indexRangeInTable(m_element.get(), true);
1382 }
1383
1384 JSRetainPtr<JSStringRef> AccessibilityUIElement::columnIndexRange()
1385 {
1386     // Range in table for columns.
1387     return indexRangeInTable(m_element.get(), false);
1388 }
1389
1390 PassRefPtr<AccessibilityUIElement> AccessibilityUIElement::cellForColumnAndRow(unsigned col, unsigned row)
1391 {
1392     if (!ATK_IS_TABLE(m_element.get()))
1393         return nullptr;
1394
1395     // Adopt the AtkObject representing the cell because
1396     // at_table_ref_at() transfers full ownership.
1397     GRefPtr<AtkObject> foundCell = adoptGRef(atk_table_ref_at(ATK_TABLE(m_element.get()), row, col));
1398     return foundCell ? AccessibilityUIElement::create(foundCell.get()) : nullptr;
1399 }
1400
1401 PassRefPtr<AccessibilityUIElement> AccessibilityUIElement::horizontalScrollbar() const
1402 {
1403     // FIXME: implement
1404     return nullptr;
1405 }
1406
1407 PassRefPtr<AccessibilityUIElement> AccessibilityUIElement::verticalScrollbar() const
1408 {
1409     // FIXME: implement
1410     return nullptr;
1411 }
1412
1413 JSRetainPtr<JSStringRef> AccessibilityUIElement::selectedTextRange()
1414 {
1415     if (!ATK_IS_TEXT(m_element.get()))
1416         return JSStringCreateWithCharacters(0, 0);
1417
1418     gint start, end;
1419     g_free(atk_text_get_selection(ATK_TEXT(m_element.get()), 0, &start, &end));
1420
1421     GUniquePtr<gchar> selection(g_strdup_printf("{%d, %d}", start, end - start));
1422     return JSStringCreateWithUTF8CString(selection.get());
1423 }
1424
1425 void AccessibilityUIElement::setSelectedTextRange(unsigned location, unsigned length)
1426 {
1427     if (!ATK_IS_TEXT(m_element.get()))
1428         return;
1429
1430     atk_text_set_selection(ATK_TEXT(m_element.get()), 0, location, location + length);
1431 }
1432
1433 void AccessibilityUIElement::increment()
1434 {
1435     alterCurrentValue(m_element.get(), 1);
1436 }
1437
1438 void AccessibilityUIElement::decrement()
1439 {
1440     alterCurrentValue(m_element.get(), -1);
1441 }
1442
1443 void AccessibilityUIElement::showMenu()
1444 {
1445     // FIXME: implement
1446 }
1447
1448 void AccessibilityUIElement::press()
1449 {
1450     if (!ATK_IS_ACTION(m_element.get()))
1451         return;
1452
1453     // Only one action per object is supported so far.
1454     atk_action_do_action(ATK_ACTION(m_element.get()), 0);
1455 }
1456
1457 void AccessibilityUIElement::setSelectedChild(AccessibilityUIElement* element) const
1458 {
1459     // FIXME: implement
1460 }
1461
1462 JSRetainPtr<JSStringRef> AccessibilityUIElement::accessibilityValue() const
1463 {
1464     // FIXME: implement
1465     return JSStringCreateWithCharacters(0, 0);
1466 }
1467
1468 JSRetainPtr<JSStringRef> AccessibilityUIElement::documentEncoding()
1469 {
1470     if (!ATK_IS_DOCUMENT(m_element.get()))
1471         return JSStringCreateWithCharacters(0, 0);
1472
1473     AtkRole role = atk_object_get_role(ATK_OBJECT(m_element.get()));
1474     if (role != ATK_ROLE_DOCUMENT_FRAME)
1475         return JSStringCreateWithCharacters(0, 0);
1476
1477     return JSStringCreateWithUTF8CString(atk_document_get_attribute_value(ATK_DOCUMENT(m_element.get()), "Encoding"));
1478 }
1479
1480 JSRetainPtr<JSStringRef> AccessibilityUIElement::documentURI()
1481 {
1482     if (!ATK_IS_DOCUMENT(m_element.get()))
1483         return JSStringCreateWithCharacters(0, 0);
1484
1485     AtkRole role = atk_object_get_role(ATK_OBJECT(m_element.get()));
1486     if (role != ATK_ROLE_DOCUMENT_FRAME)
1487         return JSStringCreateWithCharacters(0, 0);
1488
1489     return JSStringCreateWithUTF8CString(atk_document_get_attribute_value(ATK_DOCUMENT(m_element.get()), "URI"));
1490 }
1491
1492 JSRetainPtr<JSStringRef> AccessibilityUIElement::url()
1493 {
1494     if (!ATK_IS_HYPERLINK_IMPL(m_element.get()))
1495         return JSStringCreateWithCharacters(0, 0);
1496
1497     AtkHyperlink* hyperlink = atk_hyperlink_impl_get_hyperlink(ATK_HYPERLINK_IMPL(m_element.get()));
1498     GUniquePtr<char> hyperlinkURI(atk_hyperlink_get_uri(hyperlink, 0));
1499
1500     // Build the result string, stripping the absolute URL paths if present.
1501     char* localURI = g_strstr_len(hyperlinkURI.get(), -1, "LayoutTests");
1502     String axURL = String::format("AXURL: %s", localURI ? localURI : hyperlinkURI.get());
1503     return JSStringCreateWithUTF8CString(axURL.utf8().data());
1504 }
1505
1506 bool AccessibilityUIElement::addNotificationListener(JSValueRef functionCallback)
1507 {
1508     if (!functionCallback)
1509         return false;
1510
1511     // Only one notification listener per element.
1512     if (m_notificationHandler)
1513         return false;
1514
1515     m_notificationHandler = AccessibilityNotificationHandler::create();
1516     m_notificationHandler->setPlatformElement(platformUIElement());
1517     m_notificationHandler->setNotificationFunctionCallback(functionCallback);
1518
1519     return true;
1520 }
1521
1522 bool AccessibilityUIElement::removeNotificationListener()
1523 {
1524     // Programmers should not be trying to remove a listener that's already removed.
1525     ASSERT(m_notificationHandler);
1526     m_notificationHandler = nullptr;
1527
1528     return true;
1529 }
1530
1531 bool AccessibilityUIElement::isFocusable() const
1532 {
1533     return checkElementState(m_element.get(), ATK_STATE_FOCUSABLE);
1534 }
1535
1536 bool AccessibilityUIElement::isSelectable() const
1537 {
1538     return checkElementState(m_element.get(), ATK_STATE_SELECTABLE);
1539 }
1540
1541 bool AccessibilityUIElement::isMultiSelectable() const
1542 {
1543     return checkElementState(m_element.get(), ATK_STATE_MULTISELECTABLE);
1544 }
1545
1546 bool AccessibilityUIElement::isVisible() const
1547 {
1548     return checkElementState(m_element.get(), ATK_STATE_VISIBLE);
1549 }
1550
1551 bool AccessibilityUIElement::isOffScreen() const
1552 {
1553     // FIXME: implement
1554     return false;
1555 }
1556
1557 bool AccessibilityUIElement::isCollapsed() const
1558 {
1559     // FIXME: implement
1560     return false;
1561 }
1562
1563 bool AccessibilityUIElement::isIgnored() const
1564 {
1565     // FIXME: implement
1566     return false;
1567 }
1568
1569 bool AccessibilityUIElement::hasPopup() const
1570 {
1571     if (!ATK_IS_OBJECT(m_element.get()))
1572         return false;
1573
1574     String hasPopupValue = getAttributeSetValueForId(ATK_OBJECT(m_element.get()), ObjectAttributeType, "haspopup");
1575     return equalIgnoringCase(hasPopupValue, "true");
1576 }
1577
1578 void AccessibilityUIElement::takeFocus()
1579 {
1580     // FIXME: implement
1581 }
1582
1583 void AccessibilityUIElement::takeSelection()
1584 {
1585     // FIXME: implement
1586 }
1587
1588 void AccessibilityUIElement::addSelection()
1589 {
1590     // FIXME: implement
1591 }
1592
1593 void AccessibilityUIElement::removeSelection()
1594 {
1595     // FIXME: implement
1596 }
1597
1598 // Text markers
1599 PassRefPtr<AccessibilityTextMarkerRange> AccessibilityUIElement::textMarkerRangeForElement(AccessibilityUIElement* element)
1600 {
1601     // FIXME: implement
1602     return nullptr;
1603 }
1604
1605 int AccessibilityUIElement::textMarkerRangeLength(AccessibilityTextMarkerRange* range)
1606 {
1607     // FIXME: implement
1608     return 0;
1609 }
1610
1611 PassRefPtr<AccessibilityTextMarker> AccessibilityUIElement::previousTextMarker(AccessibilityTextMarker* textMarker)
1612 {
1613     // FIXME: implement
1614     return nullptr;
1615 }
1616
1617 PassRefPtr<AccessibilityTextMarker> AccessibilityUIElement::nextTextMarker(AccessibilityTextMarker* textMarker)
1618 {
1619     // FIXME: implement
1620     return nullptr;
1621 }
1622
1623 JSRetainPtr<JSStringRef> AccessibilityUIElement::stringForTextMarkerRange(AccessibilityTextMarkerRange* markerRange)
1624 {
1625     // FIXME: implement
1626     return JSStringCreateWithCharacters(0, 0);
1627 }
1628
1629 PassRefPtr<AccessibilityTextMarkerRange> AccessibilityUIElement::textMarkerRangeForMarkers(AccessibilityTextMarker* startMarker, AccessibilityTextMarker* endMarker)
1630 {
1631     // FIXME: implement
1632     return nullptr;
1633 }
1634
1635 PassRefPtr<AccessibilityTextMarker> AccessibilityUIElement::startTextMarkerForTextMarkerRange(AccessibilityTextMarkerRange* range)
1636 {
1637     // FIXME: implement
1638     return nullptr;
1639 }
1640
1641 PassRefPtr<AccessibilityTextMarker> AccessibilityUIElement::endTextMarkerForTextMarkerRange(AccessibilityTextMarkerRange* range)
1642 {
1643     // FIXME: implement
1644     return nullptr;
1645 }
1646
1647 PassRefPtr<AccessibilityTextMarker> AccessibilityUIElement::endTextMarkerForBounds(int x, int y, int width, int height)
1648 {
1649     // FIXME: implement
1650     return nullptr;
1651 }
1652
1653 PassRefPtr<AccessibilityTextMarker> AccessibilityUIElement::startTextMarkerForBounds(int x, int y, int width, int height)
1654 {
1655     // FIXME: implement
1656     return nullptr;
1657 }
1658
1659 PassRefPtr<AccessibilityTextMarker> AccessibilityUIElement::textMarkerForPoint(int x, int y)
1660 {
1661     // FIXME: implement
1662     return nullptr;
1663 }
1664
1665 PassRefPtr<AccessibilityUIElement> AccessibilityUIElement::accessibilityElementForTextMarker(AccessibilityTextMarker* marker)
1666 {
1667     // FIXME: implement
1668     return nullptr;
1669 }
1670
1671 bool AccessibilityUIElement::attributedStringForTextMarkerRangeContainsAttribute(JSStringRef attribute, AccessibilityTextMarkerRange* range)
1672 {
1673     // FIXME: implement
1674     return false;
1675 }
1676
1677 int AccessibilityUIElement::indexForTextMarker(AccessibilityTextMarker* marker)
1678 {
1679     // FIXME: implement
1680     return -1;
1681 }
1682
1683 bool AccessibilityUIElement::isTextMarkerValid(AccessibilityTextMarker* textMarker)
1684 {
1685     // FIXME: implement
1686     return false;
1687 }
1688
1689 PassRefPtr<AccessibilityTextMarker> AccessibilityUIElement::textMarkerForIndex(int textIndex)
1690 {
1691     // FIXME: implement
1692     return nullptr;
1693 }
1694     
1695 PassRefPtr<AccessibilityTextMarker> AccessibilityUIElement::startTextMarker()
1696 {
1697     // FIXME: implement
1698     return nullptr;    
1699 }
1700
1701 PassRefPtr<AccessibilityTextMarker> AccessibilityUIElement::endTextMarker()
1702 {
1703     // FIXME: implement
1704     return nullptr;
1705 }
1706
1707 void AccessibilityUIElement::scrollToMakeVisible()
1708 {
1709     // FIXME: implement
1710 }
1711
1712 JSRetainPtr<JSStringRef> AccessibilityUIElement::supportedActions() const
1713 {
1714     // FIXME: implement
1715     return nullptr;
1716 }
1717
1718 JSRetainPtr<JSStringRef> AccessibilityUIElement::pathDescription() const
1719 {
1720     notImplemented();
1721     return nullptr;
1722 }
1723
1724 JSRetainPtr<JSStringRef> AccessibilityUIElement::mathPostscriptsDescription() const
1725 {
1726     notImplemented();
1727     return nullptr;
1728 }
1729
1730 JSRetainPtr<JSStringRef> AccessibilityUIElement::mathPrescriptsDescription() const
1731 {
1732     notImplemented();
1733     return nullptr;
1734 }
1735
1736 JSRetainPtr<JSStringRef> AccessibilityUIElement::classList() const
1737 {
1738     notImplemented();
1739     return nullptr;
1740 }
1741
1742 JSRetainPtr<JSStringRef> stringAtOffset(PlatformUIElement element, AtkTextBoundary boundary, int offset)
1743 {
1744     if (!ATK_IS_TEXT(element.get()))
1745         return JSStringCreateWithCharacters(0, 0);
1746
1747     gint startOffset, endOffset;
1748     StringBuilder builder;
1749
1750 #if ATK_CHECK_VERSION(2, 10, 0)
1751     AtkTextGranularity granularity;
1752     switch (boundary) {
1753     case ATK_TEXT_BOUNDARY_CHAR:
1754         granularity = ATK_TEXT_GRANULARITY_CHAR;
1755         break;
1756     case ATK_TEXT_BOUNDARY_WORD_START:
1757         granularity = ATK_TEXT_GRANULARITY_WORD;
1758         break;
1759     case ATK_TEXT_BOUNDARY_LINE_START:
1760         granularity = ATK_TEXT_GRANULARITY_LINE;
1761         break;
1762     case ATK_TEXT_BOUNDARY_SENTENCE_START:
1763         granularity = ATK_TEXT_GRANULARITY_SENTENCE;
1764         break;
1765     default:
1766         return JSStringCreateWithCharacters(0, 0);
1767     }
1768
1769     builder.append(atk_text_get_string_at_offset(ATK_TEXT(element.get()), offset, granularity, &startOffset, &endOffset));
1770 #else
1771     builder.append(atk_text_get_text_at_offset(ATK_TEXT(element.get()), offset, boundary, &startOffset, &endOffset));
1772 #endif
1773     builder.append(String::format(", %i, %i", startOffset, endOffset));
1774     return JSStringCreateWithUTF8CString(builder.toString().utf8().data());
1775 }
1776
1777 JSRetainPtr<JSStringRef> AccessibilityUIElement::characterAtOffset(int offset)
1778 {
1779     return stringAtOffset(m_element, ATK_TEXT_BOUNDARY_CHAR, offset);
1780 }
1781
1782 JSRetainPtr<JSStringRef> AccessibilityUIElement::wordAtOffset(int offset)
1783 {
1784     return stringAtOffset(m_element, ATK_TEXT_BOUNDARY_WORD_START, offset);
1785 }
1786
1787 JSRetainPtr<JSStringRef> AccessibilityUIElement::lineAtOffset(int offset)
1788 {
1789     return stringAtOffset(m_element, ATK_TEXT_BOUNDARY_LINE_START, offset);
1790 }
1791
1792 JSRetainPtr<JSStringRef> AccessibilityUIElement::sentenceAtOffset(int offset)
1793 {
1794     return stringAtOffset(m_element, ATK_TEXT_BOUNDARY_SENTENCE_START, offset);
1795 }
1796
1797 JSRetainPtr<JSStringRef> AccessibilityUIElement::selectTextWithCriteria(JSContextRef context, JSStringRef ambiguityResolution, JSValueRef searchStrings)
1798 {
1799     // FIXME: implement
1800     return nullptr;
1801 }
1802
1803 } // namespace WTR
1804
1805 #endif // HAVE(ACCESSIBILITY)