WebKitAccessibleWrapperAtk.cpp [plain text]
#include "config.h"
#include "WebKitAccessibleWrapperAtk.h"
#if HAVE(ACCESSIBILITY)
#include "AXObjectCache.h"
#include "Document.h"
#include "Frame.h"
#include "FrameView.h"
#include "HTMLNames.h"
#include "HTMLTableElement.h"
#include "HostWindow.h"
#include "RenderObject.h"
#include "Settings.h"
#include "TextIterator.h"
#include "WebKitAccessibleHyperlink.h"
#include "WebKitAccessibleInterfaceAction.h"
#include "WebKitAccessibleInterfaceComponent.h"
#include "WebKitAccessibleInterfaceDocument.h"
#include "WebKitAccessibleInterfaceEditableText.h"
#include "WebKitAccessibleInterfaceHyperlinkImpl.h"
#include "WebKitAccessibleInterfaceHypertext.h"
#include "WebKitAccessibleInterfaceImage.h"
#include "WebKitAccessibleInterfaceSelection.h"
#include "WebKitAccessibleInterfaceTable.h"
#include "WebKitAccessibleInterfaceText.h"
#include "WebKitAccessibleInterfaceValue.h"
#include "WebKitAccessibleUtil.h"
#include "htmlediting.h"
#include "visible_units.h"
#include <glib/gprintf.h>
#include <gtk/gtk.h>
using namespace WebCore;
static AccessibilityObject* fallbackObject()
{
static AXObjectCache* fallbackCache = new AXObjectCache(0);
static AccessibilityObject* object = 0;
if (!object) {
object = fallbackCache->getOrCreate(ListBoxOptionRole);
object->ref();
}
return object;
}
static AccessibilityObject* core(WebKitAccessible* accessible)
{
if (!accessible)
return 0;
return accessible->m_object;
}
static AccessibilityObject* core(AtkObject* object)
{
if (!WEBKIT_IS_ACCESSIBLE(object))
return 0;
return core(WEBKIT_ACCESSIBLE(object));
}
static const gchar* webkitAccessibleGetName(AtkObject* object)
{
AccessibilityObject* coreObject = core(object);
if (!coreObject->isAccessibilityRenderObject())
return returnString(coreObject->stringValue());
if (coreObject->isControl()) {
AccessibilityObject* label = coreObject->correspondingLabelForControlElement();
if (label) {
AtkObject* atkObject = label->wrapper();
if (ATK_IS_TEXT(atkObject))
return atk_text_get_text(ATK_TEXT(atkObject), 0, -1);
}
String textUnder = coreObject->textUnderElement();
if (textUnder.length())
return returnString(textUnder);
}
if (coreObject->isImage() || coreObject->isInputImage()) {
Node* node = coreObject->node();
if (node && node->isHTMLElement()) {
String alt = toHTMLElement(node)->getAttribute(HTMLNames::altAttr);
if (!alt.isEmpty())
return returnString(alt);
}
}
if (coreObject->isWebArea()) {
Document* document = coreObject->document();
if (document)
return returnString(document->title());
}
String axTitle = coreObject->title();
if (!axTitle.isEmpty())
return returnString(axTitle);
return returnString(coreObject->stringValue());
}
static const gchar* webkitAccessibleGetDescription(AtkObject* object)
{
AccessibilityObject* coreObject = core(object);
Node* node = 0;
if (coreObject->isAccessibilityRenderObject())
node = coreObject->node();
if (!node || !node->isHTMLElement() || coreObject->ariaRoleAttribute() != UnknownRole)
return returnString(coreObject->accessibilityDescription());
if (coreObject->roleValue() == TableRole) {
String summary = static_cast<HTMLTableElement*>(node)->summary();
if (!summary.isEmpty())
return returnString(summary);
}
String title = toHTMLElement(node)->title();
if (!title.isEmpty())
return returnString(title);
return returnString(coreObject->accessibilityDescription());
}
static void setAtkRelationSetFromCoreObject(AccessibilityObject* coreObject, AtkRelationSet* relationSet)
{
if (coreObject->isControl()) {
AccessibilityObject* label = coreObject->correspondingLabelForControlElement();
if (label)
atk_relation_set_add_relation_by_type(relationSet, ATK_RELATION_LABELLED_BY, label->wrapper());
} else {
AccessibilityObject* control = coreObject->correspondingControlForLabelElement();
if (control)
atk_relation_set_add_relation_by_type(relationSet, ATK_RELATION_LABEL_FOR, control->wrapper());
}
}
static gpointer webkit_accessible_parent_class = 0;
static bool isRootObject(AccessibilityObject* coreObject)
{
if (!coreObject || !coreObject->isScrollView())
return false;
AccessibilityObject* firstChild = coreObject->firstChild();
if (!firstChild || !firstChild->isWebArea())
return false;
return true;
}
static AtkObject* atkParentOfRootObject(AtkObject* object)
{
AccessibilityObject* coreObject = core(object);
AccessibilityObject* coreParent = coreObject->parentObjectUnignored();
if (!coreParent && isRootObject(coreObject)) {
Document* document = coreObject->document();
if (!document)
return 0;
HostWindow* hostWindow = document->view()->hostWindow();
if (hostWindow) {
PlatformPageClient scrollView = hostWindow->platformPageClient();
if (scrollView) {
GtkWidget* scrollViewParent = gtk_widget_get_parent(scrollView);
if (scrollViewParent)
return gtk_widget_get_accessible(scrollViewParent);
}
}
}
if (!coreParent)
return 0;
return coreParent->wrapper();
}
static AtkObject* webkitAccessibleGetParent(AtkObject* object)
{
AtkObject* accessibleParent = ATK_OBJECT_CLASS(webkit_accessible_parent_class)->get_parent(object);
if (accessibleParent)
return accessibleParent;
AccessibilityObject* coreObject = core(object);
if (!coreObject)
return 0;
AccessibilityObject* coreParent = coreObject->parentObjectUnignored();
if (!coreParent && isRootObject(coreObject))
return atkParentOfRootObject(object);
if (!coreParent)
return 0;
if (coreParent->isTableRow() && coreObject->isTableCell())
coreParent = coreParent->parentObjectUnignored();
return coreParent->wrapper();
}
static gint getNChildrenForTable(AccessibilityObject* coreObject)
{
AccessibilityObject::AccessibilityChildrenVector tableChildren = coreObject->children();
size_t tableChildrenCount = tableChildren.size();
size_t cellsCount = 0;
for (unsigned i = 0; i < tableChildrenCount; ++i) {
if (tableChildren[i]->isTableRow()) {
AccessibilityObject::AccessibilityChildrenVector rowChildren = tableChildren[i]->children();
cellsCount += rowChildren.size();
} else
cellsCount++;
}
return cellsCount;
}
static gint webkitAccessibleGetNChildren(AtkObject* object)
{
AccessibilityObject* coreObject = core(object);
if (coreObject->isAccessibilityTable())
return getNChildrenForTable(coreObject);
return coreObject->children().size();
}
static AccessibilityObject* getChildForTable(AccessibilityObject* coreObject, gint index)
{
AccessibilityObject::AccessibilityChildrenVector tableChildren = coreObject->children();
size_t tableChildrenCount = tableChildren.size();
size_t cellsCount = 0;
size_t current = static_cast<size_t>(index);
for (unsigned i = 0; i < tableChildrenCount; ++i) {
if (tableChildren[i]->isTableRow()) {
AccessibilityObject::AccessibilityChildrenVector rowChildren = tableChildren[i]->children();
size_t rowChildrenCount = rowChildren.size();
if (current < cellsCount + rowChildrenCount)
return rowChildren.at(current - cellsCount).get();
cellsCount += rowChildrenCount;
} else if (cellsCount == current)
return tableChildren[i].get();
else
cellsCount++;
}
return 0;
}
static AtkObject* webkitAccessibleRefChild(AtkObject* object, gint index)
{
if (index < 0)
return 0;
AccessibilityObject* coreObject = core(object);
AccessibilityObject* coreChild = 0;
if (coreObject->isAccessibilityTable())
coreChild = getChildForTable(coreObject, index);
else {
AccessibilityObject::AccessibilityChildrenVector children = coreObject->children();
if (static_cast<unsigned>(index) >= children.size())
return 0;
coreChild = children.at(index).get();
}
if (!coreChild)
return 0;
AtkObject* child = coreChild->wrapper();
atk_object_set_parent(child, object);
g_object_ref(child);
return child;
}
static gint getIndexInParentForCellInRow(AccessibilityObject* coreObject)
{
AccessibilityObject* parent = coreObject->parentObjectUnignored();
if (!parent)
return -1;
AccessibilityObject* grandParent = parent->parentObjectUnignored();
if (!grandParent)
return -1;
AccessibilityObject::AccessibilityChildrenVector rows = grandParent->children();
size_t rowsCount = rows.size();
size_t previousCellsCount = 0;
for (unsigned i = 0; i < rowsCount; ++i) {
if (!rows[i]->isTableRow())
continue;
AccessibilityObject::AccessibilityChildrenVector cells = rows[i]->children();
size_t cellsCount = cells.size();
if (rows[i] == parent) {
for (unsigned j = 0; j < cellsCount; ++j) {
if (cells[j] == coreObject)
return previousCellsCount + j;
}
}
previousCellsCount += cellsCount;
}
return -1;
}
static gint webkitAccessibleGetIndexInParent(AtkObject* object)
{
AccessibilityObject* coreObject = core(object);
AccessibilityObject* parent = coreObject->parentObjectUnignored();
if (!parent && isRootObject(coreObject)) {
AtkObject* atkParent = atkParentOfRootObject(object);
if (!atkParent)
return -1;
unsigned count = atk_object_get_n_accessible_children(atkParent);
for (unsigned i = 0; i < count; ++i) {
AtkObject* child = atk_object_ref_accessible_child(atkParent, i);
bool childIsObject = child == object;
g_object_unref(child);
if (childIsObject)
return i;
}
}
if (parent && parent->isTableRow() && coreObject->isTableCell())
return getIndexInParentForCellInRow(coreObject);
size_t index = parent->children().find(coreObject);
return (index == WTF::notFound) ? -1 : index;
}
static AtkAttributeSet* webkitAccessibleGetAttributes(AtkObject* object)
{
AtkAttributeSet* attributeSet = 0;
attributeSet = addToAtkAttributeSet(attributeSet, "toolkit", "WebKitGtk");
AccessibilityObject* coreObject = core(object);
if (!coreObject)
return attributeSet;
int headingLevel = coreObject->headingLevel();
if (headingLevel) {
String value = String::number(headingLevel);
attributeSet = addToAtkAttributeSet(attributeSet, "level", value.utf8().data());
}
if (coreObject->isAccessibilityTable() && !coreObject->isDataTable())
attributeSet = addToAtkAttributeSet(attributeSet, "layout-guess", "true");
return attributeSet;
}
static AtkRole atkRole(AccessibilityRole role)
{
switch (role) {
case UnknownRole:
return ATK_ROLE_UNKNOWN;
case ButtonRole:
return ATK_ROLE_PUSH_BUTTON;
case RadioButtonRole:
return ATK_ROLE_RADIO_BUTTON;
case CheckBoxRole:
return ATK_ROLE_CHECK_BOX;
case SliderRole:
return ATK_ROLE_SLIDER;
case TabGroupRole:
return ATK_ROLE_PAGE_TAB_LIST;
case TextFieldRole:
case TextAreaRole:
return ATK_ROLE_ENTRY;
case StaticTextRole:
return ATK_ROLE_TEXT;
case OutlineRole:
return ATK_ROLE_TREE;
case MenuBarRole:
return ATK_ROLE_MENU_BAR;
case MenuListPopupRole:
case MenuRole:
return ATK_ROLE_MENU;
case MenuListOptionRole:
case MenuItemRole:
return ATK_ROLE_MENU_ITEM;
case ColumnRole:
return ATK_ROLE_UNKNOWN; case RowRole:
return ATK_ROLE_LIST_ITEM; case ToolbarRole:
return ATK_ROLE_TOOL_BAR;
case BusyIndicatorRole:
return ATK_ROLE_PROGRESS_BAR; case ProgressIndicatorRole:
return ATK_ROLE_PROGRESS_BAR;
case WindowRole:
return ATK_ROLE_WINDOW;
case PopUpButtonRole:
case ComboBoxRole:
return ATK_ROLE_COMBO_BOX;
case SplitGroupRole:
return ATK_ROLE_SPLIT_PANE;
case SplitterRole:
return ATK_ROLE_SEPARATOR;
case ColorWellRole:
return ATK_ROLE_COLOR_CHOOSER;
case ListRole:
return ATK_ROLE_LIST;
case ScrollBarRole:
return ATK_ROLE_SCROLL_BAR;
case ScrollAreaRole:
return ATK_ROLE_SCROLL_PANE;
case GridRole: case TableRole:
return ATK_ROLE_TABLE;
case ApplicationRole:
return ATK_ROLE_APPLICATION;
case GroupRole:
case RadioGroupRole:
return ATK_ROLE_PANEL;
case RowHeaderRole: case ColumnHeaderRole: case CellRole:
return ATK_ROLE_TABLE_CELL;
case LinkRole:
case WebCoreLinkRole:
case ImageMapLinkRole:
return ATK_ROLE_LINK;
case ImageMapRole:
case ImageRole:
return ATK_ROLE_IMAGE;
case ListMarkerRole:
return ATK_ROLE_TEXT;
case WebAreaRole:
return ATK_ROLE_DOCUMENT_FRAME;
case HeadingRole:
return ATK_ROLE_HEADING;
case ListBoxRole:
return ATK_ROLE_LIST;
case ListItemRole:
case ListBoxOptionRole:
return ATK_ROLE_LIST_ITEM;
case ParagraphRole:
return ATK_ROLE_PARAGRAPH;
case LabelRole:
return ATK_ROLE_LABEL;
case DivRole:
return ATK_ROLE_SECTION;
case FormRole:
return ATK_ROLE_FORM;
default:
return ATK_ROLE_UNKNOWN;
}
}
static AtkRole webkitAccessibleGetRole(AtkObject* object)
{
AccessibilityObject* coreObject = core(object);
if (!coreObject)
return ATK_ROLE_UNKNOWN;
if (coreObject->isPasswordField())
return ATK_ROLE_PASSWORD_TEXT;
return atkRole(coreObject->roleValue());
}
static bool isTextWithCaret(AccessibilityObject* coreObject)
{
if (!coreObject || !coreObject->isAccessibilityRenderObject())
return false;
Document* document = coreObject->document();
if (!document)
return false;
Frame* frame = document->frame();
if (!frame)
return false;
Settings* settings = frame->settings();
if (!settings || !settings->caretBrowsingEnabled())
return false;
AtkObject* axObject = coreObject->wrapper();
AtkRole role = axObject ? atk_object_get_role(axObject) : ATK_ROLE_INVALID;
if (role != ATK_ROLE_TEXT && role != ATK_ROLE_PARAGRAPH)
return false;
VisibleSelection selection = coreObject->selection();
if (!selection.isCaret())
return false;
return selectionBelongsToObject(coreObject, selection);
}
static void setAtkStateSetFromCoreObject(AccessibilityObject* coreObject, AtkStateSet* stateSet)
{
AccessibilityObject* parent = coreObject->parentObject();
bool isListBoxOption = parent && parent->isListBox();
if (coreObject->isChecked())
atk_state_set_add_state(stateSet, ATK_STATE_CHECKED);
if ((!coreObject->isReadOnly()
|| (coreObject->isControl() && coreObject->canSetValueAttribute()))
&& !isListBoxOption)
atk_state_set_add_state(stateSet, ATK_STATE_EDITABLE);
if (coreObject->isEnabled()) {
atk_state_set_add_state(stateSet, ATK_STATE_ENABLED);
atk_state_set_add_state(stateSet, ATK_STATE_SENSITIVE);
}
if (coreObject->canSetExpandedAttribute())
atk_state_set_add_state(stateSet, ATK_STATE_EXPANDABLE);
if (coreObject->isExpanded())
atk_state_set_add_state(stateSet, ATK_STATE_EXPANDED);
if (coreObject->canSetFocusAttribute())
atk_state_set_add_state(stateSet, ATK_STATE_FOCUSABLE);
if (coreObject->isFocused() || isTextWithCaret(coreObject))
atk_state_set_add_state(stateSet, ATK_STATE_FOCUSED);
if (coreObject->isIndeterminate())
atk_state_set_add_state(stateSet, ATK_STATE_INDETERMINATE);
if (coreObject->isMultiSelectable())
atk_state_set_add_state(stateSet, ATK_STATE_MULTISELECTABLE);
if (coreObject->isPressed())
atk_state_set_add_state(stateSet, ATK_STATE_PRESSED);
if (coreObject->canSetSelectedAttribute()) {
atk_state_set_add_state(stateSet, ATK_STATE_SELECTABLE);
if (isListBoxOption)
atk_state_set_add_state(stateSet, ATK_STATE_FOCUSABLE);
}
if (coreObject->isSelected()) {
atk_state_set_add_state(stateSet, ATK_STATE_SELECTED);
if (isListBoxOption)
atk_state_set_add_state(stateSet, ATK_STATE_FOCUSED);
}
if (!coreObject->isOffScreen()) {
atk_state_set_add_state(stateSet, ATK_STATE_SHOWING);
atk_state_set_add_state(stateSet, ATK_STATE_VISIBLE);
}
if (coreObject->roleValue() == TextFieldRole)
atk_state_set_add_state(stateSet, ATK_STATE_SINGLE_LINE);
else if (coreObject->roleValue() == TextAreaRole)
atk_state_set_add_state(stateSet, ATK_STATE_MULTI_LINE);
if (coreObject->isVisited())
atk_state_set_add_state(stateSet, ATK_STATE_VISITED);
}
static AtkStateSet* webkitAccessibleRefStateSet(AtkObject* object)
{
AtkStateSet* stateSet = ATK_OBJECT_CLASS(webkit_accessible_parent_class)->ref_state_set(object);
AccessibilityObject* coreObject = core(object);
if (coreObject == fallbackObject()) {
atk_state_set_add_state(stateSet, ATK_STATE_DEFUNCT);
return stateSet;
}
AtkRole role = atk_object_get_role(object);
if (role == ATK_ROLE_TEXT || role == ATK_ROLE_PARAGRAPH)
atk_state_set_add_state(stateSet, ATK_STATE_FOCUSABLE);
setAtkStateSetFromCoreObject(coreObject, stateSet);
return stateSet;
}
static AtkRelationSet* webkitAccessibleRefRelationSet(AtkObject* object)
{
AtkRelationSet* relationSet = ATK_OBJECT_CLASS(webkit_accessible_parent_class)->ref_relation_set(object);
AccessibilityObject* coreObject = core(object);
setAtkRelationSetFromCoreObject(coreObject, relationSet);
return relationSet;
}
static void webkitAccessibleInit(AtkObject* object, gpointer data)
{
if (ATK_OBJECT_CLASS(webkit_accessible_parent_class)->initialize)
ATK_OBJECT_CLASS(webkit_accessible_parent_class)->initialize(object, data);
WEBKIT_ACCESSIBLE(object)->m_object = reinterpret_cast<AccessibilityObject*>(data);
}
static void webkitAccessibleFinalize(GObject* object)
{
returnString(String());
G_OBJECT_CLASS(webkit_accessible_parent_class)->finalize(object);
}
static void webkit_accessible_class_init(AtkObjectClass* klass)
{
GObjectClass* gobjectClass = G_OBJECT_CLASS(klass);
webkit_accessible_parent_class = g_type_class_peek_parent(klass);
gobjectClass->finalize = webkitAccessibleFinalize;
klass->initialize = webkitAccessibleInit;
klass->get_name = webkitAccessibleGetName;
klass->get_description = webkitAccessibleGetDescription;
klass->get_parent = webkitAccessibleGetParent;
klass->get_n_children = webkitAccessibleGetNChildren;
klass->ref_child = webkitAccessibleRefChild;
klass->get_role = webkitAccessibleGetRole;
klass->ref_state_set = webkitAccessibleRefStateSet;
klass->get_index_in_parent = webkitAccessibleGetIndexInParent;
klass->get_attributes = webkitAccessibleGetAttributes;
klass->ref_relation_set = webkitAccessibleRefRelationSet;
}
GType
webkit_accessible_get_type(void)
{
static volatile gsize type_volatile = 0;
if (g_once_init_enter(&type_volatile)) {
static const GTypeInfo tinfo = {
sizeof(WebKitAccessibleClass),
(GBaseInitFunc) 0,
(GBaseFinalizeFunc) 0,
(GClassInitFunc) webkit_accessible_class_init,
(GClassFinalizeFunc) 0,
0,
sizeof(WebKitAccessible),
0,
(GInstanceInitFunc) 0,
0
};
GType type = g_type_register_static(ATK_TYPE_OBJECT,
"WebKitAccessible", &tinfo, GTypeFlags(0));
g_once_init_leave(&type_volatile, type);
}
return type_volatile;
}
static const GInterfaceInfo AtkInterfacesInitFunctions[] = {
{reinterpret_cast<GInterfaceInitFunc>(webkitAccessibleActionInterfaceInit), 0, 0},
{reinterpret_cast<GInterfaceInitFunc>(webkitAccessibleSelectionInterfaceInit), 0, 0},
{reinterpret_cast<GInterfaceInitFunc>(webkitAccessibleEditableTextInterfaceInit), 0, 0},
{reinterpret_cast<GInterfaceInitFunc>(webkitAccessibleTextInterfaceInit), 0, 0},
{reinterpret_cast<GInterfaceInitFunc>(webkitAccessibleComponentInterfaceInit), 0, 0},
{reinterpret_cast<GInterfaceInitFunc>(webkitAccessibleImageInterfaceInit), 0, 0},
{reinterpret_cast<GInterfaceInitFunc>(webkitAccessibleTableInterfaceInit), 0, 0},
{reinterpret_cast<GInterfaceInitFunc>(webkitAccessibleHypertextInterfaceInit), 0, 0},
{reinterpret_cast<GInterfaceInitFunc>(webkitAccessibleHyperlinkImplInterfaceInit), 0, 0},
{reinterpret_cast<GInterfaceInitFunc>(webkitAccessibleDocumentInterfaceInit), 0, 0},
{reinterpret_cast<GInterfaceInitFunc>(webkitAccessibleValueInterfaceInit), 0, 0}
};
enum WAIType {
WAI_ACTION,
WAI_SELECTION,
WAI_EDITABLE_TEXT,
WAI_TEXT,
WAI_COMPONENT,
WAI_IMAGE,
WAI_TABLE,
WAI_HYPERTEXT,
WAI_HYPERLINK,
WAI_DOCUMENT,
WAI_VALUE,
};
static GType GetAtkInterfaceTypeFromWAIType(WAIType type)
{
switch (type) {
case WAI_ACTION:
return ATK_TYPE_ACTION;
case WAI_SELECTION:
return ATK_TYPE_SELECTION;
case WAI_EDITABLE_TEXT:
return ATK_TYPE_EDITABLE_TEXT;
case WAI_TEXT:
return ATK_TYPE_TEXT;
case WAI_COMPONENT:
return ATK_TYPE_COMPONENT;
case WAI_IMAGE:
return ATK_TYPE_IMAGE;
case WAI_TABLE:
return ATK_TYPE_TABLE;
case WAI_HYPERTEXT:
return ATK_TYPE_HYPERTEXT;
case WAI_HYPERLINK:
return ATK_TYPE_HYPERLINK_IMPL;
case WAI_DOCUMENT:
return ATK_TYPE_DOCUMENT;
case WAI_VALUE:
return ATK_TYPE_VALUE;
}
return G_TYPE_INVALID;
}
static guint16 getInterfaceMaskFromObject(AccessibilityObject* coreObject)
{
guint16 interfaceMask = 0;
interfaceMask |= 1 << WAI_COMPONENT;
AccessibilityRole role = coreObject->roleValue();
interfaceMask |= 1 << WAI_ACTION;
if (coreObject->isListBox() || coreObject->isMenuList())
interfaceMask |= 1 << WAI_SELECTION;
RenderObject* renderer = 0;
if (coreObject->isAccessibilityRenderObject())
renderer = coreObject->renderer();
if (coreObject->isLink() || (renderer && renderer->isReplaced()))
interfaceMask |= 1 << WAI_HYPERLINK;
if (role == StaticTextRole || coreObject->isMenuListOption())
interfaceMask |= 1 << WAI_TEXT;
else {
if (coreObject->isTextControl()) {
interfaceMask |= 1 << WAI_TEXT;
if (!coreObject->isReadOnly())
interfaceMask |= 1 << WAI_EDITABLE_TEXT;
} else {
if (role != TableRole) {
interfaceMask |= 1 << WAI_HYPERTEXT;
if (renderer && renderer->childrenInline())
interfaceMask |= 1 << WAI_TEXT;
}
if (role == ListItemRole) {
AccessibilityObject::AccessibilityChildrenVector children = coreObject->children();
if (children.size()) {
AccessibilityObject* axRenderChild = children.at(0).get();
interfaceMask |= getInterfaceMaskFromObject(axRenderChild);
}
}
}
}
if (coreObject->isImage())
interfaceMask |= 1 << WAI_IMAGE;
if (role == TableRole)
interfaceMask |= 1 << WAI_TABLE;
if (role == WebAreaRole)
interfaceMask |= 1 << WAI_DOCUMENT;
if (role == SliderRole)
interfaceMask |= 1 << WAI_VALUE;
return interfaceMask;
}
static const char* getUniqueAccessibilityTypeName(guint16 interfaceMask)
{
#define WAI_TYPE_NAME_LEN (30)
static char name[WAI_TYPE_NAME_LEN + 1];
g_sprintf(name, "WAIType%x", interfaceMask);
name[WAI_TYPE_NAME_LEN] = '\0';
return name;
}
static GType getAccessibilityTypeFromObject(AccessibilityObject* coreObject)
{
static const GTypeInfo typeInfo = {
sizeof(WebKitAccessibleClass),
(GBaseInitFunc) 0,
(GBaseFinalizeFunc) 0,
(GClassInitFunc) 0,
(GClassFinalizeFunc) 0,
0,
sizeof(WebKitAccessible),
0,
(GInstanceInitFunc) 0,
0
};
guint16 interfaceMask = getInterfaceMaskFromObject(coreObject);
const char* atkTypeName = getUniqueAccessibilityTypeName(interfaceMask);
GType type = g_type_from_name(atkTypeName);
if (type)
return type;
type = g_type_register_static(WEBKIT_TYPE_ACCESSIBLE,
atkTypeName,
&typeInfo, GTypeFlags(0));
for (guint i = 0; i < G_N_ELEMENTS(AtkInterfacesInitFunctions); i++) {
if (interfaceMask & (1 << i))
g_type_add_interface_static(type,
GetAtkInterfaceTypeFromWAIType(static_cast<WAIType>(i)),
&AtkInterfacesInitFunctions[i]);
}
return type;
}
WebKitAccessible* webkitAccessibleNew(AccessibilityObject* coreObject)
{
GType type = getAccessibilityTypeFromObject(coreObject);
AtkObject* object = static_cast<AtkObject*>(g_object_new(type, 0));
atk_object_initialize(object, coreObject);
return WEBKIT_ACCESSIBLE(object);
}
AccessibilityObject* webkitAccessibleGetAccessibilityObject(WebKitAccessible* accessible)
{
return accessible->m_object;
}
void webkitAccessibleDetach(WebKitAccessible* accessible)
{
ASSERT(accessible->m_object);
if (core(accessible)->roleValue() == WebAreaRole)
g_signal_emit_by_name(accessible, "state-change", "defunct", true);
accessible->m_object = fallbackObject();
}
AtkObject* webkitAccessibleGetFocusedElement(WebKitAccessible* accessible)
{
if (!accessible->m_object)
return 0;
RefPtr<AccessibilityObject> focusedObj = accessible->m_object->focusedUIElement();
if (!focusedObj)
return 0;
return focusedObj->wrapper();
}
AccessibilityObject* objectFocusedAndCaretOffsetUnignored(AccessibilityObject* referenceObject, int& offset)
{
offset = -1;
Document* document = referenceObject->document();
if (!document)
return 0;
Node* focusedNode = referenceObject->selection().end().containerNode();
if (!focusedNode)
return 0;
RenderObject* focusedRenderer = focusedNode->renderer();
if (!focusedRenderer)
return 0;
AccessibilityObject* focusedObject = document->axObjectCache()->getOrCreate(focusedRenderer);
if (!focusedObject)
return 0;
AccessibilityObject* firstUnignoredParent = focusedObject;
if (firstUnignoredParent->accessibilityIsIgnored())
firstUnignoredParent = firstUnignoredParent->parentObjectUnignored();
if (!firstUnignoredParent)
return 0;
if (!referenceObject->isLink() && firstUnignoredParent->isLink())
firstUnignoredParent = firstUnignoredParent->parentObjectUnignored();
if (!firstUnignoredParent)
return 0;
if (referenceObject->isDescendantOfObject(firstUnignoredParent))
referenceObject = firstUnignoredParent;
Node* startNode = 0;
if (firstUnignoredParent != referenceObject || firstUnignoredParent->isTextControl()) {
AccessibilityObject* axFirstChild = referenceObject->firstChild();
if (axFirstChild)
startNode = axFirstChild->node();
}
if (!startNode)
startNode = firstUnignoredParent->node();
if (!startNode)
return 0;
VisiblePosition startPosition = VisiblePosition(positionBeforeNode(startNode), DOWNSTREAM);
VisiblePosition endPosition = firstUnignoredParent->selection().visibleEnd();
if (startPosition == endPosition)
offset = 0;
else if (!isStartOfLine(endPosition)) {
RefPtr<Range> range = makeRange(startPosition, endPosition.previous());
offset = TextIterator::rangeLength(range.get(), true) + 1;
} else {
RefPtr<Range> range = makeRange(startPosition, endPosition);
offset = TextIterator::rangeLength(range.get(), true);
}
return firstUnignoredParent;
}
#endif // HAVE(ACCESSIBILITY)