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