AX: Implement updated CSS3 Speech for 'speak' and 'speak-as' properties
[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 <WebKit/WKBundleFrame.h>
40 #endif
41 #include <atk/atk.h>
42 #include <wtf/Assertions.h>
43 #include <wtf/glib/GRefPtr.h>
44 #include <wtf/glib/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 #if ATK_CHECK_VERSION(2,11,92)
55 enum RangeLimit {
56     RangeLimitMinimum,
57     RangeLimitMaximum
58 };
59 #endif
60
61 enum AtkAttributeType {
62     ObjectAttributeType,
63     TextAttributeType
64 };
65
66 enum AttributeDomain {
67     CoreDomain = 0,
68     AtkDomain
69 };
70
71 enum AttributesIndex {
72     // Attribute names.
73     InvalidNameIndex = 0,
74     ColumnCount,
75     ColumnIndex,
76     ColumnSpan,
77     RowCount,
78     RowIndex,
79     RowSpan,
80     PosInSetIndex,
81     SetSizeIndex,
82     PlaceholderNameIndex,
83     SortNameIndex,
84     CurrentNameIndex,
85     AriaLiveNameIndex,
86     AriaAtomicNameIndex,
87     AriaRelevantNameIndex,
88     BusyNameIndex,
89
90     // Attribute values.
91     SortAscendingValueIndex,
92     SortDescendingValueIndex,
93     SortUnknownValueIndex,
94
95     NumberOfAttributes
96 };
97
98 // Attribute names & Values (keep on sync with enum AttributesIndex).
99 const String attributesMap[][2] = {
100     // Attribute names.
101     { "AXInvalid", "invalid" },
102     { "AXARIAColumnCount", "colcount" },
103     { "AXARIAColumnIndex", "colindex" },
104     { "AXARIAColumnSpan", "colspan" },
105     { "AXARIARowCount", "rowcount" },
106     { "AXARIARowIndex", "rowindex" },
107     { "AXARIARowSpan", "rowspan" },
108     { "AXARIAPosInSet", "posinset" },
109     { "AXARIASetSize", "setsize" },
110     { "AXPlaceholderValue", "placeholder-text" } ,
111     { "AXSortDirection", "sort" },
112     { "AXARIACurrent", "current" },
113     { "AXARIALive", "live" },
114     { "AXARIAAtomic", "atomic" },
115     { "AXARIARelevant", "relevant" },
116     { "AXElementBusy", "busy" },
117
118     // Attribute values.
119     { "AXAscendingSortDirection", "ascending" },
120     { "AXDescendingSortDirection", "descending" },
121     { "AXUnknownSortDirection", "unknown" }
122 };
123
124 #if ATK_CHECK_VERSION(2, 11, 3)
125 const char* landmarkStringBanner = "AXLandmarkBanner";
126 const char* landmarkStringComplementary = "AXLandmarkComplementary";
127 const char* landmarkStringContentinfo = "AXLandmarkContentInfo";
128 const char* landmarkStringForm = "AXLandmarkForm";
129 const char* landmarkStringMain = "AXLandmarkMain";
130 const char* landmarkStringNavigation = "AXLandmarkNavigation";
131 const char* landmarkStringRegion = "AXLandmarkRegion";
132 const char* landmarkStringSearch = "AXLandmarkSearch";
133 #endif
134
135 String jsStringToWTFString(JSStringRef attribute)
136 {
137     size_t bufferSize = JSStringGetMaximumUTF8CStringSize(attribute);
138     GUniquePtr<gchar> buffer(static_cast<gchar*>(g_malloc(bufferSize)));
139     JSStringGetUTF8CString(attribute, buffer.get(), bufferSize);
140
141     return String::fromUTF8(buffer.get());
142 }
143
144 String coreAttributeToAtkAttribute(JSStringRef attribute)
145 {
146     String attributeString = jsStringToWTFString(attribute);
147     for (int i = 0; i < NumberOfAttributes; ++i) {
148         if (attributesMap[i][CoreDomain] == attributeString)
149             return attributesMap[i][AtkDomain];
150     }
151
152     return attributeString;
153 }
154
155 String atkAttributeValueToCoreAttributeValue(AtkAttributeType type, const String& id, const String& value)
156 {
157     if (type == ObjectAttributeType) {
158         // We don't expose the "current" attribute if there is no author-provided value.
159         if (id == attributesMap[CurrentNameIndex][AtkDomain] && value.isEmpty())
160             return "false";
161
162         // We need to translate ATK values exposed for 'aria-sort' (e.g. 'ascending')
163         // into those expected by the layout tests (e.g. 'AXAscendingSortDirection').
164         if (id == attributesMap[SortNameIndex][AtkDomain] && !value.isEmpty()) {
165             if (value == attributesMap[SortAscendingValueIndex][AtkDomain])
166                 return attributesMap[SortAscendingValueIndex][CoreDomain];
167             if (value == attributesMap[SortDescendingValueIndex][AtkDomain])
168                 return attributesMap[SortDescendingValueIndex][CoreDomain];
169
170             return attributesMap[SortUnknownValueIndex][CoreDomain];
171         }
172     } else if (type == TextAttributeType) {
173         // In case of 'aria-invalid' when the attribute empty or has "false" for ATK
174         // it should not be mapped at all, but layout tests will expect 'false'.
175         if (id == attributesMap[InvalidNameIndex][AtkDomain] && value.isEmpty())
176             return "false";
177     }
178
179     return value;
180 }
181
182 AtkAttributeSet* getAttributeSet(AtkObject* accessible, AtkAttributeType type)
183 {
184     if (type == ObjectAttributeType)
185         return atk_object_get_attributes(accessible);
186
187     if (type == TextAttributeType) {
188         if (!ATK_IS_TEXT(accessible))
189             return nullptr;
190
191         return atk_text_get_default_attributes(ATK_TEXT(accessible));
192     }
193
194     ASSERT_NOT_REACHED();
195     return nullptr;
196 }
197
198 String getAttributeSetValueForId(AtkObject* accessible, AtkAttributeType type, String id)
199 {
200     AtkAttributeSet* attributeSet = getAttributeSet(accessible, type);
201     if (!attributeSet)
202         return String();
203
204     String attributeValue;
205     for (AtkAttributeSet* attributes = attributeSet; attributes; attributes = attributes->next) {
206         AtkAttribute* atkAttribute = static_cast<AtkAttribute*>(attributes->data);
207         if (id == atkAttribute->name) {
208             attributeValue = String::fromUTF8(atkAttribute->value);
209             break;
210         }
211     }
212     atk_attribute_set_free(attributeSet);
213
214     return atkAttributeValueToCoreAttributeValue(type, id, attributeValue);
215 }
216
217 String attributeSetToString(AtkAttributeSet* attributeSet, String separator=", ")
218 {
219     if (!attributeSet)
220         return String();
221
222     StringBuilder builder;
223     for (AtkAttributeSet* attributes = attributeSet; attributes; attributes = attributes->next) {
224         AtkAttribute* attribute = static_cast<AtkAttribute*>(attributes->data);
225         GUniquePtr<gchar> attributeData(g_strconcat(attribute->name, ":", attribute->value, NULL));
226         builder.append(attributeData.get());
227         if (attributes->next)
228             builder.append(separator);
229     }
230     atk_attribute_set_free(attributeSet);
231
232     return builder.toString();
233 }
234
235 String getAtkAttributeSetAsString(AtkObject* accessible, AtkAttributeType type,  String separator=", ")
236 {
237     return attributeSetToString(getAttributeSet(accessible, type), separator);
238 }
239
240 bool checkElementState(PlatformUIElement element, AtkStateType stateType)
241 {
242     if (!ATK_IS_OBJECT(element.get()))
243         return false;
244
245     GRefPtr<AtkStateSet> stateSet = adoptGRef(atk_object_ref_state_set(ATK_OBJECT(element.get())));
246     return atk_state_set_contains_state(stateSet.get(), stateType);
247 }
248
249 JSStringRef indexRangeInTable(PlatformUIElement element, bool isRowRange)
250 {
251     GUniquePtr<gchar> rangeString(g_strdup("{0, 0}"));
252 #if ATK_CHECK_VERSION(2,11,90)
253     if (!ATK_IS_TABLE_CELL(element.get()))
254         return JSStringCreateWithUTF8CString(rangeString.get());
255 #else
256     if (!ATK_IS_OBJECT(element.get()))
257         return JSStringCreateWithUTF8CString(rangeString.get());
258
259     AtkObject* axTable = atk_object_get_parent(ATK_OBJECT(element.get()));
260     if (!axTable || !ATK_IS_TABLE(axTable))
261         return JSStringCreateWithUTF8CString(rangeString.get());
262
263     // Look for the cell in the table.
264     gint indexInParent = atk_object_get_index_in_parent(ATK_OBJECT(element.get()));
265     if (indexInParent == -1)
266         return JSStringCreateWithUTF8CString(rangeString.get());
267 #endif
268
269     gint row = -1;
270     gint column = -1;
271     gint rowSpan = -1;
272     gint columnSpan = -1;
273 #if ATK_CHECK_VERSION(2,11,90)
274     atk_table_cell_get_row_column_span(ATK_TABLE_CELL(element.get()), &row, &column, &rowSpan, &columnSpan);
275 #else
276     row = atk_table_get_row_at_index(ATK_TABLE(axTable), indexInParent);
277     column = atk_table_get_column_at_index(ATK_TABLE(axTable), indexInParent);
278     rowSpan = atk_table_get_row_extent_at(ATK_TABLE(axTable), row, column);
279     columnSpan = atk_table_get_column_extent_at(ATK_TABLE(axTable), row, column);
280 #endif
281
282     // Get the actual values, if row and columns are valid values.
283     if (row != -1 && column != -1) {
284         int base = 0;
285         int length = 0;
286         if (isRowRange) {
287             base = row;
288             length = rowSpan;
289         } else {
290             base = column;
291             length = columnSpan;
292         }
293         rangeString.reset(g_strdup_printf("{%d, %d}", base, length));
294     }
295
296     return JSStringCreateWithUTF8CString(rangeString.get());
297 }
298
299 void alterCurrentValue(PlatformUIElement element, int factor)
300 {
301     if (!ATK_IS_VALUE(element.get()))
302         return;
303
304 #if ATK_CHECK_VERSION(2,11,92)
305     double currentValue;
306     atk_value_get_value_and_text(ATK_VALUE(element.get()), &currentValue, nullptr);
307
308     double increment = atk_value_get_increment(ATK_VALUE(element.get()));
309     atk_value_set_value(ATK_VALUE(element.get()), currentValue + factor * increment);
310 #else
311     GValue currentValue = G_VALUE_INIT;
312     atk_value_get_current_value(ATK_VALUE(element.get()), &currentValue);
313
314     GValue increment = G_VALUE_INIT;
315     atk_value_get_minimum_increment(ATK_VALUE(element.get()), &increment);
316
317     GValue newValue = G_VALUE_INIT;
318     g_value_init(&newValue, G_TYPE_FLOAT);
319
320     g_value_set_float(&newValue, g_value_get_float(&currentValue) + factor * g_value_get_float(&increment));
321     atk_value_set_current_value(ATK_VALUE(element.get()), &newValue);
322
323     g_value_unset(&newValue);
324     g_value_unset(&increment);
325     g_value_unset(&currentValue);
326 #endif
327 }
328
329 gchar* replaceCharactersForResults(gchar* str)
330 {
331     WTF::String uString = WTF::String::fromUTF8(str);
332
333     // The object replacement character is passed along to ATs so we need to be
334     // able to test for their presence and do so without causing test failures.
335     uString.replace(objectReplacementCharacter, "<obj>");
336
337     // The presence of newline characters in accessible text of a single object
338     // is appropriate, but it makes test results (especially the accessible tree)
339     // harder to read.
340     uString.replace("\n", "<\\n>");
341
342     return g_strdup(uString.utf8().data());
343 }
344
345 const gchar* roleToString(AtkObject* object)
346 {
347     AtkRole role = atk_object_get_role(object);
348
349 #if ATK_CHECK_VERSION(2, 11, 3)
350     if (role == ATK_ROLE_LANDMARK) {
351         String xmlRolesValue = getAttributeSetValueForId(object, ObjectAttributeType, "xml-roles");
352         if (equalLettersIgnoringASCIICase(xmlRolesValue, "banner"))
353             return landmarkStringBanner;
354         if (equalLettersIgnoringASCIICase(xmlRolesValue, "complementary"))
355             return landmarkStringComplementary;
356         if (equalLettersIgnoringASCIICase(xmlRolesValue, "contentinfo"))
357             return landmarkStringContentinfo;
358         if (equalLettersIgnoringASCIICase(xmlRolesValue, "doc-acknowledgments"))
359             return landmarkStringRegion;
360         if (equalLettersIgnoringASCIICase(xmlRolesValue, "doc-afterword"))
361             return landmarkStringRegion;
362         if (equalLettersIgnoringASCIICase(xmlRolesValue, "doc-appendix"))
363             return landmarkStringRegion;
364         if (equalLettersIgnoringASCIICase(xmlRolesValue, "doc-bibliography"))
365             return landmarkStringRegion;
366         if (equalLettersIgnoringASCIICase(xmlRolesValue, "doc-chapter"))
367             return landmarkStringRegion;
368         if (equalLettersIgnoringASCIICase(xmlRolesValue, "doc-conclusion"))
369             return landmarkStringRegion;
370         if (equalLettersIgnoringASCIICase(xmlRolesValue, "doc-credits"))
371             return landmarkStringRegion;
372         if (equalLettersIgnoringASCIICase(xmlRolesValue, "doc-endnotes"))
373             return landmarkStringRegion;
374         if (equalLettersIgnoringASCIICase(xmlRolesValue, "doc-epilogue"))
375             return landmarkStringRegion;
376         if (equalLettersIgnoringASCIICase(xmlRolesValue, "doc-errata"))
377             return landmarkStringRegion;
378         if (equalLettersIgnoringASCIICase(xmlRolesValue, "doc-foreword"))
379             return landmarkStringRegion;
380         if (equalLettersIgnoringASCIICase(xmlRolesValue, "doc-glossary"))
381             return landmarkStringRegion;
382         if (equalLettersIgnoringASCIICase(xmlRolesValue, "doc-glossref"))
383             return landmarkStringRegion;
384         if (equalLettersIgnoringASCIICase(xmlRolesValue, "doc-index"))
385             return landmarkStringRegion;
386         if (equalLettersIgnoringASCIICase(xmlRolesValue, "doc-introduction"))
387             return landmarkStringRegion;
388         if (equalLettersIgnoringASCIICase(xmlRolesValue, "doc-pagelist"))
389             return landmarkStringRegion;
390         if (equalLettersIgnoringASCIICase(xmlRolesValue, "doc-part"))
391             return landmarkStringRegion;
392         if (equalLettersIgnoringASCIICase(xmlRolesValue, "doc-preface"))
393             return landmarkStringRegion;
394         if (equalLettersIgnoringASCIICase(xmlRolesValue, "doc-prologue"))
395             return landmarkStringRegion;
396         if (equalLettersIgnoringASCIICase(xmlRolesValue, "doc-toc"))
397             return landmarkStringRegion;
398         if (equalLettersIgnoringASCIICase(xmlRolesValue, "form"))
399             return landmarkStringForm;
400         if (equalLettersIgnoringASCIICase(xmlRolesValue, "main"))
401             return landmarkStringMain;
402         if (equalLettersIgnoringASCIICase(xmlRolesValue, "navigation"))
403             return landmarkStringNavigation;
404         if (equalLettersIgnoringASCIICase(xmlRolesValue, "region"))
405             return landmarkStringRegion;
406         if (equalLettersIgnoringASCIICase(xmlRolesValue, "search"))
407             return landmarkStringSearch;
408     }
409 #endif
410
411     switch (role) {
412     case ATK_ROLE_ALERT:
413         return "AXAlert";
414     case ATK_ROLE_DIALOG:
415         return "AXDialog";
416     case ATK_ROLE_CANVAS:
417         return "AXCanvas";
418     case ATK_ROLE_CAPTION:
419         return "AXCaption";
420     case ATK_ROLE_CHECK_BOX:
421         return "AXCheckBox";
422     case ATK_ROLE_COLOR_CHOOSER:
423         return "AXColorWell";
424     case ATK_ROLE_COLUMN_HEADER:
425         return "AXColumnHeader";
426     case ATK_ROLE_COMBO_BOX:
427         return "AXComboBox";
428     case ATK_ROLE_COMMENT:
429         return "AXComment";
430     case ATK_ROLE_DOCUMENT_FRAME:
431         return "AXDocument";
432     case ATK_ROLE_DOCUMENT_WEB:
433         return "AXWebArea";
434     case ATK_ROLE_EMBEDDED:
435         return "AXEmbedded";
436     case ATK_ROLE_ENTRY:
437         return "AXTextField";
438     case ATK_ROLE_FOOTER:
439         return "AXFooter";
440     case ATK_ROLE_FORM:
441         return "AXForm";
442     case ATK_ROLE_GROUPING:
443         return "AXGroup";
444     case ATK_ROLE_HEADING:
445         return "AXHeading";
446     case ATK_ROLE_IMAGE:
447         return "AXImage";
448     case ATK_ROLE_IMAGE_MAP:
449         return "AXImageMap";
450     case ATK_ROLE_INVALID:
451         return "AXInvalid";
452     case ATK_ROLE_LABEL:
453         return "AXLabel";
454     case ATK_ROLE_LEVEL_BAR:
455         return "AXProgressIndicator";
456     case ATK_ROLE_LINK:
457         return "AXLink";
458     case ATK_ROLE_LIST:
459         return "AXList";
460     case ATK_ROLE_LIST_BOX:
461         return "AXListBox";
462     case ATK_ROLE_LIST_ITEM:
463         return "AXListItem";
464     case ATK_ROLE_MENU:
465         return "AXMenu";
466     case ATK_ROLE_MENU_BAR:
467         return "AXMenuBar";
468     case ATK_ROLE_MENU_ITEM:
469         return "AXMenuItem";
470     case ATK_ROLE_PAGE_TAB:
471         return "AXTab";
472     case ATK_ROLE_PAGE_TAB_LIST:
473         return "AXTabGroup";
474     case ATK_ROLE_PANEL:
475         return "AXGroup";
476     case ATK_ROLE_PARAGRAPH:
477         return "AXParagraph";
478     case ATK_ROLE_PASSWORD_TEXT:
479         return "AXPasswordField";
480     case ATK_ROLE_PROGRESS_BAR:
481         return "AXProgressIndicator";
482     case ATK_ROLE_PUSH_BUTTON:
483         return "AXButton";
484     case ATK_ROLE_RADIO_BUTTON:
485         return "AXRadioButton";
486     case ATK_ROLE_RADIO_MENU_ITEM:
487         return "AXRadioMenuItem";
488     case ATK_ROLE_ROW_HEADER:
489         return "AXRowHeader";
490     case ATK_ROLE_CHECK_MENU_ITEM:
491         return "AXCheckMenuItem";
492     case ATK_ROLE_RULER:
493         return "AXRuler";
494     case ATK_ROLE_SCROLL_BAR:
495         return "AXScrollBar";
496     case ATK_ROLE_SCROLL_PANE:
497         return "AXScrollArea";
498     case ATK_ROLE_SECTION:
499         return "AXSection";
500     case ATK_ROLE_SEPARATOR:
501         return "AXSeparator";
502     case ATK_ROLE_SLIDER:
503         return "AXSlider";
504     case ATK_ROLE_SPIN_BUTTON:
505         return "AXSpinButton";
506     case ATK_ROLE_STATUSBAR:
507         return "AXStatusBar";
508     case ATK_ROLE_TABLE:
509         return "AXTable";
510     case ATK_ROLE_TABLE_CELL:
511         return "AXCell";
512     case ATK_ROLE_TABLE_COLUMN_HEADER:
513         return "AXColumnHeader";
514     case ATK_ROLE_TABLE_ROW:
515         return "AXRow";
516     case ATK_ROLE_TABLE_ROW_HEADER:
517         return "AXRowHeader";
518     case ATK_ROLE_TOGGLE_BUTTON:
519         return "AXToggleButton";
520     case ATK_ROLE_TOOL_BAR:
521         return "AXToolbar";
522     case ATK_ROLE_TOOL_TIP:
523         return "AXUserInterfaceTooltip";
524     case ATK_ROLE_TREE:
525         return "AXTree";
526     case ATK_ROLE_TREE_TABLE:
527         return "AXTreeGrid";
528     case ATK_ROLE_TREE_ITEM:
529         return "AXTreeItem";
530     case ATK_ROLE_WINDOW:
531         return "AXWindow";
532     case ATK_ROLE_UNKNOWN:
533         return "AXUnknown";
534 #if ATK_CHECK_VERSION(2, 11, 3)
535     case ATK_ROLE_ARTICLE:
536         return "AXArticle";
537     case ATK_ROLE_AUDIO:
538         return "AXAudio";
539     case ATK_ROLE_BLOCK_QUOTE:
540         return "AXBlockquote";
541     case ATK_ROLE_DEFINITION:
542         return "AXDefinition";
543     case ATK_ROLE_LOG:
544         return "AXLog";
545     case ATK_ROLE_MARQUEE:
546         return "AXMarquee";
547     case ATK_ROLE_MATH:
548         return "AXMath";
549     case ATK_ROLE_TIMER:
550         return "AXTimer";
551     case ATK_ROLE_VIDEO:
552         return "AXVideo";
553 #endif
554 #if ATK_CHECK_VERSION(2, 11, 4)
555     case ATK_ROLE_DESCRIPTION_LIST:
556         return "AXDescriptionList";
557     case ATK_ROLE_DESCRIPTION_TERM:
558         return "AXDescriptionTerm";
559     case ATK_ROLE_DESCRIPTION_VALUE:
560         return "AXDescriptionValue";
561 #endif
562 #if ATK_CHECK_VERSION(2, 15, 2)
563     case ATK_ROLE_STATIC:
564         return "AXStatic";
565 #endif
566 #if ATK_CHECK_VERSION(2, 15, 4)
567     case ATK_ROLE_MATH_FRACTION:
568         return "AXMathFraction";
569     case ATK_ROLE_MATH_ROOT:
570         return "AXMathRoot";
571     case ATK_ROLE_SUBSCRIPT:
572         return "AXSubscript";
573     case ATK_ROLE_SUPERSCRIPT:
574         return "AXSuperscript";
575 #endif
576 #if ATK_CHECK_VERSION(2, 25, 2)
577     case ATK_ROLE_FOOTNOTE:
578         return "AXFootnote";
579 #endif
580     default:
581         // We want to distinguish ATK_ROLE_UNKNOWN from a known AtkRole which
582         // our DRT isn't properly handling.
583         return "FIXME not identified";
584     }
585 }
586
587 String selectedText(AtkObject* accessible)
588 {
589     if (!ATK_IS_TEXT(accessible))
590         return String();
591
592     AtkText* text = ATK_TEXT(accessible);
593
594     gint start, end;
595     g_free(atk_text_get_selection(text, 0, &start, &end));
596
597     return atk_text_get_text(text, start, end);
598 }
599
600 String attributesOfElement(AccessibilityUIElement* element)
601 {
602     StringBuilder builder;
603
604     builder.append(String::format("%s\n", element->role()->string().utf8().data()));
605
606     // For the parent we print its role and its name, if available.
607     builder.append("AXParent: ");
608     RefPtr<AccessibilityUIElement> parent = element->parentElement();
609     AtkObject* atkParent = parent ? parent->platformUIElement().get() : nullptr;
610     if (atkParent) {
611         builder.append(roleToString(atkParent));
612         const char* parentName = atk_object_get_name(atkParent);
613         if (parentName && g_utf8_strlen(parentName, -1))
614             builder.append(String::format(": %s", parentName));
615     } else
616         builder.append("(null)");
617     builder.append("\n");
618
619     builder.append(String::format("AXChildren: %d\n", element->childrenCount()));
620     builder.append(String::format("AXPosition: { %f, %f }\n", element->x(), element->y()));
621     builder.append(String::format("AXSize: { %f, %f }\n", element->width(), element->height()));
622
623     String title = element->title()->string();
624     if (!title.isEmpty())
625         builder.append(String::format("%s\n", title.utf8().data()));
626
627     String description = element->description()->string();
628     if (!description.isEmpty())
629         builder.append(String::format("%s\n", description.utf8().data()));
630
631     String value = element->stringValue()->string();
632     if (!value.isEmpty())
633         builder.append(String::format("%s\n", value.utf8().data()));
634
635     builder.append(String::format("AXFocusable: %d\n", element->isFocusable()));
636     builder.append(String::format("AXFocused: %d\n", element->isFocused()));
637     builder.append(String::format("AXSelectable: %d\n", element->isSelectable()));
638     builder.append(String::format("AXSelected: %d\n", element->isSelected()));
639     builder.append(String::format("AXMultiSelectable: %d\n", element->isMultiSelectable()));
640     builder.append(String::format("AXEnabled: %d\n", element->isEnabled()));
641     builder.append(String::format("AXExpanded: %d\n", element->isExpanded()));
642     builder.append(String::format("AXRequired: %d\n", element->isRequired()));
643     builder.append(String::format("AXChecked: %d\n", element->isChecked()));
644
645     String url = element->url()->string();
646     if (!url.isEmpty())
647         builder.append(String::format("%s\n", url.utf8().data()));
648
649     // We append the ATK specific attributes as a single line at the end.
650     builder.append("AXPlatformAttributes: ");
651     builder.append(getAtkAttributeSetAsString(element->platformUIElement().get(), ObjectAttributeType));
652
653     return builder.toString();
654 }
655
656 static JSRetainPtr<JSStringRef> createStringWithAttributes(const Vector<RefPtr<AccessibilityUIElement> >& elements)
657 {
658     StringBuilder builder;
659
660     for (Vector<RefPtr<AccessibilityUIElement> >::const_iterator it = elements.begin(); it != elements.end(); ++it) {
661         builder.append(attributesOfElement(const_cast<AccessibilityUIElement*>(it->get())));
662         builder.append("\n------------\n");
663     }
664
665     return JSStringCreateWithUTF8CString(builder.toString().utf8().data());
666 }
667
668 static Vector<RefPtr<AccessibilityUIElement> > getTableRowHeaders(AtkTable* accessible)
669 {
670     Vector<RefPtr<AccessibilityUIElement> > rowHeaders;
671
672     int rowsCount = atk_table_get_n_rows(accessible);
673     for (int row = 0; row < rowsCount; ++row) {
674         if (AtkObject* header = atk_table_get_row_header(accessible, row))
675             rowHeaders.append(AccessibilityUIElement::create(header));
676     }
677
678     return rowHeaders;
679 }
680
681 static Vector<RefPtr<AccessibilityUIElement> > getTableColumnHeaders(AtkTable* accessible)
682 {
683     Vector<RefPtr<AccessibilityUIElement> > columnHeaders;
684
685     int columnsCount = atk_table_get_n_columns(accessible);
686     for (int column = 0; column < columnsCount; ++column) {
687         if (AtkObject* header = atk_table_get_column_header(accessible, column))
688             columnHeaders.append(AccessibilityUIElement::create(header));
689     }
690
691     return columnHeaders;
692 }
693
694 static Vector<RefPtr<AccessibilityUIElement> > getVisibleCells(AccessibilityUIElement* element)
695 {
696     Vector<RefPtr<AccessibilityUIElement> > visibleCells;
697
698     AtkTable* accessible = ATK_TABLE(element->platformUIElement().get());
699     int rowsCount = atk_table_get_n_rows(accessible);
700     int columnsCount = atk_table_get_n_columns(accessible);
701
702     for (int row = 0; row < rowsCount; ++row) {
703         for (int column = 0; column < columnsCount; ++column)
704             visibleCells.append(element->cellForColumnAndRow(column, row));
705     }
706
707     return visibleCells;
708 }
709
710 #if ATK_CHECK_VERSION(2,11,90)
711 static Vector<RefPtr<AccessibilityUIElement>> convertGPtrArrayToVector(const GPtrArray* array)
712 {
713     Vector<RefPtr<AccessibilityUIElement>> cells;
714     for (guint i = 0; i < array->len; i++) {
715         if (AtkObject* atkObject = static_cast<AtkObject*>(g_ptr_array_index(array, i)))
716             cells.append(AccessibilityUIElement::create(atkObject));
717     }
718     return cells;
719 }
720
721 static JSValueRef convertToJSObjectArray(const Vector<RefPtr<AccessibilityUIElement>>& children)
722 {
723     WKBundleFrameRef mainFrame = WKBundlePageGetMainFrame(InjectedBundle::singleton().page()->page());
724     JSContextRef context = WKBundleFrameGetJavaScriptContext(mainFrame);
725
726     size_t elementCount = children.size();
727     auto valueElements = std::make_unique<JSValueRef[]>(elementCount);
728     for (size_t i = 0; i < elementCount; i++)
729         valueElements[i] = JSObjectMake(context, children[i]->wrapperClass(), children[i].get());
730
731     return JSObjectMakeArray(context, elementCount, valueElements.get(), nullptr);
732 }
733 #endif
734
735 #if ATK_CHECK_VERSION(2,11,92)
736 static double rangeMinMaxValue(AtkValue* atkValue, RangeLimit rangeLimit)
737 {
738     AtkRange* range = atk_value_get_range(atkValue);
739     if (!range)
740         return 0;
741
742     double rangeValue = 0;
743     switch (rangeLimit) {
744     case RangeLimitMinimum:
745         rangeValue = atk_range_get_lower_limit(range);
746         break;
747     case RangeLimitMaximum:
748         rangeValue = atk_range_get_upper_limit(range);
749         break;
750     };
751
752     atk_range_free(range);
753     return rangeValue;
754 }
755 #endif
756
757 } // namespace
758
759 AccessibilityUIElement::AccessibilityUIElement(PlatformUIElement element)
760     : m_element(element)
761 {
762 }
763
764 AccessibilityUIElement::AccessibilityUIElement(const AccessibilityUIElement& other)
765     : JSWrappable()
766     , m_element(other.m_element)
767 {
768 }
769
770 AccessibilityUIElement::~AccessibilityUIElement()
771 {
772 }
773
774 bool AccessibilityUIElement::isEqual(AccessibilityUIElement* otherElement)
775 {
776     return m_element == otherElement->platformUIElement();
777 }
778
779 void AccessibilityUIElement::getChildren(Vector<RefPtr<AccessibilityUIElement> >& children)
780 {
781     if (!ATK_IS_OBJECT(m_element.get()))
782         return;
783
784     int count = childrenCount();
785     for (int i = 0; i < count; i++) {
786         GRefPtr<AtkObject> child = adoptGRef(atk_object_ref_accessible_child(ATK_OBJECT(m_element.get()), i));
787         children.append(AccessibilityUIElement::create(child.get()));
788     }
789 }
790
791 void AccessibilityUIElement::getChildrenWithRange(Vector<RefPtr<AccessibilityUIElement> >& children, unsigned location, unsigned length)
792 {
793     if (!ATK_IS_OBJECT(m_element.get()))
794         return;
795     unsigned end = location + length;
796     for (unsigned i = location; i < end; i++) {
797         GRefPtr<AtkObject> child = adoptGRef(atk_object_ref_accessible_child(ATK_OBJECT(m_element.get()), i));
798         children.append(AccessibilityUIElement::create(child.get()));
799     }
800 }
801
802 int AccessibilityUIElement::childrenCount()
803 {
804     if (!ATK_IS_OBJECT(m_element.get()))
805         return 0;
806
807     return atk_object_get_n_accessible_children(ATK_OBJECT(m_element.get()));
808 }
809
810 RefPtr<AccessibilityUIElement> AccessibilityUIElement::elementAtPoint(int x, int y)
811 {
812     if (!ATK_IS_COMPONENT(m_element.get()))
813         return nullptr;
814
815     GRefPtr<AtkObject> objectAtPoint = adoptGRef(atk_component_ref_accessible_at_point(ATK_COMPONENT(m_element.get()), x, y, ATK_XY_WINDOW));
816     return AccessibilityUIElement::create(objectAtPoint ? objectAtPoint.get() : m_element.get());
817 }
818
819 unsigned AccessibilityUIElement::indexOfChild(AccessibilityUIElement* element)
820 {
821     if (!ATK_IS_OBJECT(m_element.get()))
822         return 0;
823
824     Vector<RefPtr<AccessibilityUIElement> > children;
825     getChildren(children);
826
827     unsigned count = children.size();
828     for (unsigned i = 0; i < count; i++)
829         if (children[i]->isEqual(element))
830             return i;
831
832     return 0;
833 }
834
835 RefPtr<AccessibilityUIElement> AccessibilityUIElement::childAtIndex(unsigned index)
836 {
837     if (!ATK_IS_OBJECT(m_element.get()))
838         return nullptr;
839
840     Vector<RefPtr<AccessibilityUIElement> > children;
841     getChildrenWithRange(children, index, 1);
842
843     if (children.size() == 1)
844         return children[0];
845
846     return nullptr;
847 }
848
849 static RefPtr<AccessibilityUIElement> accessibilityElementAtIndex(AtkObject* element, AtkRelationType relationType, unsigned index)
850 {
851     if (!ATK_IS_OBJECT(element))
852         return nullptr;
853
854     AtkRelationSet* relationSet = atk_object_ref_relation_set(element);
855     if (!relationSet)
856         return nullptr;
857
858     AtkRelation* relation = atk_relation_set_get_relation_by_type(relationSet, relationType);
859     if (!relation)
860         return nullptr;
861
862     GPtrArray* targetList = atk_relation_get_target(relation);
863     if (!targetList || !targetList->len || index >= targetList->len)
864         return nullptr;
865
866     AtkObject* target = static_cast<AtkObject*>(g_ptr_array_index(targetList, index));
867     g_object_unref(relationSet);
868
869     return target ? AccessibilityUIElement::create(target).ptr() : nullptr;
870 }
871
872 RefPtr<AccessibilityUIElement> AccessibilityUIElement::linkedUIElementAtIndex(unsigned index)
873 {
874     // FIXME: implement
875     return nullptr;
876 }
877
878 RefPtr<AccessibilityUIElement> AccessibilityUIElement::ariaOwnsElementAtIndex(unsigned index)
879 {
880     return accessibilityElementAtIndex(m_element.get(), ATK_RELATION_NODE_PARENT_OF, index);
881 }
882
883 RefPtr<AccessibilityUIElement> AccessibilityUIElement::ariaOwnsReferencingElementAtIndex(unsigned index)
884 {
885     return accessibilityElementAtIndex(m_element.get(), ATK_RELATION_NODE_CHILD_OF, index);
886 }
887
888 RefPtr<AccessibilityUIElement> AccessibilityUIElement::ariaFlowToElementAtIndex(unsigned index)
889 {
890     return accessibilityElementAtIndex(m_element.get(), ATK_RELATION_FLOWS_TO, index);
891 }
892
893 RefPtr<AccessibilityUIElement> AccessibilityUIElement::ariaFlowToReferencingElementAtIndex(unsigned index)
894 {
895     return accessibilityElementAtIndex(m_element.get(), ATK_RELATION_FLOWS_FROM, index);
896 }
897
898 RefPtr<AccessibilityUIElement> AccessibilityUIElement::ariaControlsElementAtIndex(unsigned index)
899 {
900     return accessibilityElementAtIndex(m_element.get(), ATK_RELATION_CONTROLLER_FOR, index);
901 }
902
903 RefPtr<AccessibilityUIElement> AccessibilityUIElement::ariaControlsReferencingElementAtIndex(unsigned index)
904 {
905     return accessibilityElementAtIndex(m_element.get(), ATK_RELATION_CONTROLLED_BY, index);
906 }
907
908 RefPtr<AccessibilityUIElement> AccessibilityUIElement::ariaLabelledByElementAtIndex(unsigned index)
909 {
910     return accessibilityElementAtIndex(m_element.get(), ATK_RELATION_LABELLED_BY, index);
911 }
912
913 RefPtr<AccessibilityUIElement> AccessibilityUIElement::ariaLabelledByReferencingElementAtIndex(unsigned index)
914 {
915     return accessibilityElementAtIndex(m_element.get(), ATK_RELATION_LABEL_FOR, index);
916 }
917
918 RefPtr<AccessibilityUIElement> AccessibilityUIElement::ariaDescribedByElementAtIndex(unsigned index)
919 {
920     return accessibilityElementAtIndex(m_element.get(), ATK_RELATION_DESCRIBED_BY, index);
921 }
922
923 RefPtr<AccessibilityUIElement> AccessibilityUIElement::ariaDescribedByReferencingElementAtIndex(unsigned index)
924 {
925     return accessibilityElementAtIndex(m_element.get(), ATK_RELATION_DESCRIPTION_FOR, index);
926 }
927
928 RefPtr<AccessibilityUIElement> AccessibilityUIElement::ariaDetailsElementAtIndex(unsigned index)
929 {
930 #if ATK_CHECK_VERSION(2, 25, 2)
931     return accessibilityElementAtIndex(m_element.get(), ATK_RELATION_DETAILS, index);
932 #endif
933     return nullptr;
934 }
935
936 RefPtr<AccessibilityUIElement> AccessibilityUIElement::ariaDetailsReferencingElementAtIndex(unsigned index)
937 {
938 #if ATK_CHECK_VERSION(2, 25, 2)
939     return accessibilityElementAtIndex(m_element.get(), ATK_RELATION_DETAILS_FOR, index);
940 #endif
941     return nullptr;
942 }
943
944 RefPtr<AccessibilityUIElement> AccessibilityUIElement::ariaErrorMessageElementAtIndex(unsigned index)
945 {
946 #if ATK_CHECK_VERSION(2, 25, 2)
947     return accessibilityElementAtIndex(m_element.get(), ATK_RELATION_ERROR_MESSAGE, index);
948 #endif
949     return nullptr;
950 }
951
952 RefPtr<AccessibilityUIElement> AccessibilityUIElement::ariaErrorMessageReferencingElementAtIndex(unsigned index)
953 {
954 #if ATK_CHECK_VERSION(2, 25, 2)
955     return accessibilityElementAtIndex(m_element.get(), ATK_RELATION_ERROR_FOR, index);
956 #endif
957     return nullptr;
958 }
959
960 RefPtr<AccessibilityUIElement> AccessibilityUIElement::disclosedRowAtIndex(unsigned index)
961 {
962     // FIXME: implement
963     return nullptr;
964 }
965
966 RefPtr<AccessibilityUIElement> AccessibilityUIElement::rowAtIndex(unsigned index)
967 {
968     // ATK doesn't have API to get an accessible row by index directly. It does, however, have
969     // API to get cells in the row specified by index. The parent of a cell should be the row.
970     AtkTable* axTable = ATK_TABLE(m_element.get());
971     unsigned nColumns = columnCount();
972     for (unsigned col = 0; col < nColumns; col++) {
973         // Find the first cell in this row that only spans one row.
974         if (atk_table_get_row_extent_at(axTable, index, col) == 1) {
975             AtkObject* cell = atk_table_ref_at(axTable, index, col);
976             return cell ? AccessibilityUIElement::create(atk_object_get_parent(cell)).ptr() : nullptr;
977         }
978     }
979
980     return nullptr;
981 }
982
983 RefPtr<AccessibilityUIElement> AccessibilityUIElement::selectedChildAtIndex(unsigned index) const
984 {
985     if (!ATK_SELECTION(m_element.get()))
986         return nullptr;
987
988     GRefPtr<AtkObject> child = adoptGRef(atk_selection_ref_selection(ATK_SELECTION(m_element.get()), index));
989     return child ? AccessibilityUIElement::create(child.get()).ptr() : nullptr;
990 }
991
992 unsigned AccessibilityUIElement::selectedChildrenCount() const
993 {
994     if (!ATK_IS_SELECTION(m_element.get()))
995         return 0;
996     return atk_selection_get_selection_count(ATK_SELECTION(m_element.get()));
997 }
998
999 RefPtr<AccessibilityUIElement> AccessibilityUIElement::selectedRowAtIndex(unsigned index)
1000 {
1001     // FIXME: implement
1002     return nullptr;
1003 }
1004
1005 RefPtr<AccessibilityUIElement> AccessibilityUIElement::titleUIElement()
1006 {
1007     if (!ATK_IS_OBJECT(m_element.get()))
1008         return nullptr;
1009
1010     AtkRelationSet* set = atk_object_ref_relation_set(ATK_OBJECT(m_element.get()));
1011     if (!set)
1012         return nullptr;
1013
1014     AtkObject* target = nullptr;
1015     int count = atk_relation_set_get_n_relations(set);
1016     for (int i = 0; i < count; i++) {
1017         AtkRelation* relation = atk_relation_set_get_relation(set, i);
1018         if (atk_relation_get_relation_type(relation) == ATK_RELATION_LABELLED_BY) {
1019             GPtrArray* targetList = atk_relation_get_target(relation);
1020             if (targetList->len)
1021                 target = static_cast<AtkObject*>(g_ptr_array_index(targetList, 0));
1022         }
1023     }
1024
1025     g_object_unref(set);
1026     return target ? AccessibilityUIElement::create(target).ptr() : nullptr;
1027 }
1028
1029 RefPtr<AccessibilityUIElement> AccessibilityUIElement::parentElement()
1030 {
1031     if (!ATK_IS_OBJECT(m_element.get()))
1032         return nullptr;
1033
1034     AtkObject* parent = atk_object_get_parent(ATK_OBJECT(m_element.get()));
1035     return parent ? AccessibilityUIElement::create(parent).ptr() : nullptr;
1036 }
1037
1038 RefPtr<AccessibilityUIElement> AccessibilityUIElement::disclosedByRow()
1039 {
1040     // FIXME: implement
1041     return nullptr;
1042 }
1043
1044 JSRetainPtr<JSStringRef> AccessibilityUIElement::attributesOfLinkedUIElements()
1045 {
1046     // FIXME: implement
1047     return JSStringCreateWithCharacters(0, 0);
1048 }
1049
1050 JSRetainPtr<JSStringRef> AccessibilityUIElement::attributesOfDocumentLinks()
1051 {
1052     // FIXME: implement
1053     return JSStringCreateWithCharacters(0, 0);
1054 }
1055
1056 JSRetainPtr<JSStringRef> AccessibilityUIElement::attributesOfChildren()
1057 {
1058     if (!ATK_IS_OBJECT(m_element.get()))
1059         return JSStringCreateWithCharacters(0, 0);
1060
1061     Vector<RefPtr<AccessibilityUIElement> > children;
1062     getChildren(children);
1063
1064     return createStringWithAttributes(children);
1065 }
1066
1067 JSRetainPtr<JSStringRef> AccessibilityUIElement::allAttributes()
1068 {
1069     if (!ATK_IS_OBJECT(m_element.get()))
1070         return JSStringCreateWithCharacters(0, 0);
1071
1072     return JSStringCreateWithUTF8CString(attributesOfElement(this).utf8().data());
1073 }
1074
1075 JSRetainPtr<JSStringRef> AccessibilityUIElement::stringAttributeValue(JSStringRef attribute)
1076 {
1077     if (!ATK_IS_OBJECT(m_element.get()))
1078         return JSStringCreateWithCharacters(0, 0);
1079
1080     String atkAttributeName = coreAttributeToAtkAttribute(attribute);
1081
1082     // The value of AXSelectedText is not exposed through any AtkAttribute.
1083     if (atkAttributeName == "AXSelectedText") {
1084         String string = selectedText(m_element.get());
1085         return JSStringCreateWithUTF8CString(string.utf8().data());
1086     }
1087
1088     // Try object attributes before text attributes.
1089     String attributeValue = getAttributeSetValueForId(ATK_OBJECT(m_element.get()), ObjectAttributeType, atkAttributeName);
1090
1091     // Try text attributes if the requested one was not found and we have an AtkText object.
1092     if (attributeValue.isEmpty() && ATK_IS_TEXT(m_element.get()))
1093         attributeValue = getAttributeSetValueForId(ATK_OBJECT(m_element.get()), TextAttributeType, atkAttributeName);
1094
1095     // Additional check to make sure that the exposure of the state ATK_STATE_INVALID_ENTRY
1096     // is consistent with the exposure of aria-invalid as a text attribute, if present.
1097     if (atkAttributeName == attributesMap[InvalidNameIndex][AtkDomain]) {
1098         bool isInvalidState = checkElementState(m_element.get(), ATK_STATE_INVALID_ENTRY);
1099         if (attributeValue.isEmpty())
1100             return JSStringCreateWithUTF8CString(isInvalidState ? "true" : "false");
1101
1102         // If the text attribute was there, check that it's consistent with
1103         // what the state says or force the test to fail otherwise.
1104         bool isAriaInvalid = attributeValue != "false";
1105         if (isInvalidState != isAriaInvalid)
1106             return JSStringCreateWithCharacters(0, 0);
1107     }
1108
1109     return JSStringCreateWithUTF8CString(attributeValue.utf8().data());
1110 }
1111
1112 double AccessibilityUIElement::numberAttributeValue(JSStringRef attribute)
1113 {
1114     if (!ATK_IS_OBJECT(m_element.get()))
1115         return 0;
1116
1117     String atkAttributeName = coreAttributeToAtkAttribute(attribute);
1118     if (atkAttributeName.isEmpty())
1119         return 0;
1120
1121     if (atkAttributeName == "setsize" || atkAttributeName == "posinset") {
1122         String attributeValue = getAttributeSetValueForId(ATK_OBJECT(m_element.get()), ObjectAttributeType, atkAttributeName);
1123         if (!attributeValue.isEmpty())
1124             return attributeValue.toDouble();
1125     }
1126
1127     if (atkAttributeName.startsWith("row") || atkAttributeName.startsWith("col")) {
1128         String attributeValue = getAttributeSetValueForId(ATK_OBJECT(m_element.get()), ObjectAttributeType, atkAttributeName);
1129         if (!attributeValue.isEmpty())
1130             return attributeValue.toInt();
1131     }
1132
1133     return 0;
1134 }
1135
1136 JSValueRef AccessibilityUIElement::uiElementArrayAttributeValue(JSStringRef attribute) const
1137 {
1138     // FIXME: implement
1139     return nullptr;
1140 }
1141
1142 JSValueRef AccessibilityUIElement::rowHeaders() const
1143 {
1144     if (ATK_IS_TABLE(m_element.get()))
1145         return convertToJSObjectArray(getTableRowHeaders(ATK_TABLE(m_element.get())));
1146
1147     Vector<RefPtr<AccessibilityUIElement>> headers;
1148     if (!ATK_IS_TABLE_CELL(m_element.get()))
1149         return convertToJSObjectArray(headers);
1150
1151 #if ATK_CHECK_VERSION(2,11,90)
1152     if (GRefPtr<GPtrArray> array = adoptGRef(atk_table_cell_get_row_header_cells(ATK_TABLE_CELL(m_element.get()))))
1153         headers = convertGPtrArrayToVector(array.get());
1154 #endif
1155     return convertToJSObjectArray(headers);
1156 }
1157
1158 JSValueRef AccessibilityUIElement::columnHeaders() const
1159 {
1160     if (ATK_IS_TABLE(m_element.get()))
1161         return convertToJSObjectArray(getTableColumnHeaders(ATK_TABLE(m_element.get())));
1162
1163     Vector<RefPtr<AccessibilityUIElement>> headers;
1164     if (!ATK_IS_TABLE_CELL(m_element.get()))
1165         return convertToJSObjectArray(headers);
1166
1167 #if ATK_CHECK_VERSION(2,11,90)
1168     if (GRefPtr<GPtrArray> array = adoptGRef(atk_table_cell_get_column_header_cells(ATK_TABLE_CELL(m_element.get()))))
1169         headers = convertGPtrArrayToVector(array.get());
1170 #endif
1171     return convertToJSObjectArray(headers);
1172 }
1173
1174 RefPtr<AccessibilityUIElement> AccessibilityUIElement::uiElementAttributeValue(JSStringRef attribute) const
1175 {
1176     if (!ATK_IS_OBJECT(m_element.get()))
1177         return nullptr;
1178
1179     // ATK does not have this API. So we're "faking it" here on a case-by-case basis.
1180     String attributeString = jsStringToWTFString(attribute);
1181     AtkRole role = atk_object_get_role(ATK_OBJECT(m_element.get()));
1182     if (role == ATK_ROLE_SPIN_BUTTON && const_cast<AccessibilityUIElement*>(this)->childrenCount() == 2) {
1183         if (attributeString == "AXDecrementButton")
1184             return const_cast<AccessibilityUIElement*>(this)->childAtIndex(0);
1185         if (attributeString == "AXIncrementButton")
1186             return const_cast<AccessibilityUIElement*>(this)->childAtIndex(1);
1187     }
1188
1189     return nullptr;
1190 }
1191
1192 bool AccessibilityUIElement::boolAttributeValue(JSStringRef attribute)
1193 {
1194     if (!ATK_IS_OBJECT(m_element.get()))
1195         return false;
1196
1197     String attributeString = jsStringToWTFString(attribute);
1198     if (attributeString == "AXElementBusy")
1199         return checkElementState(m_element.get(), ATK_STATE_BUSY);
1200     if (attributeString == "AXChecked")
1201         return checkElementState(m_element.get(), ATK_STATE_CHECKED);
1202     if (attributeString == "AXEnabled")
1203         return checkElementState(m_element.get(), ATK_STATE_ENABLED);
1204     if (attributeString == "AXExpanded")
1205         return checkElementState(m_element.get(), ATK_STATE_EXPANDED);
1206     if (attributeString == "AXFocused")
1207         return checkElementState(m_element.get(), ATK_STATE_FOCUSED);
1208     if (attributeString == "AXInvalid")
1209         return checkElementState(m_element.get(), ATK_STATE_INVALID);
1210     if (attributeString == "AXModal")
1211         return checkElementState(m_element.get(), ATK_STATE_MODAL);
1212     if (attributeString == "AXMultiSelectable")
1213         return checkElementState(m_element.get(), ATK_STATE_MULTISELECTABLE);
1214     if (attributeString == "AXRequired")
1215         return checkElementState(m_element.get(), ATK_STATE_REQUIRED);
1216     if (attributeString == "AXSelected")
1217         return checkElementState(m_element.get(), ATK_STATE_SELECTED);
1218     if (attributeString == "AXSupportsAutoCompletion")
1219         return checkElementState(m_element.get(), ATK_STATE_SUPPORTS_AUTOCOMPLETION);
1220     if (attributeString == "AXVisited")
1221         return checkElementState(m_element.get(), ATK_STATE_VISITED);
1222
1223     if (attributeString == "AXInterfaceTable")
1224         return ATK_IS_TABLE(m_element.get());
1225     if (attributeString == "AXInterfaceTableCell")
1226         return ATK_IS_TABLE_CELL(m_element.get());
1227
1228     if (attributeString == "AXARIAAtomic") {
1229         String atkAttribute = coreAttributeToAtkAttribute(attribute);
1230         return getAttributeSetValueForId(ATK_OBJECT(m_element.get()), ObjectAttributeType, atkAttribute) == "true";
1231     }
1232
1233     return false;
1234 }
1235
1236 bool AccessibilityUIElement::isAttributeSettable(JSStringRef attribute)
1237 {
1238     if (!ATK_IS_OBJECT(m_element.get()))
1239         return false;
1240
1241     String attributeString = jsStringToWTFString(attribute);
1242     if (attributeString != "AXValue")
1243         return false;
1244
1245     // ATK does not have a single state or property to indicate whether or not the value
1246     // of an accessible object can be set. ATs look at several states and properties based
1247     // on the type of object. If nothing explicitly indicates the value can or cannot be
1248     // set, ATs make role- and interface-based decisions. We'll do something similar here.
1249
1250     // This state is expected to be present only for text widgets and contenteditable elements.
1251     if (checkElementState(m_element.get(), ATK_STATE_EDITABLE))
1252         return true;
1253
1254 #if ATK_CHECK_VERSION(2,11,2)
1255     // This state is applicable to checkboxes, radiobuttons, switches, etc.
1256     if (checkElementState(m_element.get(), ATK_STATE_CHECKABLE))
1257         return true;
1258 #endif
1259
1260 #if ATK_CHECK_VERSION(2,15,3)
1261     // This state is expected to be present only for controls and only if explicitly set.
1262     if (checkElementState(m_element.get(), ATK_STATE_READ_ONLY))
1263         return false;
1264 #endif
1265
1266     // We expose an object attribute to ATs when there is an author-provided ARIA property
1267     // and also when there is a supported ARIA role but no author-provided value.
1268     String isReadOnly = getAttributeSetValueForId(ATK_OBJECT(m_element.get()), ObjectAttributeType, "readonly");
1269     if (!isReadOnly.isEmpty())
1270         return isReadOnly == "true" ? false : true;
1271
1272     // If we have a native listbox or combobox and the value can be set, the options should
1273     // have ATK_STATE_SELECTABLE.
1274     AtkRole role = atk_object_get_role(ATK_OBJECT(m_element.get()));
1275     if (role == ATK_ROLE_LIST_BOX || role == ATK_ROLE_COMBO_BOX) {
1276         if (GRefPtr<AtkObject> child = adoptGRef(atk_object_ref_accessible_child(ATK_OBJECT(m_element.get()), 0))) {
1277             if (atk_object_get_role(ATK_OBJECT(child.get())) == ATK_ROLE_MENU)
1278                 child = adoptGRef(atk_object_ref_accessible_child(ATK_OBJECT(child.get()), 0));
1279             return child && checkElementState(child.get(), ATK_STATE_SELECTABLE);
1280         }
1281     }
1282
1283     // If we have a native element which exposes a range whose value can be set, it should
1284     // be focusable and have a true range.
1285     if (ATK_IS_VALUE(m_element.get()) && checkElementState(m_element.get(), ATK_STATE_FOCUSABLE))
1286         return minValue() != maxValue();
1287
1288     return false;
1289 }
1290
1291 bool AccessibilityUIElement::isAttributeSupported(JSStringRef attribute)
1292 {
1293     if (!ATK_IS_OBJECT(m_element.get()))
1294         return false;
1295
1296     String atkAttributeName = coreAttributeToAtkAttribute(attribute);
1297     if (atkAttributeName.isEmpty())
1298         return false;
1299
1300     // In ATK, "busy" is a state and is supported on all AtkObject instances.
1301     if (atkAttributeName == "busy")
1302         return true;
1303
1304     // For now, an attribute is supported whether it's exposed as a object or a text attribute.
1305     String attributeValue = getAttributeSetValueForId(ATK_OBJECT(m_element.get()), ObjectAttributeType, atkAttributeName);
1306     if (attributeValue.isEmpty())
1307         attributeValue = getAttributeSetValueForId(ATK_OBJECT(m_element.get()), TextAttributeType, atkAttributeName);
1308
1309     // When the aria-live value is "off", we expose that value via the "live" object attribute.
1310     if (atkAttributeName == "live" && attributeValue == "off")
1311         return false;
1312
1313     return !attributeValue.isEmpty();
1314 }
1315
1316 JSRetainPtr<JSStringRef> AccessibilityUIElement::parameterizedAttributeNames()
1317 {
1318     // FIXME: implement
1319     return JSStringCreateWithCharacters(0, 0);
1320 }
1321
1322 JSRetainPtr<JSStringRef> AccessibilityUIElement::role()
1323 {
1324     if (!ATK_IS_OBJECT(m_element.get()))
1325         return JSStringCreateWithCharacters(0, 0);
1326
1327     GUniquePtr<char> roleStringWithPrefix(g_strdup_printf("AXRole: %s", roleToString(ATK_OBJECT(m_element.get()))));
1328     return JSStringCreateWithUTF8CString(roleStringWithPrefix.get());
1329 }
1330
1331 JSRetainPtr<JSStringRef> AccessibilityUIElement::subrole()
1332 {
1333     // FIXME: implement
1334     return JSStringCreateWithCharacters(0, 0);
1335 }
1336
1337 JSRetainPtr<JSStringRef> AccessibilityUIElement::roleDescription()
1338 {
1339     String roleDescription = getAttributeSetValueForId(ATK_OBJECT(m_element.get()), ObjectAttributeType, "roledescription");
1340     GUniquePtr<gchar> axRoleDescription(g_strdup_printf("AXRoleDescription: %s", roleDescription.utf8().data()));
1341
1342     return JSStringCreateWithUTF8CString(axRoleDescription.get());
1343 }
1344
1345 JSRetainPtr<JSStringRef> AccessibilityUIElement::computedRoleString()
1346 {
1347     String role = getAttributeSetValueForId(ATK_OBJECT(m_element.get()), ObjectAttributeType, "computed-role");
1348     if (!role.isEmpty())
1349         return JSStringCreateWithUTF8CString(role.utf8().data());
1350
1351     return JSStringCreateWithCharacters(0, 0);
1352 }
1353
1354 JSRetainPtr<JSStringRef> AccessibilityUIElement::title()
1355 {
1356     if (!ATK_IS_OBJECT(m_element.get()))
1357         return JSStringCreateWithCharacters(0, 0);
1358
1359     const gchar* name = atk_object_get_name(ATK_OBJECT(m_element.get()));
1360     GUniquePtr<gchar> axTitle(g_strdup_printf("AXTitle: %s", name ? name : ""));
1361
1362     return JSStringCreateWithUTF8CString(axTitle.get());
1363 }
1364
1365 JSRetainPtr<JSStringRef> AccessibilityUIElement::description()
1366 {
1367     if (!ATK_IS_OBJECT(m_element.get()))
1368         return JSStringCreateWithCharacters(0, 0);
1369
1370     const gchar* description = atk_object_get_description(ATK_OBJECT(m_element.get()));
1371     if (!description)
1372         return JSStringCreateWithCharacters(0, 0);
1373
1374     GUniquePtr<gchar> axDesc(g_strdup_printf("AXDescription: %s", description));
1375
1376     return JSStringCreateWithUTF8CString(axDesc.get());
1377 }
1378
1379 JSRetainPtr<JSStringRef> AccessibilityUIElement::orientation() const
1380 {
1381     if (!ATK_IS_OBJECT(m_element.get()))
1382         return JSStringCreateWithCharacters(0, 0);
1383
1384     const gchar* axOrientation = nullptr;
1385     if (checkElementState(m_element.get(), ATK_STATE_HORIZONTAL))
1386         axOrientation = "AXOrientation: AXHorizontalOrientation";
1387     else if (checkElementState(m_element.get(), ATK_STATE_VERTICAL))
1388         axOrientation = "AXOrientation: AXVerticalOrientation";
1389     else
1390         axOrientation = "AXOrientation: AXUnknownOrientation";
1391
1392     return JSStringCreateWithUTF8CString(axOrientation);
1393 }
1394
1395 JSRetainPtr<JSStringRef> AccessibilityUIElement::stringValue()
1396 {
1397     if (!ATK_IS_TEXT(m_element.get()))
1398         return JSStringCreateWithCharacters(0, 0);
1399
1400     GUniquePtr<gchar> text(atk_text_get_text(ATK_TEXT(m_element.get()), 0, -1));
1401     GUniquePtr<gchar> textWithReplacedCharacters(replaceCharactersForResults(text.get()));
1402     GUniquePtr<gchar> axValue(g_strdup_printf("AXValue: %s", textWithReplacedCharacters.get()));
1403
1404     return JSStringCreateWithUTF8CString(axValue.get());
1405 }
1406
1407 JSRetainPtr<JSStringRef> AccessibilityUIElement::language()
1408 {
1409     if (!ATK_IS_OBJECT(m_element.get()))
1410         return JSStringCreateWithCharacters(0, 0);
1411
1412     const gchar* locale = atk_object_get_object_locale(ATK_OBJECT(m_element.get()));
1413     if (!locale)
1414         return JSStringCreateWithCharacters(0, 0);
1415
1416     GUniquePtr<char> axValue(g_strdup_printf("AXLanguage: %s", locale));
1417     return JSStringCreateWithUTF8CString(axValue.get());
1418 }
1419
1420 JSRetainPtr<JSStringRef> AccessibilityUIElement::helpText() const
1421 {
1422     if (!ATK_IS_OBJECT(m_element.get()))
1423         return JSStringCreateWithCharacters(0, 0);
1424
1425     AtkRelationSet* relationSet = atk_object_ref_relation_set(ATK_OBJECT(m_element.get()));
1426     if (!relationSet)
1427         return JSStringCreateWithCharacters(0, 0);
1428
1429     AtkRelation* relation = atk_relation_set_get_relation_by_type(relationSet, ATK_RELATION_DESCRIBED_BY);
1430     if (!relation)
1431         return JSStringCreateWithCharacters(0, 0);
1432
1433     GPtrArray* targetList = atk_relation_get_target(relation);
1434     if (!targetList || !targetList->len)
1435         return JSStringCreateWithCharacters(0, 0);
1436
1437     StringBuilder builder;
1438     builder.append("AXHelp: ");
1439
1440     for (guint targetCount = 0; targetCount < targetList->len; targetCount++) {
1441         if (AtkObject* target = static_cast<AtkObject*>(g_ptr_array_index(targetList, targetCount))) {
1442             GUniquePtr<gchar> text(atk_text_get_text(ATK_TEXT(target), 0, -1));
1443             if (targetCount)
1444                 builder.append(" ");
1445             builder.append(text.get());
1446         }
1447     }
1448
1449     g_object_unref(relationSet);
1450
1451     return JSStringCreateWithUTF8CString(builder.toString().utf8().data());
1452 }
1453
1454 double AccessibilityUIElement::x()
1455 {
1456     if (!ATK_IS_COMPONENT(m_element.get()))
1457         return 0;
1458
1459     int x;
1460 #if ATK_CHECK_VERSION(2,11,90)
1461     atk_component_get_extents(ATK_COMPONENT(m_element.get()), &x, nullptr, nullptr, nullptr, ATK_XY_SCREEN);
1462 #else
1463     atk_component_get_position(ATK_COMPONENT(m_element.get()), &x, nullptr, ATK_XY_SCREEN);
1464 #endif
1465     return x;
1466 }
1467
1468 double AccessibilityUIElement::y()
1469 {
1470     if (!ATK_IS_COMPONENT(m_element.get()))
1471         return 0;
1472
1473     int y;
1474 #if ATK_CHECK_VERSION(2,11,90)
1475     atk_component_get_extents(ATK_COMPONENT(m_element.get()), nullptr, &y, nullptr, nullptr, ATK_XY_SCREEN);
1476 #else
1477     atk_component_get_position(ATK_COMPONENT(m_element.get()), nullptr, &y, ATK_XY_SCREEN);
1478 #endif
1479     return y;
1480 }
1481
1482 double AccessibilityUIElement::width()
1483 {
1484     if (!ATK_IS_COMPONENT(m_element.get()))
1485         return 0;
1486
1487     int width;
1488 #if ATK_CHECK_VERSION(2,11,90)
1489     atk_component_get_extents(ATK_COMPONENT(m_element.get()), nullptr, nullptr, &width, nullptr, ATK_XY_WINDOW);
1490 #else
1491     atk_component_get_size(ATK_COMPONENT(m_element.get()), &width, nullptr);
1492 #endif
1493     return width;
1494 }
1495
1496 double AccessibilityUIElement::height()
1497 {
1498     if (!ATK_IS_COMPONENT(m_element.get()))
1499         return 0;
1500
1501     int height;
1502 #if ATK_CHECK_VERSION(2,11,90)
1503     atk_component_get_extents(ATK_COMPONENT(m_element.get()), nullptr, nullptr, nullptr, &height, ATK_XY_WINDOW);
1504 #else
1505     atk_component_get_size(ATK_COMPONENT(m_element.get()), nullptr, &height);
1506 #endif
1507     return height;
1508 }
1509
1510 double AccessibilityUIElement::clickPointX()
1511 {
1512     if (!ATK_IS_COMPONENT(m_element.get()))
1513         return 0;
1514
1515     int x, width;
1516 #if ATK_CHECK_VERSION(2,11,90)
1517     atk_component_get_extents(ATK_COMPONENT(m_element.get()), &x, nullptr, &width, nullptr, ATK_XY_WINDOW);
1518 #else
1519     atk_component_get_position(ATK_COMPONENT(m_element.get()), &x, nullptr, ATK_XY_WINDOW);
1520     atk_component_get_size(ATK_COMPONENT(m_element.get()), &width, nullptr);
1521 #endif
1522
1523     return x + width / 2.0;
1524 }
1525
1526 double AccessibilityUIElement::clickPointY()
1527 {
1528     if (!ATK_IS_COMPONENT(m_element.get()))
1529         return 0;
1530
1531     int y, height;
1532 #if ATK_CHECK_VERSION(2,11,90)
1533     atk_component_get_extents(ATK_COMPONENT(m_element.get()), nullptr, &y, nullptr, &height, ATK_XY_WINDOW);
1534 #else
1535     atk_component_get_position(ATK_COMPONENT(m_element.get()), nullptr, &y, ATK_XY_WINDOW);
1536     atk_component_get_size(ATK_COMPONENT(m_element.get()), nullptr, &height);
1537 #endif
1538
1539     return y + height / 2.0;
1540 }
1541
1542 double AccessibilityUIElement::intValue() const
1543 {
1544     if (!ATK_IS_OBJECT(m_element.get()))
1545         return 0;
1546
1547     if (ATK_IS_VALUE(m_element.get())) {
1548 #if ATK_CHECK_VERSION(2,11,92)
1549         double value;
1550         atk_value_get_value_and_text(ATK_VALUE(m_element.get()), &value, nullptr);
1551         return value;
1552 #else
1553         GValue value = G_VALUE_INIT;
1554         atk_value_get_current_value(ATK_VALUE(m_element.get()), &value);
1555         if (!G_VALUE_HOLDS_FLOAT(&value))
1556             return 0;
1557         return g_value_get_float(&value);
1558 #endif
1559     }
1560
1561     // Consider headings as an special case when returning the "int value" of
1562     // an AccessibilityUIElement, so we can reuse some tests to check the level
1563     // both for HTML headings and objects with the aria-level attribute.
1564     if (atk_object_get_role(ATK_OBJECT(m_element.get())) == ATK_ROLE_HEADING) {
1565         String headingLevel = getAttributeSetValueForId(ATK_OBJECT(m_element.get()), ObjectAttributeType, "level");
1566         bool ok;
1567         double headingLevelValue = headingLevel.toDouble(&ok);
1568         if (ok)
1569             return headingLevelValue;
1570     }
1571
1572     return 0;
1573 }
1574
1575 double AccessibilityUIElement::minValue()
1576 {
1577     if (!ATK_IS_VALUE(m_element.get()))
1578         return 0;
1579 #if ATK_CHECK_VERSION(2,11,92)
1580     return rangeMinMaxValue(ATK_VALUE(m_element.get()), RangeLimitMinimum);
1581 #else
1582     GValue value = G_VALUE_INIT;
1583     atk_value_get_minimum_value(ATK_VALUE(m_element.get()), &value);
1584     if (!G_VALUE_HOLDS_FLOAT(&value))
1585         return 0;
1586
1587     return g_value_get_float(&value);
1588 #endif
1589 }
1590
1591 double AccessibilityUIElement::maxValue()
1592 {
1593     if (!ATK_IS_VALUE(m_element.get()))
1594         return 0;
1595
1596 #if ATK_CHECK_VERSION(2,11,92)
1597     return rangeMinMaxValue(ATK_VALUE(m_element.get()), RangeLimitMaximum);
1598 #else
1599     GValue value = G_VALUE_INIT;
1600     atk_value_get_maximum_value(ATK_VALUE(m_element.get()), &value);
1601     if (!G_VALUE_HOLDS_FLOAT(&value))
1602         return 0;
1603
1604     return g_value_get_float(&value);
1605 #endif
1606 }
1607
1608 JSRetainPtr<JSStringRef> AccessibilityUIElement::valueDescription()
1609 {
1610     String valueText = getAttributeSetValueForId(ATK_OBJECT(m_element.get()), ObjectAttributeType, "valuetext");
1611     GUniquePtr<gchar> valueDescription(g_strdup_printf("AXValueDescription: %s", valueText.utf8().data()));
1612     return JSStringCreateWithUTF8CString(valueDescription.get());
1613 }
1614
1615 int AccessibilityUIElement::insertionPointLineNumber()
1616 {
1617     // FIXME: implement
1618     return -1;
1619 }
1620
1621 bool AccessibilityUIElement::isPressActionSupported()
1622 {
1623     if (!ATK_IS_ACTION(m_element.get()))
1624         return false;
1625
1626     const gchar* actionName = atk_action_get_name(ATK_ACTION(m_element.get()), 0);
1627     return equalLettersIgnoringASCIICase(String(actionName), "press") || equalLettersIgnoringASCIICase(String(actionName), "jump");
1628 }
1629
1630 bool AccessibilityUIElement::isIncrementActionSupported()
1631 {
1632     // FIXME: implement
1633     return false;
1634 }
1635
1636 bool AccessibilityUIElement::isDecrementActionSupported()
1637 {
1638     // FIXME: implement
1639     return false;
1640 }
1641
1642 bool AccessibilityUIElement::isEnabled()
1643 {
1644     return checkElementState(m_element.get(), ATK_STATE_ENABLED);
1645 }
1646
1647 bool AccessibilityUIElement::isRequired() const
1648 {
1649     return checkElementState(m_element.get(), ATK_STATE_REQUIRED);
1650 }
1651
1652 bool AccessibilityUIElement::isFocused() const
1653 {
1654     return checkElementState(m_element.get(), ATK_STATE_FOCUSED);
1655 }
1656
1657 bool AccessibilityUIElement::isSelected() const
1658 {
1659     return checkElementState(m_element.get(), ATK_STATE_SELECTED);
1660 }
1661
1662 bool AccessibilityUIElement::isSelectedOptionActive() const
1663 {
1664     return checkElementState(m_element.get(), ATK_STATE_ACTIVE);
1665 }
1666
1667 bool AccessibilityUIElement::isExpanded() const
1668 {
1669     return checkElementState(m_element.get(), ATK_STATE_EXPANDED);
1670 }
1671
1672 bool AccessibilityUIElement::isChecked() const
1673 {
1674     return checkElementState(m_element.get(), ATK_STATE_CHECKED);
1675 }
1676
1677 bool AccessibilityUIElement::isIndeterminate() const
1678 {
1679     return checkElementState(m_element.get(), ATK_STATE_INDETERMINATE);
1680 }
1681
1682 int AccessibilityUIElement::hierarchicalLevel() const
1683 {
1684     String level = getAttributeSetValueForId(ATK_OBJECT(m_element.get()), ObjectAttributeType, "level");
1685     if (!level.isEmpty())
1686         return level.toInt();
1687
1688     return 0;
1689 }
1690
1691 JSRetainPtr<JSStringRef> AccessibilityUIElement::speakAs()
1692 {
1693     // FIXME: implement
1694     return JSStringCreateWithCharacters(0, 0);
1695 }
1696
1697 bool AccessibilityUIElement::ariaIsGrabbed() const
1698 {
1699     return getAttributeSetValueForId(ATK_OBJECT(m_element.get()), ObjectAttributeType, "grabbed") == "true";
1700 }
1701
1702 JSRetainPtr<JSStringRef> AccessibilityUIElement::ariaDropEffects() const
1703 {
1704     String dropEffects = getAttributeSetValueForId(ATK_OBJECT(m_element.get()), ObjectAttributeType, "dropeffect");
1705     return dropEffects.isEmpty() ? JSStringCreateWithCharacters(0, 0) : JSStringCreateWithUTF8CString(dropEffects.utf8().data());
1706 }
1707
1708 // parameterized attributes
1709 int AccessibilityUIElement::lineForIndex(int index)
1710 {
1711     if (!ATK_IS_TEXT(m_element.get()))
1712         return -1;
1713
1714     if (index < 0 || index > atk_text_get_character_count(ATK_TEXT(m_element.get())))
1715         return -1;
1716
1717     GUniquePtr<gchar> text(atk_text_get_text(ATK_TEXT(m_element.get()), 0, index));
1718     int lineNo = 0;
1719     for (gchar* offset = text.get(); *offset; ++offset) {
1720         if (*offset == '\n')
1721             ++lineNo;
1722     }
1723
1724     return lineNo;
1725 }
1726
1727 JSRetainPtr<JSStringRef> AccessibilityUIElement::rangeForLine(int line)
1728 {
1729     if (!ATK_IS_TEXT(m_element.get()))
1730         return JSStringCreateWithCharacters(0, 0);
1731
1732     AtkText* text = ATK_TEXT(m_element.get());
1733     gint startOffset = 0, endOffset = 0;
1734     for (int i = 0; i <= line; ++i)
1735         atk_text_get_string_at_offset(text, endOffset, ATK_TEXT_GRANULARITY_LINE, &startOffset, &endOffset);
1736
1737     GUniquePtr<gchar> range(g_strdup_printf("{%d, %d}", startOffset, endOffset - startOffset));
1738     return JSStringCreateWithUTF8CString(range.get());
1739 }
1740
1741 JSRetainPtr<JSStringRef> AccessibilityUIElement::rangeForPosition(int x, int y)
1742 {
1743     // FIXME: implement
1744     return JSStringCreateWithCharacters(0, 0);
1745 }
1746
1747 JSRetainPtr<JSStringRef> AccessibilityUIElement::boundsForRange(unsigned location, unsigned length)
1748 {
1749     if (!ATK_IS_TEXT(m_element.get()))
1750         return JSStringCreateWithCharacters(0, 0);
1751
1752     AtkTextRectangle rect;
1753     atk_text_get_range_extents(ATK_TEXT(m_element.get()), location, location + length, ATK_XY_WINDOW, &rect);
1754
1755     GUniquePtr<gchar> bounds(g_strdup_printf("{%d, %d, %d, %d}", rect.x, rect.y, rect.width, rect.height));
1756     return JSStringCreateWithUTF8CString(bounds.get());
1757 }
1758
1759 JSRetainPtr<JSStringRef> AccessibilityUIElement::stringForRange(unsigned location, unsigned length)
1760 {
1761     if (!ATK_IS_TEXT(m_element.get()))
1762         return JSStringCreateWithCharacters(0, 0);
1763
1764     String string = atk_text_get_text(ATK_TEXT(m_element.get()), location, location + length);
1765     return JSStringCreateWithUTF8CString(string.utf8().data());
1766 }
1767
1768 JSRetainPtr<JSStringRef> AccessibilityUIElement::attributedStringForRange(unsigned location, unsigned length)
1769 {
1770     if (!ATK_IS_TEXT(m_element.get()))
1771         return JSStringCreateWithCharacters(0, 0);
1772
1773     StringBuilder builder;
1774
1775     // The default text attributes apply to the entire element.
1776     builder.append("\n\tDefault text attributes:\n\t\t");
1777     builder.append(attributeSetToString(getAttributeSet(m_element.get(), TextAttributeType), "\n\t\t"));
1778
1779     // The attribute run provides attributes specific to the range of text at the specified offset.
1780     AtkAttributeSet* attributeSet;
1781     AtkText* text = ATK_TEXT(m_element.get());
1782     gint start = 0, end = 0;
1783     for (unsigned i = location; i < location + length; i = end) {
1784         AtkAttributeSet* attributeSet = atk_text_get_run_attributes(text, i, &start, &end);
1785         GUniquePtr<gchar> substring(replaceCharactersForResults(atk_text_get_text(text, start, end)));
1786         builder.append(String::format("\n\tRange attributes for '%s':\n\t\t", substring.get()));
1787         builder.append(attributeSetToString(attributeSet, "\n\t\t"));
1788     }
1789
1790     atk_attribute_set_free(attributeSet);
1791
1792     return JSStringCreateWithUTF8CString(builder.toString().utf8().data());
1793 }
1794
1795 bool AccessibilityUIElement::attributedStringRangeIsMisspelled(unsigned location, unsigned length)
1796 {
1797     // FIXME: implement
1798     return false;
1799 }
1800
1801 unsigned AccessibilityUIElement::uiElementCountForSearchPredicate(JSContextRef context, AccessibilityUIElement* startElement, bool isDirectionNext, JSValueRef searchKey, JSStringRef searchText, bool visibleOnly, bool immediateDescendantsOnly)
1802 {
1803     // FIXME: implement
1804     return 0;
1805 }
1806
1807 RefPtr<AccessibilityUIElement> AccessibilityUIElement::uiElementForSearchPredicate(JSContextRef context, AccessibilityUIElement* startElement, bool isDirectionNext, JSValueRef searchKey, JSStringRef searchText, bool visibleOnly, bool immediateDescendantsOnly)
1808 {
1809     // FIXME: implement
1810     return nullptr;
1811 }
1812
1813 JSRetainPtr<JSStringRef> AccessibilityUIElement::selectTextWithCriteria(JSContextRef context, JSStringRef ambiguityResolution, JSValueRef searchStrings, JSStringRef replacementString, JSStringRef activity)
1814 {
1815     // FIXME: implement
1816     return nullptr;
1817 }
1818
1819 JSRetainPtr<JSStringRef> AccessibilityUIElement::attributesOfColumnHeaders()
1820 {
1821     if (!ATK_IS_TABLE(m_element.get()))
1822         return JSStringCreateWithCharacters(0, 0);
1823
1824     Vector<RefPtr<AccessibilityUIElement> > columnHeaders = getTableColumnHeaders(ATK_TABLE(m_element.get()));
1825     return createStringWithAttributes(columnHeaders);
1826 }
1827
1828 JSRetainPtr<JSStringRef> AccessibilityUIElement::attributesOfRowHeaders()
1829 {
1830     if (!ATK_IS_TABLE(m_element.get()))
1831         return JSStringCreateWithCharacters(0, 0);
1832
1833     Vector<RefPtr<AccessibilityUIElement> > rowHeaders = getTableRowHeaders(ATK_TABLE(m_element.get()));
1834     return createStringWithAttributes(rowHeaders);
1835 }
1836
1837 JSRetainPtr<JSStringRef> AccessibilityUIElement::attributesOfColumns()
1838 {
1839     // FIXME: implement
1840     return JSStringCreateWithCharacters(0, 0);
1841 }
1842
1843 JSRetainPtr<JSStringRef> AccessibilityUIElement::attributesOfRows()
1844 {
1845     // FIXME: implement
1846     return JSStringCreateWithCharacters(0, 0);
1847 }
1848
1849 JSRetainPtr<JSStringRef> AccessibilityUIElement::attributesOfVisibleCells()
1850 {
1851     if (!ATK_IS_TABLE(m_element.get()))
1852         return JSStringCreateWithCharacters(0, 0);
1853
1854     Vector<RefPtr<AccessibilityUIElement> > visibleCells = getVisibleCells(this);
1855     return createStringWithAttributes(visibleCells);
1856 }
1857
1858 JSRetainPtr<JSStringRef> AccessibilityUIElement::attributesOfHeader()
1859 {
1860     // FIXME: implement
1861     return JSStringCreateWithCharacters(0, 0);
1862 }
1863
1864 int AccessibilityUIElement::rowCount()
1865 {
1866     if (!ATK_IS_TABLE(m_element.get()))
1867         return 0;
1868
1869     return atk_table_get_n_rows(ATK_TABLE(m_element.get()));
1870 }
1871
1872 int AccessibilityUIElement::columnCount()
1873 {
1874     if (!ATK_IS_TABLE(m_element.get()))
1875         return 0;
1876
1877     return atk_table_get_n_columns(ATK_TABLE(m_element.get()));
1878 }
1879
1880 int AccessibilityUIElement::indexInTable()
1881 {
1882     // FIXME: implement
1883     return -1;
1884 }
1885
1886 JSRetainPtr<JSStringRef> AccessibilityUIElement::rowIndexRange()
1887 {
1888     // Range in table for rows.
1889     return indexRangeInTable(m_element.get(), true);
1890 }
1891
1892 JSRetainPtr<JSStringRef> AccessibilityUIElement::columnIndexRange()
1893 {
1894     // Range in table for columns.
1895     return indexRangeInTable(m_element.get(), false);
1896 }
1897
1898 RefPtr<AccessibilityUIElement> AccessibilityUIElement::cellForColumnAndRow(unsigned col, unsigned row)
1899 {
1900     if (!ATK_IS_TABLE(m_element.get()))
1901         return nullptr;
1902
1903     // Adopt the AtkObject representing the cell because
1904     // at_table_ref_at() transfers full ownership.
1905     GRefPtr<AtkObject> foundCell = adoptGRef(atk_table_ref_at(ATK_TABLE(m_element.get()), row, col));
1906     return foundCell ? AccessibilityUIElement::create(foundCell.get()).ptr() : nullptr;
1907 }
1908
1909 RefPtr<AccessibilityUIElement> AccessibilityUIElement::horizontalScrollbar() const
1910 {
1911     // FIXME: implement
1912     return nullptr;
1913 }
1914
1915 RefPtr<AccessibilityUIElement> AccessibilityUIElement::verticalScrollbar() const
1916 {
1917     // FIXME: implement
1918     return nullptr;
1919 }
1920
1921 JSRetainPtr<JSStringRef> AccessibilityUIElement::selectedTextRange()
1922 {
1923     if (!ATK_IS_TEXT(m_element.get()))
1924         return JSStringCreateWithCharacters(0, 0);
1925
1926     gint start, end;
1927     g_free(atk_text_get_selection(ATK_TEXT(m_element.get()), 0, &start, &end));
1928
1929     GUniquePtr<gchar> selection(g_strdup_printf("{%d, %d}", start, end - start));
1930     return JSStringCreateWithUTF8CString(selection.get());
1931 }
1932
1933 bool AccessibilityUIElement::setSelectedTextRange(unsigned location, unsigned length)
1934 {
1935     if (!ATK_IS_TEXT(m_element.get()))
1936         return false;
1937
1938     if (!length)
1939         return atk_text_set_caret_offset(ATK_TEXT(m_element.get()), location);
1940
1941     return atk_text_set_selection(ATK_TEXT(m_element.get()), 0, location, location + length);
1942 }
1943
1944 void AccessibilityUIElement::increment()
1945 {
1946     alterCurrentValue(m_element.get(), 1);
1947 }
1948
1949 void AccessibilityUIElement::decrement()
1950 {
1951     alterCurrentValue(m_element.get(), -1);
1952 }
1953
1954 void AccessibilityUIElement::showMenu()
1955 {
1956     // FIXME: implement
1957 }
1958
1959 void AccessibilityUIElement::press()
1960 {
1961     if (!ATK_IS_ACTION(m_element.get()))
1962         return;
1963
1964     // Only one action per object is supported so far.
1965     atk_action_do_action(ATK_ACTION(m_element.get()), 0);
1966 }
1967
1968 void AccessibilityUIElement::setSelectedChild(AccessibilityUIElement* element) const
1969 {
1970     // FIXME: implement
1971 }
1972
1973 void AccessibilityUIElement::setSelectedChildAtIndex(unsigned index) const
1974 {
1975     if (!ATK_IS_SELECTION(m_element.get()))
1976         return;
1977
1978     atk_selection_add_selection(ATK_SELECTION(m_element.get()), index);
1979 }
1980
1981 void AccessibilityUIElement::removeSelectionAtIndex(unsigned index) const
1982 {
1983     if (!ATK_IS_SELECTION(m_element.get()))
1984         return;
1985
1986     atk_selection_remove_selection(ATK_SELECTION(m_element.get()), index);
1987 }
1988
1989 void AccessibilityUIElement::clearSelectedChildren() const
1990 {
1991     if (!ATK_IS_SELECTION(m_element.get()))
1992         return;
1993
1994     atk_selection_clear_selection(ATK_SELECTION(m_element.get()));
1995 }
1996
1997 JSRetainPtr<JSStringRef> AccessibilityUIElement::accessibilityValue() const
1998 {
1999     // FIXME: implement
2000     return JSStringCreateWithCharacters(0, 0);
2001 }
2002
2003 JSRetainPtr<JSStringRef> AccessibilityUIElement::documentEncoding()
2004 {
2005     if (!ATK_IS_DOCUMENT(m_element.get()))
2006         return JSStringCreateWithCharacters(0, 0);
2007
2008     AtkRole role = atk_object_get_role(ATK_OBJECT(m_element.get()));
2009     if (role != ATK_ROLE_DOCUMENT_WEB)
2010         return JSStringCreateWithCharacters(0, 0);
2011
2012     return JSStringCreateWithUTF8CString(atk_document_get_attribute_value(ATK_DOCUMENT(m_element.get()), "Encoding"));
2013 }
2014
2015 JSRetainPtr<JSStringRef> AccessibilityUIElement::documentURI()
2016 {
2017     if (!ATK_IS_DOCUMENT(m_element.get()))
2018         return JSStringCreateWithCharacters(0, 0);
2019
2020     AtkRole role = atk_object_get_role(ATK_OBJECT(m_element.get()));
2021     if (role != ATK_ROLE_DOCUMENT_WEB)
2022         return JSStringCreateWithCharacters(0, 0);
2023
2024     return JSStringCreateWithUTF8CString(atk_document_get_attribute_value(ATK_DOCUMENT(m_element.get()), "URI"));
2025 }
2026
2027 JSRetainPtr<JSStringRef> AccessibilityUIElement::url()
2028 {
2029     if (!ATK_IS_HYPERLINK_IMPL(m_element.get()))
2030         return JSStringCreateWithCharacters(0, 0);
2031
2032     AtkHyperlink* hyperlink = atk_hyperlink_impl_get_hyperlink(ATK_HYPERLINK_IMPL(m_element.get()));
2033     GUniquePtr<char> hyperlinkURI(atk_hyperlink_get_uri(hyperlink, 0));
2034
2035     // Build the result string, stripping the absolute URL paths if present.
2036     char* localURI = g_strstr_len(hyperlinkURI.get(), -1, "LayoutTests");
2037     String axURL = String::format("AXURL: %s", localURI ? localURI : hyperlinkURI.get());
2038     return JSStringCreateWithUTF8CString(axURL.utf8().data());
2039 }
2040
2041 bool AccessibilityUIElement::addNotificationListener(JSValueRef functionCallback)
2042 {
2043     if (!functionCallback)
2044         return false;
2045
2046     // Only one notification listener per element.
2047     if (m_notificationHandler)
2048         return false;
2049
2050     m_notificationHandler = AccessibilityNotificationHandler::create();
2051     m_notificationHandler->setPlatformElement(platformUIElement());
2052     m_notificationHandler->setNotificationFunctionCallback(functionCallback);
2053
2054     return true;
2055 }
2056
2057 bool AccessibilityUIElement::removeNotificationListener()
2058 {
2059     // Programmers should not be trying to remove a listener that's already removed.
2060     ASSERT(m_notificationHandler);
2061     m_notificationHandler = nullptr;
2062
2063     return true;
2064 }
2065
2066 bool AccessibilityUIElement::isFocusable() const
2067 {
2068     return checkElementState(m_element.get(), ATK_STATE_FOCUSABLE);
2069 }
2070
2071 bool AccessibilityUIElement::isSelectable() const
2072 {
2073     return checkElementState(m_element.get(), ATK_STATE_SELECTABLE);
2074 }
2075
2076 bool AccessibilityUIElement::isMultiSelectable() const
2077 {
2078     return checkElementState(m_element.get(), ATK_STATE_MULTISELECTABLE);
2079 }
2080
2081 bool AccessibilityUIElement::isVisible() const
2082 {
2083     return checkElementState(m_element.get(), ATK_STATE_VISIBLE);
2084 }
2085
2086 bool AccessibilityUIElement::isOffScreen() const
2087 {
2088     // FIXME: implement
2089     return false;
2090 }
2091
2092 bool AccessibilityUIElement::isCollapsed() const
2093 {
2094     // FIXME: implement
2095     return false;
2096 }
2097
2098 bool AccessibilityUIElement::isIgnored() const
2099 {
2100     // FIXME: implement
2101     return false;
2102 }
2103
2104 bool AccessibilityUIElement::isSingleLine() const
2105 {
2106     return checkElementState(m_element.get(), ATK_STATE_SINGLE_LINE);
2107 }
2108
2109 bool AccessibilityUIElement::isMultiLine() const
2110 {
2111     return checkElementState(m_element.get(), ATK_STATE_MULTI_LINE);
2112 }
2113
2114 bool AccessibilityUIElement::hasPopup() const
2115 {
2116     return checkElementState(m_element.get(), ATK_STATE_HAS_POPUP);
2117 }
2118
2119 void AccessibilityUIElement::takeFocus()
2120 {
2121     // FIXME: implement
2122 }
2123
2124 void AccessibilityUIElement::takeSelection()
2125 {
2126     // FIXME: implement
2127 }
2128
2129 void AccessibilityUIElement::addSelection()
2130 {
2131     // FIXME: implement
2132 }
2133
2134 void AccessibilityUIElement::removeSelection()
2135 {
2136     // FIXME: implement
2137 }
2138
2139 // Text markers
2140 RefPtr<AccessibilityTextMarkerRange> AccessibilityUIElement::lineTextMarkerRangeForTextMarker(AccessibilityTextMarker* textMarker)
2141 {
2142     // FIXME: implement
2143     return nullptr;
2144 }
2145
2146 RefPtr<AccessibilityTextMarkerRange> AccessibilityUIElement::textMarkerRangeForElement(AccessibilityUIElement* element)
2147 {
2148     // FIXME: implement
2149     return nullptr;
2150 }
2151
2152 int AccessibilityUIElement::textMarkerRangeLength(AccessibilityTextMarkerRange* range)
2153 {
2154     // FIXME: implement
2155     return 0;
2156 }
2157
2158 RefPtr<AccessibilityTextMarker> AccessibilityUIElement::previousTextMarker(AccessibilityTextMarker* textMarker)
2159 {
2160     // FIXME: implement
2161     return nullptr;
2162 }
2163
2164 RefPtr<AccessibilityTextMarker> AccessibilityUIElement::nextTextMarker(AccessibilityTextMarker* textMarker)
2165 {
2166     // FIXME: implement
2167     return nullptr;
2168 }
2169
2170 JSRetainPtr<JSStringRef> AccessibilityUIElement::stringForTextMarkerRange(AccessibilityTextMarkerRange* markerRange)
2171 {
2172     // FIXME: implement
2173     return JSStringCreateWithCharacters(0, 0);
2174 }
2175
2176 RefPtr<AccessibilityTextMarkerRange> AccessibilityUIElement::textMarkerRangeForMarkers(AccessibilityTextMarker* startMarker, AccessibilityTextMarker* endMarker)
2177 {
2178     // FIXME: implement
2179     return nullptr;
2180 }
2181
2182 RefPtr<AccessibilityTextMarker> AccessibilityUIElement::startTextMarkerForTextMarkerRange(AccessibilityTextMarkerRange* range)
2183 {
2184     // FIXME: implement
2185     return nullptr;
2186 }
2187
2188 RefPtr<AccessibilityTextMarker> AccessibilityUIElement::endTextMarkerForTextMarkerRange(AccessibilityTextMarkerRange* range)
2189 {
2190     // FIXME: implement
2191     return nullptr;
2192 }
2193
2194 RefPtr<AccessibilityTextMarker> AccessibilityUIElement::endTextMarkerForBounds(int x, int y, int width, int height)
2195 {
2196     // FIXME: implement
2197     return nullptr;
2198 }
2199
2200 RefPtr<AccessibilityTextMarker> AccessibilityUIElement::startTextMarkerForBounds(int x, int y, int width, int height)
2201 {
2202     // FIXME: implement
2203     return nullptr;
2204 }
2205
2206 RefPtr<AccessibilityTextMarker> AccessibilityUIElement::textMarkerForPoint(int x, int y)
2207 {
2208     // FIXME: implement
2209     return nullptr;
2210 }
2211
2212 RefPtr<AccessibilityUIElement> AccessibilityUIElement::accessibilityElementForTextMarker(AccessibilityTextMarker* marker)
2213 {
2214     // FIXME: implement
2215     return nullptr;
2216 }
2217
2218 bool AccessibilityUIElement::attributedStringForTextMarkerRangeContainsAttribute(JSStringRef attribute, AccessibilityTextMarkerRange* range)
2219 {
2220     // FIXME: implement
2221     return false;
2222 }
2223
2224 int AccessibilityUIElement::indexForTextMarker(AccessibilityTextMarker* marker)
2225 {
2226     // FIXME: implement
2227     return -1;
2228 }
2229
2230 bool AccessibilityUIElement::isTextMarkerValid(AccessibilityTextMarker* textMarker)
2231 {
2232     // FIXME: implement
2233     return false;
2234 }
2235
2236 RefPtr<AccessibilityTextMarker> AccessibilityUIElement::textMarkerForIndex(int textIndex)
2237 {
2238     // FIXME: implement
2239     return nullptr;
2240 }
2241     
2242 RefPtr<AccessibilityTextMarker> AccessibilityUIElement::startTextMarker()
2243 {
2244     // FIXME: implement
2245     return nullptr;    
2246 }
2247
2248 RefPtr<AccessibilityTextMarker> AccessibilityUIElement::endTextMarker()
2249 {
2250     // FIXME: implement
2251     return nullptr;
2252 }
2253
2254 bool AccessibilityUIElement::setSelectedVisibleTextRange(AccessibilityTextMarkerRange*)
2255 {
2256     return false;
2257 }
2258
2259 void AccessibilityUIElement::scrollToMakeVisible()
2260 {
2261     // FIXME: implement
2262 }
2263     
2264 void AccessibilityUIElement::scrollToGlobalPoint(int x, int y)
2265 {
2266     // FIXME: implement
2267 }
2268     
2269 void AccessibilityUIElement::scrollToMakeVisibleWithSubFocus(int x, int y, int width, int height)
2270 {
2271     // FIXME: implement
2272 }
2273
2274 JSRetainPtr<JSStringRef> AccessibilityUIElement::supportedActions() const
2275 {
2276     // FIXME: implement
2277     return nullptr;
2278 }
2279
2280 JSRetainPtr<JSStringRef> AccessibilityUIElement::pathDescription() const
2281 {
2282     notImplemented();
2283     return nullptr;
2284 }
2285
2286 JSRetainPtr<JSStringRef> AccessibilityUIElement::mathPostscriptsDescription() const
2287 {
2288     notImplemented();
2289     return nullptr;
2290 }
2291
2292 JSRetainPtr<JSStringRef> AccessibilityUIElement::mathPrescriptsDescription() const
2293 {
2294     notImplemented();
2295     return nullptr;
2296 }
2297
2298 JSRetainPtr<JSStringRef> AccessibilityUIElement::classList() const
2299 {
2300     notImplemented();
2301     return nullptr;
2302 }
2303
2304 JSRetainPtr<JSStringRef> stringAtOffset(PlatformUIElement element, AtkTextBoundary boundary, int offset)
2305 {
2306     if (!ATK_IS_TEXT(element.get()))
2307         return JSStringCreateWithCharacters(0, 0);
2308
2309     gint startOffset, endOffset;
2310     StringBuilder builder;
2311
2312 #if ATK_CHECK_VERSION(2, 10, 0)
2313     AtkTextGranularity granularity;
2314     switch (boundary) {
2315     case ATK_TEXT_BOUNDARY_CHAR:
2316         granularity = ATK_TEXT_GRANULARITY_CHAR;
2317         break;
2318     case ATK_TEXT_BOUNDARY_WORD_START:
2319         granularity = ATK_TEXT_GRANULARITY_WORD;
2320         break;
2321     case ATK_TEXT_BOUNDARY_LINE_START:
2322         granularity = ATK_TEXT_GRANULARITY_LINE;
2323         break;
2324     case ATK_TEXT_BOUNDARY_SENTENCE_START:
2325         granularity = ATK_TEXT_GRANULARITY_SENTENCE;
2326         break;
2327     default:
2328         return JSStringCreateWithCharacters(0, 0);
2329     }
2330
2331     builder.append(atk_text_get_string_at_offset(ATK_TEXT(element.get()), offset, granularity, &startOffset, &endOffset));
2332 #else
2333     builder.append(atk_text_get_text_at_offset(ATK_TEXT(element.get()), offset, boundary, &startOffset, &endOffset));
2334 #endif
2335     builder.append(String::format(", %i, %i", startOffset, endOffset));
2336     return JSStringCreateWithUTF8CString(builder.toString().utf8().data());
2337 }
2338
2339 JSRetainPtr<JSStringRef> AccessibilityUIElement::characterAtOffset(int offset)
2340 {
2341     return stringAtOffset(m_element, ATK_TEXT_BOUNDARY_CHAR, offset);
2342 }
2343
2344 JSRetainPtr<JSStringRef> AccessibilityUIElement::wordAtOffset(int offset)
2345 {
2346     return stringAtOffset(m_element, ATK_TEXT_BOUNDARY_WORD_START, offset);
2347 }
2348
2349 JSRetainPtr<JSStringRef> AccessibilityUIElement::lineAtOffset(int offset)
2350 {
2351     return stringAtOffset(m_element, ATK_TEXT_BOUNDARY_LINE_START, offset);
2352 }
2353
2354 JSRetainPtr<JSStringRef> AccessibilityUIElement::sentenceAtOffset(int offset)
2355 {
2356     return stringAtOffset(m_element, ATK_TEXT_BOUNDARY_SENTENCE_START, offset);
2357 }
2358
2359 } // namespace WTR
2360
2361 #endif // HAVE(ACCESSIBILITY)