PageRenderTime 142ms CodeModel.GetById 21ms app.highlight 109ms RepoModel.GetById 2ms app.codeStats 0ms

/platform/external/webkit/WebCore/accessibility/gtk/AccessibilityObjectWrapperAtk.cpp

https://github.com/aharish/totoro-gb-opensource-update2
C++ | 1786 lines | 1368 code | 276 blank | 142 comment | 227 complexity | 90bd1925842b4b0d19183d39805e95a1 MD5 | raw file

Large files files are truncated, but you can click here to view the full file

   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
  67using namespace WebCore;
  68
  69static 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.
  83static const char* returnString(const String& str)
  84{
  85    static CString returnedString;
  86    returnedString = str.utf8();
  87    return returnedString.data();
  88}
  89
  90static AccessibilityObject* core(WebKitAccessible* accessible)
  91{
  92    if (!accessible)
  93        return 0;
  94
  95    return accessible->m_object;
  96}
  97
  98static AccessibilityObject* core(AtkObject* object)
  99{
 100    if (!WEBKIT_IS_ACCESSIBLE(object))
 101        return 0;
 102
 103    return core(WEBKIT_ACCESSIBLE(object));
 104}
 105
 106static AccessibilityObject* core(AtkAction* action)
 107{
 108    return core(ATK_OBJECT(action));
 109}
 110
 111static AccessibilityObject* core(AtkSelection* selection)
 112{
 113    return core(ATK_OBJECT(selection));
 114}
 115
 116static AccessibilityObject* core(AtkText* text)
 117{
 118    return core(ATK_OBJECT(text));
 119}
 120
 121static AccessibilityObject* core(AtkEditableText* text)
 122{
 123    return core(ATK_OBJECT(text));
 124}
 125
 126static AccessibilityObject* core(AtkComponent* component)
 127{
 128    return core(ATK_OBJECT(component));
 129}
 130
 131static AccessibilityObject* core(AtkImage* image)
 132{
 133    return core(ATK_OBJECT(image));
 134}
 135
 136static AccessibilityObject* core(AtkTable* table)
 137{
 138    return core(ATK_OBJECT(table));
 139}
 140
 141static AccessibilityObject* core(AtkDocument* document)
 142{
 143    return core(ATK_OBJECT(document));
 144}
 145
 146static 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
 159static 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
 185static 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
 210static 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
 224static gpointer webkit_accessible_parent_class = 0;
 225
 226static 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
 251static 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
 263static gint webkit_accessible_get_n_children(AtkObject* object)
 264{
 265    return core(object)->children().size();
 266}
 267
 268static 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
 287static 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
 317static 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
 327static 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
 339static 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
 427static 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
 461static 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
 547static 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
 562static 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
 572static 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
 580static 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
 588static 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
 609GType
 610webkit_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
 636static 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
 642static gint webkit_accessible_action_get_n_actions(AtkAction* action)
 643{
 644    return 1;
 645}
 646
 647static 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
 655static 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
 662static 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
 668static 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
 679static 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
 692static 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
 712static 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
 724static 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
 741static 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
 753static 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
 765static 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
 774static 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
 787static 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
 805static 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
 818static 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.
 832static 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
 861gchar* 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
 912static 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
 940static 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
 952static 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
 973static 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
 978static 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
 983static 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
 988static gunichar webkit_accessible_text_get_character_at_offset(AtkText* text, gint offset)
 989{
 990    notImplemented();
 991    return 0;
 992}
 993
 994static 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
1010static 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
1016static AtkAttributeSet* webkit_accessible_text_get_default_attributes(AtkText* text)
1017{
1018    notImplemented();
1019    return 0;
1020}
1021
1022static 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
1045static 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
1055static 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
1064static 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
1073static 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
1087static 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
1108static gboolean webkit_accessible_text_add_selection(AtkText* text, gint start_offset, gint end_offset)
1109{
1110    notImplemented();
1111    return FALSE;
1112}
1113
1114static gboolean webkit_accessible_text_remove_selection(AtkText* text, gint selection_num)
1115{
1116    notImplemented();
1117    return FALSE;
1118}
1119
1120static 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
1126static 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
1143static 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
1168static 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
1174static 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
1180static 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
1198static void webkit_accessible_editable_text_copy_text(AtkEditableText* text, gint start_pos, gint end_pos)
1199{
1200    notImplemented();
1201}
1202
1203static void webkit_accessible_editable_text_cut_text(AtkEditableText* text, gint start_pos, gint end_pos)
1204{
1205    notImplemented();
1206}
1207
1208static 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
1222static void webkit_accessible_editable_text_paste_text(AtkEditableText* text, gint position)
1223{
1224    notImplemented();
1225}
1226
1227static 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
1238static 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
1263static 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
1280static 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
1290static 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
1296static gboolean webkit_accessible_component_grab_focus(AtkComponent* component)
1297{
1298    core(component)->setFocused(true);
1299    return core(component)->isFocused();
1300}
1301
1302static 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
1311static 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
1317static const gchar* webkit_accessible_image_get_image_description(AtkImage* image)
1318{
1319    return returnString(core(image)->accessibilityDescription());
1320}
1321
1322static 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
1332static 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
1341static 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
1349static 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
1362static 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
1376static 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
1384static 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
1391static 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
1402static 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
1413static 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
1421static gint webkit_accessible_table_get_n_rows(AtkTable* table)
1422{
1423    AccessibilityObject* accTable = core(table);
1424    if (accTable->isAccessibilityRenderObject())
1425        return static_cast<Accessibil

Large files files are truncated, but you can click here to view the full file