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