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 "AccessibilityList.h"
38#include "AccessibilityListBox.h"
39#include "AccessibilityListBoxOption.h"
40#include "AccessibilityTable.h"
41#include "AccessibilityTableCell.h"
42#include "AccessibilityTableColumn.h"
43#include "AccessibilityTableRow.h"
44#include "CharacterNames.h"
45#include "Document.h"
46#include "DocumentType.h"
47#include "Editor.h"
48#include "Frame.h"
49#include "FrameView.h"
50#include "GOwnPtr.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 "RenderListItem.h"
59#include "RenderListMarker.h"
60#include "RenderText.h"
61#include "SelectElement.h"
62#include "Settings.h"
63#include "TextEncoding.h"
64#include "TextIterator.h"
65#include "WebKitAccessibleHyperlink.h"
66#include "htmlediting.h"
67#include "visible_units.h"
68
69#include <atk/atk.h>
70#include <glib.h>
71#include <glib/gprintf.h>
72#include <libgail-util/gail-util.h>
73#include <pango/pango.h>
74#include <wtf/text/AtomicString.h>
75#include <wtf/text/CString.h>
76
77using namespace WebCore;
78
79static AccessibilityObject* fallbackObject()
80{
81    // FIXME: An AXObjectCache with a Document is meaningless.
82    static AXObjectCache* fallbackCache = new AXObjectCache(0);
83    static AccessibilityObject* object = 0;
84    if (!object) {
85        // FIXME: using fallbackCache->getOrCreate(ListBoxOptionRole) is a hack
86        object = fallbackCache->getOrCreate(ListBoxOptionRole);
87        object->ref();
88    }
89
90    return object;
91}
92
93// Used to provide const char* returns.
94static const char* returnString(const String& str)
95{
96    static CString returnedString;
97    returnedString = str.utf8();
98    return returnedString.data();
99}
100
101static AccessibilityObject* core(WebKitAccessible* accessible)
102{
103    if (!accessible)
104        return 0;
105
106    return accessible->m_object;
107}
108
109static AccessibilityObject* core(AtkObject* object)
110{
111    if (!WEBKIT_IS_ACCESSIBLE(object))
112        return 0;
113
114    return core(WEBKIT_ACCESSIBLE(object));
115}
116
117static AccessibilityObject* core(AtkAction* action)
118{
119    return core(ATK_OBJECT(action));
120}
121
122static AccessibilityObject* core(AtkSelection* selection)
123{
124    return core(ATK_OBJECT(selection));
125}
126
127static AccessibilityObject* core(AtkText* text)
128{
129    return core(ATK_OBJECT(text));
130}
131
132static AccessibilityObject* core(AtkEditableText* text)
133{
134    return core(ATK_OBJECT(text));
135}
136
137static AccessibilityObject* core(AtkComponent* component)
138{
139    return core(ATK_OBJECT(component));
140}
141
142static AccessibilityObject* core(AtkImage* image)
143{
144    return core(ATK_OBJECT(image));
145}
146
147static AccessibilityObject* core(AtkTable* table)
148{
149    return core(ATK_OBJECT(table));
150}
151
152static AccessibilityObject* core(AtkHypertext* hypertext)
153{
154    return core(ATK_OBJECT(hypertext));
155}
156
157static AccessibilityObject* core(AtkDocument* document)
158{
159    return core(ATK_OBJECT(document));
160}
161
162static AccessibilityObject* core(AtkValue* value)
163{
164    return core(ATK_OBJECT(value));
165}
166
167static gchar* webkit_accessible_text_get_text(AtkText* text, gint startOffset, gint endOffset);
168
169static const gchar* webkit_accessible_get_name(AtkObject* object)
170{
171    AccessibilityObject* coreObject = core(object);
172    if (!coreObject->isAccessibilityRenderObject())
173        return returnString(coreObject->stringValue());
174
175    if (coreObject->isControl()) {
176        AccessibilityObject* label = coreObject->correspondingLabelForControlElement();
177        if (label) {
178            AtkObject* atkObject = label->wrapper();
179            if (ATK_IS_TEXT(atkObject))
180                return webkit_accessible_text_get_text(ATK_TEXT(atkObject), 0, -1);
181        }
182
183        // Try text under the node.
184        String textUnder = coreObject->textUnderElement();
185        if (textUnder.length())
186            return returnString(textUnder);
187    }
188
189    if (coreObject->isImage() || coreObject->isInputImage()) {
190        Node* node = coreObject->node();
191        if (node && node->isHTMLElement()) {
192            // Get the attribute rather than altText String so as not to fall back on title.
193            String alt = toHTMLElement(node)->getAttribute(HTMLNames::altAttr);
194            if (!alt.isEmpty())
195                return returnString(alt);
196        }
197    }
198
199    // Fallback for the webArea object: just return the document's title.
200    if (coreObject->isWebArea()) {
201        Document* document = coreObject->document();
202        if (document)
203            return returnString(document->title());
204    }
205
206    return returnString(coreObject->stringValue());
207}
208
209static const gchar* webkit_accessible_get_description(AtkObject* object)
210{
211    AccessibilityObject* coreObject = core(object);
212    Node* node = 0;
213    if (coreObject->isAccessibilityRenderObject())
214        node = coreObject->node();
215    if (!node || !node->isHTMLElement() || coreObject->ariaRoleAttribute() != UnknownRole)
216        return returnString(coreObject->accessibilityDescription());
217
218    // atk_table_get_summary returns an AtkObject. We have no summary object, so expose summary here.
219    if (coreObject->roleValue() == TableRole) {
220        String summary = static_cast<HTMLTableElement*>(node)->summary();
221        if (!summary.isEmpty())
222            return returnString(summary);
223    }
224
225    // The title attribute should be reliably available as the object's descripton.
226    // We do not want to fall back on other attributes in its absence. See bug 25524.
227    String title = toHTMLElement(node)->title();
228    if (!title.isEmpty())
229        return returnString(title);
230
231    return returnString(coreObject->accessibilityDescription());
232}
233
234static void setAtkRelationSetFromCoreObject(AccessibilityObject* coreObject, AtkRelationSet* relationSet)
235{
236    if (coreObject->isControl()) {
237        AccessibilityObject* label = coreObject->correspondingLabelForControlElement();
238        if (label)
239            atk_relation_set_add_relation_by_type(relationSet, ATK_RELATION_LABELLED_BY, label->wrapper());
240    } else {
241        AccessibilityObject* control = coreObject->correspondingControlForLabelElement();
242        if (control)
243            atk_relation_set_add_relation_by_type(relationSet, ATK_RELATION_LABEL_FOR, control->wrapper());
244    }
245}
246
247static gpointer webkit_accessible_parent_class = 0;
248
249static bool isRootObject(AccessibilityObject* coreObject)
250{
251    // The root accessible object in WebCore is always an object with
252    // the ScrolledArea role with one child with the WebArea role.
253    if (!coreObject || !coreObject->isScrollView())
254        return false;
255
256    AccessibilityObject* firstChild = coreObject->firstChild();
257    if (!firstChild || !firstChild->isWebArea())
258        return false;
259
260    return true;
261}
262
263static AtkObject* atkParentOfRootObject(AtkObject* object)
264{
265    AccessibilityObject* coreObject = core(object);
266    AccessibilityObject* coreParent = coreObject->parentObjectUnignored();
267
268    // The top level object claims to not have a parent. This makes it
269    // impossible for assistive technologies to ascend the accessible
270    // hierarchy all the way to the application. (Bug 30489)
271    if (!coreParent && isRootObject(coreObject)) {
272        Document* document = coreObject->document();
273        if (!document)
274            return 0;
275
276        HostWindow* hostWindow = document->view()->hostWindow();
277        if (hostWindow) {
278            PlatformPageClient scrollView = hostWindow->platformPageClient();
279            if (scrollView) {
280                GtkWidget* scrollViewParent = gtk_widget_get_parent(scrollView);
281                if (scrollViewParent)
282                    return gtk_widget_get_accessible(scrollViewParent);
283            }
284        }
285    }
286
287    if (!coreParent)
288        return 0;
289
290    return coreParent->wrapper();
291}
292
293static AtkObject* webkit_accessible_get_parent(AtkObject* object)
294{
295    AccessibilityObject* coreObject = core(object);
296    AccessibilityObject* coreParent = coreObject->parentObjectUnignored();
297    if (!coreParent && isRootObject(coreObject))
298        return atkParentOfRootObject(object);
299
300    if (!coreParent)
301        return 0;
302
303    return coreParent->wrapper();
304}
305
306static gint webkit_accessible_get_n_children(AtkObject* object)
307{
308    return core(object)->children().size();
309}
310
311static AtkObject* webkit_accessible_ref_child(AtkObject* object, gint index)
312{
313    AccessibilityObject* coreObject = core(object);
314    AccessibilityObject::AccessibilityChildrenVector children = coreObject->children();
315    if (index < 0 || static_cast<unsigned>(index) >= children.size())
316        return 0;
317
318    AccessibilityObject* coreChild = children.at(index).get();
319
320    if (!coreChild)
321        return 0;
322
323    AtkObject* child = coreChild->wrapper();
324    atk_object_set_parent(child, object);
325    g_object_ref(child);
326
327    return child;
328}
329
330static gint webkit_accessible_get_index_in_parent(AtkObject* object)
331{
332    AccessibilityObject* coreObject = core(object);
333    AccessibilityObject* parent = coreObject->parentObjectUnignored();
334
335    if (!parent && isRootObject(coreObject)) {
336        AtkObject* atkParent = atkParentOfRootObject(object);
337        if (!atkParent)
338            return -1;
339
340        unsigned count = atk_object_get_n_accessible_children(atkParent);
341        for (unsigned i = 0; i < count; ++i) {
342            AtkObject* child = atk_object_ref_accessible_child(atkParent, i);
343            bool childIsObject = child == object;
344            g_object_unref(child);
345            if (childIsObject)
346                return i;
347        }
348    }
349
350    AccessibilityObject::AccessibilityChildrenVector children = parent->children();
351    unsigned count = children.size();
352    for (unsigned i = 0; i < count; ++i) {
353        if (children[i] == coreObject)
354            return i;
355    }
356
357    return -1;
358}
359
360static AtkAttributeSet* addAttributeToSet(AtkAttributeSet* attributeSet, const char* name, const char* value)
361{
362    AtkAttribute* attribute = static_cast<AtkAttribute*>(g_malloc(sizeof(AtkAttribute)));
363    attribute->name = g_strdup(name);
364    attribute->value = g_strdup(value);
365    attributeSet = g_slist_prepend(attributeSet, attribute);
366
367    return attributeSet;
368}
369
370static AtkAttributeSet* webkit_accessible_get_attributes(AtkObject* object)
371{
372    AtkAttributeSet* attributeSet = 0;
373    attributeSet = addAttributeToSet(attributeSet, "toolkit", "WebKitGtk");
374
375    AccessibilityObject* coreObject = core(object);
376    if (!coreObject)
377        return attributeSet;
378
379    int headingLevel = coreObject->headingLevel();
380    if (headingLevel) {
381        String value = String::number(headingLevel);
382        attributeSet = addAttributeToSet(attributeSet, "level", value.utf8().data());
383    }
384
385    // Set the 'layout-guess' attribute to help Assistive
386    // Technologies know when an exposed table is not data table.
387    if (coreObject->isAccessibilityTable() && !coreObject->isDataTable())
388        attributeSet = addAttributeToSet(attributeSet, "layout-guess", "true");
389
390    return attributeSet;
391}
392
393static AtkRole atkRole(AccessibilityRole role)
394{
395    switch (role) {
396    case UnknownRole:
397        return ATK_ROLE_UNKNOWN;
398    case ButtonRole:
399        return ATK_ROLE_PUSH_BUTTON;
400    case RadioButtonRole:
401        return ATK_ROLE_RADIO_BUTTON;
402    case CheckBoxRole:
403        return ATK_ROLE_CHECK_BOX;
404    case SliderRole:
405        return ATK_ROLE_SLIDER;
406    case TabGroupRole:
407        return ATK_ROLE_PAGE_TAB_LIST;
408    case TextFieldRole:
409    case TextAreaRole:
410        return ATK_ROLE_ENTRY;
411    case StaticTextRole:
412        return ATK_ROLE_TEXT;
413    case OutlineRole:
414        return ATK_ROLE_TREE;
415    case MenuBarRole:
416        return ATK_ROLE_MENU_BAR;
417    case MenuListPopupRole:
418    case MenuRole:
419        return ATK_ROLE_MENU;
420    case MenuListOptionRole:
421    case MenuItemRole:
422        return ATK_ROLE_MENU_ITEM;
423    case ColumnRole:
424        //return ATK_ROLE_TABLE_COLUMN_HEADER; // Is this right?
425        return ATK_ROLE_UNKNOWN; // Matches Mozilla
426    case RowRole:
427        //return ATK_ROLE_TABLE_ROW_HEADER; // Is this right?
428        return ATK_ROLE_LIST_ITEM; // Matches Mozilla
429    case ToolbarRole:
430        return ATK_ROLE_TOOL_BAR;
431    case BusyIndicatorRole:
432        return ATK_ROLE_PROGRESS_BAR; // Is this right?
433    case ProgressIndicatorRole:
434        //return ATK_ROLE_SPIN_BUTTON; // Some confusion about this role in AccessibilityRenderObject.cpp
435        return ATK_ROLE_PROGRESS_BAR;
436    case WindowRole:
437        return ATK_ROLE_WINDOW;
438    case PopUpButtonRole:
439    case ComboBoxRole:
440        return ATK_ROLE_COMBO_BOX;
441    case SplitGroupRole:
442        return ATK_ROLE_SPLIT_PANE;
443    case SplitterRole:
444        return ATK_ROLE_SEPARATOR;
445    case ColorWellRole:
446        return ATK_ROLE_COLOR_CHOOSER;
447    case ListRole:
448        return ATK_ROLE_LIST;
449    case ScrollBarRole:
450        return ATK_ROLE_SCROLL_BAR;
451    case ScrollAreaRole:
452        return ATK_ROLE_SCROLL_PANE;
453    case GridRole: // Is this right?
454    case TableRole:
455        return ATK_ROLE_TABLE;
456    case ApplicationRole:
457        return ATK_ROLE_APPLICATION;
458    case GroupRole:
459    case RadioGroupRole:
460        return ATK_ROLE_PANEL;
461    case RowHeaderRole: // Row headers are cells after all.
462    case ColumnHeaderRole: // Column headers are cells after all.
463    case CellRole:
464        return ATK_ROLE_TABLE_CELL;
465    case LinkRole:
466    case WebCoreLinkRole:
467    case ImageMapLinkRole:
468        return ATK_ROLE_LINK;
469    case ImageMapRole:
470    case ImageRole:
471        return ATK_ROLE_IMAGE;
472    case ListMarkerRole:
473        return ATK_ROLE_TEXT;
474    case WebAreaRole:
475        //return ATK_ROLE_HTML_CONTAINER; // Is this right?
476        return ATK_ROLE_DOCUMENT_FRAME;
477    case HeadingRole:
478        return ATK_ROLE_HEADING;
479    case ListBoxRole:
480        return ATK_ROLE_LIST;
481    case ListItemRole:
482    case ListBoxOptionRole:
483        return ATK_ROLE_LIST_ITEM;
484    case ParagraphRole:
485        return ATK_ROLE_PARAGRAPH;
486    case LabelRole:
487        return ATK_ROLE_LABEL;
488    case DivRole:
489        return ATK_ROLE_SECTION;
490    case FormRole:
491        return ATK_ROLE_FORM;
492    default:
493        return ATK_ROLE_UNKNOWN;
494    }
495}
496
497static AtkRole webkit_accessible_get_role(AtkObject* object)
498{
499    AccessibilityObject* coreObject = core(object);
500
501    if (!coreObject)
502        return ATK_ROLE_UNKNOWN;
503
504    // Note: Why doesn't WebCore have a password field for this
505    if (coreObject->isPasswordField())
506        return ATK_ROLE_PASSWORD_TEXT;
507
508    return atkRole(coreObject->roleValue());
509}
510
511static bool selectionBelongsToObject(AccessibilityObject* coreObject, VisibleSelection& selection)
512{
513    if (!coreObject || !coreObject->isAccessibilityRenderObject())
514        return false;
515
516    if (selection.isNone())
517        return false;
518
519    RefPtr<Range> range = selection.toNormalizedRange();
520    if (!range)
521        return false;
522
523    // We want to check that both the selection intersects the node
524    // AND that the selection is not just "touching" one of the
525    // boundaries for the selected node. We want to check whether the
526    // node is actually inside the region, at least partially.
527    Node* node = coreObject->node();
528    Node* lastDescendant = node->lastDescendant();
529    ExceptionCode ec = 0;
530    return (range->intersectsNode(node, ec)
531            && (range->endContainer() != node || range->endOffset())
532            && (range->startContainer() != lastDescendant || range->startOffset() != lastOffsetInNode(lastDescendant)));
533}
534
535static bool isTextWithCaret(AccessibilityObject* coreObject)
536{
537    if (!coreObject || !coreObject->isAccessibilityRenderObject())
538        return false;
539
540    Document* document = coreObject->document();
541    if (!document)
542        return false;
543
544    Frame* frame = document->frame();
545    if (!frame)
546        return false;
547
548    Settings* settings = frame->settings();
549    if (!settings || !settings->caretBrowsingEnabled())
550        return false;
551
552    // Check text objects and paragraphs only.
553    AtkObject* axObject = coreObject->wrapper();
554    AtkRole role = axObject ? atk_object_get_role(axObject) : ATK_ROLE_INVALID;
555    if (role != ATK_ROLE_TEXT && role != ATK_ROLE_PARAGRAPH)
556        return false;
557
558    // Finally, check whether the caret is set in the current object.
559    VisibleSelection selection = coreObject->selection();
560    if (!selection.isCaret())
561        return false;
562
563    return selectionBelongsToObject(coreObject, selection);
564}
565
566static void setAtkStateSetFromCoreObject(AccessibilityObject* coreObject, AtkStateSet* stateSet)
567{
568    AccessibilityObject* parent = coreObject->parentObject();
569    bool isListBoxOption = parent && parent->isListBox();
570
571    // Please keep the state list in alphabetical order
572    if (coreObject->isChecked())
573        atk_state_set_add_state(stateSet, ATK_STATE_CHECKED);
574
575    // FIXME: isReadOnly does not seem to do the right thing for
576    // controls, so check explicitly for them. In addition, because
577    // isReadOnly is false for listBoxOptions, we need to add one
578    // more check so that we do not present them as being "editable".
579    if ((!coreObject->isReadOnly() ||
580        (coreObject->isControl() && coreObject->canSetValueAttribute())) &&
581        !isListBoxOption)
582        atk_state_set_add_state(stateSet, ATK_STATE_EDITABLE);
583
584    // FIXME: Put both ENABLED and SENSITIVE together here for now
585    if (coreObject->isEnabled()) {
586        atk_state_set_add_state(stateSet, ATK_STATE_ENABLED);
587        atk_state_set_add_state(stateSet, ATK_STATE_SENSITIVE);
588    }
589
590    if (coreObject->canSetExpandedAttribute())
591        atk_state_set_add_state(stateSet, ATK_STATE_EXPANDABLE);
592
593    if (coreObject->isExpanded())
594        atk_state_set_add_state(stateSet, ATK_STATE_EXPANDED);
595
596    if (coreObject->canSetFocusAttribute())
597        atk_state_set_add_state(stateSet, ATK_STATE_FOCUSABLE);
598
599    if (coreObject->isFocused() || isTextWithCaret(coreObject))
600        atk_state_set_add_state(stateSet, ATK_STATE_FOCUSED);
601
602    // TODO: ATK_STATE_HORIZONTAL
603
604    if (coreObject->isIndeterminate())
605        atk_state_set_add_state(stateSet, ATK_STATE_INDETERMINATE);
606
607    if (coreObject->isMultiSelectable())
608        atk_state_set_add_state(stateSet, ATK_STATE_MULTISELECTABLE);
609
610    // TODO: ATK_STATE_OPAQUE
611
612    if (coreObject->isPressed())
613        atk_state_set_add_state(stateSet, ATK_STATE_PRESSED);
614
615    // TODO: ATK_STATE_SELECTABLE_TEXT
616
617    if (coreObject->canSetSelectedAttribute()) {
618        atk_state_set_add_state(stateSet, ATK_STATE_SELECTABLE);
619        // Items in focusable lists in Gtk have both STATE_SELECT{ABLE,ED}
620        // and STATE_FOCUS{ABLE,ED}. We'll fake the latter based on the
621        // former.
622        if (isListBoxOption)
623            atk_state_set_add_state(stateSet, ATK_STATE_FOCUSABLE);
624    }
625
626    if (coreObject->isSelected()) {
627        atk_state_set_add_state(stateSet, ATK_STATE_SELECTED);
628        // Items in focusable lists in Gtk have both STATE_SELECT{ABLE,ED}
629        // and STATE_FOCUS{ABLE,ED}. We'll fake the latter based on the
630        // former.
631        if (isListBoxOption)
632            atk_state_set_add_state(stateSet, ATK_STATE_FOCUSED);
633    }
634
635    // FIXME: Group both SHOWING and VISIBLE here for now
636    // Not sure how to handle this in WebKit, see bug
637    // http://bugzilla.gnome.org/show_bug.cgi?id=509650 for other
638    // issues with SHOWING vs VISIBLE within GTK+
639    if (!coreObject->isOffScreen()) {
640        atk_state_set_add_state(stateSet, ATK_STATE_SHOWING);
641        atk_state_set_add_state(stateSet, ATK_STATE_VISIBLE);
642    }
643
644    // Mutually exclusive, so we group these two
645    if (coreObject->roleValue() == TextFieldRole)
646        atk_state_set_add_state(stateSet, ATK_STATE_SINGLE_LINE);
647    else if (coreObject->roleValue() == TextAreaRole)
648        atk_state_set_add_state(stateSet, ATK_STATE_MULTI_LINE);
649
650    // TODO: ATK_STATE_SENSITIVE
651
652    // TODO: ATK_STATE_VERTICAL
653
654    if (coreObject->isVisited())
655        atk_state_set_add_state(stateSet, ATK_STATE_VISITED);
656}
657
658static AtkStateSet* webkit_accessible_ref_state_set(AtkObject* object)
659{
660    AtkStateSet* stateSet = ATK_OBJECT_CLASS(webkit_accessible_parent_class)->ref_state_set(object);
661    AccessibilityObject* coreObject = core(object);
662
663    if (coreObject == fallbackObject()) {
664        atk_state_set_add_state(stateSet, ATK_STATE_DEFUNCT);
665        return stateSet;
666    }
667
668    // Text objects must be focusable.
669    AtkRole role = atk_object_get_role(object);
670    if (role == ATK_ROLE_TEXT || role == ATK_ROLE_PARAGRAPH)
671        atk_state_set_add_state(stateSet, ATK_STATE_FOCUSABLE);
672
673    setAtkStateSetFromCoreObject(coreObject, stateSet);
674    return stateSet;
675}
676
677static AtkRelationSet* webkit_accessible_ref_relation_set(AtkObject* object)
678{
679    AtkRelationSet* relationSet = ATK_OBJECT_CLASS(webkit_accessible_parent_class)->ref_relation_set(object);
680    AccessibilityObject* coreObject = core(object);
681
682    setAtkRelationSetFromCoreObject(coreObject, relationSet);
683
684    return relationSet;
685}
686
687static void webkit_accessible_init(AtkObject* object, gpointer data)
688{
689    if (ATK_OBJECT_CLASS(webkit_accessible_parent_class)->initialize)
690        ATK_OBJECT_CLASS(webkit_accessible_parent_class)->initialize(object, data);
691
692    WEBKIT_ACCESSIBLE(object)->m_object = reinterpret_cast<AccessibilityObject*>(data);
693}
694
695static void webkit_accessible_finalize(GObject* object)
696{
697    // This is a good time to clear the return buffer.
698    returnString(String());
699
700    G_OBJECT_CLASS(webkit_accessible_parent_class)->finalize(object);
701}
702
703static void webkit_accessible_class_init(AtkObjectClass* klass)
704{
705    GObjectClass* gobjectClass = G_OBJECT_CLASS(klass);
706
707    webkit_accessible_parent_class = g_type_class_peek_parent(klass);
708
709    gobjectClass->finalize = webkit_accessible_finalize;
710
711    klass->initialize = webkit_accessible_init;
712    klass->get_name = webkit_accessible_get_name;
713    klass->get_description = webkit_accessible_get_description;
714    klass->get_parent = webkit_accessible_get_parent;
715    klass->get_n_children = webkit_accessible_get_n_children;
716    klass->ref_child = webkit_accessible_ref_child;
717    klass->get_role = webkit_accessible_get_role;
718    klass->ref_state_set = webkit_accessible_ref_state_set;
719    klass->get_index_in_parent = webkit_accessible_get_index_in_parent;
720    klass->get_attributes = webkit_accessible_get_attributes;
721    klass->ref_relation_set = webkit_accessible_ref_relation_set;
722}
723
724GType
725webkit_accessible_get_type(void)
726{
727    static volatile gsize type_volatile = 0;
728
729    if (g_once_init_enter(&type_volatile)) {
730        static const GTypeInfo tinfo = {
731            sizeof(WebKitAccessibleClass),
732            (GBaseInitFunc) 0,
733            (GBaseFinalizeFunc) 0,
734            (GClassInitFunc) webkit_accessible_class_init,
735            (GClassFinalizeFunc) 0,
736            0, /* class data */
737            sizeof(WebKitAccessible), /* instance size */
738            0, /* nb preallocs */
739            (GInstanceInitFunc) 0,
740            0 /* value table */
741        };
742
743        GType type = g_type_register_static(ATK_TYPE_OBJECT,
744                                            "WebKitAccessible", &tinfo, GTypeFlags(0));
745        g_once_init_leave(&type_volatile, type);
746    }
747
748    return type_volatile;
749}
750
751static gboolean webkit_accessible_action_do_action(AtkAction* action, gint i)
752{
753    g_return_val_if_fail(i == 0, FALSE);
754    return core(action)->performDefaultAction();
755}
756
757static gint webkit_accessible_action_get_n_actions(AtkAction* action)
758{
759    return 1;
760}
761
762static const gchar* webkit_accessible_action_get_description(AtkAction* action, gint i)
763{
764    g_return_val_if_fail(i == 0, 0);
765    // TODO: Need a way to provide/localize action descriptions.
766    notImplemented();
767    return "";
768}
769
770static const gchar* webkit_accessible_action_get_keybinding(AtkAction* action, gint i)
771{
772    g_return_val_if_fail(i == 0, 0);
773    // FIXME: Construct a proper keybinding string.
774    return returnString(core(action)->accessKey().string());
775}
776
777static const gchar* webkit_accessible_action_get_name(AtkAction* action, gint i)
778{
779    g_return_val_if_fail(i == 0, 0);
780    return returnString(core(action)->actionVerb());
781}
782
783static void atk_action_interface_init(AtkActionIface* iface)
784{
785    iface->do_action = webkit_accessible_action_do_action;
786    iface->get_n_actions = webkit_accessible_action_get_n_actions;
787    iface->get_description = webkit_accessible_action_get_description;
788    iface->get_keybinding = webkit_accessible_action_get_keybinding;
789    iface->get_name = webkit_accessible_action_get_name;
790}
791
792// Selection (for controls)
793
794static AccessibilityObject* listObjectForSelection(AtkSelection* selection)
795{
796    AccessibilityObject* coreSelection = core(selection);
797
798    // Only list boxes and menu lists supported so far.
799    if (!coreSelection->isListBox() && !coreSelection->isMenuList())
800        return 0;
801
802    // For list boxes the list object is just itself.
803    if (coreSelection->isListBox())
804        return coreSelection;
805
806    // For menu lists we need to return the first accessible child,
807    // with role MenuListPopupRole, since that's the one holding the list
808    // of items with role MenuListOptionRole.
809    AccessibilityObject::AccessibilityChildrenVector children = coreSelection->children();
810    if (!children.size())
811        return 0;
812
813    AccessibilityObject* listObject = children.at(0).get();
814    if (!listObject->isMenuListPopup())
815        return 0;
816
817    return listObject;
818}
819
820static AccessibilityObject* optionFromList(AtkSelection* selection, gint i)
821{
822    AccessibilityObject* coreSelection = core(selection);
823    if (!coreSelection || i < 0)
824        return 0;
825
826    // Need to select the proper list object depending on the type.
827    AccessibilityObject* listObject = listObjectForSelection(selection);
828    if (!listObject)
829        return 0;
830
831    AccessibilityObject::AccessibilityChildrenVector options = listObject->children();
832    if (i < static_cast<gint>(options.size()))
833        return options.at(i).get();
834
835    return 0;
836}
837
838static AccessibilityObject* optionFromSelection(AtkSelection* selection, gint i)
839{
840    // i is the ith selection as opposed to the ith child.
841
842    AccessibilityObject* coreSelection = core(selection);
843    if (!coreSelection || !coreSelection->isAccessibilityRenderObject() || i < 0)
844        return 0;
845
846    AccessibilityObject::AccessibilityChildrenVector selectedItems;
847    if (coreSelection->isListBox())
848        coreSelection->selectedChildren(selectedItems);
849    else if (coreSelection->isMenuList()) {
850        RenderObject* renderer = coreSelection->renderer();
851        if (!renderer)
852            return 0;
853
854        SelectElement* selectNode = toSelectElement(static_cast<Element*>(renderer->node()));
855        int selectedIndex = selectNode->selectedIndex();
856        const Vector<Element*> listItems = selectNode->listItems();
857
858        if (selectedIndex < 0 || selectedIndex >= static_cast<int>(listItems.size()))
859            return 0;
860
861        return optionFromList(selection, selectedIndex);
862    }
863
864    if (i < static_cast<gint>(selectedItems.size()))
865        return selectedItems.at(i).get();
866
867    return 0;
868}
869
870static gboolean webkit_accessible_selection_add_selection(AtkSelection* selection, gint i)
871{
872    AccessibilityObject* coreSelection = core(selection);
873    if (!coreSelection)
874        return false;
875
876    AccessibilityObject* option = optionFromList(selection, i);
877    if (option && (coreSelection->isListBox() || coreSelection->isMenuList())) {
878        option->setSelected(true);
879        return option->isSelected();
880    }
881
882    return false;
883}
884
885static gboolean webkit_accessible_selection_clear_selection(AtkSelection* selection)
886{
887    AccessibilityObject* coreSelection = core(selection);
888    if (!coreSelection)
889        return false;
890
891    AccessibilityObject::AccessibilityChildrenVector selectedItems;
892    if (coreSelection->isListBox() || coreSelection->isMenuList()) {
893        // Set the list of selected items to an empty list; then verify that it worked.
894        AccessibilityListBox* listBox = static_cast<AccessibilityListBox*>(coreSelection);
895        listBox->setSelectedChildren(selectedItems);
896        listBox->selectedChildren(selectedItems);
897        return selectedItems.size() == 0;
898    }
899    return false;
900}
901
902static AtkObject* webkit_accessible_selection_ref_selection(AtkSelection* selection, gint i)
903{
904    AccessibilityObject* option = optionFromSelection(selection, i);
905    if (option) {
906        AtkObject* child = option->wrapper();
907        g_object_ref(child);
908        return child;
909    }
910
911    return 0;
912}
913
914static gint webkit_accessible_selection_get_selection_count(AtkSelection* selection)
915{
916    AccessibilityObject* coreSelection = core(selection);
917    if (!coreSelection || !coreSelection->isAccessibilityRenderObject())
918        return 0;
919
920    if (coreSelection->isListBox()) {
921        AccessibilityObject::AccessibilityChildrenVector selectedItems;
922        coreSelection->selectedChildren(selectedItems);
923        return static_cast<gint>(selectedItems.size());
924    }
925
926    if (coreSelection->isMenuList()) {
927        RenderObject* renderer = coreSelection->renderer();
928        if (!renderer)
929            return 0;
930
931        SelectElement* selectNode = toSelectElement(static_cast<Element*>(renderer->node()));
932        int selectedIndex = selectNode->selectedIndex();
933        const Vector<Element*> listItems = selectNode->listItems();
934
935        return selectedIndex >= 0 && selectedIndex < static_cast<int>(listItems.size());
936    }
937
938    return 0;
939}
940
941static gboolean webkit_accessible_selection_is_child_selected(AtkSelection* selection, gint i)
942{
943    AccessibilityObject* coreSelection = core(selection);
944    if (!coreSelection)
945        return 0;
946
947    AccessibilityObject* option = optionFromList(selection, i);
948    if (option && (coreSelection->isListBox() || coreSelection->isMenuList()))
949        return option->isSelected();
950
951    return false;
952}
953
954static gboolean webkit_accessible_selection_remove_selection(AtkSelection* selection, gint i)
955{
956    AccessibilityObject* coreSelection = core(selection);
957    if (!coreSelection)
958        return 0;
959
960    // TODO: This is only getting called if i == 0. What is preventing the rest?
961    AccessibilityObject* option = optionFromSelection(selection, i);
962    if (option && (coreSelection->isListBox() || coreSelection->isMenuList())) {
963        option->setSelected(false);
964        return !option->isSelected();
965    }
966
967    return false;
968}
969
970static gboolean webkit_accessible_selection_select_all_selection(AtkSelection* selection)
971{
972    AccessibilityObject* coreSelection = core(selection);
973    if (!coreSelection || !coreSelection->isMultiSelectable())
974        return false;
975
976    AccessibilityObject::AccessibilityChildrenVector children = coreSelection->children();
977    if (coreSelection->isListBox()) {
978        AccessibilityListBox* listBox = static_cast<AccessibilityListBox*>(coreSelection);
979        listBox->setSelectedChildren(children);
980        AccessibilityObject::AccessibilityChildrenVector selectedItems;
981        listBox->selectedChildren(selectedItems);
982        return selectedItems.size() == children.size();
983    }
984
985    return false;
986}
987
988static void atk_selection_interface_init(AtkSelectionIface* iface)
989{
990    iface->add_selection = webkit_accessible_selection_add_selection;
991    iface->clear_selection = webkit_accessible_selection_clear_selection;
992    iface->ref_selection = webkit_accessible_selection_ref_selection;
993    iface->get_selection_count = webkit_accessible_selection_get_selection_count;
994    iface->is_child_selected = webkit_accessible_selection_is_child_selected;
995    iface->remove_selection = webkit_accessible_selection_remove_selection;
996    iface->select_all_selection = webkit_accessible_selection_select_all_selection;
997}
998
999// Text
1000
1001static gchar* utf8Substr(const gchar* string, gint start, gint end)
1002{
1003    ASSERT(string);
1004    glong strLen = g_utf8_strlen(string, -1);
1005    if (start > strLen || end > strLen)
1006        return 0;
1007    gchar* startPtr = g_utf8_offset_to_pointer(string, start);
1008    gsize lenInBytes = g_utf8_offset_to_pointer(string, end + 1) -  startPtr;
1009    gchar* output = static_cast<gchar*>(g_malloc0(lenInBytes + 1));
1010    return g_utf8_strncpy(output, startPtr, end - start + 1);
1011}
1012
1013// This function is not completely general, is it's tied to the
1014// internals of WebCore's text presentation.
1015static gchar* convertUniCharToUTF8(const UChar* characters, gint length, int from, int to)
1016{
1017    CString stringUTF8 = UTF8Encoding().encode(characters, length, QuestionMarksForUnencodables);
1018    gchar* utf8String = utf8Substr(stringUTF8.data(), from, to);
1019    if (!g_utf8_validate(utf8String, -1, 0)) {
1020        g_free(utf8String);
1021        return 0;
1022    }
1023    gsize len = strlen(utf8String);
1024    GString* ret = g_string_new_len(0, len);
1025    gchar* ptr = utf8String;
1026
1027    // WebCore introduces line breaks in the text that do not reflect
1028    // the layout you see on the screen, replace them with spaces
1029    while (len > 0) {
1030        gint index, start;
1031        pango_find_paragraph_boundary(ptr, len, &index, &start);
1032        g_string_append_len(ret, ptr, index);
1033        if (index == start)
1034            break;
1035        g_string_append_c(ret, ' ');
1036        ptr += start;
1037        len -= start;
1038    }
1039
1040    g_free(utf8String);
1041    return g_string_free(ret, FALSE);
1042}
1043
1044gchar* textForRenderer(RenderObject* renderer)
1045{
1046    GString* resultText = g_string_new(0);
1047
1048    if (!renderer)
1049        return g_string_free(resultText, FALSE);
1050
1051    // For RenderBlocks, piece together the text from the RenderText objects they contain.
1052    for (RenderObject* object = renderer->firstChild(); object; object = object->nextSibling()) {
1053        if (object->isBR()) {
1054            g_string_append(resultText, "\n");
1055            continue;
1056        }
1057
1058        RenderText* renderText;
1059        if (object->isText())
1060            renderText = toRenderText(object);
1061        else {
1062            if (object->isReplaced())
1063                g_string_append_unichar(resultText, objectReplacementCharacter);
1064
1065            // We need to check children, if any, to consider when
1066            // current object is not a text object but some of its
1067            // children are, in order not to miss those portions of
1068            // text by not properly handling those situations
1069            if (object->firstChild())
1070                g_string_append(resultText, textForRenderer(object));
1071
1072            continue;
1073        }
1074
1075        InlineTextBox* box = renderText ? renderText->firstTextBox() : 0;
1076        while (box) {
1077            gchar* text = convertUniCharToUTF8(renderText->characters(), renderText->textLength(), box->start(), box->end());
1078            g_string_append(resultText, text);
1079            // Newline chars in the source result in separate text boxes, so check
1080            // before adding a newline in the layout. See bug 25415 comment #78.
1081            // If the next sibling is a BR, we'll add the newline when we examine that child.
1082            if (!box->nextOnLineExists() && (!object->nextSibling() || !object->nextSibling()->isBR()))
1083                g_string_append(resultText, "\n");
1084            box = box->nextTextBox();
1085        }
1086    }
1087
1088    // Insert the text of the marker for list item in the right place, if present
1089    if (renderer->isListItem()) {
1090        String markerText = toRenderListItem(renderer)->markerTextWithSuffix();
1091        if (renderer->style()->direction() == LTR)
1092            g_string_prepend(resultText, markerText.utf8().data());
1093        else
1094            g_string_append(resultText, markerText.utf8().data());
1095    }
1096
1097    return g_string_free(resultText, FALSE);
1098}
1099
1100gchar* textForObject(AccessibilityObject* coreObject)
1101{
1102    GString* str = g_string_new(0);
1103
1104    // For text controls, we can get the text line by line.
1105    if (coreObject->isTextControl()) {
1106        unsigned textLength = coreObject->textLength();
1107        int lineNumber = 0;
1108        PlainTextRange range = coreObject->doAXRangeForLine(lineNumber);
1109        while (range.length) {
1110            // When a line of text wraps in a text area, the final space is removed.
1111            if (range.start + range.length < textLength)
1112                range.length -= 1;
1113            String lineText = coreObject->doAXStringForRange(range);
1114            g_string_append(str, lineText.utf8().data());
1115            g_string_append(str, "\n");
1116            range = coreObject->doAXRangeForLine(++lineNumber);
1117        }
1118    } else if (coreObject->isAccessibilityRenderObject()) {
1119        GOwnPtr<gchar> rendererText(textForRenderer(coreObject->renderer()));
1120        g_string_append(str, rendererText.get());
1121    }
1122
1123    return g_string_free(str, FALSE);
1124}
1125
1126static gchar* webkit_accessible_text_get_text(AtkText* text, gint startOffset, gint endOffset)
1127{
1128    AccessibilityObject* coreObject = core(text);
1129
1130    int end = endOffset;
1131    if (endOffset == -1) {
1132        end = coreObject->stringValue().length();
1133        if (!end)
1134            end = coreObject->textUnderElement().length();
1135    }
1136
1137    String ret;
1138    if (coreObject->isTextControl())
1139        ret = coreObject->doAXStringForRange(PlainTextRange(0, endOffset));
1140    else {
1141        ret = coreObject->stringValue();
1142        if (!ret)
1143            ret = coreObject->textUnderElement();
1144    }
1145
1146    if (!ret.length()) {
1147        // This can happen at least with anonymous RenderBlocks (e.g. body text amongst paragraphs)
1148        ret = String(textForObject(coreObject));
1149        if (!end)
1150            end = ret.length();
1151    }
1152
1153    // Prefix a item number/bullet if needed
1154    if (coreObject->roleValue() == ListItemRole) {
1155        RenderObject* objRenderer = coreObject->renderer();
1156        if (objRenderer && objRenderer->isListItem()) {
1157            String markerText = toRenderListItem(objRenderer)->markerTextWithSuffix();
1158            ret = objRenderer->style()->direction() == LTR ? markerText + ret : ret + markerText;
1159            if (endOffset == -1)
1160                end += markerText.length();
1161        }
1162    }
1163
1164    ret = ret.substring(startOffset, end - startOffset);
1165    return g_strdup(ret.utf8().data());
1166}
1167
1168static GailTextUtil* getGailTextUtilForAtk(AtkText* textObject)
1169{
1170    gpointer data = g_object_get_data(G_OBJECT(textObject), "webkit-accessible-gail-text-util");
1171    if (data)
1172        return static_cast<GailTextUtil*>(data);
1173
1174    GailTextUtil* gailTextUtil = gail_text_util_new();
1175    gail_text_util_text_setup(gailTextUtil, webkit_accessible_text_get_text(textObject, 0, -1));
1176    g_object_set_data_full(G_OBJECT(textObject), "webkit-accessible-gail-text-util", gailTextUtil, g_object_unref);
1177    return gailTextUtil;
1178}
1179
1180static PangoLayout* getPangoLayoutForAtk(AtkText* textObject)
1181{
1182    AccessibilityObject* coreObject = core(textObject);
1183
1184    Document* document = coreObject->document();
1185    if (!document)
1186        return 0;
1187
1188    HostWindow* hostWindow = document->view()->hostWindow();
1189    if (!hostWindow)
1190        return 0;
1191    PlatformPageClient webView = hostWindow->platformPageClient();
1192    if (!webView)
1193        return 0;
1194
1195    // Create a string with the layout as it appears on the screen
1196    PangoLayout* layout = gtk_widget_create_pango_layout(static_cast<GtkWidget*>(webView), textForObject(coreObject));
1197    g_object_set_data_full(G_OBJECT(textObject), "webkit-accessible-pango-layout", layout, g_object_unref);
1198    return layout;
1199}
1200
1201static gchar* webkit_accessible_text_get_text_after_offset(AtkText* text, gint offset, AtkTextBoundary boundaryType, gint* startOffset, gint* endOffset)
1202{
1203    return gail_text_util_get_text(getGailTextUtilForAtk(text), getPangoLayoutForAtk(text), GAIL_AFTER_OFFSET, boundaryType, offset, startOffset, endOffset);
1204}
1205
1206static gchar* webkit_accessible_text_get_text_at_offset(AtkText* text, gint offset, AtkTextBoundary boundaryType, gint* startOffset, gint* endOffset)
1207{
1208    return gail_text_util_get_text(getGailTextUtilForAtk(text), getPangoLayoutForAtk(text), GAIL_AT_OFFSET, boundaryType, offset, startOffset, endOffset);
1209}
1210
1211static gchar* webkit_accessible_text_get_text_before_offset(AtkText* text, gint offset, AtkTextBoundary boundaryType, gint* startOffset, gint* endOffset)
1212{
1213    return gail_text_util_get_text(getGailTextUtilForAtk(text), getPangoLayoutForAtk(text), GAIL_BEFORE_OFFSET, boundaryType, offset, startOffset, endOffset);
1214}
1215
1216static gunichar webkit_accessible_text_get_character_at_offset(AtkText* text, gint offset)
1217{
1218    notImplemented();
1219    return 0;
1220}
1221
1222static gint webkit_accessible_text_get_caret_offset(AtkText* text)
1223{
1224    // coreObject is the unignored object whose offset the caller is requesting.
1225    // focusedObject is the object with the caret. It is likely ignored -- unless it's a link.
1226    AccessibilityObject* coreObject = core(text);
1227    if (!coreObject->isAccessibilityRenderObject())
1228        return 0;
1229
1230    Document* document = coreObject->document();
1231    if (!document)
1232        return 0;
1233
1234    Node* focusedNode = coreObject->selection().end().deprecatedNode();
1235    if (!focusedNode)
1236        return 0;
1237
1238    RenderObject* focusedRenderer = focusedNode->renderer();
1239    AccessibilityObject* focusedObject = document->axObjectCache()->getOrCreate(focusedRenderer);
1240
1241    int offset;
1242    // Don't ignore links if the offset is being requested for a link.
1243    if (!objectAndOffsetUnignored(focusedObject, offset, !coreObject->isLink()))
1244        return 0;
1245
1246    RenderObject* renderer = coreObject->renderer();
1247    if (renderer && renderer->isListItem()) {
1248        String markerText = toRenderListItem(renderer)->markerTextWithSuffix();
1249
1250        // We need to adjust the offset for the list item marker.
1251        offset += markerText.length();
1252    }
1253
1254    // TODO: Verify this for RTL text.
1255    return offset;
1256}
1257
1258static int baselinePositionForRenderObject(RenderObject* renderObject)
1259{
1260    // FIXME: This implementation of baselinePosition originates from RenderObject.cpp and was
1261    // removed in r70072. The implementation looks incorrect though, because this is not the
1262    // baseline of the underlying RenderObject, but of the AccessibilityRenderObject.
1263    const FontMetrics& fontMetrics = renderObject->firstLineStyle()->fontMetrics();
1264    return fontMetrics.ascent() + (renderObject->firstLineStyle()->computedLineHeight() - fontMetrics.height()) / 2;
1265}
1266
1267static AtkAttributeSet* getAttributeSetForAccessibilityObject(const AccessibilityObject* object)
1268{
1269    if (!object->isAccessibilityRenderObject())
1270        return 0;
1271
1272    RenderObject* renderer = object->renderer();
1273    RenderStyle* style = renderer->style();
1274
1275    AtkAttributeSet* result = 0;
1276    GOwnPtr<gchar> buffer(g_strdup_printf("%i", style->fontSize()));
1277    result = addAttributeToSet(result, atk_text_attribute_get_name(ATK_TEXT_ATTR_SIZE), buffer.get());
1278
1279    Color bgColor = style->visitedDependentColor(CSSPropertyBackgroundColor);
1280    if (bgColor.isValid()) {
1281        buffer.set(g_strdup_printf("%i,%i,%i",
1282                                   bgColor.red(), bgColor.green(), bgColor.blue()));
1283        result = addAttributeToSet(result, atk_text_attribute_get_name(ATK_TEXT_ATTR_BG_COLOR), buffer.get());
1284    }
1285
1286    Color fgColor = style->visitedDependentColor(CSSPropertyColor);
1287    if (fgColor.isValid()) {
1288        buffer.set(g_strdup_printf("%i,%i,%i",
1289                                   fgColor.red(), fgColor.green(), fgColor.blue()));
1290        result = addAttributeToSet(result, atk_text_attribute_get_name(ATK_TEXT_ATTR_FG_COLOR), buffer.get());
1291    }
1292
1293    int baselinePosition;
1294    bool includeRise = true;
1295    switch (style->verticalAlign()) {
1296    case SUB:
1297        baselinePosition = -1 * baselinePositionForRenderObject(renderer);
1298        break;
1299    case SUPER:
1300        baselinePosition = baselinePositionForRenderObject(renderer);
1301        break;
1302    case BASELINE:
1303        baselinePosition = 0;
1304        break;
1305    default:
1306        includeRise = false;
1307        break;
1308    }
1309
1310    if (includeRise) {
1311        buffer.set(g_strdup_printf("%i", baselinePosition));
1312        result = addAttributeToSet(result, atk_text_attribute_get_name(ATK_TEXT_ATTR_RISE), buffer.get());
1313    }
1314
1315    int indentation = style->textIndent().calcValue(object->size().width());
1316    if (indentation != undefinedLength) {
1317        buffer.set(g_strdup_printf("%i", indentation));
1318        result = addAttributeToSet(result, atk_text_attribute_get_name(ATK_TEXT_ATTR_INDENT), buffer.get());
1319    }
1320
1321    String fontFamilyName = style->font().family().family().string();
1322    if (fontFamilyName.left(8) == "-webkit-")
1323        fontFamilyName = fontFamilyName.substring(8);
1324
1325    result = addAttributeToSet(result, atk_text_attribute_get_name(ATK_TEXT_ATTR_FAMILY_NAME), fontFamilyName.utf8().data());
1326
1327    int fontWeight = -1;
1328    switch (style->font().weight()) {
1329    case FontWeight100:
1330        fontWeight = 100;
1331        break;
1332    case FontWeight200:
1333        fontWeight = 200;
1334        break;
1335    case FontWeight300:
1336        fontWeight = 300;
1337        break;
1338    case FontWeight400:
1339        fontWeight = 400;
1340        break;
1341    case FontWeight500:
1342        fontWeight = 500;
1343        break;
1344    case FontWeight600:
1345        fontWeight = 600;
1346        break;
1347    case FontWeight700:
1348        fontWeight = 700;
1349        break;
1350    case FontWeight800:
1351        fontWeight = 800;
1352        break;
1353    case FontWeight900:
1354        fontWeight = 900;
1355    }
1356    if (fontWeight > 0) {
1357        buffer.set(g_strdup_printf("%i", fontWeight));
1358        result = addAttributeToSet(result, atk_text_attribute_get_name(ATK_TEXT_ATTR_WEIGHT), buffer.get());
1359    }
1360
1361    switch (style->textAlign()) {
1362    case TAAUTO:
1363    case TASTART:
1364    case TAEND:
1365        break;
1366    case LEFT:
1367    case WEBKIT_LEFT:
1368        result = addAttributeToSet(result, atk_text_attribute_get_name(ATK_TEXT_ATTR_JUSTIFICATION), "left");
1369        break;
1370    case RIGHT:
1371    case WEBKIT_RIGHT:
1372        result = addAttributeToSet(result, atk_text_attribute_get_name(ATK_TEXT_ATTR_JUSTIFICATION), "right");
1373        break;
1374    case CENTER:
1375    case WEBKIT_CENTER:
1376        result = addAttributeToSet(result, atk_text_attribute_get_name(ATK_TEXT_ATTR_JUSTIFICATION), "center");
1377        break;
1378    case JUSTIFY:
1379        result = addAttributeToSet(result, atk_text_attribute_get_name(ATK_TEXT_ATTR_JUSTIFICATION), "fill");
1380    }
1381
1382    result = addAttributeToSet(result, atk_text_attribute_get_name(ATK_TEXT_ATTR_UNDERLINE), (style->textDecoration() & UNDERLINE) ? "single" : "none");
1383
1384    result = addAttributeToSet(result, atk_text_attribute_get_name(ATK_TEXT_ATTR_STYLE), style->font().italic() ? "italic" : "normal");
1385
1386    result = addAttributeToSet(result, atk_text_attribute_get_name(ATK_TEXT_ATTR_STRIKETHROUGH), (style->textDecoration() & LINE_THROUGH) ? "true" : "false");
1387
1388    result = addAttributeToSet(result, atk_text_attribute_get_name(ATK_TEXT_ATTR_INVISIBLE), (style->visibility() == HIDDEN) ? "true" : "false");
1389
1390    result = addAttributeToSet(result, atk_text_attribute_get_name(ATK_TEXT_ATTR_EDITABLE), object->isReadOnly() ? "false" : "true");
1391
1392    return result;
1393}
1394
1395static gint compareAttribute(const AtkAttribute* a, const AtkAttribute* b)
1396{
1397    return g_strcmp0(a->name, b->name) || g_strcmp0(a->value, b->value);
1398}
1399
1400// Returns an AtkAttributeSet with the elements of a1 which are either
1401// not present or different in a2.  Neither a1 nor a2 should be used
1402// after calling this function.
1403static AtkAttributeSet* attributeSetDifference(AtkAttributeSet* a1, AtkAttributeSet* a2)
1404{
1405    if (!a2)
1406        return a1;
1407
1408    AtkAttributeSet* i = a1;
1409    AtkAttributeSet* found;
1410    AtkAttributeSet* toDelete = 0;
1411
1412    while (i) {
1413        found = g_slist_find_custom(a2, i->data, (GCompareFunc)compareAttribute);
1414        if (found) {
1415            AtkAttributeSet* t = i->next;
1416            toDelete = g_slist_prepend(toDelete, i->data);
1417            a1 = g_slist_delete_link(a1, i);
1418            i = t;
1419        } else
1420            i = i->next;
1421    }
1422
1423    atk_attribute_set_free(a2);
1424    atk_attribute_set_free(toDelete);
1425    return a1;
1426}
1427
1428static guint accessibilityObjectLength(const AccessibilityObject* object)
1429{
1430    // Non render objects are not taken into account
1431    if (!object->isAccessibilityRenderObject())
1432        return 0;
1433
1434    // For those objects implementing the AtkText interface we use the
1435    // well known API to always get the text in a consistent way
1436    AtkObject* atkObj = ATK_OBJECT(object->wrapper());
1437    if (ATK_IS_TEXT(atkObj)) {
1438        GOwnPtr<gchar> text(webkit_accessible_text_get_text(ATK_TEXT(atkObj), 0, -1));
1439        return g_utf8_strlen(text.get(), -1);
1440    }
1441
1442    // Even if we don't expose list markers to Assistive
1443    // Technologies, we need to have a way to measure their length
1444    // for those cases when it's needed to take it into account
1445    // separately (as in getAccessibilityObjectForOffset)
1446    RenderObject* renderer = object->renderer();
1447    if (renderer && renderer->isListMarker()) {
1448        RenderListMarker* marker = toRenderListMarker(renderer);
1449        return marker->text().length() + marker->suffix().length();
1450    }
1451
1452    return 0;
1453}
1454
1455static const AccessibilityObject* getAccessibilityObjectForOffset(const AccessibilityObject* object, guint offset, gint* startOffset, gint* endOffset)
1456{
1457    const AccessibilityObject* result;
1458    guint length = accessibilityObjectLength(object);
1459    if (length > offset) {
1460        *startOffset = 0;
1461        *endOffset = length;
1462        result = object;
1463    } else {
1464        *startOffset = -1;
1465        *endOffset = -1;
1466        result = 0;
1467    }
1468
1469    if (!object->firstChild())
1470        return result;
1471
1472    AccessibilityObject* child = object->firstChild();
1473    guint currentOffset = 0;
1474    guint childPosition = 0;
1475    while (child && currentOffset <= offset) {
1476        guint childLength = accessibilityObjectLength(child);
1477        currentOffset = childLength + childPosition;
1478        if (currentOffset > offset) {
1479            gint childStartOffset;
1480            gint childEndOffset;
1481            const AccessibilityObject* grandChild = getAccessibilityObjectForOffset(child, offset-childPosition,  &childStartOffset, &childEndOffset);
1482            if (childStartOffset >= 0) {
1483                *startOffset = childStartOffset + childPosition;
1484                *endOffset = childEndOffset + childPosition;
1485                result = grandChild;
1486            }
1487        } else {
1488            childPosition += childLength;
1489            child = child->nextSibling();
1490        }
1491    }
1492    return result;
1493}
1494
1495static AtkAttributeSet* getRunAttributesFromAccesibilityObject(const AccessibilityObject* element, gint offset, gint* startOffset, gint* endOffset)
1496{
1497    const AccessibilityObject *child = getAccessibilityObjectForOffset(element, offset, startOffset, endOffset);
1498    if (!child) {
1499        *startOffset = -1;
1500        *endOffset = -1;
1501        return 0;
1502    }
1503
1504    AtkAttributeSet* defaultAttributes = getAttributeSetForAccessibilityObject(element);
1505    AtkAttributeSet* childAttributes = getAttributeSetForAccessibilityObject(child);
1506
1507    return attributeSetDifference(childAttributes, defaultAttributes);
1508}
1509
1510static AtkAttributeSet* webkit_accessible_text_get_run_attributes(AtkText* text, gint offset, gint* startOffset, gint* endOffset)
1511{
1512    AccessibilityObject* coreObject = core(text);
1513    AtkAttributeSet* result;
1514
1515    if (!coreObject) {
1516        *startOffset = 0;
1517        *endOffset = atk_text_get_character_count(text);
1518        return 0;
1519    }
1520
1521    if (offset == -1)
1522        offset = atk_text_get_caret_offset(text);
1523
1524    result = getRunAttributesFromAccesibilityObject(coreObject, offset, startOffset, endOffset);
1525
1526    if (*startOffset < 0) {
1527        *startOffset = offset;
1528        *endOffset = offset;
1529    }
1530
1531    return result;
1532}
1533
1534static AtkAttributeSet* webkit_accessible_text_get_default_attributes(AtkText* text)
1535{
1536    AccessibilityObject* coreObject = core(text);
1537    if (!coreObject || !coreObject->isAccessibilityRenderObject())
1538        return 0;
1539
1540    return getAttributeSetForAccessibilityObject(coreObject);
1541}
1542
1543static IntRect textExtents(AtkText* text, gint startOffset, gint length, AtkCoordType coords)
1544{
1545    gchar* textContent = webkit_accessible_text_get_text(text, startOffset, -1);
1546    gint textLength = g_utf8_strlen(textContent, -1);
1547
1548    // The first case (endOffset of -1) should work, but seems broken for all Gtk+ apps.
1549    gint rangeLength = length;
1550    if (rangeLength < 0 || rangeLength > textLength)
1551        rangeLength = textLength;
1552    AccessibilityObject* coreObject = core(text);
1553
1554    IntRect extents = coreObject->doAXBoundsForRange(PlainTextRange(startOffset, rangeLength));
1555    switch(coords) {
1556    case ATK_XY_SCREEN:
1557        if (Document* document = coreObject->document())
1558            extents = document->view()->contentsToScreen(extents);
1559        break;
1560    case ATK_XY_WINDOW:
1561        // No-op
1562        break;
1563    }
1564
1565    return extents;
1566}
1567
1568static void webkit_accessible_text_get_character_extents(AtkText* text, gint offset, gint* x, gint* y, gint* width, gint* height, AtkCoordType coords)
1569{
1570    IntRect extents = textExtents(text, offset, 1, coords);
1571    *x = extents.x();
1572    *y = extents.y();
1573    *width = extents.width();
1574    *height = extents.height();
1575}
1576
1577static void webkit_accessible_text_get_range_extents(AtkText* text, gint startOffset, gint endOffset, AtkCoordType coords, AtkTextRectangle* rect)
1578{
1579    IntRect extents = textExtents(text, startOffset, endOffset - startOffset, coords);
1580    rect->x = extents.x();
1581    rect->y = extents.y();
1582    rect->width = extents.width();
1583    rect->height = extents.height();
1584}
1585
1586static gint webkit_accessible_text_get_character_count(AtkText* text)
1587{
1588    return accessibilityObjectLength(core(text));
1589}
1590
1591static gint webkit_accessible_text_get_offset_at_point(AtkText* text, gint x, gint y, AtkCoordType coords)
1592{
1593    // FIXME: Use the AtkCoordType
1594    // TODO: Is it correct to ignore range.length?
1595    IntPoint pos(x, y);
1596    PlainTextRange range = core(text)->doAXRangeForPosition(pos);
1597    return range.start;
1598}
1599
1600static void getSelectionOffsetsForObject(AccessibilityObject* coreObject, VisibleSelection& selection, gint& startOffset, gint& endOffset)
1601{
1602    if (!coreObject->isAccessibilityRenderObject())
1603        return;
1604
1605    // Early return if the selection doesn't affect the selected node.
1606    if (!selectionBelongsToObject(coreObject, selection))
1607        return;
1608
1609    // We need to find the exact start and end positions in the
1610    // selected node that intersects the selection, to later on get
1611    // the right values for the effective start and end offsets.
1612    ExceptionCode ec = 0;
1613    Position nodeRangeStart;
1614    Position nodeRangeEnd;
1615    Node* node = coreObject->node();
1616    RefPtr<Range> selRange = selection.toNormalizedRange();
1617
1618    // If the selection affects the selected node and its first
1619    // possible position is also in the selection, we must set
1620    // nodeRangeStart to that position, otherwise to the selection's
1621    // start position (it would belong to the node anyway).
1622    Node* firstLeafNode = node->firstDescendant();
1623    if (selRange->isPointInRange(firstLeafNode, 0, ec))
1624        nodeRangeStart = firstPositionInOrBeforeNode(firstLeafNode);
1625    else
1626        nodeRangeStart = selRange->startPosition();
1627
1628    // If the selection affects the selected node and its last
1629    // possible position is also in the selection, we must set
1630    // nodeRangeEnd to that position, otherwise to the selection's
1631    // end position (it would belong to the node anyway).
1632    Node* lastLeafNode = node->lastDescendant();
1633    if (selRange->isPointInRange(lastLeafNode, lastOffsetInNode(lastLeafNode), ec))
1634        nodeRangeEnd = lastPositionInOrAfterNode(lastLeafNode);
1635    else
1636        nodeRangeEnd = selRange->endPosition();
1637
1638    // Calculate position of the selected range inside the object.
1639    Position parentFirstPosition = firstPositionInOrBeforeNode(node);
1640    RefPtr<Range> rangeInParent = Range::create(node->document(), parentFirstPosition, nodeRangeStart);
1641
1642    // Set values for start and end offsets.
1643    startOffset = TextIterator::rangeLength(rangeInParent.get(), true);
1644
1645    // We need to adjust the offsets for the list item marker.
1646    RenderObject* renderer = coreObject->renderer();
1647    if (renderer && renderer->isListItem()) {
1648        String markerText = toRenderListItem(renderer)->markerTextWithSuffix();
1649        startOffset += markerText.length();
1650    }
1651
1652    RefPtr<Range> nodeRange = Range::create(node->document(), nodeRangeStart, nodeRangeEnd);
1653    endOffset = startOffset + TextIterator::rangeLength(nodeRange.get(), true);
1654}
1655
1656static gint webkit_accessible_text_get_n_selections(AtkText* text)
1657{
1658    AccessibilityObject* coreObject = core(text);
1659    VisibleSelection selection = coreObject->selection();
1660
1661    // Only range selections are needed for the purpose of this method
1662    if (!selection.isRange())
1663        return 0;
1664
1665    // We don't support multiple selections for now, so there's only
1666    // two possibilities
1667    // Also, we don't want to do anything if the selection does not
1668    // belong to the currently selected object. We have to check since
1669    // there's no way to get the selection for a given object, only
1670    // the global one (the API is a bit confusing)
1671    return selectionBelongsToObject(coreObject, selection) ? 1 : 0;
1672}
1673
1674static gchar* webkit_accessible_text_get_selection(AtkText* text, gint selectionNum, gint* startOffset, gint* endOffset)
1675{
1676    // Default values, unless the contrary is proved
1677    *startOffset = *endOffset = 0;
1678
1679    // WebCore does not support multiple selection, so anything but 0 does not make sense for now.
1680    if (selectionNum)
1681        return 0;
1682
1683    // Get the offsets of the selection for the selected object
1684    AccessibilityObject* coreObject = core(text);
1685    VisibleSelection selection = coreObject->selection();
1686    getSelectionOffsetsForObject(coreObject, selection, *startOffset, *endOffset);
1687
1688    // Return 0 instead of "", as that's the expected result for
1689    // this AtkText method when there's no selection
1690    if (*startOffset == *endOffset)
1691        return 0;
1692
1693    return webkit_accessible_text_get_text(text, *startOffset, *endOffset);
1694}
1695
1696static gboolean webkit_accessible_text_add_selection(AtkText* text, gint start_offset, gint end_offset)
1697{
1698    notImplemented();
1699    return FALSE;
1700}
1701
1702static gboolean webkit_accessible_text_set_selection(AtkText* text, gint selectionNum, gint startOffset, gint endOffset)
1703{
1704    // WebCore does not support multiple selection, so anything but 0 does not make sense for now.
1705    if (selectionNum)
1706        return FALSE;
1707
1708    AccessibilityObject* coreObject = core(text);
1709    if (!coreObject->isAccessibilityRenderObject())
1710        return FALSE;
1711
1712    // Consider -1 and out-of-bound values and correct them to length
1713    gint textCount = webkit_accessible_text_get_character_count(text);
1714    if (startOffset < 0 || startOffset > textCount)
1715        startOffset = textCount;
1716    if (endOffset < 0 || endOffset > textCount)
1717        endOffset = textCount;
1718
1719    // We need to adjust the offsets for the list item marker.
1720    RenderObject* renderer = coreObject->renderer();
1721    if (renderer && renderer->isListItem()) {
1722        String markerText = toRenderListItem(renderer)->markerTextWithSuffix();
1723        int markerLength = markerText.length();
1724        if (startOffset < markerLength || endOffset < markerLength)
1725            return FALSE;
1726
1727        startOffset -= markerLength;
1728        endOffset -= markerLength;
1729    }
1730
1731    PlainTextRange textRange(startOffset, endOffset - startOffset);
1732    VisiblePositionRange range = coreObject->visiblePositionRangeForRange(textRange);
1733    if (range.isNull())
1734        return FALSE;
1735
1736    coreObject->setSelectedVisiblePositionRange(range);
1737    return TRUE;
1738}
1739
1740static gboolean webkit_accessible_text_remove_selection(AtkText* text, gint selectionNum)
1741{
1742    // WebCore does not support multiple selection, so anything but 0 does not make sense for now.
1743    if (selectionNum)
1744        return FALSE;
1745
1746    // Do nothing if current selection doesn't belong to the object
1747    if (!webkit_accessible_text_get_n_selections(text))
1748        return FALSE;
1749
1750    // Set a new 0-sized selection to the caret position, in order
1751    // to simulate selection removal (GAIL style)
1752    gint caretOffset = webkit_accessible_text_get_caret_offset(text);
1753    return webkit_accessible_text_set_selection(text, selectionNum, caretOffset, caretOffset);
1754}
1755
1756static gboolean webkit_accessible_text_set_caret_offset(AtkText* text, gint offset)
1757{
1758    AccessibilityObject* coreObject = core(text);
1759
1760    if (!coreObject->isAccessibilityRenderObject())
1761        return FALSE;
1762
1763    RenderObject* renderer = coreObject->renderer();
1764    if (renderer && renderer->isListItem()) {
1765        String markerText = toRenderListItem(renderer)->markerTextWithSuffix();
1766        int markerLength = markerText.length();
1767        if (offset < markerLength)
1768            return FALSE;
1769
1770        // We need to adjust the offset for list items.
1771        offset -= markerLength;
1772    }
1773
1774    PlainTextRange textRange(offset, 0);
1775    VisiblePositionRange range = coreObject->visiblePositionRangeForRange(textRange);
1776    if (range.isNull())
1777        return FALSE;
1778
1779    coreObject->setSelectedVisiblePositionRange(range);
1780    return TRUE;
1781}
1782
1783static void atk_text_interface_init(AtkTextIface* iface)
1784{
1785    iface->get_text = webkit_accessible_text_get_text;
1786    iface->get_text_after_offset = webkit_accessible_text_get_text_after_offset;
1787    iface->get_text_at_offset = webkit_accessible_text_get_text_at_offset;
1788    iface->get_character_at_offset = webkit_accessible_text_get_character_at_offset;
1789    iface->get_text_before_offset = webkit_accessible_text_get_text_before_offset;
1790    iface->get_caret_offset = webkit_accessible_text_get_caret_offset;
1791    iface->get_run_attributes = webkit_accessible_text_get_run_attributes;
1792    iface->get_default_attributes = webkit_accessible_text_get_default_attributes;
1793    iface->get_character_extents = webkit_accessible_text_get_character_extents;
1794    iface->get_range_extents = webkit_accessible_text_get_range_extents;
1795    iface->get_character_count = webkit_accessible_text_get_character_count;
1796    iface->get_offset_at_point = webkit_accessible_text_get_offset_at_point;
1797    iface->get_n_selections = webkit_accessible_text_get_n_selections;
1798    iface->get_selection = webkit_accessible_text_get_selection;
1799
1800    // set methods
1801    iface->add_selection = webkit_accessible_text_add_selection;
1802    iface->remove_selection = webkit_accessible_text_remove_selection;
1803    iface->set_selection = webkit_accessible_text_set_selection;
1804    iface->set_caret_offset = webkit_accessible_text_set_caret_offset;
1805}
1806
1807// EditableText
1808
1809static gboolean webkit_accessible_editable_text_set_run_attributes(AtkEditableText* text, AtkAttributeSet* attrib_set, gint start_offset, gint end_offset)
1810{
1811    notImplemented();
1812    return FALSE;
1813}
1814
1815static void webkit_accessible_editable_text_set_text_contents(AtkEditableText* text, const gchar* string)
1816{
1817    // FIXME: string nullcheck?
1818    core(text)->setValue(String::fromUTF8(string));
1819}
1820
1821static void webkit_accessible_editable_text_insert_text(AtkEditableText* text, const gchar* string, gint length, gint* position)
1822{
1823    // FIXME: string nullcheck?
1824
1825    AccessibilityObject* coreObject = core(text);
1826    // FIXME: Not implemented in WebCore
1827    //coreObject->setSelectedTextRange(PlainTextRange(*position, 0));
1828    //coreObject->setSelectedText(String::fromUTF8(string));
1829
1830    Document* document = coreObject->document();
1831    if (!document || !document->frame())
1832        return;
1833
1834    coreObject->setSelectedVisiblePositionRange(coreObject->visiblePositionRangeForRange(PlainTextRange(*position, 0)));
1835    coreObject->setFocused(true);
1836    // FIXME: We should set position to the actual inserted text length, which may be less than that requested.
1837    if (document->frame()->editor()->insertTextWithoutSendingTextEvent(String::fromUTF8(string), false, 0))
1838        *position += length;
1839}
1840
1841static void webkit_accessible_editable_text_copy_text(AtkEditableText* text, gint start_pos, gint end_pos)
1842{
1843    notImplemented();
1844}
1845
1846static void webkit_accessible_editable_text_cut_text(AtkEditableText* text, gint start_pos, gint end_pos)
1847{
1848    notImplemented();
1849}
1850
1851static void webkit_accessible_editable_text_delete_text(AtkEditableText* text, gint start_pos, gint end_pos)
1852{
1853    AccessibilityObject* coreObject = core(text);
1854    // FIXME: Not implemented in WebCore
1855    //coreObject->setSelectedTextRange(PlainTextRange(start_pos, end_pos - start_pos));
1856    //coreObject->setSelectedText(String());
1857
1858    Document* document = coreObject->document();
1859    if (!document || !document->frame())
1860        return;
1861
1862    coreObject->setSelectedVisiblePositionRange(coreObject->visiblePositionRangeForRange(PlainTextRange(start_pos, end_pos - start_pos)));
1863    coreObject->setFocused(true);
1864    document->frame()->editor()->performDelete();
1865}
1866
1867static void webkit_accessible_editable_text_paste_text(AtkEditableText* text, gint position)
1868{
1869    notImplemented();
1870}
1871
1872static void atk_editable_text_interface_init(AtkEditableTextIface* iface)
1873{
1874    iface->set_run_attributes = webkit_accessible_editable_text_set_run_attributes;
1875    iface->set_text_contents = webkit_accessible_editable_text_set_text_contents;
1876    iface->insert_text = webkit_accessible_editable_text_insert_text;
1877    iface->copy_text = webkit_accessible_editable_text_copy_text;
1878    iface->cut_text = webkit_accessible_editable_text_cut_text;
1879    iface->delete_text = webkit_accessible_editable_text_delete_text;
1880    iface->paste_text = webkit_accessible_editable_text_paste_text;
1881}
1882
1883static void contentsToAtk(AccessibilityObject* coreObject, AtkCoordType coordType, IntRect rect, gint* x, gint* y, gint* width = 0, gint* height = 0)
1884{
1885    FrameView* frameView = coreObject->documentFrameView();
1886
1887    if (frameView) {
1888        switch (coordType) {
1889        case ATK_XY_WINDOW:
1890            rect = frameView->contentsToWindow(rect);
1891            break;
1892        case ATK_XY_SCREEN:
1893            rect = frameView->contentsToScreen(rect);
1894            break;
1895        }
1896    }
1897
1898    if (x)
1899        *x = rect.x();
1900    if (y)
1901        *y = rect.y();
1902    if (width)
1903        *width = rect.width();
1904    if (height)
1905        *height = rect.height();
1906}
1907
1908static IntPoint atkToContents(AccessibilityObject* coreObject, AtkCoordType coordType, gint x, gint y)
1909{
1910    IntPoint pos(x, y);
1911
1912    FrameView* frameView = coreObject->documentFrameView();
1913    if (frameView) {
1914        switch (coordType) {
1915        case ATK_XY_SCREEN:
1916            return frameView->screenToContents(pos);
1917        case ATK_XY_WINDOW:
1918            return frameView->windowToContents(pos);
1919        }
1920    }
1921
1922    return pos;
1923}
1924
1925static AtkObject* webkit_accessible_component_ref_accessible_at_point(AtkComponent* component, gint x, gint y, AtkCoordType coordType)
1926{
1927    IntPoint pos = atkToContents(core(component), coordType, x, y);
1928
1929    AccessibilityObject* target = core(component)->accessibilityHitTest(pos);
1930    if (!target)
1931        return 0;
1932    g_object_ref(target->wrapper());
1933    return target->wrapper();
1934}
1935
1936static void webkit_accessible_component_get_extents(AtkComponent* component, gint* x, gint* y, gint* width, gint* height, AtkCoordType coordType)
1937{
1938    IntRect rect = core(component)->elementRect();
1939    contentsToAtk(core(component), coordType, rect, x, y, width, height);
1940}
1941
1942static gboolean webkit_accessible_component_grab_focus(AtkComponent* component)
1943{
1944    core(component)->setFocused(true);
1945    return core(component)->isFocused();
1946}
1947
1948static void atk_component_interface_init(AtkComponentIface* iface)
1949{
1950    iface->ref_accessible_at_point = webkit_accessible_component_ref_accessible_at_point;
1951    iface->get_extents = webkit_accessible_component_get_extents;
1952    iface->grab_focus = webkit_accessible_component_grab_focus;
1953}
1954
1955// Image
1956
1957static void webkit_accessible_image_get_image_position(AtkImage* image, gint* x, gint* y, AtkCoordType coordType)
1958{
1959    IntRect rect = core(image)->elementRect();
1960    contentsToAtk(core(image), coordType, rect, x, y);
1961}
1962
1963static const gchar* webkit_accessible_image_get_image_description(AtkImage* image)
1964{
1965    return returnString(core(image)->accessibilityDescription());
1966}
1967
1968static void webkit_accessible_image_get_image_size(AtkImage* image, gint* width, gint* height)
1969{
1970    IntSize size = core(image)->size();
1971
1972    if (width)
1973        *width = size.width();
1974    if (height)
1975        *height = size.height();
1976}
1977
1978static void atk_image_interface_init(AtkImageIface* iface)
1979{
1980    iface->get_image_position = webkit_accessible_image_get_image_position;
1981    iface->get_image_description = webkit_accessible_image_get_image_description;
1982    iface->get_image_size = webkit_accessible_image_get_image_size;
1983}
1984
1985// Table
1986
1987static AccessibilityTableCell* cell(AtkTable* table, guint row, guint column)
1988{
1989    AccessibilityObject* accTable = core(table);
1990    if (accTable->isAccessibilityRenderObject())
1991        return static_cast<AccessibilityTable*>(accTable)->cellForColumnAndRow(column, row);
1992    return 0;
1993}
1994
1995static gint cellIndex(AccessibilityTableCell* axCell, AccessibilityTable* axTable)
1996{
1997    // Calculate the cell's index as if we had a traditional Gtk+ table in
1998    // which cells are all direct children of the table, arranged row-first.
1999    AccessibilityObject::AccessibilityChildrenVector allCells;
2000    axTable->cells(allCells);
2001    AccessibilityObject::AccessibilityChildrenVector::iterator position;
2002    position = std::find(allCells.begin(), allCells.end(), axCell);
2003    if (position == allCells.end())
2004        return -1;
2005    return position - allCells.begin();
2006}
2007
2008static AccessibilityTableCell* cellAtIndex(AtkTable* table, gint index)
2009{
2010    AccessibilityObject* accTable = core(table);
2011    if (accTable->isAccessibilityRenderObject()) {
2012        AccessibilityObject::AccessibilityChildrenVector allCells;
2013        static_cast<AccessibilityTable*>(accTable)->cells(allCells);
2014        if (0 <= index && static_cast<unsigned>(index) < allCells.size()) {
2015            AccessibilityObject* accCell = allCells.at(index).get();
2016            return static_cast<AccessibilityTableCell*>(accCell);
2017        }
2018    }
2019    return 0;
2020}
2021
2022static AtkObject* webkit_accessible_table_ref_at(AtkTable* table, gint row, gint column)
2023{
2024    AccessibilityTableCell* axCell = cell(table, row, column);
2025    if (!axCell)
2026        return 0;
2027    return axCell->wrapper();
2028}
2029
2030static gint webkit_accessible_table_get_index_at(AtkTable* table, gint row, gint column)
2031{
2032    AccessibilityTableCell* axCell = cell(table, row, column);
2033    AccessibilityTable* axTable = static_cast<AccessibilityTable*>(core(table));
2034    return cellIndex(axCell, axTable);
2035}
2036
2037static gint webkit_accessible_table_get_column_at_index(AtkTable* table, gint index)
2038{
2039    AccessibilityTableCell* axCell = cellAtIndex(table, index);
2040    if (axCell){
2041        pair<int, int> columnRange;
2042        axCell->columnIndexRange(columnRange);
2043        return columnRange.first;
2044    }
2045    return -1;
2046}
2047
2048static gint webkit_accessible_table_get_row_at_index(AtkTable* table, gint index)
2049{
2050    AccessibilityTableCell* axCell = cellAtIndex(table, index);
2051    if (axCell){
2052        pair<int, int> rowRange;
2053        axCell->rowIndexRange(rowRange);
2054        return rowRange.first;
2055    }
2056    return -1;
2057}
2058
2059static gint webkit_accessible_table_get_n_columns(AtkTable* table)
2060{
2061    AccessibilityObject* accTable = core(table);
2062    if (accTable->isAccessibilityRenderObject())
2063        return static_cast<AccessibilityTable*>(accTable)->columnCount();
2064    return 0;
2065}
2066
2067static gint webkit_accessible_table_get_n_rows(AtkTable* table)
2068{
2069    AccessibilityObject* accTable = core(table);
2070    if (accTable->isAccessibilityRenderObject())
2071        return static_cast<AccessibilityTable*>(accTable)->rowCount();
2072    return 0;
2073}
2074
2075static gint webkit_accessible_table_get_column_extent_at(AtkTable* table, gint row, gint column)
2076{
2077    AccessibilityTableCell* axCell = cell(table, row, column);
2078    if (axCell) {
2079        pair<int, int> columnRange;
2080        axCell->columnIndexRange(columnRange);
2081        return columnRange.second;
2082    }
2083    return 0;
2084}
2085
2086static gint webkit_accessible_table_get_row_extent_at(AtkTable* table, gint row, gint column)
2087{
2088    AccessibilityTableCell* axCell = cell(table, row, column);
2089    if (axCell) {
2090        pair<int, int> rowRange;
2091        axCell->rowIndexRange(rowRange);
2092        return rowRange.second;
2093    }
2094    return 0;
2095}
2096
2097static AtkObject* webkit_accessible_table_get_column_header(AtkTable* table, gint column)
2098{
2099    AccessibilityObject* accTable = core(table);
2100    if (accTable->isAccessibilityRenderObject()) {
2101        AccessibilityObject::AccessibilityChildrenVector allColumnHeaders;
2102        static_cast<AccessibilityTable*>(accTable)->columnHeaders(allColumnHeaders);
2103        unsigned columnCount = allColumnHeaders.size();
2104        for (unsigned k = 0; k < columnCount; ++k) {
2105            pair<int, int> columnRange;
2106            AccessibilityTableCell* cell = static_cast<AccessibilityTableCell*>(allColumnHeaders.at(k).get());
2107            cell->columnIndexRange(columnRange);
2108            if (columnRange.first <= column && column < columnRange.first + columnRange.second)
2109                return allColumnHeaders[k]->wrapper();
2110        }
2111    }
2112    return 0;
2113}
2114
2115static AtkObject* webkit_accessible_table_get_row_header(AtkTable* table, gint row)
2116{
2117    AccessibilityObject* accTable = core(table);
2118    if (accTable->isAccessibilityRenderObject()) {
2119        AccessibilityObject::AccessibilityChildrenVector allRowHeaders;
2120        static_cast<AccessibilityTable*>(accTable)->rowHeaders(allRowHeaders);
2121        unsigned rowCount = allRowHeaders.size();
2122        for (unsigned k = 0; k < rowCount; ++k) {
2123            pair<int, int> rowRange;
2124            AccessibilityTableCell* cell = static_cast<AccessibilityTableCell*>(allRowHeaders.at(k).get());
2125            cell->rowIndexRange(rowRange);
2126            if (rowRange.first <= row && row < rowRange.first + rowRange.second)
2127                return allRowHeaders[k]->wrapper();
2128        }
2129    }
2130    return 0;
2131}
2132
2133static AtkObject* webkit_accessible_table_get_caption(AtkTable* table)
2134{
2135    AccessibilityObject* accTable = core(table);
2136    if (accTable->isAccessibilityRenderObject()) {
2137        Node* node = accTable->node();
2138        if (node && node->hasTagName(HTMLNames::tableTag)) {
2139            HTMLTableCaptionElement* caption = static_cast<HTMLTableElement*>(node)->caption();
2140            if (caption)
2141                return AccessibilityObject::firstAccessibleObjectFromNode(caption->renderer()->node())->wrapper();
2142        }
2143    }
2144    return 0;
2145}
2146
2147static const gchar* webkit_accessible_table_get_column_description(AtkTable* table, gint column)
2148{
2149    AtkObject* columnHeader = atk_table_get_column_header(table, column);
2150    if (columnHeader && ATK_IS_TEXT(columnHeader))
2151        return webkit_accessible_text_get_text(ATK_TEXT(columnHeader), 0, -1);
2152
2153    return 0;
2154}
2155
2156static const gchar* webkit_accessible_table_get_row_description(AtkTable* table, gint row)
2157{
2158    AtkObject* rowHeader = atk_table_get_row_header(table, row);
2159    if (rowHeader && ATK_IS_TEXT(rowHeader))
2160        return webkit_accessible_text_get_text(ATK_TEXT(rowHeader), 0, -1);
2161
2162    return 0;
2163}
2164
2165static void atk_table_interface_init(AtkTableIface* iface)
2166{
2167    iface->ref_at = webkit_accessible_table_ref_at;
2168    iface->get_index_at = webkit_accessible_table_get_index_at;
2169    iface->get_column_at_index = webkit_accessible_table_get_column_at_index;
2170    iface->get_row_at_index = webkit_accessible_table_get_row_at_index;
2171    iface->get_n_columns = webkit_accessible_table_get_n_columns;
2172    iface->get_n_rows = webkit_accessible_table_get_n_rows;
2173    iface->get_column_extent_at = webkit_accessible_table_get_column_extent_at;
2174    iface->get_row_extent_at = webkit_accessible_table_get_row_extent_at;
2175    iface->get_column_header = webkit_accessible_table_get_column_header;
2176    iface->get_row_header = webkit_accessible_table_get_row_header;
2177    iface->get_caption = webkit_accessible_table_get_caption;
2178    iface->get_column_description = webkit_accessible_table_get_column_description;
2179    iface->get_row_description = webkit_accessible_table_get_row_description;
2180}
2181
2182static AtkHyperlink* webkitAccessibleHypertextGetLink(AtkHypertext* hypertext, gint index)
2183{
2184    AccessibilityObject::AccessibilityChildrenVector children = core(hypertext)->children();
2185    if (index < 0 || static_cast<unsigned>(index) >= children.size())
2186        return 0;
2187
2188    gint currentLink = -1;
2189    for (unsigned i = 0; i < children.size(); i++) {
2190        AccessibilityObject* coreChild = children.at(i).get();
2191        if (!coreChild->accessibilityIsIgnored()) {
2192            AtkObject* axObject = coreChild->wrapper();
2193            if (!axObject || !ATK_IS_HYPERLINK_IMPL(axObject))
2194                continue;
2195
2196            currentLink++;
2197            if (index != currentLink)
2198                continue;
2199
2200            return atk_hyperlink_impl_get_hyperlink(ATK_HYPERLINK_IMPL(axObject));
2201        }
2202    }
2203
2204    return 0;
2205}
2206
2207static gint webkitAccessibleHypertextGetNLinks(AtkHypertext* hypertext)
2208{
2209    AccessibilityObject::AccessibilityChildrenVector children = core(hypertext)->children();
2210    if (!children.size())
2211        return 0;
2212
2213    gint linksFound = 0;
2214    for (size_t i = 0; i < children.size(); i++) {
2215        AccessibilityObject* coreChild = children.at(i).get();
2216        if (!coreChild->accessibilityIsIgnored()) {
2217            AtkObject* axObject = coreChild->wrapper();
2218            if (axObject && ATK_IS_HYPERLINK_IMPL(axObject))
2219                linksFound++;
2220        }
2221    }
2222
2223    return linksFound;
2224}
2225
2226static gint webkitAccessibleHypertextGetLinkIndex(AtkHypertext* hypertext, gint charIndex)
2227{
2228    size_t linksCount = webkitAccessibleHypertextGetNLinks(hypertext);
2229    if (!linksCount)
2230        return -1;
2231
2232    for (size_t i = 0; i < linksCount; i++) {
2233        AtkHyperlink* hyperlink = ATK_HYPERLINK(webkitAccessibleHypertextGetLink(hypertext, i));
2234        gint startIndex = atk_hyperlink_get_start_index(hyperlink);
2235        gint endIndex = atk_hyperlink_get_end_index(hyperlink);
2236
2237        // Check if the char index in the link's offset range
2238        if (startIndex <= charIndex && charIndex < endIndex)
2239            return i;
2240    }
2241
2242    // Not found if reached
2243    return -1;
2244}
2245
2246static void atkHypertextInterfaceInit(AtkHypertextIface* iface)
2247{
2248    iface->get_link = webkitAccessibleHypertextGetLink;
2249    iface->get_n_links = webkitAccessibleHypertextGetNLinks;
2250    iface->get_link_index = webkitAccessibleHypertextGetLinkIndex;
2251}
2252
2253static AtkHyperlink* webkitAccessibleHyperlinkImplGetHyperlink(AtkHyperlinkImpl* hyperlink)
2254{
2255    AtkHyperlink* hyperlinkObject = ATK_HYPERLINK(g_object_get_data(G_OBJECT(hyperlink), "hyperlink-object"));
2256    if (!hyperlinkObject) {
2257        hyperlinkObject = ATK_HYPERLINK(webkitAccessibleHyperlinkNew(hyperlink));
2258        g_object_set_data(G_OBJECT(hyperlink), "hyperlink-object", hyperlinkObject);
2259    }
2260    return hyperlinkObject;
2261}
2262
2263static void atkHyperlinkImplInterfaceInit(AtkHyperlinkImplIface* iface)
2264{
2265    iface->get_hyperlink = webkitAccessibleHyperlinkImplGetHyperlink;
2266}
2267
2268static const gchar* documentAttributeValue(AtkDocument* document, const gchar* attribute)
2269{
2270    Document* coreDocument = core(document)->document();
2271    if (!coreDocument)
2272        return 0;
2273
2274    String value = String();
2275    if (!g_ascii_strcasecmp(attribute, "DocType") && coreDocument->doctype())
2276        value = coreDocument->doctype()->name();
2277    else if (!g_ascii_strcasecmp(attribute, "Encoding"))
2278        value = coreDocument->charset();
2279    else if (!g_ascii_strcasecmp(attribute, "URI"))
2280        value = coreDocument->documentURI();
2281    if (!value.isEmpty())
2282        return returnString(value);
2283
2284    return 0;
2285}
2286
2287static const gchar* webkit_accessible_document_get_attribute_value(AtkDocument* document, const gchar* attribute)
2288{
2289    return documentAttributeValue(document, attribute);
2290}
2291
2292static AtkAttributeSet* webkit_accessible_document_get_attributes(AtkDocument* document)
2293{
2294    AtkAttributeSet* attributeSet = 0;
2295    const gchar* attributes [] = {"DocType", "Encoding", "URI"};
2296
2297    for (unsigned i = 0; i < G_N_ELEMENTS(attributes); i++) {
2298        const gchar* value = documentAttributeValue(document, attributes[i]);
2299        if (value)
2300            attributeSet = addAttributeToSet(attributeSet, attributes[i], value);
2301    }
2302
2303    return attributeSet;
2304}
2305
2306static const gchar* webkit_accessible_document_get_locale(AtkDocument* document)
2307{
2308
2309    // TODO: Should we fall back on lang xml:lang when the following comes up empty?
2310    String language = core(document)->language();
2311    if (!language.isEmpty())
2312        return returnString(language);
2313
2314    return 0;
2315}
2316
2317static void atk_document_interface_init(AtkDocumentIface* iface)
2318{
2319    iface->get_document_attribute_value = webkit_accessible_document_get_attribute_value;
2320    iface->get_document_attributes = webkit_accessible_document_get_attributes;
2321    iface->get_document_locale = webkit_accessible_document_get_locale;
2322}
2323
2324
2325static void webkitAccessibleValueGetCurrentValue(AtkValue* value, GValue* gValue)
2326{
2327    memset(gValue,  0, sizeof(GValue));
2328    g_value_init(gValue, G_TYPE_DOUBLE);
2329    g_value_set_double(gValue, core(value)->valueForRange());
2330}
2331
2332static void webkitAccessibleValueGetMaximumValue(AtkValue* value, GValue* gValue)
2333{
2334    memset(gValue,  0, sizeof(GValue));
2335    g_value_init(gValue, G_TYPE_DOUBLE);
2336    g_value_set_double(gValue, core(value)->maxValueForRange());
2337}
2338
2339static void webkitAccessibleValueGetMinimumValue(AtkValue* value, GValue* gValue)
2340{
2341    memset(gValue,  0, sizeof(GValue));
2342    g_value_init(gValue, G_TYPE_DOUBLE);
2343    g_value_set_double(gValue, core(value)->minValueForRange());
2344}
2345
2346static gboolean webkitAccessibleValueSetCurrentValue(AtkValue* value, const GValue* gValue)
2347{
2348    if (!G_VALUE_HOLDS_DOUBLE(gValue) && !G_VALUE_HOLDS_INT(gValue))
2349        return FALSE;
2350
2351    AccessibilityObject* coreObject = core(value);
2352    if (!coreObject->canSetValueAttribute())
2353        return FALSE;
2354
2355    if (G_VALUE_HOLDS_DOUBLE(gValue))
2356        coreObject->setValue(String::number(g_value_get_double(gValue)));
2357    else
2358        coreObject->setValue(String::number(g_value_get_int(gValue)));
2359
2360    return TRUE;
2361}
2362
2363static void webkitAccessibleValueGetMinimumIncrement(AtkValue* value, GValue* gValue)
2364{
2365    memset(gValue,  0, sizeof(GValue));
2366    g_value_init(gValue, G_TYPE_DOUBLE);
2367
2368    // There's not such a thing in the WAI-ARIA specification, thus return zero.
2369    g_value_set_double(gValue, 0.0);
2370}
2371
2372static void atkValueInterfaceInit(AtkValueIface* iface)
2373{
2374    iface->get_current_value = webkitAccessibleValueGetCurrentValue;
2375    iface->get_maximum_value = webkitAccessibleValueGetMaximumValue;
2376    iface->get_minimum_value = webkitAccessibleValueGetMinimumValue;
2377    iface->set_current_value = webkitAccessibleValueSetCurrentValue;
2378    iface->get_minimum_increment = webkitAccessibleValueGetMinimumIncrement;
2379}
2380
2381static const GInterfaceInfo AtkInterfacesInitFunctions[] = {
2382    {(GInterfaceInitFunc)atk_action_interface_init,
2383     (GInterfaceFinalizeFunc) 0, 0},
2384    {(GInterfaceInitFunc)atk_selection_interface_init,
2385     (GInterfaceFinalizeFunc) 0, 0},
2386    {(GInterfaceInitFunc)atk_editable_text_interface_init,
2387     (GInterfaceFinalizeFunc) 0, 0},
2388    {(GInterfaceInitFunc)atk_text_interface_init,
2389     (GInterfaceFinalizeFunc) 0, 0},
2390    {(GInterfaceInitFunc)atk_component_interface_init,
2391     (GInterfaceFinalizeFunc) 0, 0},
2392    {(GInterfaceInitFunc)atk_image_interface_init,
2393     (GInterfaceFinalizeFunc) 0, 0},
2394    {(GInterfaceInitFunc)atk_table_interface_init,
2395     (GInterfaceFinalizeFunc) 0, 0},
2396    {(GInterfaceInitFunc)atkHypertextInterfaceInit,
2397     (GInterfaceFinalizeFunc) 0, 0},
2398    {(GInterfaceInitFunc)atkHyperlinkImplInterfaceInit,
2399     (GInterfaceFinalizeFunc) 0, 0},
2400    {(GInterfaceInitFunc)atk_document_interface_init,
2401     (GInterfaceFinalizeFunc) 0, 0},
2402    {(GInterfaceInitFunc)atkValueInterfaceInit,
2403     (GInterfaceFinalizeFunc) 0, 0}
2404};
2405
2406enum WAIType {
2407    WAI_ACTION,
2408    WAI_SELECTION,
2409    WAI_EDITABLE_TEXT,
2410    WAI_TEXT,
2411    WAI_COMPONENT,
2412    WAI_IMAGE,
2413    WAI_TABLE,
2414    WAI_HYPERTEXT,
2415    WAI_HYPERLINK,
2416    WAI_DOCUMENT,
2417    WAI_VALUE,
2418};
2419
2420static GType GetAtkInterfaceTypeFromWAIType(WAIType type)
2421{
2422    switch (type) {
2423    case WAI_ACTION:
2424        return ATK_TYPE_ACTION;
2425    case WAI_SELECTION:
2426        return ATK_TYPE_SELECTION;
2427    case WAI_EDITABLE_TEXT:
2428        return ATK_TYPE_EDITABLE_TEXT;
2429    case WAI_TEXT:
2430        return ATK_TYPE_TEXT;
2431    case WAI_COMPONENT:
2432        return ATK_TYPE_COMPONENT;
2433    case WAI_IMAGE:
2434        return ATK_TYPE_IMAGE;
2435    case WAI_TABLE:
2436        return ATK_TYPE_TABLE;
2437    case WAI_HYPERTEXT:
2438        return ATK_TYPE_HYPERTEXT;
2439    case WAI_HYPERLINK:
2440        return ATK_TYPE_HYPERLINK_IMPL;
2441    case WAI_DOCUMENT:
2442        return ATK_TYPE_DOCUMENT;
2443    case WAI_VALUE:
2444        return ATK_TYPE_VALUE;
2445    }
2446
2447    return G_TYPE_INVALID;
2448}
2449
2450static guint16 getInterfaceMaskFromObject(AccessibilityObject* coreObject)
2451{
2452    guint16 interfaceMask = 0;
2453
2454    // Component interface is always supported
2455    interfaceMask |= 1 << WAI_COMPONENT;
2456
2457    AccessibilityRole role = coreObject->roleValue();
2458
2459    // Action
2460    // As the implementation of the AtkAction interface is a very
2461    // basic one (just relays in executing the default action for each
2462    // object, and only supports having one action per object), it is
2463    // better just to implement this interface for every instance of
2464    // the WebKitAccessible class and let WebCore decide what to do.
2465    interfaceMask |= 1 << WAI_ACTION;
2466
2467    // Selection
2468    if (coreObject->isListBox() || coreObject->isMenuList())
2469        interfaceMask |= 1 << WAI_SELECTION;
2470
2471    // Get renderer if available.
2472    RenderObject* renderer = 0;
2473    if (coreObject->isAccessibilityRenderObject())
2474        renderer = coreObject->renderer();
2475
2476    // Hyperlink (links and embedded objects).
2477    if (coreObject->isLink() || (renderer && renderer->isReplaced()))
2478        interfaceMask |= 1 << WAI_HYPERLINK;
2479
2480    // Text & Editable Text
2481    if (role == StaticTextRole || coreObject->isMenuListOption())
2482        interfaceMask |= 1 << WAI_TEXT;
2483    else {
2484        if (coreObject->isTextControl()) {
2485            interfaceMask |= 1 << WAI_TEXT;
2486            if (!coreObject->isReadOnly())
2487                interfaceMask |= 1 << WAI_EDITABLE_TEXT;
2488        } else {
2489            if (role != TableRole) {
2490                interfaceMask |= 1 << WAI_HYPERTEXT;
2491                if (renderer && renderer->childrenInline())
2492                    interfaceMask |= 1 << WAI_TEXT;
2493            }
2494
2495            // Add the TEXT interface for list items whose
2496            // first accessible child has a text renderer
2497            if (role == ListItemRole) {
2498                AccessibilityObject::AccessibilityChildrenVector children = coreObject->children();
2499                if (children.size()) {
2500                    AccessibilityObject* axRenderChild = children.at(0).get();
2501                    interfaceMask |= getInterfaceMaskFromObject(axRenderChild);
2502                }
2503            }
2504        }
2505    }
2506
2507    // Image
2508    if (coreObject->isImage())
2509        interfaceMask |= 1 << WAI_IMAGE;
2510
2511    // Table
2512    if (role == TableRole)
2513        interfaceMask |= 1 << WAI_TABLE;
2514
2515    // Document
2516    if (role == WebAreaRole)
2517        interfaceMask |= 1 << WAI_DOCUMENT;
2518
2519    // Value
2520    if (role == SliderRole)
2521        interfaceMask |= 1 << WAI_VALUE;
2522
2523    return interfaceMask;
2524}
2525
2526static const char* getUniqueAccessibilityTypeName(guint16 interfaceMask)
2527{
2528#define WAI_TYPE_NAME_LEN (30) /* Enough for prefix + 5 hex characters (max) */
2529    static char name[WAI_TYPE_NAME_LEN + 1];
2530
2531    g_sprintf(name, "WAIType%x", interfaceMask);
2532    name[WAI_TYPE_NAME_LEN] = '\0';
2533
2534    return name;
2535}
2536
2537static GType getAccessibilityTypeFromObject(AccessibilityObject* coreObject)
2538{
2539    static const GTypeInfo typeInfo = {
2540        sizeof(WebKitAccessibleClass),
2541        (GBaseInitFunc) 0,
2542        (GBaseFinalizeFunc) 0,
2543        (GClassInitFunc) 0,
2544        (GClassFinalizeFunc) 0,
2545        0, /* class data */
2546        sizeof(WebKitAccessible), /* instance size */
2547        0, /* nb preallocs */
2548        (GInstanceInitFunc) 0,
2549        0 /* value table */
2550    };
2551
2552    guint16 interfaceMask = getInterfaceMaskFromObject(coreObject);
2553    const char* atkTypeName = getUniqueAccessibilityTypeName(interfaceMask);
2554    GType type = g_type_from_name(atkTypeName);
2555    if (type)
2556        return type;
2557
2558    type = g_type_register_static(WEBKIT_TYPE_ACCESSIBLE,
2559                                  atkTypeName,
2560                                  &typeInfo, GTypeFlags(0));
2561    for (guint i = 0; i < G_N_ELEMENTS(AtkInterfacesInitFunctions); i++) {
2562        if (interfaceMask & (1 << i))
2563            g_type_add_interface_static(type,
2564                                        GetAtkInterfaceTypeFromWAIType(static_cast<WAIType>(i)),
2565                                        &AtkInterfacesInitFunctions[i]);
2566    }
2567
2568    return type;
2569}
2570
2571WebKitAccessible* webkit_accessible_new(AccessibilityObject* coreObject)
2572{
2573    GType type = getAccessibilityTypeFromObject(coreObject);
2574    AtkObject* object = static_cast<AtkObject*>(g_object_new(type, 0));
2575
2576    atk_object_initialize(object, coreObject);
2577
2578    return WEBKIT_ACCESSIBLE(object);
2579}
2580
2581AccessibilityObject* webkit_accessible_get_accessibility_object(WebKitAccessible* accessible)
2582{
2583    return accessible->m_object;
2584}
2585
2586void webkit_accessible_detach(WebKitAccessible* accessible)
2587{
2588    ASSERT(accessible->m_object);
2589
2590    if (core(accessible)->roleValue() == WebAreaRole)
2591        g_signal_emit_by_name(accessible, "state-change", "defunct", true);
2592
2593    // We replace the WebCore AccessibilityObject with a fallback object that
2594    // provides default implementations to avoid repetitive null-checking after
2595    // detachment.
2596    accessible->m_object = fallbackObject();
2597}
2598
2599AtkObject* webkit_accessible_get_focused_element(WebKitAccessible* accessible)
2600{
2601    if (!accessible->m_object)
2602        return 0;
2603
2604    RefPtr<AccessibilityObject> focusedObj = accessible->m_object->focusedUIElement();
2605    if (!focusedObj)
2606        return 0;
2607
2608    return focusedObj->wrapper();
2609}
2610
2611AccessibilityObject* objectAndOffsetUnignored(AccessibilityObject* coreObject, int& offset, bool ignoreLinks)
2612{
2613    // Indication that something bogus has transpired.
2614    offset = -1;
2615
2616    AccessibilityObject* realObject = coreObject;
2617    if (realObject->accessibilityIsIgnored())
2618        realObject = realObject->parentObjectUnignored();
2619    if (!realObject)
2620        return 0;
2621
2622    if (ignoreLinks && realObject->isLink())
2623        realObject = realObject->parentObjectUnignored();
2624    if (!realObject)
2625        return 0;
2626
2627    Node* node = realObject->node();
2628    if (node) {
2629        VisiblePosition startPosition = VisiblePosition(positionBeforeNode(node), DOWNSTREAM);
2630        VisiblePosition endPosition = realObject->selection().visibleEnd();
2631
2632        if (startPosition == endPosition)
2633            offset = 0;
2634        else if (!isStartOfLine(endPosition)) {
2635            RefPtr<Range> range = makeRange(startPosition, endPosition.previous());
2636            offset = TextIterator::rangeLength(range.get(), true) + 1;
2637        } else {
2638            RefPtr<Range> range = makeRange(startPosition, endPosition);
2639            offset = TextIterator::rangeLength(range.get(), true);
2640        }
2641
2642    }
2643
2644    return realObject;
2645}
2646
2647#endif // HAVE(ACCESSIBILITY)
2648