AX: [ATK] Table captions and table rows are missing from the accessible hierarchy
[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 webkitAccessibleGetNChildren(AtkObject* object)
331 {
332     g_return_val_if_fail(WEBKIT_IS_ACCESSIBLE(object), 0);
333     returnValIfWebKitAccessibleIsInvalid(WEBKIT_ACCESSIBLE(object), 0);
334
335     AccessibilityObject* coreObject = core(object);
336
337     return coreObject->children().size();
338 }
339
340 static AtkObject* webkitAccessibleRefChild(AtkObject* object, gint index)
341 {
342     g_return_val_if_fail(WEBKIT_IS_ACCESSIBLE(object), 0);
343     returnValIfWebKitAccessibleIsInvalid(WEBKIT_ACCESSIBLE(object), 0);
344
345     if (index < 0)
346         return 0;
347
348     AccessibilityObject* coreObject = core(object);
349     AccessibilityObject* coreChild = 0;
350
351     const AccessibilityObject::AccessibilityChildrenVector& children = coreObject->children();
352     if (static_cast<size_t>(index) >= children.size())
353         return 0;
354     coreChild = children.at(index).get();
355
356     if (!coreChild)
357         return 0;
358
359     AtkObject* child = coreChild->wrapper();
360     atk_object_set_parent(child, object);
361     g_object_ref(child);
362
363     return child;
364 }
365
366 static gint webkitAccessibleGetIndexInParent(AtkObject* object)
367 {
368     g_return_val_if_fail(WEBKIT_IS_ACCESSIBLE(object), -1);
369     returnValIfWebKitAccessibleIsInvalid(WEBKIT_ACCESSIBLE(object), -1);
370
371     AccessibilityObject* coreObject = core(object);
372     AccessibilityObject* parent = coreObject->parentObjectUnignored();
373
374     if (!parent && isRootObject(coreObject)) {
375         AtkObject* atkParent = atkParentOfRootObject(object);
376         if (!atkParent)
377             return -1;
378
379         unsigned count = atk_object_get_n_accessible_children(atkParent);
380         for (unsigned i = 0; i < count; ++i) {
381             AtkObject* child = atk_object_ref_accessible_child(atkParent, i);
382             bool childIsObject = child == object;
383             g_object_unref(child);
384             if (childIsObject)
385                 return i;
386         }
387     }
388
389     if (!parent)
390         return -1;
391
392     size_t index = parent->children().find(coreObject);
393     return (index == WTF::notFound) ? -1 : index;
394 }
395
396 static AtkAttributeSet* webkitAccessibleGetAttributes(AtkObject* object)
397 {
398     g_return_val_if_fail(WEBKIT_IS_ACCESSIBLE(object), 0);
399     returnValIfWebKitAccessibleIsInvalid(WEBKIT_ACCESSIBLE(object), 0);
400
401     AtkAttributeSet* attributeSet = 0;
402 #if PLATFORM(GTK)
403     attributeSet = addToAtkAttributeSet(attributeSet, "toolkit", "WebKitGtk");
404 #elif PLATFORM(EFL)
405     attributeSet = addToAtkAttributeSet(attributeSet, "toolkit", "WebKitEfl");
406 #endif
407
408     AccessibilityObject* coreObject = core(object);
409     if (!coreObject)
410         return attributeSet;
411
412     // Hack needed for WebKit2 tests because obtaining an element by its ID
413     // cannot be done from the UIProcess. Assistive technologies have no need
414     // for this information.
415     Element* element = coreObject->element() ? coreObject->element() : coreObject->actionElement();
416     if (element) {
417         String id = element->getIdAttribute().string();
418         if (!id.isEmpty())
419             attributeSet = addToAtkAttributeSet(attributeSet, "html-id", id.utf8().data());
420     }
421
422     int headingLevel = coreObject->headingLevel();
423     if (headingLevel) {
424         String value = String::number(headingLevel);
425         attributeSet = addToAtkAttributeSet(attributeSet, "level", value.utf8().data());
426     }
427
428     // Set the 'layout-guess' attribute to help Assistive
429     // Technologies know when an exposed table is not data table.
430     if (is<AccessibilityTable>(*coreObject) && downcast<AccessibilityTable>(*coreObject).isExposableThroughAccessibility() && !coreObject->isDataTable())
431         attributeSet = addToAtkAttributeSet(attributeSet, "layout-guess", "true");
432
433     String placeholder = coreObject->placeholderValue();
434     if (!placeholder.isEmpty())
435         attributeSet = addToAtkAttributeSet(attributeSet, "placeholder-text", placeholder.utf8().data());
436
437     if (coreObject->ariaHasPopup())
438         attributeSet = addToAtkAttributeSet(attributeSet, "haspopup", "true");
439
440     AccessibilitySortDirection sortDirection = coreObject->sortDirection();
441     if (sortDirection != SortDirectionNone) {
442         // WAI-ARIA spec says to translate the value as is from the attribute.
443         const AtomicString& sortAttribute = coreObject->getAttribute(HTMLNames::aria_sortAttr);
444         attributeSet = addToAtkAttributeSet(attributeSet, "sort", sortAttribute.string().utf8().data());
445     }
446
447     if (coreObject->supportsARIAPosInSet())
448         attributeSet = addToAtkAttributeSet(attributeSet, "posinset", String::number(coreObject->ariaPosInSet()).utf8().data());
449
450     if (coreObject->supportsARIASetSize())
451         attributeSet = addToAtkAttributeSet(attributeSet, "setsize", String::number(coreObject->ariaSetSize()).utf8().data());
452
453     // Landmarks will be exposed with xml-roles object attributes, with the exception
454     // of LandmarkApplicationRole, which will be exposed with ATK_ROLE_EMBEDDED.
455     AccessibilityRole role = coreObject->roleValue();
456     switch (role) {
457     case LandmarkBannerRole:
458         attributeSet = addToAtkAttributeSet(attributeSet, "xml-roles", "banner");
459         break;
460     case LandmarkComplementaryRole:
461         attributeSet = addToAtkAttributeSet(attributeSet, "xml-roles", "complementary");
462         break;
463     case LandmarkContentInfoRole:
464         attributeSet = addToAtkAttributeSet(attributeSet, "xml-roles", "contentinfo");
465         break;
466     case LandmarkMainRole:
467         attributeSet = addToAtkAttributeSet(attributeSet, "xml-roles", "main");
468         break;
469     case LandmarkNavigationRole:
470         attributeSet = addToAtkAttributeSet(attributeSet, "xml-roles", "navigation");
471         break;
472     case LandmarkSearchRole:
473         attributeSet = addToAtkAttributeSet(attributeSet, "xml-roles", "search");
474         break;
475     default:
476         break;
477     }
478
479     return attributeSet;
480 }
481
482 static AtkRole atkRole(AccessibilityObject* coreObject)
483 {
484     AccessibilityRole role = coreObject->roleValue();
485     switch (role) {
486     case ApplicationAlertDialogRole:
487     case ApplicationAlertRole:
488         return ATK_ROLE_ALERT;
489     case ApplicationDialogRole:
490         return ATK_ROLE_DIALOG;
491     case ApplicationStatusRole:
492         return ATK_ROLE_STATUSBAR;
493     case UnknownRole:
494         return ATK_ROLE_UNKNOWN;
495     case AudioRole:
496 #if ATK_CHECK_VERSION(2, 11, 3)
497         return ATK_ROLE_AUDIO;
498 #endif
499     case VideoRole:
500 #if ATK_CHECK_VERSION(2, 11, 3)
501         return ATK_ROLE_VIDEO;
502 #endif
503         return ATK_ROLE_EMBEDDED;
504     case ButtonRole:
505         return ATK_ROLE_PUSH_BUTTON;
506     case ToggleButtonRole:
507         return ATK_ROLE_TOGGLE_BUTTON;
508     case RadioButtonRole:
509         return ATK_ROLE_RADIO_BUTTON;
510     case CheckBoxRole:
511         return ATK_ROLE_CHECK_BOX;
512     case SliderRole:
513         return ATK_ROLE_SLIDER;
514     case TabGroupRole:
515     case TabListRole:
516         return ATK_ROLE_PAGE_TAB_LIST;
517     case TextFieldRole:
518     case TextAreaRole:
519         return ATK_ROLE_ENTRY;
520     case StaticTextRole:
521         return ATK_ROLE_TEXT;
522     case OutlineRole:
523     case TreeRole:
524         return ATK_ROLE_TREE;
525     case TreeItemRole:
526         return ATK_ROLE_TREE_ITEM;
527     case MenuBarRole:
528         return ATK_ROLE_MENU_BAR;
529     case MenuListPopupRole:
530     case MenuRole:
531         return ATK_ROLE_MENU;
532     case MenuListOptionRole:
533     case MenuItemRole:
534         return ATK_ROLE_MENU_ITEM;
535     case MenuItemCheckboxRole:
536         return ATK_ROLE_CHECK_MENU_ITEM;
537     case MenuItemRadioRole:
538         return ATK_ROLE_RADIO_MENU_ITEM;
539     case ColumnRole:
540         // return ATK_ROLE_TABLE_COLUMN_HEADER; // Is this right?
541         return ATK_ROLE_UNKNOWN; // Matches Mozilla
542     case RowRole:
543         return ATK_ROLE_TABLE_ROW;
544     case ToolbarRole:
545         return ATK_ROLE_TOOL_BAR;
546     case BusyIndicatorRole:
547         return ATK_ROLE_PROGRESS_BAR; // Is this right?
548     case ProgressIndicatorRole:
549         // return ATK_ROLE_SPIN_BUTTON; // Some confusion about this role in AccessibilityRenderObject.cpp
550         return ATK_ROLE_PROGRESS_BAR;
551     case WindowRole:
552         return ATK_ROLE_WINDOW;
553     case PopUpButtonRole:
554     case ComboBoxRole:
555         return ATK_ROLE_COMBO_BOX;
556     case SplitGroupRole:
557         return ATK_ROLE_SPLIT_PANE;
558     case SplitterRole:
559         return ATK_ROLE_SEPARATOR;
560     case ColorWellRole:
561         return ATK_ROLE_COLOR_CHOOSER;
562     case ListRole:
563         return ATK_ROLE_LIST;
564     case ScrollBarRole:
565         return ATK_ROLE_SCROLL_BAR;
566     case ScrollAreaRole:
567         return ATK_ROLE_SCROLL_PANE;
568     case GridRole: // Is this right?
569     case TableRole:
570         return ATK_ROLE_TABLE;
571     case ApplicationRole:
572         return ATK_ROLE_APPLICATION;
573     case DocumentRegionRole:
574     case GroupRole:
575     case RadioGroupRole:
576     case TabPanelRole:
577         return ATK_ROLE_PANEL;
578     case RowHeaderRole:
579         return ATK_ROLE_ROW_HEADER;
580     case ColumnHeaderRole:
581         return ATK_ROLE_COLUMN_HEADER;
582     case CaptionRole:
583         return ATK_ROLE_CAPTION;
584     case CellRole:
585         return coreObject->inheritsPresentationalRole() ? ATK_ROLE_SECTION : ATK_ROLE_TABLE_CELL;
586     case LinkRole:
587     case WebCoreLinkRole:
588     case ImageMapLinkRole:
589         return ATK_ROLE_LINK;
590     case ImageMapRole:
591         return ATK_ROLE_IMAGE_MAP;
592     case ImageRole:
593         return ATK_ROLE_IMAGE;
594     case ListMarkerRole:
595         return ATK_ROLE_TEXT;
596     case DocumentArticleRole:
597 #if ATK_CHECK_VERSION(2, 11, 3)
598         return ATK_ROLE_ARTICLE;
599 #endif
600     case DocumentRole:
601         return ATK_ROLE_DOCUMENT_FRAME;
602     case DocumentNoteRole:
603         return ATK_ROLE_COMMENT;
604     case HeadingRole:
605         return ATK_ROLE_HEADING;
606     case ListBoxRole:
607         return ATK_ROLE_LIST_BOX;
608     case ListItemRole:
609         return coreObject->inheritsPresentationalRole() ? ATK_ROLE_SECTION : ATK_ROLE_LIST_ITEM;
610     case ListBoxOptionRole:
611         return ATK_ROLE_LIST_ITEM;
612     case ParagraphRole:
613         return ATK_ROLE_PARAGRAPH;
614     case LabelRole:
615     case LegendRole:
616         return ATK_ROLE_LABEL;
617     case BlockquoteRole:
618 #if ATK_CHECK_VERSION(2, 11, 3)
619         return ATK_ROLE_BLOCK_QUOTE;
620 #endif
621     case DivRole:
622         return ATK_ROLE_SECTION;
623     case FooterRole:
624         return ATK_ROLE_FOOTER;
625     case FormRole:
626         return ATK_ROLE_FORM;
627     case CanvasRole:
628         return ATK_ROLE_CANVAS;
629     case HorizontalRuleRole:
630         return ATK_ROLE_SEPARATOR;
631     case SpinButtonRole:
632         return ATK_ROLE_SPIN_BUTTON;
633     case TabRole:
634         return ATK_ROLE_PAGE_TAB;
635     case UserInterfaceTooltipRole:
636         return ATK_ROLE_TOOL_TIP;
637     case WebAreaRole:
638         return ATK_ROLE_DOCUMENT_WEB;
639     case LandmarkApplicationRole:
640         return ATK_ROLE_EMBEDDED;
641 #if ATK_CHECK_VERSION(2, 11, 3)
642     case ApplicationLogRole:
643         return ATK_ROLE_LOG;
644     case ApplicationMarqueeRole:
645         return ATK_ROLE_MARQUEE;
646     case ApplicationTimerRole:
647         return ATK_ROLE_TIMER;
648     case DefinitionRole:
649         return ATK_ROLE_DEFINITION;
650     case DocumentMathRole:
651         return ATK_ROLE_MATH;
652     case LandmarkBannerRole:
653     case LandmarkComplementaryRole:
654     case LandmarkContentInfoRole:
655     case LandmarkMainRole:
656     case LandmarkNavigationRole:
657     case LandmarkSearchRole:
658         return ATK_ROLE_LANDMARK;
659 #endif
660 #if ATK_CHECK_VERSION(2, 11, 4)
661     case DescriptionListRole:
662         return ATK_ROLE_DESCRIPTION_LIST;
663     case DescriptionListTermRole:
664         return ATK_ROLE_DESCRIPTION_TERM;
665     case DescriptionListDetailRole:
666         return ATK_ROLE_DESCRIPTION_VALUE;
667 #endif
668     default:
669         return ATK_ROLE_UNKNOWN;
670     }
671 }
672
673 static AtkRole webkitAccessibleGetRole(AtkObject* object)
674 {
675     // ATK_ROLE_UNKNOWN should only be applied in cases where there is a valid
676     // WebCore accessible object for which the platform role mapping is unknown.
677     g_return_val_if_fail(WEBKIT_IS_ACCESSIBLE(object), ATK_ROLE_INVALID);
678     returnValIfWebKitAccessibleIsInvalid(WEBKIT_ACCESSIBLE(object), ATK_ROLE_INVALID);
679
680     AccessibilityObject* coreObject = core(object);
681
682     if (!coreObject)
683         return ATK_ROLE_INVALID;
684
685     // Note: Why doesn't WebCore have a password field for this
686     if (coreObject->isPasswordField())
687         return ATK_ROLE_PASSWORD_TEXT;
688
689     return atkRole(coreObject);
690 }
691
692 static bool isTextWithCaret(AccessibilityObject* coreObject)
693 {
694     if (!coreObject || !coreObject->isAccessibilityRenderObject())
695         return false;
696
697     Document* document = coreObject->document();
698     if (!document)
699         return false;
700
701     Frame* frame = document->frame();
702     if (!frame)
703         return false;
704
705     if (!frame->settings().caretBrowsingEnabled())
706         return false;
707
708     // Check text objects and paragraphs only.
709     AtkObject* axObject = coreObject->wrapper();
710     AtkRole role = axObject ? atk_object_get_role(axObject) : ATK_ROLE_INVALID;
711     if (role != ATK_ROLE_TEXT && role != ATK_ROLE_PARAGRAPH)
712         return false;
713
714     // Finally, check whether the caret is set in the current object.
715     VisibleSelection selection = coreObject->selection();
716     if (!selection.isCaret())
717         return false;
718
719     return selectionBelongsToObject(coreObject, selection);
720 }
721
722 static void setAtkStateSetFromCoreObject(AccessibilityObject* coreObject, AtkStateSet* stateSet)
723 {
724     AccessibilityObject* parent = coreObject->parentObject();
725     bool isListBoxOption = parent && parent->isListBox();
726
727     // Please keep the state list in alphabetical order
728     if (isListBoxOption && coreObject->isSelectedOptionActive())
729         atk_state_set_add_state(stateSet, ATK_STATE_ACTIVE);
730
731     if (coreObject->isChecked())
732         atk_state_set_add_state(stateSet, ATK_STATE_CHECKED);
733
734     // FIXME: isReadOnly does not seem to do the right thing for
735     // controls, so check explicitly for them. In addition, because
736     // isReadOnly is false for listBoxOptions, we need to add one
737     // more check so that we do not present them as being "editable".
738     if ((!coreObject->isReadOnly()
739         || (coreObject->isControl() && coreObject->canSetValueAttribute()))
740         && !isListBoxOption)
741         atk_state_set_add_state(stateSet, ATK_STATE_EDITABLE);
742
743     // FIXME: Put both ENABLED and SENSITIVE together here for now
744     if (coreObject->isEnabled()) {
745         atk_state_set_add_state(stateSet, ATK_STATE_ENABLED);
746         atk_state_set_add_state(stateSet, ATK_STATE_SENSITIVE);
747     }
748
749     if (coreObject->canSetExpandedAttribute())
750         atk_state_set_add_state(stateSet, ATK_STATE_EXPANDABLE);
751
752     if (coreObject->isExpanded())
753         atk_state_set_add_state(stateSet, ATK_STATE_EXPANDED);
754
755     if (coreObject->canSetFocusAttribute())
756         atk_state_set_add_state(stateSet, ATK_STATE_FOCUSABLE);
757
758     if (coreObject->isFocused() || isTextWithCaret(coreObject))
759         atk_state_set_add_state(stateSet, ATK_STATE_FOCUSED);
760
761     if (coreObject->orientation() == AccessibilityOrientationHorizontal)
762         atk_state_set_add_state(stateSet, ATK_STATE_HORIZONTAL);
763     else if (coreObject->orientation() == AccessibilityOrientationVertical)
764         atk_state_set_add_state(stateSet, ATK_STATE_VERTICAL);
765
766     if (coreObject->isIndeterminate())
767         atk_state_set_add_state(stateSet, ATK_STATE_INDETERMINATE);
768
769     if (coreObject->isCheckboxOrRadio() || coreObject->isMenuItem()) {
770         if (coreObject->checkboxOrRadioValue() == ButtonStateMixed)
771             atk_state_set_add_state(stateSet, ATK_STATE_INDETERMINATE);
772     }
773
774     if (coreObject->invalidStatus() != "false")
775         atk_state_set_add_state(stateSet, ATK_STATE_INVALID_ENTRY);
776
777     if (coreObject->isMultiSelectable())
778         atk_state_set_add_state(stateSet, ATK_STATE_MULTISELECTABLE);
779
780     // TODO: ATK_STATE_OPAQUE
781
782     if (coreObject->isPressed())
783         atk_state_set_add_state(stateSet, ATK_STATE_PRESSED);
784
785     if (coreObject->isRequired())
786         atk_state_set_add_state(stateSet, ATK_STATE_REQUIRED);
787
788     // TODO: ATK_STATE_SELECTABLE_TEXT
789
790     if (coreObject->canSetSelectedAttribute()) {
791         atk_state_set_add_state(stateSet, ATK_STATE_SELECTABLE);
792         // Items in focusable lists have both STATE_SELECT{ABLE,ED}
793         // and STATE_FOCUS{ABLE,ED}. We'll fake the latter based on
794         // the former.
795         if (isListBoxOption)
796             atk_state_set_add_state(stateSet, ATK_STATE_FOCUSABLE);
797     }
798
799     if (coreObject->isSelected()) {
800         atk_state_set_add_state(stateSet, ATK_STATE_SELECTED);
801         // Items in focusable lists have both STATE_SELECT{ABLE,ED}
802         // and STATE_FOCUS{ABLE,ED}. We'll fake the latter based on the
803         // former.
804         if (isListBoxOption)
805             atk_state_set_add_state(stateSet, ATK_STATE_FOCUSED);
806     }
807
808     // FIXME: Group both SHOWING and VISIBLE here for now
809     // Not sure how to handle this in WebKit, see bug
810     // http://bugzilla.gnome.org/show_bug.cgi?id=509650 for other
811     // issues with SHOWING vs VISIBLE.
812     if (!coreObject->isOffScreen()) {
813         atk_state_set_add_state(stateSet, ATK_STATE_SHOWING);
814         atk_state_set_add_state(stateSet, ATK_STATE_VISIBLE);
815     }
816
817     // Mutually exclusive, so we group these two
818     if (coreObject->roleValue() == TextFieldRole)
819         atk_state_set_add_state(stateSet, ATK_STATE_SINGLE_LINE);
820     else if (coreObject->roleValue() == TextAreaRole)
821         atk_state_set_add_state(stateSet, ATK_STATE_MULTI_LINE);
822
823     // TODO: ATK_STATE_SENSITIVE
824
825     if (coreObject->isVisited())
826         atk_state_set_add_state(stateSet, ATK_STATE_VISITED);
827 }
828
829 static AtkStateSet* webkitAccessibleRefStateSet(AtkObject* object)
830 {
831     g_return_val_if_fail(WEBKIT_IS_ACCESSIBLE(object), 0);
832
833     AtkStateSet* stateSet = ATK_OBJECT_CLASS(webkitAccessibleParentClass)->ref_state_set(object);
834     AccessibilityObject* coreObject = core(object);
835
836     // Make sure the layout is updated to really know whether the object
837     // is defunct or not, so we can return the proper state.
838     coreObject->updateBackingStore();
839
840     if (coreObject == fallbackObject()) {
841         atk_state_set_add_state(stateSet, ATK_STATE_DEFUNCT);
842         return stateSet;
843     }
844
845     // Text objects must be focusable.
846     AtkRole role = atk_object_get_role(object);
847     if (role == ATK_ROLE_TEXT || role == ATK_ROLE_PARAGRAPH)
848         atk_state_set_add_state(stateSet, ATK_STATE_FOCUSABLE);
849
850     setAtkStateSetFromCoreObject(coreObject, stateSet);
851     return stateSet;
852 }
853
854 static AtkRelationSet* webkitAccessibleRefRelationSet(AtkObject* object)
855 {
856     g_return_val_if_fail(WEBKIT_IS_ACCESSIBLE(object), 0);
857     returnValIfWebKitAccessibleIsInvalid(WEBKIT_ACCESSIBLE(object), 0);
858
859     AtkRelationSet* relationSet = ATK_OBJECT_CLASS(webkitAccessibleParentClass)->ref_relation_set(object);
860     AccessibilityObject* coreObject = core(object);
861
862     setAtkRelationSetFromCoreObject(coreObject, relationSet);
863
864     return relationSet;
865 }
866
867 static void webkitAccessibleInit(AtkObject* object, gpointer data)
868 {
869     if (ATK_OBJECT_CLASS(webkitAccessibleParentClass)->initialize)
870         ATK_OBJECT_CLASS(webkitAccessibleParentClass)->initialize(object, data);
871
872     WebKitAccessible* accessible = WEBKIT_ACCESSIBLE(object);
873     accessible->m_object = reinterpret_cast<AccessibilityObject*>(data);
874     accessible->priv = WEBKIT_ACCESSIBLE_GET_PRIVATE(accessible);
875 }
876
877 static const gchar* webkitAccessibleGetObjectLocale(AtkObject* object)
878 {
879     g_return_val_if_fail(WEBKIT_IS_ACCESSIBLE(object), 0);
880     returnValIfWebKitAccessibleIsInvalid(WEBKIT_ACCESSIBLE(object), 0);
881
882     AccessibilityObject* coreObject = core(object);
883     if (!coreObject)
884         return 0;
885
886     if (ATK_IS_DOCUMENT(object)) {
887         // TODO: Should we fall back on lang xml:lang when the following comes up empty?
888         String language = coreObject->language();
889         if (!language.isEmpty())
890             return cacheAndReturnAtkProperty(object, AtkCachedDocumentLocale, language);
891
892     } else if (ATK_IS_TEXT(object)) {
893         const gchar* locale = 0;
894
895         AtkAttributeSet* textAttributes = atk_text_get_default_attributes(ATK_TEXT(object));
896         for (AtkAttributeSet* attributes = textAttributes; attributes; attributes = attributes->next) {
897             AtkAttribute* atkAttribute = static_cast<AtkAttribute*>(attributes->data);
898             if (!strcmp(atkAttribute->name, atk_text_attribute_get_name(ATK_TEXT_ATTR_LANGUAGE))) {
899                 locale = cacheAndReturnAtkProperty(object, AtkCachedDocumentLocale, String::fromUTF8(atkAttribute->value));
900                 break;
901             }
902         }
903         atk_attribute_set_free(textAttributes);
904
905         return locale;
906     }
907
908     return 0;
909 }
910
911 static void webkitAccessibleFinalize(GObject* object)
912 {
913     G_OBJECT_CLASS(webkitAccessibleParentClass)->finalize(object);
914 }
915
916 static void webkitAccessibleClassInit(AtkObjectClass* klass)
917 {
918     GObjectClass* gobjectClass = G_OBJECT_CLASS(klass);
919
920     webkitAccessibleParentClass = g_type_class_peek_parent(klass);
921
922     gobjectClass->finalize = webkitAccessibleFinalize;
923
924     klass->initialize = webkitAccessibleInit;
925     klass->get_name = webkitAccessibleGetName;
926     klass->get_description = webkitAccessibleGetDescription;
927     klass->get_parent = webkitAccessibleGetParent;
928     klass->get_n_children = webkitAccessibleGetNChildren;
929     klass->ref_child = webkitAccessibleRefChild;
930     klass->get_role = webkitAccessibleGetRole;
931     klass->ref_state_set = webkitAccessibleRefStateSet;
932     klass->get_index_in_parent = webkitAccessibleGetIndexInParent;
933     klass->get_attributes = webkitAccessibleGetAttributes;
934     klass->ref_relation_set = webkitAccessibleRefRelationSet;
935     klass->get_object_locale = webkitAccessibleGetObjectLocale;
936
937     g_type_class_add_private(klass, sizeof(WebKitAccessiblePrivate));
938 }
939
940 GType
941 webkitAccessibleGetType(void)
942 {
943     static volatile gsize typeVolatile = 0;
944
945     if (g_once_init_enter(&typeVolatile)) {
946         static const GTypeInfo tinfo = {
947             sizeof(WebKitAccessibleClass),
948             (GBaseInitFunc) 0,
949             (GBaseFinalizeFunc) 0,
950             (GClassInitFunc) webkitAccessibleClassInit,
951             (GClassFinalizeFunc) 0,
952             0, /* class data */
953             sizeof(WebKitAccessible), /* instance size */
954             0, /* nb preallocs */
955             (GInstanceInitFunc) 0,
956             0 /* value table */
957         };
958
959         GType type = g_type_register_static(ATK_TYPE_OBJECT, "WebKitAccessible", &tinfo, GTypeFlags(0));
960         g_once_init_leave(&typeVolatile, type);
961     }
962
963     return typeVolatile;
964 }
965
966 static const GInterfaceInfo AtkInterfacesInitFunctions[] = {
967     {reinterpret_cast<GInterfaceInitFunc>(webkitAccessibleActionInterfaceInit), 0, 0},
968     {reinterpret_cast<GInterfaceInitFunc>(webkitAccessibleSelectionInterfaceInit), 0, 0},
969     {reinterpret_cast<GInterfaceInitFunc>(webkitAccessibleEditableTextInterfaceInit), 0, 0},
970     {reinterpret_cast<GInterfaceInitFunc>(webkitAccessibleTextInterfaceInit), 0, 0},
971     {reinterpret_cast<GInterfaceInitFunc>(webkitAccessibleComponentInterfaceInit), 0, 0},
972     {reinterpret_cast<GInterfaceInitFunc>(webkitAccessibleImageInterfaceInit), 0, 0},
973     {reinterpret_cast<GInterfaceInitFunc>(webkitAccessibleTableInterfaceInit), 0, 0},
974 #if ATK_CHECK_VERSION(2,11,90)
975     {reinterpret_cast<GInterfaceInitFunc>(webkitAccessibleTableCellInterfaceInit), 0, 0},
976 #endif
977     {reinterpret_cast<GInterfaceInitFunc>(webkitAccessibleHypertextInterfaceInit), 0, 0},
978     {reinterpret_cast<GInterfaceInitFunc>(webkitAccessibleHyperlinkImplInterfaceInit), 0, 0},
979     {reinterpret_cast<GInterfaceInitFunc>(webkitAccessibleDocumentInterfaceInit), 0, 0},
980     {reinterpret_cast<GInterfaceInitFunc>(webkitAccessibleValueInterfaceInit), 0, 0}
981 };
982
983 enum WAIType {
984     WAIAction,
985     WAISelection,
986     WAIEditableText,
987     WAIText,
988     WAIComponent,
989     WAIImage,
990     WAITable,
991 #if ATK_CHECK_VERSION(2,11,90)
992     WAITableCell,
993 #endif
994     WAIHypertext,
995     WAIHyperlink,
996     WAIDocument,
997     WAIValue,
998 };
999
1000 static GType GetAtkInterfaceTypeFromWAIType(WAIType type)
1001 {
1002     switch (type) {
1003     case WAIAction:
1004         return ATK_TYPE_ACTION;
1005     case WAISelection:
1006         return ATK_TYPE_SELECTION;
1007     case WAIEditableText:
1008         return ATK_TYPE_EDITABLE_TEXT;
1009     case WAIText:
1010         return ATK_TYPE_TEXT;
1011     case WAIComponent:
1012         return ATK_TYPE_COMPONENT;
1013     case WAIImage:
1014         return ATK_TYPE_IMAGE;
1015     case WAITable:
1016         return ATK_TYPE_TABLE;
1017 #if ATK_CHECK_VERSION(2,11,90)
1018     case WAITableCell:
1019         return ATK_TYPE_TABLE_CELL;
1020 #endif
1021     case WAIHypertext:
1022         return ATK_TYPE_HYPERTEXT;
1023     case WAIHyperlink:
1024         return ATK_TYPE_HYPERLINK_IMPL;
1025     case WAIDocument:
1026         return ATK_TYPE_DOCUMENT;
1027     case WAIValue:
1028         return ATK_TYPE_VALUE;
1029     }
1030
1031     return G_TYPE_INVALID;
1032 }
1033
1034 static bool roleIsTextType(AccessibilityRole role)
1035 {
1036     return role == ParagraphRole || role == HeadingRole || role == DivRole || role == CellRole
1037         || role == LinkRole || role == WebCoreLinkRole || role == ListItemRole;
1038 }
1039
1040 static guint16 getInterfaceMaskFromObject(AccessibilityObject* coreObject)
1041 {
1042     guint16 interfaceMask = 0;
1043
1044     // Component interface is always supported
1045     interfaceMask |= 1 << WAIComponent;
1046
1047     AccessibilityRole role = coreObject->roleValue();
1048
1049     // Action
1050     // As the implementation of the AtkAction interface is a very
1051     // basic one (just relays in executing the default action for each
1052     // object, and only supports having one action per object), it is
1053     // better just to implement this interface for every instance of
1054     // the WebKitAccessible class and let WebCore decide what to do.
1055     interfaceMask |= 1 << WAIAction;
1056
1057     // Selection
1058     if (coreObject->isListBox() || coreObject->isMenuList())
1059         interfaceMask |= 1 << WAISelection;
1060
1061     // Get renderer if available.
1062     RenderObject* renderer = 0;
1063     if (coreObject->isAccessibilityRenderObject())
1064         renderer = coreObject->renderer();
1065
1066     // Hyperlink (links and embedded objects).
1067     if (coreObject->isLink() || (renderer && renderer->isReplaced()))
1068         interfaceMask |= 1 << WAIHyperlink;
1069
1070     // Text, Editable Text & Hypertext
1071     if (role == StaticTextRole || coreObject->isMenuListOption())
1072         interfaceMask |= 1 << WAIText;
1073     else if (coreObject->isTextControl()) {
1074         interfaceMask |= 1 << WAIText;
1075         if (!coreObject->isReadOnly())
1076             interfaceMask |= 1 << WAIEditableText;
1077     } else if (!coreObject->isWebArea()) {
1078         if (role != TableRole) {
1079             interfaceMask |= 1 << WAIHypertext;
1080             if ((renderer && renderer->childrenInline()) || roleIsTextType(role))
1081                 interfaceMask |= 1 << WAIText;
1082         }
1083
1084         // Add the TEXT interface for list items whose
1085         // first accessible child has a text renderer
1086         if (role == ListItemRole) {
1087             const AccessibilityObject::AccessibilityChildrenVector& children = coreObject->children();
1088             if (children.size()) {
1089                 AccessibilityObject* axRenderChild = children.at(0).get();
1090                 interfaceMask |= getInterfaceMaskFromObject(axRenderChild);
1091             }
1092         }
1093     }
1094
1095     // Image
1096     if (coreObject->isImage())
1097         interfaceMask |= 1 << WAIImage;
1098
1099     // Table
1100     if (role == TableRole)
1101         interfaceMask |= 1 << WAITable;
1102
1103 #if ATK_CHECK_VERSION(2,11,90)
1104     if (role == CellRole || role == ColumnHeaderRole || role == RowHeaderRole)
1105         interfaceMask |= 1 << WAITableCell;
1106 #endif
1107
1108     // Document
1109     if (role == WebAreaRole)
1110         interfaceMask |= 1 << WAIDocument;
1111
1112     // Value
1113     if (role == SliderRole || role == SpinButtonRole || role == ScrollBarRole || role == ProgressIndicatorRole)
1114         interfaceMask |= 1 << WAIValue;
1115
1116 #if ENABLE(INPUT_TYPE_COLOR)
1117     // Color type.
1118     if (role == ColorWellRole)
1119         interfaceMask |= 1 << WAIText;
1120 #endif
1121
1122     return interfaceMask;
1123 }
1124
1125 static const char* getUniqueAccessibilityTypeName(guint16 interfaceMask)
1126 {
1127 #define WAI_TYPE_NAME_LEN (30) /* Enough for prefix + 5 hex characters (max) */
1128     static char name[WAI_TYPE_NAME_LEN + 1];
1129
1130     g_sprintf(name, "WAIType%x", interfaceMask);
1131     name[WAI_TYPE_NAME_LEN] = '\0';
1132
1133     return name;
1134 }
1135
1136 static GType getAccessibilityTypeFromObject(AccessibilityObject* coreObject)
1137 {
1138     static const GTypeInfo typeInfo = {
1139         sizeof(WebKitAccessibleClass),
1140         (GBaseInitFunc) 0,
1141         (GBaseFinalizeFunc) 0,
1142         (GClassInitFunc) 0,
1143         (GClassFinalizeFunc) 0,
1144         0, /* class data */
1145         sizeof(WebKitAccessible), /* instance size */
1146         0, /* nb preallocs */
1147         (GInstanceInitFunc) 0,
1148         0 /* value table */
1149     };
1150
1151     guint16 interfaceMask = getInterfaceMaskFromObject(coreObject);
1152     const char* atkTypeName = getUniqueAccessibilityTypeName(interfaceMask);
1153     GType type = g_type_from_name(atkTypeName);
1154     if (type)
1155         return type;
1156
1157     type = g_type_register_static(WEBKIT_TYPE_ACCESSIBLE, atkTypeName, &typeInfo, GTypeFlags(0));
1158     for (guint i = 0; i < G_N_ELEMENTS(AtkInterfacesInitFunctions); i++) {
1159         if (interfaceMask & (1 << i))
1160             g_type_add_interface_static(type,
1161                 GetAtkInterfaceTypeFromWAIType(static_cast<WAIType>(i)),
1162                 &AtkInterfacesInitFunctions[i]);
1163     }
1164
1165     return type;
1166 }
1167
1168 WebKitAccessible* webkitAccessibleNew(AccessibilityObject* coreObject)
1169 {
1170     GType type = getAccessibilityTypeFromObject(coreObject);
1171     AtkObject* object = static_cast<AtkObject*>(g_object_new(type, 0));
1172
1173     atk_object_initialize(object, coreObject);
1174
1175     return WEBKIT_ACCESSIBLE(object);
1176 }
1177
1178 AccessibilityObject* webkitAccessibleGetAccessibilityObject(WebKitAccessible* accessible)
1179 {
1180     return accessible->m_object;
1181 }
1182
1183 void webkitAccessibleDetach(WebKitAccessible* accessible)
1184 {
1185     ASSERT(accessible->m_object);
1186
1187     if (accessible->m_object->roleValue() == WebAreaRole)
1188         atk_object_notify_state_change(ATK_OBJECT(accessible), ATK_STATE_DEFUNCT, true);
1189
1190     // We replace the WebCore AccessibilityObject with a fallback object that
1191     // provides default implementations to avoid repetitive null-checking after
1192     // detachment.
1193     accessible->m_object = fallbackObject();
1194 }
1195
1196 bool webkitAccessibleIsDetached(WebKitAccessible* accessible)
1197 {
1198     ASSERT(accessible->m_object);
1199     return accessible->m_object == fallbackObject();
1200 }
1201
1202 AccessibilityObject* objectFocusedAndCaretOffsetUnignored(AccessibilityObject* referenceObject, int& offset)
1203 {
1204     // Indication that something bogus has transpired.
1205     offset = -1;
1206
1207     Document* document = referenceObject->document();
1208     if (!document)
1209         return 0;
1210
1211     Node* focusedNode = referenceObject->selection().end().containerNode();
1212     if (!focusedNode)
1213         return 0;
1214
1215     RenderObject* focusedRenderer = focusedNode->renderer();
1216     if (!focusedRenderer)
1217         return 0;
1218
1219     AccessibilityObject* focusedObject = document->axObjectCache()->getOrCreate(focusedRenderer);
1220     if (!focusedObject)
1221         return 0;
1222
1223     // Look for the actual (not ignoring accessibility) selected object.
1224     AccessibilityObject* firstUnignoredParent = focusedObject;
1225     if (firstUnignoredParent->accessibilityIsIgnored())
1226         firstUnignoredParent = firstUnignoredParent->parentObjectUnignored();
1227     if (!firstUnignoredParent)
1228         return 0;
1229
1230     // Don't ignore links if the offset is being requested for a link
1231     // or if the link is a block.
1232     if (!referenceObject->isLink() && firstUnignoredParent->isLink()
1233         && !(firstUnignoredParent->renderer() && !firstUnignoredParent->renderer()->isInline()))
1234         firstUnignoredParent = firstUnignoredParent->parentObjectUnignored();
1235     if (!firstUnignoredParent)
1236         return 0;
1237
1238     // The reference object must either coincide with the focused
1239     // object being considered, or be a descendant of it.
1240     if (referenceObject->isDescendantOfObject(firstUnignoredParent))
1241         referenceObject = firstUnignoredParent;
1242
1243     Node* startNode = 0;
1244     if (firstUnignoredParent != referenceObject || firstUnignoredParent->isTextControl()) {
1245         // We need to use the first child's node of the reference
1246         // object as the start point to calculate the caret offset
1247         // because we want it to be relative to the object of
1248         // reference, not just to the focused object (which could have
1249         // previous siblings which should be taken into account too).
1250         AccessibilityObject* axFirstChild = referenceObject->firstChild();
1251         if (axFirstChild)
1252             startNode = axFirstChild->node();
1253     }
1254     // Getting the Position of a PseudoElement now triggers an assertion.
1255     // This can occur when clicking on empty space in a render block.
1256     if (!startNode || startNode->isPseudoElement())
1257         startNode = firstUnignoredParent->node();
1258
1259     // Check if the node for the first parent object not ignoring
1260     // accessibility is null again before using it. This might happen
1261     // with certain kind of accessibility objects, such as the root
1262     // one (the scroller containing the webArea object).
1263     if (!startNode)
1264         return 0;
1265
1266     VisiblePosition startPosition = VisiblePosition(positionBeforeNode(startNode), DOWNSTREAM);
1267     VisiblePosition endPosition = firstUnignoredParent->selection().visibleEnd();
1268
1269     if (startPosition == endPosition)
1270         offset = 0;
1271     else if (!isStartOfLine(endPosition)) {
1272         RefPtr<Range> range = makeRange(startPosition, endPosition.previous());
1273         offset = TextIterator::rangeLength(range.get(), true) + 1;
1274     } else {
1275         RefPtr<Range> range = makeRange(startPosition, endPosition);
1276         offset = TextIterator::rangeLength(range.get(), true);
1277     }
1278
1279     return firstUnignoredParent;
1280 }
1281
1282 const char* cacheAndReturnAtkProperty(AtkObject* object, AtkCachedProperty property, String value)
1283 {
1284     WebKitAccessiblePrivate* priv = WEBKIT_ACCESSIBLE(object)->priv;
1285     CString* propertyPtr = 0;
1286
1287     switch (property) {
1288     case AtkCachedAccessibleName:
1289         propertyPtr = &priv->accessibleName;
1290         break;
1291
1292     case AtkCachedAccessibleDescription:
1293         propertyPtr = &priv->accessibleDescription;
1294         break;
1295
1296     case AtkCachedActionName:
1297         propertyPtr = &priv->actionName;
1298         break;
1299
1300     case AtkCachedActionKeyBinding:
1301         propertyPtr = &priv->actionKeyBinding;
1302         break;
1303
1304     case AtkCachedDocumentLocale:
1305         propertyPtr = &priv->documentLocale;
1306         break;
1307
1308     case AtkCachedDocumentType:
1309         propertyPtr = &priv->documentType;
1310         break;
1311
1312     case AtkCachedDocumentEncoding:
1313         propertyPtr = &priv->documentEncoding;
1314         break;
1315
1316     case AtkCachedDocumentURI:
1317         propertyPtr = &priv->documentURI;
1318         break;
1319
1320     case AtkCachedImageDescription:
1321         propertyPtr = &priv->imageDescription;
1322         break;
1323
1324     default:
1325         ASSERT_NOT_REACHED();
1326     }
1327
1328     // Don't invalidate old memory if not stricly needed, since other
1329     // callers might be still holding on to it.
1330     if (*propertyPtr != value.utf8())
1331         *propertyPtr = value.utf8();
1332
1333     return (*propertyPtr).data();
1334 }
1335
1336 #endif // HAVE(ACCESSIBILITY)