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