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