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