BUILD FIX: Qt build broke after r38235.
[WebKit-https.git] / WebCore / page / gtk / AccessibilityObjectWrapperAtk.cpp
1 /*
2  * Copyright (C) 2008 Nuanti Ltd.
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Library General Public
6  * License as published by the Free Software Foundation; either
7  * version 2 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Library General Public License for more details.
13  *
14  * You should have received a copy of the GNU Library General Public License
15  * along with this library; see the file COPYING.LIB.  If not, write to
16  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
17  * Boston, MA 02110-1301, USA.
18  */
19
20 #include "config.h"
21 #include "AccessibilityObjectWrapperAtk.h"
22
23 #if HAVE(ACCESSIBILITY)
24
25 #include "AXObjectCache.h"
26 #include "AccessibilityListBox.h"
27 #include "AccessibilityRenderObject.h"
28 #include "AtomicString.h"
29 #include "CString.h"
30 #include "Document.h"
31 #include "Editor.h"
32 #include "Frame.h"
33 #include "FrameView.h"
34 #include "IntRect.h"
35 #include "NotImplemented.h"
36
37 #include <atk/atk.h>
38
39 using namespace WebCore;
40
41 // Used to provide const char* returns.
42 static const char* returnString(const String& str)
43 {
44     static CString returnedString;
45     returnedString = str.utf8();
46     return returnedString.data();
47 }
48
49 static AccessibilityObject* core(WebKitAccessible* accessible)
50 {
51     if (!accessible)
52         return 0;
53
54     return accessible->m_object;
55 }
56
57 static AccessibilityObject* core(AtkObject* object)
58 {
59     if (!WEBKIT_IS_ACCESSIBLE(object))
60         return 0;
61
62     return core(WEBKIT_ACCESSIBLE(object));
63 }
64
65 static AccessibilityObject* core(AtkAction* action)
66 {
67     return core(ATK_OBJECT(action));
68 }
69
70 static AccessibilityObject* core(AtkStreamableContent* streamable)
71 {
72     return core(ATK_OBJECT(streamable));
73 }
74
75 static AccessibilityObject* core(AtkText* text)
76 {
77     return core(ATK_OBJECT(text));
78 }
79
80 static AccessibilityObject* core(AtkEditableText* text)
81 {
82     return core(ATK_OBJECT(text));
83 }
84
85 extern "C" {
86
87 static gpointer parent_class = NULL;
88
89 static void webkit_accessible_init(AtkObject* object, gpointer data)
90 {
91     g_return_if_fail(WEBKIT_IS_ACCESSIBLE(object));
92     g_return_if_fail(data);
93
94     if (ATK_OBJECT_CLASS(parent_class)->initialize)
95         ATK_OBJECT_CLASS(parent_class)->initialize(object, data);
96
97     WEBKIT_ACCESSIBLE(object)->m_object = reinterpret_cast<AccessibilityObject*>(data);
98 }
99
100 static void webkit_accessible_finalize(GObject* object)
101 {
102     // This is a good time to clear the return buffer.
103     returnString(String());
104
105     if (G_OBJECT_CLASS(parent_class)->finalize)
106         G_OBJECT_CLASS(parent_class)->finalize(object);
107 }
108
109 static const gchar* webkit_accessible_get_name(AtkObject* object)
110 {
111     // TODO: Deal with later changes.
112     if (!object->name)
113         atk_object_set_name(object, core(object)->title().utf8().data());
114     return object->name;
115 }
116
117 static const gchar* webkit_accessible_get_description(AtkObject* object)
118 {
119     // TODO: the Mozilla MSAA implementation prepends "Description: "
120     // Should we do this too?
121
122     // TODO: Deal with later changes.
123     if (!object->description)
124         atk_object_set_description(object, core(object)->accessibilityDescription().utf8().data());
125     return object->description;
126 }
127
128 static AtkObject* webkit_accessible_get_parent(AtkObject* object)
129 {
130     AccessibilityObject* coreParent = core(object)->parentObject();
131
132     if (!coreParent)
133         return NULL;
134
135     return coreParent->wrapper();
136 }
137
138 static gint webkit_accessible_get_n_children(AtkObject* object)
139 {
140     return core(object)->children().size();
141 }
142
143 static AtkObject* webkit_accessible_ref_child(AtkObject* object, gint index)
144 {
145     AccessibilityObject* coreObject = core(object);
146
147     g_return_val_if_fail(index >= 0, NULL);
148     g_return_val_if_fail(static_cast<size_t>(index) < coreObject->children().size(), NULL);
149
150     AccessibilityObject* coreChild = coreObject->children().at(index).get();
151
152     if (!coreChild)
153         return NULL;
154
155     AtkObject* child = coreChild->wrapper();
156     // TODO: Should we call atk_object_set_parent() here?
157     //atk_object_set_parent(child, object);
158     g_object_ref(child);
159
160     return child;
161 }
162
163 static gint webkit_accessible_get_index_in_parent(AtkObject* object)
164 {
165     // FIXME: This needs to be implemented.
166     notImplemented();
167     return 0;
168 }
169
170 static AtkRole atkRole(AccessibilityRole role)
171 {
172     switch (role) {
173     case WebCore::ButtonRole:
174         return ATK_ROLE_PUSH_BUTTON;
175     case WebCore::RadioButtonRole:
176         return ATK_ROLE_RADIO_BUTTON;
177     case WebCore::CheckBoxRole:
178         return ATK_ROLE_CHECK_BOX;
179     case WebCore::SliderRole:
180         return ATK_ROLE_SLIDER;
181     case WebCore::TabGroupRole:
182         return ATK_ROLE_PAGE_TAB_LIST;
183     case WebCore::TextFieldRole:
184     case WebCore::TextAreaRole:
185     case WebCore::ListMarkerRole:
186         return ATK_ROLE_ENTRY;
187     case WebCore::StaticTextRole:
188         return ATK_ROLE_TEXT; //?
189     case WebCore::OutlineRole:
190         return ATK_ROLE_TREE;
191     case WebCore::ColumnRole:
192         return ATK_ROLE_UNKNOWN; //?
193     case WebCore::RowRole:
194         return ATK_ROLE_LIST_ITEM;
195     case WebCore::GroupRole:
196         return ATK_ROLE_UNKNOWN; //?
197     case WebCore::ListRole:
198         return ATK_ROLE_LIST;
199     case WebCore::TableRole:
200         return ATK_ROLE_TABLE;
201     case WebCore::LinkRole:
202     case WebCore::WebCoreLinkRole:
203         return ATK_ROLE_LINK;
204     case WebCore::ImageMapRole:
205     case WebCore::ImageRole:
206         return ATK_ROLE_IMAGE;
207     default:
208         return ATK_ROLE_UNKNOWN;
209     }
210 }
211
212 static AtkRole webkit_accessible_get_role(AtkObject* object)
213 {
214     return atkRole(core(object)->roleValue());
215 }
216
217 static void webkit_accessible_class_init(AtkObjectClass* klass)
218 {
219     GObjectClass* gobject_class = G_OBJECT_CLASS(klass);
220
221     parent_class = g_type_class_peek_parent(klass);
222
223     klass->initialize = webkit_accessible_init;
224
225     gobject_class->finalize = webkit_accessible_finalize;
226
227     klass->get_name = webkit_accessible_get_name;
228     klass->get_description = webkit_accessible_get_description;
229     klass->get_parent = webkit_accessible_get_parent;
230     klass->get_n_children = webkit_accessible_get_n_children;
231     klass->ref_child = webkit_accessible_ref_child;
232     //klass->get_index_in_parent = webkit_accessible_get_index_in_parent;
233     klass->get_role = webkit_accessible_get_role;
234     //klass->get_attributes = webkit_accessible_get_attributes;
235     //klass->ref_state_set = webkit_accessible_ref_state_set;
236     //klass->ref_relation_set = webkit_accessible_ref_relation_set;
237 }
238
239 static gboolean webkit_accessible_action_do_action(AtkAction* action, gint i)
240 {
241     g_return_val_if_fail(i == 0, FALSE);
242     return core(action)->performDefaultAction();
243 }
244
245 static gint webkit_accessible_action_get_n_actions(AtkAction* action)
246 {
247     return 1;
248 }
249
250 static const gchar* webkit_accessible_action_get_description(AtkAction* action, gint i)
251 {
252     g_return_val_if_fail(i == 0, NULL);
253     // TODO: Need a way to provide/localize action descriptions.
254     notImplemented();
255     return "";
256 }
257
258 static const gchar* webkit_accessible_action_get_keybinding(AtkAction* action, gint i)
259 {
260     g_return_val_if_fail(i == 0, NULL);
261     // FIXME: Construct a proper keybinding string.
262     return returnString(core(action)->accessKey().string());
263 }
264
265 static const gchar* webkit_accessible_action_get_name(AtkAction* action, gint i)
266 {
267     g_return_val_if_fail(i == 0, NULL);
268     return returnString(core(action)->actionVerb());
269 }
270
271 static void atk_action_interface_init(AtkActionIface* iface)
272 {
273     g_return_if_fail(iface);
274
275     iface->do_action = webkit_accessible_action_do_action;
276     iface->get_n_actions = webkit_accessible_action_get_n_actions;
277     iface->get_description = webkit_accessible_action_get_description;
278     iface->get_keybinding = webkit_accessible_action_get_keybinding;
279     iface->get_name = webkit_accessible_action_get_name;
280 }
281
282 // Text
283
284 static gchar* webkit_accessible_text_get_text(AtkText* text, gint start_offset, gint end_offset)
285 {
286     String ret = core(text)->doAXStringForRange(PlainTextRange(start_offset, end_offset - start_offset));
287     // TODO: Intentionally copied?
288     return g_strdup(ret.utf8().data());
289 }
290
291 static gchar* webkit_accessible_text_get_text_after_offset(AtkText* text, gint offset, AtkTextBoundary boundary_type, gint* start_offset, gint* end_offset)
292 {
293     notImplemented();
294     return NULL;
295 }
296
297 static gchar* webkit_accessible_text_get_text_at_offset(AtkText* text, gint offset, AtkTextBoundary boundary_type, gint* start_offset, gint* end_offset)
298 {
299     notImplemented();
300     return NULL;
301 }
302
303 static gunichar webkit_accessible_text_get_character_at_offset(AtkText* text, gint offset)
304 {
305     notImplemented();
306     return 0;
307 }
308
309 static gchar* webkit_accessible_text_get_text_before_offset(AtkText* text, gint offset, AtkTextBoundary boundary_type, gint* start_offset, gint* end_offset)
310 {
311     notImplemented();
312     return NULL;
313 }
314
315 static gint webkit_accessible_text_get_caret_offset(AtkText* text)
316 {
317     // TODO: Verify this, especially for RTL text.
318     return core(text)->selectionStart();
319 }
320
321 static AtkAttributeSet* webkit_accessible_text_get_run_attributes(AtkText* text, gint offset, gint* start_offset, gint* end_offset)
322 {
323     notImplemented();
324     return NULL;
325 }
326
327 static AtkAttributeSet* webkit_accessible_text_get_default_attributes(AtkText* text)
328 {
329     notImplemented();
330     return NULL;
331 }
332
333 static void webkit_accessible_text_get_character_extents(AtkText* text, gint offset, gint* x, gint* y, gint* width, gint* height, AtkCoordType coords)
334 {
335     IntRect extents = core(text)->doAXBoundsForRange(PlainTextRange(offset, 1));
336     // FIXME: Use the AtkCoordType
337     // Requires WebCore::ScrollView::contentsToScreen() to be implemented
338
339 #if 0
340     switch(coords) {
341     case ATK_XY_SCREEN:
342         extents = core(text)->document()->view()->contentsToScreen(extents);
343         break;
344     case ATK_XY_WINDOW:
345         // No-op
346         break;
347     }
348 #endif
349
350     *x = extents.x();
351     *y = extents.y();
352     *width = extents.width();
353     *height = extents.height();
354 }
355
356 static gint webkit_accessible_text_get_character_count(AtkText* text)
357 {
358     return core(text)->textLength();
359 }
360
361 static gint webkit_accessible_text_get_offset_at_point(AtkText* text, gint x, gint y, AtkCoordType coords)
362 {
363     // FIXME: Use the AtkCoordType
364     // TODO: Is it correct to ignore range.length?
365     IntPoint pos(x, y);
366     PlainTextRange range = core(text)->doAXRangeForPosition(pos);
367     return range.start;
368 }
369
370 static gint webkit_accessible_text_get_n_selections(AtkText* text)
371 {
372     notImplemented();
373     return 0;
374 }
375
376 static gchar* webkit_accessible_text_get_selection(AtkText* text, gint selection_num, gint* start_offset, gint* end_offset)
377 {
378     notImplemented();
379     return NULL;
380 }
381
382 static gboolean webkit_accessible_text_add_selection(AtkText* text, gint start_offset, gint end_offset)
383 {
384     notImplemented();
385     return FALSE;
386 }
387
388 static gboolean webkit_accessible_text_remove_selection(AtkText* text, gint selection_num)
389 {
390     notImplemented();
391     return FALSE;
392 }
393
394 static gboolean webkit_accessible_text_set_selection(AtkText* text, gint selection_num, gint start_offset, gint end_offset)
395 {
396     notImplemented();
397     return FALSE;
398 }
399
400 static gboolean webkit_accessible_text_set_caret_offset(AtkText* text, gint offset)
401 {
402     // TODO: Verify
403     //core(text)->setSelectedTextRange(PlainTextRange(offset, 0));
404     AccessibilityObject* coreObject = core(text);
405     coreObject->setSelectedVisiblePositionRange(coreObject->visiblePositionRangeForRange(PlainTextRange(offset, 0)));
406     return TRUE;
407 }
408
409 #if 0
410 // Signal handlers
411 static void webkit_accessible_text_text_changed(AtkText* text, gint position, gint length)
412 {
413 }
414
415 static void webkit_accessible_text_text_caret_moved(AtkText* text, gint location)
416 {
417 }
418
419 static void webkit_accessible_text_text_selection_changed(AtkText* text)
420 {
421 }
422
423 static void webkit_accessible_text_text_attributes_changed(AtkText* text)
424 {
425 }
426
427 static void webkit_accessible_text_get_range_extents(AtkText* text, gint start_offset, gint end_offset, AtkCoordType coord_type, AtkTextRectangle* rect)
428 {
429 }
430
431 static AtkTextRange** webkit_accessible_text_get_bounded_ranges(AtkText* text, AtkTextRectangle* rect, AtkCoordType coord_type, AtkTextClipType x_clip_type, AtkTextClipType y_clip_type)
432 {
433 }
434 #endif
435
436 static void atk_text_interface_init(AtkTextIface* iface)
437 {
438     g_return_if_fail(iface);
439
440     iface->get_text = webkit_accessible_text_get_text;
441     iface->get_text_after_offset = webkit_accessible_text_get_text_after_offset;
442     iface->get_text_at_offset = webkit_accessible_text_get_text_at_offset;
443     iface->get_character_at_offset = webkit_accessible_text_get_character_at_offset;
444     iface->get_text_before_offset = webkit_accessible_text_get_text_before_offset;
445     iface->get_caret_offset = webkit_accessible_text_get_caret_offset;
446     iface->get_run_attributes = webkit_accessible_text_get_run_attributes;
447     iface->get_default_attributes = webkit_accessible_text_get_default_attributes;
448     iface->get_character_extents = webkit_accessible_text_get_character_extents;
449     //iface->get_range_extents = ;
450     iface->get_character_count = webkit_accessible_text_get_character_count;
451     iface->get_offset_at_point = webkit_accessible_text_get_offset_at_point;
452     iface->get_n_selections = webkit_accessible_text_get_n_selections;
453     iface->get_selection = webkit_accessible_text_get_selection;
454
455     // set methods
456     iface->add_selection = webkit_accessible_text_add_selection;
457     iface->remove_selection = webkit_accessible_text_remove_selection;
458     iface->set_selection = webkit_accessible_text_set_selection;
459     iface->set_caret_offset = webkit_accessible_text_set_caret_offset;
460 }
461
462 // EditableText
463
464 static gboolean webkit_accessible_editable_text_set_run_attributes(AtkEditableText* text, AtkAttributeSet* attrib_set, gint start_offset, gint end_offset)
465 {
466     notImplemented();
467     return FALSE;
468 }
469
470 static void webkit_accessible_editable_text_set_text_contents(AtkEditableText* text, const gchar* string)
471 {
472     // FIXME: string nullcheck?
473     core(text)->setValue(String::fromUTF8(string));
474 }
475
476 static void webkit_accessible_editable_text_insert_text(AtkEditableText* text, const gchar* string, gint length, gint* position)
477 {
478     // FIXME: string nullcheck?
479
480     AccessibilityObject* coreObject = core(text);
481     // FIXME: Not implemented in WebCore
482     //coreObject->setSelectedTextRange(PlainTextRange(*position, 0));
483     //coreObject->setSelectedText(String::fromUTF8(string));
484
485     if (!coreObject->document() || !coreObject->document()->frame())
486         return;
487     coreObject->setSelectedVisiblePositionRange(coreObject->visiblePositionRangeForRange(PlainTextRange(*position, 0)));
488     coreObject->setFocused(true);
489     // FIXME: We should set position to the actual inserted text length, which may be less than that requested.
490     if (coreObject->document()->frame()->editor()->insertTextWithoutSendingTextEvent(String::fromUTF8(string), false, 0))
491         *position += length;
492 }
493
494 static void webkit_accessible_editable_text_copy_text(AtkEditableText* text, gint start_pos, gint end_pos)
495 {
496     notImplemented();
497 }
498
499 static void webkit_accessible_editable_text_cut_text(AtkEditableText* text, gint start_pos, gint end_pos)
500 {
501     notImplemented();
502 }
503
504 static void webkit_accessible_editable_text_delete_text(AtkEditableText* text, gint start_pos, gint end_pos)
505 {
506     AccessibilityObject* coreObject = core(text);
507     // FIXME: Not implemented in WebCore
508     //coreObject->setSelectedTextRange(PlainTextRange(start_pos, end_pos - start_pos));
509     //coreObject->setSelectedText(String());
510
511     if (!coreObject->document() || !coreObject->document()->frame())
512         return;
513     coreObject->setSelectedVisiblePositionRange(coreObject->visiblePositionRangeForRange(PlainTextRange(start_pos, end_pos - start_pos)));
514     coreObject->setFocused(true);
515     coreObject->document()->frame()->editor()->performDelete();
516 }
517
518 static void webkit_accessible_editable_text_paste_text(AtkEditableText* text, gint position)
519 {
520     notImplemented();
521 }
522
523 static void atk_editable_text_interface_init(AtkEditableTextIface* iface)
524 {
525     g_return_if_fail(iface);
526
527     iface->set_run_attributes = webkit_accessible_editable_text_set_run_attributes;
528     iface->set_text_contents = webkit_accessible_editable_text_set_text_contents;
529     iface->insert_text = webkit_accessible_editable_text_insert_text;
530     iface->copy_text = webkit_accessible_editable_text_copy_text;
531     iface->cut_text = webkit_accessible_editable_text_cut_text;
532     iface->delete_text = webkit_accessible_editable_text_delete_text;
533     iface->paste_text = webkit_accessible_editable_text_paste_text;
534 }
535
536 // StreamableContent
537
538 static gint webkit_accessible_streamable_content_get_n_mime_types(AtkStreamableContent* streamable)
539 {
540     notImplemented();
541     return 0;
542 }
543
544 static G_CONST_RETURN gchar* webkit_accessible_streamable_content_get_mime_type(AtkStreamableContent* streamable, gint i)
545 {
546     notImplemented();
547     return "";
548 }
549
550 static GIOChannel* webkit_accessible_streamable_content_get_stream(AtkStreamableContent* streamable, const gchar* mime_type)
551 {
552     notImplemented();
553     return NULL;
554 }
555
556 static G_CONST_RETURN gchar* webkit_accessible_streamable_content_get_uri(AtkStreamableContent* streamable, const gchar* mime_type)
557 {
558     notImplemented();
559     return NULL;
560 }
561
562 static void atk_streamable_content_interface_init(AtkStreamableContentIface* iface)
563 {
564     g_return_if_fail(iface);
565
566     iface->get_n_mime_types = webkit_accessible_streamable_content_get_n_mime_types;
567     iface->get_mime_type = webkit_accessible_streamable_content_get_mime_type;
568     iface->get_stream = webkit_accessible_streamable_content_get_stream;
569     iface->get_uri = webkit_accessible_streamable_content_get_uri;
570 }
571
572 GType webkit_accessible_get_type()
573 {
574     static GType type = 0;
575
576     if (!type) {
577         static const GTypeInfo tinfo = {
578             sizeof(WebKitAccessibleClass),
579             (GBaseInitFunc)NULL,
580             (GBaseFinalizeFunc)NULL,
581             (GClassInitFunc)webkit_accessible_class_init,
582             (GClassFinalizeFunc)NULL,
583             NULL, /* class data */
584             sizeof(WebKitAccessible), /* instance size */
585             0, /* nb preallocs */
586             (GInstanceInitFunc)NULL,
587             NULL /* value table */
588         };
589
590         type = g_type_register_static(ATK_TYPE_OBJECT, "WebKitAccessible", &tinfo, static_cast<GTypeFlags>(0));
591
592         // TODO: Only implement interfaces when necessary, not for all objects.
593         static const GInterfaceInfo atk_action_info =
594         {
595             (GInterfaceInitFunc) atk_action_interface_init,
596             (GInterfaceFinalizeFunc) NULL,
597             NULL
598         };
599         g_type_add_interface_static(type, ATK_TYPE_ACTION, &atk_action_info);
600
601         static const GInterfaceInfo atk_text_info =
602         {
603             (GInterfaceInitFunc) atk_text_interface_init,
604             (GInterfaceFinalizeFunc) NULL,
605             NULL
606         };
607         g_type_add_interface_static(type, ATK_TYPE_TEXT, &atk_text_info);
608
609         static const GInterfaceInfo atk_editable_text_info =
610         {
611             (GInterfaceInitFunc) atk_editable_text_interface_init,
612             (GInterfaceFinalizeFunc) NULL,
613             NULL
614         };
615         g_type_add_interface_static(type, ATK_TYPE_EDITABLE_TEXT, &atk_editable_text_info);
616
617         static const GInterfaceInfo atk_streamable_content_info =
618         {
619             (GInterfaceInitFunc) atk_streamable_content_interface_init,
620             (GInterfaceFinalizeFunc) NULL,
621             NULL
622         };
623         g_type_add_interface_static(type, ATK_TYPE_STREAMABLE_CONTENT, &atk_streamable_content_info);
624     }
625     return type;
626 }
627
628 WebKitAccessible* webkit_accessible_new(AccessibilityObject* coreObject)
629 {
630     GType type = WEBKIT_TYPE_ACCESSIBLE;
631     AtkObject* object = static_cast<AtkObject*>(g_object_new(type, NULL));
632     atk_object_initialize(object, coreObject);
633     return WEBKIT_ACCESSIBLE(object);
634 }
635
636 AccessibilityObject* webkit_accessible_get_accessibility_object(WebKitAccessible* accessible)
637 {
638     return accessible->m_object;
639 }
640
641 // FIXME: Remove this static initialization.
642 static AXObjectCache* fallbackCache = new AXObjectCache();
643
644 void webkit_accessible_detach(WebKitAccessible* accessible)
645 {
646     ASSERT(accessible->m_object);
647
648     // We replace the WebCore AccessibilityObject with a fallback object that
649     // provides default implementations to avoid repetitive null-checking after
650     // detachment.
651
652     // FIXME: Using fallbackCache->get(ListBoxOptionRole) is a hack.
653     accessible->m_object = fallbackCache->get(ListBoxOptionRole);
654 }
655
656 }
657
658 namespace WebCore {
659
660 // AccessibilityObject implementations
661
662 AccessibilityObjectWrapper* AccessibilityObject::wrapper() const
663 {
664     return m_wrapper;
665 }
666
667 void AccessibilityObject::setWrapper(AccessibilityObjectWrapper* wrapper)
668 {
669     if (m_wrapper)
670         g_object_unref(m_wrapper);
671
672     m_wrapper = wrapper;
673
674     if (m_wrapper)
675         g_object_ref(m_wrapper);
676 }
677
678 } // namespace WebCore
679
680 #endif // HAVE(ACCESSIBILITY)