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