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