AX: [GTK] Implement computedRoleString in AccessibilityUIElement
[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     String role = getAttributeSetValueForId(ATK_OBJECT(m_element.get()), ObjectAttributeType, "computed-role");
1074     if (!role.isEmpty())
1075         return JSStringCreateWithUTF8CString(role.utf8().data());
1076
1077     return JSStringCreateWithCharacters(0, 0);
1078 }
1079
1080 JSRetainPtr<JSStringRef> AccessibilityUIElement::title()
1081 {
1082     if (!ATK_IS_OBJECT(m_element.get()))
1083         return JSStringCreateWithCharacters(0, 0);
1084
1085     const gchar* name = atk_object_get_name(ATK_OBJECT(m_element.get()));
1086     GUniquePtr<gchar> axTitle(g_strdup_printf("AXTitle: %s", name ? name : ""));
1087
1088     return JSStringCreateWithUTF8CString(axTitle.get());
1089 }
1090
1091 JSRetainPtr<JSStringRef> AccessibilityUIElement::description()
1092 {
1093     if (!ATK_IS_OBJECT(m_element.get()))
1094         return JSStringCreateWithCharacters(0, 0);
1095
1096     const gchar* description = atk_object_get_description(ATK_OBJECT(m_element.get()));
1097     if (!description)
1098         return JSStringCreateWithCharacters(0, 0);
1099
1100     GUniquePtr<gchar> axDesc(g_strdup_printf("AXDescription: %s", description));
1101
1102     return JSStringCreateWithUTF8CString(axDesc.get());
1103 }
1104
1105 JSRetainPtr<JSStringRef> AccessibilityUIElement::orientation() const
1106 {
1107     if (!ATK_IS_OBJECT(m_element.get()))
1108         return JSStringCreateWithCharacters(0, 0);
1109
1110     const gchar* axOrientation = nullptr;
1111     if (checkElementState(m_element.get(), ATK_STATE_HORIZONTAL))
1112         axOrientation = "AXOrientation: AXHorizontalOrientation";
1113     else if (checkElementState(m_element.get(), ATK_STATE_VERTICAL))
1114         axOrientation = "AXOrientation: AXVerticalOrientation";
1115
1116     if (!axOrientation)
1117         return JSStringCreateWithCharacters(0, 0);
1118
1119     return JSStringCreateWithUTF8CString(axOrientation);
1120 }
1121
1122 JSRetainPtr<JSStringRef> AccessibilityUIElement::stringValue()
1123 {
1124     if (!ATK_IS_TEXT(m_element.get()))
1125         return JSStringCreateWithCharacters(0, 0);
1126
1127     GUniquePtr<gchar> text(atk_text_get_text(ATK_TEXT(m_element.get()), 0, -1));
1128     GUniquePtr<gchar> textWithReplacedCharacters(replaceCharactersForResults(text.get()));
1129     GUniquePtr<gchar> axValue(g_strdup_printf("AXValue: %s", textWithReplacedCharacters.get()));
1130
1131     return JSStringCreateWithUTF8CString(axValue.get());
1132 }
1133
1134 JSRetainPtr<JSStringRef> AccessibilityUIElement::language()
1135 {
1136     if (!ATK_IS_OBJECT(m_element.get()))
1137         return JSStringCreateWithCharacters(0, 0);
1138
1139     const gchar* locale = atk_object_get_object_locale(ATK_OBJECT(m_element.get()));
1140     if (!locale)
1141         return JSStringCreateWithCharacters(0, 0);
1142
1143     GUniquePtr<char> axValue(g_strdup_printf("AXLanguage: %s", locale));
1144     return JSStringCreateWithUTF8CString(axValue.get());
1145 }
1146
1147 JSRetainPtr<JSStringRef> AccessibilityUIElement::helpText() const
1148 {
1149     if (!ATK_IS_OBJECT(m_element.get()))
1150         return JSStringCreateWithCharacters(0, 0);
1151
1152     AtkRelationSet* relationSet = atk_object_ref_relation_set(ATK_OBJECT(m_element.get()));
1153     if (!relationSet)
1154         return nullptr;
1155
1156     AtkRelation* relation = atk_relation_set_get_relation_by_type(relationSet, ATK_RELATION_DESCRIBED_BY);
1157     if (!relation)
1158         return nullptr;
1159
1160     GPtrArray* targetList = atk_relation_get_target(relation);
1161     if (!targetList || !targetList->len)
1162         return nullptr;
1163
1164     StringBuilder builder;
1165     builder.append("AXHelp: ");
1166
1167     for (int targetCount = 0; targetCount < targetList->len; targetCount++) {
1168         if (AtkObject* target = static_cast<AtkObject*>(g_ptr_array_index(targetList, targetCount))) {
1169             GUniquePtr<gchar> text(atk_text_get_text(ATK_TEXT(target), 0, -1));
1170             if (targetCount)
1171                 builder.append(" ");
1172             builder.append(text.get());
1173         }
1174     }
1175
1176     g_object_unref(relationSet);
1177
1178     return JSStringCreateWithUTF8CString(builder.toString().utf8().data());
1179 }
1180
1181 double AccessibilityUIElement::x()
1182 {
1183     if (!ATK_IS_COMPONENT(m_element.get()))
1184         return 0;
1185
1186     int x;
1187 #if ATK_CHECK_VERSION(2,11,90)
1188     atk_component_get_extents(ATK_COMPONENT(m_element.get()), &x, nullptr, nullptr, nullptr, ATK_XY_SCREEN);
1189 #else
1190     atk_component_get_position(ATK_COMPONENT(m_element.get()), &x, nullptr, ATK_XY_SCREEN);
1191 #endif
1192     return x;
1193 }
1194
1195 double AccessibilityUIElement::y()
1196 {
1197     if (!ATK_IS_COMPONENT(m_element.get()))
1198         return 0;
1199
1200     int y;
1201 #if ATK_CHECK_VERSION(2,11,90)
1202     atk_component_get_extents(ATK_COMPONENT(m_element.get()), nullptr, &y, nullptr, nullptr, ATK_XY_SCREEN);
1203 #else
1204     atk_component_get_position(ATK_COMPONENT(m_element.get()), nullptr, &y, ATK_XY_SCREEN);
1205 #endif
1206     return y;
1207 }
1208
1209 double AccessibilityUIElement::width()
1210 {
1211     if (!ATK_IS_COMPONENT(m_element.get()))
1212         return 0;
1213
1214     int width;
1215 #if ATK_CHECK_VERSION(2,11,90)
1216     atk_component_get_extents(ATK_COMPONENT(m_element.get()), nullptr, nullptr, &width, nullptr, ATK_XY_WINDOW);
1217 #else
1218     atk_component_get_size(ATK_COMPONENT(m_element.get()), &width, nullptr);
1219 #endif
1220     return width;
1221 }
1222
1223 double AccessibilityUIElement::height()
1224 {
1225     if (!ATK_IS_COMPONENT(m_element.get()))
1226         return 0;
1227
1228     int height;
1229 #if ATK_CHECK_VERSION(2,11,90)
1230     atk_component_get_extents(ATK_COMPONENT(m_element.get()), nullptr, nullptr, nullptr, &height, ATK_XY_WINDOW);
1231 #else
1232     atk_component_get_size(ATK_COMPONENT(m_element.get()), nullptr, &height);
1233 #endif
1234     return height;
1235 }
1236
1237 double AccessibilityUIElement::clickPointX()
1238 {
1239     if (!ATK_IS_COMPONENT(m_element.get()))
1240         return 0;
1241
1242     int x, width;
1243 #if ATK_CHECK_VERSION(2,11,90)
1244     atk_component_get_extents(ATK_COMPONENT(m_element.get()), &x, nullptr, &width, nullptr, ATK_XY_WINDOW);
1245 #else
1246     atk_component_get_position(ATK_COMPONENT(m_element.get()), &x, nullptr, ATK_XY_WINDOW);
1247     atk_component_get_size(ATK_COMPONENT(m_element.get()), &width, nullptr);
1248 #endif
1249
1250     return x + width / 2.0;
1251 }
1252
1253 double AccessibilityUIElement::clickPointY()
1254 {
1255     if (!ATK_IS_COMPONENT(m_element.get()))
1256         return 0;
1257
1258     int y, height;
1259 #if ATK_CHECK_VERSION(2,11,90)
1260     atk_component_get_extents(ATK_COMPONENT(m_element.get()), nullptr, &y, nullptr, &height, ATK_XY_WINDOW);
1261 #else
1262     atk_component_get_position(ATK_COMPONENT(m_element.get()), nullptr, &y, ATK_XY_WINDOW);
1263     atk_component_get_size(ATK_COMPONENT(m_element.get()), nullptr, &height);
1264 #endif
1265
1266     return y + height / 2.0;
1267 }
1268
1269 double AccessibilityUIElement::intValue() const
1270 {
1271     if (!ATK_IS_OBJECT(m_element.get()))
1272         return 0;
1273
1274     if (ATK_IS_VALUE(m_element.get())) {
1275 #if ATK_CHECK_VERSION(2,11,92)
1276         double value;
1277         atk_value_get_value_and_text(ATK_VALUE(m_element.get()), &value, nullptr);
1278         return value;
1279 #else
1280         GValue value = G_VALUE_INIT;
1281         atk_value_get_current_value(ATK_VALUE(m_element.get()), &value);
1282         if (!G_VALUE_HOLDS_FLOAT(&value))
1283             return 0;
1284         return g_value_get_float(&value);
1285 #endif
1286     }
1287
1288     // Consider headings as an special case when returning the "int value" of
1289     // an AccessibilityUIElement, so we can reuse some tests to check the level
1290     // both for HTML headings and objects with the aria-level attribute.
1291     if (atk_object_get_role(ATK_OBJECT(m_element.get())) == ATK_ROLE_HEADING) {
1292         String headingLevel = getAttributeSetValueForId(ATK_OBJECT(m_element.get()), ObjectAttributeType, "level");
1293         bool ok;
1294         double headingLevelValue = headingLevel.toDouble(&ok);
1295         if (ok)
1296             return headingLevelValue;
1297     }
1298
1299     return 0;
1300 }
1301
1302 double AccessibilityUIElement::minValue()
1303 {
1304     if (!ATK_IS_VALUE(m_element.get()))
1305         return 0;
1306 #if ATK_CHECK_VERSION(2,11,92)
1307     return rangeMinMaxValue(ATK_VALUE(m_element.get()), RangeLimitMinimum);
1308 #else
1309     GValue value = G_VALUE_INIT;
1310     atk_value_get_minimum_value(ATK_VALUE(m_element.get()), &value);
1311     if (!G_VALUE_HOLDS_FLOAT(&value))
1312         return 0;
1313
1314     return g_value_get_float(&value);
1315 #endif
1316 }
1317
1318 double AccessibilityUIElement::maxValue()
1319 {
1320     if (!ATK_IS_VALUE(m_element.get()))
1321         return 0;
1322
1323 #if ATK_CHECK_VERSION(2,11,92)
1324     return rangeMinMaxValue(ATK_VALUE(m_element.get()), RangeLimitMaximum);
1325 #else
1326     GValue value = G_VALUE_INIT;
1327     atk_value_get_maximum_value(ATK_VALUE(m_element.get()), &value);
1328     if (!G_VALUE_HOLDS_FLOAT(&value))
1329         return 0;
1330
1331     return g_value_get_float(&value);
1332 #endif
1333 }
1334
1335 JSRetainPtr<JSStringRef> AccessibilityUIElement::valueDescription()
1336 {
1337     // FIXME: implement
1338     return JSStringCreateWithCharacters(0, 0);
1339 }
1340
1341 int AccessibilityUIElement::insertionPointLineNumber()
1342 {
1343     // FIXME: implement
1344     return -1;
1345 }
1346
1347 bool AccessibilityUIElement::isPressActionSupported()
1348 {
1349     if (!ATK_IS_ACTION(m_element.get()))
1350         return false;
1351
1352     const gchar* actionName = atk_action_get_name(ATK_ACTION(m_element.get()), 0);
1353     return equalIgnoringCase(actionName, String("press")) || equalIgnoringCase(actionName, String("jump"));
1354 }
1355
1356 bool AccessibilityUIElement::isIncrementActionSupported()
1357 {
1358     // FIXME: implement
1359     return false;
1360 }
1361
1362 bool AccessibilityUIElement::isDecrementActionSupported()
1363 {
1364     // FIXME: implement
1365     return false;
1366 }
1367
1368 bool AccessibilityUIElement::isEnabled()
1369 {
1370     return checkElementState(m_element.get(), ATK_STATE_ENABLED);
1371 }
1372
1373 bool AccessibilityUIElement::isRequired() const
1374 {
1375     return checkElementState(m_element.get(), ATK_STATE_REQUIRED);
1376 }
1377
1378 bool AccessibilityUIElement::isFocused() const
1379 {
1380     return checkElementState(m_element.get(), ATK_STATE_FOCUSED);
1381 }
1382
1383 bool AccessibilityUIElement::isSelected() const
1384 {
1385     return checkElementState(m_element.get(), ATK_STATE_SELECTED);
1386 }
1387
1388 bool AccessibilityUIElement::isSelectedOptionActive() const
1389 {
1390     return checkElementState(m_element.get(), ATK_STATE_ACTIVE);
1391 }
1392
1393 bool AccessibilityUIElement::isExpanded() const
1394 {
1395     return checkElementState(m_element.get(), ATK_STATE_EXPANDED);
1396 }
1397
1398 bool AccessibilityUIElement::isChecked() const
1399 {
1400     return checkElementState(m_element.get(), ATK_STATE_CHECKED);
1401 }
1402
1403 bool AccessibilityUIElement::isIndeterminate() const
1404 {
1405     return checkElementState(m_element.get(), ATK_STATE_INDETERMINATE);
1406 }
1407
1408 int AccessibilityUIElement::hierarchicalLevel() const
1409 {
1410     // FIXME: implement
1411     return 0;
1412 }
1413
1414 JSRetainPtr<JSStringRef> AccessibilityUIElement::speak()
1415 {
1416     // FIXME: implement
1417     return JSStringCreateWithCharacters(0, 0);
1418 }
1419
1420 bool AccessibilityUIElement::ariaIsGrabbed() const
1421 {
1422     // FIXME: implement
1423     return false;
1424 }
1425
1426 JSRetainPtr<JSStringRef> AccessibilityUIElement::ariaDropEffects() const
1427 {
1428     // FIXME: implement
1429     return JSStringCreateWithCharacters(0, 0);
1430 }
1431
1432 // parameterized attributes
1433 int AccessibilityUIElement::lineForIndex(int index)
1434 {
1435     if (!ATK_IS_TEXT(m_element.get()))
1436         return -1;
1437
1438     if (index < 0 || index > atk_text_get_character_count(ATK_TEXT(m_element.get())))
1439         return -1;
1440
1441     GUniquePtr<gchar> text(atk_text_get_text(ATK_TEXT(m_element.get()), 0, index));
1442     int lineNo = 0;
1443     for (gchar* offset = text.get(); *offset; ++offset) {
1444         if (*offset == '\n')
1445             ++lineNo;
1446     }
1447
1448     return lineNo;
1449 }
1450
1451 JSRetainPtr<JSStringRef> AccessibilityUIElement::rangeForLine(int line)
1452 {
1453     // FIXME: implement
1454     return JSStringCreateWithCharacters(0, 0);
1455 }
1456
1457 JSRetainPtr<JSStringRef> AccessibilityUIElement::rangeForPosition(int x, int y)
1458 {
1459     // FIXME: implement
1460     return JSStringCreateWithCharacters(0, 0);
1461 }
1462
1463 JSRetainPtr<JSStringRef> AccessibilityUIElement::boundsForRange(unsigned location, unsigned length)
1464 {
1465     // FIXME: implement
1466     return JSStringCreateWithCharacters(0, 0);
1467 }
1468
1469 JSRetainPtr<JSStringRef> AccessibilityUIElement::stringForRange(unsigned location, unsigned length)
1470 {
1471     if (!ATK_IS_TEXT(m_element.get()))
1472         return JSStringCreateWithCharacters(0, 0);
1473
1474     String string = atk_text_get_text(ATK_TEXT(m_element.get()), location, location + length);
1475     return JSStringCreateWithUTF8CString(string.utf8().data());
1476 }
1477
1478 JSRetainPtr<JSStringRef> AccessibilityUIElement::attributedStringForRange(unsigned location, unsigned length)
1479 {
1480     // FIXME: implement
1481     return JSStringCreateWithCharacters(0, 0);
1482 }
1483
1484 bool AccessibilityUIElement::attributedStringRangeIsMisspelled(unsigned location, unsigned length)
1485 {
1486     // FIXME: implement
1487     return false;
1488 }
1489
1490 unsigned AccessibilityUIElement::uiElementCountForSearchPredicate(JSContextRef context, AccessibilityUIElement* startElement, bool isDirectionNext, JSValueRef searchKey, JSStringRef searchText, bool visibleOnly, bool immediateDescendantsOnly)
1491 {
1492     // FIXME: implement
1493     return 0;
1494 }
1495
1496 PassRefPtr<AccessibilityUIElement> AccessibilityUIElement::uiElementForSearchPredicate(JSContextRef context, AccessibilityUIElement* startElement, bool isDirectionNext, JSValueRef searchKey, JSStringRef searchText, bool visibleOnly, bool immediateDescendantsOnly)
1497 {
1498     // FIXME: implement
1499     return nullptr;
1500 }
1501
1502 JSRetainPtr<JSStringRef> AccessibilityUIElement::selectTextWithCriteria(JSContextRef context, JSStringRef ambiguityResolution, JSValueRef searchStrings, JSStringRef replacementString, JSStringRef activity)
1503 {
1504     // FIXME: implement
1505     return nullptr;
1506 }
1507
1508 JSRetainPtr<JSStringRef> AccessibilityUIElement::attributesOfColumnHeaders()
1509 {
1510     if (!ATK_IS_TABLE(m_element.get()))
1511         return JSStringCreateWithCharacters(0, 0);
1512
1513     Vector<RefPtr<AccessibilityUIElement> > columnHeaders = getColumnHeaders(ATK_TABLE(m_element.get()));
1514     return createStringWithAttributes(columnHeaders);
1515 }
1516
1517 JSRetainPtr<JSStringRef> AccessibilityUIElement::attributesOfRowHeaders()
1518 {
1519     if (!ATK_IS_TABLE(m_element.get()))
1520         return JSStringCreateWithCharacters(0, 0);
1521
1522     Vector<RefPtr<AccessibilityUIElement> > rowHeaders = getRowHeaders(ATK_TABLE(m_element.get()));
1523     return createStringWithAttributes(rowHeaders);
1524 }
1525
1526 JSRetainPtr<JSStringRef> AccessibilityUIElement::attributesOfColumns()
1527 {
1528     // FIXME: implement
1529     return JSStringCreateWithCharacters(0, 0);
1530 }
1531
1532 JSRetainPtr<JSStringRef> AccessibilityUIElement::attributesOfRows()
1533 {
1534     // FIXME: implement
1535     return JSStringCreateWithCharacters(0, 0);
1536 }
1537
1538 JSRetainPtr<JSStringRef> AccessibilityUIElement::attributesOfVisibleCells()
1539 {
1540     if (!ATK_IS_TABLE(m_element.get()))
1541         return JSStringCreateWithCharacters(0, 0);
1542
1543     Vector<RefPtr<AccessibilityUIElement> > visibleCells = getVisibleCells(this);
1544     return createStringWithAttributes(visibleCells);
1545 }
1546
1547 JSRetainPtr<JSStringRef> AccessibilityUIElement::attributesOfHeader()
1548 {
1549     // FIXME: implement
1550     return JSStringCreateWithCharacters(0, 0);
1551 }
1552
1553 int AccessibilityUIElement::rowCount()
1554 {
1555     if (!ATK_IS_TABLE(m_element.get()))
1556         return 0;
1557
1558     return atk_table_get_n_rows(ATK_TABLE(m_element.get()));
1559 }
1560
1561 int AccessibilityUIElement::columnCount()
1562 {
1563     if (!ATK_IS_TABLE(m_element.get()))
1564         return 0;
1565
1566     return atk_table_get_n_columns(ATK_TABLE(m_element.get()));
1567 }
1568
1569 int AccessibilityUIElement::indexInTable()
1570 {
1571     // FIXME: implement
1572     return -1;
1573 }
1574
1575 JSRetainPtr<JSStringRef> AccessibilityUIElement::rowIndexRange()
1576 {
1577     // Range in table for rows.
1578     return indexRangeInTable(m_element.get(), true);
1579 }
1580
1581 JSRetainPtr<JSStringRef> AccessibilityUIElement::columnIndexRange()
1582 {
1583     // Range in table for columns.
1584     return indexRangeInTable(m_element.get(), false);
1585 }
1586
1587 PassRefPtr<AccessibilityUIElement> AccessibilityUIElement::cellForColumnAndRow(unsigned col, unsigned row)
1588 {
1589     if (!ATK_IS_TABLE(m_element.get()))
1590         return nullptr;
1591
1592     // Adopt the AtkObject representing the cell because
1593     // at_table_ref_at() transfers full ownership.
1594     GRefPtr<AtkObject> foundCell = adoptGRef(atk_table_ref_at(ATK_TABLE(m_element.get()), row, col));
1595     return foundCell ? AccessibilityUIElement::create(foundCell.get()) : nullptr;
1596 }
1597
1598 PassRefPtr<AccessibilityUIElement> AccessibilityUIElement::horizontalScrollbar() const
1599 {
1600     // FIXME: implement
1601     return nullptr;
1602 }
1603
1604 PassRefPtr<AccessibilityUIElement> AccessibilityUIElement::verticalScrollbar() const
1605 {
1606     // FIXME: implement
1607     return nullptr;
1608 }
1609
1610 JSRetainPtr<JSStringRef> AccessibilityUIElement::selectedTextRange()
1611 {
1612     if (!ATK_IS_TEXT(m_element.get()))
1613         return JSStringCreateWithCharacters(0, 0);
1614
1615     gint start, end;
1616     g_free(atk_text_get_selection(ATK_TEXT(m_element.get()), 0, &start, &end));
1617
1618     GUniquePtr<gchar> selection(g_strdup_printf("{%d, %d}", start, end - start));
1619     return JSStringCreateWithUTF8CString(selection.get());
1620 }
1621
1622 bool AccessibilityUIElement::setSelectedTextRange(unsigned location, unsigned length)
1623 {
1624     if (!ATK_IS_TEXT(m_element.get()))
1625         return false;
1626
1627     if (!length)
1628         return atk_text_set_caret_offset(ATK_TEXT(m_element.get()), location);
1629
1630     return atk_text_set_selection(ATK_TEXT(m_element.get()), 0, location, location + length);
1631 }
1632
1633 void AccessibilityUIElement::increment()
1634 {
1635     alterCurrentValue(m_element.get(), 1);
1636 }
1637
1638 void AccessibilityUIElement::decrement()
1639 {
1640     alterCurrentValue(m_element.get(), -1);
1641 }
1642
1643 void AccessibilityUIElement::showMenu()
1644 {
1645     // FIXME: implement
1646 }
1647
1648 void AccessibilityUIElement::press()
1649 {
1650     if (!ATK_IS_ACTION(m_element.get()))
1651         return;
1652
1653     // Only one action per object is supported so far.
1654     atk_action_do_action(ATK_ACTION(m_element.get()), 0);
1655 }
1656
1657 void AccessibilityUIElement::setSelectedChild(AccessibilityUIElement* element) const
1658 {
1659     // FIXME: implement
1660 }
1661
1662 void AccessibilityUIElement::setSelectedChildAtIndex(unsigned index) const
1663 {
1664     if (!ATK_IS_SELECTION(m_element.get()))
1665         return;
1666
1667     atk_selection_add_selection(ATK_SELECTION(m_element.get()), index);
1668 }
1669
1670 void AccessibilityUIElement::removeSelectionAtIndex(unsigned index) const
1671 {
1672     if (!ATK_IS_SELECTION(m_element.get()))
1673         return;
1674
1675     atk_selection_remove_selection(ATK_SELECTION(m_element.get()), index);
1676 }
1677
1678 JSRetainPtr<JSStringRef> AccessibilityUIElement::accessibilityValue() const
1679 {
1680     // FIXME: implement
1681     return JSStringCreateWithCharacters(0, 0);
1682 }
1683
1684 JSRetainPtr<JSStringRef> AccessibilityUIElement::documentEncoding()
1685 {
1686     if (!ATK_IS_DOCUMENT(m_element.get()))
1687         return JSStringCreateWithCharacters(0, 0);
1688
1689     AtkRole role = atk_object_get_role(ATK_OBJECT(m_element.get()));
1690     if (role != ATK_ROLE_DOCUMENT_FRAME)
1691         return JSStringCreateWithCharacters(0, 0);
1692
1693     return JSStringCreateWithUTF8CString(atk_document_get_attribute_value(ATK_DOCUMENT(m_element.get()), "Encoding"));
1694 }
1695
1696 JSRetainPtr<JSStringRef> AccessibilityUIElement::documentURI()
1697 {
1698     if (!ATK_IS_DOCUMENT(m_element.get()))
1699         return JSStringCreateWithCharacters(0, 0);
1700
1701     AtkRole role = atk_object_get_role(ATK_OBJECT(m_element.get()));
1702     if (role != ATK_ROLE_DOCUMENT_FRAME)
1703         return JSStringCreateWithCharacters(0, 0);
1704
1705     return JSStringCreateWithUTF8CString(atk_document_get_attribute_value(ATK_DOCUMENT(m_element.get()), "URI"));
1706 }
1707
1708 JSRetainPtr<JSStringRef> AccessibilityUIElement::url()
1709 {
1710     if (!ATK_IS_HYPERLINK_IMPL(m_element.get()))
1711         return JSStringCreateWithCharacters(0, 0);
1712
1713     AtkHyperlink* hyperlink = atk_hyperlink_impl_get_hyperlink(ATK_HYPERLINK_IMPL(m_element.get()));
1714     GUniquePtr<char> hyperlinkURI(atk_hyperlink_get_uri(hyperlink, 0));
1715
1716     // Build the result string, stripping the absolute URL paths if present.
1717     char* localURI = g_strstr_len(hyperlinkURI.get(), -1, "LayoutTests");
1718     String axURL = String::format("AXURL: %s", localURI ? localURI : hyperlinkURI.get());
1719     return JSStringCreateWithUTF8CString(axURL.utf8().data());
1720 }
1721
1722 bool AccessibilityUIElement::addNotificationListener(JSValueRef functionCallback)
1723 {
1724     if (!functionCallback)
1725         return false;
1726
1727     // Only one notification listener per element.
1728     if (m_notificationHandler)
1729         return false;
1730
1731     m_notificationHandler = AccessibilityNotificationHandler::create();
1732     m_notificationHandler->setPlatformElement(platformUIElement());
1733     m_notificationHandler->setNotificationFunctionCallback(functionCallback);
1734
1735     return true;
1736 }
1737
1738 bool AccessibilityUIElement::removeNotificationListener()
1739 {
1740     // Programmers should not be trying to remove a listener that's already removed.
1741     ASSERT(m_notificationHandler);
1742     m_notificationHandler = nullptr;
1743
1744     return true;
1745 }
1746
1747 bool AccessibilityUIElement::isFocusable() const
1748 {
1749     return checkElementState(m_element.get(), ATK_STATE_FOCUSABLE);
1750 }
1751
1752 bool AccessibilityUIElement::isSelectable() const
1753 {
1754     return checkElementState(m_element.get(), ATK_STATE_SELECTABLE);
1755 }
1756
1757 bool AccessibilityUIElement::isMultiSelectable() const
1758 {
1759     return checkElementState(m_element.get(), ATK_STATE_MULTISELECTABLE);
1760 }
1761
1762 bool AccessibilityUIElement::isVisible() const
1763 {
1764     return checkElementState(m_element.get(), ATK_STATE_VISIBLE);
1765 }
1766
1767 bool AccessibilityUIElement::isOffScreen() const
1768 {
1769     // FIXME: implement
1770     return false;
1771 }
1772
1773 bool AccessibilityUIElement::isCollapsed() const
1774 {
1775     // FIXME: implement
1776     return false;
1777 }
1778
1779 bool AccessibilityUIElement::isIgnored() const
1780 {
1781     // FIXME: implement
1782     return false;
1783 }
1784
1785 bool AccessibilityUIElement::hasPopup() const
1786 {
1787     if (!ATK_IS_OBJECT(m_element.get()))
1788         return false;
1789
1790     String hasPopupValue = getAttributeSetValueForId(ATK_OBJECT(m_element.get()), ObjectAttributeType, "haspopup");
1791     return equalIgnoringCase(hasPopupValue, "true");
1792 }
1793
1794 void AccessibilityUIElement::takeFocus()
1795 {
1796     // FIXME: implement
1797 }
1798
1799 void AccessibilityUIElement::takeSelection()
1800 {
1801     // FIXME: implement
1802 }
1803
1804 void AccessibilityUIElement::addSelection()
1805 {
1806     // FIXME: implement
1807 }
1808
1809 void AccessibilityUIElement::removeSelection()
1810 {
1811     // FIXME: implement
1812 }
1813
1814 // Text markers
1815 PassRefPtr<AccessibilityTextMarkerRange> AccessibilityUIElement::lineTextMarkerRangeForTextMarker(AccessibilityTextMarker* textMarker)
1816 {
1817     // FIXME: implement
1818     return nullptr;
1819 }
1820
1821 PassRefPtr<AccessibilityTextMarkerRange> AccessibilityUIElement::textMarkerRangeForElement(AccessibilityUIElement* element)
1822 {
1823     // FIXME: implement
1824     return nullptr;
1825 }
1826
1827 int AccessibilityUIElement::textMarkerRangeLength(AccessibilityTextMarkerRange* range)
1828 {
1829     // FIXME: implement
1830     return 0;
1831 }
1832
1833 PassRefPtr<AccessibilityTextMarker> AccessibilityUIElement::previousTextMarker(AccessibilityTextMarker* textMarker)
1834 {
1835     // FIXME: implement
1836     return nullptr;
1837 }
1838
1839 PassRefPtr<AccessibilityTextMarker> AccessibilityUIElement::nextTextMarker(AccessibilityTextMarker* textMarker)
1840 {
1841     // FIXME: implement
1842     return nullptr;
1843 }
1844
1845 JSRetainPtr<JSStringRef> AccessibilityUIElement::stringForTextMarkerRange(AccessibilityTextMarkerRange* markerRange)
1846 {
1847     // FIXME: implement
1848     return JSStringCreateWithCharacters(0, 0);
1849 }
1850
1851 PassRefPtr<AccessibilityTextMarkerRange> AccessibilityUIElement::textMarkerRangeForMarkers(AccessibilityTextMarker* startMarker, AccessibilityTextMarker* endMarker)
1852 {
1853     // FIXME: implement
1854     return nullptr;
1855 }
1856
1857 PassRefPtr<AccessibilityTextMarker> AccessibilityUIElement::startTextMarkerForTextMarkerRange(AccessibilityTextMarkerRange* range)
1858 {
1859     // FIXME: implement
1860     return nullptr;
1861 }
1862
1863 PassRefPtr<AccessibilityTextMarker> AccessibilityUIElement::endTextMarkerForTextMarkerRange(AccessibilityTextMarkerRange* range)
1864 {
1865     // FIXME: implement
1866     return nullptr;
1867 }
1868
1869 PassRefPtr<AccessibilityTextMarker> AccessibilityUIElement::endTextMarkerForBounds(int x, int y, int width, int height)
1870 {
1871     // FIXME: implement
1872     return nullptr;
1873 }
1874
1875 PassRefPtr<AccessibilityTextMarker> AccessibilityUIElement::startTextMarkerForBounds(int x, int y, int width, int height)
1876 {
1877     // FIXME: implement
1878     return nullptr;
1879 }
1880
1881 PassRefPtr<AccessibilityTextMarker> AccessibilityUIElement::textMarkerForPoint(int x, int y)
1882 {
1883     // FIXME: implement
1884     return nullptr;
1885 }
1886
1887 PassRefPtr<AccessibilityUIElement> AccessibilityUIElement::accessibilityElementForTextMarker(AccessibilityTextMarker* marker)
1888 {
1889     // FIXME: implement
1890     return nullptr;
1891 }
1892
1893 bool AccessibilityUIElement::attributedStringForTextMarkerRangeContainsAttribute(JSStringRef attribute, AccessibilityTextMarkerRange* range)
1894 {
1895     // FIXME: implement
1896     return false;
1897 }
1898
1899 int AccessibilityUIElement::indexForTextMarker(AccessibilityTextMarker* marker)
1900 {
1901     // FIXME: implement
1902     return -1;
1903 }
1904
1905 bool AccessibilityUIElement::isTextMarkerValid(AccessibilityTextMarker* textMarker)
1906 {
1907     // FIXME: implement
1908     return false;
1909 }
1910
1911 PassRefPtr<AccessibilityTextMarker> AccessibilityUIElement::textMarkerForIndex(int textIndex)
1912 {
1913     // FIXME: implement
1914     return nullptr;
1915 }
1916     
1917 PassRefPtr<AccessibilityTextMarker> AccessibilityUIElement::startTextMarker()
1918 {
1919     // FIXME: implement
1920     return nullptr;    
1921 }
1922
1923 PassRefPtr<AccessibilityTextMarker> AccessibilityUIElement::endTextMarker()
1924 {
1925     // FIXME: implement
1926     return nullptr;
1927 }
1928
1929 bool AccessibilityUIElement::setSelectedVisibleTextRange(AccessibilityTextMarkerRange*)
1930 {
1931     return false;
1932 }
1933
1934 void AccessibilityUIElement::scrollToMakeVisible()
1935 {
1936     // FIXME: implement
1937 }
1938
1939 JSRetainPtr<JSStringRef> AccessibilityUIElement::supportedActions() const
1940 {
1941     // FIXME: implement
1942     return nullptr;
1943 }
1944
1945 JSRetainPtr<JSStringRef> AccessibilityUIElement::pathDescription() const
1946 {
1947     notImplemented();
1948     return nullptr;
1949 }
1950
1951 JSRetainPtr<JSStringRef> AccessibilityUIElement::mathPostscriptsDescription() const
1952 {
1953     notImplemented();
1954     return nullptr;
1955 }
1956
1957 JSRetainPtr<JSStringRef> AccessibilityUIElement::mathPrescriptsDescription() const
1958 {
1959     notImplemented();
1960     return nullptr;
1961 }
1962
1963 JSRetainPtr<JSStringRef> AccessibilityUIElement::classList() const
1964 {
1965     notImplemented();
1966     return nullptr;
1967 }
1968
1969 JSRetainPtr<JSStringRef> stringAtOffset(PlatformUIElement element, AtkTextBoundary boundary, int offset)
1970 {
1971     if (!ATK_IS_TEXT(element.get()))
1972         return JSStringCreateWithCharacters(0, 0);
1973
1974     gint startOffset, endOffset;
1975     StringBuilder builder;
1976
1977 #if ATK_CHECK_VERSION(2, 10, 0)
1978     AtkTextGranularity granularity;
1979     switch (boundary) {
1980     case ATK_TEXT_BOUNDARY_CHAR:
1981         granularity = ATK_TEXT_GRANULARITY_CHAR;
1982         break;
1983     case ATK_TEXT_BOUNDARY_WORD_START:
1984         granularity = ATK_TEXT_GRANULARITY_WORD;
1985         break;
1986     case ATK_TEXT_BOUNDARY_LINE_START:
1987         granularity = ATK_TEXT_GRANULARITY_LINE;
1988         break;
1989     case ATK_TEXT_BOUNDARY_SENTENCE_START:
1990         granularity = ATK_TEXT_GRANULARITY_SENTENCE;
1991         break;
1992     default:
1993         return JSStringCreateWithCharacters(0, 0);
1994     }
1995
1996     builder.append(atk_text_get_string_at_offset(ATK_TEXT(element.get()), offset, granularity, &startOffset, &endOffset));
1997 #else
1998     builder.append(atk_text_get_text_at_offset(ATK_TEXT(element.get()), offset, boundary, &startOffset, &endOffset));
1999 #endif
2000     builder.append(String::format(", %i, %i", startOffset, endOffset));
2001     return JSStringCreateWithUTF8CString(builder.toString().utf8().data());
2002 }
2003
2004 JSRetainPtr<JSStringRef> AccessibilityUIElement::characterAtOffset(int offset)
2005 {
2006     return stringAtOffset(m_element, ATK_TEXT_BOUNDARY_CHAR, offset);
2007 }
2008
2009 JSRetainPtr<JSStringRef> AccessibilityUIElement::wordAtOffset(int offset)
2010 {
2011     return stringAtOffset(m_element, ATK_TEXT_BOUNDARY_WORD_START, offset);
2012 }
2013
2014 JSRetainPtr<JSStringRef> AccessibilityUIElement::lineAtOffset(int offset)
2015 {
2016     return stringAtOffset(m_element, ATK_TEXT_BOUNDARY_LINE_START, offset);
2017 }
2018
2019 JSRetainPtr<JSStringRef> AccessibilityUIElement::sentenceAtOffset(int offset)
2020 {
2021     return stringAtOffset(m_element, ATK_TEXT_BOUNDARY_SENTENCE_START, offset);
2022 }
2023
2024 } // namespace WTR
2025
2026 #endif // HAVE(ACCESSIBILITY)