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