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