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