2009-12-07 Joanmarie Diggs <joanmarie.diggs@gmail.com>
[WebKit-https.git] / WebCore / accessibility / gtk / AccessibilityObjectWrapperAtk.cpp
1 /*
2  * Copyright (C) 2008 Nuanti Ltd.
3  * Copyright (C) 2009 Igalia S.L.
4  * Copyright (C) 2009 Jan Alonzo
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 "AccessibilityObjectWrapperAtk.h"
33
34 #if HAVE(ACCESSIBILITY)
35
36 #include "AXObjectCache.h"
37 #include "AccessibilityListBox.h"
38 #include "AccessibilityListBoxOption.h"
39 #include "AccessibilityRenderObject.h"
40 #include "AccessibilityTable.h"
41 #include "AccessibilityTableCell.h"
42 #include "AccessibilityTableColumn.h"
43 #include "AccessibilityTableRow.h"
44 #include "AtomicString.h"
45 #include "CString.h"
46 #include "Document.h"
47 #include "DocumentType.h"
48 #include "Editor.h"
49 #include "Frame.h"
50 #include "FrameView.h"
51 #include "HostWindow.h"
52 #include "HTMLNames.h"
53 #include "HTMLTableCaptionElement.h"
54 #include "HTMLTableElement.h"
55 #include "InlineTextBox.h"
56 #include "IntRect.h"
57 #include "NotImplemented.h"
58 #include "RenderText.h"
59 #include "TextEncoding.h"
60
61 #include <atk/atk.h>
62 #include <glib.h>
63 #include <glib/gprintf.h>
64 #include <libgail-util/gail-util.h>
65 #include <pango/pango.h>
66
67 using namespace WebCore;
68
69 static AccessibilityObject* fallbackObject()
70 {
71     static AXObjectCache* fallbackCache = new AXObjectCache;
72     static AccessibilityObject* object = 0;
73     if (!object) {
74         // FIXME: using fallbackCache->getOrCreate(ListBoxOptionRole) is a hack
75         object = fallbackCache->getOrCreate(ListBoxOptionRole);
76         object->ref();
77     }
78
79     return object;
80 }
81
82 // Used to provide const char* returns.
83 static const char* returnString(const String& str)
84 {
85     static CString returnedString;
86     returnedString = str.utf8();
87     return returnedString.data();
88 }
89
90 static AccessibilityObject* core(WebKitAccessible* accessible)
91 {
92     if (!accessible)
93         return 0;
94
95     return accessible->m_object;
96 }
97
98 static AccessibilityObject* core(AtkObject* object)
99 {
100     if (!WEBKIT_IS_ACCESSIBLE(object))
101         return 0;
102
103     return core(WEBKIT_ACCESSIBLE(object));
104 }
105
106 static AccessibilityObject* core(AtkAction* action)
107 {
108     return core(ATK_OBJECT(action));
109 }
110
111 static AccessibilityObject* core(AtkSelection* selection)
112 {
113     return core(ATK_OBJECT(selection));
114 }
115
116 static AccessibilityObject* core(AtkText* text)
117 {
118     return core(ATK_OBJECT(text));
119 }
120
121 static AccessibilityObject* core(AtkEditableText* text)
122 {
123     return core(ATK_OBJECT(text));
124 }
125
126 static AccessibilityObject* core(AtkComponent* component)
127 {
128     return core(ATK_OBJECT(component));
129 }
130
131 static AccessibilityObject* core(AtkImage* image)
132 {
133     return core(ATK_OBJECT(image));
134 }
135
136 static AccessibilityObject* core(AtkTable* table)
137 {
138     return core(ATK_OBJECT(table));
139 }
140
141 static AccessibilityObject* core(AtkDocument* document)
142 {
143     return core(ATK_OBJECT(document));
144 }
145
146 static const gchar* nameFromChildren(AccessibilityObject* object)
147 {
148     if (!object)
149         return 0;
150
151     AccessibilityRenderObject::AccessibilityChildrenVector children = object->children();
152     // Currently, object->stringValue() should be an empty String. This might not be the case down the road.
153     String name = object->stringValue();
154     for (unsigned i = 0; i < children.size(); ++i)
155         name += children.at(i).get()->stringValue();
156     return returnString(name);
157 }
158
159 static const gchar* webkit_accessible_get_name(AtkObject* object)
160 {
161     AccessibilityObject* coreObject = core(object);
162     if (!coreObject->isAccessibilityRenderObject())
163         return returnString(coreObject->stringValue());
164
165     AccessibilityRenderObject* renderObject = static_cast<AccessibilityRenderObject*>(coreObject);
166     if (coreObject->isControl()) {
167         AccessibilityObject* label = renderObject->correspondingLabelForControlElement();
168         if (label)
169             return returnString(nameFromChildren(label));
170     }
171
172     if (renderObject->isImage() || renderObject->isInputImage()) {
173         Node* node = renderObject->renderer()->node();
174         if (node && node->isHTMLElement()) {
175             // Get the attribute rather than altText String so as not to fall back on title.
176             String alt = static_cast<HTMLElement*>(node)->getAttribute(HTMLNames::altAttr);
177             if (!alt.isEmpty())
178                 return returnString(alt);
179         }
180     }
181
182     return returnString(coreObject->stringValue());
183 }
184
185 static const gchar* webkit_accessible_get_description(AtkObject* object)
186 {
187     AccessibilityObject* coreObject = core(object);
188     Node* node = 0;
189     if (coreObject->isAccessibilityRenderObject())
190         node = static_cast<AccessibilityRenderObject*>(coreObject)->renderer()->node();
191     if (!node || !node->isHTMLElement() || coreObject->ariaRoleAttribute() != UnknownRole)
192         return returnString(coreObject->accessibilityDescription());
193
194     // atk_table_get_summary returns an AtkObject. We have no summary object, so expose summary here.
195     if (coreObject->roleValue() == TableRole) {
196         String summary = static_cast<HTMLTableElement*>(node)->summary();
197         if (!summary.isEmpty())
198             return returnString(summary);
199     }
200
201     // The title attribute should be reliably available as the object's descripton.
202     // We do not want to fall back on other attributes in its absence. See bug 25524.
203     String title = static_cast<HTMLElement*>(node)->title();
204     if (!title.isEmpty())
205         return returnString(title);
206
207     return returnString(coreObject->accessibilityDescription());
208 }
209
210 static void setAtkRelationSetFromCoreObject(AccessibilityObject* coreObject, AtkRelationSet* relationSet)
211 {
212     AccessibilityRenderObject* accObject = static_cast<AccessibilityRenderObject*>(coreObject);
213     if (accObject->isControl()) {
214         AccessibilityObject* label = accObject->correspondingLabelForControlElement();
215         if (label)
216             atk_relation_set_add_relation_by_type(relationSet, ATK_RELATION_LABELLED_BY, label->wrapper());
217     } else {
218         AccessibilityObject* control = accObject->correspondingControlForLabelElement();
219         if (control)
220             atk_relation_set_add_relation_by_type(relationSet, ATK_RELATION_LABEL_FOR, control->wrapper());
221     }
222 }
223
224 static gpointer webkit_accessible_parent_class = 0;
225
226 static AtkObject* atkParentOfWebView(AtkObject* object)
227 {
228     AccessibilityObject* coreParent = core(object)->parentObjectUnignored();
229
230     // The top level web view 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 && core(object)->isWebArea()) {
234         HostWindow* hostWindow = core(object)->document()->view()->hostWindow();
235         if (hostWindow) {
236             PlatformPageClient webView = hostWindow->platformPageClient();
237             if (webView) {
238                 GtkWidget* webViewParent = gtk_widget_get_parent(webView);
239                 if (webViewParent)
240                     return gtk_widget_get_accessible(webViewParent);
241             }
242         }
243     }
244
245     if (!coreParent)
246         return 0;
247
248     return coreParent->wrapper();
249 }
250
251 static AtkObject* webkit_accessible_get_parent(AtkObject* object)
252 {
253     AccessibilityObject* coreParent = core(object)->parentObjectUnignored();
254     if (!coreParent && core(object)->isWebArea())
255         return atkParentOfWebView(object);
256
257     if (!coreParent)
258         return 0;
259
260     return coreParent->wrapper();
261 }
262
263 static gint webkit_accessible_get_n_children(AtkObject* object)
264 {
265     return core(object)->children().size();
266 }
267
268 static AtkObject* webkit_accessible_ref_child(AtkObject* object, gint index)
269 {
270     AccessibilityObject* coreObject = core(object);
271     AccessibilityObject::AccessibilityChildrenVector children = coreObject->children();
272     if (index < 0 || static_cast<unsigned>(index) >= children.size())
273         return 0;
274
275     AccessibilityObject* coreChild = children.at(index).get();
276
277     if (!coreChild)
278         return 0;
279
280     AtkObject* child = coreChild->wrapper();
281     atk_object_set_parent(child, object);
282     g_object_ref(child);
283
284     return child;
285 }
286
287 static gint webkit_accessible_get_index_in_parent(AtkObject* object)
288 {
289     AccessibilityObject* coreObject = core(object);
290     AccessibilityObject* parent = coreObject->parentObjectUnignored();
291
292     if (!parent && core(object)->isWebArea()) {
293         AtkObject* atkParent = atkParentOfWebView(object);
294         if (!atkParent)
295             return -1;
296
297         unsigned count = atk_object_get_n_accessible_children(atkParent);
298         for (unsigned i = 0; i < count; ++i) {
299             AtkObject* child = atk_object_ref_accessible_child(atkParent, i);
300             bool childIsObject = child == object;
301             g_object_unref(child);
302             if (childIsObject)
303                 return i;
304         }
305     }
306
307     AccessibilityObject::AccessibilityChildrenVector children = parent->children();
308     unsigned count = children.size();
309     for (unsigned i = 0; i < count; ++i) {
310         if (children[i] == coreObject)
311             return i;
312     }
313
314     return -1;
315 }
316
317 static AtkAttributeSet* addAttributeToSet(AtkAttributeSet* attributeSet, const char* name, const char* value)
318 {
319     AtkAttribute* attribute = static_cast<AtkAttribute*>(g_malloc(sizeof(AtkAttribute)));
320     attribute->name = g_strdup(name);
321     attribute->value = g_strdup(value);
322     attributeSet = g_slist_prepend(attributeSet, attribute);
323
324     return attributeSet;
325 }
326
327 static AtkAttributeSet* webkit_accessible_get_attributes(AtkObject* object)
328 {
329     AtkAttributeSet* attributeSet = 0;
330
331     int headingLevel = core(object)->headingLevel();
332     if (headingLevel) {
333         String value = String::number(headingLevel);
334         attributeSet = addAttributeToSet(attributeSet, "level", value.utf8().data());
335     }
336     return attributeSet;
337 }
338
339 static AtkRole atkRole(AccessibilityRole role)
340 {
341     switch (role) {
342     case UnknownRole:
343         return ATK_ROLE_UNKNOWN;
344     case ButtonRole:
345         return ATK_ROLE_PUSH_BUTTON;
346     case RadioButtonRole:
347         return ATK_ROLE_RADIO_BUTTON;
348     case CheckBoxRole:
349         return ATK_ROLE_CHECK_BOX;
350     case SliderRole:
351         return ATK_ROLE_SLIDER;
352     case TabGroupRole:
353         return ATK_ROLE_PAGE_TAB_LIST;
354     case TextFieldRole:
355     case TextAreaRole:
356         return ATK_ROLE_ENTRY;
357     case StaticTextRole:
358         return ATK_ROLE_TEXT;
359     case OutlineRole:
360         return ATK_ROLE_TREE;
361     case MenuBarRole:
362         return ATK_ROLE_MENU_BAR;
363     case MenuRole:
364         return ATK_ROLE_MENU;
365     case MenuItemRole:
366         return ATK_ROLE_MENU_ITEM;
367     case ColumnRole:
368         //return ATK_ROLE_TABLE_COLUMN_HEADER; // Is this right?
369         return ATK_ROLE_UNKNOWN; // Matches Mozilla
370     case RowRole:
371         //return ATK_ROLE_TABLE_ROW_HEADER; // Is this right?
372         return ATK_ROLE_LIST_ITEM; // Matches Mozilla
373     case ToolbarRole:
374         return ATK_ROLE_TOOL_BAR;
375     case BusyIndicatorRole:
376         return ATK_ROLE_PROGRESS_BAR; // Is this right?
377     case ProgressIndicatorRole:
378         //return ATK_ROLE_SPIN_BUTTON; // Some confusion about this role in AccessibilityRenderObject.cpp
379         return ATK_ROLE_PROGRESS_BAR;
380     case WindowRole:
381         return ATK_ROLE_WINDOW;
382     case ComboBoxRole:
383         return ATK_ROLE_COMBO_BOX;
384     case SplitGroupRole:
385         return ATK_ROLE_SPLIT_PANE;
386     case SplitterRole:
387         return ATK_ROLE_SEPARATOR;
388     case ColorWellRole:
389         return ATK_ROLE_COLOR_CHOOSER;
390     case ListRole:
391         return ATK_ROLE_LIST;
392     case ScrollBarRole:
393         return ATK_ROLE_SCROLL_BAR;
394     case GridRole: // Is this right?
395     case TableRole:
396         return ATK_ROLE_TABLE;
397     case ApplicationRole:
398         return ATK_ROLE_APPLICATION;
399     case GroupRole:
400     case RadioGroupRole:
401         return ATK_ROLE_PANEL;
402     case CellRole:
403         return ATK_ROLE_TABLE_CELL;
404     case LinkRole:
405     case WebCoreLinkRole:
406     case ImageMapLinkRole:
407         return ATK_ROLE_LINK;
408     case ImageMapRole:
409     case ImageRole:
410         return ATK_ROLE_IMAGE;
411     case ListMarkerRole:
412         return ATK_ROLE_TEXT;
413     case WebAreaRole:
414         //return ATK_ROLE_HTML_CONTAINER; // Is this right?
415         return ATK_ROLE_DOCUMENT_FRAME;
416     case HeadingRole:
417         return ATK_ROLE_HEADING;
418     case ListBoxRole:
419         return ATK_ROLE_LIST;
420     case ListBoxOptionRole:
421         return ATK_ROLE_LIST_ITEM;
422     default:
423         return ATK_ROLE_UNKNOWN;
424     }
425 }
426
427 static AtkRole webkit_accessible_get_role(AtkObject* object)
428 {
429     AccessibilityObject* axObject = core(object);
430
431     if (!axObject)
432         return ATK_ROLE_UNKNOWN;
433
434     // WebCore does not seem to have a role for list items
435     if (axObject->isGroup()) {
436         AccessibilityObject* parent = axObject->parentObjectUnignored();
437         if (parent && parent->isList())
438             return ATK_ROLE_LIST_ITEM;
439     }
440
441     // WebCore does not know about paragraph role, label role, or section role
442     if (axObject->isAccessibilityRenderObject()) {
443         Node* node = static_cast<AccessibilityRenderObject*>(axObject)->renderer()->node();
444         if (node) {
445             if (node->hasTagName(HTMLNames::pTag))
446                 return ATK_ROLE_PARAGRAPH;
447             if (node->hasTagName(HTMLNames::labelTag))
448                 return ATK_ROLE_LABEL;
449             if (node->hasTagName(HTMLNames::divTag))
450                 return ATK_ROLE_SECTION;
451         }
452     }
453
454     // Note: Why doesn't WebCore have a password field for this
455     if (axObject->isPasswordField())
456         return ATK_ROLE_PASSWORD_TEXT;
457
458     return atkRole(axObject->roleValue());
459 }
460
461 static void setAtkStateSetFromCoreObject(AccessibilityObject* coreObject, AtkStateSet* stateSet)
462 {
463     AccessibilityObject* parent = coreObject->parentObject();
464     bool isListBoxOption = parent && parent->isListBox();
465
466     // Please keep the state list in alphabetical order
467     if (coreObject->isChecked())
468         atk_state_set_add_state(stateSet, ATK_STATE_CHECKED);
469
470     // FIXME: isReadOnly does not seem to do the right thing for
471     // controls, so check explicitly for them. In addition, because
472     // isReadOnly is false for listBoxOptions, we need to add one
473     // more check so that we do not present them as being "editable".
474     if ((!coreObject->isReadOnly() ||
475         (coreObject->isControl() && coreObject->canSetValueAttribute())) &&
476         !isListBoxOption)
477         atk_state_set_add_state(stateSet, ATK_STATE_EDITABLE);
478
479     // FIXME: Put both ENABLED and SENSITIVE together here for now
480     if (coreObject->isEnabled()) {
481         atk_state_set_add_state(stateSet, ATK_STATE_ENABLED);
482         atk_state_set_add_state(stateSet, ATK_STATE_SENSITIVE);
483     }
484
485     if (coreObject->canSetFocusAttribute())
486         atk_state_set_add_state(stateSet, ATK_STATE_FOCUSABLE);
487
488     if (coreObject->isFocused())
489         atk_state_set_add_state(stateSet, ATK_STATE_FOCUSED);
490
491     // TODO: ATK_STATE_HORIZONTAL
492
493     if (coreObject->isIndeterminate())
494         atk_state_set_add_state(stateSet, ATK_STATE_INDETERMINATE);
495
496     if (coreObject->isMultiSelect())
497         atk_state_set_add_state(stateSet, ATK_STATE_MULTISELECTABLE);
498
499     // TODO: ATK_STATE_OPAQUE
500
501     if (coreObject->isPressed())
502         atk_state_set_add_state(stateSet, ATK_STATE_PRESSED);
503
504     // TODO: ATK_STATE_SELECTABLE_TEXT
505
506     if (coreObject->canSetSelectedAttribute()) {
507         atk_state_set_add_state(stateSet, ATK_STATE_SELECTABLE);
508         // Items in focusable lists in Gtk have both STATE_SELECT{ABLE,ED}
509         // and STATE_FOCUS{ABLE,ED}. We'll fake the latter based on the
510         // former.
511         if (isListBoxOption)
512             atk_state_set_add_state(stateSet, ATK_STATE_FOCUSABLE);
513     }
514
515     if (coreObject->isSelected()) {
516         atk_state_set_add_state(stateSet, ATK_STATE_SELECTED);
517         // Items in focusable lists in Gtk have both STATE_SELECT{ABLE,ED}
518         // and STATE_FOCUS{ABLE,ED}. We'll fake the latter based on the
519         // former.
520         if (isListBoxOption)
521             atk_state_set_add_state(stateSet, ATK_STATE_FOCUSED);
522     }
523
524     // FIXME: Group both SHOWING and VISIBLE here for now
525     // Not sure how to handle this in WebKit, see bug
526     // http://bugzilla.gnome.org/show_bug.cgi?id=509650 for other
527     // issues with SHOWING vs VISIBLE within GTK+
528     if (!coreObject->isOffScreen()) {
529         atk_state_set_add_state(stateSet, ATK_STATE_SHOWING);
530         atk_state_set_add_state(stateSet, ATK_STATE_VISIBLE);
531     }
532
533     // Mutually exclusive, so we group these two
534     if (coreObject->roleValue() == TextFieldRole)
535         atk_state_set_add_state(stateSet, ATK_STATE_SINGLE_LINE);
536     else if (coreObject->roleValue() == TextAreaRole)
537         atk_state_set_add_state(stateSet, ATK_STATE_MULTI_LINE);
538
539     // TODO: ATK_STATE_SENSITIVE
540
541     // TODO: ATK_STATE_VERTICAL
542
543     if (coreObject->isVisited())
544         atk_state_set_add_state(stateSet, ATK_STATE_VISITED);
545 }
546
547 static AtkStateSet* webkit_accessible_ref_state_set(AtkObject* object)
548 {
549     AtkStateSet* stateSet = ATK_OBJECT_CLASS(webkit_accessible_parent_class)->ref_state_set(object);
550     AccessibilityObject* coreObject = core(object);
551
552     if (coreObject == fallbackObject()) {
553         atk_state_set_add_state(stateSet, ATK_STATE_DEFUNCT);
554         return stateSet;
555     }
556
557     setAtkStateSetFromCoreObject(coreObject, stateSet);
558
559     return stateSet;
560 }
561
562 static AtkRelationSet* webkit_accessible_ref_relation_set(AtkObject* object)
563 {
564     AtkRelationSet* relationSet = ATK_OBJECT_CLASS(webkit_accessible_parent_class)->ref_relation_set(object);
565     AccessibilityObject* coreObject = core(object);
566
567     setAtkRelationSetFromCoreObject(coreObject, relationSet);
568
569     return relationSet;
570 }
571
572 static void webkit_accessible_init(AtkObject* object, gpointer data)
573 {
574     if (ATK_OBJECT_CLASS(webkit_accessible_parent_class)->initialize)
575         ATK_OBJECT_CLASS(webkit_accessible_parent_class)->initialize(object, data);
576
577     WEBKIT_ACCESSIBLE(object)->m_object = reinterpret_cast<AccessibilityObject*>(data);
578 }
579
580 static void webkit_accessible_finalize(GObject* object)
581 {
582     // This is a good time to clear the return buffer.
583     returnString(String());
584
585     G_OBJECT_CLASS(webkit_accessible_parent_class)->finalize(object);
586 }
587
588 static void webkit_accessible_class_init(AtkObjectClass* klass)
589 {
590     GObjectClass* gobjectClass = G_OBJECT_CLASS(klass);
591
592     webkit_accessible_parent_class = g_type_class_peek_parent(klass);
593
594     gobjectClass->finalize = webkit_accessible_finalize;
595
596     klass->initialize = webkit_accessible_init;
597     klass->get_name = webkit_accessible_get_name;
598     klass->get_description = webkit_accessible_get_description;
599     klass->get_parent = webkit_accessible_get_parent;
600     klass->get_n_children = webkit_accessible_get_n_children;
601     klass->ref_child = webkit_accessible_ref_child;
602     klass->get_role = webkit_accessible_get_role;
603     klass->ref_state_set = webkit_accessible_ref_state_set;
604     klass->get_index_in_parent = webkit_accessible_get_index_in_parent;
605     klass->get_attributes = webkit_accessible_get_attributes;
606     klass->ref_relation_set = webkit_accessible_ref_relation_set;
607 }
608
609 GType
610 webkit_accessible_get_type(void)
611 {
612     static volatile gsize type_volatile = 0;
613
614     if (g_once_init_enter(&type_volatile)) {
615         static const GTypeInfo tinfo = {
616             sizeof(WebKitAccessibleClass),
617             (GBaseInitFunc) 0,
618             (GBaseFinalizeFunc) 0,
619             (GClassInitFunc) webkit_accessible_class_init,
620             (GClassFinalizeFunc) 0,
621             0, /* class data */
622             sizeof(WebKitAccessible), /* instance size */
623             0, /* nb preallocs */
624             (GInstanceInitFunc) 0,
625             0 /* value table */
626         };
627
628         GType type = g_type_register_static(ATK_TYPE_OBJECT,
629                                             "WebKitAccessible", &tinfo, GTypeFlags(0));
630         g_once_init_leave(&type_volatile, type);
631     }
632
633     return type_volatile;
634 }
635
636 static gboolean webkit_accessible_action_do_action(AtkAction* action, gint i)
637 {
638     g_return_val_if_fail(i == 0, FALSE);
639     return core(action)->performDefaultAction();
640 }
641
642 static gint webkit_accessible_action_get_n_actions(AtkAction* action)
643 {
644     return 1;
645 }
646
647 static const gchar* webkit_accessible_action_get_description(AtkAction* action, gint i)
648 {
649     g_return_val_if_fail(i == 0, 0);
650     // TODO: Need a way to provide/localize action descriptions.
651     notImplemented();
652     return "";
653 }
654
655 static const gchar* webkit_accessible_action_get_keybinding(AtkAction* action, gint i)
656 {
657     g_return_val_if_fail(i == 0, 0);
658     // FIXME: Construct a proper keybinding string.
659     return returnString(core(action)->accessKey().string());
660 }
661
662 static const gchar* webkit_accessible_action_get_name(AtkAction* action, gint i)
663 {
664     g_return_val_if_fail(i == 0, 0);
665     return returnString(core(action)->actionVerb());
666 }
667
668 static void atk_action_interface_init(AtkActionIface* iface)
669 {
670     iface->do_action = webkit_accessible_action_do_action;
671     iface->get_n_actions = webkit_accessible_action_get_n_actions;
672     iface->get_description = webkit_accessible_action_get_description;
673     iface->get_keybinding = webkit_accessible_action_get_keybinding;
674     iface->get_name = webkit_accessible_action_get_name;
675 }
676
677 // Selection (for controls)
678
679 static AccessibilityObject* optionFromList(AtkSelection* selection, gint i)
680 {
681     AccessibilityObject* coreSelection = core(selection);
682     if (!coreSelection || i < 0)
683         return 0;
684
685     AccessibilityRenderObject::AccessibilityChildrenVector options = core(selection)->children();
686     if (i < static_cast<gint>(options.size()))
687         return options.at(i).get();
688
689     return 0;
690 }
691
692 static AccessibilityObject* optionFromSelection(AtkSelection* selection, gint i)
693 {
694     // i is the ith selection as opposed to the ith child.
695
696     AccessibilityObject* coreSelection = core(selection);
697     if (!coreSelection || i < 0)
698         return 0;
699
700     AccessibilityRenderObject::AccessibilityChildrenVector selectedItems;
701     if (coreSelection->isListBox())
702         static_cast<AccessibilityListBox*>(coreSelection)->selectedChildren(selectedItems);
703
704     // TODO: Combo boxes
705
706     if (i < static_cast<gint>(selectedItems.size()))
707         return selectedItems.at(i).get();
708
709     return 0;
710 }
711
712 static gboolean webkit_accessible_selection_add_selection(AtkSelection* selection, gint i)
713 {
714     AccessibilityObject* option = optionFromList(selection, i);
715     if (option && core(selection)->isListBox()) {
716         AccessibilityListBoxOption* listBoxOption = static_cast<AccessibilityListBoxOption*>(option);
717         listBoxOption->setSelected(true);
718         return listBoxOption->isSelected();
719     }
720
721     return false;
722 }
723
724 static gboolean webkit_accessible_selection_clear_selection(AtkSelection* selection)
725 {
726     AccessibilityObject* coreSelection = core(selection);
727     if (!coreSelection)
728         return false;
729
730     AccessibilityRenderObject::AccessibilityChildrenVector selectedItems;
731     if (coreSelection->isListBox()) {
732         // Set the list of selected items to an empty list; then verify that it worked.
733         AccessibilityListBox* listBox = static_cast<AccessibilityListBox*>(coreSelection);
734         listBox->setSelectedChildren(selectedItems);
735         listBox->selectedChildren(selectedItems);
736         return selectedItems.size() == 0;
737     }
738     return false;
739 }
740
741 static AtkObject* webkit_accessible_selection_ref_selection(AtkSelection* selection, gint i)
742 {
743     AccessibilityObject* option = optionFromSelection(selection, i);
744     if (option) {
745         AtkObject* child = option->wrapper();
746         g_object_ref(child);
747         return child;
748     }
749
750     return 0;
751 }
752
753 static gint webkit_accessible_selection_get_selection_count(AtkSelection* selection)
754 {
755     AccessibilityObject* coreSelection = core(selection);
756     if (coreSelection && coreSelection->isListBox()) {
757         AccessibilityRenderObject::AccessibilityChildrenVector selectedItems;
758         static_cast<AccessibilityListBox*>(coreSelection)->selectedChildren(selectedItems);
759         return static_cast<gint>(selectedItems.size());
760     }
761
762     return 0;
763 }
764
765 static gboolean webkit_accessible_selection_is_child_selected(AtkSelection* selection, gint i)
766 {
767     AccessibilityObject* option = optionFromList(selection, i);
768     if (option && core(selection)->isListBox())
769         return static_cast<AccessibilityListBoxOption*>(option)->isSelected();
770
771     return false;
772 }
773
774 static gboolean webkit_accessible_selection_remove_selection(AtkSelection* selection, gint i)
775 {
776     // TODO: This is only getting called if i == 0. What is preventing the rest?
777     AccessibilityObject* option = optionFromSelection(selection, i);
778     if (option && core(selection)->isListBox()) {
779         AccessibilityListBoxOption* listBoxOption = static_cast<AccessibilityListBoxOption*>(option);
780         listBoxOption->setSelected(false);
781         return !listBoxOption->isSelected();
782     }
783
784     return false;
785 }
786
787 static gboolean webkit_accessible_selection_select_all_selection(AtkSelection* selection)
788 {
789     AccessibilityObject* coreSelection = core(selection);
790     if (!coreSelection || !coreSelection->isMultiSelect())
791         return false;
792
793     AccessibilityRenderObject::AccessibilityChildrenVector children = coreSelection->children();
794     if (coreSelection->isListBox()) {
795         AccessibilityListBox* listBox = static_cast<AccessibilityListBox*>(coreSelection);
796         listBox->setSelectedChildren(children);
797         AccessibilityRenderObject::AccessibilityChildrenVector selectedItems;
798         listBox->selectedChildren(selectedItems);
799         return selectedItems.size() == children.size();
800     }
801
802     return false;
803 }
804
805 static void atk_selection_interface_init(AtkSelectionIface* iface)
806 {
807     iface->add_selection = webkit_accessible_selection_add_selection;
808     iface->clear_selection = webkit_accessible_selection_clear_selection;
809     iface->ref_selection = webkit_accessible_selection_ref_selection;
810     iface->get_selection_count = webkit_accessible_selection_get_selection_count;
811     iface->is_child_selected = webkit_accessible_selection_is_child_selected;
812     iface->remove_selection = webkit_accessible_selection_remove_selection;
813     iface->select_all_selection = webkit_accessible_selection_select_all_selection;
814 }
815
816 // Text
817
818 static gchar* webkit_accessible_text_get_text(AtkText* text, gint startOffset, gint endOffset)
819 {
820     AccessibilityObject* coreObject = core(text);
821     String ret;
822     unsigned start = startOffset;
823     if (endOffset == -1)
824         endOffset = coreObject->stringValue().length();
825     int length = endOffset - startOffset;
826
827     if (coreObject->isTextControl())
828         ret = coreObject->doAXStringForRange(PlainTextRange(start, length));
829     else
830         ret = coreObject->textUnderElement().substring(start, length);
831
832     return g_strdup(ret.utf8().data());
833 }
834
835 static GailTextUtil* getGailTextUtilForAtk(AtkText* textObject)
836 {
837     gpointer data = g_object_get_data(G_OBJECT(textObject), "webkit-accessible-gail-text-util");
838     if (data)
839         return static_cast<GailTextUtil*>(data);
840
841     GailTextUtil* gailTextUtil = gail_text_util_new();
842     gail_text_util_text_setup(gailTextUtil, webkit_accessible_text_get_text(textObject, 0, -1));
843     g_object_set_data_full(G_OBJECT(textObject), "webkit-accessible-gail-text-util", gailTextUtil, g_object_unref);
844     return gailTextUtil;
845 }
846
847 static gchar* utf8Substr(const gchar* string, gint start, gint end)
848 {
849     ASSERT(string);
850     glong strLen = g_utf8_strlen(string, -1);
851     if (start > strLen || end > strLen)
852         return 0;
853     gchar* startPtr = g_utf8_offset_to_pointer(string, start);
854     gsize lenInBytes = g_utf8_offset_to_pointer(string, end) -  startPtr + 1;
855     gchar* output = static_cast<gchar*>(g_malloc0(lenInBytes + 1));
856     return g_utf8_strncpy(output, startPtr, end - start + 1);
857 }
858
859 // This function is not completely general, is it's tied to the
860 // internals of WebCore's text presentation.
861 static gchar* convertUniCharToUTF8(const UChar* characters, gint length, int from, int to)
862 {
863     CString stringUTF8 = UTF8Encoding().encode(characters, length, QuestionMarksForUnencodables);
864     gchar* utf8String = utf8Substr(stringUTF8.data(), from, to);
865     if (!g_utf8_validate(utf8String, -1, 0)) {
866         g_free(utf8String);
867         return 0;
868     }
869     gsize len = strlen(utf8String);
870     GString* ret = g_string_new_len(0, len);
871     gchar* ptr = utf8String;
872
873     // WebCore introduces line breaks in the text that do not reflect
874     // the layout you see on the screen, replace them with spaces
875     while (len > 0) {
876         gint index, start;
877         pango_find_paragraph_boundary(ptr, len, &index, &start);
878         g_string_append_len(ret, ptr, index);
879         if (index == start)
880             break;
881         g_string_append_c(ret, ' ');
882         ptr += start;
883         len -= start;
884     }
885
886     g_free(utf8String);
887     return g_string_free(ret, FALSE);
888 }
889
890 static PangoLayout* getPangoLayoutForAtk(AtkText* textObject)
891 {
892     AccessibilityObject* coreObject = core(textObject);
893
894     HostWindow* hostWindow = coreObject->document()->view()->hostWindow();
895     if (!hostWindow)
896         return 0;
897     PlatformPageClient webView = hostWindow->platformPageClient();
898     if (!webView)
899         return 0;
900
901     GString* str = g_string_new(0);
902
903     AccessibilityRenderObject* accObject = static_cast<AccessibilityRenderObject*>(coreObject);
904     if (!accObject)
905         return 0;
906     RenderText* renderText = toRenderText(accObject->renderer());
907     if (!renderText)
908         return 0;
909
910     // Create a string with the layout as it appears on the screen
911     if (accObject->isTextControl()) {
912         unsigned textLength = accObject->textLength();
913         int lineNumber = 0;
914         PlainTextRange range = accObject->doAXRangeForLine(lineNumber);
915         while (range.length) {
916             // When a line of text wraps in a text area, the final space is removed.
917             if (range.start + range.length < textLength)
918                 range.length -= 1;
919             String lineText = accObject->doAXStringForRange(range);
920             g_string_append(str, lineText.utf8().data());
921             g_string_append(str, "\n");
922             range = accObject->doAXRangeForLine(++lineNumber);
923         }
924     } else {
925         InlineTextBox* box = renderText->firstTextBox();
926         while (box) {
927             gchar* text = convertUniCharToUTF8(renderText->characters(), renderText->textLength(), box->start(), box->end());
928             g_string_append(str, text);
929             // Newline chars in the source result in separate text boxes, so check
930             // before adding a newline in the layout. See bug 25415 comment #78.
931             if (!box->nextOnLineExists())
932                 g_string_append(str, "\n");
933             box = box->nextTextBox();
934         }
935     }
936
937     PangoLayout* layout = gtk_widget_create_pango_layout(static_cast<GtkWidget*>(webView), g_string_free(str, FALSE));
938     g_object_set_data_full(G_OBJECT(textObject), "webkit-accessible-pango-layout", layout, g_object_unref);
939     return layout;
940 }
941
942 static gchar* webkit_accessible_text_get_text_after_offset(AtkText* text, gint offset, AtkTextBoundary boundaryType, gint* startOffset, gint* endOffset)
943 {
944     return gail_text_util_get_text(getGailTextUtilForAtk(text), getPangoLayoutForAtk(text), GAIL_AFTER_OFFSET, boundaryType, offset, startOffset, endOffset);
945 }
946
947 static gchar* webkit_accessible_text_get_text_at_offset(AtkText* text, gint offset, AtkTextBoundary boundaryType, gint* startOffset, gint* endOffset)
948 {
949     return gail_text_util_get_text(getGailTextUtilForAtk(text), getPangoLayoutForAtk(text), GAIL_AT_OFFSET, boundaryType, offset, startOffset, endOffset);
950 }
951
952 static gchar* webkit_accessible_text_get_text_before_offset(AtkText* text, gint offset, AtkTextBoundary boundaryType, gint* startOffset, gint* endOffset)
953 {
954     return gail_text_util_get_text(getGailTextUtilForAtk(text), getPangoLayoutForAtk(text), GAIL_BEFORE_OFFSET, boundaryType, offset, startOffset, endOffset);
955 }
956
957 static gunichar webkit_accessible_text_get_character_at_offset(AtkText* text, gint offset)
958 {
959     notImplemented();
960     return 0;
961 }
962
963 static gint webkit_accessible_text_get_caret_offset(AtkText* text)
964 {
965     // TODO: Verify this for RTL text.
966     return core(text)->selection().end().offsetInContainerNode();
967 }
968
969 static AtkAttributeSet* webkit_accessible_text_get_run_attributes(AtkText* text, gint offset, gint* start_offset, gint* end_offset)
970 {
971     notImplemented();
972     return 0;
973 }
974
975 static AtkAttributeSet* webkit_accessible_text_get_default_attributes(AtkText* text)
976 {
977     notImplemented();
978     return 0;
979 }
980
981 static void webkit_accessible_text_get_character_extents(AtkText* text, gint offset, gint* x, gint* y, gint* width, gint* height, AtkCoordType coords)
982 {
983     IntRect extents = core(text)->doAXBoundsForRange(PlainTextRange(offset, 1));
984     // FIXME: Use the AtkCoordType
985     // Requires WebCore::ScrollView::contentsToScreen() to be implemented
986
987 #if 0
988     switch(coords) {
989     case ATK_XY_SCREEN:
990         extents = core(text)->document()->view()->contentsToScreen(extents);
991         break;
992     case ATK_XY_WINDOW:
993         // No-op
994         break;
995     }
996 #endif
997
998     *x = extents.x();
999     *y = extents.y();
1000     *width = extents.width();
1001     *height = extents.height();
1002 }
1003
1004 static gint webkit_accessible_text_get_character_count(AtkText* text)
1005 {
1006     AccessibilityObject* coreObject = core(text);
1007
1008     if (coreObject->isTextControl())
1009         return coreObject->textLength();
1010     else
1011         return coreObject->textUnderElement().length();
1012 }
1013
1014 static gint webkit_accessible_text_get_offset_at_point(AtkText* text, gint x, gint y, AtkCoordType coords)
1015 {
1016     // FIXME: Use the AtkCoordType
1017     // TODO: Is it correct to ignore range.length?
1018     IntPoint pos(x, y);
1019     PlainTextRange range = core(text)->doAXRangeForPosition(pos);
1020     return range.start;
1021 }
1022
1023 static bool selectionBelongsToObject(AccessibilityObject* coreObject, VisibleSelection& selection)
1024 {
1025     if (!coreObject->isAccessibilityRenderObject())
1026         return false;
1027
1028     Node* node = static_cast<AccessibilityRenderObject*>(coreObject)->renderer()->node();
1029     return node == selection.base().containerNode();
1030 }
1031
1032 static gint webkit_accessible_text_get_n_selections(AtkText* text)
1033 {
1034     AccessibilityObject* coreObject = core(text);
1035     VisibleSelection selection = coreObject->selection();
1036
1037     // We don't support multiple selections for now, so there's only
1038     // two possibilities
1039     // Also, we don't want to do anything if the selection does not
1040     // belong to the currently selected object. We have to check since
1041     // there's no way to get the selection for a given object, only
1042     // the global one (the API is a bit confusing)
1043     return !selectionBelongsToObject(coreObject, selection) || selection.isNone() ? 0 : 1;
1044 }
1045
1046 static gchar* webkit_accessible_text_get_selection(AtkText* text, gint selection_num, gint* start_offset, gint* end_offset)
1047 {
1048     AccessibilityObject* coreObject = core(text);
1049     VisibleSelection selection = coreObject->selection();
1050
1051     // WebCore does not support multiple selection, so anything but 0 does not make sense for now.
1052     // Also, we don't want to do anything if the selection does not
1053     // belong to the currently selected object. We have to check since
1054     // there's no way to get the selection for a given object, only
1055     // the global one (the API is a bit confusing)
1056     if (selection_num != 0 || !selectionBelongsToObject(coreObject, selection)) {
1057         *start_offset = *end_offset = 0;
1058         return 0;
1059     }
1060
1061     *start_offset = selection.start().offsetInContainerNode();
1062     *end_offset = selection.end().offsetInContainerNode();
1063
1064     return webkit_accessible_text_get_text(text, *start_offset, *end_offset);
1065 }
1066
1067 static gboolean webkit_accessible_text_add_selection(AtkText* text, gint start_offset, gint end_offset)
1068 {
1069     notImplemented();
1070     return FALSE;
1071 }
1072
1073 static gboolean webkit_accessible_text_remove_selection(AtkText* text, gint selection_num)
1074 {
1075     notImplemented();
1076     return FALSE;
1077 }
1078
1079 static gboolean webkit_accessible_text_set_selection(AtkText* text, gint selection_num, gint start_offset, gint end_offset)
1080 {
1081     notImplemented();
1082     return FALSE;
1083 }
1084
1085 static gboolean webkit_accessible_text_set_caret_offset(AtkText* text, gint offset)
1086 {
1087     AccessibilityObject* coreObject = core(text);
1088
1089     // FIXME: We need to reimplement visiblePositionRangeForRange here
1090     // because the actual function checks the offset is within the
1091     // boundaries of text().length(), but text() only works for text
1092     // controls...
1093     VisiblePosition startPosition = coreObject->visiblePositionForIndex(offset);
1094     startPosition.setAffinity(DOWNSTREAM);
1095     VisiblePosition endPosition = coreObject->visiblePositionForIndex(offset);
1096     VisiblePositionRange range = VisiblePositionRange(startPosition, endPosition);
1097
1098     coreObject->setSelectedVisiblePositionRange(range);
1099     return TRUE;
1100 }
1101
1102 static void atk_text_interface_init(AtkTextIface* iface)
1103 {
1104     iface->get_text = webkit_accessible_text_get_text;
1105     iface->get_text_after_offset = webkit_accessible_text_get_text_after_offset;
1106     iface->get_text_at_offset = webkit_accessible_text_get_text_at_offset;
1107     iface->get_character_at_offset = webkit_accessible_text_get_character_at_offset;
1108     iface->get_text_before_offset = webkit_accessible_text_get_text_before_offset;
1109     iface->get_caret_offset = webkit_accessible_text_get_caret_offset;
1110     iface->get_run_attributes = webkit_accessible_text_get_run_attributes;
1111     iface->get_default_attributes = webkit_accessible_text_get_default_attributes;
1112     iface->get_character_extents = webkit_accessible_text_get_character_extents;
1113     iface->get_character_count = webkit_accessible_text_get_character_count;
1114     iface->get_offset_at_point = webkit_accessible_text_get_offset_at_point;
1115     iface->get_n_selections = webkit_accessible_text_get_n_selections;
1116     iface->get_selection = webkit_accessible_text_get_selection;
1117
1118     // set methods
1119     iface->add_selection = webkit_accessible_text_add_selection;
1120     iface->remove_selection = webkit_accessible_text_remove_selection;
1121     iface->set_selection = webkit_accessible_text_set_selection;
1122     iface->set_caret_offset = webkit_accessible_text_set_caret_offset;
1123 }
1124
1125 // EditableText
1126
1127 static gboolean webkit_accessible_editable_text_set_run_attributes(AtkEditableText* text, AtkAttributeSet* attrib_set, gint start_offset, gint end_offset)
1128 {
1129     notImplemented();
1130     return FALSE;
1131 }
1132
1133 static void webkit_accessible_editable_text_set_text_contents(AtkEditableText* text, const gchar* string)
1134 {
1135     // FIXME: string nullcheck?
1136     core(text)->setValue(String::fromUTF8(string));
1137 }
1138
1139 static void webkit_accessible_editable_text_insert_text(AtkEditableText* text, const gchar* string, gint length, gint* position)
1140 {
1141     // FIXME: string nullcheck?
1142
1143     AccessibilityObject* coreObject = core(text);
1144     // FIXME: Not implemented in WebCore
1145     //coreObject->setSelectedTextRange(PlainTextRange(*position, 0));
1146     //coreObject->setSelectedText(String::fromUTF8(string));
1147
1148     if (!coreObject->document() || !coreObject->document()->frame())
1149         return;
1150     coreObject->setSelectedVisiblePositionRange(coreObject->visiblePositionRangeForRange(PlainTextRange(*position, 0)));
1151     coreObject->setFocused(true);
1152     // FIXME: We should set position to the actual inserted text length, which may be less than that requested.
1153     if (coreObject->document()->frame()->editor()->insertTextWithoutSendingTextEvent(String::fromUTF8(string), false, 0))
1154         *position += length;
1155 }
1156
1157 static void webkit_accessible_editable_text_copy_text(AtkEditableText* text, gint start_pos, gint end_pos)
1158 {
1159     notImplemented();
1160 }
1161
1162 static void webkit_accessible_editable_text_cut_text(AtkEditableText* text, gint start_pos, gint end_pos)
1163 {
1164     notImplemented();
1165 }
1166
1167 static void webkit_accessible_editable_text_delete_text(AtkEditableText* text, gint start_pos, gint end_pos)
1168 {
1169     AccessibilityObject* coreObject = core(text);
1170     // FIXME: Not implemented in WebCore
1171     //coreObject->setSelectedTextRange(PlainTextRange(start_pos, end_pos - start_pos));
1172     //coreObject->setSelectedText(String());
1173
1174     if (!coreObject->document() || !coreObject->document()->frame())
1175         return;
1176     coreObject->setSelectedVisiblePositionRange(coreObject->visiblePositionRangeForRange(PlainTextRange(start_pos, end_pos - start_pos)));
1177     coreObject->setFocused(true);
1178     coreObject->document()->frame()->editor()->performDelete();
1179 }
1180
1181 static void webkit_accessible_editable_text_paste_text(AtkEditableText* text, gint position)
1182 {
1183     notImplemented();
1184 }
1185
1186 static void atk_editable_text_interface_init(AtkEditableTextIface* iface)
1187 {
1188     iface->set_run_attributes = webkit_accessible_editable_text_set_run_attributes;
1189     iface->set_text_contents = webkit_accessible_editable_text_set_text_contents;
1190     iface->insert_text = webkit_accessible_editable_text_insert_text;
1191     iface->copy_text = webkit_accessible_editable_text_copy_text;
1192     iface->cut_text = webkit_accessible_editable_text_cut_text;
1193     iface->delete_text = webkit_accessible_editable_text_delete_text;
1194     iface->paste_text = webkit_accessible_editable_text_paste_text;
1195 }
1196
1197 static void contentsToAtk(AccessibilityObject* coreObject, AtkCoordType coordType, IntRect rect, gint* x, gint* y, gint* width = 0, gint* height = 0)
1198 {
1199     FrameView* frameView = coreObject->documentFrameView();
1200
1201     if (frameView) {
1202         switch (coordType) {
1203         case ATK_XY_WINDOW:
1204             rect = frameView->contentsToWindow(rect);
1205             break;
1206         case ATK_XY_SCREEN:
1207             rect = frameView->contentsToScreen(rect);
1208             break;
1209         }
1210     }
1211
1212     if (x)
1213         *x = rect.x();
1214     if (y)
1215         *y = rect.y();
1216     if (width)
1217         *width = rect.width();
1218     if (height)
1219         *height = rect.height();
1220 }
1221
1222 static IntPoint atkToContents(AccessibilityObject* coreObject, AtkCoordType coordType, gint x, gint y)
1223 {
1224     IntPoint pos(x, y);
1225
1226     FrameView* frameView = coreObject->documentFrameView();
1227     if (frameView) {
1228         switch (coordType) {
1229         case ATK_XY_SCREEN:
1230             return frameView->screenToContents(pos);
1231         case ATK_XY_WINDOW:
1232             return frameView->windowToContents(pos);
1233         }
1234     }
1235
1236     return pos;
1237 }
1238
1239 static AtkObject* webkit_accessible_component_ref_accessible_at_point(AtkComponent* component, gint x, gint y, AtkCoordType coordType)
1240 {
1241     IntPoint pos = atkToContents(core(component), coordType, x, y);
1242     AccessibilityObject* target = core(component)->doAccessibilityHitTest(pos);
1243     if (!target)
1244         return 0;
1245     g_object_ref(target->wrapper());
1246     return target->wrapper();
1247 }
1248
1249 static void webkit_accessible_component_get_extents(AtkComponent* component, gint* x, gint* y, gint* width, gint* height, AtkCoordType coordType)
1250 {
1251     IntRect rect = core(component)->elementRect();
1252     contentsToAtk(core(component), coordType, rect, x, y, width, height);
1253 }
1254
1255 static gboolean webkit_accessible_component_grab_focus(AtkComponent* component)
1256 {
1257     core(component)->setFocused(true);
1258     return core(component)->isFocused();
1259 }
1260
1261 static void atk_component_interface_init(AtkComponentIface* iface)
1262 {
1263     iface->ref_accessible_at_point = webkit_accessible_component_ref_accessible_at_point;
1264     iface->get_extents = webkit_accessible_component_get_extents;
1265     iface->grab_focus = webkit_accessible_component_grab_focus;
1266 }
1267
1268 // Image
1269
1270 static void webkit_accessible_image_get_image_position(AtkImage* image, gint* x, gint* y, AtkCoordType coordType)
1271 {
1272     IntRect rect = core(image)->elementRect();
1273     contentsToAtk(core(image), coordType, rect, x, y);
1274 }
1275
1276 static const gchar* webkit_accessible_image_get_image_description(AtkImage* image)
1277 {
1278     return returnString(core(image)->accessibilityDescription());
1279 }
1280
1281 static void webkit_accessible_image_get_image_size(AtkImage* image, gint* width, gint* height)
1282 {
1283     IntSize size = core(image)->size();
1284
1285     if (width)
1286         *width = size.width();
1287     if (height)
1288         *height = size.height();
1289 }
1290
1291 static void atk_image_interface_init(AtkImageIface* iface)
1292 {
1293     iface->get_image_position = webkit_accessible_image_get_image_position;
1294     iface->get_image_description = webkit_accessible_image_get_image_description;
1295     iface->get_image_size = webkit_accessible_image_get_image_size;
1296 }
1297
1298 // Table
1299
1300 static AccessibilityTableCell* cell(AtkTable* table, guint row, guint column)
1301 {
1302     AccessibilityObject* accTable = core(table);
1303     if (accTable->isAccessibilityRenderObject())
1304         return static_cast<AccessibilityTable*>(accTable)->cellForColumnAndRow(column, row);
1305     return 0;
1306 }
1307
1308 static gint cellIndex(AccessibilityTableCell* axCell, AccessibilityTable* axTable)
1309 {
1310     // Calculate the cell's index as if we had a traditional Gtk+ table in
1311     // which cells are all direct children of the table, arranged row-first.
1312     AccessibilityObject::AccessibilityChildrenVector allCells;
1313     axTable->cells(allCells);
1314     AccessibilityObject::AccessibilityChildrenVector::iterator position;
1315     position = std::find(allCells.begin(), allCells.end(), axCell);
1316     if (position == allCells.end())
1317         return -1;
1318     return position - allCells.begin();
1319 }
1320
1321 static AccessibilityTableCell* cellAtIndex(AtkTable* table, gint index)
1322 {
1323     AccessibilityObject* accTable = core(table);
1324     if (accTable->isAccessibilityRenderObject()) {
1325         AccessibilityObject::AccessibilityChildrenVector allCells;
1326         static_cast<AccessibilityTable*>(accTable)->cells(allCells);
1327         if (0 <= index && static_cast<unsigned>(index) < allCells.size()) {
1328             AccessibilityObject* accCell = allCells.at(index).get();
1329             return static_cast<AccessibilityTableCell*>(accCell);
1330         }
1331     }
1332     return 0;
1333 }
1334
1335 static AtkObject* webkit_accessible_table_ref_at(AtkTable* table, gint row, gint column)
1336 {
1337     AccessibilityTableCell* axCell = cell(table, row, column);
1338     if (!axCell)
1339         return 0;
1340     return axCell->wrapper();
1341 }
1342
1343 static gint webkit_accessible_table_get_index_at(AtkTable* table, gint row, gint column)
1344 {
1345     AccessibilityTableCell* axCell = cell(table, row, column);
1346     AccessibilityTable* axTable = static_cast<AccessibilityTable*>(core(table));
1347     return cellIndex(axCell, axTable);
1348 }
1349
1350 static gint webkit_accessible_table_get_column_at_index(AtkTable* table, gint index)
1351 {
1352     AccessibilityTableCell* axCell = cellAtIndex(table, index);
1353     if (axCell){
1354         pair<int, int> columnRange;
1355         axCell->columnIndexRange(columnRange);
1356         return columnRange.first;
1357     }
1358     return -1;
1359 }
1360
1361 static gint webkit_accessible_table_get_row_at_index(AtkTable* table, gint index)
1362 {
1363     AccessibilityTableCell* axCell = cellAtIndex(table, index);
1364     if (axCell){
1365         pair<int, int> rowRange;
1366         axCell->rowIndexRange(rowRange);
1367         return rowRange.first;
1368     }
1369     return -1;
1370 }
1371
1372 static gint webkit_accessible_table_get_n_columns(AtkTable* table)
1373 {
1374     AccessibilityObject* accTable = core(table);
1375     if (accTable->isAccessibilityRenderObject())
1376         return static_cast<AccessibilityTable*>(accTable)->columnCount();
1377     return 0;
1378 }
1379
1380 static gint webkit_accessible_table_get_n_rows(AtkTable* table)
1381 {
1382     AccessibilityObject* accTable = core(table);
1383     if (accTable->isAccessibilityRenderObject())
1384         return static_cast<AccessibilityTable*>(accTable)->rowCount();
1385     return 0;
1386 }
1387
1388 static gint webkit_accessible_table_get_column_extent_at(AtkTable* table, gint row, gint column)
1389 {
1390     AccessibilityTableCell* axCell = cell(table, row, column);
1391     if (axCell) {
1392         pair<int, int> columnRange;
1393         axCell->columnIndexRange(columnRange);
1394         return columnRange.second;
1395     }
1396     return 0;
1397 }
1398
1399 static gint webkit_accessible_table_get_row_extent_at(AtkTable* table, gint row, gint column)
1400 {
1401     AccessibilityTableCell* axCell = cell(table, row, column);
1402     if (axCell) {
1403         pair<int, int> rowRange;
1404         axCell->rowIndexRange(rowRange);
1405         return rowRange.second;
1406     }
1407     return 0;
1408 }
1409
1410 static AtkObject* webkit_accessible_table_get_column_header(AtkTable* table, gint column)
1411 {
1412     // FIXME: This needs to be implemented.
1413     notImplemented();
1414     return 0;
1415 }
1416
1417 static AtkObject* webkit_accessible_table_get_row_header(AtkTable* table, gint row)
1418 {
1419     AccessibilityObject* accTable = core(table);
1420     if (accTable->isAccessibilityRenderObject()) {
1421         AccessibilityObject::AccessibilityChildrenVector allRowHeaders;
1422         static_cast<AccessibilityTable*>(accTable)->rowHeaders(allRowHeaders);
1423
1424         unsigned rowCount = allRowHeaders.size();
1425         for (unsigned k = 0; k < rowCount; ++k) {
1426             AccessibilityObject* rowObject = allRowHeaders[k]->parentObject();
1427             if (static_cast<AccessibilityTableRow*>(rowObject)->rowIndex() == row)
1428                 return allRowHeaders[k]->wrapper();
1429         }
1430     }
1431     return 0;
1432 }
1433
1434 static AtkObject* webkit_accessible_table_get_caption(AtkTable* table)
1435 {
1436     AccessibilityObject* accTable = core(table);
1437     if (accTable->isAccessibilityRenderObject()) {
1438         Node* node = static_cast<AccessibilityRenderObject*>(accTable)->renderer()->node();
1439         if (node && node->hasTagName(HTMLNames::tableTag)) {
1440             HTMLTableCaptionElement* caption = static_cast<HTMLTableElement*>(node)->caption();
1441             if (caption)
1442                 return AccessibilityObject::firstAccessibleObjectFromNode(caption->renderer()->node())->wrapper();
1443         }
1444     }
1445     return 0;
1446 }
1447
1448 static const gchar* webkit_accessible_table_get_column_description(AtkTable* table, gint column)
1449 {
1450     AtkObject* columnHeader = atk_table_get_column_header(table, column);
1451     if (columnHeader)
1452         return returnString(nameFromChildren(core(columnHeader)));
1453
1454     return 0;
1455 }
1456
1457 static const gchar* webkit_accessible_table_get_row_description(AtkTable* table, gint row)
1458 {
1459     AtkObject* rowHeader = atk_table_get_row_header(table, row);
1460     if (rowHeader)
1461         return returnString(nameFromChildren(core(rowHeader)));
1462
1463     return 0;
1464 }
1465
1466 static void atk_table_interface_init(AtkTableIface* iface)
1467 {
1468     iface->ref_at = webkit_accessible_table_ref_at;
1469     iface->get_index_at = webkit_accessible_table_get_index_at;
1470     iface->get_column_at_index = webkit_accessible_table_get_column_at_index;
1471     iface->get_row_at_index = webkit_accessible_table_get_row_at_index;
1472     iface->get_n_columns = webkit_accessible_table_get_n_columns;
1473     iface->get_n_rows = webkit_accessible_table_get_n_rows;
1474     iface->get_column_extent_at = webkit_accessible_table_get_column_extent_at;
1475     iface->get_row_extent_at = webkit_accessible_table_get_row_extent_at;
1476     iface->get_column_header = webkit_accessible_table_get_column_header;
1477     iface->get_row_header = webkit_accessible_table_get_row_header;
1478     iface->get_caption = webkit_accessible_table_get_caption;
1479     iface->get_column_description = webkit_accessible_table_get_column_description;
1480     iface->get_row_description = webkit_accessible_table_get_row_description;
1481 }
1482
1483 static const gchar* documentAttributeValue(AtkDocument* document, const gchar* attribute)
1484 {
1485     Document* coreDocument = core(document)->document();
1486     if (!coreDocument)
1487         return 0;
1488
1489     String value = String();
1490     if (!g_ascii_strcasecmp(attribute, "DocType") && coreDocument->doctype())
1491         value = coreDocument->doctype()->name();
1492     else if (!g_ascii_strcasecmp(attribute, "Encoding"))
1493         value = coreDocument->charset();
1494     else if (!g_ascii_strcasecmp(attribute, "URI"))
1495         value = coreDocument->documentURI();
1496     if (!value.isEmpty())
1497         return returnString(value);
1498
1499     return 0;
1500 }
1501
1502 static const gchar* webkit_accessible_document_get_attribute_value(AtkDocument* document, const gchar* attribute)
1503 {
1504     return documentAttributeValue(document, attribute);
1505 }
1506
1507 static AtkAttributeSet* webkit_accessible_document_get_attributes(AtkDocument* document)
1508 {
1509     AtkAttributeSet* attributeSet = 0;
1510     const gchar* attributes [] = {"DocType", "Encoding", "URI"};
1511
1512     for (unsigned i = 0; i < G_N_ELEMENTS(attributes); i++) {
1513         const gchar* value = documentAttributeValue(document, attributes[i]);
1514         if (value)
1515             attributeSet = addAttributeToSet(attributeSet, attributes[i], value);
1516     }
1517
1518     return attributeSet;
1519 }
1520
1521 static const gchar* webkit_accessible_document_get_locale(AtkDocument* document)
1522 {
1523
1524     // TODO: Should we fall back on lang xml:lang when the following comes up empty?
1525     String language = static_cast<AccessibilityRenderObject*>(core(document))->language();
1526     if (!language.isEmpty())
1527         return returnString(language);
1528
1529     return 0;
1530 }
1531
1532 static void atk_document_interface_init(AtkDocumentIface* iface)
1533 {
1534     iface->get_document_attribute_value = webkit_accessible_document_get_attribute_value;
1535     iface->get_document_attributes = webkit_accessible_document_get_attributes;
1536     iface->get_document_locale = webkit_accessible_document_get_locale;
1537 }
1538
1539 static const GInterfaceInfo AtkInterfacesInitFunctions[] = {
1540     {(GInterfaceInitFunc)atk_action_interface_init,
1541      (GInterfaceFinalizeFunc) 0, 0},
1542     {(GInterfaceInitFunc)atk_selection_interface_init,
1543      (GInterfaceFinalizeFunc) 0, 0},
1544     {(GInterfaceInitFunc)atk_editable_text_interface_init,
1545      (GInterfaceFinalizeFunc) 0, 0},
1546     {(GInterfaceInitFunc)atk_text_interface_init,
1547      (GInterfaceFinalizeFunc) 0, 0},
1548     {(GInterfaceInitFunc)atk_component_interface_init,
1549      (GInterfaceFinalizeFunc) 0, 0},
1550     {(GInterfaceInitFunc)atk_image_interface_init,
1551      (GInterfaceFinalizeFunc) 0, 0},
1552     {(GInterfaceInitFunc)atk_table_interface_init,
1553      (GInterfaceFinalizeFunc) 0, 0},
1554     {(GInterfaceInitFunc)atk_document_interface_init,
1555      (GInterfaceFinalizeFunc) 0, 0}
1556 };
1557
1558 enum WAIType {
1559     WAI_ACTION,
1560     WAI_SELECTION,
1561     WAI_EDITABLE_TEXT,
1562     WAI_TEXT,
1563     WAI_COMPONENT,
1564     WAI_IMAGE,
1565     WAI_TABLE,
1566     WAI_DOCUMENT
1567 };
1568
1569 static GType GetAtkInterfaceTypeFromWAIType(WAIType type)
1570 {
1571   switch (type) {
1572   case WAI_ACTION:
1573       return ATK_TYPE_ACTION;
1574   case WAI_SELECTION:
1575       return ATK_TYPE_SELECTION;
1576   case WAI_EDITABLE_TEXT:
1577       return ATK_TYPE_EDITABLE_TEXT;
1578   case WAI_TEXT:
1579       return ATK_TYPE_TEXT;
1580   case WAI_COMPONENT:
1581       return ATK_TYPE_COMPONENT;
1582   case WAI_IMAGE:
1583       return ATK_TYPE_IMAGE;
1584   case WAI_TABLE:
1585       return ATK_TYPE_TABLE;
1586   case WAI_DOCUMENT:
1587       return ATK_TYPE_DOCUMENT;
1588   }
1589
1590   return G_TYPE_INVALID;
1591 }
1592
1593 static guint16 getInterfaceMaskFromObject(AccessibilityObject* coreObject)
1594 {
1595     guint16 interfaceMask = 0;
1596
1597     // Component interface is always supported
1598     interfaceMask |= 1 << WAI_COMPONENT;
1599
1600     // Action
1601     if (!coreObject->actionVerb().isEmpty())
1602         interfaceMask |= 1 << WAI_ACTION;
1603
1604     // Selection
1605     if (coreObject->isListBox())
1606         interfaceMask |= 1 << WAI_SELECTION;
1607
1608     // Text & Editable Text
1609     AccessibilityRole role = coreObject->roleValue();
1610
1611     if (role == StaticTextRole)
1612         interfaceMask |= 1 << WAI_TEXT;
1613     else if (coreObject->isAccessibilityRenderObject() && coreObject->isTextControl()) {
1614         interfaceMask |= 1 << WAI_TEXT;
1615         if (!coreObject->isReadOnly())
1616             interfaceMask |= 1 << WAI_EDITABLE_TEXT;
1617     }
1618
1619     // Image
1620     if (coreObject->isImage())
1621         interfaceMask |= 1 << WAI_IMAGE;
1622
1623     // Table
1624     if (role == TableRole)
1625         interfaceMask |= 1 << WAI_TABLE;
1626
1627     // Document
1628     if (role == WebAreaRole)
1629         interfaceMask |= 1 << WAI_DOCUMENT;
1630
1631     return interfaceMask;
1632 }
1633
1634 static const char* getUniqueAccessibilityTypeName(guint16 interfaceMask)
1635 {
1636 #define WAI_TYPE_NAME_LEN (30) /* Enough for prefix + 5 hex characters (max) */
1637     static char name[WAI_TYPE_NAME_LEN + 1];
1638
1639     g_sprintf(name, "WAIType%x", interfaceMask);
1640     name[WAI_TYPE_NAME_LEN] = '\0';
1641
1642     return name;
1643 }
1644
1645 static GType getAccessibilityTypeFromObject(AccessibilityObject* coreObject)
1646 {
1647     static const GTypeInfo typeInfo = {
1648         sizeof(WebKitAccessibleClass),
1649         (GBaseInitFunc) 0,
1650         (GBaseFinalizeFunc) 0,
1651         (GClassInitFunc) 0,
1652         (GClassFinalizeFunc) 0,
1653         0, /* class data */
1654         sizeof(WebKitAccessible), /* instance size */
1655         0, /* nb preallocs */
1656         (GInstanceInitFunc) 0,
1657         0 /* value table */
1658     };
1659
1660     guint16 interfaceMask = getInterfaceMaskFromObject(coreObject);
1661     const char* atkTypeName = getUniqueAccessibilityTypeName(interfaceMask);
1662     GType type = g_type_from_name(atkTypeName);
1663     if (type)
1664         return type;
1665
1666     type = g_type_register_static(WEBKIT_TYPE_ACCESSIBLE,
1667                                   atkTypeName,
1668                                   &typeInfo, GTypeFlags(0));
1669     for (guint i = 0; i < G_N_ELEMENTS(AtkInterfacesInitFunctions); i++) {
1670         if (interfaceMask & (1 << i))
1671             g_type_add_interface_static(type,
1672                                         GetAtkInterfaceTypeFromWAIType(static_cast<WAIType>(i)),
1673                                         &AtkInterfacesInitFunctions[i]);
1674     }
1675
1676     return type;
1677 }
1678
1679 WebKitAccessible* webkit_accessible_new(AccessibilityObject* coreObject)
1680 {
1681     GType type = getAccessibilityTypeFromObject(coreObject);
1682     AtkObject* object = static_cast<AtkObject*>(g_object_new(type, 0));
1683
1684     atk_object_initialize(object, coreObject);
1685
1686     return WEBKIT_ACCESSIBLE(object);
1687 }
1688
1689 AccessibilityObject* webkit_accessible_get_accessibility_object(WebKitAccessible* accessible)
1690 {
1691     return accessible->m_object;
1692 }
1693
1694 void webkit_accessible_detach(WebKitAccessible* accessible)
1695 {
1696     ASSERT(accessible->m_object);
1697
1698     // We replace the WebCore AccessibilityObject with a fallback object that
1699     // provides default implementations to avoid repetitive null-checking after
1700     // detachment.
1701     accessible->m_object = fallbackObject();
1702 }
1703
1704 AtkObject* webkit_accessible_get_focused_element(WebKitAccessible* accessible)
1705 {
1706     if (!accessible->m_object)
1707         return 0;
1708
1709     RefPtr<AccessibilityObject> focusedObj = accessible->m_object->focusedUIElement();
1710     if (!focusedObj)
1711         return 0;
1712
1713     return focusedObj->wrapper();
1714 }
1715
1716 #endif // HAVE(ACCESSIBILITY)