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