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