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