3e65701acceb429b4f50107e78c657044e22b990
[WebKit-https.git] / WebCore / page / 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 extern "C" {
111
112 static const gchar* webkit_accessible_get_name(AtkObject* object)
113 {
114     // TODO: Deal with later changes.
115     if (!object->name)
116         atk_object_set_name(object, core(object)->stringValue().utf8().data());
117     return object->name;
118 }
119
120 static const gchar* webkit_accessible_get_description(AtkObject* object)
121 {
122     // TODO: the Mozilla MSAA implementation prepends "Description: "
123     // Should we do this too?
124
125     // TODO: Deal with later changes.
126     if (!object->description)
127         atk_object_set_description(object, core(object)->accessibilityDescription().utf8().data());
128     return object->description;
129 }
130
131 static AtkObject* webkit_accessible_get_parent(AtkObject* object)
132 {
133     AccessibilityObject* coreParent = core(object)->parentObject();
134
135     if (!coreParent)
136         return NULL;
137
138     return coreParent->wrapper();
139 }
140
141 static gint webkit_accessible_get_n_children(AtkObject* object)
142 {
143     return core(object)->children().size();
144 }
145
146 static AtkObject* webkit_accessible_ref_child(AtkObject* object, gint index)
147 {
148     AccessibilityObject* coreObject = core(object);
149
150     g_return_val_if_fail(index >= 0, NULL);
151     g_return_val_if_fail(static_cast<size_t>(index) < coreObject->children().size(), NULL);
152
153     AccessibilityObject* coreChild = coreObject->children().at(index).get();
154
155     if (!coreChild)
156         return NULL;
157
158     AtkObject* child = coreChild->wrapper();
159     // TODO: Should we call atk_object_set_parent() here?
160     //atk_object_set_parent(child, object);
161     g_object_ref(child);
162
163     return child;
164 }
165
166 static gint webkit_accessible_get_index_in_parent(AtkObject* object)
167 {
168     // FIXME: This needs to be implemented.
169     notImplemented();
170     return 0;
171 }
172
173 static AtkRole atkRole(AccessibilityRole role)
174 {
175     switch (role) {
176     case WebCore::ButtonRole:
177         return ATK_ROLE_PUSH_BUTTON;
178     case WebCore::RadioButtonRole:
179         return ATK_ROLE_RADIO_BUTTON;
180     case WebCore::CheckBoxRole:
181         return ATK_ROLE_CHECK_BOX;
182     case WebCore::SliderRole:
183         return ATK_ROLE_SLIDER;
184     case WebCore::TabGroupRole:
185         return ATK_ROLE_PAGE_TAB_LIST;
186     case WebCore::TextFieldRole:
187     case WebCore::TextAreaRole:
188     case WebCore::ListMarkerRole:
189         return ATK_ROLE_ENTRY;
190     case WebCore::StaticTextRole:
191         return ATK_ROLE_TEXT; //?
192     case WebCore::OutlineRole:
193         return ATK_ROLE_TREE;
194     case WebCore::ColumnRole:
195         return ATK_ROLE_UNKNOWN; //?
196     case WebCore::RowRole:
197         return ATK_ROLE_LIST_ITEM;
198     case WebCore::GroupRole:
199         return ATK_ROLE_UNKNOWN; //?
200     case WebCore::ListRole:
201         return ATK_ROLE_LIST;
202     case WebCore::TableRole:
203         return ATK_ROLE_TABLE;
204     case WebCore::LinkRole:
205     case WebCore::WebCoreLinkRole:
206         return ATK_ROLE_LINK;
207     case WebCore::ImageMapRole:
208     case WebCore::ImageRole:
209         return ATK_ROLE_IMAGE;
210     default:
211         return ATK_ROLE_UNKNOWN;
212     }
213 }
214
215 static AtkRole webkit_accessible_get_role(AtkObject* object)
216 {
217     return atkRole(core(object)->roleValue());
218 }
219
220 static void setAtkStateSetFromCoreObject(AccessibilityObject* coreObject, AtkStateSet* stateSet)
221 {
222     // Please keep the state list in alphabetical order
223
224     if (coreObject->isChecked())
225         atk_state_set_add_state(stateSet, ATK_STATE_CHECKED);
226
227     if (!coreObject->isReadOnly())
228         atk_state_set_add_state(stateSet, ATK_STATE_EDITABLE);
229
230     if (coreObject->isEnabled())
231         atk_state_set_add_state(stateSet, ATK_STATE_ENABLED);
232
233     if (coreObject->canSetFocusAttribute())
234         atk_state_set_add_state(stateSet, ATK_STATE_FOCUSABLE);
235
236     if (coreObject->isFocused())
237         atk_state_set_add_state(stateSet, ATK_STATE_FOCUSED);
238
239     // TODO: ATK_STATE_HORIZONTAL
240
241     if (coreObject->isIndeterminate())
242         atk_state_set_add_state(stateSet, ATK_STATE_INDETERMINATE);
243
244     if (coreObject->isMultiSelect())
245         atk_state_set_add_state(stateSet, ATK_STATE_MULTISELECTABLE);
246
247     // TODO: ATK_STATE_OPAQUE
248
249     if (coreObject->isPressed())
250         atk_state_set_add_state(stateSet, ATK_STATE_PRESSED);
251
252     // TODO: ATK_STATE_SELECTABLE_TEXT
253
254     // TODO: ATK_STATE_SENSITIVE
255
256     if (coreObject->isSelected())
257         atk_state_set_add_state(stateSet, ATK_STATE_SELECTED);
258
259     if (!coreObject->isOffScreen())
260         atk_state_set_add_state(stateSet, ATK_STATE_SHOWING);
261
262     // Mutually exclusive, so we group these two
263     if (coreObject->roleValue() == TextFieldRole)
264         atk_state_set_add_state(stateSet, ATK_STATE_SINGLE_LINE);
265     else if (coreObject->roleValue() == TextAreaRole)
266         atk_state_set_add_state(stateSet, ATK_STATE_MULTI_LINE);
267
268     // TODO: ATK_STATE_SENSITIVE
269
270     // TODO: ATK_STATE_VERTICAL
271
272     if (coreObject->isVisited())
273         atk_state_set_add_state(stateSet, ATK_STATE_VISITED);
274 }
275
276 static gpointer webkit_accessible_parent_class = NULL;
277
278 static AtkStateSet* webkit_accessible_ref_state_set(AtkObject* object)
279 {
280     AtkStateSet* stateSet = ATK_OBJECT_CLASS(webkit_accessible_parent_class)->ref_state_set(object);
281     AccessibilityObject* coreObject = core(object);
282
283     if (coreObject == fallbackObject()) {
284         atk_state_set_add_state(stateSet, ATK_STATE_DEFUNCT);
285         return stateSet;
286     }
287
288     setAtkStateSetFromCoreObject(coreObject, stateSet);
289
290     return stateSet;
291 }
292
293 static void webkit_accessible_init(AtkObject* object, gpointer data)
294 {
295     if (ATK_OBJECT_CLASS(webkit_accessible_parent_class)->initialize)
296         ATK_OBJECT_CLASS(webkit_accessible_parent_class)->initialize(object, data);
297
298     WEBKIT_ACCESSIBLE(object)->m_object = reinterpret_cast<AccessibilityObject*>(data);
299 }
300
301 static void webkit_accessible_finalize(GObject* object)
302 {
303     // This is a good time to clear the return buffer.
304     returnString(String());
305
306     if (G_OBJECT_CLASS(webkit_accessible_parent_class)->finalize)
307         G_OBJECT_CLASS(webkit_accessible_parent_class)->finalize(object);
308 }
309
310 static void webkit_accessible_class_init(AtkObjectClass* klass)
311 {
312     GObjectClass* gobjectClass = G_OBJECT_CLASS(klass);
313
314     webkit_accessible_parent_class = g_type_class_peek_parent(klass);
315
316     gobjectClass->finalize = webkit_accessible_finalize;
317
318     klass->initialize = webkit_accessible_init;
319     klass->get_name = webkit_accessible_get_name;
320     klass->get_description = webkit_accessible_get_description;
321     klass->get_parent = webkit_accessible_get_parent;
322     klass->get_n_children = webkit_accessible_get_n_children;
323     klass->ref_child = webkit_accessible_ref_child;
324     klass->get_role = webkit_accessible_get_role;
325     klass->ref_state_set = webkit_accessible_ref_state_set;
326 }
327
328 GType
329 webkit_accessible_get_type(void)
330 {
331     static volatile gsize type_volatile = 0;
332
333     if (g_once_init_enter(&type_volatile)) {
334         static const GTypeInfo tinfo = {
335             sizeof(WebKitAccessibleClass),
336             (GBaseInitFunc)NULL,
337             (GBaseFinalizeFunc)NULL,
338             (GClassInitFunc)webkit_accessible_class_init,
339             (GClassFinalizeFunc)NULL,
340             NULL, /* class data */
341             sizeof(WebKitAccessible), /* instance size */
342             0, /* nb preallocs */
343             (GInstanceInitFunc)NULL,
344             NULL /* value table */
345         };
346
347         GType type = g_type_register_static(ATK_TYPE_OBJECT,
348                                             "WebKitAccessible", &tinfo, GTypeFlags(0));
349         g_once_init_leave(&type_volatile, type);
350     }
351
352     return type_volatile;
353 }
354
355 static gboolean webkit_accessible_action_do_action(AtkAction* action, gint i)
356 {
357     g_return_val_if_fail(i == 0, FALSE);
358     return core(action)->performDefaultAction();
359 }
360
361 static gint webkit_accessible_action_get_n_actions(AtkAction* action)
362 {
363     return 1;
364 }
365
366 static const gchar* webkit_accessible_action_get_description(AtkAction* action, gint i)
367 {
368     g_return_val_if_fail(i == 0, NULL);
369     // TODO: Need a way to provide/localize action descriptions.
370     notImplemented();
371     return "";
372 }
373
374 static const gchar* webkit_accessible_action_get_keybinding(AtkAction* action, gint i)
375 {
376     g_return_val_if_fail(i == 0, NULL);
377     // FIXME: Construct a proper keybinding string.
378     return returnString(core(action)->accessKey().string());
379 }
380
381 static const gchar* webkit_accessible_action_get_name(AtkAction* action, gint i)
382 {
383     g_return_val_if_fail(i == 0, NULL);
384     return returnString(core(action)->actionVerb());
385 }
386
387 static void atk_action_interface_init(AtkActionIface* iface)
388 {
389     iface->do_action = webkit_accessible_action_do_action;
390     iface->get_n_actions = webkit_accessible_action_get_n_actions;
391     iface->get_description = webkit_accessible_action_get_description;
392     iface->get_keybinding = webkit_accessible_action_get_keybinding;
393     iface->get_name = webkit_accessible_action_get_name;
394 }
395
396 // Text
397
398 static gchar* webkit_accessible_text_get_text(AtkText* text, gint startOffset, gint endOffset)
399 {
400     AccessibilityObject* coreObject = core(text);
401     String ret;
402     unsigned start = startOffset;
403     int length = endOffset - startOffset;
404
405     if (coreObject->isTextControl())
406         ret = coreObject->doAXStringForRange(PlainTextRange(start, length));
407     else
408         ret = coreObject->textUnderElement().substring(start, length);
409
410     return g_strdup(ret.utf8().data());
411 }
412
413 static gchar* webkit_accessible_text_get_text_after_offset(AtkText* text, gint offset, AtkTextBoundary boundary_type, gint* start_offset, gint* end_offset)
414 {
415     notImplemented();
416     return NULL;
417 }
418
419 static gchar* webkit_accessible_text_get_text_at_offset(AtkText* text, gint offset, AtkTextBoundary boundary_type, gint* start_offset, gint* end_offset)
420 {
421     notImplemented();
422     return NULL;
423 }
424
425 static gunichar webkit_accessible_text_get_character_at_offset(AtkText* text, gint offset)
426 {
427     notImplemented();
428     return 0;
429 }
430
431 static gchar* webkit_accessible_text_get_text_before_offset(AtkText* text, gint offset, AtkTextBoundary boundary_type, gint* start_offset, gint* end_offset)
432 {
433     notImplemented();
434     return NULL;
435 }
436
437 static gint webkit_accessible_text_get_caret_offset(AtkText* text)
438 {
439     // TODO: Verify this, especially for RTL text.
440     return core(text)->selectionStart();
441 }
442
443 static AtkAttributeSet* webkit_accessible_text_get_run_attributes(AtkText* text, gint offset, gint* start_offset, gint* end_offset)
444 {
445     notImplemented();
446     return NULL;
447 }
448
449 static AtkAttributeSet* webkit_accessible_text_get_default_attributes(AtkText* text)
450 {
451     notImplemented();
452     return NULL;
453 }
454
455 static void webkit_accessible_text_get_character_extents(AtkText* text, gint offset, gint* x, gint* y, gint* width, gint* height, AtkCoordType coords)
456 {
457     IntRect extents = core(text)->doAXBoundsForRange(PlainTextRange(offset, 1));
458     // FIXME: Use the AtkCoordType
459     // Requires WebCore::ScrollView::contentsToScreen() to be implemented
460
461 #if 0
462     switch(coords) {
463     case ATK_XY_SCREEN:
464         extents = core(text)->document()->view()->contentsToScreen(extents);
465         break;
466     case ATK_XY_WINDOW:
467         // No-op
468         break;
469     }
470 #endif
471
472     *x = extents.x();
473     *y = extents.y();
474     *width = extents.width();
475     *height = extents.height();
476 }
477
478 static gint webkit_accessible_text_get_character_count(AtkText* text)
479 {
480     AccessibilityObject* coreObject = core(text);
481
482     if (coreObject->isTextControl())
483         return coreObject->textLength();
484     else
485         return coreObject->textUnderElement().length();
486 }
487
488 static gint webkit_accessible_text_get_offset_at_point(AtkText* text, gint x, gint y, AtkCoordType coords)
489 {
490     // FIXME: Use the AtkCoordType
491     // TODO: Is it correct to ignore range.length?
492     IntPoint pos(x, y);
493     PlainTextRange range = core(text)->doAXRangeForPosition(pos);
494     return range.start;
495 }
496
497 static gint webkit_accessible_text_get_n_selections(AtkText* text)
498 {
499     notImplemented();
500     return 0;
501 }
502
503 static gchar* webkit_accessible_text_get_selection(AtkText* text, gint selection_num, gint* start_offset, gint* end_offset)
504 {
505     notImplemented();
506     return NULL;
507 }
508
509 static gboolean webkit_accessible_text_add_selection(AtkText* text, gint start_offset, gint end_offset)
510 {
511     notImplemented();
512     return FALSE;
513 }
514
515 static gboolean webkit_accessible_text_remove_selection(AtkText* text, gint selection_num)
516 {
517     notImplemented();
518     return FALSE;
519 }
520
521 static gboolean webkit_accessible_text_set_selection(AtkText* text, gint selection_num, gint start_offset, gint end_offset)
522 {
523     notImplemented();
524     return FALSE;
525 }
526
527 static gboolean webkit_accessible_text_set_caret_offset(AtkText* text, gint offset)
528 {
529     // TODO: Verify
530     //core(text)->setSelectedTextRange(PlainTextRange(offset, 0));
531     AccessibilityObject* coreObject = core(text);
532     coreObject->setSelectedVisiblePositionRange(coreObject->visiblePositionRangeForRange(PlainTextRange(offset, 0)));
533     return TRUE;
534 }
535
536 static void atk_text_interface_init(AtkTextIface* iface)
537 {
538     iface->get_text = webkit_accessible_text_get_text;
539     iface->get_text_after_offset = webkit_accessible_text_get_text_after_offset;
540     iface->get_text_at_offset = webkit_accessible_text_get_text_at_offset;
541     iface->get_character_at_offset = webkit_accessible_text_get_character_at_offset;
542     iface->get_text_before_offset = webkit_accessible_text_get_text_before_offset;
543     iface->get_caret_offset = webkit_accessible_text_get_caret_offset;
544     iface->get_run_attributes = webkit_accessible_text_get_run_attributes;
545     iface->get_default_attributes = webkit_accessible_text_get_default_attributes;
546     iface->get_character_extents = webkit_accessible_text_get_character_extents;
547     iface->get_character_count = webkit_accessible_text_get_character_count;
548     iface->get_offset_at_point = webkit_accessible_text_get_offset_at_point;
549     iface->get_n_selections = webkit_accessible_text_get_n_selections;
550     iface->get_selection = webkit_accessible_text_get_selection;
551
552     // set methods
553     iface->add_selection = webkit_accessible_text_add_selection;
554     iface->remove_selection = webkit_accessible_text_remove_selection;
555     iface->set_selection = webkit_accessible_text_set_selection;
556     iface->set_caret_offset = webkit_accessible_text_set_caret_offset;
557 }
558
559 // EditableText
560
561 static gboolean webkit_accessible_editable_text_set_run_attributes(AtkEditableText* text, AtkAttributeSet* attrib_set, gint start_offset, gint end_offset)
562 {
563     notImplemented();
564     return FALSE;
565 }
566
567 static void webkit_accessible_editable_text_set_text_contents(AtkEditableText* text, const gchar* string)
568 {
569     // FIXME: string nullcheck?
570     core(text)->setValue(String::fromUTF8(string));
571 }
572
573 static void webkit_accessible_editable_text_insert_text(AtkEditableText* text, const gchar* string, gint length, gint* position)
574 {
575     // FIXME: string nullcheck?
576
577     AccessibilityObject* coreObject = core(text);
578     // FIXME: Not implemented in WebCore
579     //coreObject->setSelectedTextRange(PlainTextRange(*position, 0));
580     //coreObject->setSelectedText(String::fromUTF8(string));
581
582     if (!coreObject->document() || !coreObject->document()->frame())
583         return;
584     coreObject->setSelectedVisiblePositionRange(coreObject->visiblePositionRangeForRange(PlainTextRange(*position, 0)));
585     coreObject->setFocused(true);
586     // FIXME: We should set position to the actual inserted text length, which may be less than that requested.
587     if (coreObject->document()->frame()->editor()->insertTextWithoutSendingTextEvent(String::fromUTF8(string), false, 0))
588         *position += length;
589 }
590
591 static void webkit_accessible_editable_text_copy_text(AtkEditableText* text, gint start_pos, gint end_pos)
592 {
593     notImplemented();
594 }
595
596 static void webkit_accessible_editable_text_cut_text(AtkEditableText* text, gint start_pos, gint end_pos)
597 {
598     notImplemented();
599 }
600
601 static void webkit_accessible_editable_text_delete_text(AtkEditableText* text, gint start_pos, gint end_pos)
602 {
603     AccessibilityObject* coreObject = core(text);
604     // FIXME: Not implemented in WebCore
605     //coreObject->setSelectedTextRange(PlainTextRange(start_pos, end_pos - start_pos));
606     //coreObject->setSelectedText(String());
607
608     if (!coreObject->document() || !coreObject->document()->frame())
609         return;
610     coreObject->setSelectedVisiblePositionRange(coreObject->visiblePositionRangeForRange(PlainTextRange(start_pos, end_pos - start_pos)));
611     coreObject->setFocused(true);
612     coreObject->document()->frame()->editor()->performDelete();
613 }
614
615 static void webkit_accessible_editable_text_paste_text(AtkEditableText* text, gint position)
616 {
617     notImplemented();
618 }
619
620 static void atk_editable_text_interface_init(AtkEditableTextIface* iface)
621 {
622     iface->set_run_attributes = webkit_accessible_editable_text_set_run_attributes;
623     iface->set_text_contents = webkit_accessible_editable_text_set_text_contents;
624     iface->insert_text = webkit_accessible_editable_text_insert_text;
625     iface->copy_text = webkit_accessible_editable_text_copy_text;
626     iface->cut_text = webkit_accessible_editable_text_cut_text;
627     iface->delete_text = webkit_accessible_editable_text_delete_text;
628     iface->paste_text = webkit_accessible_editable_text_paste_text;
629 }
630
631 // StreamableContent
632
633 static gint webkit_accessible_streamable_content_get_n_mime_types(AtkStreamableContent* streamable)
634 {
635     notImplemented();
636     return 0;
637 }
638
639 static G_CONST_RETURN gchar* webkit_accessible_streamable_content_get_mime_type(AtkStreamableContent* streamable, gint i)
640 {
641     notImplemented();
642     return "";
643 }
644
645 static GIOChannel* webkit_accessible_streamable_content_get_stream(AtkStreamableContent* streamable, const gchar* mime_type)
646 {
647     notImplemented();
648     return NULL;
649 }
650
651 static G_CONST_RETURN gchar* webkit_accessible_streamable_content_get_uri(AtkStreamableContent* streamable, const gchar* mime_type)
652 {
653     notImplemented();
654     return NULL;
655 }
656
657 static void atk_streamable_content_interface_init(AtkStreamableContentIface* iface)
658 {
659     iface->get_n_mime_types = webkit_accessible_streamable_content_get_n_mime_types;
660     iface->get_mime_type = webkit_accessible_streamable_content_get_mime_type;
661     iface->get_stream = webkit_accessible_streamable_content_get_stream;
662     iface->get_uri = webkit_accessible_streamable_content_get_uri;
663 }
664
665 static const GInterfaceInfo AtkInterfacesInitFunctions[] = {
666     {(GInterfaceInitFunc)atk_action_interface_init,
667      (GInterfaceFinalizeFunc) NULL, NULL},
668     {(GInterfaceInitFunc)atk_streamable_content_interface_init,
669      (GInterfaceFinalizeFunc) NULL, NULL},
670     {(GInterfaceInitFunc)atk_editable_text_interface_init,
671      (GInterfaceFinalizeFunc) NULL, NULL},
672     {(GInterfaceInitFunc)atk_text_interface_init,
673      (GInterfaceFinalizeFunc) NULL, NULL}
674 };
675
676 enum WAIType {
677     WAI_ACTION,
678     WAI_STREAMABLE,
679     WAI_EDITABLE_TEXT,
680     WAI_TEXT
681 };
682
683 static GType GetAtkInterfaceTypeFromWAIType(WAIType type)
684 {
685   switch (type) {
686   case WAI_ACTION:
687       return ATK_TYPE_ACTION;
688   case WAI_STREAMABLE:
689       return ATK_TYPE_STREAMABLE_CONTENT;
690   case WAI_EDITABLE_TEXT:
691       return ATK_TYPE_EDITABLE_TEXT;
692   case WAI_TEXT:
693       return ATK_TYPE_TEXT;
694   }
695
696   return G_TYPE_INVALID;
697 }
698
699 static guint16 getInterfaceMaskFromObject(AccessibilityObject* coreObject)
700 {
701     guint16 interfaceMask = 0;
702
703     // Streamable is always supported (FIXME: This is wrong)
704     interfaceMask |= 1 << WAI_STREAMABLE;
705
706     // Action
707     if (!coreObject->actionVerb().isEmpty())
708         interfaceMask |= 1 << WAI_ACTION;
709
710     // Text & Editable Text
711     AccessibilityRole role = coreObject->roleValue();
712
713     if (role == StaticTextRole)
714         interfaceMask |= 1 << WAI_TEXT;
715
716     if (coreObject->isAccessibilityRenderObject() && coreObject->isTextControl()) {
717         if (coreObject->isReadOnly())
718             interfaceMask |= 1 << WAI_TEXT;
719         else
720             interfaceMask |= 1 << WAI_EDITABLE_TEXT;
721     }
722
723     return interfaceMask;
724 }
725
726 static const char* getUniqueAccessibilityTypeName(guint16 interfaceMask)
727 {
728 #define WAI_TYPE_NAME_LEN (30) /* Enough for prefix + 5 hex characters (max) */
729     static char name[WAI_TYPE_NAME_LEN + 1];
730     
731     g_sprintf(name, "WAIType%x", interfaceMask);
732     name[WAI_TYPE_NAME_LEN] = '\0';
733     
734     return name;
735 }
736
737 static GType getAccessibilityTypeFromObject(AccessibilityObject* coreObject)
738 {
739     static const GTypeInfo typeInfo = {
740         sizeof(WebKitAccessibleClass),
741         (GBaseInitFunc) NULL,
742         (GBaseFinalizeFunc) NULL,
743         (GClassInitFunc) NULL,
744         (GClassFinalizeFunc) NULL,
745         NULL, /* class data */
746         sizeof(WebKitAccessible), /* instance size */
747         0, /* nb preallocs */
748         (GInstanceInitFunc) NULL,
749         NULL /* value table */
750     };
751
752     guint16 interfaceMask = getInterfaceMaskFromObject(coreObject);
753     const char* atkTypeName = getUniqueAccessibilityTypeName(interfaceMask);
754     GType type = g_type_from_name(atkTypeName);
755     if (type)
756         return type;
757
758     type = g_type_register_static(WEBKIT_TYPE_ACCESSIBLE,
759                                   atkTypeName,
760                                   &typeInfo, GTypeFlags(0));
761     for (guint i = 0; i < G_N_ELEMENTS(AtkInterfacesInitFunctions); i++) {
762         if (interfaceMask & (1 << i))
763             g_type_add_interface_static(type,
764                                         GetAtkInterfaceTypeFromWAIType(static_cast<WAIType>(i)),
765                                         &AtkInterfacesInitFunctions[i]);
766     }
767
768     return type;
769 }
770
771 WebKitAccessible* webkit_accessible_new(AccessibilityObject* coreObject)
772 {
773     GType type = getAccessibilityTypeFromObject(coreObject);
774     AtkObject* object = static_cast<AtkObject*>(g_object_new(type, NULL));
775
776     atk_object_initialize(object, coreObject);
777
778     return WEBKIT_ACCESSIBLE(object);
779 }
780
781 AccessibilityObject* webkit_accessible_get_accessibility_object(WebKitAccessible* accessible)
782 {
783     return accessible->m_object;
784 }
785
786 void webkit_accessible_detach(WebKitAccessible* accessible)
787 {
788     ASSERT(accessible->m_object);
789
790     // We replace the WebCore AccessibilityObject with a fallback object that
791     // provides default implementations to avoid repetitive null-checking after
792     // detachment.
793     accessible->m_object = fallbackObject();
794 }
795
796 }
797
798 #endif // HAVE(ACCESSIBILITY)