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