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