[ATK] Expose aria-posinset and aria-setsize through object attributes
[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, y;
1111     atk_component_get_position(ATK_COMPONENT(m_element.get()), &x, &y, ATK_XY_SCREEN);
1112     return x;
1113 }
1114
1115 double AccessibilityUIElement::y()
1116 {
1117     if (!ATK_IS_COMPONENT(m_element.get()))
1118         return 0;
1119
1120     int x, y;
1121     atk_component_get_position(ATK_COMPONENT(m_element.get()), &x, &y, ATK_XY_SCREEN);
1122     return y;
1123 }
1124
1125 double AccessibilityUIElement::width()
1126 {
1127     if (!ATK_IS_COMPONENT(m_element.get()))
1128         return 0;
1129
1130     int width, height;
1131     atk_component_get_size(ATK_COMPONENT(m_element.get()), &width, &height);
1132     return width;
1133 }
1134
1135 double AccessibilityUIElement::height()
1136 {
1137     if (!ATK_IS_COMPONENT(m_element.get()))
1138         return 0;
1139
1140     int width, height;
1141     atk_component_get_size(ATK_COMPONENT(m_element.get()), &width, &height);
1142     return height;
1143 }
1144
1145 double AccessibilityUIElement::clickPointX()
1146 {
1147     if (!ATK_IS_COMPONENT(m_element.get()))
1148         return 0;
1149
1150     int x, y;
1151     atk_component_get_position(ATK_COMPONENT(m_element.get()), &x, &y, ATK_XY_WINDOW);
1152
1153     int width, height;
1154     atk_component_get_size(ATK_COMPONENT(m_element.get()), &width, &height);
1155
1156     return x + width / 2.0;
1157 }
1158
1159 double AccessibilityUIElement::clickPointY()
1160 {
1161     if (!ATK_IS_COMPONENT(m_element.get()))
1162         return 0;
1163
1164     int x, y;
1165     atk_component_get_position(ATK_COMPONENT(m_element.get()), &x, &y, ATK_XY_WINDOW);
1166
1167     int width, height;
1168     atk_component_get_size(ATK_COMPONENT(m_element.get()), &width, &height);
1169
1170     return y + height / 2.0;
1171 }
1172
1173 double AccessibilityUIElement::intValue() const
1174 {
1175     if (!ATK_IS_OBJECT(m_element.get()))
1176         return 0;
1177
1178     if (ATK_IS_VALUE(m_element.get())) {
1179         GValue value = G_VALUE_INIT;
1180         atk_value_get_current_value(ATK_VALUE(m_element.get()), &value);
1181         if (!G_VALUE_HOLDS_FLOAT(&value))
1182             return 0;
1183         return g_value_get_float(&value);
1184     }
1185
1186     // Consider headings as an special case when returning the "int value" of
1187     // an AccessibilityUIElement, so we can reuse some tests to check the level
1188     // both for HTML headings and objects with the aria-level attribute.
1189     if (atk_object_get_role(ATK_OBJECT(m_element.get())) == ATK_ROLE_HEADING) {
1190         String headingLevel = getAttributeSetValueForId(ATK_OBJECT(m_element.get()), ObjectAttributeType, "level");
1191         bool ok;
1192         double headingLevelValue = headingLevel.toDouble(&ok);
1193         if (ok)
1194             return headingLevelValue;
1195     }
1196
1197     return 0;
1198 }
1199
1200 double AccessibilityUIElement::minValue()
1201 {
1202     if (!ATK_IS_VALUE(m_element.get()))
1203         return 0;
1204
1205     GValue value = G_VALUE_INIT;
1206     atk_value_get_minimum_value(ATK_VALUE(m_element.get()), &value);
1207     if (!G_VALUE_HOLDS_FLOAT(&value))
1208         return 0;
1209
1210     return g_value_get_float(&value);
1211 }
1212
1213 double AccessibilityUIElement::maxValue()
1214 {
1215     if (!ATK_IS_VALUE(m_element.get()))
1216         return 0;
1217
1218     GValue value = G_VALUE_INIT;
1219     atk_value_get_maximum_value(ATK_VALUE(m_element.get()), &value);
1220     if (!G_VALUE_HOLDS_FLOAT(&value))
1221         return 0;
1222
1223     return g_value_get_float(&value);
1224 }
1225
1226 JSRetainPtr<JSStringRef> AccessibilityUIElement::valueDescription()
1227 {
1228     // FIXME: implement
1229     return JSStringCreateWithCharacters(0, 0);
1230 }
1231
1232 int AccessibilityUIElement::insertionPointLineNumber()
1233 {
1234     // FIXME: implement
1235     return -1;
1236 }
1237
1238 bool AccessibilityUIElement::isPressActionSupported()
1239 {
1240     if (!ATK_IS_ACTION(m_element.get()))
1241         return false;
1242
1243     const gchar* actionName = atk_action_get_name(ATK_ACTION(m_element.get()), 0);
1244     return equalIgnoringCase(actionName, String("press")) || equalIgnoringCase(actionName, String("jump"));
1245 }
1246
1247 bool AccessibilityUIElement::isIncrementActionSupported()
1248 {
1249     // FIXME: implement
1250     return false;
1251 }
1252
1253 bool AccessibilityUIElement::isDecrementActionSupported()
1254 {
1255     // FIXME: implement
1256     return false;
1257 }
1258
1259 bool AccessibilityUIElement::isEnabled()
1260 {
1261     return checkElementState(m_element.get(), ATK_STATE_ENABLED);
1262 }
1263
1264 bool AccessibilityUIElement::isRequired() const
1265 {
1266     return checkElementState(m_element.get(), ATK_STATE_REQUIRED);
1267 }
1268
1269 bool AccessibilityUIElement::isFocused() const
1270 {
1271     return checkElementState(m_element.get(), ATK_STATE_FOCUSED);
1272 }
1273
1274 bool AccessibilityUIElement::isSelected() const
1275 {
1276     return checkElementState(m_element.get(), ATK_STATE_SELECTED);
1277 }
1278
1279 bool AccessibilityUIElement::isSelectedOptionActive() const
1280 {
1281     return checkElementState(m_element.get(), ATK_STATE_ACTIVE);
1282 }
1283
1284 bool AccessibilityUIElement::isExpanded() const
1285 {
1286     return checkElementState(m_element.get(), ATK_STATE_EXPANDED);
1287 }
1288
1289 bool AccessibilityUIElement::isChecked() const
1290 {
1291     return checkElementState(m_element.get(), ATK_STATE_CHECKED);
1292 }
1293
1294 bool AccessibilityUIElement::isIndeterminate() const
1295 {
1296     return checkElementState(m_element.get(), ATK_STATE_INDETERMINATE);
1297 }
1298
1299 int AccessibilityUIElement::hierarchicalLevel() const
1300 {
1301     // FIXME: implement
1302     return 0;
1303 }
1304
1305 JSRetainPtr<JSStringRef> AccessibilityUIElement::speak()
1306 {
1307     // FIXME: implement
1308     return JSStringCreateWithCharacters(0, 0);
1309 }
1310
1311 bool AccessibilityUIElement::ariaIsGrabbed() const
1312 {
1313     // FIXME: implement
1314     return false;
1315 }
1316
1317 JSRetainPtr<JSStringRef> AccessibilityUIElement::ariaDropEffects() const
1318 {
1319     // FIXME: implement
1320     return JSStringCreateWithCharacters(0, 0);
1321 }
1322
1323 // parameterized attributes
1324 int AccessibilityUIElement::lineForIndex(int index)
1325 {
1326     if (!ATK_IS_TEXT(m_element.get()))
1327         return -1;
1328
1329     if (index < 0 || index > atk_text_get_character_count(ATK_TEXT(m_element.get())))
1330         return -1;
1331
1332     GUniquePtr<gchar> text(atk_text_get_text(ATK_TEXT(m_element.get()), 0, index));
1333     int lineNo = 0;
1334     for (gchar* offset = text.get(); *offset; ++offset) {
1335         if (*offset == '\n')
1336             ++lineNo;
1337     }
1338
1339     return lineNo;
1340 }
1341
1342 JSRetainPtr<JSStringRef> AccessibilityUIElement::rangeForLine(int line)
1343 {
1344     // FIXME: implement
1345     return JSStringCreateWithCharacters(0, 0);
1346 }
1347
1348 JSRetainPtr<JSStringRef> AccessibilityUIElement::rangeForPosition(int x, int y)
1349 {
1350     // FIXME: implement
1351     return JSStringCreateWithCharacters(0, 0);
1352 }
1353
1354 JSRetainPtr<JSStringRef> AccessibilityUIElement::boundsForRange(unsigned location, unsigned length)
1355 {
1356     // FIXME: implement
1357     return JSStringCreateWithCharacters(0, 0);
1358 }
1359
1360 JSRetainPtr<JSStringRef> AccessibilityUIElement::stringForRange(unsigned location, unsigned length)
1361 {
1362     if (!ATK_IS_TEXT(m_element.get()))
1363         return JSStringCreateWithCharacters(0, 0);
1364
1365     String string = atk_text_get_text(ATK_TEXT(m_element.get()), location, location + length);
1366     return JSStringCreateWithUTF8CString(string.utf8().data());
1367 }
1368
1369 JSRetainPtr<JSStringRef> AccessibilityUIElement::attributedStringForRange(unsigned location, unsigned length)
1370 {
1371     // FIXME: implement
1372     return JSStringCreateWithCharacters(0, 0);
1373 }
1374
1375 bool AccessibilityUIElement::attributedStringRangeIsMisspelled(unsigned location, unsigned length)
1376 {
1377     // FIXME: implement
1378     return false;
1379 }
1380
1381 unsigned AccessibilityUIElement::uiElementCountForSearchPredicate(JSContextRef context, AccessibilityUIElement* startElement, bool isDirectionNext, JSValueRef searchKey, JSStringRef searchText, bool visibleOnly, bool immediateDescendantsOnly)
1382 {
1383     // FIXME: implement
1384     return 0;
1385 }
1386
1387 PassRefPtr<AccessibilityUIElement> AccessibilityUIElement::uiElementForSearchPredicate(JSContextRef context, AccessibilityUIElement* startElement, bool isDirectionNext, JSValueRef searchKey, JSStringRef searchText, bool visibleOnly, bool immediateDescendantsOnly)
1388 {
1389     // FIXME: implement
1390     return nullptr;
1391 }
1392
1393 JSRetainPtr<JSStringRef> AccessibilityUIElement::selectTextWithCriteria(JSContextRef context, JSStringRef ambiguityResolution, JSValueRef searchStrings, JSStringRef replacementString)
1394 {
1395     // FIXME: implement
1396     return nullptr;
1397 }
1398
1399 JSRetainPtr<JSStringRef> AccessibilityUIElement::attributesOfColumnHeaders()
1400 {
1401     if (!ATK_IS_TABLE(m_element.get()))
1402         return JSStringCreateWithCharacters(0, 0);
1403
1404     Vector<RefPtr<AccessibilityUIElement> > columnHeaders = getColumnHeaders(ATK_TABLE(m_element.get()));
1405     return createStringWithAttributes(columnHeaders);
1406 }
1407
1408 JSRetainPtr<JSStringRef> AccessibilityUIElement::attributesOfRowHeaders()
1409 {
1410     if (!ATK_IS_TABLE(m_element.get()))
1411         return JSStringCreateWithCharacters(0, 0);
1412
1413     Vector<RefPtr<AccessibilityUIElement> > rowHeaders = getRowHeaders(ATK_TABLE(m_element.get()));
1414     return createStringWithAttributes(rowHeaders);
1415 }
1416
1417 JSRetainPtr<JSStringRef> AccessibilityUIElement::attributesOfColumns()
1418 {
1419     // FIXME: implement
1420     return JSStringCreateWithCharacters(0, 0);
1421 }
1422
1423 JSRetainPtr<JSStringRef> AccessibilityUIElement::attributesOfRows()
1424 {
1425     // FIXME: implement
1426     return JSStringCreateWithCharacters(0, 0);
1427 }
1428
1429 JSRetainPtr<JSStringRef> AccessibilityUIElement::attributesOfVisibleCells()
1430 {
1431     if (!ATK_IS_TABLE(m_element.get()))
1432         return JSStringCreateWithCharacters(0, 0);
1433
1434     Vector<RefPtr<AccessibilityUIElement> > visibleCells = getVisibleCells(this);
1435     return createStringWithAttributes(visibleCells);
1436 }
1437
1438 JSRetainPtr<JSStringRef> AccessibilityUIElement::attributesOfHeader()
1439 {
1440     // FIXME: implement
1441     return JSStringCreateWithCharacters(0, 0);
1442 }
1443
1444 int AccessibilityUIElement::rowCount()
1445 {
1446     if (!ATK_IS_TABLE(m_element.get()))
1447         return 0;
1448
1449     return atk_table_get_n_rows(ATK_TABLE(m_element.get()));
1450 }
1451
1452 int AccessibilityUIElement::columnCount()
1453 {
1454     if (!ATK_IS_TABLE(m_element.get()))
1455         return 0;
1456
1457     return atk_table_get_n_columns(ATK_TABLE(m_element.get()));
1458 }
1459
1460 int AccessibilityUIElement::indexInTable()
1461 {
1462     // FIXME: implement
1463     return -1;
1464 }
1465
1466 JSRetainPtr<JSStringRef> AccessibilityUIElement::rowIndexRange()
1467 {
1468     // Range in table for rows.
1469     return indexRangeInTable(m_element.get(), true);
1470 }
1471
1472 JSRetainPtr<JSStringRef> AccessibilityUIElement::columnIndexRange()
1473 {
1474     // Range in table for columns.
1475     return indexRangeInTable(m_element.get(), false);
1476 }
1477
1478 PassRefPtr<AccessibilityUIElement> AccessibilityUIElement::cellForColumnAndRow(unsigned col, unsigned row)
1479 {
1480     if (!ATK_IS_TABLE(m_element.get()))
1481         return nullptr;
1482
1483     // Adopt the AtkObject representing the cell because
1484     // at_table_ref_at() transfers full ownership.
1485     GRefPtr<AtkObject> foundCell = adoptGRef(atk_table_ref_at(ATK_TABLE(m_element.get()), row, col));
1486     return foundCell ? AccessibilityUIElement::create(foundCell.get()) : nullptr;
1487 }
1488
1489 PassRefPtr<AccessibilityUIElement> AccessibilityUIElement::horizontalScrollbar() const
1490 {
1491     // FIXME: implement
1492     return nullptr;
1493 }
1494
1495 PassRefPtr<AccessibilityUIElement> AccessibilityUIElement::verticalScrollbar() const
1496 {
1497     // FIXME: implement
1498     return nullptr;
1499 }
1500
1501 JSRetainPtr<JSStringRef> AccessibilityUIElement::selectedTextRange()
1502 {
1503     if (!ATK_IS_TEXT(m_element.get()))
1504         return JSStringCreateWithCharacters(0, 0);
1505
1506     gint start, end;
1507     g_free(atk_text_get_selection(ATK_TEXT(m_element.get()), 0, &start, &end));
1508
1509     GUniquePtr<gchar> selection(g_strdup_printf("{%d, %d}", start, end - start));
1510     return JSStringCreateWithUTF8CString(selection.get());
1511 }
1512
1513 void AccessibilityUIElement::setSelectedTextRange(unsigned location, unsigned length)
1514 {
1515     if (!ATK_IS_TEXT(m_element.get()))
1516         return;
1517
1518     atk_text_set_selection(ATK_TEXT(m_element.get()), 0, location, location + length);
1519 }
1520
1521 void AccessibilityUIElement::increment()
1522 {
1523     alterCurrentValue(m_element.get(), 1);
1524 }
1525
1526 void AccessibilityUIElement::decrement()
1527 {
1528     alterCurrentValue(m_element.get(), -1);
1529 }
1530
1531 void AccessibilityUIElement::showMenu()
1532 {
1533     // FIXME: implement
1534 }
1535
1536 void AccessibilityUIElement::press()
1537 {
1538     if (!ATK_IS_ACTION(m_element.get()))
1539         return;
1540
1541     // Only one action per object is supported so far.
1542     atk_action_do_action(ATK_ACTION(m_element.get()), 0);
1543 }
1544
1545 void AccessibilityUIElement::setSelectedChild(AccessibilityUIElement* element) const
1546 {
1547     // FIXME: implement
1548 }
1549
1550 void AccessibilityUIElement::setSelectedChildAtIndex(unsigned index) const
1551 {
1552     if (!ATK_IS_SELECTION(m_element.get()))
1553         return;
1554
1555     atk_selection_add_selection(ATK_SELECTION(m_element.get()), index);
1556 }
1557
1558 void AccessibilityUIElement::removeSelectionAtIndex(unsigned index) const
1559 {
1560     if (!ATK_IS_SELECTION(m_element.get()))
1561         return;
1562
1563     atk_selection_remove_selection(ATK_SELECTION(m_element.get()), index);
1564 }
1565
1566 JSRetainPtr<JSStringRef> AccessibilityUIElement::accessibilityValue() const
1567 {
1568     // FIXME: implement
1569     return JSStringCreateWithCharacters(0, 0);
1570 }
1571
1572 JSRetainPtr<JSStringRef> AccessibilityUIElement::documentEncoding()
1573 {
1574     if (!ATK_IS_DOCUMENT(m_element.get()))
1575         return JSStringCreateWithCharacters(0, 0);
1576
1577     AtkRole role = atk_object_get_role(ATK_OBJECT(m_element.get()));
1578     if (role != ATK_ROLE_DOCUMENT_FRAME)
1579         return JSStringCreateWithCharacters(0, 0);
1580
1581     return JSStringCreateWithUTF8CString(atk_document_get_attribute_value(ATK_DOCUMENT(m_element.get()), "Encoding"));
1582 }
1583
1584 JSRetainPtr<JSStringRef> AccessibilityUIElement::documentURI()
1585 {
1586     if (!ATK_IS_DOCUMENT(m_element.get()))
1587         return JSStringCreateWithCharacters(0, 0);
1588
1589     AtkRole role = atk_object_get_role(ATK_OBJECT(m_element.get()));
1590     if (role != ATK_ROLE_DOCUMENT_FRAME)
1591         return JSStringCreateWithCharacters(0, 0);
1592
1593     return JSStringCreateWithUTF8CString(atk_document_get_attribute_value(ATK_DOCUMENT(m_element.get()), "URI"));
1594 }
1595
1596 JSRetainPtr<JSStringRef> AccessibilityUIElement::url()
1597 {
1598     if (!ATK_IS_HYPERLINK_IMPL(m_element.get()))
1599         return JSStringCreateWithCharacters(0, 0);
1600
1601     AtkHyperlink* hyperlink = atk_hyperlink_impl_get_hyperlink(ATK_HYPERLINK_IMPL(m_element.get()));
1602     GUniquePtr<char> hyperlinkURI(atk_hyperlink_get_uri(hyperlink, 0));
1603
1604     // Build the result string, stripping the absolute URL paths if present.
1605     char* localURI = g_strstr_len(hyperlinkURI.get(), -1, "LayoutTests");
1606     String axURL = String::format("AXURL: %s", localURI ? localURI : hyperlinkURI.get());
1607     return JSStringCreateWithUTF8CString(axURL.utf8().data());
1608 }
1609
1610 bool AccessibilityUIElement::addNotificationListener(JSValueRef functionCallback)
1611 {
1612     if (!functionCallback)
1613         return false;
1614
1615     // Only one notification listener per element.
1616     if (m_notificationHandler)
1617         return false;
1618
1619     m_notificationHandler = AccessibilityNotificationHandler::create();
1620     m_notificationHandler->setPlatformElement(platformUIElement());
1621     m_notificationHandler->setNotificationFunctionCallback(functionCallback);
1622
1623     return true;
1624 }
1625
1626 bool AccessibilityUIElement::removeNotificationListener()
1627 {
1628     // Programmers should not be trying to remove a listener that's already removed.
1629     ASSERT(m_notificationHandler);
1630     m_notificationHandler = nullptr;
1631
1632     return true;
1633 }
1634
1635 bool AccessibilityUIElement::isFocusable() const
1636 {
1637     return checkElementState(m_element.get(), ATK_STATE_FOCUSABLE);
1638 }
1639
1640 bool AccessibilityUIElement::isSelectable() const
1641 {
1642     return checkElementState(m_element.get(), ATK_STATE_SELECTABLE);
1643 }
1644
1645 bool AccessibilityUIElement::isMultiSelectable() const
1646 {
1647     return checkElementState(m_element.get(), ATK_STATE_MULTISELECTABLE);
1648 }
1649
1650 bool AccessibilityUIElement::isVisible() const
1651 {
1652     return checkElementState(m_element.get(), ATK_STATE_VISIBLE);
1653 }
1654
1655 bool AccessibilityUIElement::isOffScreen() const
1656 {
1657     // FIXME: implement
1658     return false;
1659 }
1660
1661 bool AccessibilityUIElement::isCollapsed() const
1662 {
1663     // FIXME: implement
1664     return false;
1665 }
1666
1667 bool AccessibilityUIElement::isIgnored() const
1668 {
1669     // FIXME: implement
1670     return false;
1671 }
1672
1673 bool AccessibilityUIElement::hasPopup() const
1674 {
1675     if (!ATK_IS_OBJECT(m_element.get()))
1676         return false;
1677
1678     String hasPopupValue = getAttributeSetValueForId(ATK_OBJECT(m_element.get()), ObjectAttributeType, "haspopup");
1679     return equalIgnoringCase(hasPopupValue, "true");
1680 }
1681
1682 void AccessibilityUIElement::takeFocus()
1683 {
1684     // FIXME: implement
1685 }
1686
1687 void AccessibilityUIElement::takeSelection()
1688 {
1689     // FIXME: implement
1690 }
1691
1692 void AccessibilityUIElement::addSelection()
1693 {
1694     // FIXME: implement
1695 }
1696
1697 void AccessibilityUIElement::removeSelection()
1698 {
1699     // FIXME: implement
1700 }
1701
1702 // Text markers
1703 PassRefPtr<AccessibilityTextMarkerRange> AccessibilityUIElement::textMarkerRangeForElement(AccessibilityUIElement* element)
1704 {
1705     // FIXME: implement
1706     return nullptr;
1707 }
1708
1709 int AccessibilityUIElement::textMarkerRangeLength(AccessibilityTextMarkerRange* range)
1710 {
1711     // FIXME: implement
1712     return 0;
1713 }
1714
1715 PassRefPtr<AccessibilityTextMarker> AccessibilityUIElement::previousTextMarker(AccessibilityTextMarker* textMarker)
1716 {
1717     // FIXME: implement
1718     return nullptr;
1719 }
1720
1721 PassRefPtr<AccessibilityTextMarker> AccessibilityUIElement::nextTextMarker(AccessibilityTextMarker* textMarker)
1722 {
1723     // FIXME: implement
1724     return nullptr;
1725 }
1726
1727 JSRetainPtr<JSStringRef> AccessibilityUIElement::stringForTextMarkerRange(AccessibilityTextMarkerRange* markerRange)
1728 {
1729     // FIXME: implement
1730     return JSStringCreateWithCharacters(0, 0);
1731 }
1732
1733 PassRefPtr<AccessibilityTextMarkerRange> AccessibilityUIElement::textMarkerRangeForMarkers(AccessibilityTextMarker* startMarker, AccessibilityTextMarker* endMarker)
1734 {
1735     // FIXME: implement
1736     return nullptr;
1737 }
1738
1739 PassRefPtr<AccessibilityTextMarker> AccessibilityUIElement::startTextMarkerForTextMarkerRange(AccessibilityTextMarkerRange* range)
1740 {
1741     // FIXME: implement
1742     return nullptr;
1743 }
1744
1745 PassRefPtr<AccessibilityTextMarker> AccessibilityUIElement::endTextMarkerForTextMarkerRange(AccessibilityTextMarkerRange* range)
1746 {
1747     // FIXME: implement
1748     return nullptr;
1749 }
1750
1751 PassRefPtr<AccessibilityTextMarker> AccessibilityUIElement::endTextMarkerForBounds(int x, int y, int width, int height)
1752 {
1753     // FIXME: implement
1754     return nullptr;
1755 }
1756
1757 PassRefPtr<AccessibilityTextMarker> AccessibilityUIElement::startTextMarkerForBounds(int x, int y, int width, int height)
1758 {
1759     // FIXME: implement
1760     return nullptr;
1761 }
1762
1763 PassRefPtr<AccessibilityTextMarker> AccessibilityUIElement::textMarkerForPoint(int x, int y)
1764 {
1765     // FIXME: implement
1766     return nullptr;
1767 }
1768
1769 PassRefPtr<AccessibilityUIElement> AccessibilityUIElement::accessibilityElementForTextMarker(AccessibilityTextMarker* marker)
1770 {
1771     // FIXME: implement
1772     return nullptr;
1773 }
1774
1775 bool AccessibilityUIElement::attributedStringForTextMarkerRangeContainsAttribute(JSStringRef attribute, AccessibilityTextMarkerRange* range)
1776 {
1777     // FIXME: implement
1778     return false;
1779 }
1780
1781 int AccessibilityUIElement::indexForTextMarker(AccessibilityTextMarker* marker)
1782 {
1783     // FIXME: implement
1784     return -1;
1785 }
1786
1787 bool AccessibilityUIElement::isTextMarkerValid(AccessibilityTextMarker* textMarker)
1788 {
1789     // FIXME: implement
1790     return false;
1791 }
1792
1793 PassRefPtr<AccessibilityTextMarker> AccessibilityUIElement::textMarkerForIndex(int textIndex)
1794 {
1795     // FIXME: implement
1796     return nullptr;
1797 }
1798     
1799 PassRefPtr<AccessibilityTextMarker> AccessibilityUIElement::startTextMarker()
1800 {
1801     // FIXME: implement
1802     return nullptr;    
1803 }
1804
1805 PassRefPtr<AccessibilityTextMarker> AccessibilityUIElement::endTextMarker()
1806 {
1807     // FIXME: implement
1808     return nullptr;
1809 }
1810
1811 void AccessibilityUIElement::scrollToMakeVisible()
1812 {
1813     // FIXME: implement
1814 }
1815
1816 JSRetainPtr<JSStringRef> AccessibilityUIElement::supportedActions() const
1817 {
1818     // FIXME: implement
1819     return nullptr;
1820 }
1821
1822 JSRetainPtr<JSStringRef> AccessibilityUIElement::pathDescription() const
1823 {
1824     notImplemented();
1825     return nullptr;
1826 }
1827
1828 JSRetainPtr<JSStringRef> AccessibilityUIElement::mathPostscriptsDescription() const
1829 {
1830     notImplemented();
1831     return nullptr;
1832 }
1833
1834 JSRetainPtr<JSStringRef> AccessibilityUIElement::mathPrescriptsDescription() const
1835 {
1836     notImplemented();
1837     return nullptr;
1838 }
1839
1840 JSRetainPtr<JSStringRef> AccessibilityUIElement::classList() const
1841 {
1842     notImplemented();
1843     return nullptr;
1844 }
1845
1846 JSRetainPtr<JSStringRef> stringAtOffset(PlatformUIElement element, AtkTextBoundary boundary, int offset)
1847 {
1848     if (!ATK_IS_TEXT(element.get()))
1849         return JSStringCreateWithCharacters(0, 0);
1850
1851     gint startOffset, endOffset;
1852     StringBuilder builder;
1853
1854 #if ATK_CHECK_VERSION(2, 10, 0)
1855     AtkTextGranularity granularity;
1856     switch (boundary) {
1857     case ATK_TEXT_BOUNDARY_CHAR:
1858         granularity = ATK_TEXT_GRANULARITY_CHAR;
1859         break;
1860     case ATK_TEXT_BOUNDARY_WORD_START:
1861         granularity = ATK_TEXT_GRANULARITY_WORD;
1862         break;
1863     case ATK_TEXT_BOUNDARY_LINE_START:
1864         granularity = ATK_TEXT_GRANULARITY_LINE;
1865         break;
1866     case ATK_TEXT_BOUNDARY_SENTENCE_START:
1867         granularity = ATK_TEXT_GRANULARITY_SENTENCE;
1868         break;
1869     default:
1870         return JSStringCreateWithCharacters(0, 0);
1871     }
1872
1873     builder.append(atk_text_get_string_at_offset(ATK_TEXT(element.get()), offset, granularity, &startOffset, &endOffset));
1874 #else
1875     builder.append(atk_text_get_text_at_offset(ATK_TEXT(element.get()), offset, boundary, &startOffset, &endOffset));
1876 #endif
1877     builder.append(String::format(", %i, %i", startOffset, endOffset));
1878     return JSStringCreateWithUTF8CString(builder.toString().utf8().data());
1879 }
1880
1881 JSRetainPtr<JSStringRef> AccessibilityUIElement::characterAtOffset(int offset)
1882 {
1883     return stringAtOffset(m_element, ATK_TEXT_BOUNDARY_CHAR, offset);
1884 }
1885
1886 JSRetainPtr<JSStringRef> AccessibilityUIElement::wordAtOffset(int offset)
1887 {
1888     return stringAtOffset(m_element, ATK_TEXT_BOUNDARY_WORD_START, offset);
1889 }
1890
1891 JSRetainPtr<JSStringRef> AccessibilityUIElement::lineAtOffset(int offset)
1892 {
1893     return stringAtOffset(m_element, ATK_TEXT_BOUNDARY_LINE_START, offset);
1894 }
1895
1896 JSRetainPtr<JSStringRef> AccessibilityUIElement::sentenceAtOffset(int offset)
1897 {
1898     return stringAtOffset(m_element, ATK_TEXT_BOUNDARY_SENTENCE_START, offset);
1899 }
1900
1901 } // namespace WTR
1902
1903 #endif // HAVE(ACCESSIBILITY)