AX: [ATK] Expose aria-busy via ATK_STATE_BUSY
[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     if (!ATK_IS_OBJECT(m_element.get()))
1052         return false;
1053
1054     String attributeString = jsStringToWTFString(attribute);
1055     if (attributeString == "AXElementBusy")
1056         return checkElementState(m_element.get(), ATK_STATE_BUSY);
1057     if (attributeString == "AXChecked")
1058         return checkElementState(m_element.get(), ATK_STATE_CHECKED);
1059     if (attributeString == "AXEnabled")
1060         return checkElementState(m_element.get(), ATK_STATE_ENABLED);
1061     if (attributeString == "AXExpanded")
1062         return checkElementState(m_element.get(), ATK_STATE_EXPANDED);
1063     if (attributeString == "AXFocused")
1064         return checkElementState(m_element.get(), ATK_STATE_FOCUSED);
1065     if (attributeString == "AXInvalid")
1066         return checkElementState(m_element.get(), ATK_STATE_INVALID);
1067     if (attributeString == "AXMultiSelectable")
1068         return checkElementState(m_element.get(), ATK_STATE_MULTISELECTABLE);
1069     if (attributeString == "AXRequired")
1070         return checkElementState(m_element.get(), ATK_STATE_REQUIRED);
1071     if (attributeString == "AXSelected")
1072         return checkElementState(m_element.get(), ATK_STATE_SELECTED);
1073     if (attributeString == "AXVisited")
1074         return checkElementState(m_element.get(), ATK_STATE_VISITED);
1075
1076     return false;
1077 }
1078
1079 bool AccessibilityUIElement::isAttributeSettable(JSStringRef attribute)
1080 {
1081     if (!ATK_IS_OBJECT(m_element.get()))
1082         return false;
1083
1084     String attributeString = jsStringToWTFString(attribute);
1085     if (attributeString != "AXValue")
1086         return false;
1087
1088     // ATK does not have a single state or property to indicate whether or not the value
1089     // of an accessible object can be set. ATs look at several states and properties based
1090     // on the type of object. If nothing explicitly indicates the value can or cannot be
1091     // set, ATs make role- and interface-based decisions. We'll do something similar here.
1092
1093     // This state is expected to be present only for text widgets and contenteditable elements.
1094     if (checkElementState(m_element.get(), ATK_STATE_EDITABLE))
1095         return true;
1096
1097 #if ATK_CHECK_VERSION(2,11,2)
1098     // This state is applicable to checkboxes, radiobuttons, switches, etc.
1099     if (checkElementState(m_element.get(), ATK_STATE_CHECKABLE))
1100         return true;
1101 #endif
1102
1103 #if ATK_CHECK_VERSION(2,15,3)
1104     // This state is expected to be present only for controls and only if explicitly set.
1105     if (checkElementState(m_element.get(), ATK_STATE_READ_ONLY))
1106         return false;
1107 #endif
1108
1109     // We expose an object attribute to ATs when there is an author-provided ARIA property
1110     // and also when there is a supported ARIA role but no author-provided value.
1111     String isReadOnly = getAttributeSetValueForId(ATK_OBJECT(m_element.get()), ObjectAttributeType, "readonly");
1112     if (!isReadOnly.isEmpty())
1113         return isReadOnly == "true" ? false : true;
1114
1115     // If we have a native listbox or combobox and the value can be set, the options should
1116     // have ATK_STATE_SELECTABLE.
1117     AtkRole role = atk_object_get_role(ATK_OBJECT(m_element.get()));
1118     if (role == ATK_ROLE_LIST_BOX || role == ATK_ROLE_COMBO_BOX) {
1119         if (GRefPtr<AtkObject> child = adoptGRef(atk_object_ref_accessible_child(ATK_OBJECT(m_element.get()), 0))) {
1120             if (atk_object_get_role(ATK_OBJECT(child.get())) == ATK_ROLE_MENU)
1121                 child = adoptGRef(atk_object_ref_accessible_child(ATK_OBJECT(child.get()), 0));
1122             return child && checkElementState(child.get(), ATK_STATE_SELECTABLE);
1123         }
1124     }
1125
1126     // If we have a native element which exposes a range whose value can be set, it should
1127     // be focusable and have a true range.
1128     if (ATK_IS_VALUE(m_element.get()) && checkElementState(m_element.get(), ATK_STATE_FOCUSABLE))
1129         return minValue() != maxValue();
1130
1131     return false;
1132 }
1133
1134 bool AccessibilityUIElement::isAttributeSupported(JSStringRef attribute)
1135 {
1136     if (!ATK_IS_OBJECT(m_element.get()))
1137         return false;
1138
1139     String atkAttributeName = coreAttributeToAtkAttribute(attribute);
1140     if (atkAttributeName.isEmpty())
1141         return false;
1142
1143     // For now, an attribute is supported whether it's exposed as a object or a text attribute.
1144     String attributeValue = getAttributeSetValueForId(ATK_OBJECT(m_element.get()), ObjectAttributeType, atkAttributeName);
1145     if (attributeValue.isEmpty())
1146         attributeValue = getAttributeSetValueForId(ATK_OBJECT(m_element.get()), TextAttributeType, atkAttributeName);
1147
1148     return !attributeValue.isEmpty();
1149 }
1150
1151 JSRetainPtr<JSStringRef> AccessibilityUIElement::parameterizedAttributeNames()
1152 {
1153     // FIXME: implement
1154     return JSStringCreateWithCharacters(0, 0);
1155 }
1156
1157 JSRetainPtr<JSStringRef> AccessibilityUIElement::role()
1158 {
1159     if (!ATK_IS_OBJECT(m_element.get()))
1160         return JSStringCreateWithCharacters(0, 0);
1161
1162     GUniquePtr<char> roleStringWithPrefix(g_strdup_printf("AXRole: %s", roleToString(ATK_OBJECT(m_element.get()))));
1163     return JSStringCreateWithUTF8CString(roleStringWithPrefix.get());
1164 }
1165
1166 JSRetainPtr<JSStringRef> AccessibilityUIElement::subrole()
1167 {
1168     // FIXME: implement
1169     return JSStringCreateWithCharacters(0, 0);
1170 }
1171
1172 JSRetainPtr<JSStringRef> AccessibilityUIElement::roleDescription()
1173 {
1174     String roleDescription = getAttributeSetValueForId(ATK_OBJECT(m_element.get()), ObjectAttributeType, "roledescription");
1175     GUniquePtr<gchar> axRoleDescription(g_strdup_printf("AXRoleDescription: %s", roleDescription.utf8().data()));
1176
1177     return JSStringCreateWithUTF8CString(axRoleDescription.get());
1178 }
1179
1180 JSRetainPtr<JSStringRef> AccessibilityUIElement::computedRoleString()
1181 {
1182     String role = getAttributeSetValueForId(ATK_OBJECT(m_element.get()), ObjectAttributeType, "computed-role");
1183     if (!role.isEmpty())
1184         return JSStringCreateWithUTF8CString(role.utf8().data());
1185
1186     return JSStringCreateWithCharacters(0, 0);
1187 }
1188
1189 JSRetainPtr<JSStringRef> AccessibilityUIElement::title()
1190 {
1191     if (!ATK_IS_OBJECT(m_element.get()))
1192         return JSStringCreateWithCharacters(0, 0);
1193
1194     const gchar* name = atk_object_get_name(ATK_OBJECT(m_element.get()));
1195     GUniquePtr<gchar> axTitle(g_strdup_printf("AXTitle: %s", name ? name : ""));
1196
1197     return JSStringCreateWithUTF8CString(axTitle.get());
1198 }
1199
1200 JSRetainPtr<JSStringRef> AccessibilityUIElement::description()
1201 {
1202     if (!ATK_IS_OBJECT(m_element.get()))
1203         return JSStringCreateWithCharacters(0, 0);
1204
1205     const gchar* description = atk_object_get_description(ATK_OBJECT(m_element.get()));
1206     if (!description)
1207         return JSStringCreateWithCharacters(0, 0);
1208
1209     GUniquePtr<gchar> axDesc(g_strdup_printf("AXDescription: %s", description));
1210
1211     return JSStringCreateWithUTF8CString(axDesc.get());
1212 }
1213
1214 JSRetainPtr<JSStringRef> AccessibilityUIElement::orientation() const
1215 {
1216     if (!ATK_IS_OBJECT(m_element.get()))
1217         return JSStringCreateWithCharacters(0, 0);
1218
1219     const gchar* axOrientation = nullptr;
1220     if (checkElementState(m_element.get(), ATK_STATE_HORIZONTAL))
1221         axOrientation = "AXOrientation: AXHorizontalOrientation";
1222     else if (checkElementState(m_element.get(), ATK_STATE_VERTICAL))
1223         axOrientation = "AXOrientation: AXVerticalOrientation";
1224
1225     if (!axOrientation)
1226         return JSStringCreateWithCharacters(0, 0);
1227
1228     return JSStringCreateWithUTF8CString(axOrientation);
1229 }
1230
1231 JSRetainPtr<JSStringRef> AccessibilityUIElement::stringValue()
1232 {
1233     if (!ATK_IS_TEXT(m_element.get()))
1234         return JSStringCreateWithCharacters(0, 0);
1235
1236     GUniquePtr<gchar> text(atk_text_get_text(ATK_TEXT(m_element.get()), 0, -1));
1237     GUniquePtr<gchar> textWithReplacedCharacters(replaceCharactersForResults(text.get()));
1238     GUniquePtr<gchar> axValue(g_strdup_printf("AXValue: %s", textWithReplacedCharacters.get()));
1239
1240     return JSStringCreateWithUTF8CString(axValue.get());
1241 }
1242
1243 JSRetainPtr<JSStringRef> AccessibilityUIElement::language()
1244 {
1245     if (!ATK_IS_OBJECT(m_element.get()))
1246         return JSStringCreateWithCharacters(0, 0);
1247
1248     const gchar* locale = atk_object_get_object_locale(ATK_OBJECT(m_element.get()));
1249     if (!locale)
1250         return JSStringCreateWithCharacters(0, 0);
1251
1252     GUniquePtr<char> axValue(g_strdup_printf("AXLanguage: %s", locale));
1253     return JSStringCreateWithUTF8CString(axValue.get());
1254 }
1255
1256 JSRetainPtr<JSStringRef> AccessibilityUIElement::helpText() const
1257 {
1258     if (!ATK_IS_OBJECT(m_element.get()))
1259         return JSStringCreateWithCharacters(0, 0);
1260
1261     AtkRelationSet* relationSet = atk_object_ref_relation_set(ATK_OBJECT(m_element.get()));
1262     if (!relationSet)
1263         return JSStringCreateWithCharacters(0, 0);
1264
1265     AtkRelation* relation = atk_relation_set_get_relation_by_type(relationSet, ATK_RELATION_DESCRIBED_BY);
1266     if (!relation)
1267         return JSStringCreateWithCharacters(0, 0);
1268
1269     GPtrArray* targetList = atk_relation_get_target(relation);
1270     if (!targetList || !targetList->len)
1271         return JSStringCreateWithCharacters(0, 0);
1272
1273     StringBuilder builder;
1274     builder.append("AXHelp: ");
1275
1276     for (int targetCount = 0; targetCount < targetList->len; targetCount++) {
1277         if (AtkObject* target = static_cast<AtkObject*>(g_ptr_array_index(targetList, targetCount))) {
1278             GUniquePtr<gchar> text(atk_text_get_text(ATK_TEXT(target), 0, -1));
1279             if (targetCount)
1280                 builder.append(" ");
1281             builder.append(text.get());
1282         }
1283     }
1284
1285     g_object_unref(relationSet);
1286
1287     return JSStringCreateWithUTF8CString(builder.toString().utf8().data());
1288 }
1289
1290 double AccessibilityUIElement::x()
1291 {
1292     if (!ATK_IS_COMPONENT(m_element.get()))
1293         return 0;
1294
1295     int x;
1296 #if ATK_CHECK_VERSION(2,11,90)
1297     atk_component_get_extents(ATK_COMPONENT(m_element.get()), &x, nullptr, nullptr, nullptr, ATK_XY_SCREEN);
1298 #else
1299     atk_component_get_position(ATK_COMPONENT(m_element.get()), &x, nullptr, ATK_XY_SCREEN);
1300 #endif
1301     return x;
1302 }
1303
1304 double AccessibilityUIElement::y()
1305 {
1306     if (!ATK_IS_COMPONENT(m_element.get()))
1307         return 0;
1308
1309     int y;
1310 #if ATK_CHECK_VERSION(2,11,90)
1311     atk_component_get_extents(ATK_COMPONENT(m_element.get()), nullptr, &y, nullptr, nullptr, ATK_XY_SCREEN);
1312 #else
1313     atk_component_get_position(ATK_COMPONENT(m_element.get()), nullptr, &y, ATK_XY_SCREEN);
1314 #endif
1315     return y;
1316 }
1317
1318 double AccessibilityUIElement::width()
1319 {
1320     if (!ATK_IS_COMPONENT(m_element.get()))
1321         return 0;
1322
1323     int width;
1324 #if ATK_CHECK_VERSION(2,11,90)
1325     atk_component_get_extents(ATK_COMPONENT(m_element.get()), nullptr, nullptr, &width, nullptr, ATK_XY_WINDOW);
1326 #else
1327     atk_component_get_size(ATK_COMPONENT(m_element.get()), &width, nullptr);
1328 #endif
1329     return width;
1330 }
1331
1332 double AccessibilityUIElement::height()
1333 {
1334     if (!ATK_IS_COMPONENT(m_element.get()))
1335         return 0;
1336
1337     int height;
1338 #if ATK_CHECK_VERSION(2,11,90)
1339     atk_component_get_extents(ATK_COMPONENT(m_element.get()), nullptr, nullptr, nullptr, &height, ATK_XY_WINDOW);
1340 #else
1341     atk_component_get_size(ATK_COMPONENT(m_element.get()), nullptr, &height);
1342 #endif
1343     return height;
1344 }
1345
1346 double AccessibilityUIElement::clickPointX()
1347 {
1348     if (!ATK_IS_COMPONENT(m_element.get()))
1349         return 0;
1350
1351     int x, width;
1352 #if ATK_CHECK_VERSION(2,11,90)
1353     atk_component_get_extents(ATK_COMPONENT(m_element.get()), &x, nullptr, &width, nullptr, ATK_XY_WINDOW);
1354 #else
1355     atk_component_get_position(ATK_COMPONENT(m_element.get()), &x, nullptr, ATK_XY_WINDOW);
1356     atk_component_get_size(ATK_COMPONENT(m_element.get()), &width, nullptr);
1357 #endif
1358
1359     return x + width / 2.0;
1360 }
1361
1362 double AccessibilityUIElement::clickPointY()
1363 {
1364     if (!ATK_IS_COMPONENT(m_element.get()))
1365         return 0;
1366
1367     int y, height;
1368 #if ATK_CHECK_VERSION(2,11,90)
1369     atk_component_get_extents(ATK_COMPONENT(m_element.get()), nullptr, &y, nullptr, &height, ATK_XY_WINDOW);
1370 #else
1371     atk_component_get_position(ATK_COMPONENT(m_element.get()), nullptr, &y, ATK_XY_WINDOW);
1372     atk_component_get_size(ATK_COMPONENT(m_element.get()), nullptr, &height);
1373 #endif
1374
1375     return y + height / 2.0;
1376 }
1377
1378 double AccessibilityUIElement::intValue() const
1379 {
1380     if (!ATK_IS_OBJECT(m_element.get()))
1381         return 0;
1382
1383     if (ATK_IS_VALUE(m_element.get())) {
1384 #if ATK_CHECK_VERSION(2,11,92)
1385         double value;
1386         atk_value_get_value_and_text(ATK_VALUE(m_element.get()), &value, nullptr);
1387         return value;
1388 #else
1389         GValue value = G_VALUE_INIT;
1390         atk_value_get_current_value(ATK_VALUE(m_element.get()), &value);
1391         if (!G_VALUE_HOLDS_FLOAT(&value))
1392             return 0;
1393         return g_value_get_float(&value);
1394 #endif
1395     }
1396
1397     // Consider headings as an special case when returning the "int value" of
1398     // an AccessibilityUIElement, so we can reuse some tests to check the level
1399     // both for HTML headings and objects with the aria-level attribute.
1400     if (atk_object_get_role(ATK_OBJECT(m_element.get())) == ATK_ROLE_HEADING) {
1401         String headingLevel = getAttributeSetValueForId(ATK_OBJECT(m_element.get()), ObjectAttributeType, "level");
1402         bool ok;
1403         double headingLevelValue = headingLevel.toDouble(&ok);
1404         if (ok)
1405             return headingLevelValue;
1406     }
1407
1408     return 0;
1409 }
1410
1411 double AccessibilityUIElement::minValue()
1412 {
1413     if (!ATK_IS_VALUE(m_element.get()))
1414         return 0;
1415 #if ATK_CHECK_VERSION(2,11,92)
1416     return rangeMinMaxValue(ATK_VALUE(m_element.get()), RangeLimitMinimum);
1417 #else
1418     GValue value = G_VALUE_INIT;
1419     atk_value_get_minimum_value(ATK_VALUE(m_element.get()), &value);
1420     if (!G_VALUE_HOLDS_FLOAT(&value))
1421         return 0;
1422
1423     return g_value_get_float(&value);
1424 #endif
1425 }
1426
1427 double AccessibilityUIElement::maxValue()
1428 {
1429     if (!ATK_IS_VALUE(m_element.get()))
1430         return 0;
1431
1432 #if ATK_CHECK_VERSION(2,11,92)
1433     return rangeMinMaxValue(ATK_VALUE(m_element.get()), RangeLimitMaximum);
1434 #else
1435     GValue value = G_VALUE_INIT;
1436     atk_value_get_maximum_value(ATK_VALUE(m_element.get()), &value);
1437     if (!G_VALUE_HOLDS_FLOAT(&value))
1438         return 0;
1439
1440     return g_value_get_float(&value);
1441 #endif
1442 }
1443
1444 JSRetainPtr<JSStringRef> AccessibilityUIElement::valueDescription()
1445 {
1446     String valueText = getAttributeSetValueForId(ATK_OBJECT(m_element.get()), ObjectAttributeType, "valuetext");
1447     GUniquePtr<gchar> valueDescription(g_strdup_printf("AXValueDescription: %s", valueText.utf8().data()));
1448     return JSStringCreateWithUTF8CString(valueDescription.get());
1449 }
1450
1451 int AccessibilityUIElement::insertionPointLineNumber()
1452 {
1453     // FIXME: implement
1454     return -1;
1455 }
1456
1457 bool AccessibilityUIElement::isPressActionSupported()
1458 {
1459     if (!ATK_IS_ACTION(m_element.get()))
1460         return false;
1461
1462     const gchar* actionName = atk_action_get_name(ATK_ACTION(m_element.get()), 0);
1463     return equalLettersIgnoringASCIICase(String(actionName), "press") || equalLettersIgnoringASCIICase(String(actionName), "jump");
1464 }
1465
1466 bool AccessibilityUIElement::isIncrementActionSupported()
1467 {
1468     // FIXME: implement
1469     return false;
1470 }
1471
1472 bool AccessibilityUIElement::isDecrementActionSupported()
1473 {
1474     // FIXME: implement
1475     return false;
1476 }
1477
1478 bool AccessibilityUIElement::isEnabled()
1479 {
1480     return checkElementState(m_element.get(), ATK_STATE_ENABLED);
1481 }
1482
1483 bool AccessibilityUIElement::isRequired() const
1484 {
1485     return checkElementState(m_element.get(), ATK_STATE_REQUIRED);
1486 }
1487
1488 bool AccessibilityUIElement::isFocused() const
1489 {
1490     return checkElementState(m_element.get(), ATK_STATE_FOCUSED);
1491 }
1492
1493 bool AccessibilityUIElement::isSelected() const
1494 {
1495     return checkElementState(m_element.get(), ATK_STATE_SELECTED);
1496 }
1497
1498 bool AccessibilityUIElement::isSelectedOptionActive() const
1499 {
1500     return checkElementState(m_element.get(), ATK_STATE_ACTIVE);
1501 }
1502
1503 bool AccessibilityUIElement::isExpanded() const
1504 {
1505     return checkElementState(m_element.get(), ATK_STATE_EXPANDED);
1506 }
1507
1508 bool AccessibilityUIElement::isChecked() const
1509 {
1510     return checkElementState(m_element.get(), ATK_STATE_CHECKED);
1511 }
1512
1513 bool AccessibilityUIElement::isIndeterminate() const
1514 {
1515     return checkElementState(m_element.get(), ATK_STATE_INDETERMINATE);
1516 }
1517
1518 int AccessibilityUIElement::hierarchicalLevel() const
1519 {
1520     // FIXME: implement
1521     return 0;
1522 }
1523
1524 JSRetainPtr<JSStringRef> AccessibilityUIElement::speak()
1525 {
1526     // FIXME: implement
1527     return JSStringCreateWithCharacters(0, 0);
1528 }
1529
1530 bool AccessibilityUIElement::ariaIsGrabbed() const
1531 {
1532     // FIXME: implement
1533     return false;
1534 }
1535
1536 JSRetainPtr<JSStringRef> AccessibilityUIElement::ariaDropEffects() const
1537 {
1538     // FIXME: implement
1539     return JSStringCreateWithCharacters(0, 0);
1540 }
1541
1542 // parameterized attributes
1543 int AccessibilityUIElement::lineForIndex(int index)
1544 {
1545     if (!ATK_IS_TEXT(m_element.get()))
1546         return -1;
1547
1548     if (index < 0 || index > atk_text_get_character_count(ATK_TEXT(m_element.get())))
1549         return -1;
1550
1551     GUniquePtr<gchar> text(atk_text_get_text(ATK_TEXT(m_element.get()), 0, index));
1552     int lineNo = 0;
1553     for (gchar* offset = text.get(); *offset; ++offset) {
1554         if (*offset == '\n')
1555             ++lineNo;
1556     }
1557
1558     return lineNo;
1559 }
1560
1561 JSRetainPtr<JSStringRef> AccessibilityUIElement::rangeForLine(int line)
1562 {
1563     if (!ATK_IS_TEXT(m_element.get()))
1564         return JSStringCreateWithCharacters(0, 0);
1565
1566     AtkText* text = ATK_TEXT(m_element.get());
1567     gint startOffset = 0, endOffset = 0;
1568     for (int i = 0; i <= line; ++i)
1569         atk_text_get_string_at_offset(text, endOffset, ATK_TEXT_GRANULARITY_LINE, &startOffset, &endOffset);
1570
1571     GUniquePtr<gchar> range(g_strdup_printf("{%d, %d}", startOffset, endOffset - startOffset));
1572     return JSStringCreateWithUTF8CString(range.get());
1573 }
1574
1575 JSRetainPtr<JSStringRef> AccessibilityUIElement::rangeForPosition(int x, int y)
1576 {
1577     // FIXME: implement
1578     return JSStringCreateWithCharacters(0, 0);
1579 }
1580
1581 JSRetainPtr<JSStringRef> AccessibilityUIElement::boundsForRange(unsigned location, unsigned length)
1582 {
1583     if (!ATK_IS_TEXT(m_element.get()))
1584         return JSStringCreateWithCharacters(0, 0);
1585
1586     AtkTextRectangle rect;
1587     atk_text_get_range_extents(ATK_TEXT(m_element.get()), location, location + length, ATK_XY_WINDOW, &rect);
1588
1589     GUniquePtr<gchar> bounds(g_strdup_printf("{%d, %d, %d, %d}", rect.x, rect.y, rect.width, rect.height));
1590     return JSStringCreateWithUTF8CString(bounds.get());
1591 }
1592
1593 JSRetainPtr<JSStringRef> AccessibilityUIElement::stringForRange(unsigned location, unsigned length)
1594 {
1595     if (!ATK_IS_TEXT(m_element.get()))
1596         return JSStringCreateWithCharacters(0, 0);
1597
1598     String string = atk_text_get_text(ATK_TEXT(m_element.get()), location, location + length);
1599     return JSStringCreateWithUTF8CString(string.utf8().data());
1600 }
1601
1602 JSRetainPtr<JSStringRef> AccessibilityUIElement::attributedStringForRange(unsigned location, unsigned length)
1603 {
1604     if (!ATK_IS_TEXT(m_element.get()))
1605         return JSStringCreateWithCharacters(0, 0);
1606
1607     StringBuilder builder;
1608
1609     // The default text attributes apply to the entire element.
1610     builder.append("\n\tDefault text attributes:\n\t\t");
1611     builder.append(attributeSetToString(getAttributeSet(m_element.get(), TextAttributeType), "\n\t\t"));
1612
1613     // The attribute run provides attributes specific to the range of text at the specified offset.
1614     AtkAttributeSet* attributeSet;
1615     AtkText* text = ATK_TEXT(m_element.get());
1616     gint start = 0, end = 0;
1617     for (int i = location; i < location + length; i = end) {
1618         AtkAttributeSet* attributeSet = atk_text_get_run_attributes(text, i, &start, &end);
1619         GUniquePtr<gchar> substring(replaceCharactersForResults(atk_text_get_text(text, start, end)));
1620         builder.append(String::format("\n\tRange attributes for '%s':\n\t\t", substring.get()));
1621         builder.append(attributeSetToString(attributeSet, "\n\t\t"));
1622     }
1623
1624     atk_attribute_set_free(attributeSet);
1625
1626     return JSStringCreateWithUTF8CString(builder.toString().utf8().data());
1627 }
1628
1629 bool AccessibilityUIElement::attributedStringRangeIsMisspelled(unsigned location, unsigned length)
1630 {
1631     // FIXME: implement
1632     return false;
1633 }
1634
1635 unsigned AccessibilityUIElement::uiElementCountForSearchPredicate(JSContextRef context, AccessibilityUIElement* startElement, bool isDirectionNext, JSValueRef searchKey, JSStringRef searchText, bool visibleOnly, bool immediateDescendantsOnly)
1636 {
1637     // FIXME: implement
1638     return 0;
1639 }
1640
1641 RefPtr<AccessibilityUIElement> AccessibilityUIElement::uiElementForSearchPredicate(JSContextRef context, AccessibilityUIElement* startElement, bool isDirectionNext, JSValueRef searchKey, JSStringRef searchText, bool visibleOnly, bool immediateDescendantsOnly)
1642 {
1643     // FIXME: implement
1644     return nullptr;
1645 }
1646
1647 JSRetainPtr<JSStringRef> AccessibilityUIElement::selectTextWithCriteria(JSContextRef context, JSStringRef ambiguityResolution, JSValueRef searchStrings, JSStringRef replacementString, JSStringRef activity)
1648 {
1649     // FIXME: implement
1650     return nullptr;
1651 }
1652
1653 JSRetainPtr<JSStringRef> AccessibilityUIElement::attributesOfColumnHeaders()
1654 {
1655     if (!ATK_IS_TABLE(m_element.get()))
1656         return JSStringCreateWithCharacters(0, 0);
1657
1658     Vector<RefPtr<AccessibilityUIElement> > columnHeaders = getColumnHeaders(ATK_TABLE(m_element.get()));
1659     return createStringWithAttributes(columnHeaders);
1660 }
1661
1662 JSRetainPtr<JSStringRef> AccessibilityUIElement::attributesOfRowHeaders()
1663 {
1664     if (!ATK_IS_TABLE(m_element.get()))
1665         return JSStringCreateWithCharacters(0, 0);
1666
1667     Vector<RefPtr<AccessibilityUIElement> > rowHeaders = getRowHeaders(ATK_TABLE(m_element.get()));
1668     return createStringWithAttributes(rowHeaders);
1669 }
1670
1671 JSRetainPtr<JSStringRef> AccessibilityUIElement::attributesOfColumns()
1672 {
1673     // FIXME: implement
1674     return JSStringCreateWithCharacters(0, 0);
1675 }
1676
1677 JSRetainPtr<JSStringRef> AccessibilityUIElement::attributesOfRows()
1678 {
1679     // FIXME: implement
1680     return JSStringCreateWithCharacters(0, 0);
1681 }
1682
1683 JSRetainPtr<JSStringRef> AccessibilityUIElement::attributesOfVisibleCells()
1684 {
1685     if (!ATK_IS_TABLE(m_element.get()))
1686         return JSStringCreateWithCharacters(0, 0);
1687
1688     Vector<RefPtr<AccessibilityUIElement> > visibleCells = getVisibleCells(this);
1689     return createStringWithAttributes(visibleCells);
1690 }
1691
1692 JSRetainPtr<JSStringRef> AccessibilityUIElement::attributesOfHeader()
1693 {
1694     // FIXME: implement
1695     return JSStringCreateWithCharacters(0, 0);
1696 }
1697
1698 int AccessibilityUIElement::rowCount()
1699 {
1700     if (!ATK_IS_TABLE(m_element.get()))
1701         return 0;
1702
1703     return atk_table_get_n_rows(ATK_TABLE(m_element.get()));
1704 }
1705
1706 int AccessibilityUIElement::columnCount()
1707 {
1708     if (!ATK_IS_TABLE(m_element.get()))
1709         return 0;
1710
1711     return atk_table_get_n_columns(ATK_TABLE(m_element.get()));
1712 }
1713
1714 int AccessibilityUIElement::indexInTable()
1715 {
1716     // FIXME: implement
1717     return -1;
1718 }
1719
1720 JSRetainPtr<JSStringRef> AccessibilityUIElement::rowIndexRange()
1721 {
1722     // Range in table for rows.
1723     return indexRangeInTable(m_element.get(), true);
1724 }
1725
1726 JSRetainPtr<JSStringRef> AccessibilityUIElement::columnIndexRange()
1727 {
1728     // Range in table for columns.
1729     return indexRangeInTable(m_element.get(), false);
1730 }
1731
1732 RefPtr<AccessibilityUIElement> AccessibilityUIElement::cellForColumnAndRow(unsigned col, unsigned row)
1733 {
1734     if (!ATK_IS_TABLE(m_element.get()))
1735         return nullptr;
1736
1737     // Adopt the AtkObject representing the cell because
1738     // at_table_ref_at() transfers full ownership.
1739     GRefPtr<AtkObject> foundCell = adoptGRef(atk_table_ref_at(ATK_TABLE(m_element.get()), row, col));
1740     return foundCell ? AccessibilityUIElement::create(foundCell.get()).ptr() : nullptr;
1741 }
1742
1743 RefPtr<AccessibilityUIElement> AccessibilityUIElement::horizontalScrollbar() const
1744 {
1745     // FIXME: implement
1746     return nullptr;
1747 }
1748
1749 RefPtr<AccessibilityUIElement> AccessibilityUIElement::verticalScrollbar() const
1750 {
1751     // FIXME: implement
1752     return nullptr;
1753 }
1754
1755 JSRetainPtr<JSStringRef> AccessibilityUIElement::selectedTextRange()
1756 {
1757     if (!ATK_IS_TEXT(m_element.get()))
1758         return JSStringCreateWithCharacters(0, 0);
1759
1760     gint start, end;
1761     g_free(atk_text_get_selection(ATK_TEXT(m_element.get()), 0, &start, &end));
1762
1763     GUniquePtr<gchar> selection(g_strdup_printf("{%d, %d}", start, end - start));
1764     return JSStringCreateWithUTF8CString(selection.get());
1765 }
1766
1767 bool AccessibilityUIElement::setSelectedTextRange(unsigned location, unsigned length)
1768 {
1769     if (!ATK_IS_TEXT(m_element.get()))
1770         return false;
1771
1772     if (!length)
1773         return atk_text_set_caret_offset(ATK_TEXT(m_element.get()), location);
1774
1775     return atk_text_set_selection(ATK_TEXT(m_element.get()), 0, location, location + length);
1776 }
1777
1778 void AccessibilityUIElement::increment()
1779 {
1780     alterCurrentValue(m_element.get(), 1);
1781 }
1782
1783 void AccessibilityUIElement::decrement()
1784 {
1785     alterCurrentValue(m_element.get(), -1);
1786 }
1787
1788 void AccessibilityUIElement::showMenu()
1789 {
1790     // FIXME: implement
1791 }
1792
1793 void AccessibilityUIElement::press()
1794 {
1795     if (!ATK_IS_ACTION(m_element.get()))
1796         return;
1797
1798     // Only one action per object is supported so far.
1799     atk_action_do_action(ATK_ACTION(m_element.get()), 0);
1800 }
1801
1802 void AccessibilityUIElement::setSelectedChild(AccessibilityUIElement* element) const
1803 {
1804     // FIXME: implement
1805 }
1806
1807 void AccessibilityUIElement::setSelectedChildAtIndex(unsigned index) const
1808 {
1809     if (!ATK_IS_SELECTION(m_element.get()))
1810         return;
1811
1812     atk_selection_add_selection(ATK_SELECTION(m_element.get()), index);
1813 }
1814
1815 void AccessibilityUIElement::removeSelectionAtIndex(unsigned index) const
1816 {
1817     if (!ATK_IS_SELECTION(m_element.get()))
1818         return;
1819
1820     atk_selection_remove_selection(ATK_SELECTION(m_element.get()), index);
1821 }
1822
1823 void AccessibilityUIElement::clearSelectedChildren() const
1824 {
1825     if (!ATK_IS_SELECTION(m_element.get()))
1826         return;
1827
1828     atk_selection_clear_selection(ATK_SELECTION(m_element.get()));
1829 }
1830
1831 JSRetainPtr<JSStringRef> AccessibilityUIElement::accessibilityValue() const
1832 {
1833     // FIXME: implement
1834     return JSStringCreateWithCharacters(0, 0);
1835 }
1836
1837 JSRetainPtr<JSStringRef> AccessibilityUIElement::documentEncoding()
1838 {
1839     if (!ATK_IS_DOCUMENT(m_element.get()))
1840         return JSStringCreateWithCharacters(0, 0);
1841
1842     AtkRole role = atk_object_get_role(ATK_OBJECT(m_element.get()));
1843     if (role != ATK_ROLE_DOCUMENT_WEB)
1844         return JSStringCreateWithCharacters(0, 0);
1845
1846     return JSStringCreateWithUTF8CString(atk_document_get_attribute_value(ATK_DOCUMENT(m_element.get()), "Encoding"));
1847 }
1848
1849 JSRetainPtr<JSStringRef> AccessibilityUIElement::documentURI()
1850 {
1851     if (!ATK_IS_DOCUMENT(m_element.get()))
1852         return JSStringCreateWithCharacters(0, 0);
1853
1854     AtkRole role = atk_object_get_role(ATK_OBJECT(m_element.get()));
1855     if (role != ATK_ROLE_DOCUMENT_WEB)
1856         return JSStringCreateWithCharacters(0, 0);
1857
1858     return JSStringCreateWithUTF8CString(atk_document_get_attribute_value(ATK_DOCUMENT(m_element.get()), "URI"));
1859 }
1860
1861 JSRetainPtr<JSStringRef> AccessibilityUIElement::url()
1862 {
1863     if (!ATK_IS_HYPERLINK_IMPL(m_element.get()))
1864         return JSStringCreateWithCharacters(0, 0);
1865
1866     AtkHyperlink* hyperlink = atk_hyperlink_impl_get_hyperlink(ATK_HYPERLINK_IMPL(m_element.get()));
1867     GUniquePtr<char> hyperlinkURI(atk_hyperlink_get_uri(hyperlink, 0));
1868
1869     // Build the result string, stripping the absolute URL paths if present.
1870     char* localURI = g_strstr_len(hyperlinkURI.get(), -1, "LayoutTests");
1871     String axURL = String::format("AXURL: %s", localURI ? localURI : hyperlinkURI.get());
1872     return JSStringCreateWithUTF8CString(axURL.utf8().data());
1873 }
1874
1875 bool AccessibilityUIElement::addNotificationListener(JSValueRef functionCallback)
1876 {
1877     if (!functionCallback)
1878         return false;
1879
1880     // Only one notification listener per element.
1881     if (m_notificationHandler)
1882         return false;
1883
1884     m_notificationHandler = AccessibilityNotificationHandler::create();
1885     m_notificationHandler->setPlatformElement(platformUIElement());
1886     m_notificationHandler->setNotificationFunctionCallback(functionCallback);
1887
1888     return true;
1889 }
1890
1891 bool AccessibilityUIElement::removeNotificationListener()
1892 {
1893     // Programmers should not be trying to remove a listener that's already removed.
1894     ASSERT(m_notificationHandler);
1895     m_notificationHandler = nullptr;
1896
1897     return true;
1898 }
1899
1900 bool AccessibilityUIElement::isFocusable() const
1901 {
1902     return checkElementState(m_element.get(), ATK_STATE_FOCUSABLE);
1903 }
1904
1905 bool AccessibilityUIElement::isSelectable() const
1906 {
1907     return checkElementState(m_element.get(), ATK_STATE_SELECTABLE);
1908 }
1909
1910 bool AccessibilityUIElement::isMultiSelectable() const
1911 {
1912     return checkElementState(m_element.get(), ATK_STATE_MULTISELECTABLE);
1913 }
1914
1915 bool AccessibilityUIElement::isVisible() const
1916 {
1917     return checkElementState(m_element.get(), ATK_STATE_VISIBLE);
1918 }
1919
1920 bool AccessibilityUIElement::isOffScreen() const
1921 {
1922     // FIXME: implement
1923     return false;
1924 }
1925
1926 bool AccessibilityUIElement::isCollapsed() const
1927 {
1928     // FIXME: implement
1929     return false;
1930 }
1931
1932 bool AccessibilityUIElement::isIgnored() const
1933 {
1934     // FIXME: implement
1935     return false;
1936 }
1937
1938 bool AccessibilityUIElement::isSingleLine() const
1939 {
1940     return checkElementState(m_element.get(), ATK_STATE_SINGLE_LINE);
1941 }
1942
1943 bool AccessibilityUIElement::isMultiLine() const
1944 {
1945     return checkElementState(m_element.get(), ATK_STATE_MULTI_LINE);
1946 }
1947
1948 bool AccessibilityUIElement::hasPopup() const
1949 {
1950     if (!ATK_IS_OBJECT(m_element.get()))
1951         return false;
1952
1953     String hasPopupValue = getAttributeSetValueForId(ATK_OBJECT(m_element.get()), ObjectAttributeType, "haspopup");
1954     return equalLettersIgnoringASCIICase(hasPopupValue, "true");
1955 }
1956
1957 void AccessibilityUIElement::takeFocus()
1958 {
1959     // FIXME: implement
1960 }
1961
1962 void AccessibilityUIElement::takeSelection()
1963 {
1964     // FIXME: implement
1965 }
1966
1967 void AccessibilityUIElement::addSelection()
1968 {
1969     // FIXME: implement
1970 }
1971
1972 void AccessibilityUIElement::removeSelection()
1973 {
1974     // FIXME: implement
1975 }
1976
1977 // Text markers
1978 RefPtr<AccessibilityTextMarkerRange> AccessibilityUIElement::lineTextMarkerRangeForTextMarker(AccessibilityTextMarker* textMarker)
1979 {
1980     // FIXME: implement
1981     return nullptr;
1982 }
1983
1984 RefPtr<AccessibilityTextMarkerRange> AccessibilityUIElement::textMarkerRangeForElement(AccessibilityUIElement* element)
1985 {
1986     // FIXME: implement
1987     return nullptr;
1988 }
1989
1990 int AccessibilityUIElement::textMarkerRangeLength(AccessibilityTextMarkerRange* range)
1991 {
1992     // FIXME: implement
1993     return 0;
1994 }
1995
1996 RefPtr<AccessibilityTextMarker> AccessibilityUIElement::previousTextMarker(AccessibilityTextMarker* textMarker)
1997 {
1998     // FIXME: implement
1999     return nullptr;
2000 }
2001
2002 RefPtr<AccessibilityTextMarker> AccessibilityUIElement::nextTextMarker(AccessibilityTextMarker* textMarker)
2003 {
2004     // FIXME: implement
2005     return nullptr;
2006 }
2007
2008 JSRetainPtr<JSStringRef> AccessibilityUIElement::stringForTextMarkerRange(AccessibilityTextMarkerRange* markerRange)
2009 {
2010     // FIXME: implement
2011     return JSStringCreateWithCharacters(0, 0);
2012 }
2013
2014 RefPtr<AccessibilityTextMarkerRange> AccessibilityUIElement::textMarkerRangeForMarkers(AccessibilityTextMarker* startMarker, AccessibilityTextMarker* endMarker)
2015 {
2016     // FIXME: implement
2017     return nullptr;
2018 }
2019
2020 RefPtr<AccessibilityTextMarker> AccessibilityUIElement::startTextMarkerForTextMarkerRange(AccessibilityTextMarkerRange* range)
2021 {
2022     // FIXME: implement
2023     return nullptr;
2024 }
2025
2026 RefPtr<AccessibilityTextMarker> AccessibilityUIElement::endTextMarkerForTextMarkerRange(AccessibilityTextMarkerRange* range)
2027 {
2028     // FIXME: implement
2029     return nullptr;
2030 }
2031
2032 RefPtr<AccessibilityTextMarker> AccessibilityUIElement::endTextMarkerForBounds(int x, int y, int width, int height)
2033 {
2034     // FIXME: implement
2035     return nullptr;
2036 }
2037
2038 RefPtr<AccessibilityTextMarker> AccessibilityUIElement::startTextMarkerForBounds(int x, int y, int width, int height)
2039 {
2040     // FIXME: implement
2041     return nullptr;
2042 }
2043
2044 RefPtr<AccessibilityTextMarker> AccessibilityUIElement::textMarkerForPoint(int x, int y)
2045 {
2046     // FIXME: implement
2047     return nullptr;
2048 }
2049
2050 RefPtr<AccessibilityUIElement> AccessibilityUIElement::accessibilityElementForTextMarker(AccessibilityTextMarker* marker)
2051 {
2052     // FIXME: implement
2053     return nullptr;
2054 }
2055
2056 bool AccessibilityUIElement::attributedStringForTextMarkerRangeContainsAttribute(JSStringRef attribute, AccessibilityTextMarkerRange* range)
2057 {
2058     // FIXME: implement
2059     return false;
2060 }
2061
2062 int AccessibilityUIElement::indexForTextMarker(AccessibilityTextMarker* marker)
2063 {
2064     // FIXME: implement
2065     return -1;
2066 }
2067
2068 bool AccessibilityUIElement::isTextMarkerValid(AccessibilityTextMarker* textMarker)
2069 {
2070     // FIXME: implement
2071     return false;
2072 }
2073
2074 RefPtr<AccessibilityTextMarker> AccessibilityUIElement::textMarkerForIndex(int textIndex)
2075 {
2076     // FIXME: implement
2077     return nullptr;
2078 }
2079     
2080 RefPtr<AccessibilityTextMarker> AccessibilityUIElement::startTextMarker()
2081 {
2082     // FIXME: implement
2083     return nullptr;    
2084 }
2085
2086 RefPtr<AccessibilityTextMarker> AccessibilityUIElement::endTextMarker()
2087 {
2088     // FIXME: implement
2089     return nullptr;
2090 }
2091
2092 bool AccessibilityUIElement::setSelectedVisibleTextRange(AccessibilityTextMarkerRange*)
2093 {
2094     return false;
2095 }
2096
2097 void AccessibilityUIElement::scrollToMakeVisible()
2098 {
2099     // FIXME: implement
2100 }
2101     
2102 void AccessibilityUIElement::scrollToGlobalPoint(int x, int y)
2103 {
2104     // FIXME: implement
2105 }
2106     
2107 void AccessibilityUIElement::scrollToMakeVisibleWithSubFocus(int x, int y, int width, int height)
2108 {
2109     // FIXME: implement
2110 }
2111
2112 JSRetainPtr<JSStringRef> AccessibilityUIElement::supportedActions() const
2113 {
2114     // FIXME: implement
2115     return nullptr;
2116 }
2117
2118 JSRetainPtr<JSStringRef> AccessibilityUIElement::pathDescription() const
2119 {
2120     notImplemented();
2121     return nullptr;
2122 }
2123
2124 JSRetainPtr<JSStringRef> AccessibilityUIElement::mathPostscriptsDescription() const
2125 {
2126     notImplemented();
2127     return nullptr;
2128 }
2129
2130 JSRetainPtr<JSStringRef> AccessibilityUIElement::mathPrescriptsDescription() const
2131 {
2132     notImplemented();
2133     return nullptr;
2134 }
2135
2136 JSRetainPtr<JSStringRef> AccessibilityUIElement::classList() const
2137 {
2138     notImplemented();
2139     return nullptr;
2140 }
2141
2142 JSRetainPtr<JSStringRef> stringAtOffset(PlatformUIElement element, AtkTextBoundary boundary, int offset)
2143 {
2144     if (!ATK_IS_TEXT(element.get()))
2145         return JSStringCreateWithCharacters(0, 0);
2146
2147     gint startOffset, endOffset;
2148     StringBuilder builder;
2149
2150 #if ATK_CHECK_VERSION(2, 10, 0)
2151     AtkTextGranularity granularity;
2152     switch (boundary) {
2153     case ATK_TEXT_BOUNDARY_CHAR:
2154         granularity = ATK_TEXT_GRANULARITY_CHAR;
2155         break;
2156     case ATK_TEXT_BOUNDARY_WORD_START:
2157         granularity = ATK_TEXT_GRANULARITY_WORD;
2158         break;
2159     case ATK_TEXT_BOUNDARY_LINE_START:
2160         granularity = ATK_TEXT_GRANULARITY_LINE;
2161         break;
2162     case ATK_TEXT_BOUNDARY_SENTENCE_START:
2163         granularity = ATK_TEXT_GRANULARITY_SENTENCE;
2164         break;
2165     default:
2166         return JSStringCreateWithCharacters(0, 0);
2167     }
2168
2169     builder.append(atk_text_get_string_at_offset(ATK_TEXT(element.get()), offset, granularity, &startOffset, &endOffset));
2170 #else
2171     builder.append(atk_text_get_text_at_offset(ATK_TEXT(element.get()), offset, boundary, &startOffset, &endOffset));
2172 #endif
2173     builder.append(String::format(", %i, %i", startOffset, endOffset));
2174     return JSStringCreateWithUTF8CString(builder.toString().utf8().data());
2175 }
2176
2177 JSRetainPtr<JSStringRef> AccessibilityUIElement::characterAtOffset(int offset)
2178 {
2179     return stringAtOffset(m_element, ATK_TEXT_BOUNDARY_CHAR, offset);
2180 }
2181
2182 JSRetainPtr<JSStringRef> AccessibilityUIElement::wordAtOffset(int offset)
2183 {
2184     return stringAtOffset(m_element, ATK_TEXT_BOUNDARY_WORD_START, offset);
2185 }
2186
2187 JSRetainPtr<JSStringRef> AccessibilityUIElement::lineAtOffset(int offset)
2188 {
2189     return stringAtOffset(m_element, ATK_TEXT_BOUNDARY_LINE_START, offset);
2190 }
2191
2192 JSRetainPtr<JSStringRef> AccessibilityUIElement::sentenceAtOffset(int offset)
2193 {
2194     return stringAtOffset(m_element, ATK_TEXT_BOUNDARY_SENTENCE_START, offset);
2195 }
2196
2197 } // namespace WTR
2198
2199 #endif // HAVE(ACCESSIBILITY)