2009-05-20 Jan Michael Alonzo <jmalonzo@webkit.org>
[WebKit-https.git] / WebCore / accessibility / gtk / AccessibilityObjectWrapperAtk.cpp
1 /*
2  * Copyright (C) 2008 Nuanti Ltd.
3  * Copyright (C) 2009 Igalia S.L.
4  *
5  * Portions from Mozilla a11y, copyright as follows:
6  *
7  * The Original Code is mozilla.org code.
8  *
9  * The Initial Developer of the Original Code is
10  * Sun Microsystems, Inc.
11  * Portions created by the Initial Developer are Copyright (C) 2002
12  * the Initial Developer. All Rights Reserved.
13  *
14  * This library is free software; you can redistribute it and/or
15  * modify it under the terms of the GNU Library General Public
16  * License as published by the Free Software Foundation; either
17  * version 2 of the License, or (at your option) any later version.
18  *
19  * This library is distributed in the hope that it will be useful,
20  * but WITHOUT ANY WARRANTY; without even the implied warranty of
21  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
22  * Library General Public License for more details.
23  *
24  * You should have received a copy of the GNU Library General Public License
25  * along with this library; see the file COPYING.LIB.  If not, write to
26  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
27  * Boston, MA 02110-1301, USA.
28  */
29
30 #include "config.h"
31 #include "AccessibilityObjectWrapperAtk.h"
32
33 #if HAVE(ACCESSIBILITY)
34
35 #include "AXObjectCache.h"
36 #include "AccessibilityListBox.h"
37 #include "AccessibilityRenderObject.h"
38 #include "AtomicString.h"
39 #include "CString.h"
40 #include "Document.h"
41 #include "Editor.h"
42 #include "Frame.h"
43 #include "FrameView.h"
44 #include "IntRect.h"
45 #include "NotImplemented.h"
46
47 #include <atk/atk.h>
48 #include <glib.h>
49 #include <glib/gprintf.h>
50
51 using namespace WebCore;
52
53 static AccessibilityObject* fallbackObject()
54 {
55     static AXObjectCache* fallbackCache = new AXObjectCache();
56     static AccessibilityObject* object = 0;
57     if (!object) {
58         // FIXME: using fallbackCache->getOrCreate(ListBoxOptionRole) is a hack
59         object = fallbackCache->getOrCreate(ListBoxOptionRole);
60         object->ref();
61     }
62
63     return object;
64 }
65
66 // Used to provide const char* returns.
67 static const char* returnString(const String& str)
68 {
69     static CString returnedString;
70     returnedString = str.utf8();
71     return returnedString.data();
72 }
73
74 static AccessibilityObject* core(WebKitAccessible* accessible)
75 {
76     if (!accessible)
77         return 0;
78
79     return accessible->m_object;
80 }
81
82 static AccessibilityObject* core(AtkObject* object)
83 {
84     if (!WEBKIT_IS_ACCESSIBLE(object))
85         return 0;
86
87     return core(WEBKIT_ACCESSIBLE(object));
88 }
89
90 static AccessibilityObject* core(AtkAction* action)
91 {
92     return core(ATK_OBJECT(action));
93 }
94
95 static AccessibilityObject* core(AtkStreamableContent* streamable)
96 {
97     return core(ATK_OBJECT(streamable));
98 }
99
100 static AccessibilityObject* core(AtkText* text)
101 {
102     return core(ATK_OBJECT(text));
103 }
104
105 static AccessibilityObject* core(AtkEditableText* text)
106 {
107     return core(ATK_OBJECT(text));
108 }
109
110 static AccessibilityObject* core(AtkComponent* component)
111 {
112     return core(ATK_OBJECT(component));
113 }
114
115 static AccessibilityObject* core(AtkImage* image)
116 {
117     return core(ATK_OBJECT(image));
118 }
119
120 static const gchar* webkit_accessible_get_name(AtkObject* object)
121 {
122     return returnString(core(object)->stringValue());
123 }
124
125 static const gchar* webkit_accessible_get_description(AtkObject* object)
126 {
127     // TODO: the Mozilla MSAA implementation prepends "Description: "
128     // Should we do this too?
129     return returnString(core(object)->accessibilityDescription());
130 }
131
132 static AtkObject* webkit_accessible_get_parent(AtkObject* object)
133 {
134     AccessibilityObject* coreParent = core(object)->parentObject();
135
136     if (!coreParent)
137         return NULL;
138
139     return coreParent->wrapper();
140 }
141
142 static gint webkit_accessible_get_n_children(AtkObject* object)
143 {
144     return core(object)->children().size();
145 }
146
147 static AtkObject* webkit_accessible_ref_child(AtkObject* object, gint index)
148 {
149     AccessibilityObject* coreObject = core(object);
150
151     g_return_val_if_fail(index >= 0, NULL);
152     g_return_val_if_fail(static_cast<size_t>(index) < coreObject->children().size(), NULL);
153
154     AccessibilityObject* coreChild = coreObject->children().at(index).get();
155
156     if (!coreChild)
157         return NULL;
158
159     AtkObject* child = coreChild->wrapper();
160     // TODO: Should we call atk_object_set_parent() here?
161     //atk_object_set_parent(child, object);
162     g_object_ref(child);
163
164     return child;
165 }
166
167 static gint webkit_accessible_get_index_in_parent(AtkObject* object)
168 {
169     // FIXME: This needs to be implemented.
170     notImplemented();
171     return 0;
172 }
173
174 static AtkRole atkRole(AccessibilityRole role)
175 {
176     switch (role) {
177     case UnknownRole:
178         return ATK_ROLE_UNKNOWN;
179     case ButtonRole:
180         return ATK_ROLE_PUSH_BUTTON;
181     case RadioButtonRole:
182         return ATK_ROLE_RADIO_BUTTON;
183     case CheckBoxRole:
184         return ATK_ROLE_CHECK_BOX;
185     case SliderRole:
186         return ATK_ROLE_SLIDER;
187     case TabGroupRole:
188         return ATK_ROLE_PAGE_TAB_LIST;
189     case TextFieldRole:
190     case TextAreaRole:
191         return ATK_ROLE_ENTRY;
192     case StaticTextRole:
193         return ATK_ROLE_TEXT;
194     case OutlineRole:
195         return ATK_ROLE_TREE;
196     case MenuBarRole:
197         return ATK_ROLE_MENU_BAR;
198     case MenuRole:
199         return ATK_ROLE_MENU;
200     case MenuItemRole:
201         return ATK_ROLE_MENU_ITEM;
202     case ColumnRole:
203         //return ATK_ROLE_TABLE_COLUMN_HEADER; // Is this right?
204         return ATK_ROLE_UNKNOWN; // Matches Mozilla
205     case RowRole:
206         //return ATK_ROLE_TABLE_ROW_HEADER; // Is this right?
207         return ATK_ROLE_LIST_ITEM; // Matches Mozilla
208     case ToolbarRole:
209         return ATK_ROLE_TOOL_BAR;
210     case BusyIndicatorRole:
211         return ATK_ROLE_PROGRESS_BAR; // Is this right?
212     case ProgressIndicatorRole:
213         //return ATK_ROLE_SPIN_BUTTON; // Some confusion about this role in AccessibilityRenderObject.cpp
214         return ATK_ROLE_PROGRESS_BAR;
215     case WindowRole:
216         return ATK_ROLE_WINDOW;
217     case ComboBoxRole:
218         return ATK_ROLE_COMBO_BOX;
219     case SplitGroupRole:
220         return ATK_ROLE_SPLIT_PANE;
221     case SplitterRole:
222         return ATK_ROLE_SEPARATOR;
223     case ColorWellRole:
224         return ATK_ROLE_COLOR_CHOOSER;
225     case ListRole:
226         return ATK_ROLE_LIST;
227     case ScrollBarRole:
228         return ATK_ROLE_SCROLL_BAR;
229     case GridRole: // Is this right?
230     case TableRole:
231         return ATK_ROLE_TABLE;
232     case ApplicationRole:
233         return ATK_ROLE_APPLICATION;
234     //case LabelRole: // TODO: should this be covered in the switch?
235     //    return ATK_ROLE_LABEL;
236     case GroupRole:
237     case RadioGroupRole:
238         return ATK_ROLE_PANEL;
239     case CellRole:
240         return ATK_ROLE_TABLE_CELL;
241     case LinkRole:
242     case WebCoreLinkRole:
243     case ImageMapLinkRole:
244         return ATK_ROLE_LINK;
245     case ImageMapRole:
246     case ImageRole:
247         return ATK_ROLE_IMAGE;
248     case ListMarkerRole:
249         return ATK_ROLE_ROW_HEADER;
250     case WebAreaRole:
251         //return ATK_ROLE_HTML_CONTAINER; // Is this right?
252         return ATK_ROLE_DOCUMENT_FRAME;
253     case HeadingRole:
254         return ATK_ROLE_HEADING;
255     case ListBoxRole:
256         return ATK_ROLE_LIST;
257     case ListBoxOptionRole:
258         return ATK_ROLE_LIST_ITEM;
259     default:
260         return ATK_ROLE_UNKNOWN;
261     }
262 }
263
264 static AtkRole webkit_accessible_get_role(AtkObject* object)
265 {
266     AccessibilityObject* AXObject = core(object);
267
268     if (!AXObject)
269         return ATK_ROLE_UNKNOWN;
270
271     // Note: Why doesn't WebCore have a password field for this
272     if (AXObject->isPasswordField())
273         return ATK_ROLE_PASSWORD_TEXT;
274
275     return atkRole(AXObject->roleValue());
276 }
277
278 static void setAtkStateSetFromCoreObject(AccessibilityObject* coreObject, AtkStateSet* stateSet)
279 {
280     // Please keep the state list in alphabetical order
281
282     if (coreObject->isChecked())
283         atk_state_set_add_state(stateSet, ATK_STATE_CHECKED);
284
285     if (!coreObject->isReadOnly())
286         atk_state_set_add_state(stateSet, ATK_STATE_EDITABLE);
287
288     if (coreObject->isEnabled())
289         atk_state_set_add_state(stateSet, ATK_STATE_ENABLED);
290
291     if (coreObject->canSetFocusAttribute())
292         atk_state_set_add_state(stateSet, ATK_STATE_FOCUSABLE);
293
294     if (coreObject->isFocused())
295         atk_state_set_add_state(stateSet, ATK_STATE_FOCUSED);
296
297     // TODO: ATK_STATE_HORIZONTAL
298
299     if (coreObject->isIndeterminate())
300         atk_state_set_add_state(stateSet, ATK_STATE_INDETERMINATE);
301
302     if (coreObject->isMultiSelect())
303         atk_state_set_add_state(stateSet, ATK_STATE_MULTISELECTABLE);
304
305     // TODO: ATK_STATE_OPAQUE
306
307     if (coreObject->isPressed())
308         atk_state_set_add_state(stateSet, ATK_STATE_PRESSED);
309
310     // TODO: ATK_STATE_SELECTABLE_TEXT
311
312     // TODO: ATK_STATE_SENSITIVE
313
314     if (coreObject->isSelected())
315         atk_state_set_add_state(stateSet, ATK_STATE_SELECTED);
316
317     if (!coreObject->isOffScreen())
318         atk_state_set_add_state(stateSet, ATK_STATE_SHOWING);
319
320     // Mutually exclusive, so we group these two
321     if (coreObject->roleValue() == TextFieldRole)
322         atk_state_set_add_state(stateSet, ATK_STATE_SINGLE_LINE);
323     else if (coreObject->roleValue() == TextAreaRole)
324         atk_state_set_add_state(stateSet, ATK_STATE_MULTI_LINE);
325
326     // TODO: ATK_STATE_SENSITIVE
327
328     // TODO: ATK_STATE_VERTICAL
329
330     if (coreObject->isVisited())
331         atk_state_set_add_state(stateSet, ATK_STATE_VISITED);
332 }
333
334 static gpointer webkit_accessible_parent_class = NULL;
335
336 static AtkStateSet* webkit_accessible_ref_state_set(AtkObject* object)
337 {
338     AtkStateSet* stateSet = ATK_OBJECT_CLASS(webkit_accessible_parent_class)->ref_state_set(object);
339     AccessibilityObject* coreObject = core(object);
340
341     if (coreObject == fallbackObject()) {
342         atk_state_set_add_state(stateSet, ATK_STATE_DEFUNCT);
343         return stateSet;
344     }
345
346     setAtkStateSetFromCoreObject(coreObject, stateSet);
347
348     return stateSet;
349 }
350
351 static void webkit_accessible_init(AtkObject* object, gpointer data)
352 {
353     if (ATK_OBJECT_CLASS(webkit_accessible_parent_class)->initialize)
354         ATK_OBJECT_CLASS(webkit_accessible_parent_class)->initialize(object, data);
355
356     WEBKIT_ACCESSIBLE(object)->m_object = reinterpret_cast<AccessibilityObject*>(data);
357 }
358
359 static void webkit_accessible_finalize(GObject* object)
360 {
361     // This is a good time to clear the return buffer.
362     returnString(String());
363
364     if (G_OBJECT_CLASS(webkit_accessible_parent_class)->finalize)
365         G_OBJECT_CLASS(webkit_accessible_parent_class)->finalize(object);
366 }
367
368 static void webkit_accessible_class_init(AtkObjectClass* klass)
369 {
370     GObjectClass* gobjectClass = G_OBJECT_CLASS(klass);
371
372     webkit_accessible_parent_class = g_type_class_peek_parent(klass);
373
374     gobjectClass->finalize = webkit_accessible_finalize;
375
376     klass->initialize = webkit_accessible_init;
377     klass->get_name = webkit_accessible_get_name;
378     klass->get_description = webkit_accessible_get_description;
379     klass->get_parent = webkit_accessible_get_parent;
380     klass->get_n_children = webkit_accessible_get_n_children;
381     klass->ref_child = webkit_accessible_ref_child;
382     klass->get_role = webkit_accessible_get_role;
383     klass->ref_state_set = webkit_accessible_ref_state_set;
384 }
385
386 GType
387 webkit_accessible_get_type(void)
388 {
389     static volatile gsize type_volatile = 0;
390
391     if (g_once_init_enter(&type_volatile)) {
392         static const GTypeInfo tinfo = {
393             sizeof(WebKitAccessibleClass),
394             (GBaseInitFunc)NULL,
395             (GBaseFinalizeFunc)NULL,
396             (GClassInitFunc)webkit_accessible_class_init,
397             (GClassFinalizeFunc)NULL,
398             NULL, /* class data */
399             sizeof(WebKitAccessible), /* instance size */
400             0, /* nb preallocs */
401             (GInstanceInitFunc)NULL,
402             NULL /* value table */
403         };
404
405         GType type = g_type_register_static(ATK_TYPE_OBJECT,
406                                             "WebKitAccessible", &tinfo, GTypeFlags(0));
407         g_once_init_leave(&type_volatile, type);
408     }
409
410     return type_volatile;
411 }
412
413 static gboolean webkit_accessible_action_do_action(AtkAction* action, gint i)
414 {
415     g_return_val_if_fail(i == 0, FALSE);
416     return core(action)->performDefaultAction();
417 }
418
419 static gint webkit_accessible_action_get_n_actions(AtkAction* action)
420 {
421     return 1;
422 }
423
424 static const gchar* webkit_accessible_action_get_description(AtkAction* action, gint i)
425 {
426     g_return_val_if_fail(i == 0, NULL);
427     // TODO: Need a way to provide/localize action descriptions.
428     notImplemented();
429     return "";
430 }
431
432 static const gchar* webkit_accessible_action_get_keybinding(AtkAction* action, gint i)
433 {
434     g_return_val_if_fail(i == 0, NULL);
435     // FIXME: Construct a proper keybinding string.
436     return returnString(core(action)->accessKey().string());
437 }
438
439 static const gchar* webkit_accessible_action_get_name(AtkAction* action, gint i)
440 {
441     g_return_val_if_fail(i == 0, NULL);
442     return returnString(core(action)->actionVerb());
443 }
444
445 static void atk_action_interface_init(AtkActionIface* iface)
446 {
447     iface->do_action = webkit_accessible_action_do_action;
448     iface->get_n_actions = webkit_accessible_action_get_n_actions;
449     iface->get_description = webkit_accessible_action_get_description;
450     iface->get_keybinding = webkit_accessible_action_get_keybinding;
451     iface->get_name = webkit_accessible_action_get_name;
452 }
453
454 // Text
455
456 static gchar* webkit_accessible_text_get_text(AtkText* text, gint startOffset, gint endOffset)
457 {
458     AccessibilityObject* coreObject = core(text);
459     String ret;
460     unsigned start = startOffset;
461     int length = endOffset - startOffset;
462
463     if (coreObject->isTextControl())
464         ret = coreObject->doAXStringForRange(PlainTextRange(start, length));
465     else
466         ret = coreObject->textUnderElement().substring(start, length);
467
468     return g_strdup(ret.utf8().data());
469 }
470
471 static gchar* webkit_accessible_text_get_text_after_offset(AtkText* text, gint offset, AtkTextBoundary boundary_type, gint* start_offset, gint* end_offset)
472 {
473     notImplemented();
474     return NULL;
475 }
476
477 static gchar* webkit_accessible_text_get_text_at_offset(AtkText* text, gint offset, AtkTextBoundary boundary_type, gint* start_offset, gint* end_offset)
478 {
479     notImplemented();
480     return NULL;
481 }
482
483 static gunichar webkit_accessible_text_get_character_at_offset(AtkText* text, gint offset)
484 {
485     notImplemented();
486     return 0;
487 }
488
489 static gchar* webkit_accessible_text_get_text_before_offset(AtkText* text, gint offset, AtkTextBoundary boundary_type, gint* start_offset, gint* end_offset)
490 {
491     notImplemented();
492     return NULL;
493 }
494
495 static gint webkit_accessible_text_get_caret_offset(AtkText* text)
496 {
497     // TODO: Verify this for RTL text.
498     return core(text)->selection().start().offsetInContainerNode();
499 }
500
501 static AtkAttributeSet* webkit_accessible_text_get_run_attributes(AtkText* text, gint offset, gint* start_offset, gint* end_offset)
502 {
503     notImplemented();
504     return NULL;
505 }
506
507 static AtkAttributeSet* webkit_accessible_text_get_default_attributes(AtkText* text)
508 {
509     notImplemented();
510     return NULL;
511 }
512
513 static void webkit_accessible_text_get_character_extents(AtkText* text, gint offset, gint* x, gint* y, gint* width, gint* height, AtkCoordType coords)
514 {
515     IntRect extents = core(text)->doAXBoundsForRange(PlainTextRange(offset, 1));
516     // FIXME: Use the AtkCoordType
517     // Requires WebCore::ScrollView::contentsToScreen() to be implemented
518
519 #if 0
520     switch(coords) {
521     case ATK_XY_SCREEN:
522         extents = core(text)->document()->view()->contentsToScreen(extents);
523         break;
524     case ATK_XY_WINDOW:
525         // No-op
526         break;
527     }
528 #endif
529
530     *x = extents.x();
531     *y = extents.y();
532     *width = extents.width();
533     *height = extents.height();
534 }
535
536 static gint webkit_accessible_text_get_character_count(AtkText* text)
537 {
538     AccessibilityObject* coreObject = core(text);
539
540     if (coreObject->isTextControl())
541         return coreObject->textLength();
542     else
543         return coreObject->textUnderElement().length();
544 }
545
546 static gint webkit_accessible_text_get_offset_at_point(AtkText* text, gint x, gint y, AtkCoordType coords)
547 {
548     // FIXME: Use the AtkCoordType
549     // TODO: Is it correct to ignore range.length?
550     IntPoint pos(x, y);
551     PlainTextRange range = core(text)->doAXRangeForPosition(pos);
552     return range.start;
553 }
554
555 static gint webkit_accessible_text_get_n_selections(AtkText* text)
556 {
557     notImplemented();
558     return 0;
559 }
560
561 static gchar* webkit_accessible_text_get_selection(AtkText* text, gint selection_num, gint* start_offset, gint* end_offset)
562 {
563     notImplemented();
564     return NULL;
565 }
566
567 static gboolean webkit_accessible_text_add_selection(AtkText* text, gint start_offset, gint end_offset)
568 {
569     notImplemented();
570     return FALSE;
571 }
572
573 static gboolean webkit_accessible_text_remove_selection(AtkText* text, gint selection_num)
574 {
575     notImplemented();
576     return FALSE;
577 }
578
579 static gboolean webkit_accessible_text_set_selection(AtkText* text, gint selection_num, gint start_offset, gint end_offset)
580 {
581     notImplemented();
582     return FALSE;
583 }
584
585 static gboolean webkit_accessible_text_set_caret_offset(AtkText* text, gint offset)
586 {
587     // TODO: Verify
588     //core(text)->setSelectedTextRange(PlainTextRange(offset, 0));
589     AccessibilityObject* coreObject = core(text);
590     coreObject->setSelectedVisiblePositionRange(coreObject->visiblePositionRangeForRange(PlainTextRange(offset, 0)));
591     return TRUE;
592 }
593
594 static void atk_text_interface_init(AtkTextIface* iface)
595 {
596     iface->get_text = webkit_accessible_text_get_text;
597     iface->get_text_after_offset = webkit_accessible_text_get_text_after_offset;
598     iface->get_text_at_offset = webkit_accessible_text_get_text_at_offset;
599     iface->get_character_at_offset = webkit_accessible_text_get_character_at_offset;
600     iface->get_text_before_offset = webkit_accessible_text_get_text_before_offset;
601     iface->get_caret_offset = webkit_accessible_text_get_caret_offset;
602     iface->get_run_attributes = webkit_accessible_text_get_run_attributes;
603     iface->get_default_attributes = webkit_accessible_text_get_default_attributes;
604     iface->get_character_extents = webkit_accessible_text_get_character_extents;
605     iface->get_character_count = webkit_accessible_text_get_character_count;
606     iface->get_offset_at_point = webkit_accessible_text_get_offset_at_point;
607     iface->get_n_selections = webkit_accessible_text_get_n_selections;
608     iface->get_selection = webkit_accessible_text_get_selection;
609
610     // set methods
611     iface->add_selection = webkit_accessible_text_add_selection;
612     iface->remove_selection = webkit_accessible_text_remove_selection;
613     iface->set_selection = webkit_accessible_text_set_selection;
614     iface->set_caret_offset = webkit_accessible_text_set_caret_offset;
615 }
616
617 // EditableText
618
619 static gboolean webkit_accessible_editable_text_set_run_attributes(AtkEditableText* text, AtkAttributeSet* attrib_set, gint start_offset, gint end_offset)
620 {
621     notImplemented();
622     return FALSE;
623 }
624
625 static void webkit_accessible_editable_text_set_text_contents(AtkEditableText* text, const gchar* string)
626 {
627     // FIXME: string nullcheck?
628     core(text)->setValue(String::fromUTF8(string));
629 }
630
631 static void webkit_accessible_editable_text_insert_text(AtkEditableText* text, const gchar* string, gint length, gint* position)
632 {
633     // FIXME: string nullcheck?
634
635     AccessibilityObject* coreObject = core(text);
636     // FIXME: Not implemented in WebCore
637     //coreObject->setSelectedTextRange(PlainTextRange(*position, 0));
638     //coreObject->setSelectedText(String::fromUTF8(string));
639
640     if (!coreObject->document() || !coreObject->document()->frame())
641         return;
642     coreObject->setSelectedVisiblePositionRange(coreObject->visiblePositionRangeForRange(PlainTextRange(*position, 0)));
643     coreObject->setFocused(true);
644     // FIXME: We should set position to the actual inserted text length, which may be less than that requested.
645     if (coreObject->document()->frame()->editor()->insertTextWithoutSendingTextEvent(String::fromUTF8(string), false, 0))
646         *position += length;
647 }
648
649 static void webkit_accessible_editable_text_copy_text(AtkEditableText* text, gint start_pos, gint end_pos)
650 {
651     notImplemented();
652 }
653
654 static void webkit_accessible_editable_text_cut_text(AtkEditableText* text, gint start_pos, gint end_pos)
655 {
656     notImplemented();
657 }
658
659 static void webkit_accessible_editable_text_delete_text(AtkEditableText* text, gint start_pos, gint end_pos)
660 {
661     AccessibilityObject* coreObject = core(text);
662     // FIXME: Not implemented in WebCore
663     //coreObject->setSelectedTextRange(PlainTextRange(start_pos, end_pos - start_pos));
664     //coreObject->setSelectedText(String());
665
666     if (!coreObject->document() || !coreObject->document()->frame())
667         return;
668     coreObject->setSelectedVisiblePositionRange(coreObject->visiblePositionRangeForRange(PlainTextRange(start_pos, end_pos - start_pos)));
669     coreObject->setFocused(true);
670     coreObject->document()->frame()->editor()->performDelete();
671 }
672
673 static void webkit_accessible_editable_text_paste_text(AtkEditableText* text, gint position)
674 {
675     notImplemented();
676 }
677
678 static void atk_editable_text_interface_init(AtkEditableTextIface* iface)
679 {
680     iface->set_run_attributes = webkit_accessible_editable_text_set_run_attributes;
681     iface->set_text_contents = webkit_accessible_editable_text_set_text_contents;
682     iface->insert_text = webkit_accessible_editable_text_insert_text;
683     iface->copy_text = webkit_accessible_editable_text_copy_text;
684     iface->cut_text = webkit_accessible_editable_text_cut_text;
685     iface->delete_text = webkit_accessible_editable_text_delete_text;
686     iface->paste_text = webkit_accessible_editable_text_paste_text;
687 }
688
689 // StreamableContent
690
691 static gint webkit_accessible_streamable_content_get_n_mime_types(AtkStreamableContent* streamable)
692 {
693     notImplemented();
694     return 0;
695 }
696
697 static G_CONST_RETURN gchar* webkit_accessible_streamable_content_get_mime_type(AtkStreamableContent* streamable, gint i)
698 {
699     notImplemented();
700     return "";
701 }
702
703 static GIOChannel* webkit_accessible_streamable_content_get_stream(AtkStreamableContent* streamable, const gchar* mime_type)
704 {
705     notImplemented();
706     return NULL;
707 }
708
709 static G_CONST_RETURN gchar* webkit_accessible_streamable_content_get_uri(AtkStreamableContent* streamable, const gchar* mime_type)
710 {
711     notImplemented();
712     return NULL;
713 }
714
715 static void atk_streamable_content_interface_init(AtkStreamableContentIface* iface)
716 {
717     iface->get_n_mime_types = webkit_accessible_streamable_content_get_n_mime_types;
718     iface->get_mime_type = webkit_accessible_streamable_content_get_mime_type;
719     iface->get_stream = webkit_accessible_streamable_content_get_stream;
720     iface->get_uri = webkit_accessible_streamable_content_get_uri;
721 }
722
723 static void contentsToAtk(AccessibilityObject* coreObject, AtkCoordType coordType, IntRect rect, gint* x, gint* y, gint* width = 0, gint* height = 0)
724 {
725     FrameView* frameView = coreObject->documentFrameView();
726
727     if (frameView) {
728         switch (coordType) {
729         case ATK_XY_WINDOW:
730             rect = frameView->contentsToWindow(rect);
731             break;
732         case ATK_XY_SCREEN:
733             rect = frameView->contentsToScreen(rect);
734             break;
735         }
736     }
737
738     if (x)
739         *x = rect.x();
740     if (y)
741         *y = rect.y();
742     if (width)
743         *width = rect.width();
744     if (height)
745         *height = rect.height();
746 }
747
748 static IntPoint atkToContents(AccessibilityObject* coreObject, AtkCoordType coordType, gint x, gint y)
749 {
750     IntPoint pos(x, y);
751
752     FrameView* frameView = coreObject->documentFrameView();
753     if (frameView) {
754         switch (coordType) {
755         case ATK_XY_SCREEN:
756             return frameView->screenToContents(pos);
757         case ATK_XY_WINDOW:
758             return frameView->windowToContents(pos);
759         }
760     }
761
762     return pos;
763 }
764
765 static AtkObject* webkit_accessible_component_ref_accessible_at_point(AtkComponent* component, gint x, gint y, AtkCoordType coordType)
766 {
767     IntPoint pos = atkToContents(core(component), coordType, x, y);
768     AccessibilityObject* target = core(component)->doAccessibilityHitTest(pos);
769     if (!target)
770         return NULL;
771     g_object_ref(target->wrapper());
772     return target->wrapper();
773 }
774
775 static void webkit_accessible_component_get_extents(AtkComponent* component, gint* x, gint* y, gint* width, gint* height, AtkCoordType coordType)
776 {
777     IntRect rect = core(component)->elementRect();
778     contentsToAtk(core(component), coordType, rect, x, y, width, height);
779 }
780
781 static gboolean webkit_accessible_component_grab_focus(AtkComponent* component)
782 {
783     core(component)->setFocused(true);
784     return core(component)->isFocused();
785 }
786
787 static void atk_component_interface_init(AtkComponentIface *iface)
788 {
789     iface->ref_accessible_at_point = webkit_accessible_component_ref_accessible_at_point;
790     iface->get_extents = webkit_accessible_component_get_extents;
791     iface->grab_focus = webkit_accessible_component_grab_focus;
792 }
793
794 // Image
795
796 static void webkit_accessible_image_get_image_position(AtkImage* image, gint* x, gint* y, AtkCoordType coordType)
797 {
798     IntRect rect = core(image)->elementRect();
799     contentsToAtk(core(image), coordType, rect, x, y);
800 }
801
802 static const gchar* webkit_accessible_image_get_image_description(AtkImage* image)
803 {
804     return returnString(core(image)->accessibilityDescription());
805 }
806
807 static void webkit_accessible_image_get_image_size(AtkImage* image, gint* width, gint* height)
808 {
809     IntSize size = core(image)->size();
810
811     if (width)
812         *width = size.width();
813     if (height)
814         *height = size.height();
815 }
816
817 static void atk_image_interface_init(AtkImageIface* iface)
818 {
819     iface->get_image_position = webkit_accessible_image_get_image_position;
820     iface->get_image_description = webkit_accessible_image_get_image_description;
821     iface->get_image_size = webkit_accessible_image_get_image_size;
822 }
823
824 static const GInterfaceInfo AtkInterfacesInitFunctions[] = {
825     {(GInterfaceInitFunc)atk_action_interface_init,
826      (GInterfaceFinalizeFunc) NULL, NULL},
827     {(GInterfaceInitFunc)atk_streamable_content_interface_init,
828      (GInterfaceFinalizeFunc) NULL, NULL},
829     {(GInterfaceInitFunc)atk_editable_text_interface_init,
830      (GInterfaceFinalizeFunc) NULL, NULL},
831     {(GInterfaceInitFunc)atk_text_interface_init,
832      (GInterfaceFinalizeFunc) NULL, NULL},
833     {(GInterfaceInitFunc)atk_component_interface_init,
834      (GInterfaceFinalizeFunc) NULL, NULL},
835     {(GInterfaceInitFunc)atk_image_interface_init,
836      (GInterfaceFinalizeFunc) NULL, NULL}
837 };
838
839 enum WAIType {
840     WAI_ACTION,
841     WAI_STREAMABLE,
842     WAI_EDITABLE_TEXT,
843     WAI_TEXT,
844     WAI_COMPONENT,
845     WAI_IMAGE
846 };
847
848 static GType GetAtkInterfaceTypeFromWAIType(WAIType type)
849 {
850   switch (type) {
851   case WAI_ACTION:
852       return ATK_TYPE_ACTION;
853   case WAI_STREAMABLE:
854       return ATK_TYPE_STREAMABLE_CONTENT;
855   case WAI_EDITABLE_TEXT:
856       return ATK_TYPE_EDITABLE_TEXT;
857   case WAI_TEXT:
858       return ATK_TYPE_TEXT;
859   case WAI_COMPONENT:
860       return ATK_TYPE_COMPONENT;
861   case WAI_IMAGE:
862       return ATK_TYPE_IMAGE;
863   }
864
865   return G_TYPE_INVALID;
866 }
867
868 static guint16 getInterfaceMaskFromObject(AccessibilityObject* coreObject)
869 {
870     guint16 interfaceMask = 0;
871
872     // Streamable is always supported (FIXME: This is wrong)
873     interfaceMask |= 1 << WAI_STREAMABLE;
874
875     // Component interface is always supported
876     interfaceMask |= 1 << WAI_COMPONENT;
877
878     // Action
879     if (!coreObject->actionVerb().isEmpty())
880         interfaceMask |= 1 << WAI_ACTION;
881
882     // Text & Editable Text
883     AccessibilityRole role = coreObject->roleValue();
884
885     if (role == StaticTextRole)
886         interfaceMask |= 1 << WAI_TEXT;
887
888     if (coreObject->isAccessibilityRenderObject() && coreObject->isTextControl()) {
889         if (coreObject->isReadOnly())
890             interfaceMask |= 1 << WAI_TEXT;
891         else
892             interfaceMask |= 1 << WAI_EDITABLE_TEXT;
893     }
894
895     // Image
896     if (coreObject->isImage())
897         interfaceMask |= 1 << WAI_IMAGE;
898
899     return interfaceMask;
900 }
901
902 static const char* getUniqueAccessibilityTypeName(guint16 interfaceMask)
903 {
904 #define WAI_TYPE_NAME_LEN (30) /* Enough for prefix + 5 hex characters (max) */
905     static char name[WAI_TYPE_NAME_LEN + 1];
906     
907     g_sprintf(name, "WAIType%x", interfaceMask);
908     name[WAI_TYPE_NAME_LEN] = '\0';
909     
910     return name;
911 }
912
913 static GType getAccessibilityTypeFromObject(AccessibilityObject* coreObject)
914 {
915     static const GTypeInfo typeInfo = {
916         sizeof(WebKitAccessibleClass),
917         (GBaseInitFunc) NULL,
918         (GBaseFinalizeFunc) NULL,
919         (GClassInitFunc) NULL,
920         (GClassFinalizeFunc) NULL,
921         NULL, /* class data */
922         sizeof(WebKitAccessible), /* instance size */
923         0, /* nb preallocs */
924         (GInstanceInitFunc) NULL,
925         NULL /* value table */
926     };
927
928     guint16 interfaceMask = getInterfaceMaskFromObject(coreObject);
929     const char* atkTypeName = getUniqueAccessibilityTypeName(interfaceMask);
930     GType type = g_type_from_name(atkTypeName);
931     if (type)
932         return type;
933
934     type = g_type_register_static(WEBKIT_TYPE_ACCESSIBLE,
935                                   atkTypeName,
936                                   &typeInfo, GTypeFlags(0));
937     for (guint i = 0; i < G_N_ELEMENTS(AtkInterfacesInitFunctions); i++) {
938         if (interfaceMask & (1 << i))
939             g_type_add_interface_static(type,
940                                         GetAtkInterfaceTypeFromWAIType(static_cast<WAIType>(i)),
941                                         &AtkInterfacesInitFunctions[i]);
942     }
943
944     return type;
945 }
946
947 WebKitAccessible* webkit_accessible_new(AccessibilityObject* coreObject)
948 {
949     GType type = getAccessibilityTypeFromObject(coreObject);
950     AtkObject* object = static_cast<AtkObject*>(g_object_new(type, NULL));
951
952     atk_object_initialize(object, coreObject);
953
954     return WEBKIT_ACCESSIBLE(object);
955 }
956
957 AccessibilityObject* webkit_accessible_get_accessibility_object(WebKitAccessible* accessible)
958 {
959     return accessible->m_object;
960 }
961
962 void webkit_accessible_detach(WebKitAccessible* accessible)
963 {
964     ASSERT(accessible->m_object);
965
966     // We replace the WebCore AccessibilityObject with a fallback object that
967     // provides default implementations to avoid repetitive null-checking after
968     // detachment.
969     accessible->m_object = fallbackObject();
970 }
971
972 #endif // HAVE(ACCESSIBILITY)