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