3700b4dc727584d21b40f37f325543ff66952870
[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, unsigned) 
1084 {
1085     // FIXME: implement
1086     return JSStringCreateWithCharacters(0, 0);
1087
1088
1089 JSStringRef AccessibilityUIElement::attributedStringForRange(unsigned, unsigned)
1090 {
1091     // FIXME: implement
1092     return JSStringCreateWithCharacters(0, 0);
1093 }
1094
1095 bool AccessibilityUIElement::attributedStringRangeIsMisspelled(unsigned location, unsigned length)
1096 {
1097     // FIXME: implement
1098     return false;
1099 }
1100
1101 unsigned AccessibilityUIElement::uiElementCountForSearchPredicate(JSContextRef context, AccessibilityUIElement* startElement, bool isDirectionNext, JSValueRef searchKey, JSStringRef searchText, bool visibleOnly)
1102 {
1103     // FIXME: implement
1104     return 0;
1105 }
1106
1107 AccessibilityUIElement AccessibilityUIElement::uiElementForSearchPredicate(JSContextRef context, AccessibilityUIElement* startElement, bool isDirectionNext, JSValueRef searchKey, JSStringRef searchText, bool visibleOnly)
1108 {
1109     // FIXME: implement
1110     return nullptr;
1111 }
1112
1113 AccessibilityUIElement AccessibilityUIElement::cellForColumnAndRow(unsigned column, unsigned row)
1114 {
1115     if (!ATK_IS_TABLE(m_element))
1116         return nullptr;
1117
1118     // Adopt the AtkObject representing the cell because
1119     // at_table_ref_at() transfers full ownership.
1120     GRefPtr<AtkObject> foundCell = adoptGRef(atk_table_ref_at(ATK_TABLE(m_element), row, column));
1121     return foundCell ? AccessibilityUIElement(foundCell.get()) : nullptr;
1122 }
1123
1124 JSStringRef AccessibilityUIElement::selectedTextRange()
1125 {
1126     if (!ATK_IS_TEXT(m_element))
1127         return JSStringCreateWithCharacters(0, 0);
1128
1129     gint start, end;
1130     g_free(atk_text_get_selection(ATK_TEXT(m_element), 0, &start, &end));
1131
1132     GOwnPtr<gchar> selection(g_strdup_printf("{%d, %d}", start, end - start));
1133     return JSStringCreateWithUTF8CString(selection.get());
1134 }
1135
1136 void AccessibilityUIElement::setSelectedTextRange(unsigned location, unsigned length)
1137 {
1138     if (!ATK_IS_TEXT(m_element))
1139         return;
1140
1141     atk_text_set_selection(ATK_TEXT(m_element), 0, location, location + length);
1142 }
1143
1144 JSStringRef AccessibilityUIElement::stringAttributeValue(JSStringRef attribute)
1145 {
1146     if (!ATK_IS_OBJECT(m_element))
1147         return JSStringCreateWithCharacters(0, 0);
1148
1149     String atkAttributeName = coreAttributeToAtkAttribute(attribute);
1150
1151     // Try object attributes first.
1152     String attributeValue = getAttributeSetValueForId(ATK_OBJECT(m_element), ObjectAttributeType, atkAttributeName);
1153
1154     // Try text attributes if the requested one was not found and we have an AtkText object.
1155     if (attributeValue.isEmpty() && ATK_IS_TEXT(m_element))
1156         attributeValue = getAttributeSetValueForId(ATK_OBJECT(m_element), TextAttributeType, atkAttributeName);
1157
1158     // Additional check to make sure that the exposure of the state ATK_STATE_INVALID_ENTRY
1159     // is consistent with the exposure of aria-invalid as a text attribute, if present.
1160     if (atkAttributeName == attributesMap[InvalidNameIndex][AtkDomain]) {
1161         bool isInvalidState = checkElementState(m_element, ATK_STATE_INVALID_ENTRY);
1162         if (attributeValue.isEmpty())
1163             return JSStringCreateWithUTF8CString(isInvalidState ? "true" : "false");
1164
1165         // If the text attribute was there, check that it's consistent with
1166         // what the state says or force the test to fail otherwise.
1167         bool isAriaInvalid = attributeValue != "false";
1168         if (isInvalidState != isAriaInvalid)
1169             return JSStringCreateWithCharacters(0, 0);
1170     }
1171
1172     return JSStringCreateWithUTF8CString(attributeValue.utf8().data());
1173 }
1174
1175 double AccessibilityUIElement::numberAttributeValue(JSStringRef attribute)
1176 {
1177     // FIXME: implement
1178     return 0;
1179 }
1180
1181 bool AccessibilityUIElement::boolAttributeValue(JSStringRef attribute)
1182 {
1183     // FIXME: implement
1184     return false;
1185 }
1186
1187 bool AccessibilityUIElement::isAttributeSettable(JSStringRef attribute)
1188 {
1189     if (!ATK_IS_OBJECT(m_element))
1190         return false;
1191
1192     String attributeString = jsStringToWTFString(attribute);
1193     if (attributeString == "AXValue")
1194         return checkElementState(m_element, ATK_STATE_EDITABLE);
1195
1196     return false;
1197 }
1198
1199 bool AccessibilityUIElement::isAttributeSupported(JSStringRef attribute)
1200 {
1201     if (!ATK_IS_OBJECT(m_element))
1202         return false;
1203
1204     String atkAttributeName = coreAttributeToAtkAttribute(attribute);
1205     if (atkAttributeName.isEmpty())
1206         return false;
1207
1208     // For now, an attribute is supported whether it's exposed as a object or a text attribute.
1209     String attributeValue = getAttributeSetValueForId(ATK_OBJECT(m_element), ObjectAttributeType, atkAttributeName);
1210     if (attributeValue.isEmpty())
1211         attributeValue = getAttributeSetValueForId(ATK_OBJECT(m_element), TextAttributeType, atkAttributeName);
1212
1213     return !attributeValue.isEmpty();
1214 }
1215
1216 void AccessibilityUIElement::increment()
1217 {
1218     alterCurrentValue(m_element, 1);
1219 }
1220
1221 void AccessibilityUIElement::decrement()
1222 {
1223     alterCurrentValue(m_element, -1);
1224 }
1225
1226 void AccessibilityUIElement::press()
1227 {
1228     if (!ATK_IS_ACTION(m_element))
1229         return;
1230
1231     // Only one action per object is supported so far.
1232     atk_action_do_action(ATK_ACTION(m_element), 0);
1233 }
1234
1235 void AccessibilityUIElement::showMenu()
1236 {
1237     // FIXME: implement
1238 }
1239
1240 AccessibilityUIElement AccessibilityUIElement::disclosedRowAtIndex(unsigned index)
1241 {
1242     return nullptr;
1243 }
1244
1245 AccessibilityUIElement AccessibilityUIElement::ariaOwnsElementAtIndex(unsigned index)
1246 {
1247     return nullptr;
1248 }
1249
1250 AccessibilityUIElement AccessibilityUIElement::ariaFlowToElementAtIndex(unsigned index)
1251 {
1252     return nullptr;
1253 }
1254
1255 AccessibilityUIElement AccessibilityUIElement::selectedRowAtIndex(unsigned index)
1256 {
1257     return nullptr;
1258 }
1259
1260 AccessibilityUIElement AccessibilityUIElement::rowAtIndex(unsigned index)
1261 {
1262     return nullptr;
1263 }
1264
1265 AccessibilityUIElement AccessibilityUIElement::disclosedByRow()
1266 {
1267     return nullptr;
1268 }
1269
1270 JSStringRef AccessibilityUIElement::accessibilityValue() const
1271 {
1272     // FIXME: implement
1273     return JSStringCreateWithCharacters(0, 0);
1274 }
1275
1276 JSStringRef AccessibilityUIElement::documentEncoding()
1277 {
1278     if (!ATK_IS_DOCUMENT(m_element))
1279         return JSStringCreateWithCharacters(0, 0);
1280
1281     AtkRole role = atk_object_get_role(ATK_OBJECT(m_element));
1282     if (role != ATK_ROLE_DOCUMENT_FRAME)
1283         return JSStringCreateWithCharacters(0, 0);
1284
1285     return JSStringCreateWithUTF8CString(atk_document_get_attribute_value(ATK_DOCUMENT(m_element), "Encoding"));
1286 }
1287
1288 JSStringRef AccessibilityUIElement::documentURI()
1289 {
1290     if (!ATK_IS_DOCUMENT(m_element))
1291         return JSStringCreateWithCharacters(0, 0);
1292
1293     AtkRole role = atk_object_get_role(ATK_OBJECT(m_element));
1294     if (role != ATK_ROLE_DOCUMENT_FRAME)
1295         return JSStringCreateWithCharacters(0, 0);
1296
1297     return JSStringCreateWithUTF8CString(atk_document_get_attribute_value(ATK_DOCUMENT(m_element), "URI"));
1298 }
1299
1300 JSStringRef AccessibilityUIElement::url()
1301 {
1302     if (!ATK_IS_HYPERLINK_IMPL(m_element))
1303         return JSStringCreateWithCharacters(0, 0);
1304
1305     AtkHyperlink* hyperlink = atk_hyperlink_impl_get_hyperlink(ATK_HYPERLINK_IMPL(m_element));
1306     GOwnPtr<char> hyperlinkURI(atk_hyperlink_get_uri(hyperlink, 0));
1307
1308     // Build the result string, stripping the absolute URL paths if present.
1309     char* localURI = g_strstr_len(hyperlinkURI.get(), -1, "LayoutTests");
1310     String axURL = String::format("AXURL: %s", localURI ? localURI : hyperlinkURI.get());
1311     return JSStringCreateWithUTF8CString(axURL.utf8().data());
1312 }
1313
1314 bool AccessibilityUIElement::addNotificationListener(JSObjectRef functionCallback)
1315 {
1316     if (!functionCallback)
1317         return false;
1318
1319     // Only one notification listener per element.
1320     if (m_notificationHandler)
1321         return false;
1322
1323     m_notificationHandler = AccessibilityNotificationHandler::create();
1324     m_notificationHandler->setPlatformElement(platformUIElement());
1325     m_notificationHandler->setNotificationFunctionCallback(functionCallback);
1326
1327     return true;
1328 }
1329
1330 void AccessibilityUIElement::removeNotificationListener()
1331 {
1332     // Programmers should not be trying to remove a listener that's already removed.
1333     ASSERT(m_notificationHandler);
1334
1335     m_notificationHandler = nullptr;
1336 }
1337
1338 bool AccessibilityUIElement::isFocusable() const
1339 {
1340     if (!ATK_IS_OBJECT(m_element))
1341         return false;
1342
1343     GRefPtr<AtkStateSet> stateSet = adoptGRef(atk_object_ref_state_set(ATK_OBJECT(m_element)));
1344     gboolean isFocusable = atk_state_set_contains_state(stateSet.get(), ATK_STATE_FOCUSABLE);
1345
1346     return isFocusable;
1347 }
1348
1349 bool AccessibilityUIElement::isSelectable() const
1350 {
1351     return checkElementState(m_element, ATK_STATE_SELECTABLE);
1352 }
1353
1354 bool AccessibilityUIElement::isMultiSelectable() const
1355 {
1356     return checkElementState(m_element, ATK_STATE_MULTISELECTABLE);
1357 }
1358
1359 bool AccessibilityUIElement::isSelectedOptionActive() const
1360 {
1361     return checkElementState(m_element, ATK_STATE_ACTIVE);
1362 }
1363
1364 bool AccessibilityUIElement::isVisible() const
1365 {
1366     // FIXME: implement
1367     return false;
1368 }
1369
1370 bool AccessibilityUIElement::isOffScreen() const
1371 {
1372     // FIXME: implement
1373     return false;
1374 }
1375
1376 bool AccessibilityUIElement::isCollapsed() const
1377 {
1378     // FIXME: implement
1379     return false;
1380 }
1381
1382 bool AccessibilityUIElement::isIgnored() const
1383 {
1384     // FIXME: implement
1385     return false;
1386 }
1387
1388 bool AccessibilityUIElement::hasPopup() const
1389 {
1390     if (!ATK_IS_OBJECT(m_element))
1391         return false;
1392
1393     String hasPopupValue = getAttributeSetValueForId(ATK_OBJECT(m_element), ObjectAttributeType, "haspopup");
1394     return equalIgnoringCase(hasPopupValue, "true");
1395 }
1396
1397 void AccessibilityUIElement::takeFocus()
1398 {
1399     // FIXME: implement
1400 }
1401
1402 void AccessibilityUIElement::takeSelection()
1403 {
1404     // FIXME: implement
1405 }
1406
1407 void AccessibilityUIElement::addSelection()
1408 {
1409     // FIXME: implement
1410 }
1411
1412 void AccessibilityUIElement::removeSelection()
1413 {
1414     // FIXME: implement
1415 }
1416
1417 void AccessibilityUIElement::scrollToMakeVisible()
1418 {
1419     // FIXME: implement
1420 }
1421
1422 void AccessibilityUIElement::scrollToMakeVisibleWithSubFocus(int x, int y, int width, int height)
1423 {
1424     // FIXME: implement
1425 }
1426
1427 void AccessibilityUIElement::scrollToGlobalPoint(int x, int y)
1428 {
1429     // FIXME: implement
1430 }
1431
1432 JSStringRef AccessibilityUIElement::classList() const
1433 {
1434     // FIXME: implement
1435     return nullptr;
1436 }
1437
1438 JSStringRef stringAtOffset(PlatformUIElement element, AtkTextBoundary boundary, int offset)
1439 {
1440     if (!ATK_IS_TEXT(element))
1441         return JSStringCreateWithCharacters(0, 0);
1442
1443     gint startOffset, endOffset;
1444     StringBuilder builder;
1445
1446 #if ATK_CHECK_VERSION(2, 10, 0)
1447     AtkTextGranularity granularity;
1448     switch (boundary) {
1449     case ATK_TEXT_BOUNDARY_CHAR:
1450         granularity = ATK_TEXT_GRANULARITY_CHAR;
1451         break;
1452     case ATK_TEXT_BOUNDARY_WORD_START:
1453         granularity = ATK_TEXT_GRANULARITY_WORD;
1454         break;
1455     case ATK_TEXT_BOUNDARY_LINE_START:
1456         granularity = ATK_TEXT_GRANULARITY_LINE;
1457         break;
1458     case ATK_TEXT_BOUNDARY_SENTENCE_START:
1459         granularity = ATK_TEXT_GRANULARITY_SENTENCE;
1460         break;
1461     default:
1462         return JSStringCreateWithCharacters(0, 0);
1463     }
1464
1465     builder.append(atk_text_get_string_at_offset(ATK_TEXT(element), offset, granularity, &startOffset, &endOffset));
1466 #else
1467     builder.append(atk_text_get_text_at_offset(ATK_TEXT(element), offset, boundary, &startOffset, &endOffset));
1468 #endif
1469     builder.append(String::format(", %i, %i", startOffset, endOffset));
1470     return JSStringCreateWithUTF8CString(builder.toString().utf8().data());
1471 }
1472
1473 JSStringRef AccessibilityUIElement::characterAtOffset(int offset)
1474 {
1475     return stringAtOffset(m_element, ATK_TEXT_BOUNDARY_CHAR, offset);
1476 }
1477
1478 JSStringRef AccessibilityUIElement::wordAtOffset(int offset)
1479 {
1480     return stringAtOffset(m_element, ATK_TEXT_BOUNDARY_WORD_START, offset);
1481 }
1482
1483 JSStringRef AccessibilityUIElement::lineAtOffset(int offset)
1484 {
1485     return stringAtOffset(m_element, ATK_TEXT_BOUNDARY_LINE_START, offset);
1486 }
1487
1488 JSStringRef AccessibilityUIElement::sentenceAtOffset(int offset)
1489 {
1490     return stringAtOffset(m_element, ATK_TEXT_BOUNDARY_SENTENCE_START, offset);
1491 }
1492
1493 #endif