blob: 3e233df71d17e2350e8d154e7cb627358539af01 [file] [log] [blame]
alp@webkit.org3a5e7ca2008-05-20 01:12:20 +00001/*
2 * Copyright (C) 2008 Nuanti Ltd.
jmalonzo@webkit.orgaaaea3b2009-08-08 07:39:48 +00003 * Copyright (C) 2009 Jan Alonzo
mario@webkit.org9adab662012-01-23 10:21:03 +00004 * Copyright (C) 2009, 2010, 2011, 2012 Igalia S.L.
mario@webkit.orgfecbacd2013-03-04 17:06:15 +00005 * Copyright (C) 2013 Samsung Electronics
xan@webkit.orge4387102009-04-09 11:08:48 +00006 *
7 * Portions from Mozilla a11y, copyright as follows:
8 *
9 * The Original Code is mozilla.org code.
10 *
11 * The Initial Developer of the Original Code is
12 * Sun Microsystems, Inc.
13 * Portions created by the Initial Developer are Copyright (C) 2002
14 * the Initial Developer. All Rights Reserved.
alp@webkit.org3a5e7ca2008-05-20 01:12:20 +000015 *
16 * This library is free software; you can redistribute it and/or
17 * modify it under the terms of the GNU Library General Public
18 * License as published by the Free Software Foundation; either
19 * version 2 of the License, or (at your option) any later version.
20 *
21 * This library is distributed in the hope that it will be useful,
22 * but WITHOUT ANY WARRANTY; without even the implied warranty of
23 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
24 * Library General Public License for more details.
25 *
26 * You should have received a copy of the GNU Library General Public License
27 * along with this library; see the file COPYING.LIB. If not, write to
28 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
29 * Boston, MA 02110-1301, USA.
30 */
31
32#include "config.h"
mario@webkit.org9adab662012-01-23 10:21:03 +000033#include "WebKitAccessibleWrapperAtk.h"
alp@webkit.org3a5e7ca2008-05-20 01:12:20 +000034
ddkilzer@apple.com8d878632008-11-09 19:50:37 +000035#if HAVE(ACCESSIBILITY)
36
alp@webkit.org3a5e7ca2008-05-20 01:12:20 +000037#include "AXObjectCache.h"
alp@webkit.orgc7738992008-05-27 02:48:14 +000038#include "Document.h"
alp@webkit.orgc7738992008-05-27 02:48:14 +000039#include "Frame.h"
40#include "FrameView.h"
xan@webkit.org9561b2c2009-05-20 14:33:19 +000041#include "HTMLNames.h"
xan@webkit.org45b26ac2009-10-27 12:20:35 +000042#include "HTMLTableElement.h"
mario@webkit.org9adab662012-01-23 10:21:03 +000043#include "HostWindow.h"
mario@webkit.org970eaf32012-01-24 18:37:45 +000044#include "RenderObject.h"
mario@webkit.org6a24ba12010-12-14 15:35:22 +000045#include "Settings.h"
commit-queue@webkit.org74bd2e82010-09-12 11:16:14 +000046#include "TextIterator.h"
tkent@chromium.org8c35c122013-03-06 13:00:14 +000047#include "VisibleUnits.h"
mario@webkit.org7f95c622010-11-01 15:05:36 +000048#include "WebKitAccessibleHyperlink.h"
mario@webkit.orgdeec8392012-01-23 14:45:23 +000049#include "WebKitAccessibleInterfaceAction.h"
mario@webkit.orgbe1ce552012-01-24 11:03:51 +000050#include "WebKitAccessibleInterfaceComponent.h"
mario@webkit.orgf8344ff2012-01-24 11:40:44 +000051#include "WebKitAccessibleInterfaceDocument.h"
mario@webkit.orgfc51ca62012-01-24 11:47:51 +000052#include "WebKitAccessibleInterfaceEditableText.h"
mario@webkit.org4dbd9822012-01-24 11:55:18 +000053#include "WebKitAccessibleInterfaceHyperlinkImpl.h"
mario@webkit.org70243532012-01-24 11:58:52 +000054#include "WebKitAccessibleInterfaceHypertext.h"
mario@webkit.orgda3e6082012-01-24 12:04:16 +000055#include "WebKitAccessibleInterfaceImage.h"
mario@webkit.org7e5931d2012-01-24 12:25:13 +000056#include "WebKitAccessibleInterfaceSelection.h"
mario@webkit.orgcd9f1b32012-01-24 18:28:22 +000057#include "WebKitAccessibleInterfaceTable.h"
mario@webkit.org987d7372012-01-24 18:02:08 +000058#include "WebKitAccessibleInterfaceText.h"
mario@webkit.org980269e2012-01-24 16:22:57 +000059#include "WebKitAccessibleInterfaceValue.h"
mario@webkit.org7ceffa42012-01-23 11:55:01 +000060#include "WebKitAccessibleUtil.h"
mario@webkit.orgba16aea2011-04-13 16:33:02 +000061#include "htmlediting.h"
xan@webkit.orge4387102009-04-09 11:08:48 +000062#include <glib/gprintf.h>
benjamin@webkit.org9d72cb02013-04-22 22:52:23 +000063#include <wtf/text/CString.h>
mario@webkit.org8c5dd902012-11-09 19:47:40 +000064
65#if PLATFORM(GTK)
mario@webkit.org970eaf32012-01-24 18:37:45 +000066#include <gtk/gtk.h>
mario@webkit.org8c5dd902012-11-09 19:47:40 +000067#endif
alp@webkit.org3a5e7ca2008-05-20 01:12:20 +000068
alp@webkit.org3a5e7ca2008-05-20 01:12:20 +000069using namespace WebCore;
70
mario@webkit.orgfecbacd2013-03-04 17:06:15 +000071struct _WebKitAccessiblePrivate {
72 // Cached data for AtkObject.
73 CString accessibleName;
74 CString accessibleDescription;
75
76 // Cached data for AtkAction.
77 CString actionName;
78 CString actionKeyBinding;
79
80 // Cached data for AtkDocument.
81 CString documentLocale;
82 CString documentType;
83 CString documentEncoding;
84 CString documentURI;
85
86 // Cached data for AtkImage.
87 CString imageDescription;
88};
89
90#define WEBKIT_ACCESSIBLE_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE((obj), WEBKIT_TYPE_ACCESSIBLE, WebKitAccessiblePrivate))
91
xan@webkit.orgc886cc62009-04-09 11:17:48 +000092static AccessibilityObject* fallbackObject()
93{
cfleizach@apple.com6bd30ef2011-01-05 01:36:57 +000094 // FIXME: An AXObjectCache with a Document is meaningless.
95 static AXObjectCache* fallbackCache = new AXObjectCache(0);
xan@webkit.orgc886cc62009-04-09 11:17:48 +000096 static AccessibilityObject* object = 0;
97 if (!object) {
98 // FIXME: using fallbackCache->getOrCreate(ListBoxOptionRole) is a hack
99 object = fallbackCache->getOrCreate(ListBoxOptionRole);
100 object->ref();
101 }
102
103 return object;
104}
105
alp@webkit.orgc7738992008-05-27 02:48:14 +0000106static AccessibilityObject* core(WebKitAccessible* accessible)
107{
108 if (!accessible)
109 return 0;
110
111 return accessible->m_object;
112}
113
114static AccessibilityObject* core(AtkObject* object)
115{
116 if (!WEBKIT_IS_ACCESSIBLE(object))
117 return 0;
118
119 return core(WEBKIT_ACCESSIBLE(object));
120}
121
mario@webkit.org5c966772012-01-24 18:48:50 +0000122static const gchar* webkitAccessibleGetName(AtkObject* object)
alp@webkit.org3a5e7ca2008-05-20 01:12:20 +0000123{
eric@webkit.org0f6cb452009-10-22 23:07:56 +0000124 AccessibilityObject* coreObject = core(object);
eric@webkit.org5a5bba52009-12-07 14:27:44 +0000125 if (!coreObject->isAccessibilityRenderObject())
mario@webkit.orgfecbacd2013-03-04 17:06:15 +0000126 return cacheAndReturnAtkProperty(object, AtkCachedAccessibleName, coreObject->stringValue());
eric@webkit.org5a5bba52009-12-07 14:27:44 +0000127
commit-queue@webkit.org802c0122012-09-11 01:41:27 +0000128 if (coreObject->isFieldset()) {
129 AccessibilityObject* label = coreObject->titleUIElement();
130 if (label) {
131 AtkObject* atkObject = label->wrapper();
132 if (ATK_IS_TEXT(atkObject))
133 return atk_text_get_text(ATK_TEXT(atkObject), 0, -1);
134 }
135 }
136
eric@webkit.org0f6cb452009-10-22 23:07:56 +0000137 if (coreObject->isControl()) {
mario@webkit.org7e9f2412011-04-06 16:50:25 +0000138 AccessibilityObject* label = coreObject->correspondingLabelForControlElement();
commit-queue@webkit.orgc5b95312010-06-24 03:18:17 +0000139 if (label) {
140 AtkObject* atkObject = label->wrapper();
141 if (ATK_IS_TEXT(atkObject))
mario@webkit.orgadc13a82012-01-24 21:18:36 +0000142 return atk_text_get_text(ATK_TEXT(atkObject), 0, -1);
commit-queue@webkit.orgc5b95312010-06-24 03:18:17 +0000143 }
commit-queue@webkit.orgee601242010-08-04 18:29:10 +0000144
mario@webkit.org92daa812011-02-16 17:12:38 +0000145 // Try text under the node.
mario@webkit.org7e9f2412011-04-06 16:50:25 +0000146 String textUnder = coreObject->textUnderElement();
commit-queue@webkit.orgee601242010-08-04 18:29:10 +0000147 if (textUnder.length())
mario@webkit.orgfecbacd2013-03-04 17:06:15 +0000148 return cacheAndReturnAtkProperty(object, AtkCachedAccessibleName, textUnder);
eric@webkit.org0f6cb452009-10-22 23:07:56 +0000149 }
eric@webkit.org5a5bba52009-12-07 14:27:44 +0000150
mario@webkit.org7e9f2412011-04-06 16:50:25 +0000151 if (coreObject->isImage() || coreObject->isInputImage()) {
152 Node* node = coreObject->node();
eric@webkit.org5a5bba52009-12-07 14:27:44 +0000153 if (node && node->isHTMLElement()) {
154 // Get the attribute rather than altText String so as not to fall back on title.
commit-queue@webkit.org03477c82011-09-02 17:07:51 +0000155 String alt = toHTMLElement(node)->getAttribute(HTMLNames::altAttr);
eric@webkit.org5a5bba52009-12-07 14:27:44 +0000156 if (!alt.isEmpty())
mario@webkit.orgfecbacd2013-03-04 17:06:15 +0000157 return cacheAndReturnAtkProperty(object, AtkCachedAccessibleName, alt);
eric@webkit.org5a5bba52009-12-07 14:27:44 +0000158 }
159 }
160
mario@webkit.org92daa812011-02-16 17:12:38 +0000161 // Fallback for the webArea object: just return the document's title.
mario@webkit.org7e9f2412011-04-06 16:50:25 +0000162 if (coreObject->isWebArea()) {
mario@webkit.org92daa812011-02-16 17:12:38 +0000163 Document* document = coreObject->document();
164 if (document)
mario@webkit.orgfecbacd2013-03-04 17:06:15 +0000165 return cacheAndReturnAtkProperty(object, AtkCachedAccessibleName, document->title());
mario@webkit.org92daa812011-02-16 17:12:38 +0000166 }
167
mario@webkit.orgd684f512011-11-29 11:00:58 +0000168 // Nothing worked so far, try with the AccessibilityObject's
169 // title() before going ahead with stringValue().
mario@webkit.org99e78ca2012-10-22 17:32:52 +0000170 String axTitle = accessibilityTitle(coreObject);
mario@webkit.orgd684f512011-11-29 11:00:58 +0000171 if (!axTitle.isEmpty())
mario@webkit.orgfecbacd2013-03-04 17:06:15 +0000172 return cacheAndReturnAtkProperty(object, AtkCachedAccessibleName, axTitle);
mario@webkit.orgd684f512011-11-29 11:00:58 +0000173
mario@webkit.orgfecbacd2013-03-04 17:06:15 +0000174 return cacheAndReturnAtkProperty(object, AtkCachedAccessibleName, coreObject->stringValue());
alp@webkit.org3a5e7ca2008-05-20 01:12:20 +0000175}
176
mario@webkit.org5c966772012-01-24 18:48:50 +0000177static const gchar* webkitAccessibleGetDescription(AtkObject* object)
alp@webkit.org3a5e7ca2008-05-20 01:12:20 +0000178{
eric@webkit.orgd7b62942009-10-28 16:53:41 +0000179 AccessibilityObject* coreObject = core(object);
eric@webkit.org5a5bba52009-12-07 14:27:44 +0000180 Node* node = 0;
181 if (coreObject->isAccessibilityRenderObject())
mario@webkit.org7e9f2412011-04-06 16:50:25 +0000182 node = coreObject->node();
eric@webkit.org5a5bba52009-12-07 14:27:44 +0000183 if (!node || !node->isHTMLElement() || coreObject->ariaRoleAttribute() != UnknownRole)
mario@webkit.orgfecbacd2013-03-04 17:06:15 +0000184 return cacheAndReturnAtkProperty(object, AtkCachedAccessibleDescription, accessibilityDescription(coreObject));
eric@webkit.orgd7b62942009-10-28 16:53:41 +0000185
186 // atk_table_get_summary returns an AtkObject. We have no summary object, so expose summary here.
eric@webkit.org5a5bba52009-12-07 14:27:44 +0000187 if (coreObject->roleValue() == TableRole) {
kangil.han@samsung.comb44a6812013-07-08 06:52:41 +0000188 String summary = toHTMLTableElement(node)->summary();
eric@webkit.org5a5bba52009-12-07 14:27:44 +0000189 if (!summary.isEmpty())
mario@webkit.orgfecbacd2013-03-04 17:06:15 +0000190 return cacheAndReturnAtkProperty(object, AtkCachedAccessibleDescription, summary);
eric@webkit.orgd7b62942009-10-28 16:53:41 +0000191 }
192
eric@webkit.org5a5bba52009-12-07 14:27:44 +0000193 // The title attribute should be reliably available as the object's descripton.
194 // We do not want to fall back on other attributes in its absence. See bug 25524.
yael.aharon@nokia.com3be82c12011-02-09 23:13:27 +0000195 String title = toHTMLElement(node)->title();
eric@webkit.org5a5bba52009-12-07 14:27:44 +0000196 if (!title.isEmpty())
mario@webkit.orgfecbacd2013-03-04 17:06:15 +0000197 return cacheAndReturnAtkProperty(object, AtkCachedAccessibleDescription, title);
eric@webkit.org5a5bba52009-12-07 14:27:44 +0000198
mario@webkit.orgfecbacd2013-03-04 17:06:15 +0000199 return cacheAndReturnAtkProperty(object, AtkCachedAccessibleDescription, accessibilityDescription(coreObject));
alp@webkit.org3a5e7ca2008-05-20 01:12:20 +0000200}
201
eric@webkit.org0f6cb452009-10-22 23:07:56 +0000202static void setAtkRelationSetFromCoreObject(AccessibilityObject* coreObject, AtkRelationSet* relationSet)
203{
commit-queue@webkit.org802c0122012-09-11 01:41:27 +0000204 if (coreObject->isFieldset()) {
205 AccessibilityObject* label = coreObject->titleUIElement();
206 if (label)
207 atk_relation_set_add_relation_by_type(relationSet, ATK_RELATION_LABELLED_BY, label->wrapper());
208 return;
209 }
210
211 if (coreObject->roleValue() == LegendRole) {
212 for (AccessibilityObject* parent = coreObject->parentObjectUnignored(); parent; parent = parent->parentObjectUnignored()) {
213 if (parent->isFieldset()) {
214 atk_relation_set_add_relation_by_type(relationSet, ATK_RELATION_LABEL_FOR, parent->wrapper());
215 break;
216 }
217 }
218 return;
219 }
220
mario@webkit.org7e9f2412011-04-06 16:50:25 +0000221 if (coreObject->isControl()) {
222 AccessibilityObject* label = coreObject->correspondingLabelForControlElement();
eric@webkit.org0f6cb452009-10-22 23:07:56 +0000223 if (label)
224 atk_relation_set_add_relation_by_type(relationSet, ATK_RELATION_LABELLED_BY, label->wrapper());
225 } else {
mario@webkit.org7e9f2412011-04-06 16:50:25 +0000226 AccessibilityObject* control = coreObject->correspondingControlForLabelElement();
eric@webkit.org0f6cb452009-10-22 23:07:56 +0000227 if (control)
228 atk_relation_set_add_relation_by_type(relationSet, ATK_RELATION_LABEL_FOR, control->wrapper());
229 }
230}
231
mario@webkit.orgf88ffcb2012-11-16 13:47:03 +0000232static gpointer webkitAccessibleParentClass = 0;
eric@webkit.orgb84b4742009-10-20 21:20:50 +0000233
mario@webkit.org86390a12011-01-07 17:34:02 +0000234static bool isRootObject(AccessibilityObject* coreObject)
alp@webkit.org3a5e7ca2008-05-20 01:12:20 +0000235{
mario@webkit.org86390a12011-01-07 17:34:02 +0000236 // The root accessible object in WebCore is always an object with
237 // the ScrolledArea role with one child with the WebArea role.
238 if (!coreObject || !coreObject->isScrollView())
239 return false;
alp@webkit.org3a5e7ca2008-05-20 01:12:20 +0000240
mario@webkit.org86390a12011-01-07 17:34:02 +0000241 AccessibilityObject* firstChild = coreObject->firstChild();
242 if (!firstChild || !firstChild->isWebArea())
243 return false;
244
245 return true;
246}
247
248static AtkObject* atkParentOfRootObject(AtkObject* object)
249{
250 AccessibilityObject* coreObject = core(object);
251 AccessibilityObject* coreParent = coreObject->parentObjectUnignored();
252
253 // The top level object claims to not have a parent. This makes it
eric@webkit.org03b220b2009-10-19 11:58:38 +0000254 // impossible for assistive technologies to ascend the accessible
255 // hierarchy all the way to the application. (Bug 30489)
mario@webkit.org86390a12011-01-07 17:34:02 +0000256 if (!coreParent && isRootObject(coreObject)) {
mario@webkit.org8d00fa72011-04-13 16:27:23 +0000257 Document* document = coreObject->document();
258 if (!document)
259 return 0;
260
mario@webkit.org8c5dd902012-11-09 19:47:40 +0000261#if PLATFORM(GTK)
mario@webkit.org8d00fa72011-04-13 16:27:23 +0000262 HostWindow* hostWindow = document->view()->hostWindow();
eric@webkit.org03b220b2009-10-19 11:58:38 +0000263 if (hostWindow) {
mario@webkit.org86390a12011-01-07 17:34:02 +0000264 PlatformPageClient scrollView = hostWindow->platformPageClient();
265 if (scrollView) {
266 GtkWidget* scrollViewParent = gtk_widget_get_parent(scrollView);
267 if (scrollViewParent)
268 return gtk_widget_get_accessible(scrollViewParent);
eric@webkit.org03b220b2009-10-19 11:58:38 +0000269 }
270 }
mario@webkit.org8c5dd902012-11-09 19:47:40 +0000271#endif // PLATFORM(GTK)
eric@webkit.org03b220b2009-10-19 11:58:38 +0000272 }
273
alp@webkit.org3a5e7ca2008-05-20 01:12:20 +0000274 if (!coreParent)
eric@webkit.org1f5f7fe2009-11-04 10:35:56 +0000275 return 0;
276
277 return coreParent->wrapper();
278}
279
mario@webkit.org5c966772012-01-24 18:48:50 +0000280static AtkObject* webkitAccessibleGetParent(AtkObject* object)
eric@webkit.org1f5f7fe2009-11-04 10:35:56 +0000281{
mario@webkit.org46e9b262011-11-17 11:50:07 +0000282 // Check first if the parent has been already set.
mario@webkit.orgf88ffcb2012-11-16 13:47:03 +0000283 AtkObject* accessibleParent = ATK_OBJECT_CLASS(webkitAccessibleParentClass)->get_parent(object);
mario@webkit.org46e9b262011-11-17 11:50:07 +0000284 if (accessibleParent)
285 return accessibleParent;
286
287 // Parent not set yet, so try to find it in the hierarchy.
mario@webkit.org86390a12011-01-07 17:34:02 +0000288 AccessibilityObject* coreObject = core(object);
mario@webkit.org868b5ea2011-06-24 09:06:32 +0000289 if (!coreObject)
290 return 0;
291
mario@webkit.org86390a12011-01-07 17:34:02 +0000292 AccessibilityObject* coreParent = coreObject->parentObjectUnignored();
mario@webkit.org868b5ea2011-06-24 09:06:32 +0000293
mario@webkit.org86390a12011-01-07 17:34:02 +0000294 if (!coreParent && isRootObject(coreObject))
295 return atkParentOfRootObject(object);
eric@webkit.org1f5f7fe2009-11-04 10:35:56 +0000296
297 if (!coreParent)
298 return 0;
alp@webkit.org3a5e7ca2008-05-20 01:12:20 +0000299
mario@webkit.org8c5dd902012-11-09 19:47:40 +0000300 // We don't expose table rows to Assistive technologies, but we
mario@webkit.org868b5ea2011-06-24 09:06:32 +0000301 // need to have them anyway in the hierarchy from WebCore to
302 // properly perform coordinates calculations when requested.
303 if (coreParent->isTableRow() && coreObject->isTableCell())
304 coreParent = coreParent->parentObjectUnignored();
305
alp@webkit.org3a5e7ca2008-05-20 01:12:20 +0000306 return coreParent->wrapper();
307}
308
mario@webkit.org868b5ea2011-06-24 09:06:32 +0000309static gint getNChildrenForTable(AccessibilityObject* coreObject)
310{
311 AccessibilityObject::AccessibilityChildrenVector tableChildren = coreObject->children();
312 size_t tableChildrenCount = tableChildren.size();
313 size_t cellsCount = 0;
314
315 // Look for the actual index of the cell inside the table.
316 for (unsigned i = 0; i < tableChildrenCount; ++i) {
317 if (tableChildren[i]->isTableRow()) {
318 AccessibilityObject::AccessibilityChildrenVector rowChildren = tableChildren[i]->children();
319 cellsCount += rowChildren.size();
320 } else
321 cellsCount++;
322 }
323
324 return cellsCount;
325}
326
mario@webkit.org5c966772012-01-24 18:48:50 +0000327static gint webkitAccessibleGetNChildren(AtkObject* object)
alp@webkit.org3a5e7ca2008-05-20 01:12:20 +0000328{
mario@webkit.org868b5ea2011-06-24 09:06:32 +0000329 AccessibilityObject* coreObject = core(object);
330
331 // Tables should be treated in a different way because rows should
mario@webkit.org8c5dd902012-11-09 19:47:40 +0000332 // be bypassed when exposing the accessible hierarchy.
mario@webkit.org868b5ea2011-06-24 09:06:32 +0000333 if (coreObject->isAccessibilityTable())
334 return getNChildrenForTable(coreObject);
335
336 return coreObject->children().size();
337}
338
339static AccessibilityObject* getChildForTable(AccessibilityObject* coreObject, gint index)
340{
341 AccessibilityObject::AccessibilityChildrenVector tableChildren = coreObject->children();
342 size_t tableChildrenCount = tableChildren.size();
343 size_t cellsCount = 0;
mario@webkit.org868b5ea2011-06-24 09:06:32 +0000344
345 // Look for the actual index of the cell inside the table.
346 size_t current = static_cast<size_t>(index);
347 for (unsigned i = 0; i < tableChildrenCount; ++i) {
348 if (tableChildren[i]->isTableRow()) {
349 AccessibilityObject::AccessibilityChildrenVector rowChildren = tableChildren[i]->children();
350 size_t rowChildrenCount = rowChildren.size();
351 if (current < cellsCount + rowChildrenCount)
352 return rowChildren.at(current - cellsCount).get();
353 cellsCount += rowChildrenCount;
354 } else if (cellsCount == current)
355 return tableChildren[i].get();
356 else
357 cellsCount++;
358 }
359
360 // Shouldn't reach if the child was found.
361 return 0;
alp@webkit.org3a5e7ca2008-05-20 01:12:20 +0000362}
363
mario@webkit.org5c966772012-01-24 18:48:50 +0000364static AtkObject* webkitAccessibleRefChild(AtkObject* object, gint index)
alp@webkit.org3a5e7ca2008-05-20 01:12:20 +0000365{
mario@webkit.org868b5ea2011-06-24 09:06:32 +0000366 if (index < 0)
eric@webkit.orgdbd4d402009-11-04 09:31:06 +0000367 return 0;
alp@webkit.org3a5e7ca2008-05-20 01:12:20 +0000368
mario@webkit.org868b5ea2011-06-24 09:06:32 +0000369 AccessibilityObject* coreObject = core(object);
370 AccessibilityObject* coreChild = 0;
371
mario@webkit.org8c5dd902012-11-09 19:47:40 +0000372 // Tables are special cases because rows should be bypassed, but
373 // still taking their cells into account.
mario@webkit.org868b5ea2011-06-24 09:06:32 +0000374 if (coreObject->isAccessibilityTable())
375 coreChild = getChildForTable(coreObject, index);
376 else {
377 AccessibilityObject::AccessibilityChildrenVector children = coreObject->children();
378 if (static_cast<unsigned>(index) >= children.size())
379 return 0;
380 coreChild = children.at(index).get();
381 }
alp@webkit.org3a5e7ca2008-05-20 01:12:20 +0000382
383 if (!coreChild)
eric@webkit.org4ff6fd72009-11-10 09:53:33 +0000384 return 0;
alp@webkit.org3a5e7ca2008-05-20 01:12:20 +0000385
386 AtkObject* child = coreChild->wrapper();
eric@webkit.orgb84b4742009-10-20 21:20:50 +0000387 atk_object_set_parent(child, object);
alp@webkit.org3a5e7ca2008-05-20 01:12:20 +0000388 g_object_ref(child);
389
390 return child;
391}
392
mario@webkit.org868b5ea2011-06-24 09:06:32 +0000393static gint getIndexInParentForCellInRow(AccessibilityObject* coreObject)
394{
395 AccessibilityObject* parent = coreObject->parentObjectUnignored();
396 if (!parent)
397 return -1;
398
399 AccessibilityObject* grandParent = parent->parentObjectUnignored();
400 if (!grandParent)
401 return -1;
402
403 AccessibilityObject::AccessibilityChildrenVector rows = grandParent->children();
404 size_t rowsCount = rows.size();
405 size_t previousCellsCount = 0;
406
407 // Look for the actual index of the cell inside the table.
408 for (unsigned i = 0; i < rowsCount; ++i) {
409 if (!rows[i]->isTableRow())
410 continue;
411
412 AccessibilityObject::AccessibilityChildrenVector cells = rows[i]->children();
413 size_t cellsCount = cells.size();
414
415 if (rows[i] == parent) {
416 for (unsigned j = 0; j < cellsCount; ++j) {
417 if (cells[j] == coreObject)
418 return previousCellsCount + j;
419 }
420 }
421
422 previousCellsCount += cellsCount;
423 }
424
425 return -1;
426}
427
mario@webkit.org5c966772012-01-24 18:48:50 +0000428static gint webkitAccessibleGetIndexInParent(AtkObject* object)
alp@webkit.org3a5e7ca2008-05-20 01:12:20 +0000429{
xan@webkit.org1f5349a2009-10-27 09:20:21 +0000430 AccessibilityObject* coreObject = core(object);
431 AccessibilityObject* parent = coreObject->parentObjectUnignored();
432
mario@webkit.org86390a12011-01-07 17:34:02 +0000433 if (!parent && isRootObject(coreObject)) {
434 AtkObject* atkParent = atkParentOfRootObject(object);
eric@webkit.org1f5f7fe2009-11-04 10:35:56 +0000435 if (!atkParent)
436 return -1;
437
438 unsigned count = atk_object_get_n_accessible_children(atkParent);
439 for (unsigned i = 0; i < count; ++i) {
440 AtkObject* child = atk_object_ref_accessible_child(atkParent, i);
441 bool childIsObject = child == object;
442 g_object_unref(child);
443 if (childIsObject)
444 return i;
445 }
446 }
xan@webkit.org1f5349a2009-10-27 09:20:21 +0000447
mario@webkit.org868b5ea2011-06-24 09:06:32 +0000448 // Need to calculate the index of the cell in the table, as
mario@webkit.org8c5dd902012-11-09 19:47:40 +0000449 // rows won't be exposed to assistive technologies.
mario@webkit.org868b5ea2011-06-24 09:06:32 +0000450 if (parent && parent->isTableRow() && coreObject->isTableCell())
451 return getIndexInParentForCellInRow(coreObject);
452
commit-queue@webkit.org54a624e2013-01-16 18:00:17 +0000453 if (!parent)
454 return -1;
455
commit-queue@webkit.orgbf183552012-02-22 09:28:57 +0000456 size_t index = parent->children().find(coreObject);
457 return (index == WTF::notFound) ? -1 : index;
alp@webkit.org3a5e7ca2008-05-20 01:12:20 +0000458}
459
mario@webkit.org5c966772012-01-24 18:48:50 +0000460static AtkAttributeSet* webkitAccessibleGetAttributes(AtkObject* object)
eric@webkit.org84496192009-10-17 20:00:33 +0000461{
eric@webkit.org4ff6fd72009-11-10 09:53:33 +0000462 AtkAttributeSet* attributeSet = 0;
mario@webkit.org8c5dd902012-11-09 19:47:40 +0000463#if PLATFORM(GTK)
mario@webkit.orgf8344ff2012-01-24 11:40:44 +0000464 attributeSet = addToAtkAttributeSet(attributeSet, "toolkit", "WebKitGtk");
commit-queue@webkit.orgd9947352012-12-05 17:56:12 +0000465#elif PLATFORM(EFL)
466 attributeSet = addToAtkAttributeSet(attributeSet, "toolkit", "WebKitEfl");
mario@webkit.org8c5dd902012-11-09 19:47:40 +0000467#endif
eric@webkit.orgf4efc402010-05-15 09:55:52 +0000468
commit-queue@webkit.orga9398dd2010-10-26 19:19:29 +0000469 AccessibilityObject* coreObject = core(object);
470 if (!coreObject)
471 return attributeSet;
472
commit-queue@webkit.org357dc222013-01-10 00:09:41 +0000473 // Hack needed for WebKit2 tests because obtaining an element by its ID
474 // cannot be done from the UIProcess. Assistive technologies have no need
475 // for this information.
476 Node* node = coreObject->node();
477 if (node && node->isElementNode()) {
478 String id = toElement(node)->getIdAttribute().string();
479 if (!id.isEmpty())
480 attributeSet = addToAtkAttributeSet(attributeSet, "html-id", id.utf8().data());
481 }
482
commit-queue@webkit.orga9398dd2010-10-26 19:19:29 +0000483 int headingLevel = coreObject->headingLevel();
eric@webkit.org84496192009-10-17 20:00:33 +0000484 if (headingLevel) {
485 String value = String::number(headingLevel);
mario@webkit.orgf8344ff2012-01-24 11:40:44 +0000486 attributeSet = addToAtkAttributeSet(attributeSet, "level", value.utf8().data());
eric@webkit.org84496192009-10-17 20:00:33 +0000487 }
commit-queue@webkit.orga9398dd2010-10-26 19:19:29 +0000488
489 // Set the 'layout-guess' attribute to help Assistive
490 // Technologies know when an exposed table is not data table.
491 if (coreObject->isAccessibilityTable() && !coreObject->isDataTable())
mario@webkit.orgf8344ff2012-01-24 11:40:44 +0000492 attributeSet = addToAtkAttributeSet(attributeSet, "layout-guess", "true");
commit-queue@webkit.orga9398dd2010-10-26 19:19:29 +0000493
commit-queue@webkit.org2db4aaa2012-12-10 01:09:32 +0000494 String placeholder = coreObject->placeholderValue();
495 if (!placeholder.isEmpty())
496 attributeSet = addToAtkAttributeSet(attributeSet, "placeholder-text", placeholder.utf8().data());
497
commit-queue@webkit.org5945f762013-07-16 12:55:56 +0000498 if (coreObject->ariaHasPopup())
499 attributeSet = addToAtkAttributeSet(attributeSet, "aria-haspopup", "true");
500
commit-queue@webkit.orge084cca2013-09-02 15:44:30 +0000501 String invalidStatus = coreObject->invalidStatus().string();
502 if (!invalidStatus.isEmpty() && invalidStatus != "false")
503 attributeSet = addToAtkAttributeSet(attributeSet, "aria-invalid", coreObject->invalidStatus().string().utf8().data());
504
commit-queue@webkit.org88d00b62013-09-03 11:29:33 +0000505 String helpText = coreObject->helpText();
506 if (!helpText.isEmpty())
507 attributeSet = addToAtkAttributeSet(attributeSet, "aria-help", helpText.utf8().data());
508
509 const char* sortDescription = "AXUnknownSortDirection";
510 AccessibilitySortDirection sortDirection = coreObject->sortDirection();
511 if (sortDirection == SortDirectionAscending)
512 sortDescription = "AXAscendingSortDirection";
513 else if (sortDirection == SortDirectionDescending)
514 sortDescription = "AXDescendingSortDirection";
515
516 attributeSet = addToAtkAttributeSet(attributeSet, "aria-sort", sortDescription);
517
eric@webkit.org84496192009-10-17 20:00:33 +0000518 return attributeSet;
519}
520
alp@webkit.org3a5e7ca2008-05-20 01:12:20 +0000521static AtkRole atkRole(AccessibilityRole role)
522{
523 switch (role) {
xan@webkit.org0ea2f732009-04-27 21:56:05 +0000524 case UnknownRole:
525 return ATK_ROLE_UNKNOWN;
526 case ButtonRole:
alp@webkit.org3a5e7ca2008-05-20 01:12:20 +0000527 return ATK_ROLE_PUSH_BUTTON;
commit-queue@webkit.org6152cb92012-08-23 00:36:06 +0000528 case ToggleButtonRole:
529 return ATK_ROLE_TOGGLE_BUTTON;
xan@webkit.org0ea2f732009-04-27 21:56:05 +0000530 case RadioButtonRole:
alp@webkit.org3a5e7ca2008-05-20 01:12:20 +0000531 return ATK_ROLE_RADIO_BUTTON;
xan@webkit.org0ea2f732009-04-27 21:56:05 +0000532 case CheckBoxRole:
alp@webkit.org3a5e7ca2008-05-20 01:12:20 +0000533 return ATK_ROLE_CHECK_BOX;
xan@webkit.org0ea2f732009-04-27 21:56:05 +0000534 case SliderRole:
alp@webkit.org3a5e7ca2008-05-20 01:12:20 +0000535 return ATK_ROLE_SLIDER;
xan@webkit.org0ea2f732009-04-27 21:56:05 +0000536 case TabGroupRole:
commit-queue@webkit.org52553892012-12-13 15:43:38 +0000537 case TabListRole:
alp@webkit.org3a5e7ca2008-05-20 01:12:20 +0000538 return ATK_ROLE_PAGE_TAB_LIST;
xan@webkit.org0ea2f732009-04-27 21:56:05 +0000539 case TextFieldRole:
540 case TextAreaRole:
alp@webkit.org3a5e7ca2008-05-20 01:12:20 +0000541 return ATK_ROLE_ENTRY;
xan@webkit.org0ea2f732009-04-27 21:56:05 +0000542 case StaticTextRole:
543 return ATK_ROLE_TEXT;
544 case OutlineRole:
alp@webkit.org3a5e7ca2008-05-20 01:12:20 +0000545 return ATK_ROLE_TREE;
xan@webkit.org0ea2f732009-04-27 21:56:05 +0000546 case MenuBarRole:
547 return ATK_ROLE_MENU_BAR;
eric@webkit.orga0669292010-04-22 14:36:19 +0000548 case MenuListPopupRole:
xan@webkit.org0ea2f732009-04-27 21:56:05 +0000549 case MenuRole:
550 return ATK_ROLE_MENU;
eric@webkit.orga0669292010-04-22 14:36:19 +0000551 case MenuListOptionRole:
xan@webkit.org0ea2f732009-04-27 21:56:05 +0000552 case MenuItemRole:
553 return ATK_ROLE_MENU_ITEM;
554 case ColumnRole:
mario@webkit.org9adab662012-01-23 10:21:03 +0000555 // return ATK_ROLE_TABLE_COLUMN_HEADER; // Is this right?
xan@webkit.org0ea2f732009-04-27 21:56:05 +0000556 return ATK_ROLE_UNKNOWN; // Matches Mozilla
557 case RowRole:
mario@webkit.org9adab662012-01-23 10:21:03 +0000558 // return ATK_ROLE_TABLE_ROW_HEADER; // Is this right?
xan@webkit.org0ea2f732009-04-27 21:56:05 +0000559 return ATK_ROLE_LIST_ITEM; // Matches Mozilla
560 case ToolbarRole:
561 return ATK_ROLE_TOOL_BAR;
562 case BusyIndicatorRole:
563 return ATK_ROLE_PROGRESS_BAR; // Is this right?
564 case ProgressIndicatorRole:
mario@webkit.org9adab662012-01-23 10:21:03 +0000565 // return ATK_ROLE_SPIN_BUTTON; // Some confusion about this role in AccessibilityRenderObject.cpp
xan@webkit.org0ea2f732009-04-27 21:56:05 +0000566 return ATK_ROLE_PROGRESS_BAR;
567 case WindowRole:
568 return ATK_ROLE_WINDOW;
eric@webkit.orga0669292010-04-22 14:36:19 +0000569 case PopUpButtonRole:
xan@webkit.org0ea2f732009-04-27 21:56:05 +0000570 case ComboBoxRole:
571 return ATK_ROLE_COMBO_BOX;
572 case SplitGroupRole:
573 return ATK_ROLE_SPLIT_PANE;
574 case SplitterRole:
dmazzoni@google.comf3cf2c42012-09-07 23:46:45 +0000575 return ATK_ROLE_UNKNOWN;
xan@webkit.org0ea2f732009-04-27 21:56:05 +0000576 case ColorWellRole:
577 return ATK_ROLE_COLOR_CHOOSER;
578 case ListRole:
alp@webkit.org3a5e7ca2008-05-20 01:12:20 +0000579 return ATK_ROLE_LIST;
xan@webkit.org0ea2f732009-04-27 21:56:05 +0000580 case ScrollBarRole:
581 return ATK_ROLE_SCROLL_BAR;
mario@webkit.org86390a12011-01-07 17:34:02 +0000582 case ScrollAreaRole:
583 return ATK_ROLE_SCROLL_PANE;
commit-queue@webkit.org777f3cc2011-04-01 18:05:04 +0000584 case GridRole: // Is this right?
xan@webkit.org0ea2f732009-04-27 21:56:05 +0000585 case TableRole:
alp@webkit.org3a5e7ca2008-05-20 01:12:20 +0000586 return ATK_ROLE_TABLE;
xan@webkit.org0ea2f732009-04-27 21:56:05 +0000587 case ApplicationRole:
588 return ATK_ROLE_APPLICATION;
xan@webkit.org0ea2f732009-04-27 21:56:05 +0000589 case GroupRole:
590 case RadioGroupRole:
commit-queue@webkit.org52553892012-12-13 15:43:38 +0000591 case TabPanelRole:
xan@webkit.org0ea2f732009-04-27 21:56:05 +0000592 return ATK_ROLE_PANEL;
mario@webkit.org47a021b2011-04-11 17:39:11 +0000593 case RowHeaderRole: // Row headers are cells after all.
594 case ColumnHeaderRole: // Column headers are cells after all.
xan@webkit.org0ea2f732009-04-27 21:56:05 +0000595 case CellRole:
596 return ATK_ROLE_TABLE_CELL;
597 case LinkRole:
598 case WebCoreLinkRole:
599 case ImageMapLinkRole:
alp@webkit.org3a5e7ca2008-05-20 01:12:20 +0000600 return ATK_ROLE_LINK;
xan@webkit.org0ea2f732009-04-27 21:56:05 +0000601 case ImageMapRole:
602 case ImageRole:
alp@webkit.org3a5e7ca2008-05-20 01:12:20 +0000603 return ATK_ROLE_IMAGE;
xan@webkit.org0ea2f732009-04-27 21:56:05 +0000604 case ListMarkerRole:
jmalonzo@webkit.orgad9783b2009-05-23 22:22:52 +0000605 return ATK_ROLE_TEXT;
xan@webkit.org0ea2f732009-04-27 21:56:05 +0000606 case WebAreaRole:
mario@webkit.org9adab662012-01-23 10:21:03 +0000607 // return ATK_ROLE_HTML_CONTAINER; // Is this right?
xan@webkit.org0ea2f732009-04-27 21:56:05 +0000608 return ATK_ROLE_DOCUMENT_FRAME;
609 case HeadingRole:
610 return ATK_ROLE_HEADING;
611 case ListBoxRole:
612 return ATK_ROLE_LIST;
cfleizach@apple.com432ee572010-06-15 06:17:18 +0000613 case ListItemRole:
xan@webkit.org0ea2f732009-04-27 21:56:05 +0000614 case ListBoxOptionRole:
615 return ATK_ROLE_LIST_ITEM;
mario@webkit.org56f6bf22011-03-30 16:51:08 +0000616 case ParagraphRole:
617 return ATK_ROLE_PARAGRAPH;
618 case LabelRole:
commit-queue@webkit.org802c0122012-09-11 01:41:27 +0000619 case LegendRole:
mario@webkit.org56f6bf22011-03-30 16:51:08 +0000620 return ATK_ROLE_LABEL;
621 case DivRole:
622 return ATK_ROLE_SECTION;
623 case FormRole:
624 return ATK_ROLE_FORM;
commit-queue@webkit.org52f67612012-09-07 05:09:04 +0000625 case CanvasRole:
626 return ATK_ROLE_CANVAS;
dmazzoni@google.comf3cf2c42012-09-07 23:46:45 +0000627 case HorizontalRuleRole:
628 return ATK_ROLE_SEPARATOR;
commit-queue@webkit.orgc985400e2012-09-21 20:03:57 +0000629 case SpinButtonRole:
630 return ATK_ROLE_SPIN_BUTTON;
commit-queue@webkit.org52553892012-12-13 15:43:38 +0000631 case TabRole:
632 return ATK_ROLE_PAGE_TAB;
alp@webkit.org3a5e7ca2008-05-20 01:12:20 +0000633 default:
634 return ATK_ROLE_UNKNOWN;
635 }
636}
637
mario@webkit.org5c966772012-01-24 18:48:50 +0000638static AtkRole webkitAccessibleGetRole(AtkObject* object)
alp@webkit.org3a5e7ca2008-05-20 01:12:20 +0000639{
mario@webkit.org56f6bf22011-03-30 16:51:08 +0000640 AccessibilityObject* coreObject = core(object);
jmalonzo@webkit.org7ce37142009-05-20 11:16:01 +0000641
mario@webkit.org56f6bf22011-03-30 16:51:08 +0000642 if (!coreObject)
jmalonzo@webkit.org7ce37142009-05-20 11:16:01 +0000643 return ATK_ROLE_UNKNOWN;
644
645 // Note: Why doesn't WebCore have a password field for this
mario@webkit.org56f6bf22011-03-30 16:51:08 +0000646 if (coreObject->isPasswordField())
jmalonzo@webkit.org7ce37142009-05-20 11:16:01 +0000647 return ATK_ROLE_PASSWORD_TEXT;
648
mario@webkit.org56f6bf22011-03-30 16:51:08 +0000649 return atkRole(coreObject->roleValue());
alp@webkit.org3a5e7ca2008-05-20 01:12:20 +0000650}
651
mario@webkit.org6a24ba12010-12-14 15:35:22 +0000652static bool isTextWithCaret(AccessibilityObject* coreObject)
653{
654 if (!coreObject || !coreObject->isAccessibilityRenderObject())
655 return false;
656
657 Document* document = coreObject->document();
658 if (!document)
659 return false;
660
661 Frame* frame = document->frame();
662 if (!frame)
663 return false;
664
akling@apple.com17523502013-08-17 10:58:40 +0000665 if (!frame->settings().caretBrowsingEnabled())
mario@webkit.org6a24ba12010-12-14 15:35:22 +0000666 return false;
667
668 // Check text objects and paragraphs only.
669 AtkObject* axObject = coreObject->wrapper();
670 AtkRole role = axObject ? atk_object_get_role(axObject) : ATK_ROLE_INVALID;
671 if (role != ATK_ROLE_TEXT && role != ATK_ROLE_PARAGRAPH)
672 return false;
673
674 // Finally, check whether the caret is set in the current object.
675 VisibleSelection selection = coreObject->selection();
676 if (!selection.isCaret())
677 return false;
678
679 return selectionBelongsToObject(coreObject, selection);
680}
681
xan@webkit.org8be15d62009-04-09 11:20:57 +0000682static void setAtkStateSetFromCoreObject(AccessibilityObject* coreObject, AtkStateSet* stateSet)
683{
eric@webkit.org2dd47342009-10-26 11:42:42 +0000684 AccessibilityObject* parent = coreObject->parentObject();
685 bool isListBoxOption = parent && parent->isListBox();
xan@webkit.org8be15d62009-04-09 11:20:57 +0000686
eric@webkit.org2dd47342009-10-26 11:42:42 +0000687 // Please keep the state list in alphabetical order
xan@webkit.org8be15d62009-04-09 11:20:57 +0000688 if (coreObject->isChecked())
689 atk_state_set_add_state(stateSet, ATK_STATE_CHECKED);
690
xan@webkit.orgdf7d13a2009-06-26 09:12:44 +0000691 // FIXME: isReadOnly does not seem to do the right thing for
eric@webkit.org2dd47342009-10-26 11:42:42 +0000692 // controls, so check explicitly for them. In addition, because
693 // isReadOnly is false for listBoxOptions, we need to add one
694 // more check so that we do not present them as being "editable".
mario@webkit.org9adab662012-01-23 10:21:03 +0000695 if ((!coreObject->isReadOnly()
mario@webkit.orgf88ffcb2012-11-16 13:47:03 +0000696 || (coreObject->isControl() && coreObject->canSetValueAttribute()))
mario@webkit.org9adab662012-01-23 10:21:03 +0000697 && !isListBoxOption)
xan@webkit.org8be15d62009-04-09 11:20:57 +0000698 atk_state_set_add_state(stateSet, ATK_STATE_EDITABLE);
699
xan@webkit.orgdf7d13a2009-06-26 09:12:44 +0000700 // FIXME: Put both ENABLED and SENSITIVE together here for now
701 if (coreObject->isEnabled()) {
xan@webkit.org8be15d62009-04-09 11:20:57 +0000702 atk_state_set_add_state(stateSet, ATK_STATE_ENABLED);
xan@webkit.orgdf7d13a2009-06-26 09:12:44 +0000703 atk_state_set_add_state(stateSet, ATK_STATE_SENSITIVE);
704 }
xan@webkit.org8be15d62009-04-09 11:20:57 +0000705
mario@webkit.org8ce6a152010-11-30 21:16:14 +0000706 if (coreObject->canSetExpandedAttribute())
707 atk_state_set_add_state(stateSet, ATK_STATE_EXPANDABLE);
708
709 if (coreObject->isExpanded())
710 atk_state_set_add_state(stateSet, ATK_STATE_EXPANDED);
711
xan@webkit.org8be15d62009-04-09 11:20:57 +0000712 if (coreObject->canSetFocusAttribute())
713 atk_state_set_add_state(stateSet, ATK_STATE_FOCUSABLE);
714
mario@webkit.org6a24ba12010-12-14 15:35:22 +0000715 if (coreObject->isFocused() || isTextWithCaret(coreObject))
xan@webkit.org8be15d62009-04-09 11:20:57 +0000716 atk_state_set_add_state(stateSet, ATK_STATE_FOCUSED);
717
commit-queue@webkit.org471dff52013-03-20 08:12:15 +0000718 if (coreObject->orientation() == AccessibilityOrientationHorizontal)
719 atk_state_set_add_state(stateSet, ATK_STATE_HORIZONTAL);
720 else if (coreObject->orientation() == AccessibilityOrientationVertical)
721 atk_state_set_add_state(stateSet, ATK_STATE_VERTICAL);
xan@webkit.org8be15d62009-04-09 11:20:57 +0000722
723 if (coreObject->isIndeterminate())
724 atk_state_set_add_state(stateSet, ATK_STATE_INDETERMINATE);
725
jhoneycutt@apple.com8acea082010-01-14 01:16:15 +0000726 if (coreObject->isMultiSelectable())
xan@webkit.org8be15d62009-04-09 11:20:57 +0000727 atk_state_set_add_state(stateSet, ATK_STATE_MULTISELECTABLE);
728
729 // TODO: ATK_STATE_OPAQUE
730
731 if (coreObject->isPressed())
732 atk_state_set_add_state(stateSet, ATK_STATE_PRESSED);
733
commit-queue@webkit.org60f8d312013-06-21 10:33:34 +0000734 if (coreObject->isRequired())
735 atk_state_set_add_state(stateSet, ATK_STATE_REQUIRED);
736
xan@webkit.org8be15d62009-04-09 11:20:57 +0000737 // TODO: ATK_STATE_SELECTABLE_TEXT
738
eric@webkit.org2dd47342009-10-26 11:42:42 +0000739 if (coreObject->canSetSelectedAttribute()) {
740 atk_state_set_add_state(stateSet, ATK_STATE_SELECTABLE);
mario@webkit.org8c5dd902012-11-09 19:47:40 +0000741 // Items in focusable lists have both STATE_SELECT{ABLE,ED}
742 // and STATE_FOCUS{ABLE,ED}. We'll fake the latter based on
743 // the former.
eric@webkit.org2dd47342009-10-26 11:42:42 +0000744 if (isListBoxOption)
745 atk_state_set_add_state(stateSet, ATK_STATE_FOCUSABLE);
746 }
747
748 if (coreObject->isSelected()) {
xan@webkit.org8be15d62009-04-09 11:20:57 +0000749 atk_state_set_add_state(stateSet, ATK_STATE_SELECTED);
mario@webkit.org8c5dd902012-11-09 19:47:40 +0000750 // Items in focusable lists have both STATE_SELECT{ABLE,ED}
eric@webkit.org2dd47342009-10-26 11:42:42 +0000751 // and STATE_FOCUS{ABLE,ED}. We'll fake the latter based on the
752 // former.
753 if (isListBoxOption)
754 atk_state_set_add_state(stateSet, ATK_STATE_FOCUSED);
755 }
xan@webkit.org8be15d62009-04-09 11:20:57 +0000756
xan@webkit.orgdf7d13a2009-06-26 09:12:44 +0000757 // FIXME: Group both SHOWING and VISIBLE here for now
758 // Not sure how to handle this in WebKit, see bug
759 // http://bugzilla.gnome.org/show_bug.cgi?id=509650 for other
mario@webkit.org8c5dd902012-11-09 19:47:40 +0000760 // issues with SHOWING vs VISIBLE.
xan@webkit.orgdf7d13a2009-06-26 09:12:44 +0000761 if (!coreObject->isOffScreen()) {
xan@webkit.org8be15d62009-04-09 11:20:57 +0000762 atk_state_set_add_state(stateSet, ATK_STATE_SHOWING);
xan@webkit.orgdf7d13a2009-06-26 09:12:44 +0000763 atk_state_set_add_state(stateSet, ATK_STATE_VISIBLE);
764 }
xan@webkit.org8be15d62009-04-09 11:20:57 +0000765
766 // Mutually exclusive, so we group these two
767 if (coreObject->roleValue() == TextFieldRole)
768 atk_state_set_add_state(stateSet, ATK_STATE_SINGLE_LINE);
769 else if (coreObject->roleValue() == TextAreaRole)
770 atk_state_set_add_state(stateSet, ATK_STATE_MULTI_LINE);
771
772 // TODO: ATK_STATE_SENSITIVE
773
xan@webkit.org8be15d62009-04-09 11:20:57 +0000774 if (coreObject->isVisited())
775 atk_state_set_add_state(stateSet, ATK_STATE_VISITED);
776}
777
mario@webkit.org5c966772012-01-24 18:48:50 +0000778static AtkStateSet* webkitAccessibleRefStateSet(AtkObject* object)
xan@webkit.org8be15d62009-04-09 11:20:57 +0000779{
mario@webkit.orgf88ffcb2012-11-16 13:47:03 +0000780 AtkStateSet* stateSet = ATK_OBJECT_CLASS(webkitAccessibleParentClass)->ref_state_set(object);
xan@webkit.org8be15d62009-04-09 11:20:57 +0000781 AccessibilityObject* coreObject = core(object);
782
783 if (coreObject == fallbackObject()) {
784 atk_state_set_add_state(stateSet, ATK_STATE_DEFUNCT);
785 return stateSet;
786 }
787
mario@webkit.org6a24ba12010-12-14 15:35:22 +0000788 // Text objects must be focusable.
789 AtkRole role = atk_object_get_role(object);
790 if (role == ATK_ROLE_TEXT || role == ATK_ROLE_PARAGRAPH)
791 atk_state_set_add_state(stateSet, ATK_STATE_FOCUSABLE);
xan@webkit.org8be15d62009-04-09 11:20:57 +0000792
mario@webkit.org6a24ba12010-12-14 15:35:22 +0000793 setAtkStateSetFromCoreObject(coreObject, stateSet);
xan@webkit.org8be15d62009-04-09 11:20:57 +0000794 return stateSet;
795}
796
mario@webkit.org5c966772012-01-24 18:48:50 +0000797static AtkRelationSet* webkitAccessibleRefRelationSet(AtkObject* object)
eric@webkit.org0f6cb452009-10-22 23:07:56 +0000798{
mario@webkit.orgf88ffcb2012-11-16 13:47:03 +0000799 AtkRelationSet* relationSet = ATK_OBJECT_CLASS(webkitAccessibleParentClass)->ref_relation_set(object);
eric@webkit.org0f6cb452009-10-22 23:07:56 +0000800 AccessibilityObject* coreObject = core(object);
801
802 setAtkRelationSetFromCoreObject(coreObject, relationSet);
803
804 return relationSet;
805}
806
mario@webkit.org5c966772012-01-24 18:48:50 +0000807static void webkitAccessibleInit(AtkObject* object, gpointer data)
xan@webkit.orge4387102009-04-09 11:08:48 +0000808{
mario@webkit.orgf88ffcb2012-11-16 13:47:03 +0000809 if (ATK_OBJECT_CLASS(webkitAccessibleParentClass)->initialize)
810 ATK_OBJECT_CLASS(webkitAccessibleParentClass)->initialize(object, data);
xan@webkit.orge4387102009-04-09 11:08:48 +0000811
mario@webkit.orgfecbacd2013-03-04 17:06:15 +0000812 WebKitAccessible* accessible = WEBKIT_ACCESSIBLE(object);
813 accessible->m_object = reinterpret_cast<AccessibilityObject*>(data);
814 accessible->priv = WEBKIT_ACCESSIBLE_GET_PRIVATE(accessible);
xan@webkit.orge4387102009-04-09 11:08:48 +0000815}
816
commit-queue@webkit.org120f5652013-06-12 21:39:42 +0000817static const gchar* webkitAccessibleGetObjectLocale(AtkObject* object)
818{
819 if (ATK_IS_DOCUMENT(object)) {
820 AccessibilityObject* coreObject = core(object);
821 if (!coreObject)
822 return 0;
823
824 // TODO: Should we fall back on lang xml:lang when the following comes up empty?
825 String language = coreObject->language();
826 if (!language.isEmpty())
827 return cacheAndReturnAtkProperty(object, AtkCachedDocumentLocale, language);
828
829 } else if (ATK_IS_TEXT(object)) {
830 const gchar* locale = 0;
831
832 AtkAttributeSet* textAttributes = atk_text_get_default_attributes(ATK_TEXT(object));
commit-queue@webkit.org44a695d2013-07-04 12:10:45 +0000833 for (AtkAttributeSet* attributes = textAttributes; attributes; attributes = attributes->next) {
commit-queue@webkit.org120f5652013-06-12 21:39:42 +0000834 AtkAttribute* atkAttribute = static_cast<AtkAttribute*>(attributes->data);
835 if (!strcmp(atkAttribute->name, atk_text_attribute_get_name(ATK_TEXT_ATTR_LANGUAGE))) {
836 locale = cacheAndReturnAtkProperty(object, AtkCachedDocumentLocale, String::fromUTF8(atkAttribute->value));
837 break;
838 }
839 }
commit-queue@webkit.org120f5652013-06-12 21:39:42 +0000840 atk_attribute_set_free(textAttributes);
841
842 return locale;
843 }
844
845 return 0;
846}
847
mario@webkit.org5c966772012-01-24 18:48:50 +0000848static void webkitAccessibleFinalize(GObject* object)
xan@webkit.orge4387102009-04-09 11:08:48 +0000849{
mario@webkit.orgf88ffcb2012-11-16 13:47:03 +0000850 G_OBJECT_CLASS(webkitAccessibleParentClass)->finalize(object);
xan@webkit.orge4387102009-04-09 11:08:48 +0000851}
852
mario@webkit.orgf88ffcb2012-11-16 13:47:03 +0000853static void webkitAccessibleClassInit(AtkObjectClass* klass)
alp@webkit.org3a5e7ca2008-05-20 01:12:20 +0000854{
xan@webkit.orge4387102009-04-09 11:08:48 +0000855 GObjectClass* gobjectClass = G_OBJECT_CLASS(klass);
alp@webkit.org3a5e7ca2008-05-20 01:12:20 +0000856
mario@webkit.orgf88ffcb2012-11-16 13:47:03 +0000857 webkitAccessibleParentClass = g_type_class_peek_parent(klass);
xan@webkit.orge4387102009-04-09 11:08:48 +0000858
mario@webkit.org5c966772012-01-24 18:48:50 +0000859 gobjectClass->finalize = webkitAccessibleFinalize;
alp@webkit.org3a5e7ca2008-05-20 01:12:20 +0000860
mario@webkit.org5c966772012-01-24 18:48:50 +0000861 klass->initialize = webkitAccessibleInit;
862 klass->get_name = webkitAccessibleGetName;
863 klass->get_description = webkitAccessibleGetDescription;
864 klass->get_parent = webkitAccessibleGetParent;
865 klass->get_n_children = webkitAccessibleGetNChildren;
866 klass->ref_child = webkitAccessibleRefChild;
867 klass->get_role = webkitAccessibleGetRole;
868 klass->ref_state_set = webkitAccessibleRefStateSet;
869 klass->get_index_in_parent = webkitAccessibleGetIndexInParent;
870 klass->get_attributes = webkitAccessibleGetAttributes;
871 klass->ref_relation_set = webkitAccessibleRefRelationSet;
commit-queue@webkit.org120f5652013-06-12 21:39:42 +0000872 klass->get_object_locale = webkitAccessibleGetObjectLocale;
mario@webkit.orgfecbacd2013-03-04 17:06:15 +0000873
874 g_type_class_add_private(klass, sizeof(WebKitAccessiblePrivate));
xan@webkit.orge4387102009-04-09 11:08:48 +0000875}
876
877GType
mario@webkit.orgf88ffcb2012-11-16 13:47:03 +0000878webkitAccessibleGetType(void)
xan@webkit.orge4387102009-04-09 11:08:48 +0000879{
mario@webkit.orgf88ffcb2012-11-16 13:47:03 +0000880 static volatile gsize typeVolatile = 0;
xan@webkit.orge4387102009-04-09 11:08:48 +0000881
mario@webkit.orgf88ffcb2012-11-16 13:47:03 +0000882 if (g_once_init_enter(&typeVolatile)) {
xan@webkit.orge4387102009-04-09 11:08:48 +0000883 static const GTypeInfo tinfo = {
884 sizeof(WebKitAccessibleClass),
eric@webkit.org4ff6fd72009-11-10 09:53:33 +0000885 (GBaseInitFunc) 0,
886 (GBaseFinalizeFunc) 0,
mario@webkit.orgf88ffcb2012-11-16 13:47:03 +0000887 (GClassInitFunc) webkitAccessibleClassInit,
eric@webkit.org4ff6fd72009-11-10 09:53:33 +0000888 (GClassFinalizeFunc) 0,
889 0, /* class data */
xan@webkit.orge4387102009-04-09 11:08:48 +0000890 sizeof(WebKitAccessible), /* instance size */
891 0, /* nb preallocs */
eric@webkit.org4ff6fd72009-11-10 09:53:33 +0000892 (GInstanceInitFunc) 0,
893 0 /* value table */
xan@webkit.orge4387102009-04-09 11:08:48 +0000894 };
895
mario@webkit.orgf88ffcb2012-11-16 13:47:03 +0000896 GType type = g_type_register_static(ATK_TYPE_OBJECT, "WebKitAccessible", &tinfo, GTypeFlags(0));
897 g_once_init_leave(&typeVolatile, type);
xan@webkit.orge4387102009-04-09 11:08:48 +0000898 }
899
mario@webkit.orgf88ffcb2012-11-16 13:47:03 +0000900 return typeVolatile;
alp@webkit.orgc7738992008-05-27 02:48:14 +0000901}
902
xan@webkit.orge4387102009-04-09 11:08:48 +0000903static const GInterfaceInfo AtkInterfacesInitFunctions[] = {
mario@webkit.orgdeec8392012-01-23 14:45:23 +0000904 {reinterpret_cast<GInterfaceInitFunc>(webkitAccessibleActionInterfaceInit), 0, 0},
mario@webkit.org7e5931d2012-01-24 12:25:13 +0000905 {reinterpret_cast<GInterfaceInitFunc>(webkitAccessibleSelectionInterfaceInit), 0, 0},
mario@webkit.orgfc51ca62012-01-24 11:47:51 +0000906 {reinterpret_cast<GInterfaceInitFunc>(webkitAccessibleEditableTextInterfaceInit), 0, 0},
mario@webkit.org987d7372012-01-24 18:02:08 +0000907 {reinterpret_cast<GInterfaceInitFunc>(webkitAccessibleTextInterfaceInit), 0, 0},
mario@webkit.orgbe1ce552012-01-24 11:03:51 +0000908 {reinterpret_cast<GInterfaceInitFunc>(webkitAccessibleComponentInterfaceInit), 0, 0},
mario@webkit.orgda3e6082012-01-24 12:04:16 +0000909 {reinterpret_cast<GInterfaceInitFunc>(webkitAccessibleImageInterfaceInit), 0, 0},
mario@webkit.orgcd9f1b32012-01-24 18:28:22 +0000910 {reinterpret_cast<GInterfaceInitFunc>(webkitAccessibleTableInterfaceInit), 0, 0},
mario@webkit.org70243532012-01-24 11:58:52 +0000911 {reinterpret_cast<GInterfaceInitFunc>(webkitAccessibleHypertextInterfaceInit), 0, 0},
mario@webkit.org4dbd9822012-01-24 11:55:18 +0000912 {reinterpret_cast<GInterfaceInitFunc>(webkitAccessibleHyperlinkImplInterfaceInit), 0, 0},
mario@webkit.orgf8344ff2012-01-24 11:40:44 +0000913 {reinterpret_cast<GInterfaceInitFunc>(webkitAccessibleDocumentInterfaceInit), 0, 0},
mario@webkit.org980269e2012-01-24 16:22:57 +0000914 {reinterpret_cast<GInterfaceInitFunc>(webkitAccessibleValueInterfaceInit), 0, 0}
xan@webkit.orge4387102009-04-09 11:08:48 +0000915};
916
917enum WAIType {
918 WAI_ACTION,
eric@webkit.orgf84ff632009-10-29 17:34:39 +0000919 WAI_SELECTION,
xan@webkit.orge4387102009-04-09 11:08:48 +0000920 WAI_EDITABLE_TEXT,
xan@webkit.org92b91692009-04-21 07:02:17 +0000921 WAI_TEXT,
xan@webkit.orge43e70b2009-04-27 21:33:55 +0000922 WAI_COMPONENT,
xan@webkit.org45b26ac2009-10-27 12:20:35 +0000923 WAI_IMAGE,
eric@webkit.org65e12ba2009-11-01 21:22:30 +0000924 WAI_TABLE,
mario@webkit.org7f95c622010-11-01 15:05:36 +0000925 WAI_HYPERTEXT,
926 WAI_HYPERLINK,
mario@webkit.org04e38e92011-03-28 10:16:38 +0000927 WAI_DOCUMENT,
928 WAI_VALUE,
xan@webkit.orge4387102009-04-09 11:08:48 +0000929};
930
931static GType GetAtkInterfaceTypeFromWAIType(WAIType type)
alp@webkit.org3a5e7ca2008-05-20 01:12:20 +0000932{
mario@webkit.org7f95c622010-11-01 15:05:36 +0000933 switch (type) {
934 case WAI_ACTION:
935 return ATK_TYPE_ACTION;
936 case WAI_SELECTION:
937 return ATK_TYPE_SELECTION;
938 case WAI_EDITABLE_TEXT:
939 return ATK_TYPE_EDITABLE_TEXT;
940 case WAI_TEXT:
941 return ATK_TYPE_TEXT;
942 case WAI_COMPONENT:
943 return ATK_TYPE_COMPONENT;
944 case WAI_IMAGE:
945 return ATK_TYPE_IMAGE;
946 case WAI_TABLE:
947 return ATK_TYPE_TABLE;
948 case WAI_HYPERTEXT:
949 return ATK_TYPE_HYPERTEXT;
950 case WAI_HYPERLINK:
951 return ATK_TYPE_HYPERLINK_IMPL;
952 case WAI_DOCUMENT:
953 return ATK_TYPE_DOCUMENT;
mario@webkit.org04e38e92011-03-28 10:16:38 +0000954 case WAI_VALUE:
955 return ATK_TYPE_VALUE;
mario@webkit.org7f95c622010-11-01 15:05:36 +0000956 }
alp@webkit.org3a5e7ca2008-05-20 01:12:20 +0000957
mario@webkit.org7f95c622010-11-01 15:05:36 +0000958 return G_TYPE_INVALID;
xan@webkit.orge4387102009-04-09 11:08:48 +0000959}
alp@webkit.org3a5e7ca2008-05-20 01:12:20 +0000960
commit-queue@webkit.orgca1b1d22012-08-22 01:37:04 +0000961static bool roleIsTextType(AccessibilityRole role)
962{
zandobersek@gmail.com2b2075f2012-09-01 08:53:50 +0000963 return role == ParagraphRole || role == HeadingRole || role == DivRole || role == CellRole || role == ListItemRole;
commit-queue@webkit.orgca1b1d22012-08-22 01:37:04 +0000964}
965
xan@webkit.orge4387102009-04-09 11:08:48 +0000966static guint16 getInterfaceMaskFromObject(AccessibilityObject* coreObject)
967{
968 guint16 interfaceMask = 0;
alp@webkit.orgc7738992008-05-27 02:48:14 +0000969
xan@webkit.org92b91692009-04-21 07:02:17 +0000970 // Component interface is always supported
971 interfaceMask |= 1 << WAI_COMPONENT;
972
mario@webkit.org7f95c622010-11-01 15:05:36 +0000973 AccessibilityRole role = coreObject->roleValue();
974
xan@webkit.orgb3c59bf2009-04-21 07:01:27 +0000975 // Action
mario@webkit.org8ce6a152010-11-30 21:16:14 +0000976 // As the implementation of the AtkAction interface is a very
977 // basic one (just relays in executing the default action for each
978 // object, and only supports having one action per object), it is
979 // better just to implement this interface for every instance of
980 // the WebKitAccessible class and let WebCore decide what to do.
981 interfaceMask |= 1 << WAI_ACTION;
xan@webkit.orgb3c59bf2009-04-21 07:01:27 +0000982
eric@webkit.orgf84ff632009-10-29 17:34:39 +0000983 // Selection
mario@webkit.org8ce6a152010-11-30 21:16:14 +0000984 if (coreObject->isListBox() || coreObject->isMenuList())
eric@webkit.orgf84ff632009-10-29 17:34:39 +0000985 interfaceMask |= 1 << WAI_SELECTION;
986
mario@webkit.org9f0aee12011-04-11 21:02:23 +0000987 // Get renderer if available.
988 RenderObject* renderer = 0;
989 if (coreObject->isAccessibilityRenderObject())
990 renderer = coreObject->renderer();
991
992 // Hyperlink (links and embedded objects).
993 if (coreObject->isLink() || (renderer && renderer->isReplaced()))
994 interfaceMask |= 1 << WAI_HYPERLINK;
995
xan@webkit.orge4387102009-04-09 11:08:48 +0000996 // Text & Editable Text
mario@webkit.org8ce6a152010-11-30 21:16:14 +0000997 if (role == StaticTextRole || coreObject->isMenuListOption())
xan@webkit.org7a07be52009-04-14 15:56:10 +0000998 interfaceMask |= 1 << WAI_TEXT;
mario@webkit.org9f0aee12011-04-11 21:02:23 +0000999 else {
eric@webkit.org10e02f82010-01-07 02:05:01 +00001000 if (coreObject->isTextControl()) {
1001 interfaceMask |= 1 << WAI_TEXT;
1002 if (!coreObject->isReadOnly())
1003 interfaceMask |= 1 << WAI_EDITABLE_TEXT;
commit-queue@webkit.org1f3aafe2010-09-22 07:49:34 +00001004 } else {
mario@webkit.org7f95c622010-11-01 15:05:36 +00001005 if (role != TableRole) {
1006 interfaceMask |= 1 << WAI_HYPERTEXT;
commit-queue@webkit.orgca1b1d22012-08-22 01:37:04 +00001007 if ((renderer && renderer->childrenInline()) || roleIsTextType(role))
mario@webkit.org7f95c622010-11-01 15:05:36 +00001008 interfaceMask |= 1 << WAI_TEXT;
1009 }
1010
1011 // Add the TEXT interface for list items whose
1012 // first accessible child has a text renderer
1013 if (role == ListItemRole) {
mario@webkit.org7e9f2412011-04-06 16:50:25 +00001014 AccessibilityObject::AccessibilityChildrenVector children = coreObject->children();
commit-queue@webkit.org1f3aafe2010-09-22 07:49:34 +00001015 if (children.size()) {
1016 AccessibilityObject* axRenderChild = children.at(0).get();
1017 interfaceMask |= getInterfaceMaskFromObject(axRenderChild);
1018 }
1019 }
1020 }
mario@webkit.org7f95c622010-11-01 15:05:36 +00001021 }
xan@webkit.orge4387102009-04-09 11:08:48 +00001022
xan@webkit.orge43e70b2009-04-27 21:33:55 +00001023 // Image
1024 if (coreObject->isImage())
1025 interfaceMask |= 1 << WAI_IMAGE;
1026
xan@webkit.org45b26ac2009-10-27 12:20:35 +00001027 // Table
1028 if (role == TableRole)
1029 interfaceMask |= 1 << WAI_TABLE;
1030
eric@webkit.org65e12ba2009-11-01 21:22:30 +00001031 // Document
1032 if (role == WebAreaRole)
1033 interfaceMask |= 1 << WAI_DOCUMENT;
1034
mario@webkit.org04e38e92011-03-28 10:16:38 +00001035 // Value
commit-queue@webkit.org3e37d782013-09-09 09:39:34 +00001036 if (role == SliderRole || role == SpinButtonRole || role == ScrollBarRole || role == ProgressIndicatorRole)
mario@webkit.org04e38e92011-03-28 10:16:38 +00001037 interfaceMask |= 1 << WAI_VALUE;
1038
commit-queue@webkit.orgfd70fd32013-09-04 14:19:02 +00001039#if ENABLE(INPUT_TYPE_COLOR)
1040 // Color type.
1041 if (role == ColorWellRole)
1042 interfaceMask |= 1 << WAI_TEXT;
1043#endif
1044
xan@webkit.orge4387102009-04-09 11:08:48 +00001045 return interfaceMask;
1046}
1047
1048static const char* getUniqueAccessibilityTypeName(guint16 interfaceMask)
1049{
1050#define WAI_TYPE_NAME_LEN (30) /* Enough for prefix + 5 hex characters (max) */
1051 static char name[WAI_TYPE_NAME_LEN + 1];
eric@webkit.org4ff6fd72009-11-10 09:53:33 +00001052
xan@webkit.orge4387102009-04-09 11:08:48 +00001053 g_sprintf(name, "WAIType%x", interfaceMask);
1054 name[WAI_TYPE_NAME_LEN] = '\0';
eric@webkit.org4ff6fd72009-11-10 09:53:33 +00001055
xan@webkit.orge4387102009-04-09 11:08:48 +00001056 return name;
1057}
1058
1059static GType getAccessibilityTypeFromObject(AccessibilityObject* coreObject)
1060{
1061 static const GTypeInfo typeInfo = {
1062 sizeof(WebKitAccessibleClass),
eric@webkit.org4ff6fd72009-11-10 09:53:33 +00001063 (GBaseInitFunc) 0,
1064 (GBaseFinalizeFunc) 0,
1065 (GClassInitFunc) 0,
1066 (GClassFinalizeFunc) 0,
1067 0, /* class data */
xan@webkit.orge4387102009-04-09 11:08:48 +00001068 sizeof(WebKitAccessible), /* instance size */
1069 0, /* nb preallocs */
eric@webkit.org4ff6fd72009-11-10 09:53:33 +00001070 (GInstanceInitFunc) 0,
1071 0 /* value table */
xan@webkit.orge4387102009-04-09 11:08:48 +00001072 };
1073
1074 guint16 interfaceMask = getInterfaceMaskFromObject(coreObject);
1075 const char* atkTypeName = getUniqueAccessibilityTypeName(interfaceMask);
1076 GType type = g_type_from_name(atkTypeName);
1077 if (type)
1078 return type;
1079
mario@webkit.orgf88ffcb2012-11-16 13:47:03 +00001080 type = g_type_register_static(WEBKIT_TYPE_ACCESSIBLE, atkTypeName, &typeInfo, GTypeFlags(0));
xan@webkit.orge4387102009-04-09 11:08:48 +00001081 for (guint i = 0; i < G_N_ELEMENTS(AtkInterfacesInitFunctions); i++) {
1082 if (interfaceMask & (1 << i))
1083 g_type_add_interface_static(type,
mario@webkit.orgf88ffcb2012-11-16 13:47:03 +00001084 GetAtkInterfaceTypeFromWAIType(static_cast<WAIType>(i)),
1085 &AtkInterfacesInitFunctions[i]);
xan@webkit.orge4387102009-04-09 11:08:48 +00001086 }
1087
alp@webkit.org3a5e7ca2008-05-20 01:12:20 +00001088 return type;
1089}
1090
mario@webkit.orgaacb2172012-01-23 11:43:28 +00001091WebKitAccessible* webkitAccessibleNew(AccessibilityObject* coreObject)
alp@webkit.org3a5e7ca2008-05-20 01:12:20 +00001092{
xan@webkit.orge4387102009-04-09 11:08:48 +00001093 GType type = getAccessibilityTypeFromObject(coreObject);
eric@webkit.org4ff6fd72009-11-10 09:53:33 +00001094 AtkObject* object = static_cast<AtkObject*>(g_object_new(type, 0));
xan@webkit.orge4387102009-04-09 11:08:48 +00001095
alp@webkit.org3a5e7ca2008-05-20 01:12:20 +00001096 atk_object_initialize(object, coreObject);
xan@webkit.orge4387102009-04-09 11:08:48 +00001097
alp@webkit.org3a5e7ca2008-05-20 01:12:20 +00001098 return WEBKIT_ACCESSIBLE(object);
1099}
1100
mario@webkit.orgaacb2172012-01-23 11:43:28 +00001101AccessibilityObject* webkitAccessibleGetAccessibilityObject(WebKitAccessible* accessible)
alp@webkit.org3a5e7ca2008-05-20 01:12:20 +00001102{
1103 return accessible->m_object;
1104}
1105
mario@webkit.orgaacb2172012-01-23 11:43:28 +00001106void webkitAccessibleDetach(WebKitAccessible* accessible)
alp@webkit.org3a5e7ca2008-05-20 01:12:20 +00001107{
alp@webkit.orgc7738992008-05-27 02:48:14 +00001108 ASSERT(accessible->m_object);
1109
mario@webkit.org92daa812011-02-16 17:12:38 +00001110 if (core(accessible)->roleValue() == WebAreaRole)
1111 g_signal_emit_by_name(accessible, "state-change", "defunct", true);
1112
alp@webkit.orgc7738992008-05-27 02:48:14 +00001113 // We replace the WebCore AccessibilityObject with a fallback object that
1114 // provides default implementations to avoid repetitive null-checking after
1115 // detachment.
xan@webkit.orgc886cc62009-04-09 11:17:48 +00001116 accessible->m_object = fallbackObject();
alp@webkit.org3a5e7ca2008-05-20 01:12:20 +00001117}
1118
mario@webkit.orgaacb2172012-01-23 11:43:28 +00001119AtkObject* webkitAccessibleGetFocusedElement(WebKitAccessible* accessible)
jmalonzo@webkit.orgaaaea3b2009-08-08 07:39:48 +00001120{
1121 if (!accessible->m_object)
1122 return 0;
1123
1124 RefPtr<AccessibilityObject> focusedObj = accessible->m_object->focusedUIElement();
1125 if (!focusedObj)
1126 return 0;
1127
1128 return focusedObj->wrapper();
1129}
1130
mario@webkit.org4c778d72012-01-09 10:16:20 +00001131AccessibilityObject* objectFocusedAndCaretOffsetUnignored(AccessibilityObject* referenceObject, int& offset)
eric@webkit.org2fade492010-01-11 12:03:36 +00001132{
eric@webkit.org9cf227c2010-01-19 21:31:01 +00001133 // Indication that something bogus has transpired.
1134 offset = -1;
eric@webkit.org2fade492010-01-11 12:03:36 +00001135
mario@webkit.org4c778d72012-01-09 10:16:20 +00001136 Document* document = referenceObject->document();
1137 if (!document)
mrobinson@webkit.orgbc2c5f22010-09-29 17:58:13 +00001138 return 0;
eric@webkit.org2fade492010-01-11 12:03:36 +00001139
mario@webkit.org4c778d72012-01-09 10:16:20 +00001140 Node* focusedNode = referenceObject->selection().end().containerNode();
1141 if (!focusedNode)
mrobinson@webkit.orgbc2c5f22010-09-29 17:58:13 +00001142 return 0;
eric@webkit.org2fade492010-01-11 12:03:36 +00001143
mario@webkit.org4c778d72012-01-09 10:16:20 +00001144 RenderObject* focusedRenderer = focusedNode->renderer();
1145 if (!focusedRenderer)
1146 return 0;
mario@webkit.org6698d352011-02-01 09:49:25 +00001147
mario@webkit.org4c778d72012-01-09 10:16:20 +00001148 AccessibilityObject* focusedObject = document->axObjectCache()->getOrCreate(focusedRenderer);
1149 if (!focusedObject)
1150 return 0;
mario@webkit.org6698d352011-02-01 09:49:25 +00001151
mario@webkit.org4c778d72012-01-09 10:16:20 +00001152 // Look for the actual (not ignoring accessibility) selected object.
mario@webkit.orgd048f762012-01-22 19:29:04 +00001153 AccessibilityObject* firstUnignoredParent = focusedObject;
1154 if (firstUnignoredParent->accessibilityIsIgnored())
1155 firstUnignoredParent = firstUnignoredParent->parentObjectUnignored();
1156 if (!firstUnignoredParent)
mario@webkit.org4c778d72012-01-09 10:16:20 +00001157 return 0;
1158
1159 // Don't ignore links if the offset is being requested for a link.
mario@webkit.orgd048f762012-01-22 19:29:04 +00001160 if (!referenceObject->isLink() && firstUnignoredParent->isLink())
1161 firstUnignoredParent = firstUnignoredParent->parentObjectUnignored();
1162 if (!firstUnignoredParent)
mario@webkit.org4c778d72012-01-09 10:16:20 +00001163 return 0;
1164
mario@webkit.orgd048f762012-01-22 19:29:04 +00001165 // The reference object must either coincide with the focused
1166 // object being considered, or be a descendant of it.
1167 if (referenceObject->isDescendantOfObject(firstUnignoredParent))
1168 referenceObject = firstUnignoredParent;
1169
mario@webkit.org4c778d72012-01-09 10:16:20 +00001170 Node* startNode = 0;
mario@webkit.orgd048f762012-01-22 19:29:04 +00001171 if (firstUnignoredParent != referenceObject || firstUnignoredParent->isTextControl()) {
mario@webkit.org4c778d72012-01-09 10:16:20 +00001172 // We need to use the first child's node of the reference
1173 // object as the start point to calculate the caret offset
1174 // because we want it to be relative to the object of
1175 // reference, not just to the focused object (which could have
1176 // previous siblings which should be taken into account too).
1177 AccessibilityObject* axFirstChild = referenceObject->firstChild();
1178 if (axFirstChild)
1179 startNode = axFirstChild->node();
1180 }
commit-queue@webkit.orgfb3e9152013-02-01 00:37:22 +00001181 // Getting the Position of a PseudoElement now triggers an assertion.
1182 // This can occur when clicking on empty space in a render block.
1183 if (!startNode || startNode->isPseudoElement())
mario@webkit.orgd048f762012-01-22 19:29:04 +00001184 startNode = firstUnignoredParent->node();
mario@webkit.org4c778d72012-01-09 10:16:20 +00001185
mario@webkit.orgb311bdb2012-02-01 17:58:57 +00001186 // Check if the node for the first parent object not ignoring
1187 // accessibility is null again before using it. This might happen
1188 // with certain kind of accessibility objects, such as the root
1189 // one (the scroller containing the webArea object).
1190 if (!startNode)
1191 return 0;
1192
mario@webkit.orgd048f762012-01-22 19:29:04 +00001193 VisiblePosition startPosition = VisiblePosition(positionBeforeNode(startNode), DOWNSTREAM);
1194 VisiblePosition endPosition = firstUnignoredParent->selection().visibleEnd();
mario@webkit.org4c778d72012-01-09 10:16:20 +00001195
1196 if (startPosition == endPosition)
1197 offset = 0;
1198 else if (!isStartOfLine(endPosition)) {
1199 RefPtr<Range> range = makeRange(startPosition, endPosition.previous());
1200 offset = TextIterator::rangeLength(range.get(), true) + 1;
1201 } else {
1202 RefPtr<Range> range = makeRange(startPosition, endPosition);
1203 offset = TextIterator::rangeLength(range.get(), true);
eric@webkit.org9cf227c2010-01-19 21:31:01 +00001204 }
mario@webkit.orgff7fd422011-01-27 19:38:03 +00001205
mario@webkit.orgd048f762012-01-22 19:29:04 +00001206 return firstUnignoredParent;
eric@webkit.org2fade492010-01-11 12:03:36 +00001207}
1208
mario@webkit.orgfecbacd2013-03-04 17:06:15 +00001209const char* cacheAndReturnAtkProperty(AtkObject* object, AtkCachedProperty property, String value)
1210{
1211 WebKitAccessiblePrivate* priv = WEBKIT_ACCESSIBLE(object)->priv;
1212 CString* propertyPtr = 0;
1213
1214 switch (property) {
1215 case AtkCachedAccessibleName:
1216 propertyPtr = &priv->accessibleName;
1217 break;
1218
1219 case AtkCachedAccessibleDescription:
1220 propertyPtr = &priv->accessibleDescription;
1221 break;
1222
1223 case AtkCachedActionName:
1224 propertyPtr = &priv->actionName;
1225 break;
1226
1227 case AtkCachedActionKeyBinding:
1228 propertyPtr = &priv->actionKeyBinding;
1229 break;
1230
1231 case AtkCachedDocumentLocale:
1232 propertyPtr = &priv->documentLocale;
1233 break;
1234
1235 case AtkCachedDocumentType:
1236 propertyPtr = &priv->documentType;
1237 break;
1238
1239 case AtkCachedDocumentEncoding:
1240 propertyPtr = &priv->documentEncoding;
1241 break;
1242
1243 case AtkCachedDocumentURI:
1244 propertyPtr = &priv->documentURI;
1245 break;
1246
1247 case AtkCachedImageDescription:
1248 propertyPtr = &priv->imageDescription;
1249 break;
1250
1251 default:
1252 ASSERT_NOT_REACHED();
1253 }
1254
1255 // Don't invalidate old memory if not stricly needed, since other
1256 // callers might be still holding on to it.
1257 if (*propertyPtr != value.utf8())
1258 *propertyPtr = value.utf8();
1259
1260 return (*propertyPtr).data();
1261}
1262
ddkilzer@apple.com8d878632008-11-09 19:50:37 +00001263#endif // HAVE(ACCESSIBILITY)