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