7b3bd0677fcf2dc31ff0f3cb45bd890d33f6d30d
[WebKit-https.git] / Source / WebCore / accessibility / atk / WebKitAccessibleWrapperAtk.cpp
1 /*
2  * Copyright (C) 2008 Nuanti Ltd.
3  * Copyright (C) 2009 Jan Alonzo
4  * Copyright (C) 2009, 2010, 2011, 2012 Igalia S.L.
5  * Copyright (C) 2013 Samsung Electronics
6  *
7  * Portions from Mozilla a11y, copyright as follows:
8  *
9  * The Original Code is mozilla.org code.
10  *
11  * The Initial Developer of the Original Code is
12  * Sun Microsystems, Inc.
13  * Portions created by the Initial Developer are Copyright (C) 2002
14  * the Initial Developer. All Rights Reserved.
15  *
16  * This library is free software; you can redistribute it and/or
17  * modify it under the terms of the GNU Library General Public
18  * License as published by the Free Software Foundation; either
19  * version 2 of the License, or (at your option) any later version.
20  *
21  * This library is distributed in the hope that it will be useful,
22  * but WITHOUT ANY WARRANTY; without even the implied warranty of
23  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
24  * Library General Public License for more details.
25  *
26  * You should have received a copy of the GNU Library General Public License
27  * along with this library; see the file COPYING.LIB.  If not, write to
28  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
29  * Boston, MA 02110-1301, USA.
30  */
31
32 #include "config.h"
33 #include "WebKitAccessibleWrapperAtk.h"
34
35 #if HAVE(ACCESSIBILITY)
36
37 #include "AXObjectCache.h"
38 #include "AccessibilityList.h"
39 #include "AccessibilityListBoxOption.h"
40 #include "AccessibilityTable.h"
41 #include "AccessibilityTableCell.h"
42 #include "AccessibilityTableRow.h"
43 #include "Document.h"
44 #include "Editing.h"
45 #include "Frame.h"
46 #include "FrameView.h"
47 #include "HTMLNames.h"
48 #include "HTMLTableElement.h"
49 #include "HostWindow.h"
50 #include "RenderAncestorIterator.h"
51 #include "RenderBlock.h"
52 #include "RenderObject.h"
53 #include "SVGElement.h"
54 #include "Settings.h"
55 #include "TextIterator.h"
56 #include "VisibleUnits.h"
57 #include "WebKitAccessibleHyperlink.h"
58 #include "WebKitAccessibleInterfaceAction.h"
59 #include "WebKitAccessibleInterfaceComponent.h"
60 #include "WebKitAccessibleInterfaceDocument.h"
61 #include "WebKitAccessibleInterfaceEditableText.h"
62 #include "WebKitAccessibleInterfaceHyperlinkImpl.h"
63 #include "WebKitAccessibleInterfaceHypertext.h"
64 #include "WebKitAccessibleInterfaceImage.h"
65 #include "WebKitAccessibleInterfaceSelection.h"
66 #include "WebKitAccessibleInterfaceTable.h"
67 #include "WebKitAccessibleInterfaceTableCell.h"
68 #include "WebKitAccessibleInterfaceText.h"
69 #include "WebKitAccessibleInterfaceValue.h"
70 #include "WebKitAccessibleUtil.h"
71 #include <glib/gprintf.h>
72 #include <wtf/text/CString.h>
73
74 using namespace WebCore;
75
76 struct _WebKitAccessiblePrivate {
77     // Cached data for AtkObject.
78     CString accessibleName;
79     CString accessibleDescription;
80
81     // Cached data for AtkAction.
82     CString actionName;
83     CString actionKeyBinding;
84
85     // Cached data for AtkDocument.
86     CString documentLocale;
87     CString documentType;
88     CString documentEncoding;
89     CString documentURI;
90
91     // Cached data for AtkImage.
92     CString imageDescription;
93 };
94
95 #define WEBKIT_ACCESSIBLE_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE((obj), WEBKIT_TYPE_ACCESSIBLE, WebKitAccessiblePrivate))
96
97 static AccessibilityObject* fallbackObject()
98 {
99     static AccessibilityObject* object = &AccessibilityListBoxOption::create().leakRef();
100     return object;
101 }
102
103 static AccessibilityObject* core(AtkObject* object)
104 {
105     if (!WEBKIT_IS_ACCESSIBLE(object))
106         return 0;
107
108     return webkitAccessibleGetAccessibilityObject(WEBKIT_ACCESSIBLE(object));
109 }
110
111 static const gchar* webkitAccessibleGetName(AtkObject* object)
112 {
113     g_return_val_if_fail(WEBKIT_IS_ACCESSIBLE(object), 0);
114     returnValIfWebKitAccessibleIsInvalid(WEBKIT_ACCESSIBLE(object), 0);
115
116     Vector<AccessibilityText> textOrder;
117     core(object)->accessibilityText(textOrder);
118
119     for (const auto& text : textOrder) {
120         // FIXME: This check is here because AccessibilityNodeObject::titleElementText()
121         // appends an empty String for the LabelByElementText source when there is a
122         // titleUIElement(). Removing this check makes some fieldsets lose their name.
123         if (text.text.isEmpty())
124             continue;
125
126         // WebCore Accessibility should provide us with the text alternative computation
127         // in the order defined by that spec. So take the first thing that our platform
128         // does not expose via the AtkObject description.
129         if (text.textSource != HelpText && text.textSource != SummaryText)
130             return cacheAndReturnAtkProperty(object, AtkCachedAccessibleName, text.text);
131     }
132
133     return cacheAndReturnAtkProperty(object, AtkCachedAccessibleName, "");
134 }
135
136 static const gchar* webkitAccessibleGetDescription(AtkObject* object)
137 {
138     g_return_val_if_fail(WEBKIT_IS_ACCESSIBLE(object), 0);
139     returnValIfWebKitAccessibleIsInvalid(WEBKIT_ACCESSIBLE(object), 0);
140
141     Vector<AccessibilityText> textOrder;
142     core(object)->accessibilityText(textOrder);
143
144     bool nameTextAvailable = false;
145     for (const auto& text : textOrder) {
146         // WebCore Accessibility should provide us with the text alternative computation
147         // in the order defined by that spec. So take the first thing that our platform
148         // does not expose via the AtkObject name.
149         if (text.textSource == HelpText || text.textSource == SummaryText)
150             return cacheAndReturnAtkProperty(object, AtkCachedAccessibleDescription, text.text);
151
152         // If there is no other text alternative, the title tag contents will have been
153         // used for the AtkObject name. We don't want to duplicate it here.
154         if (text.textSource == TitleTagText && nameTextAvailable)
155             return cacheAndReturnAtkProperty(object, AtkCachedAccessibleDescription, text.text);
156
157         nameTextAvailable = true;
158     }
159
160     return cacheAndReturnAtkProperty(object, AtkCachedAccessibleDescription, "");
161 }
162
163 static void removeAtkRelationByType(AtkRelationSet* relationSet, AtkRelationType relationType)
164 {
165     int count = atk_relation_set_get_n_relations(relationSet);
166     for (int i = 0; i < count; i++) {
167         AtkRelation* relation = atk_relation_set_get_relation(relationSet, i);
168         if (atk_relation_get_relation_type(relation) == relationType) {
169             atk_relation_set_remove(relationSet, relation);
170             break;
171         }
172     }
173 }
174
175 static void setAtkRelationSetFromCoreObject(AccessibilityObject* coreObject, AtkRelationSet* relationSet)
176 {
177     // Elements with aria-labelledby should have the labelled-by relation as per the ARIA AAM spec.
178     // Controls with a label element and fieldsets with a legend element should also use this relation
179     // as per the HTML AAM spec. The reciprocal label-for relation should also be used.
180     removeAtkRelationByType(relationSet, ATK_RELATION_LABELLED_BY);
181     removeAtkRelationByType(relationSet, ATK_RELATION_LABEL_FOR);
182     if (coreObject->isControl()) {
183         if (AccessibilityObject* label = coreObject->correspondingLabelForControlElement())
184             atk_relation_set_add_relation_by_type(relationSet, ATK_RELATION_LABELLED_BY, label->wrapper());
185     } else if (coreObject->isFieldset()) {
186         if (AccessibilityObject* label = coreObject->titleUIElement())
187             atk_relation_set_add_relation_by_type(relationSet, ATK_RELATION_LABELLED_BY, label->wrapper());
188     } else if (coreObject->roleValue() == LegendRole) {
189         if (RenderBlock* renderFieldset = ancestorsOfType<RenderBlock>(*coreObject->renderer()).first()) {
190             if (renderFieldset->isFieldset()) {
191                 AccessibilityObject* fieldset = coreObject->axObjectCache()->getOrCreate(renderFieldset);
192                 atk_relation_set_add_relation_by_type(relationSet, ATK_RELATION_LABEL_FOR, fieldset->wrapper());
193             }
194         }
195     } else if (AccessibilityObject* control = coreObject->correspondingControlForLabelElement()) {
196         atk_relation_set_add_relation_by_type(relationSet, ATK_RELATION_LABEL_FOR, control->wrapper());
197     } else {
198         AccessibilityObject::AccessibilityChildrenVector ariaLabelledByElements;
199         coreObject->ariaLabelledByElements(ariaLabelledByElements);
200         for (const auto& accessibilityObject : ariaLabelledByElements)
201             atk_relation_set_add_relation_by_type(relationSet, ATK_RELATION_LABELLED_BY, accessibilityObject->wrapper());
202     }
203
204     // Elements referenced by aria-labelledby should have the label-for relation as per the ARIA AAM spec.
205     AccessibilityObject::AccessibilityChildrenVector labels;
206     coreObject->ariaLabelledByReferencingElements(labels);
207     for (const auto& accessibilityObject : labels)
208         atk_relation_set_add_relation_by_type(relationSet, ATK_RELATION_LABEL_FOR, accessibilityObject->wrapper());
209
210     // Elements with aria-flowto should have the flows-to relation as per the ARIA AAM spec.
211     removeAtkRelationByType(relationSet, ATK_RELATION_FLOWS_TO);
212     AccessibilityObject::AccessibilityChildrenVector ariaFlowToElements;
213     coreObject->ariaFlowToElements(ariaFlowToElements);
214     for (const auto& accessibilityObject : ariaFlowToElements)
215         atk_relation_set_add_relation_by_type(relationSet, ATK_RELATION_FLOWS_TO, accessibilityObject->wrapper());
216
217     // Elements referenced by aria-flowto should have the flows-from relation as per the ARIA AAM spec.
218     removeAtkRelationByType(relationSet, ATK_RELATION_FLOWS_FROM);
219     AccessibilityObject::AccessibilityChildrenVector flowFrom;
220     coreObject->ariaFlowToReferencingElements(flowFrom);
221     for (const auto& accessibilityObject : flowFrom)
222         atk_relation_set_add_relation_by_type(relationSet, ATK_RELATION_FLOWS_FROM, accessibilityObject->wrapper());
223
224     // Elements with aria-describedby should have the described-by relation as per the ARIA AAM spec.
225     removeAtkRelationByType(relationSet, ATK_RELATION_DESCRIBED_BY);
226     AccessibilityObject::AccessibilityChildrenVector ariaDescribedByElements;
227     coreObject->ariaDescribedByElements(ariaDescribedByElements);
228     for (const auto& accessibilityObject : ariaDescribedByElements)
229         atk_relation_set_add_relation_by_type(relationSet, ATK_RELATION_DESCRIBED_BY, accessibilityObject->wrapper());
230
231     // Elements referenced by aria-describedby should have the description-for relation as per the ARIA AAM spec.
232     removeAtkRelationByType(relationSet, ATK_RELATION_DESCRIPTION_FOR);
233     AccessibilityObject::AccessibilityChildrenVector describers;
234     coreObject->ariaDescribedByReferencingElements(describers);
235     for (const auto& accessibilityObject : describers)
236         atk_relation_set_add_relation_by_type(relationSet, ATK_RELATION_DESCRIPTION_FOR, accessibilityObject->wrapper());
237
238     // Elements with aria-controls should have the controller-for relation as per the ARIA AAM spec.
239     removeAtkRelationByType(relationSet, ATK_RELATION_CONTROLLER_FOR);
240     AccessibilityObject::AccessibilityChildrenVector ariaControls;
241     coreObject->ariaControlsElements(ariaControls);
242     for (const auto& accessibilityObject : ariaControls)
243         atk_relation_set_add_relation_by_type(relationSet, ATK_RELATION_CONTROLLER_FOR, accessibilityObject->wrapper());
244
245     // Elements referenced by aria-controls should have the controlled-by relation as per the ARIA AAM spec.
246     removeAtkRelationByType(relationSet, ATK_RELATION_CONTROLLED_BY);
247     AccessibilityObject::AccessibilityChildrenVector controllers;
248     coreObject->ariaControlsReferencingElements(controllers);
249     for (const auto& accessibilityObject : controllers)
250         atk_relation_set_add_relation_by_type(relationSet, ATK_RELATION_CONTROLLED_BY, accessibilityObject->wrapper());
251
252     // Elements with aria-owns should have the node-parent-of relation as per the ARIA AAM spec.
253     removeAtkRelationByType(relationSet, ATK_RELATION_NODE_PARENT_OF);
254     AccessibilityObject::AccessibilityChildrenVector ariaOwns;
255     coreObject->ariaOwnsElements(ariaOwns);
256     for (const auto& accessibilityObject : ariaOwns)
257         atk_relation_set_add_relation_by_type(relationSet, ATK_RELATION_NODE_PARENT_OF, accessibilityObject->wrapper());
258
259     // Elements referenced by aria-owns should have the node-child-of relation as per the ARIA AAM spec.
260     removeAtkRelationByType(relationSet, ATK_RELATION_NODE_CHILD_OF);
261     AccessibilityObject::AccessibilityChildrenVector owners;
262     coreObject->ariaOwnsReferencingElements(owners);
263     for (const auto& accessibilityObject : owners)
264         atk_relation_set_add_relation_by_type(relationSet, ATK_RELATION_NODE_CHILD_OF, accessibilityObject->wrapper());
265
266 #if ATK_CHECK_VERSION(2, 25, 2)
267     // Elements with aria-details should have the details relation as per the ARIA AAM spec.
268     removeAtkRelationByType(relationSet, ATK_RELATION_DETAILS);
269     AccessibilityObject::AccessibilityChildrenVector ariaDetails;
270     coreObject->ariaDetailsElements(ariaDetails);
271     for (const auto& accessibilityObject : ariaDetails)
272         atk_relation_set_add_relation_by_type(relationSet, ATK_RELATION_DETAILS, accessibilityObject->wrapper());
273
274     // Elements referenced by aria-details should have the details-for relation as per the ARIA AAM spec.
275     removeAtkRelationByType(relationSet, ATK_RELATION_DETAILS_FOR);
276     AccessibilityObject::AccessibilityChildrenVector details;
277     coreObject->ariaDetailsReferencingElements(details);
278     for (const auto& accessibilityObject : details)
279         atk_relation_set_add_relation_by_type(relationSet, ATK_RELATION_DETAILS_FOR, accessibilityObject->wrapper());
280
281     // Elements with aria-errormessage should have the error-message relation as per the ARIA AAM spec.
282     removeAtkRelationByType(relationSet, ATK_RELATION_ERROR_MESSAGE);
283     AccessibilityObject::AccessibilityChildrenVector ariaErrorMessage;
284     coreObject->ariaErrorMessageElements(ariaErrorMessage);
285     for (const auto& accessibilityObject : ariaErrorMessage)
286         atk_relation_set_add_relation_by_type(relationSet, ATK_RELATION_ERROR_MESSAGE, accessibilityObject->wrapper());
287
288     // Elements referenced by aria-errormessage should have the error-for relation as per the ARIA AAM spec.
289     removeAtkRelationByType(relationSet, ATK_RELATION_ERROR_FOR);
290     AccessibilityObject::AccessibilityChildrenVector errors;
291     coreObject->ariaErrorMessageReferencingElements(errors);
292     for (const auto& accessibilityObject : errors)
293         atk_relation_set_add_relation_by_type(relationSet, ATK_RELATION_ERROR_FOR, accessibilityObject->wrapper());
294 #endif
295 }
296
297 static gpointer webkitAccessibleParentClass = nullptr;
298
299 static bool isRootObject(AccessibilityObject* coreObject)
300 {
301     // The root accessible object in WebCore is always an object with
302     // the ScrolledArea role with one child with the WebArea role.
303     if (!coreObject || !coreObject->isScrollView())
304         return false;
305
306     AccessibilityObject* firstChild = coreObject->firstChild();
307     if (!firstChild || !firstChild->isWebArea())
308         return false;
309
310     return true;
311 }
312
313 static AtkObject* atkParentOfRootObject(AtkObject* object)
314 {
315     AccessibilityObject* coreObject = core(object);
316     AccessibilityObject* coreParent = coreObject->parentObjectUnignored();
317
318     // The top level object claims to not have a parent. This makes it
319     // impossible for assistive technologies to ascend the accessible
320     // hierarchy all the way to the application. (Bug 30489)
321     if (!coreParent && isRootObject(coreObject)) {
322         Document* document = coreObject->document();
323         if (!document)
324             return 0;
325     }
326
327     if (!coreParent)
328         return 0;
329
330     return coreParent->wrapper();
331 }
332
333 static AtkObject* webkitAccessibleGetParent(AtkObject* object)
334 {
335     g_return_val_if_fail(WEBKIT_IS_ACCESSIBLE(object), 0);
336     returnValIfWebKitAccessibleIsInvalid(WEBKIT_ACCESSIBLE(object), 0);
337
338     // Check first if the parent has been already set.
339     AtkObject* accessibleParent = ATK_OBJECT_CLASS(webkitAccessibleParentClass)->get_parent(object);
340     if (accessibleParent)
341         return accessibleParent;
342
343     // Parent not set yet, so try to find it in the hierarchy.
344     AccessibilityObject* coreObject = core(object);
345     if (!coreObject)
346         return 0;
347
348     AccessibilityObject* coreParent = coreObject->parentObjectUnignored();
349
350     if (!coreParent && isRootObject(coreObject))
351         return atkParentOfRootObject(object);
352
353     if (!coreParent)
354         return 0;
355
356     return coreParent->wrapper();
357 }
358
359 static gint webkitAccessibleGetNChildren(AtkObject* object)
360 {
361     g_return_val_if_fail(WEBKIT_IS_ACCESSIBLE(object), 0);
362     returnValIfWebKitAccessibleIsInvalid(WEBKIT_ACCESSIBLE(object), 0);
363
364     AccessibilityObject* coreObject = core(object);
365
366     return coreObject->children().size();
367 }
368
369 static AtkObject* webkitAccessibleRefChild(AtkObject* object, gint index)
370 {
371     g_return_val_if_fail(WEBKIT_IS_ACCESSIBLE(object), 0);
372     returnValIfWebKitAccessibleIsInvalid(WEBKIT_ACCESSIBLE(object), 0);
373
374     if (index < 0)
375         return 0;
376
377     AccessibilityObject* coreObject = core(object);
378     AccessibilityObject* coreChild = nullptr;
379
380     const AccessibilityObject::AccessibilityChildrenVector& children = coreObject->children();
381     if (static_cast<size_t>(index) >= children.size())
382         return 0;
383     coreChild = children.at(index).get();
384
385     if (!coreChild)
386         return 0;
387
388     AtkObject* child = coreChild->wrapper();
389     atk_object_set_parent(child, object);
390     g_object_ref(child);
391
392     return child;
393 }
394
395 static gint webkitAccessibleGetIndexInParent(AtkObject* object)
396 {
397     g_return_val_if_fail(WEBKIT_IS_ACCESSIBLE(object), -1);
398     returnValIfWebKitAccessibleIsInvalid(WEBKIT_ACCESSIBLE(object), -1);
399
400     AccessibilityObject* coreObject = core(object);
401     AccessibilityObject* parent = coreObject->parentObjectUnignored();
402
403     if (!parent && isRootObject(coreObject)) {
404         AtkObject* atkParent = atkParentOfRootObject(object);
405         if (!atkParent)
406             return -1;
407
408         unsigned count = atk_object_get_n_accessible_children(atkParent);
409         for (unsigned i = 0; i < count; ++i) {
410             AtkObject* child = atk_object_ref_accessible_child(atkParent, i);
411             bool childIsObject = child == object;
412             g_object_unref(child);
413             if (childIsObject)
414                 return i;
415         }
416     }
417
418     if (!parent)
419         return -1;
420
421     size_t index = parent->children().find(coreObject);
422     return (index == WTF::notFound) ? -1 : index;
423 }
424
425 static AtkAttributeSet* webkitAccessibleGetAttributes(AtkObject* object)
426 {
427     g_return_val_if_fail(WEBKIT_IS_ACCESSIBLE(object), 0);
428     returnValIfWebKitAccessibleIsInvalid(WEBKIT_ACCESSIBLE(object), 0);
429
430     AtkAttributeSet* attributeSet = nullptr;
431 #if PLATFORM(GTK)
432     attributeSet = addToAtkAttributeSet(attributeSet, "toolkit", "WebKitGtk");
433 #endif
434
435     AccessibilityObject* coreObject = core(object);
436     if (!coreObject)
437         return attributeSet;
438
439     // Hack needed for WebKit2 tests because obtaining an element by its ID
440     // cannot be done from the UIProcess. Assistive technologies have no need
441     // for this information.
442     Element* element = coreObject->element() ? coreObject->element() : coreObject->actionElement();
443     if (element) {
444         String tagName = element->tagName();
445         if (!tagName.isEmpty())
446             attributeSet = addToAtkAttributeSet(attributeSet, "tag", tagName.convertToASCIILowercase().utf8().data());
447         String id = element->getIdAttribute().string();
448         if (!id.isEmpty())
449             attributeSet = addToAtkAttributeSet(attributeSet, "html-id", id.utf8().data());
450     }
451
452     int level = coreObject->isHeading() ? coreObject->headingLevel() : coreObject->hierarchicalLevel();
453     if (level) {
454         String value = String::number(level);
455         attributeSet = addToAtkAttributeSet(attributeSet, "level", value.utf8().data());
456     }
457
458     if (coreObject->roleValue() == MathElementRole) {
459         if (coreObject->isMathMultiscriptObject(PreSuperscript) || coreObject->isMathMultiscriptObject(PreSubscript))
460             attributeSet = addToAtkAttributeSet(attributeSet, "multiscript-type", "pre");
461         else if (coreObject->isMathMultiscriptObject(PostSuperscript) || coreObject->isMathMultiscriptObject(PostSubscript))
462             attributeSet = addToAtkAttributeSet(attributeSet, "multiscript-type", "post");
463     }
464
465     if (is<AccessibilityTable>(*coreObject) && downcast<AccessibilityTable>(*coreObject).isExposableThroughAccessibility()) {
466         auto& table = downcast<AccessibilityTable>(*coreObject);
467         int rowCount = table.ariaRowCount();
468         if (rowCount)
469             attributeSet = addToAtkAttributeSet(attributeSet, "rowcount", String::number(rowCount).utf8().data());
470
471         int columnCount = table.ariaColumnCount();
472         if (columnCount)
473             attributeSet = addToAtkAttributeSet(attributeSet, "colcount", String::number(columnCount).utf8().data());
474     } else if (is<AccessibilityTableRow>(*coreObject)) {
475         auto& row = downcast<AccessibilityTableRow>(*coreObject);
476         int rowIndex = row.ariaRowIndex();
477         if (rowIndex != -1)
478             attributeSet = addToAtkAttributeSet(attributeSet, "rowindex", String::number(rowIndex).utf8().data());
479     } else if (is<AccessibilityTableCell>(*coreObject)) {
480         auto& cell = downcast<AccessibilityTableCell>(*coreObject);
481         int rowIndex = cell.ariaRowIndex();
482         if (rowIndex != -1)
483             attributeSet = addToAtkAttributeSet(attributeSet, "rowindex", String::number(rowIndex).utf8().data());
484
485         int columnIndex = cell.ariaColumnIndex();
486         if (columnIndex != -1)
487             attributeSet = addToAtkAttributeSet(attributeSet, "colindex", String::number(columnIndex).utf8().data());
488
489         int rowSpan = cell.ariaRowSpan();
490         if (rowSpan != -1)
491             attributeSet = addToAtkAttributeSet(attributeSet, "rowspan", String::number(rowSpan).utf8().data());
492
493         int columnSpan = cell.ariaColumnSpan();
494         if (columnSpan != -1)
495             attributeSet = addToAtkAttributeSet(attributeSet, "colspan", String::number(columnSpan).utf8().data());
496     }
497
498     String placeholder = coreObject->placeholderValue();
499     if (!placeholder.isEmpty())
500         attributeSet = addToAtkAttributeSet(attributeSet, "placeholder-text", placeholder.utf8().data());
501
502     if (coreObject->supportsARIAAutoComplete())
503         attributeSet = addToAtkAttributeSet(attributeSet, "autocomplete", coreObject->ariaAutoCompleteValue().utf8().data());
504
505     if (coreObject->supportsARIAHasPopup())
506         attributeSet = addToAtkAttributeSet(attributeSet, "haspopup", coreObject->ariaPopupValue().utf8().data());
507
508     if (coreObject->supportsARIACurrent())
509         attributeSet = addToAtkAttributeSet(attributeSet, "current", coreObject->ariaCurrentValue().utf8().data());
510
511     AccessibilitySortDirection sortDirection = coreObject->sortDirection();
512     if (sortDirection != SortDirectionNone) {
513         // WAI-ARIA spec says to translate the value as is from the attribute.
514         const AtomicString& sortAttribute = coreObject->getAttribute(HTMLNames::aria_sortAttr);
515         attributeSet = addToAtkAttributeSet(attributeSet, "sort", sortAttribute.string().utf8().data());
516     }
517
518     if (coreObject->supportsARIAPosInSet())
519         attributeSet = addToAtkAttributeSet(attributeSet, "posinset", String::number(coreObject->ariaPosInSet()).utf8().data());
520
521     if (coreObject->supportsARIASetSize())
522         attributeSet = addToAtkAttributeSet(attributeSet, "setsize", String::number(coreObject->ariaSetSize()).utf8().data());
523
524     String isReadOnly = coreObject->ariaReadOnlyValue();
525     if (!isReadOnly.isEmpty())
526         attributeSet = addToAtkAttributeSet(attributeSet, "readonly", isReadOnly.utf8().data());
527
528     String valueDescription = coreObject->valueDescription();
529     if (!valueDescription.isEmpty())
530         attributeSet = addToAtkAttributeSet(attributeSet, "valuetext", valueDescription.utf8().data());
531
532     // According to the W3C Core Accessibility API Mappings 1.1, section 5.4.1 General Rules:
533     // "User agents must expose the WAI-ARIA role string if the API supports a mechanism to do so."
534     // In the case of ATK, the mechanism to do so is an object attribute pair (xml-roles:"string").
535     // We cannot use the computedRoleString for this purpose because it is not limited to elements
536     // with ARIA roles, and it might not contain the actual ARIA role value (e.g. DPub ARIA).
537     String roleString = coreObject->getAttribute(HTMLNames::roleAttr);
538     if (!roleString.isEmpty())
539         attributeSet = addToAtkAttributeSet(attributeSet, "xml-roles", roleString.utf8().data());
540
541     String computedRoleString = coreObject->computedRoleString();
542     if (!computedRoleString.isEmpty()) {
543         attributeSet = addToAtkAttributeSet(attributeSet, "computed-role", computedRoleString.utf8().data());
544
545         // The HTML AAM maps several elements to ARIA landmark roles. In order for the type of landmark
546         // to be obtainable in the same fashion as an ARIA landmark, fall back on the computedRoleString.
547         if (coreObject->ariaRoleAttribute() == UnknownRole && coreObject->isLandmark())
548             attributeSet = addToAtkAttributeSet(attributeSet, "xml-roles", computedRoleString.utf8().data());
549     }
550
551     String roleDescription = coreObject->roleDescription();
552     if (!roleDescription.isEmpty())
553         attributeSet = addToAtkAttributeSet(attributeSet, "roledescription", roleDescription.utf8().data());
554
555     // We need to expose the live region attributes even if the live region is currently disabled/off.
556     if (auto liveContainer = coreObject->ariaLiveRegionAncestor(false)) {
557         String liveStatus = liveContainer->ariaLiveRegionStatus();
558         String relevant = liveContainer->ariaLiveRegionRelevant();
559         bool isAtomic = liveContainer->ariaLiveRegionAtomic();
560         String liveRole = roleString.isEmpty() ? computedRoleString : roleString;
561
562         // According to the Core AAM, we need to expose the above properties with "container-" prefixed
563         // object attributes regardless of whether the container is this object, or an ancestor of it.
564         attributeSet = addToAtkAttributeSet(attributeSet, "container-live", liveStatus.utf8().data());
565         attributeSet = addToAtkAttributeSet(attributeSet, "container-relevant", relevant.utf8().data());
566         if (isAtomic)
567             attributeSet = addToAtkAttributeSet(attributeSet, "container-atomic", "true");
568         if (!liveRole.isEmpty())
569             attributeSet = addToAtkAttributeSet(attributeSet, "container-live-role", liveRole.utf8().data());
570
571         // According to the Core AAM, if this object is the live region (rather than its descendant),
572         // we must expose the above properties on the object without a "container-" prefix.
573         if (liveContainer == coreObject) {
574             attributeSet = addToAtkAttributeSet(attributeSet, "live", liveStatus.utf8().data());
575             attributeSet = addToAtkAttributeSet(attributeSet, "relevant", relevant.utf8().data());
576             if (isAtomic)
577                 attributeSet = addToAtkAttributeSet(attributeSet, "atomic", "true");
578         } else if (!isAtomic && coreObject->ariaLiveRegionAtomic())
579             attributeSet = addToAtkAttributeSet(attributeSet, "atomic", "true");
580     }
581
582     // The Core AAM states the author-provided value should be exposed as-is.
583     String dropEffect = coreObject->getAttribute(HTMLNames::aria_dropeffectAttr);
584     if (!dropEffect.isEmpty())
585         attributeSet = addToAtkAttributeSet(attributeSet, "dropeffect", dropEffect.utf8().data());
586
587     if (coreObject->isARIAGrabbed())
588         attributeSet = addToAtkAttributeSet(attributeSet, "grabbed", "true");
589     else if (coreObject->supportsARIADragging())
590         attributeSet = addToAtkAttributeSet(attributeSet, "grabbed", "false");
591
592     return attributeSet;
593 }
594
595 static AtkRole atkRole(AccessibilityObject* coreObject)
596 {
597     AccessibilityRole role = coreObject->roleValue();
598     switch (role) {
599     case ApplicationAlertRole:
600         return ATK_ROLE_ALERT;
601     case ApplicationAlertDialogRole:
602     case ApplicationDialogRole:
603         return ATK_ROLE_DIALOG;
604     case ApplicationStatusRole:
605         return ATK_ROLE_STATUSBAR;
606     case UnknownRole:
607         return ATK_ROLE_UNKNOWN;
608     case AudioRole:
609 #if ATK_CHECK_VERSION(2, 11, 3)
610         return ATK_ROLE_AUDIO;
611 #endif
612     case VideoRole:
613 #if ATK_CHECK_VERSION(2, 11, 3)
614         return ATK_ROLE_VIDEO;
615 #endif
616         return ATK_ROLE_EMBEDDED;
617     case ButtonRole:
618         return ATK_ROLE_PUSH_BUTTON;
619     case SwitchRole:
620     case ToggleButtonRole:
621         return ATK_ROLE_TOGGLE_BUTTON;
622     case RadioButtonRole:
623         return ATK_ROLE_RADIO_BUTTON;
624     case CheckBoxRole:
625         return ATK_ROLE_CHECK_BOX;
626     case SliderRole:
627         return ATK_ROLE_SLIDER;
628     case TabGroupRole:
629     case TabListRole:
630         return ATK_ROLE_PAGE_TAB_LIST;
631     case TextFieldRole:
632     case TextAreaRole:
633     case SearchFieldRole:
634         return ATK_ROLE_ENTRY;
635     case StaticTextRole:
636 #if ATK_CHECK_VERSION(2, 15, 2)
637         return ATK_ROLE_STATIC;
638 #else
639         return ATK_ROLE_TEXT;
640 #endif
641     case OutlineRole:
642     case TreeRole:
643         return ATK_ROLE_TREE;
644     case TreeItemRole:
645         return ATK_ROLE_TREE_ITEM;
646     case MenuBarRole:
647         return ATK_ROLE_MENU_BAR;
648     case MenuListPopupRole:
649     case MenuRole:
650         return ATK_ROLE_MENU;
651     case MenuListOptionRole:
652     case MenuItemRole:
653     case MenuButtonRole:
654         return ATK_ROLE_MENU_ITEM;
655     case MenuItemCheckboxRole:
656         return ATK_ROLE_CHECK_MENU_ITEM;
657     case MenuItemRadioRole:
658         return ATK_ROLE_RADIO_MENU_ITEM;
659     case ColumnRole:
660         // return ATK_ROLE_TABLE_COLUMN_HEADER; // Is this right?
661         return ATK_ROLE_UNKNOWN; // Matches Mozilla
662     case RowRole:
663         return ATK_ROLE_TABLE_ROW;
664     case ToolbarRole:
665         return ATK_ROLE_TOOL_BAR;
666     case BusyIndicatorRole:
667         return ATK_ROLE_PROGRESS_BAR; // Is this right?
668     case ProgressIndicatorRole:
669         return coreObject->isMeter() ? ATK_ROLE_LEVEL_BAR : ATK_ROLE_PROGRESS_BAR;
670     case WindowRole:
671         return ATK_ROLE_WINDOW;
672     case PopUpButtonRole:
673         return coreObject->ariaHasPopup() ? ATK_ROLE_PUSH_BUTTON : ATK_ROLE_COMBO_BOX;
674     case ComboBoxRole:
675         return ATK_ROLE_COMBO_BOX;
676     case SplitGroupRole:
677         return ATK_ROLE_SPLIT_PANE;
678     case SplitterRole:
679         return ATK_ROLE_SEPARATOR;
680     case ColorWellRole:
681 #if PLATFORM(GTK)
682         // ATK_ROLE_COLOR_CHOOSER is defined as a dialog (i.e. it's what appears when you push the button).
683         return ATK_ROLE_PUSH_BUTTON;
684 #endif
685     case ListRole:
686         return ATK_ROLE_LIST;
687     case ScrollBarRole:
688         return ATK_ROLE_SCROLL_BAR;
689     case ScrollAreaRole:
690     case TabPanelRole:
691         return ATK_ROLE_SCROLL_PANE;
692     case GridRole:
693     case TableRole:
694         return ATK_ROLE_TABLE;
695     case TreeGridRole:
696         return ATK_ROLE_TREE_TABLE;
697     case ApplicationRole:
698         return ATK_ROLE_APPLICATION;
699     case ApplicationGroupRole:
700     case FeedRole:
701     case FigureRole:
702     case GroupRole:
703     case RadioGroupRole:
704     case SVGRootRole:
705         return ATK_ROLE_PANEL;
706     case RowHeaderRole:
707         return ATK_ROLE_ROW_HEADER;
708     case ColumnHeaderRole:
709         return ATK_ROLE_COLUMN_HEADER;
710     case CaptionRole:
711         return ATK_ROLE_CAPTION;
712     case CellRole:
713     case GridCellRole:
714         return coreObject->inheritsPresentationalRole() ? ATK_ROLE_SECTION : ATK_ROLE_TABLE_CELL;
715     case LinkRole:
716     case WebCoreLinkRole:
717     case ImageMapLinkRole:
718         return ATK_ROLE_LINK;
719     case ImageMapRole:
720         return ATK_ROLE_IMAGE_MAP;
721     case ImageRole:
722         return ATK_ROLE_IMAGE;
723     case ListMarkerRole:
724         return ATK_ROLE_TEXT;
725     case DocumentArticleRole:
726 #if ATK_CHECK_VERSION(2, 11, 3)
727         return ATK_ROLE_ARTICLE;
728 #endif
729     case DocumentRole:
730         return ATK_ROLE_DOCUMENT_FRAME;
731     case DocumentNoteRole:
732         return ATK_ROLE_COMMENT;
733     case HeadingRole:
734         return ATK_ROLE_HEADING;
735     case ListBoxRole:
736         // https://rawgit.com/w3c/aria/master/core-aam/core-aam.html#role-map-listbox
737         return coreObject->isDescendantOfRole(ComboBoxRole) ? ATK_ROLE_MENU : ATK_ROLE_LIST_BOX;
738     case ListItemRole:
739         return coreObject->inheritsPresentationalRole() ? ATK_ROLE_SECTION : ATK_ROLE_LIST_ITEM;
740     case ListBoxOptionRole:
741         return coreObject->isDescendantOfRole(ComboBoxRole) ? ATK_ROLE_MENU_ITEM : ATK_ROLE_LIST_ITEM;
742     case ParagraphRole:
743         return ATK_ROLE_PARAGRAPH;
744     case LabelRole:
745     case LegendRole:
746         return ATK_ROLE_LABEL;
747     case BlockquoteRole:
748 #if ATK_CHECK_VERSION(2, 11, 3)
749         return ATK_ROLE_BLOCK_QUOTE;
750 #endif
751     case FootnoteRole:
752 #if ATK_CHECK_VERSION(2, 25, 2)
753         return ATK_ROLE_FOOTNOTE;
754 #endif
755     case ApplicationTextGroupRole:
756     case DivRole:
757     case PreRole:
758     case SVGTextRole:
759     case TextGroupRole:
760         return ATK_ROLE_SECTION;
761     case FooterRole:
762         return ATK_ROLE_FOOTER;
763     case FormRole:
764         return ATK_ROLE_FORM;
765     case CanvasRole:
766         return ATK_ROLE_CANVAS;
767     case HorizontalRuleRole:
768         return ATK_ROLE_SEPARATOR;
769     case SpinButtonRole:
770         return ATK_ROLE_SPIN_BUTTON;
771     case TabRole:
772         return ATK_ROLE_PAGE_TAB;
773     case UserInterfaceTooltipRole:
774         return ATK_ROLE_TOOL_TIP;
775     case WebAreaRole:
776         return ATK_ROLE_DOCUMENT_WEB;
777     case WebApplicationRole:
778         return ATK_ROLE_EMBEDDED;
779 #if ATK_CHECK_VERSION(2, 11, 3)
780     case ApplicationLogRole:
781         return ATK_ROLE_LOG;
782     case ApplicationMarqueeRole:
783         return ATK_ROLE_MARQUEE;
784     case ApplicationTimerRole:
785         return ATK_ROLE_TIMER;
786     case DefinitionRole:
787         return ATK_ROLE_DEFINITION;
788     case DocumentMathRole:
789         return ATK_ROLE_MATH;
790     case MathElementRole:
791         if (coreObject->isMathRow())
792             return ATK_ROLE_PANEL;
793         if (coreObject->isMathTable())
794             return ATK_ROLE_TABLE;
795         if (coreObject->isMathTableRow())
796             return ATK_ROLE_TABLE_ROW;
797         if (coreObject->isMathTableCell())
798             return ATK_ROLE_TABLE_CELL;
799         if (coreObject->isMathSubscriptSuperscript() || coreObject->isMathMultiscript())
800             return ATK_ROLE_SECTION;
801 #if ATK_CHECK_VERSION(2, 15, 4)
802         if (coreObject->isMathFraction())
803             return ATK_ROLE_MATH_FRACTION;
804         if (coreObject->isMathSquareRoot() || coreObject->isMathRoot())
805             return ATK_ROLE_MATH_ROOT;
806         if (coreObject->isMathScriptObject(Subscript)
807             || coreObject->isMathMultiscriptObject(PreSubscript) || coreObject->isMathMultiscriptObject(PostSubscript))
808             return ATK_ROLE_SUBSCRIPT;
809         if (coreObject->isMathScriptObject(Superscript)
810             || coreObject->isMathMultiscriptObject(PreSuperscript) || coreObject->isMathMultiscriptObject(PostSuperscript))
811             return ATK_ROLE_SUPERSCRIPT;
812 #endif
813 #if ATK_CHECK_VERSION(2, 15, 2)
814         if (coreObject->isMathToken())
815             return ATK_ROLE_STATIC;
816 #endif
817         return ATK_ROLE_UNKNOWN;
818     case LandmarkBannerRole:
819     case LandmarkComplementaryRole:
820     case LandmarkContentInfoRole:
821     case LandmarkDocRegionRole:
822     case LandmarkMainRole:
823     case LandmarkNavigationRole:
824     case LandmarkRegionRole:
825     case LandmarkSearchRole:
826         return ATK_ROLE_LANDMARK;
827 #endif
828 #if ATK_CHECK_VERSION(2, 11, 4)
829     case DescriptionListRole:
830         return ATK_ROLE_DESCRIPTION_LIST;
831     case TermRole:
832     case DescriptionListTermRole:
833         return ATK_ROLE_DESCRIPTION_TERM;
834     case DescriptionListDetailRole:
835         return ATK_ROLE_DESCRIPTION_VALUE;
836 #endif
837     case InlineRole:
838 #if ATK_CHECK_VERSION(2, 15, 4)
839         if (coreObject->isSubscriptStyleGroup())
840             return ATK_ROLE_SUBSCRIPT;
841         if (coreObject->isSuperscriptStyleGroup())
842             return ATK_ROLE_SUPERSCRIPT;
843 #endif
844 #if ATK_CHECK_VERSION(2, 15, 2)
845         return ATK_ROLE_STATIC;
846     case SVGTextPathRole:
847     case SVGTSpanRole:
848     case TimeRole:
849         return ATK_ROLE_STATIC;
850 #endif
851     default:
852         return ATK_ROLE_UNKNOWN;
853     }
854 }
855
856 static AtkRole webkitAccessibleGetRole(AtkObject* object)
857 {
858     // ATK_ROLE_UNKNOWN should only be applied in cases where there is a valid
859     // WebCore accessible object for which the platform role mapping is unknown.
860     g_return_val_if_fail(WEBKIT_IS_ACCESSIBLE(object), ATK_ROLE_INVALID);
861     returnValIfWebKitAccessibleIsInvalid(WEBKIT_ACCESSIBLE(object), ATK_ROLE_INVALID);
862
863     AccessibilityObject* coreObject = core(object);
864
865     if (!coreObject)
866         return ATK_ROLE_INVALID;
867
868     // Note: Why doesn't WebCore have a password field for this
869     if (coreObject->isPasswordField())
870         return ATK_ROLE_PASSWORD_TEXT;
871
872     return atkRole(coreObject);
873 }
874
875 static bool isTextWithCaret(AccessibilityObject* coreObject)
876 {
877     if (!coreObject || !coreObject->isAccessibilityRenderObject())
878         return false;
879
880     Document* document = coreObject->document();
881     if (!document)
882         return false;
883
884     Frame* frame = document->frame();
885     if (!frame)
886         return false;
887
888     if (!frame->settings().caretBrowsingEnabled())
889         return false;
890
891     // Check text objects and paragraphs only.
892     AtkObject* axObject = coreObject->wrapper();
893     AtkRole role = axObject ? atk_object_get_role(axObject) : ATK_ROLE_INVALID;
894     if (role != ATK_ROLE_TEXT && role != ATK_ROLE_PARAGRAPH)
895         return false;
896
897     // Finally, check whether the caret is set in the current object.
898     VisibleSelection selection = coreObject->selection();
899     if (!selection.isCaret())
900         return false;
901
902     return selectionBelongsToObject(coreObject, selection);
903 }
904
905 static void setAtkStateSetFromCoreObject(AccessibilityObject* coreObject, AtkStateSet* stateSet)
906 {
907     AccessibilityObject* parent = coreObject->parentObject();
908     bool isListBoxOption = parent && parent->isListBox();
909
910     // Please keep the state list in alphabetical order
911     if ((isListBoxOption && coreObject->isSelectedOptionActive())
912         || coreObject->ariaCurrentState() != ARIACurrentFalse)
913         atk_state_set_add_state(stateSet, ATK_STATE_ACTIVE);
914
915     if (coreObject->isBusy())
916         atk_state_set_add_state(stateSet, ATK_STATE_BUSY);
917
918 #if ATK_CHECK_VERSION(2,11,2)
919     if (coreObject->supportsChecked() && coreObject->canSetValueAttribute())
920         atk_state_set_add_state(stateSet, ATK_STATE_CHECKABLE);
921 #endif
922
923     if (coreObject->isChecked())
924         atk_state_set_add_state(stateSet, ATK_STATE_CHECKED);
925
926     if ((coreObject->isTextControl() || coreObject->isNonNativeTextControl()) && coreObject->canSetValueAttribute())
927         atk_state_set_add_state(stateSet, ATK_STATE_EDITABLE);
928
929     // FIXME: Put both ENABLED and SENSITIVE together here for now
930     if (coreObject->isEnabled()) {
931         atk_state_set_add_state(stateSet, ATK_STATE_ENABLED);
932         atk_state_set_add_state(stateSet, ATK_STATE_SENSITIVE);
933     }
934
935     if (coreObject->canSetExpandedAttribute())
936         atk_state_set_add_state(stateSet, ATK_STATE_EXPANDABLE);
937
938     if (coreObject->isExpanded())
939         atk_state_set_add_state(stateSet, ATK_STATE_EXPANDED);
940
941     if (coreObject->canSetFocusAttribute())
942         atk_state_set_add_state(stateSet, ATK_STATE_FOCUSABLE);
943
944     if (coreObject->isFocused() || isTextWithCaret(coreObject))
945         atk_state_set_add_state(stateSet, ATK_STATE_FOCUSED);
946
947     if (coreObject->orientation() == AccessibilityOrientationHorizontal)
948         atk_state_set_add_state(stateSet, ATK_STATE_HORIZONTAL);
949     else if (coreObject->orientation() == AccessibilityOrientationVertical)
950         atk_state_set_add_state(stateSet, ATK_STATE_VERTICAL);
951
952     if (coreObject->ariaHasPopup())
953         atk_state_set_add_state(stateSet, ATK_STATE_HAS_POPUP);
954
955     if (coreObject->isIndeterminate())
956         atk_state_set_add_state(stateSet, ATK_STATE_INDETERMINATE);
957     else if (coreObject->isCheckboxOrRadio() || coreObject->isMenuItem() || coreObject->isToggleButton()) {
958         if (coreObject->checkboxOrRadioValue() == ButtonStateMixed)
959             atk_state_set_add_state(stateSet, ATK_STATE_INDETERMINATE);
960     }
961
962     if (coreObject->isAriaModalNode())
963         atk_state_set_add_state(stateSet, ATK_STATE_MODAL);
964
965     if (coreObject->invalidStatus() != "false")
966         atk_state_set_add_state(stateSet, ATK_STATE_INVALID_ENTRY);
967
968     if (coreObject->isMultiSelectable())
969         atk_state_set_add_state(stateSet, ATK_STATE_MULTISELECTABLE);
970
971     // TODO: ATK_STATE_OPAQUE
972
973     if (coreObject->isPressed())
974         atk_state_set_add_state(stateSet, ATK_STATE_PRESSED);
975
976 #if ATK_CHECK_VERSION(2,15,3)
977     if (!coreObject->canSetValueAttribute() && (coreObject->supportsARIAReadOnly()))
978         atk_state_set_add_state(stateSet, ATK_STATE_READ_ONLY);
979 #endif
980
981     if (coreObject->isRequired())
982         atk_state_set_add_state(stateSet, ATK_STATE_REQUIRED);
983
984     // TODO: ATK_STATE_SELECTABLE_TEXT
985
986     if (coreObject->canSetSelectedAttribute()) {
987         atk_state_set_add_state(stateSet, ATK_STATE_SELECTABLE);
988         // Items in focusable lists have both STATE_SELECT{ABLE,ED}
989         // and STATE_FOCUS{ABLE,ED}. We'll fake the latter based on
990         // the former.
991         if (isListBoxOption)
992             atk_state_set_add_state(stateSet, ATK_STATE_FOCUSABLE);
993     }
994
995     if (coreObject->isSelected()) {
996         atk_state_set_add_state(stateSet, ATK_STATE_SELECTED);
997         // Items in focusable lists have both STATE_SELECT{ABLE,ED}
998         // and STATE_FOCUS{ABLE,ED}. We'll fake the latter based on the
999         // former.
1000         if (isListBoxOption)
1001             atk_state_set_add_state(stateSet, ATK_STATE_FOCUSED);
1002     }
1003
1004     // FIXME: Group both SHOWING and VISIBLE here for now
1005     // Not sure how to handle this in WebKit, see bug
1006     // http://bugzilla.gnome.org/show_bug.cgi?id=509650 for other
1007     // issues with SHOWING vs VISIBLE.
1008     if (!coreObject->isOffScreen()) {
1009         atk_state_set_add_state(stateSet, ATK_STATE_SHOWING);
1010         atk_state_set_add_state(stateSet, ATK_STATE_VISIBLE);
1011     }
1012
1013     // Mutually exclusive, so we group these two
1014     if (coreObject->roleValue() == TextAreaRole || coreObject->ariaIsMultiline())
1015         atk_state_set_add_state(stateSet, ATK_STATE_MULTI_LINE);
1016     else if (coreObject->roleValue() == TextFieldRole || coreObject->roleValue() == SearchFieldRole)
1017         atk_state_set_add_state(stateSet, ATK_STATE_SINGLE_LINE);
1018
1019     // TODO: ATK_STATE_SENSITIVE
1020
1021     if (coreObject->supportsARIAAutoComplete() && coreObject->ariaAutoCompleteValue() != "none")
1022         atk_state_set_add_state(stateSet, ATK_STATE_SUPPORTS_AUTOCOMPLETION);
1023
1024     if (coreObject->isVisited())
1025         atk_state_set_add_state(stateSet, ATK_STATE_VISITED);
1026 }
1027
1028 static AtkStateSet* webkitAccessibleRefStateSet(AtkObject* object)
1029 {
1030     g_return_val_if_fail(WEBKIT_IS_ACCESSIBLE(object), 0);
1031
1032     AtkStateSet* stateSet = ATK_OBJECT_CLASS(webkitAccessibleParentClass)->ref_state_set(object);
1033     AccessibilityObject* coreObject = core(object);
1034
1035     // Make sure the layout is updated to really know whether the object
1036     // is defunct or not, so we can return the proper state.
1037     coreObject->updateBackingStore();
1038
1039     if (coreObject == fallbackObject()) {
1040         atk_state_set_add_state(stateSet, ATK_STATE_DEFUNCT);
1041         return stateSet;
1042     }
1043
1044     // Text objects must be focusable.
1045     AtkRole role = atk_object_get_role(object);
1046     if (role == ATK_ROLE_TEXT || role == ATK_ROLE_PARAGRAPH)
1047         atk_state_set_add_state(stateSet, ATK_STATE_FOCUSABLE);
1048
1049     setAtkStateSetFromCoreObject(coreObject, stateSet);
1050     return stateSet;
1051 }
1052
1053 static AtkRelationSet* webkitAccessibleRefRelationSet(AtkObject* object)
1054 {
1055     g_return_val_if_fail(WEBKIT_IS_ACCESSIBLE(object), 0);
1056     returnValIfWebKitAccessibleIsInvalid(WEBKIT_ACCESSIBLE(object), 0);
1057
1058     AtkRelationSet* relationSet = ATK_OBJECT_CLASS(webkitAccessibleParentClass)->ref_relation_set(object);
1059     AccessibilityObject* coreObject = core(object);
1060
1061     setAtkRelationSetFromCoreObject(coreObject, relationSet);
1062
1063     return relationSet;
1064 }
1065
1066 static void webkitAccessibleInit(AtkObject* object, gpointer data)
1067 {
1068     if (ATK_OBJECT_CLASS(webkitAccessibleParentClass)->initialize)
1069         ATK_OBJECT_CLASS(webkitAccessibleParentClass)->initialize(object, data);
1070
1071     WebKitAccessible* accessible = WEBKIT_ACCESSIBLE(object);
1072     accessible->m_object = reinterpret_cast<AccessibilityObject*>(data);
1073     accessible->priv = WEBKIT_ACCESSIBLE_GET_PRIVATE(accessible);
1074 }
1075
1076 static const gchar* webkitAccessibleGetObjectLocale(AtkObject* object)
1077 {
1078     g_return_val_if_fail(WEBKIT_IS_ACCESSIBLE(object), 0);
1079     returnValIfWebKitAccessibleIsInvalid(WEBKIT_ACCESSIBLE(object), 0);
1080
1081     AccessibilityObject* coreObject = core(object);
1082     if (!coreObject)
1083         return 0;
1084
1085     if (ATK_IS_DOCUMENT(object)) {
1086         // TODO: Should we fall back on lang xml:lang when the following comes up empty?
1087         String language = coreObject->language();
1088         if (!language.isEmpty())
1089             return cacheAndReturnAtkProperty(object, AtkCachedDocumentLocale, language);
1090
1091     } else if (ATK_IS_TEXT(object)) {
1092         const gchar* locale = nullptr;
1093
1094         AtkAttributeSet* textAttributes = atk_text_get_default_attributes(ATK_TEXT(object));
1095         for (AtkAttributeSet* attributes = textAttributes; attributes; attributes = attributes->next) {
1096             AtkAttribute* atkAttribute = static_cast<AtkAttribute*>(attributes->data);
1097             if (!strcmp(atkAttribute->name, atk_text_attribute_get_name(ATK_TEXT_ATTR_LANGUAGE))) {
1098                 locale = cacheAndReturnAtkProperty(object, AtkCachedDocumentLocale, String::fromUTF8(atkAttribute->value));
1099                 break;
1100             }
1101         }
1102         atk_attribute_set_free(textAttributes);
1103
1104         return locale;
1105     }
1106
1107     return 0;
1108 }
1109
1110 static void webkitAccessibleFinalize(GObject* object)
1111 {
1112     G_OBJECT_CLASS(webkitAccessibleParentClass)->finalize(object);
1113 }
1114
1115 static void webkitAccessibleClassInit(AtkObjectClass* klass)
1116 {
1117     GObjectClass* gobjectClass = G_OBJECT_CLASS(klass);
1118
1119     webkitAccessibleParentClass = g_type_class_peek_parent(klass);
1120
1121     gobjectClass->finalize = webkitAccessibleFinalize;
1122
1123     klass->initialize = webkitAccessibleInit;
1124     klass->get_name = webkitAccessibleGetName;
1125     klass->get_description = webkitAccessibleGetDescription;
1126     klass->get_parent = webkitAccessibleGetParent;
1127     klass->get_n_children = webkitAccessibleGetNChildren;
1128     klass->ref_child = webkitAccessibleRefChild;
1129     klass->get_role = webkitAccessibleGetRole;
1130     klass->ref_state_set = webkitAccessibleRefStateSet;
1131     klass->get_index_in_parent = webkitAccessibleGetIndexInParent;
1132     klass->get_attributes = webkitAccessibleGetAttributes;
1133     klass->ref_relation_set = webkitAccessibleRefRelationSet;
1134     klass->get_object_locale = webkitAccessibleGetObjectLocale;
1135
1136     g_type_class_add_private(klass, sizeof(WebKitAccessiblePrivate));
1137 }
1138
1139 GType
1140 webkitAccessibleGetType(void)
1141 {
1142     static volatile gsize typeVolatile = 0;
1143
1144     if (g_once_init_enter(&typeVolatile)) {
1145         static const GTypeInfo tinfo = {
1146             sizeof(WebKitAccessibleClass),
1147             (GBaseInitFunc) 0,
1148             (GBaseFinalizeFunc) 0,
1149             (GClassInitFunc) webkitAccessibleClassInit,
1150             (GClassFinalizeFunc) 0,
1151             0, /* class data */
1152             sizeof(WebKitAccessible), /* instance size */
1153             0, /* nb preallocs */
1154             (GInstanceInitFunc) 0,
1155             0 /* value table */
1156         };
1157
1158         GType type = g_type_register_static(ATK_TYPE_OBJECT, "WebKitAccessible", &tinfo, GTypeFlags(0));
1159         g_once_init_leave(&typeVolatile, type);
1160     }
1161
1162     return typeVolatile;
1163 }
1164
1165 static const GInterfaceInfo AtkInterfacesInitFunctions[] = {
1166     {reinterpret_cast<GInterfaceInitFunc>(webkitAccessibleActionInterfaceInit), 0, 0},
1167     {reinterpret_cast<GInterfaceInitFunc>(webkitAccessibleSelectionInterfaceInit), 0, 0},
1168     {reinterpret_cast<GInterfaceInitFunc>(webkitAccessibleEditableTextInterfaceInit), 0, 0},
1169     {reinterpret_cast<GInterfaceInitFunc>(webkitAccessibleTextInterfaceInit), 0, 0},
1170     {reinterpret_cast<GInterfaceInitFunc>(webkitAccessibleComponentInterfaceInit), 0, 0},
1171     {reinterpret_cast<GInterfaceInitFunc>(webkitAccessibleImageInterfaceInit), 0, 0},
1172     {reinterpret_cast<GInterfaceInitFunc>(webkitAccessibleTableInterfaceInit), 0, 0},
1173 #if ATK_CHECK_VERSION(2,11,90)
1174     {reinterpret_cast<GInterfaceInitFunc>(webkitAccessibleTableCellInterfaceInit), 0, 0},
1175 #endif
1176     {reinterpret_cast<GInterfaceInitFunc>(webkitAccessibleHypertextInterfaceInit), 0, 0},
1177     {reinterpret_cast<GInterfaceInitFunc>(webkitAccessibleHyperlinkImplInterfaceInit), 0, 0},
1178     {reinterpret_cast<GInterfaceInitFunc>(webkitAccessibleDocumentInterfaceInit), 0, 0},
1179     {reinterpret_cast<GInterfaceInitFunc>(webkitAccessibleValueInterfaceInit), 0, 0}
1180 };
1181
1182 enum WAIType {
1183     WAIAction,
1184     WAISelection,
1185     WAIEditableText,
1186     WAIText,
1187     WAIComponent,
1188     WAIImage,
1189     WAITable,
1190 #if ATK_CHECK_VERSION(2,11,90)
1191     WAITableCell,
1192 #endif
1193     WAIHypertext,
1194     WAIHyperlink,
1195     WAIDocument,
1196     WAIValue,
1197 };
1198
1199 static GType GetAtkInterfaceTypeFromWAIType(WAIType type)
1200 {
1201     switch (type) {
1202     case WAIAction:
1203         return ATK_TYPE_ACTION;
1204     case WAISelection:
1205         return ATK_TYPE_SELECTION;
1206     case WAIEditableText:
1207         return ATK_TYPE_EDITABLE_TEXT;
1208     case WAIText:
1209         return ATK_TYPE_TEXT;
1210     case WAIComponent:
1211         return ATK_TYPE_COMPONENT;
1212     case WAIImage:
1213         return ATK_TYPE_IMAGE;
1214     case WAITable:
1215         return ATK_TYPE_TABLE;
1216 #if ATK_CHECK_VERSION(2,11,90)
1217     case WAITableCell:
1218         return ATK_TYPE_TABLE_CELL;
1219 #endif
1220     case WAIHypertext:
1221         return ATK_TYPE_HYPERTEXT;
1222     case WAIHyperlink:
1223         return ATK_TYPE_HYPERLINK_IMPL;
1224     case WAIDocument:
1225         return ATK_TYPE_DOCUMENT;
1226     case WAIValue:
1227         return ATK_TYPE_VALUE;
1228     }
1229
1230     return G_TYPE_INVALID;
1231 }
1232
1233 static bool roleIsTextType(AccessibilityRole role)
1234 {
1235     return role == ParagraphRole || role == HeadingRole || role == DivRole || role == CellRole
1236         || role == LinkRole || role == WebCoreLinkRole || role == ListItemRole || role == PreRole
1237         || role == GridCellRole || role == TextGroupRole || role == ApplicationTextGroupRole;
1238 }
1239
1240 static guint16 getInterfaceMaskFromObject(AccessibilityObject* coreObject)
1241 {
1242     guint16 interfaceMask = 0;
1243
1244     // Component interface is always supported
1245     interfaceMask |= 1 << WAIComponent;
1246
1247     AccessibilityRole role = coreObject->roleValue();
1248
1249     // Action
1250     // As the implementation of the AtkAction interface is a very
1251     // basic one (just relays in executing the default action for each
1252     // object, and only supports having one action per object), it is
1253     // better just to implement this interface for every instance of
1254     // the WebKitAccessible class and let WebCore decide what to do.
1255     interfaceMask |= 1 << WAIAction;
1256
1257     // Selection
1258     if (coreObject->canHaveSelectedChildren() || coreObject->isMenuList())
1259         interfaceMask |= 1 << WAISelection;
1260
1261     // Get renderer if available.
1262     RenderObject* renderer = nullptr;
1263     if (coreObject->isAccessibilityRenderObject())
1264         renderer = coreObject->renderer();
1265
1266     // Hyperlink (links and embedded objects).
1267     if (coreObject->isLink() || (renderer && renderer->isReplaced()))
1268         interfaceMask |= 1 << WAIHyperlink;
1269
1270     // Text, Editable Text & Hypertext
1271     if (role == StaticTextRole || coreObject->isMenuListOption())
1272         interfaceMask |= 1 << WAIText;
1273     else if (coreObject->isTextControl() || coreObject->isNonNativeTextControl()) {
1274         interfaceMask |= 1 << WAIText;
1275         if (coreObject->canSetValueAttribute())
1276             interfaceMask |= 1 << WAIEditableText;
1277     } else if (!coreObject->isWebArea()) {
1278         if (role != TableRole) {
1279             interfaceMask |= 1 << WAIHypertext;
1280             if ((renderer && renderer->childrenInline()) || roleIsTextType(role) || coreObject->isMathToken())
1281                 interfaceMask |= 1 << WAIText;
1282         }
1283
1284         // Add the TEXT interface for list items whose
1285         // first accessible child has a text renderer
1286         if (role == ListItemRole) {
1287             const AccessibilityObject::AccessibilityChildrenVector& children = coreObject->children();
1288             if (children.size()) {
1289                 AccessibilityObject* axRenderChild = children.at(0).get();
1290                 interfaceMask |= getInterfaceMaskFromObject(axRenderChild);
1291             }
1292         }
1293     }
1294
1295     // Image
1296     if (coreObject->isImage())
1297         interfaceMask |= 1 << WAIImage;
1298
1299     // Table
1300     if (coreObject->isTable())
1301         interfaceMask |= 1 << WAITable;
1302
1303 #if ATK_CHECK_VERSION(2,11,90)
1304     if (role == CellRole || role == GridCellRole || role == ColumnHeaderRole || role == RowHeaderRole)
1305         interfaceMask |= 1 << WAITableCell;
1306 #endif
1307
1308     // Document
1309     if (role == WebAreaRole)
1310         interfaceMask |= 1 << WAIDocument;
1311
1312     // Value
1313     if (coreObject->supportsRangeValue())
1314         interfaceMask |= 1 << WAIValue;
1315
1316 #if ENABLE(INPUT_TYPE_COLOR)
1317     // Color type.
1318     if (role == ColorWellRole)
1319         interfaceMask |= 1 << WAIText;
1320 #endif
1321
1322     return interfaceMask;
1323 }
1324
1325 static const char* getUniqueAccessibilityTypeName(guint16 interfaceMask)
1326 {
1327 #define WAI_TYPE_NAME_LEN (30) /* Enough for prefix + 5 hex characters (max) */
1328     static char name[WAI_TYPE_NAME_LEN + 1];
1329
1330     g_sprintf(name, "WAIType%x", interfaceMask);
1331     name[WAI_TYPE_NAME_LEN] = '\0';
1332
1333     return name;
1334 }
1335
1336 static GType getAccessibilityTypeFromObject(AccessibilityObject* coreObject)
1337 {
1338     static const GTypeInfo typeInfo = {
1339         sizeof(WebKitAccessibleClass),
1340         (GBaseInitFunc) 0,
1341         (GBaseFinalizeFunc) 0,
1342         (GClassInitFunc) 0,
1343         (GClassFinalizeFunc) 0,
1344         0, /* class data */
1345         sizeof(WebKitAccessible), /* instance size */
1346         0, /* nb preallocs */
1347         (GInstanceInitFunc) 0,
1348         0 /* value table */
1349     };
1350
1351     guint16 interfaceMask = getInterfaceMaskFromObject(coreObject);
1352     const char* atkTypeName = getUniqueAccessibilityTypeName(interfaceMask);
1353     GType type = g_type_from_name(atkTypeName);
1354     if (type)
1355         return type;
1356
1357     type = g_type_register_static(WEBKIT_TYPE_ACCESSIBLE, atkTypeName, &typeInfo, GTypeFlags(0));
1358     for (guint i = 0; i < G_N_ELEMENTS(AtkInterfacesInitFunctions); i++) {
1359         if (interfaceMask & (1 << i))
1360             g_type_add_interface_static(type,
1361                 GetAtkInterfaceTypeFromWAIType(static_cast<WAIType>(i)),
1362                 &AtkInterfacesInitFunctions[i]);
1363     }
1364
1365     return type;
1366 }
1367
1368 WebKitAccessible* webkitAccessibleNew(AccessibilityObject* coreObject)
1369 {
1370     GType type = getAccessibilityTypeFromObject(coreObject);
1371     AtkObject* object = static_cast<AtkObject*>(g_object_new(type, 0));
1372
1373     atk_object_initialize(object, coreObject);
1374
1375     return WEBKIT_ACCESSIBLE(object);
1376 }
1377
1378 AccessibilityObject* webkitAccessibleGetAccessibilityObject(WebKitAccessible* accessible)
1379 {
1380     return accessible->m_object;
1381 }
1382
1383 void webkitAccessibleDetach(WebKitAccessible* accessible)
1384 {
1385     ASSERT(accessible->m_object);
1386
1387     if (accessible->m_object->roleValue() == WebAreaRole)
1388         atk_object_notify_state_change(ATK_OBJECT(accessible), ATK_STATE_DEFUNCT, true);
1389
1390     // We replace the WebCore AccessibilityObject with a fallback object that
1391     // provides default implementations to avoid repetitive null-checking after
1392     // detachment.
1393     accessible->m_object = fallbackObject();
1394 }
1395
1396 bool webkitAccessibleIsDetached(WebKitAccessible* accessible)
1397 {
1398     ASSERT(accessible->m_object);
1399     return accessible->m_object == fallbackObject();
1400 }
1401
1402 AccessibilityObject* objectFocusedAndCaretOffsetUnignored(AccessibilityObject* referenceObject, int& offset)
1403 {
1404     // Indication that something bogus has transpired.
1405     offset = -1;
1406
1407     Document* document = referenceObject->document();
1408     if (!document)
1409         return 0;
1410
1411     Node* focusedNode = referenceObject->selection().end().containerNode();
1412     if (!focusedNode)
1413         return 0;
1414
1415     RenderObject* focusedRenderer = focusedNode->renderer();
1416     if (!focusedRenderer)
1417         return 0;
1418
1419     AccessibilityObject* focusedObject = document->axObjectCache()->getOrCreate(focusedRenderer);
1420     if (!focusedObject)
1421         return 0;
1422
1423     // Look for the actual (not ignoring accessibility) selected object.
1424     AccessibilityObject* firstUnignoredParent = focusedObject;
1425     if (firstUnignoredParent->accessibilityIsIgnored())
1426         firstUnignoredParent = firstUnignoredParent->parentObjectUnignored();
1427     if (!firstUnignoredParent)
1428         return 0;
1429
1430     // Don't ignore links if the offset is being requested for a link
1431     // or if the link is a block.
1432     if (!referenceObject->isLink() && firstUnignoredParent->isLink()
1433         && !(firstUnignoredParent->renderer() && !firstUnignoredParent->renderer()->isInline()))
1434         firstUnignoredParent = firstUnignoredParent->parentObjectUnignored();
1435     if (!firstUnignoredParent)
1436         return 0;
1437
1438     // The reference object must either coincide with the focused
1439     // object being considered, or be a descendant of it.
1440     if (referenceObject->isDescendantOfObject(firstUnignoredParent))
1441         referenceObject = firstUnignoredParent;
1442
1443     Node* startNode = nullptr;
1444     if (firstUnignoredParent != referenceObject || firstUnignoredParent->isTextControl()) {
1445         // We need to use the first child's node of the reference
1446         // object as the start point to calculate the caret offset
1447         // because we want it to be relative to the object of
1448         // reference, not just to the focused object (which could have
1449         // previous siblings which should be taken into account too).
1450         AccessibilityObject* axFirstChild = referenceObject->firstChild();
1451         if (axFirstChild)
1452             startNode = axFirstChild->node();
1453     }
1454     // Getting the Position of a PseudoElement now triggers an assertion.
1455     // This can occur when clicking on empty space in a render block.
1456     if (!startNode || startNode->isPseudoElement())
1457         startNode = firstUnignoredParent->node();
1458
1459     // Check if the node for the first parent object not ignoring
1460     // accessibility is null again before using it. This might happen
1461     // with certain kind of accessibility objects, such as the root
1462     // one (the scroller containing the webArea object).
1463     if (!startNode)
1464         return 0;
1465
1466     VisiblePosition startPosition = VisiblePosition(positionBeforeNode(startNode), DOWNSTREAM);
1467     VisiblePosition endPosition = firstUnignoredParent->selection().visibleEnd();
1468
1469     if (startPosition == endPosition)
1470         offset = 0;
1471     else if (!isStartOfLine(endPosition)) {
1472         RefPtr<Range> range = makeRange(startPosition, endPosition.previous());
1473         offset = TextIterator::rangeLength(range.get(), true) + 1;
1474     } else {
1475         RefPtr<Range> range = makeRange(startPosition, endPosition);
1476         offset = TextIterator::rangeLength(range.get(), true);
1477     }
1478
1479     return firstUnignoredParent;
1480 }
1481
1482 const char* cacheAndReturnAtkProperty(AtkObject* object, AtkCachedProperty property, String value)
1483 {
1484     WebKitAccessiblePrivate* priv = WEBKIT_ACCESSIBLE(object)->priv;
1485     CString* propertyPtr = nullptr;
1486
1487     switch (property) {
1488     case AtkCachedAccessibleName:
1489         propertyPtr = &priv->accessibleName;
1490         break;
1491
1492     case AtkCachedAccessibleDescription:
1493         propertyPtr = &priv->accessibleDescription;
1494         break;
1495
1496     case AtkCachedActionName:
1497         propertyPtr = &priv->actionName;
1498         break;
1499
1500     case AtkCachedActionKeyBinding:
1501         propertyPtr = &priv->actionKeyBinding;
1502         break;
1503
1504     case AtkCachedDocumentLocale:
1505         propertyPtr = &priv->documentLocale;
1506         break;
1507
1508     case AtkCachedDocumentType:
1509         propertyPtr = &priv->documentType;
1510         break;
1511
1512     case AtkCachedDocumentEncoding:
1513         propertyPtr = &priv->documentEncoding;
1514         break;
1515
1516     case AtkCachedDocumentURI:
1517         propertyPtr = &priv->documentURI;
1518         break;
1519
1520     case AtkCachedImageDescription:
1521         propertyPtr = &priv->imageDescription;
1522         break;
1523
1524     default:
1525         ASSERT_NOT_REACHED();
1526     }
1527
1528     // Don't invalidate old memory if not stricly needed, since other
1529     // callers might be still holding on to it.
1530     if (*propertyPtr != value.utf8())
1531         *propertyPtr = value.utf8();
1532
1533     return (*propertyPtr).data();
1534 }
1535
1536 #endif // HAVE(ACCESSIBILITY)