46feea5b9b4afcfe9093478b47eec10f7cb73287
[WebKit-https.git] / Source / WebCore / accessibility / gtk / 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  *
6  * Portions from Mozilla a11y, copyright as follows:
7  *
8  * The Original Code is mozilla.org code.
9  *
10  * The Initial Developer of the Original Code is
11  * Sun Microsystems, Inc.
12  * Portions created by the Initial Developer are Copyright (C) 2002
13  * the Initial Developer. All Rights Reserved.
14  *
15  * This library is free software; you can redistribute it and/or
16  * modify it under the terms of the GNU Library General Public
17  * License as published by the Free Software Foundation; either
18  * version 2 of the License, or (at your option) any later version.
19  *
20  * This library is distributed in the hope that it will be useful,
21  * but WITHOUT ANY WARRANTY; without even the implied warranty of
22  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
23  * Library General Public License for more details.
24  *
25  * You should have received a copy of the GNU Library General Public License
26  * along with this library; see the file COPYING.LIB.  If not, write to
27  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
28  * Boston, MA 02110-1301, USA.
29  */
30
31 #include "config.h"
32 #include "WebKitAccessibleWrapperAtk.h"
33
34 #if HAVE(ACCESSIBILITY)
35
36 #include "AXObjectCache.h"
37 #include "Document.h"
38 #include "Frame.h"
39 #include "FrameView.h"
40 #include "HTMLNames.h"
41 #include "HTMLTableElement.h"
42 #include "HostWindow.h"
43 #include "RenderObject.h"
44 #include "Settings.h"
45 #include "TextIterator.h"
46 #include "WebKitAccessibleHyperlink.h"
47 #include "WebKitAccessibleInterfaceAction.h"
48 #include "WebKitAccessibleInterfaceComponent.h"
49 #include "WebKitAccessibleInterfaceDocument.h"
50 #include "WebKitAccessibleInterfaceEditableText.h"
51 #include "WebKitAccessibleInterfaceHyperlinkImpl.h"
52 #include "WebKitAccessibleInterfaceHypertext.h"
53 #include "WebKitAccessibleInterfaceImage.h"
54 #include "WebKitAccessibleInterfaceSelection.h"
55 #include "WebKitAccessibleInterfaceTable.h"
56 #include "WebKitAccessibleInterfaceText.h"
57 #include "WebKitAccessibleInterfaceValue.h"
58 #include "WebKitAccessibleUtil.h"
59 #include "htmlediting.h"
60 #include "visible_units.h"
61 #include <glib/gprintf.h>
62
63 #if PLATFORM(GTK)
64 #include <gtk/gtk.h>
65 #endif
66
67 using namespace WebCore;
68
69 static AccessibilityObject* fallbackObject()
70 {
71     // FIXME: An AXObjectCache with a Document is meaningless.
72     static AXObjectCache* fallbackCache = new AXObjectCache(0);
73     static AccessibilityObject* object = 0;
74     if (!object) {
75         // FIXME: using fallbackCache->getOrCreate(ListBoxOptionRole) is a hack
76         object = fallbackCache->getOrCreate(ListBoxOptionRole);
77         object->ref();
78     }
79
80     return object;
81 }
82
83 static AccessibilityObject* core(WebKitAccessible* accessible)
84 {
85     if (!accessible)
86         return 0;
87
88     return accessible->m_object;
89 }
90
91 static AccessibilityObject* core(AtkObject* object)
92 {
93     if (!WEBKIT_IS_ACCESSIBLE(object))
94         return 0;
95
96     return core(WEBKIT_ACCESSIBLE(object));
97 }
98
99 static const gchar* webkitAccessibleGetName(AtkObject* object)
100 {
101     AccessibilityObject* coreObject = core(object);
102     if (!coreObject->isAccessibilityRenderObject())
103         return returnString(coreObject->stringValue());
104
105     if (coreObject->isFieldset()) {
106         AccessibilityObject* label = coreObject->titleUIElement();
107         if (label) {
108             AtkObject* atkObject = label->wrapper();
109             if (ATK_IS_TEXT(atkObject))
110                 return atk_text_get_text(ATK_TEXT(atkObject), 0, -1);
111         }
112     }
113
114     if (coreObject->isControl()) {
115         AccessibilityObject* label = coreObject->correspondingLabelForControlElement();
116         if (label) {
117             AtkObject* atkObject = label->wrapper();
118             if (ATK_IS_TEXT(atkObject))
119                 return atk_text_get_text(ATK_TEXT(atkObject), 0, -1);
120         }
121
122         // Try text under the node.
123         String textUnder = coreObject->textUnderElement();
124         if (textUnder.length())
125             return returnString(textUnder);
126     }
127
128     if (coreObject->isImage() || coreObject->isInputImage()) {
129         Node* node = coreObject->node();
130         if (node && node->isHTMLElement()) {
131             // Get the attribute rather than altText String so as not to fall back on title.
132             String alt = toHTMLElement(node)->getAttribute(HTMLNames::altAttr);
133             if (!alt.isEmpty())
134                 return returnString(alt);
135         }
136     }
137
138     // Fallback for the webArea object: just return the document's title.
139     if (coreObject->isWebArea()) {
140         Document* document = coreObject->document();
141         if (document)
142             return returnString(document->title());
143     }
144
145     // Nothing worked so far, try with the AccessibilityObject's
146     // title() before going ahead with stringValue().
147     String axTitle = accessibilityTitle(coreObject);
148     if (!axTitle.isEmpty())
149         return returnString(axTitle);
150
151     return returnString(coreObject->stringValue());
152 }
153
154 static const gchar* webkitAccessibleGetDescription(AtkObject* object)
155 {
156     AccessibilityObject* coreObject = core(object);
157     Node* node = 0;
158     if (coreObject->isAccessibilityRenderObject())
159         node = coreObject->node();
160     if (!node || !node->isHTMLElement() || coreObject->ariaRoleAttribute() != UnknownRole)
161         return returnString(accessibilityDescription(coreObject));
162
163     // atk_table_get_summary returns an AtkObject. We have no summary object, so expose summary here.
164     if (coreObject->roleValue() == TableRole) {
165         String summary = static_cast<HTMLTableElement*>(node)->summary();
166         if (!summary.isEmpty())
167             return returnString(summary);
168     }
169
170     // The title attribute should be reliably available as the object's descripton.
171     // We do not want to fall back on other attributes in its absence. See bug 25524.
172     String title = toHTMLElement(node)->title();
173     if (!title.isEmpty())
174         return returnString(title);
175
176     return returnString(accessibilityDescription(coreObject));
177 }
178
179 static void setAtkRelationSetFromCoreObject(AccessibilityObject* coreObject, AtkRelationSet* relationSet)
180 {
181     if (coreObject->isFieldset()) {
182         AccessibilityObject* label = coreObject->titleUIElement();
183         if (label)
184             atk_relation_set_add_relation_by_type(relationSet, ATK_RELATION_LABELLED_BY, label->wrapper());
185         return;
186     }
187
188     if (coreObject->roleValue() == LegendRole) {
189         for (AccessibilityObject* parent = coreObject->parentObjectUnignored(); parent; parent = parent->parentObjectUnignored()) {
190             if (parent->isFieldset()) {
191                 atk_relation_set_add_relation_by_type(relationSet, ATK_RELATION_LABEL_FOR, parent->wrapper());
192                 break;
193             }
194         }
195         return;
196     }
197
198     if (coreObject->isControl()) {
199         AccessibilityObject* label = coreObject->correspondingLabelForControlElement();
200         if (label)
201             atk_relation_set_add_relation_by_type(relationSet, ATK_RELATION_LABELLED_BY, label->wrapper());
202     } else {
203         AccessibilityObject* control = coreObject->correspondingControlForLabelElement();
204         if (control)
205             atk_relation_set_add_relation_by_type(relationSet, ATK_RELATION_LABEL_FOR, control->wrapper());
206     }
207 }
208
209 static gpointer webkit_accessible_parent_class = 0;
210
211 static bool isRootObject(AccessibilityObject* coreObject)
212 {
213     // The root accessible object in WebCore is always an object with
214     // the ScrolledArea role with one child with the WebArea role.
215     if (!coreObject || !coreObject->isScrollView())
216         return false;
217
218     AccessibilityObject* firstChild = coreObject->firstChild();
219     if (!firstChild || !firstChild->isWebArea())
220         return false;
221
222     return true;
223 }
224
225 static AtkObject* atkParentOfRootObject(AtkObject* object)
226 {
227     AccessibilityObject* coreObject = core(object);
228     AccessibilityObject* coreParent = coreObject->parentObjectUnignored();
229
230     // The top level object claims to not have a parent. This makes it
231     // impossible for assistive technologies to ascend the accessible
232     // hierarchy all the way to the application. (Bug 30489)
233     if (!coreParent && isRootObject(coreObject)) {
234         Document* document = coreObject->document();
235         if (!document)
236             return 0;
237
238 #if PLATFORM(GTK)
239         HostWindow* hostWindow = document->view()->hostWindow();
240         if (hostWindow) {
241             PlatformPageClient scrollView = hostWindow->platformPageClient();
242             if (scrollView) {
243                 GtkWidget* scrollViewParent = gtk_widget_get_parent(scrollView);
244                 if (scrollViewParent)
245                     return gtk_widget_get_accessible(scrollViewParent);
246             }
247         }
248 #endif // PLATFORM(GTK)
249     }
250
251     if (!coreParent)
252         return 0;
253
254     return coreParent->wrapper();
255 }
256
257 static AtkObject* webkitAccessibleGetParent(AtkObject* object)
258 {
259     // Check first if the parent has been already set.
260     AtkObject* accessibleParent = ATK_OBJECT_CLASS(webkit_accessible_parent_class)->get_parent(object);
261     if (accessibleParent)
262         return accessibleParent;
263
264     // Parent not set yet, so try to find it in the hierarchy.
265     AccessibilityObject* coreObject = core(object);
266     if (!coreObject)
267         return 0;
268
269     AccessibilityObject* coreParent = coreObject->parentObjectUnignored();
270
271     if (!coreParent && isRootObject(coreObject))
272         return atkParentOfRootObject(object);
273
274     if (!coreParent)
275         return 0;
276
277     // We don't expose table rows to Assistive technologies, but we
278     // need to have them anyway in the hierarchy from WebCore to
279     // properly perform coordinates calculations when requested.
280     if (coreParent->isTableRow() && coreObject->isTableCell())
281         coreParent = coreParent->parentObjectUnignored();
282
283     return coreParent->wrapper();
284 }
285
286 static gint getNChildrenForTable(AccessibilityObject* coreObject)
287 {
288     AccessibilityObject::AccessibilityChildrenVector tableChildren = coreObject->children();
289     size_t tableChildrenCount = tableChildren.size();
290     size_t cellsCount = 0;
291
292     // Look for the actual index of the cell inside the table.
293     for (unsigned i = 0; i < tableChildrenCount; ++i) {
294         if (tableChildren[i]->isTableRow()) {
295             AccessibilityObject::AccessibilityChildrenVector rowChildren = tableChildren[i]->children();
296             cellsCount += rowChildren.size();
297         } else
298             cellsCount++;
299     }
300
301     return cellsCount;
302 }
303
304 static gint webkitAccessibleGetNChildren(AtkObject* object)
305 {
306     AccessibilityObject* coreObject = core(object);
307
308     // Tables should be treated in a different way because rows should
309     // be bypassed when exposing the accessible hierarchy.
310     if (coreObject->isAccessibilityTable())
311         return getNChildrenForTable(coreObject);
312
313     return coreObject->children().size();
314 }
315
316 static AccessibilityObject* getChildForTable(AccessibilityObject* coreObject, gint index)
317 {
318     AccessibilityObject::AccessibilityChildrenVector tableChildren = coreObject->children();
319     size_t tableChildrenCount = tableChildren.size();
320     size_t cellsCount = 0;
321
322     // Look for the actual index of the cell inside the table.
323     size_t current = static_cast<size_t>(index);
324     for (unsigned i = 0; i < tableChildrenCount; ++i) {
325         if (tableChildren[i]->isTableRow()) {
326             AccessibilityObject::AccessibilityChildrenVector rowChildren = tableChildren[i]->children();
327             size_t rowChildrenCount = rowChildren.size();
328             if (current < cellsCount + rowChildrenCount)
329                 return rowChildren.at(current - cellsCount).get();
330             cellsCount += rowChildrenCount;
331         } else if (cellsCount == current)
332             return tableChildren[i].get();
333         else
334             cellsCount++;
335     }
336
337     // Shouldn't reach if the child was found.
338     return 0;
339 }
340
341 static AtkObject* webkitAccessibleRefChild(AtkObject* object, gint index)
342 {
343     if (index < 0)
344         return 0;
345
346     AccessibilityObject* coreObject = core(object);
347     AccessibilityObject* coreChild = 0;
348
349     // Tables are special cases because rows should be bypassed, but
350     // still taking their cells into account.
351     if (coreObject->isAccessibilityTable())
352         coreChild = getChildForTable(coreObject, index);
353     else {
354         AccessibilityObject::AccessibilityChildrenVector children = coreObject->children();
355         if (static_cast<unsigned>(index) >= children.size())
356             return 0;
357         coreChild = children.at(index).get();
358     }
359
360     if (!coreChild)
361         return 0;
362
363     AtkObject* child = coreChild->wrapper();
364     atk_object_set_parent(child, object);
365     g_object_ref(child);
366
367     return child;
368 }
369
370 static gint getIndexInParentForCellInRow(AccessibilityObject* coreObject)
371 {
372     AccessibilityObject* parent = coreObject->parentObjectUnignored();
373     if (!parent)
374         return -1;
375
376     AccessibilityObject* grandParent = parent->parentObjectUnignored();
377     if (!grandParent)
378         return -1;
379
380     AccessibilityObject::AccessibilityChildrenVector rows = grandParent->children();
381     size_t rowsCount = rows.size();
382     size_t previousCellsCount = 0;
383
384     // Look for the actual index of the cell inside the table.
385     for (unsigned i = 0; i < rowsCount; ++i) {
386         if (!rows[i]->isTableRow())
387             continue;
388
389         AccessibilityObject::AccessibilityChildrenVector cells = rows[i]->children();
390         size_t cellsCount = cells.size();
391
392         if (rows[i] == parent) {
393             for (unsigned j = 0; j < cellsCount; ++j) {
394                 if (cells[j] == coreObject)
395                     return previousCellsCount + j;
396             }
397         }
398
399         previousCellsCount += cellsCount;
400     }
401
402     return -1;
403 }
404
405 static gint webkitAccessibleGetIndexInParent(AtkObject* object)
406 {
407     AccessibilityObject* coreObject = core(object);
408     AccessibilityObject* parent = coreObject->parentObjectUnignored();
409
410     if (!parent && isRootObject(coreObject)) {
411         AtkObject* atkParent = atkParentOfRootObject(object);
412         if (!atkParent)
413             return -1;
414
415         unsigned count = atk_object_get_n_accessible_children(atkParent);
416         for (unsigned i = 0; i < count; ++i) {
417             AtkObject* child = atk_object_ref_accessible_child(atkParent, i);
418             bool childIsObject = child == object;
419             g_object_unref(child);
420             if (childIsObject)
421                 return i;
422         }
423     }
424
425     // Need to calculate the index of the cell in the table, as
426     // rows won't be exposed to assistive technologies.
427     if (parent && parent->isTableRow() && coreObject->isTableCell())
428         return getIndexInParentForCellInRow(coreObject);
429
430     size_t index = parent->children().find(coreObject);
431     return (index == WTF::notFound) ? -1 : index;
432 }
433
434 static AtkAttributeSet* webkitAccessibleGetAttributes(AtkObject* object)
435 {
436     AtkAttributeSet* attributeSet = 0;
437 #if PLATFORM(GTK)
438     attributeSet = addToAtkAttributeSet(attributeSet, "toolkit", "WebKitGtk");
439 #endif
440
441     AccessibilityObject* coreObject = core(object);
442     if (!coreObject)
443         return attributeSet;
444
445     int headingLevel = coreObject->headingLevel();
446     if (headingLevel) {
447         String value = String::number(headingLevel);
448         attributeSet = addToAtkAttributeSet(attributeSet, "level", value.utf8().data());
449     }
450
451     // Set the 'layout-guess' attribute to help Assistive
452     // Technologies know when an exposed table is not data table.
453     if (coreObject->isAccessibilityTable() && !coreObject->isDataTable())
454         attributeSet = addToAtkAttributeSet(attributeSet, "layout-guess", "true");
455
456     return attributeSet;
457 }
458
459 static AtkRole atkRole(AccessibilityRole role)
460 {
461     switch (role) {
462     case UnknownRole:
463         return ATK_ROLE_UNKNOWN;
464     case ButtonRole:
465         return ATK_ROLE_PUSH_BUTTON;
466     case ToggleButtonRole:
467         return ATK_ROLE_TOGGLE_BUTTON;
468     case RadioButtonRole:
469         return ATK_ROLE_RADIO_BUTTON;
470     case CheckBoxRole:
471         return ATK_ROLE_CHECK_BOX;
472     case SliderRole:
473         return ATK_ROLE_SLIDER;
474     case TabGroupRole:
475         return ATK_ROLE_PAGE_TAB_LIST;
476     case TextFieldRole:
477     case TextAreaRole:
478         return ATK_ROLE_ENTRY;
479     case StaticTextRole:
480         return ATK_ROLE_TEXT;
481     case OutlineRole:
482         return ATK_ROLE_TREE;
483     case MenuBarRole:
484         return ATK_ROLE_MENU_BAR;
485     case MenuListPopupRole:
486     case MenuRole:
487         return ATK_ROLE_MENU;
488     case MenuListOptionRole:
489     case MenuItemRole:
490         return ATK_ROLE_MENU_ITEM;
491     case ColumnRole:
492         // return ATK_ROLE_TABLE_COLUMN_HEADER; // Is this right?
493         return ATK_ROLE_UNKNOWN; // Matches Mozilla
494     case RowRole:
495         // return ATK_ROLE_TABLE_ROW_HEADER; // Is this right?
496         return ATK_ROLE_LIST_ITEM; // Matches Mozilla
497     case ToolbarRole:
498         return ATK_ROLE_TOOL_BAR;
499     case BusyIndicatorRole:
500         return ATK_ROLE_PROGRESS_BAR; // Is this right?
501     case ProgressIndicatorRole:
502         // return ATK_ROLE_SPIN_BUTTON; // Some confusion about this role in AccessibilityRenderObject.cpp
503         return ATK_ROLE_PROGRESS_BAR;
504     case WindowRole:
505         return ATK_ROLE_WINDOW;
506     case PopUpButtonRole:
507     case ComboBoxRole:
508         return ATK_ROLE_COMBO_BOX;
509     case SplitGroupRole:
510         return ATK_ROLE_SPLIT_PANE;
511     case SplitterRole:
512         return ATK_ROLE_UNKNOWN;
513     case ColorWellRole:
514         return ATK_ROLE_COLOR_CHOOSER;
515     case ListRole:
516         return ATK_ROLE_LIST;
517     case ScrollBarRole:
518         return ATK_ROLE_SCROLL_BAR;
519     case ScrollAreaRole:
520         return ATK_ROLE_SCROLL_PANE;
521     case GridRole: // Is this right?
522     case TableRole:
523         return ATK_ROLE_TABLE;
524     case ApplicationRole:
525         return ATK_ROLE_APPLICATION;
526     case GroupRole:
527     case RadioGroupRole:
528         return ATK_ROLE_PANEL;
529     case RowHeaderRole: // Row headers are cells after all.
530     case ColumnHeaderRole: // Column headers are cells after all.
531     case CellRole:
532         return ATK_ROLE_TABLE_CELL;
533     case LinkRole:
534     case WebCoreLinkRole:
535     case ImageMapLinkRole:
536         return ATK_ROLE_LINK;
537     case ImageMapRole:
538     case ImageRole:
539         return ATK_ROLE_IMAGE;
540     case ListMarkerRole:
541         return ATK_ROLE_TEXT;
542     case WebAreaRole:
543         // return ATK_ROLE_HTML_CONTAINER; // Is this right?
544         return ATK_ROLE_DOCUMENT_FRAME;
545     case HeadingRole:
546         return ATK_ROLE_HEADING;
547     case ListBoxRole:
548         return ATK_ROLE_LIST;
549     case ListItemRole:
550     case ListBoxOptionRole:
551         return ATK_ROLE_LIST_ITEM;
552     case ParagraphRole:
553         return ATK_ROLE_PARAGRAPH;
554     case LabelRole:
555     case LegendRole:
556         return ATK_ROLE_LABEL;
557     case DivRole:
558         return ATK_ROLE_SECTION;
559     case FormRole:
560         return ATK_ROLE_FORM;
561     case CanvasRole:
562         return ATK_ROLE_CANVAS;
563     case HorizontalRuleRole:
564         return ATK_ROLE_SEPARATOR;
565     case SpinButtonRole:
566         return ATK_ROLE_SPIN_BUTTON;
567     default:
568         return ATK_ROLE_UNKNOWN;
569     }
570 }
571
572 static AtkRole webkitAccessibleGetRole(AtkObject* object)
573 {
574     AccessibilityObject* coreObject = core(object);
575
576     if (!coreObject)
577         return ATK_ROLE_UNKNOWN;
578
579     // Note: Why doesn't WebCore have a password field for this
580     if (coreObject->isPasswordField())
581         return ATK_ROLE_PASSWORD_TEXT;
582
583     return atkRole(coreObject->roleValue());
584 }
585
586 static bool isTextWithCaret(AccessibilityObject* coreObject)
587 {
588     if (!coreObject || !coreObject->isAccessibilityRenderObject())
589         return false;
590
591     Document* document = coreObject->document();
592     if (!document)
593         return false;
594
595     Frame* frame = document->frame();
596     if (!frame)
597         return false;
598
599     Settings* settings = frame->settings();
600     if (!settings || !settings->caretBrowsingEnabled())
601         return false;
602
603     // Check text objects and paragraphs only.
604     AtkObject* axObject = coreObject->wrapper();
605     AtkRole role = axObject ? atk_object_get_role(axObject) : ATK_ROLE_INVALID;
606     if (role != ATK_ROLE_TEXT && role != ATK_ROLE_PARAGRAPH)
607         return false;
608
609     // Finally, check whether the caret is set in the current object.
610     VisibleSelection selection = coreObject->selection();
611     if (!selection.isCaret())
612         return false;
613
614     return selectionBelongsToObject(coreObject, selection);
615 }
616
617 static void setAtkStateSetFromCoreObject(AccessibilityObject* coreObject, AtkStateSet* stateSet)
618 {
619     AccessibilityObject* parent = coreObject->parentObject();
620     bool isListBoxOption = parent && parent->isListBox();
621
622     // Please keep the state list in alphabetical order
623     if (coreObject->isChecked())
624         atk_state_set_add_state(stateSet, ATK_STATE_CHECKED);
625
626     // FIXME: isReadOnly does not seem to do the right thing for
627     // controls, so check explicitly for them. In addition, because
628     // isReadOnly is false for listBoxOptions, we need to add one
629     // more check so that we do not present them as being "editable".
630     if ((!coreObject->isReadOnly()
631          || (coreObject->isControl() && coreObject->canSetValueAttribute()))
632         && !isListBoxOption)
633         atk_state_set_add_state(stateSet, ATK_STATE_EDITABLE);
634
635     // FIXME: Put both ENABLED and SENSITIVE together here for now
636     if (coreObject->isEnabled()) {
637         atk_state_set_add_state(stateSet, ATK_STATE_ENABLED);
638         atk_state_set_add_state(stateSet, ATK_STATE_SENSITIVE);
639     }
640
641     if (coreObject->canSetExpandedAttribute())
642         atk_state_set_add_state(stateSet, ATK_STATE_EXPANDABLE);
643
644     if (coreObject->isExpanded())
645         atk_state_set_add_state(stateSet, ATK_STATE_EXPANDED);
646
647     if (coreObject->canSetFocusAttribute())
648         atk_state_set_add_state(stateSet, ATK_STATE_FOCUSABLE);
649
650     if (coreObject->isFocused() || isTextWithCaret(coreObject))
651         atk_state_set_add_state(stateSet, ATK_STATE_FOCUSED);
652
653     // TODO: ATK_STATE_HORIZONTAL
654
655     if (coreObject->isIndeterminate())
656         atk_state_set_add_state(stateSet, ATK_STATE_INDETERMINATE);
657
658     if (coreObject->isMultiSelectable())
659         atk_state_set_add_state(stateSet, ATK_STATE_MULTISELECTABLE);
660
661     // TODO: ATK_STATE_OPAQUE
662
663     if (coreObject->isPressed())
664         atk_state_set_add_state(stateSet, ATK_STATE_PRESSED);
665
666     // TODO: ATK_STATE_SELECTABLE_TEXT
667
668     if (coreObject->canSetSelectedAttribute()) {
669         atk_state_set_add_state(stateSet, ATK_STATE_SELECTABLE);
670         // Items in focusable lists have both STATE_SELECT{ABLE,ED}
671         // and STATE_FOCUS{ABLE,ED}. We'll fake the latter based on
672         // the former.
673         if (isListBoxOption)
674             atk_state_set_add_state(stateSet, ATK_STATE_FOCUSABLE);
675     }
676
677     if (coreObject->isSelected()) {
678         atk_state_set_add_state(stateSet, ATK_STATE_SELECTED);
679         // Items in focusable lists have both STATE_SELECT{ABLE,ED}
680         // and STATE_FOCUS{ABLE,ED}. We'll fake the latter based on the
681         // former.
682         if (isListBoxOption)
683             atk_state_set_add_state(stateSet, ATK_STATE_FOCUSED);
684     }
685
686     // FIXME: Group both SHOWING and VISIBLE here for now
687     // Not sure how to handle this in WebKit, see bug
688     // http://bugzilla.gnome.org/show_bug.cgi?id=509650 for other
689     // issues with SHOWING vs VISIBLE.
690     if (!coreObject->isOffScreen()) {
691         atk_state_set_add_state(stateSet, ATK_STATE_SHOWING);
692         atk_state_set_add_state(stateSet, ATK_STATE_VISIBLE);
693     }
694
695     // Mutually exclusive, so we group these two
696     if (coreObject->roleValue() == TextFieldRole)
697         atk_state_set_add_state(stateSet, ATK_STATE_SINGLE_LINE);
698     else if (coreObject->roleValue() == TextAreaRole)
699         atk_state_set_add_state(stateSet, ATK_STATE_MULTI_LINE);
700
701     // TODO: ATK_STATE_SENSITIVE
702
703     // TODO: ATK_STATE_VERTICAL
704
705     if (coreObject->isVisited())
706         atk_state_set_add_state(stateSet, ATK_STATE_VISITED);
707 }
708
709 static AtkStateSet* webkitAccessibleRefStateSet(AtkObject* object)
710 {
711     AtkStateSet* stateSet = ATK_OBJECT_CLASS(webkit_accessible_parent_class)->ref_state_set(object);
712     AccessibilityObject* coreObject = core(object);
713
714     if (coreObject == fallbackObject()) {
715         atk_state_set_add_state(stateSet, ATK_STATE_DEFUNCT);
716         return stateSet;
717     }
718
719     // Text objects must be focusable.
720     AtkRole role = atk_object_get_role(object);
721     if (role == ATK_ROLE_TEXT || role == ATK_ROLE_PARAGRAPH)
722         atk_state_set_add_state(stateSet, ATK_STATE_FOCUSABLE);
723
724     setAtkStateSetFromCoreObject(coreObject, stateSet);
725     return stateSet;
726 }
727
728 static AtkRelationSet* webkitAccessibleRefRelationSet(AtkObject* object)
729 {
730     AtkRelationSet* relationSet = ATK_OBJECT_CLASS(webkit_accessible_parent_class)->ref_relation_set(object);
731     AccessibilityObject* coreObject = core(object);
732
733     setAtkRelationSetFromCoreObject(coreObject, relationSet);
734
735     return relationSet;
736 }
737
738 static void webkitAccessibleInit(AtkObject* object, gpointer data)
739 {
740     if (ATK_OBJECT_CLASS(webkit_accessible_parent_class)->initialize)
741         ATK_OBJECT_CLASS(webkit_accessible_parent_class)->initialize(object, data);
742
743     WEBKIT_ACCESSIBLE(object)->m_object = reinterpret_cast<AccessibilityObject*>(data);
744 }
745
746 static void webkitAccessibleFinalize(GObject* object)
747 {
748     // This is a good time to clear the return buffer.
749     returnString(String());
750
751     G_OBJECT_CLASS(webkit_accessible_parent_class)->finalize(object);
752 }
753
754 static void webkit_accessible_class_init(AtkObjectClass* klass)
755 {
756     GObjectClass* gobjectClass = G_OBJECT_CLASS(klass);
757
758     webkit_accessible_parent_class = g_type_class_peek_parent(klass);
759
760     gobjectClass->finalize = webkitAccessibleFinalize;
761
762     klass->initialize = webkitAccessibleInit;
763     klass->get_name = webkitAccessibleGetName;
764     klass->get_description = webkitAccessibleGetDescription;
765     klass->get_parent = webkitAccessibleGetParent;
766     klass->get_n_children = webkitAccessibleGetNChildren;
767     klass->ref_child = webkitAccessibleRefChild;
768     klass->get_role = webkitAccessibleGetRole;
769     klass->ref_state_set = webkitAccessibleRefStateSet;
770     klass->get_index_in_parent = webkitAccessibleGetIndexInParent;
771     klass->get_attributes = webkitAccessibleGetAttributes;
772     klass->ref_relation_set = webkitAccessibleRefRelationSet;
773 }
774
775 GType
776 webkit_accessible_get_type(void)
777 {
778     static volatile gsize type_volatile = 0;
779
780     if (g_once_init_enter(&type_volatile)) {
781         static const GTypeInfo tinfo = {
782             sizeof(WebKitAccessibleClass),
783             (GBaseInitFunc) 0,
784             (GBaseFinalizeFunc) 0,
785             (GClassInitFunc) webkit_accessible_class_init,
786             (GClassFinalizeFunc) 0,
787             0, /* class data */
788             sizeof(WebKitAccessible), /* instance size */
789             0, /* nb preallocs */
790             (GInstanceInitFunc) 0,
791             0 /* value table */
792         };
793
794         GType type = g_type_register_static(ATK_TYPE_OBJECT,
795                                             "WebKitAccessible", &tinfo, GTypeFlags(0));
796         g_once_init_leave(&type_volatile, type);
797     }
798
799     return type_volatile;
800 }
801
802 static const GInterfaceInfo AtkInterfacesInitFunctions[] = {
803     {reinterpret_cast<GInterfaceInitFunc>(webkitAccessibleActionInterfaceInit), 0, 0},
804     {reinterpret_cast<GInterfaceInitFunc>(webkitAccessibleSelectionInterfaceInit), 0, 0},
805     {reinterpret_cast<GInterfaceInitFunc>(webkitAccessibleEditableTextInterfaceInit), 0, 0},
806     {reinterpret_cast<GInterfaceInitFunc>(webkitAccessibleTextInterfaceInit), 0, 0},
807     {reinterpret_cast<GInterfaceInitFunc>(webkitAccessibleComponentInterfaceInit), 0, 0},
808     {reinterpret_cast<GInterfaceInitFunc>(webkitAccessibleImageInterfaceInit), 0, 0},
809     {reinterpret_cast<GInterfaceInitFunc>(webkitAccessibleTableInterfaceInit), 0, 0},
810     {reinterpret_cast<GInterfaceInitFunc>(webkitAccessibleHypertextInterfaceInit), 0, 0},
811     {reinterpret_cast<GInterfaceInitFunc>(webkitAccessibleHyperlinkImplInterfaceInit), 0, 0},
812     {reinterpret_cast<GInterfaceInitFunc>(webkitAccessibleDocumentInterfaceInit), 0, 0},
813     {reinterpret_cast<GInterfaceInitFunc>(webkitAccessibleValueInterfaceInit), 0, 0}
814 };
815
816 enum WAIType {
817     WAI_ACTION,
818     WAI_SELECTION,
819     WAI_EDITABLE_TEXT,
820     WAI_TEXT,
821     WAI_COMPONENT,
822     WAI_IMAGE,
823     WAI_TABLE,
824     WAI_HYPERTEXT,
825     WAI_HYPERLINK,
826     WAI_DOCUMENT,
827     WAI_VALUE,
828 };
829
830 static GType GetAtkInterfaceTypeFromWAIType(WAIType type)
831 {
832     switch (type) {
833     case WAI_ACTION:
834         return ATK_TYPE_ACTION;
835     case WAI_SELECTION:
836         return ATK_TYPE_SELECTION;
837     case WAI_EDITABLE_TEXT:
838         return ATK_TYPE_EDITABLE_TEXT;
839     case WAI_TEXT:
840         return ATK_TYPE_TEXT;
841     case WAI_COMPONENT:
842         return ATK_TYPE_COMPONENT;
843     case WAI_IMAGE:
844         return ATK_TYPE_IMAGE;
845     case WAI_TABLE:
846         return ATK_TYPE_TABLE;
847     case WAI_HYPERTEXT:
848         return ATK_TYPE_HYPERTEXT;
849     case WAI_HYPERLINK:
850         return ATK_TYPE_HYPERLINK_IMPL;
851     case WAI_DOCUMENT:
852         return ATK_TYPE_DOCUMENT;
853     case WAI_VALUE:
854         return ATK_TYPE_VALUE;
855     }
856
857     return G_TYPE_INVALID;
858 }
859
860 static bool roleIsTextType(AccessibilityRole role)
861 {
862     return role == ParagraphRole || role == HeadingRole || role == DivRole || role == CellRole || role == ListItemRole;
863 }
864
865 static guint16 getInterfaceMaskFromObject(AccessibilityObject* coreObject)
866 {
867     guint16 interfaceMask = 0;
868
869     // Component interface is always supported
870     interfaceMask |= 1 << WAI_COMPONENT;
871
872     AccessibilityRole role = coreObject->roleValue();
873
874     // Action
875     // As the implementation of the AtkAction interface is a very
876     // basic one (just relays in executing the default action for each
877     // object, and only supports having one action per object), it is
878     // better just to implement this interface for every instance of
879     // the WebKitAccessible class and let WebCore decide what to do.
880     interfaceMask |= 1 << WAI_ACTION;
881
882     // Selection
883     if (coreObject->isListBox() || coreObject->isMenuList())
884         interfaceMask |= 1 << WAI_SELECTION;
885
886     // Get renderer if available.
887     RenderObject* renderer = 0;
888     if (coreObject->isAccessibilityRenderObject())
889         renderer = coreObject->renderer();
890
891     // Hyperlink (links and embedded objects).
892     if (coreObject->isLink() || (renderer && renderer->isReplaced()))
893         interfaceMask |= 1 << WAI_HYPERLINK;
894
895     // Text & Editable Text
896     if (role == StaticTextRole || coreObject->isMenuListOption())
897         interfaceMask |= 1 << WAI_TEXT;
898     else {
899         if (coreObject->isTextControl()) {
900             interfaceMask |= 1 << WAI_TEXT;
901             if (!coreObject->isReadOnly())
902                 interfaceMask |= 1 << WAI_EDITABLE_TEXT;
903         } else {
904             if (role != TableRole) {
905                 interfaceMask |= 1 << WAI_HYPERTEXT;
906                 if ((renderer && renderer->childrenInline()) || roleIsTextType(role))
907                     interfaceMask |= 1 << WAI_TEXT;
908             }
909
910             // Add the TEXT interface for list items whose
911             // first accessible child has a text renderer
912             if (role == ListItemRole) {
913                 AccessibilityObject::AccessibilityChildrenVector children = coreObject->children();
914                 if (children.size()) {
915                     AccessibilityObject* axRenderChild = children.at(0).get();
916                     interfaceMask |= getInterfaceMaskFromObject(axRenderChild);
917                 }
918             }
919         }
920     }
921
922     // Image
923     if (coreObject->isImage())
924         interfaceMask |= 1 << WAI_IMAGE;
925
926     // Table
927     if (role == TableRole)
928         interfaceMask |= 1 << WAI_TABLE;
929
930     // Document
931     if (role == WebAreaRole)
932         interfaceMask |= 1 << WAI_DOCUMENT;
933
934     // Value
935     if (role == SliderRole || role == SpinButtonRole)
936         interfaceMask |= 1 << WAI_VALUE;
937
938     return interfaceMask;
939 }
940
941 static const char* getUniqueAccessibilityTypeName(guint16 interfaceMask)
942 {
943 #define WAI_TYPE_NAME_LEN (30) /* Enough for prefix + 5 hex characters (max) */
944     static char name[WAI_TYPE_NAME_LEN + 1];
945
946     g_sprintf(name, "WAIType%x", interfaceMask);
947     name[WAI_TYPE_NAME_LEN] = '\0';
948
949     return name;
950 }
951
952 static GType getAccessibilityTypeFromObject(AccessibilityObject* coreObject)
953 {
954     static const GTypeInfo typeInfo = {
955         sizeof(WebKitAccessibleClass),
956         (GBaseInitFunc) 0,
957         (GBaseFinalizeFunc) 0,
958         (GClassInitFunc) 0,
959         (GClassFinalizeFunc) 0,
960         0, /* class data */
961         sizeof(WebKitAccessible), /* instance size */
962         0, /* nb preallocs */
963         (GInstanceInitFunc) 0,
964         0 /* value table */
965     };
966
967     guint16 interfaceMask = getInterfaceMaskFromObject(coreObject);
968     const char* atkTypeName = getUniqueAccessibilityTypeName(interfaceMask);
969     GType type = g_type_from_name(atkTypeName);
970     if (type)
971         return type;
972
973     type = g_type_register_static(WEBKIT_TYPE_ACCESSIBLE,
974                                   atkTypeName,
975                                   &typeInfo, GTypeFlags(0));
976     for (guint i = 0; i < G_N_ELEMENTS(AtkInterfacesInitFunctions); i++) {
977         if (interfaceMask & (1 << i))
978             g_type_add_interface_static(type,
979                                         GetAtkInterfaceTypeFromWAIType(static_cast<WAIType>(i)),
980                                         &AtkInterfacesInitFunctions[i]);
981     }
982
983     return type;
984 }
985
986 WebKitAccessible* webkitAccessibleNew(AccessibilityObject* coreObject)
987 {
988     GType type = getAccessibilityTypeFromObject(coreObject);
989     AtkObject* object = static_cast<AtkObject*>(g_object_new(type, 0));
990
991     atk_object_initialize(object, coreObject);
992
993     return WEBKIT_ACCESSIBLE(object);
994 }
995
996 AccessibilityObject* webkitAccessibleGetAccessibilityObject(WebKitAccessible* accessible)
997 {
998     return accessible->m_object;
999 }
1000
1001 void webkitAccessibleDetach(WebKitAccessible* accessible)
1002 {
1003     ASSERT(accessible->m_object);
1004
1005     if (core(accessible)->roleValue() == WebAreaRole)
1006         g_signal_emit_by_name(accessible, "state-change", "defunct", true);
1007
1008     // We replace the WebCore AccessibilityObject with a fallback object that
1009     // provides default implementations to avoid repetitive null-checking after
1010     // detachment.
1011     accessible->m_object = fallbackObject();
1012 }
1013
1014 AtkObject* webkitAccessibleGetFocusedElement(WebKitAccessible* accessible)
1015 {
1016     if (!accessible->m_object)
1017         return 0;
1018
1019     RefPtr<AccessibilityObject> focusedObj = accessible->m_object->focusedUIElement();
1020     if (!focusedObj)
1021         return 0;
1022
1023     return focusedObj->wrapper();
1024 }
1025
1026 AccessibilityObject* objectFocusedAndCaretOffsetUnignored(AccessibilityObject* referenceObject, int& offset)
1027 {
1028     // Indication that something bogus has transpired.
1029     offset = -1;
1030
1031     Document* document = referenceObject->document();
1032     if (!document)
1033         return 0;
1034
1035     Node* focusedNode = referenceObject->selection().end().containerNode();
1036     if (!focusedNode)
1037         return 0;
1038
1039     RenderObject* focusedRenderer = focusedNode->renderer();
1040     if (!focusedRenderer)
1041         return 0;
1042
1043     AccessibilityObject* focusedObject = document->axObjectCache()->getOrCreate(focusedRenderer);
1044     if (!focusedObject)
1045         return 0;
1046
1047     // Look for the actual (not ignoring accessibility) selected object.
1048     AccessibilityObject* firstUnignoredParent = focusedObject;
1049     if (firstUnignoredParent->accessibilityIsIgnored())
1050         firstUnignoredParent = firstUnignoredParent->parentObjectUnignored();
1051     if (!firstUnignoredParent)
1052         return 0;
1053
1054     // Don't ignore links if the offset is being requested for a link.
1055     if (!referenceObject->isLink() && firstUnignoredParent->isLink())
1056         firstUnignoredParent = firstUnignoredParent->parentObjectUnignored();
1057     if (!firstUnignoredParent)
1058         return 0;
1059
1060     // The reference object must either coincide with the focused
1061     // object being considered, or be a descendant of it.
1062     if (referenceObject->isDescendantOfObject(firstUnignoredParent))
1063         referenceObject = firstUnignoredParent;
1064
1065     Node* startNode = 0;
1066     if (firstUnignoredParent != referenceObject || firstUnignoredParent->isTextControl()) {
1067         // We need to use the first child's node of the reference
1068         // object as the start point to calculate the caret offset
1069         // because we want it to be relative to the object of
1070         // reference, not just to the focused object (which could have
1071         // previous siblings which should be taken into account too).
1072         AccessibilityObject* axFirstChild = referenceObject->firstChild();
1073         if (axFirstChild)
1074             startNode = axFirstChild->node();
1075     }
1076     if (!startNode)
1077         startNode = firstUnignoredParent->node();
1078
1079     // Check if the node for the first parent object not ignoring
1080     // accessibility is null again before using it. This might happen
1081     // with certain kind of accessibility objects, such as the root
1082     // one (the scroller containing the webArea object).
1083     if (!startNode)
1084         return 0;
1085
1086     VisiblePosition startPosition = VisiblePosition(positionBeforeNode(startNode), DOWNSTREAM);
1087     VisiblePosition endPosition = firstUnignoredParent->selection().visibleEnd();
1088
1089     if (startPosition == endPosition)
1090         offset = 0;
1091     else if (!isStartOfLine(endPosition)) {
1092         RefPtr<Range> range = makeRange(startPosition, endPosition.previous());
1093         offset = TextIterator::rangeLength(range.get(), true) + 1;
1094     } else {
1095         RefPtr<Range> range = makeRange(startPosition, endPosition);
1096         offset = TextIterator::rangeLength(range.get(), true);
1097     }
1098
1099     return firstUnignoredParent;
1100 }
1101
1102 #endif // HAVE(ACCESSIBILITY)