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