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