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