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