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