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