blob: 76b760dbf09eb779a06727cb09e2404e853bb957 [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"
akling@apple.com88e05f62013-09-16 15:17:24 +000038#include "AccessibilityListBoxOption.h"
alp@webkit.orgc7738992008-05-27 02:48:14 +000039#include "Document.h"
alp@webkit.orgc7738992008-05-27 02:48:14 +000040#include "Frame.h"
41#include "FrameView.h"
xan@webkit.org9561b2c2009-05-20 14:33:19 +000042#include "HTMLNames.h"
xan@webkit.org45b26ac2009-10-27 12:20:35 +000043#include "HTMLTableElement.h"
mario@webkit.org9adab662012-01-23 10:21:03 +000044#include "HostWindow.h"
mario@webkit.org970eaf32012-01-24 18:37:45 +000045#include "RenderObject.h"
mario@webkit.org6a24ba12010-12-14 15:35:22 +000046#include "Settings.h"
commit-queue@webkit.org74bd2e82010-09-12 11:16:14 +000047#include "TextIterator.h"
tkent@chromium.org8c35c122013-03-06 13:00:14 +000048#include "VisibleUnits.h"
mario@webkit.org7f95c622010-11-01 15:05:36 +000049#include "WebKitAccessibleHyperlink.h"
mario@webkit.orgdeec8392012-01-23 14:45:23 +000050#include "WebKitAccessibleInterfaceAction.h"
mario@webkit.orgbe1ce552012-01-24 11:03:51 +000051#include "WebKitAccessibleInterfaceComponent.h"
mario@webkit.orgf8344ff2012-01-24 11:40:44 +000052#include "WebKitAccessibleInterfaceDocument.h"
mario@webkit.orgfc51ca62012-01-24 11:47:51 +000053#include "WebKitAccessibleInterfaceEditableText.h"
mario@webkit.org4dbd9822012-01-24 11:55:18 +000054#include "WebKitAccessibleInterfaceHyperlinkImpl.h"
mario@webkit.org70243532012-01-24 11:58:52 +000055#include "WebKitAccessibleInterfaceHypertext.h"
mario@webkit.orgda3e6082012-01-24 12:04:16 +000056#include "WebKitAccessibleInterfaceImage.h"
mario@webkit.org7e5931d2012-01-24 12:25:13 +000057#include "WebKitAccessibleInterfaceSelection.h"
mario@webkit.orgcd9f1b32012-01-24 18:28:22 +000058#include "WebKitAccessibleInterfaceTable.h"
mario@webkit.org987d7372012-01-24 18:02:08 +000059#include "WebKitAccessibleInterfaceText.h"
mario@webkit.org980269e2012-01-24 16:22:57 +000060#include "WebKitAccessibleInterfaceValue.h"
mario@webkit.org7ceffa42012-01-23 11:55:01 +000061#include "WebKitAccessibleUtil.h"
mario@webkit.orgba16aea2011-04-13 16:33:02 +000062#include "htmlediting.h"
xan@webkit.orge4387102009-04-09 11:08:48 +000063#include <glib/gprintf.h>
benjamin@webkit.org9d72cb02013-04-22 22:52:23 +000064#include <wtf/text/CString.h>
mario@webkit.org8c5dd902012-11-09 19:47:40 +000065
66#if PLATFORM(GTK)
mario@webkit.org970eaf32012-01-24 18:37:45 +000067#include <gtk/gtk.h>
mario@webkit.org8c5dd902012-11-09 19:47:40 +000068#endif
alp@webkit.org3a5e7ca2008-05-20 01:12:20 +000069
alp@webkit.org3a5e7ca2008-05-20 01:12:20 +000070using namespace WebCore;
71
mario@webkit.orgfecbacd2013-03-04 17:06:15 +000072struct _WebKitAccessiblePrivate {
73 // Cached data for AtkObject.
74 CString accessibleName;
75 CString accessibleDescription;
76
77 // Cached data for AtkAction.
78 CString actionName;
79 CString actionKeyBinding;
80
81 // Cached data for AtkDocument.
82 CString documentLocale;
83 CString documentType;
84 CString documentEncoding;
85 CString documentURI;
86
87 // Cached data for AtkImage.
88 CString imageDescription;
89};
90
91#define WEBKIT_ACCESSIBLE_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE((obj), WEBKIT_TYPE_ACCESSIBLE, WebKitAccessiblePrivate))
92
xan@webkit.orgc886cc62009-04-09 11:17:48 +000093static AccessibilityObject* fallbackObject()
94{
akling@apple.com88e05f62013-09-16 15:17:24 +000095 static AccessibilityObject* object = AccessibilityListBoxOption::create().leakRef();
xan@webkit.orgc886cc62009-04-09 11:17:48 +000096 return object;
97}
98
alp@webkit.orgc7738992008-05-27 02:48:14 +000099static AccessibilityObject* core(WebKitAccessible* accessible)
100{
101 if (!accessible)
102 return 0;
103
104 return accessible->m_object;
105}
106
107static AccessibilityObject* core(AtkObject* object)
108{
109 if (!WEBKIT_IS_ACCESSIBLE(object))
110 return 0;
111
112 return core(WEBKIT_ACCESSIBLE(object));
113}
114
mario@webkit.org5c966772012-01-24 18:48:50 +0000115static const gchar* webkitAccessibleGetName(AtkObject* object)
alp@webkit.org3a5e7ca2008-05-20 01:12:20 +0000116{
eric@webkit.org0f6cb452009-10-22 23:07:56 +0000117 AccessibilityObject* coreObject = core(object);
eric@webkit.org5a5bba52009-12-07 14:27:44 +0000118 if (!coreObject->isAccessibilityRenderObject())
mario@webkit.orgfecbacd2013-03-04 17:06:15 +0000119 return cacheAndReturnAtkProperty(object, AtkCachedAccessibleName, coreObject->stringValue());
eric@webkit.org5a5bba52009-12-07 14:27:44 +0000120
commit-queue@webkit.org802c0122012-09-11 01:41:27 +0000121 if (coreObject->isFieldset()) {
122 AccessibilityObject* label = coreObject->titleUIElement();
123 if (label) {
124 AtkObject* atkObject = label->wrapper();
125 if (ATK_IS_TEXT(atkObject))
126 return atk_text_get_text(ATK_TEXT(atkObject), 0, -1);
127 }
128 }
129
eric@webkit.org0f6cb452009-10-22 23:07:56 +0000130 if (coreObject->isControl()) {
mario@webkit.org7e9f2412011-04-06 16:50:25 +0000131 AccessibilityObject* label = coreObject->correspondingLabelForControlElement();
commit-queue@webkit.orgc5b95312010-06-24 03:18:17 +0000132 if (label) {
133 AtkObject* atkObject = label->wrapper();
134 if (ATK_IS_TEXT(atkObject))
mario@webkit.orgadc13a82012-01-24 21:18:36 +0000135 return atk_text_get_text(ATK_TEXT(atkObject), 0, -1);
commit-queue@webkit.orgc5b95312010-06-24 03:18:17 +0000136 }
commit-queue@webkit.orgee601242010-08-04 18:29:10 +0000137
mario@webkit.org92daa812011-02-16 17:12:38 +0000138 // Try text under the node.
mario@webkit.org7e9f2412011-04-06 16:50:25 +0000139 String textUnder = coreObject->textUnderElement();
commit-queue@webkit.orgee601242010-08-04 18:29:10 +0000140 if (textUnder.length())
mario@webkit.orgfecbacd2013-03-04 17:06:15 +0000141 return cacheAndReturnAtkProperty(object, AtkCachedAccessibleName, textUnder);
eric@webkit.org0f6cb452009-10-22 23:07:56 +0000142 }
eric@webkit.org5a5bba52009-12-07 14:27:44 +0000143
mario@webkit.org7e9f2412011-04-06 16:50:25 +0000144 if (coreObject->isImage() || coreObject->isInputImage()) {
145 Node* node = coreObject->node();
eric@webkit.org5a5bba52009-12-07 14:27:44 +0000146 if (node && node->isHTMLElement()) {
147 // Get the attribute rather than altText String so as not to fall back on title.
commit-queue@webkit.org03477c82011-09-02 17:07:51 +0000148 String alt = toHTMLElement(node)->getAttribute(HTMLNames::altAttr);
eric@webkit.org5a5bba52009-12-07 14:27:44 +0000149 if (!alt.isEmpty())
mario@webkit.orgfecbacd2013-03-04 17:06:15 +0000150 return cacheAndReturnAtkProperty(object, AtkCachedAccessibleName, alt);
eric@webkit.org5a5bba52009-12-07 14:27:44 +0000151 }
152 }
153
mario@webkit.org92daa812011-02-16 17:12:38 +0000154 // Fallback for the webArea object: just return the document's title.
mario@webkit.org7e9f2412011-04-06 16:50:25 +0000155 if (coreObject->isWebArea()) {
mario@webkit.org92daa812011-02-16 17:12:38 +0000156 Document* document = coreObject->document();
157 if (document)
mario@webkit.orgfecbacd2013-03-04 17:06:15 +0000158 return cacheAndReturnAtkProperty(object, AtkCachedAccessibleName, document->title());
mario@webkit.org92daa812011-02-16 17:12:38 +0000159 }
160
mario@webkit.orgd684f512011-11-29 11:00:58 +0000161 // Nothing worked so far, try with the AccessibilityObject's
162 // title() before going ahead with stringValue().
mario@webkit.org99e78ca2012-10-22 17:32:52 +0000163 String axTitle = accessibilityTitle(coreObject);
mario@webkit.orgd684f512011-11-29 11:00:58 +0000164 if (!axTitle.isEmpty())
mario@webkit.orgfecbacd2013-03-04 17:06:15 +0000165 return cacheAndReturnAtkProperty(object, AtkCachedAccessibleName, axTitle);
mario@webkit.orgd684f512011-11-29 11:00:58 +0000166
mario@webkit.orgfecbacd2013-03-04 17:06:15 +0000167 return cacheAndReturnAtkProperty(object, AtkCachedAccessibleName, coreObject->stringValue());
alp@webkit.org3a5e7ca2008-05-20 01:12:20 +0000168}
169
mario@webkit.org5c966772012-01-24 18:48:50 +0000170static const gchar* webkitAccessibleGetDescription(AtkObject* object)
alp@webkit.org3a5e7ca2008-05-20 01:12:20 +0000171{
eric@webkit.orgd7b62942009-10-28 16:53:41 +0000172 AccessibilityObject* coreObject = core(object);
eric@webkit.org5a5bba52009-12-07 14:27:44 +0000173 Node* node = 0;
174 if (coreObject->isAccessibilityRenderObject())
mario@webkit.org7e9f2412011-04-06 16:50:25 +0000175 node = coreObject->node();
eric@webkit.org5a5bba52009-12-07 14:27:44 +0000176 if (!node || !node->isHTMLElement() || coreObject->ariaRoleAttribute() != UnknownRole)
mario@webkit.orgfecbacd2013-03-04 17:06:15 +0000177 return cacheAndReturnAtkProperty(object, AtkCachedAccessibleDescription, accessibilityDescription(coreObject));
eric@webkit.orgd7b62942009-10-28 16:53:41 +0000178
179 // atk_table_get_summary returns an AtkObject. We have no summary object, so expose summary here.
eric@webkit.org5a5bba52009-12-07 14:27:44 +0000180 if (coreObject->roleValue() == TableRole) {
kangil.han@samsung.comb44a6812013-07-08 06:52:41 +0000181 String summary = toHTMLTableElement(node)->summary();
eric@webkit.org5a5bba52009-12-07 14:27:44 +0000182 if (!summary.isEmpty())
mario@webkit.orgfecbacd2013-03-04 17:06:15 +0000183 return cacheAndReturnAtkProperty(object, AtkCachedAccessibleDescription, summary);
eric@webkit.orgd7b62942009-10-28 16:53:41 +0000184 }
185
eric@webkit.org5a5bba52009-12-07 14:27:44 +0000186 // The title attribute should be reliably available as the object's descripton.
187 // 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 +0000188 String title = toHTMLElement(node)->title();
eric@webkit.org5a5bba52009-12-07 14:27:44 +0000189 if (!title.isEmpty())
mario@webkit.orgfecbacd2013-03-04 17:06:15 +0000190 return cacheAndReturnAtkProperty(object, AtkCachedAccessibleDescription, title);
eric@webkit.org5a5bba52009-12-07 14:27:44 +0000191
mario@webkit.orgfecbacd2013-03-04 17:06:15 +0000192 return cacheAndReturnAtkProperty(object, AtkCachedAccessibleDescription, accessibilityDescription(coreObject));
alp@webkit.org3a5e7ca2008-05-20 01:12:20 +0000193}
194
eric@webkit.org0f6cb452009-10-22 23:07:56 +0000195static void setAtkRelationSetFromCoreObject(AccessibilityObject* coreObject, AtkRelationSet* relationSet)
196{
commit-queue@webkit.org802c0122012-09-11 01:41:27 +0000197 if (coreObject->isFieldset()) {
198 AccessibilityObject* label = coreObject->titleUIElement();
199 if (label)
200 atk_relation_set_add_relation_by_type(relationSet, ATK_RELATION_LABELLED_BY, label->wrapper());
201 return;
202 }
203
204 if (coreObject->roleValue() == LegendRole) {
205 for (AccessibilityObject* parent = coreObject->parentObjectUnignored(); parent; parent = parent->parentObjectUnignored()) {
206 if (parent->isFieldset()) {
207 atk_relation_set_add_relation_by_type(relationSet, ATK_RELATION_LABEL_FOR, parent->wrapper());
208 break;
209 }
210 }
211 return;
212 }
213
mario@webkit.org7e9f2412011-04-06 16:50:25 +0000214 if (coreObject->isControl()) {
215 AccessibilityObject* label = coreObject->correspondingLabelForControlElement();
eric@webkit.org0f6cb452009-10-22 23:07:56 +0000216 if (label)
217 atk_relation_set_add_relation_by_type(relationSet, ATK_RELATION_LABELLED_BY, label->wrapper());
218 } else {
mario@webkit.org7e9f2412011-04-06 16:50:25 +0000219 AccessibilityObject* control = coreObject->correspondingControlForLabelElement();
eric@webkit.org0f6cb452009-10-22 23:07:56 +0000220 if (control)
221 atk_relation_set_add_relation_by_type(relationSet, ATK_RELATION_LABEL_FOR, control->wrapper());
222 }
223}
224
mario@webkit.orgf88ffcb2012-11-16 13:47:03 +0000225static gpointer webkitAccessibleParentClass = 0;
eric@webkit.orgb84b4742009-10-20 21:20:50 +0000226
mario@webkit.org86390a12011-01-07 17:34:02 +0000227static bool isRootObject(AccessibilityObject* coreObject)
alp@webkit.org3a5e7ca2008-05-20 01:12:20 +0000228{
mario@webkit.org86390a12011-01-07 17:34:02 +0000229 // The root accessible object in WebCore is always an object with
230 // the ScrolledArea role with one child with the WebArea role.
231 if (!coreObject || !coreObject->isScrollView())
232 return false;
alp@webkit.org3a5e7ca2008-05-20 01:12:20 +0000233
mario@webkit.org86390a12011-01-07 17:34:02 +0000234 AccessibilityObject* firstChild = coreObject->firstChild();
235 if (!firstChild || !firstChild->isWebArea())
236 return false;
237
238 return true;
239}
240
241static AtkObject* atkParentOfRootObject(AtkObject* object)
242{
243 AccessibilityObject* coreObject = core(object);
244 AccessibilityObject* coreParent = coreObject->parentObjectUnignored();
245
246 // The top level object claims to not have a parent. This makes it
eric@webkit.org03b220b2009-10-19 11:58:38 +0000247 // impossible for assistive technologies to ascend the accessible
248 // hierarchy all the way to the application. (Bug 30489)
mario@webkit.org86390a12011-01-07 17:34:02 +0000249 if (!coreParent && isRootObject(coreObject)) {
mario@webkit.org8d00fa72011-04-13 16:27:23 +0000250 Document* document = coreObject->document();
251 if (!document)
252 return 0;
253
mario@webkit.org8c5dd902012-11-09 19:47:40 +0000254#if PLATFORM(GTK)
mario@webkit.org8d00fa72011-04-13 16:27:23 +0000255 HostWindow* hostWindow = document->view()->hostWindow();
eric@webkit.org03b220b2009-10-19 11:58:38 +0000256 if (hostWindow) {
mario@webkit.org86390a12011-01-07 17:34:02 +0000257 PlatformPageClient scrollView = hostWindow->platformPageClient();
258 if (scrollView) {
259 GtkWidget* scrollViewParent = gtk_widget_get_parent(scrollView);
260 if (scrollViewParent)
261 return gtk_widget_get_accessible(scrollViewParent);
eric@webkit.org03b220b2009-10-19 11:58:38 +0000262 }
263 }
mario@webkit.org8c5dd902012-11-09 19:47:40 +0000264#endif // PLATFORM(GTK)
eric@webkit.org03b220b2009-10-19 11:58:38 +0000265 }
266
alp@webkit.org3a5e7ca2008-05-20 01:12:20 +0000267 if (!coreParent)
eric@webkit.org1f5f7fe2009-11-04 10:35:56 +0000268 return 0;
269
270 return coreParent->wrapper();
271}
272
mario@webkit.org5c966772012-01-24 18:48:50 +0000273static AtkObject* webkitAccessibleGetParent(AtkObject* object)
eric@webkit.org1f5f7fe2009-11-04 10:35:56 +0000274{
mario@webkit.org46e9b262011-11-17 11:50:07 +0000275 // Check first if the parent has been already set.
mario@webkit.orgf88ffcb2012-11-16 13:47:03 +0000276 AtkObject* accessibleParent = ATK_OBJECT_CLASS(webkitAccessibleParentClass)->get_parent(object);
mario@webkit.org46e9b262011-11-17 11:50:07 +0000277 if (accessibleParent)
278 return accessibleParent;
279
280 // Parent not set yet, so try to find it in the hierarchy.
mario@webkit.org86390a12011-01-07 17:34:02 +0000281 AccessibilityObject* coreObject = core(object);
mario@webkit.org868b5ea2011-06-24 09:06:32 +0000282 if (!coreObject)
283 return 0;
284
mario@webkit.org86390a12011-01-07 17:34:02 +0000285 AccessibilityObject* coreParent = coreObject->parentObjectUnignored();
mario@webkit.org868b5ea2011-06-24 09:06:32 +0000286
mario@webkit.org86390a12011-01-07 17:34:02 +0000287 if (!coreParent && isRootObject(coreObject))
288 return atkParentOfRootObject(object);
eric@webkit.org1f5f7fe2009-11-04 10:35:56 +0000289
290 if (!coreParent)
291 return 0;
alp@webkit.org3a5e7ca2008-05-20 01:12:20 +0000292
mario@webkit.org8c5dd902012-11-09 19:47:40 +0000293 // We don't expose table rows to Assistive technologies, but we
mario@webkit.org868b5ea2011-06-24 09:06:32 +0000294 // need to have them anyway in the hierarchy from WebCore to
295 // properly perform coordinates calculations when requested.
296 if (coreParent->isTableRow() && coreObject->isTableCell())
297 coreParent = coreParent->parentObjectUnignored();
298
alp@webkit.org3a5e7ca2008-05-20 01:12:20 +0000299 return coreParent->wrapper();
300}
301
mario@webkit.org868b5ea2011-06-24 09:06:32 +0000302static gint getNChildrenForTable(AccessibilityObject* coreObject)
303{
304 AccessibilityObject::AccessibilityChildrenVector tableChildren = coreObject->children();
305 size_t tableChildrenCount = tableChildren.size();
306 size_t cellsCount = 0;
307
308 // Look for the actual index of the cell inside the table.
309 for (unsigned i = 0; i < tableChildrenCount; ++i) {
310 if (tableChildren[i]->isTableRow()) {
311 AccessibilityObject::AccessibilityChildrenVector rowChildren = tableChildren[i]->children();
312 cellsCount += rowChildren.size();
313 } else
314 cellsCount++;
315 }
316
317 return cellsCount;
318}
319
mario@webkit.org5c966772012-01-24 18:48:50 +0000320static gint webkitAccessibleGetNChildren(AtkObject* object)
alp@webkit.org3a5e7ca2008-05-20 01:12:20 +0000321{
mario@webkit.org868b5ea2011-06-24 09:06:32 +0000322 AccessibilityObject* coreObject = core(object);
323
324 // Tables should be treated in a different way because rows should
mario@webkit.org8c5dd902012-11-09 19:47:40 +0000325 // be bypassed when exposing the accessible hierarchy.
mario@webkit.org868b5ea2011-06-24 09:06:32 +0000326 if (coreObject->isAccessibilityTable())
327 return getNChildrenForTable(coreObject);
328
329 return coreObject->children().size();
330}
331
332static AccessibilityObject* getChildForTable(AccessibilityObject* coreObject, gint index)
333{
334 AccessibilityObject::AccessibilityChildrenVector tableChildren = coreObject->children();
335 size_t tableChildrenCount = tableChildren.size();
336 size_t cellsCount = 0;
mario@webkit.org868b5ea2011-06-24 09:06:32 +0000337
338 // Look for the actual index of the cell inside the table.
339 size_t current = static_cast<size_t>(index);
340 for (unsigned i = 0; i < tableChildrenCount; ++i) {
341 if (tableChildren[i]->isTableRow()) {
342 AccessibilityObject::AccessibilityChildrenVector rowChildren = tableChildren[i]->children();
343 size_t rowChildrenCount = rowChildren.size();
344 if (current < cellsCount + rowChildrenCount)
345 return rowChildren.at(current - cellsCount).get();
346 cellsCount += rowChildrenCount;
347 } else if (cellsCount == current)
348 return tableChildren[i].get();
349 else
350 cellsCount++;
351 }
352
353 // Shouldn't reach if the child was found.
354 return 0;
alp@webkit.org3a5e7ca2008-05-20 01:12:20 +0000355}
356
mario@webkit.org5c966772012-01-24 18:48:50 +0000357static AtkObject* webkitAccessibleRefChild(AtkObject* object, gint index)
alp@webkit.org3a5e7ca2008-05-20 01:12:20 +0000358{
mario@webkit.org868b5ea2011-06-24 09:06:32 +0000359 if (index < 0)
eric@webkit.orgdbd4d402009-11-04 09:31:06 +0000360 return 0;
alp@webkit.org3a5e7ca2008-05-20 01:12:20 +0000361
mario@webkit.org868b5ea2011-06-24 09:06:32 +0000362 AccessibilityObject* coreObject = core(object);
363 AccessibilityObject* coreChild = 0;
364
mario@webkit.org8c5dd902012-11-09 19:47:40 +0000365 // Tables are special cases because rows should be bypassed, but
366 // still taking their cells into account.
mario@webkit.org868b5ea2011-06-24 09:06:32 +0000367 if (coreObject->isAccessibilityTable())
368 coreChild = getChildForTable(coreObject, index);
369 else {
370 AccessibilityObject::AccessibilityChildrenVector children = coreObject->children();
371 if (static_cast<unsigned>(index) >= children.size())
372 return 0;
373 coreChild = children.at(index).get();
374 }
alp@webkit.org3a5e7ca2008-05-20 01:12:20 +0000375
376 if (!coreChild)
eric@webkit.org4ff6fd72009-11-10 09:53:33 +0000377 return 0;
alp@webkit.org3a5e7ca2008-05-20 01:12:20 +0000378
379 AtkObject* child = coreChild->wrapper();
eric@webkit.orgb84b4742009-10-20 21:20:50 +0000380 atk_object_set_parent(child, object);
alp@webkit.org3a5e7ca2008-05-20 01:12:20 +0000381 g_object_ref(child);
382
383 return child;
384}
385
mario@webkit.org868b5ea2011-06-24 09:06:32 +0000386static gint getIndexInParentForCellInRow(AccessibilityObject* coreObject)
387{
388 AccessibilityObject* parent = coreObject->parentObjectUnignored();
389 if (!parent)
390 return -1;
391
392 AccessibilityObject* grandParent = parent->parentObjectUnignored();
393 if (!grandParent)
394 return -1;
395
396 AccessibilityObject::AccessibilityChildrenVector rows = grandParent->children();
397 size_t rowsCount = rows.size();
398 size_t previousCellsCount = 0;
399
400 // Look for the actual index of the cell inside the table.
401 for (unsigned i = 0; i < rowsCount; ++i) {
402 if (!rows[i]->isTableRow())
403 continue;
404
405 AccessibilityObject::AccessibilityChildrenVector cells = rows[i]->children();
406 size_t cellsCount = cells.size();
407
408 if (rows[i] == parent) {
409 for (unsigned j = 0; j < cellsCount; ++j) {
410 if (cells[j] == coreObject)
411 return previousCellsCount + j;
412 }
413 }
414
415 previousCellsCount += cellsCount;
416 }
417
418 return -1;
419}
420
mario@webkit.org5c966772012-01-24 18:48:50 +0000421static gint webkitAccessibleGetIndexInParent(AtkObject* object)
alp@webkit.org3a5e7ca2008-05-20 01:12:20 +0000422{
xan@webkit.org1f5349a2009-10-27 09:20:21 +0000423 AccessibilityObject* coreObject = core(object);
424 AccessibilityObject* parent = coreObject->parentObjectUnignored();
425
mario@webkit.org86390a12011-01-07 17:34:02 +0000426 if (!parent && isRootObject(coreObject)) {
427 AtkObject* atkParent = atkParentOfRootObject(object);
eric@webkit.org1f5f7fe2009-11-04 10:35:56 +0000428 if (!atkParent)
429 return -1;
430
431 unsigned count = atk_object_get_n_accessible_children(atkParent);
432 for (unsigned i = 0; i < count; ++i) {
433 AtkObject* child = atk_object_ref_accessible_child(atkParent, i);
434 bool childIsObject = child == object;
435 g_object_unref(child);
436 if (childIsObject)
437 return i;
438 }
439 }
xan@webkit.org1f5349a2009-10-27 09:20:21 +0000440
mario@webkit.org868b5ea2011-06-24 09:06:32 +0000441 // Need to calculate the index of the cell in the table, as
mario@webkit.org8c5dd902012-11-09 19:47:40 +0000442 // rows won't be exposed to assistive technologies.
mario@webkit.org868b5ea2011-06-24 09:06:32 +0000443 if (parent && parent->isTableRow() && coreObject->isTableCell())
444 return getIndexInParentForCellInRow(coreObject);
445
commit-queue@webkit.org54a624e2013-01-16 18:00:17 +0000446 if (!parent)
447 return -1;
448
commit-queue@webkit.orgbf183552012-02-22 09:28:57 +0000449 size_t index = parent->children().find(coreObject);
450 return (index == WTF::notFound) ? -1 : index;
alp@webkit.org3a5e7ca2008-05-20 01:12:20 +0000451}
452
mario@webkit.org5c966772012-01-24 18:48:50 +0000453static AtkAttributeSet* webkitAccessibleGetAttributes(AtkObject* object)
eric@webkit.org84496192009-10-17 20:00:33 +0000454{
eric@webkit.org4ff6fd72009-11-10 09:53:33 +0000455 AtkAttributeSet* attributeSet = 0;
mario@webkit.org8c5dd902012-11-09 19:47:40 +0000456#if PLATFORM(GTK)
mario@webkit.orgf8344ff2012-01-24 11:40:44 +0000457 attributeSet = addToAtkAttributeSet(attributeSet, "toolkit", "WebKitGtk");
commit-queue@webkit.orgd9947352012-12-05 17:56:12 +0000458#elif PLATFORM(EFL)
459 attributeSet = addToAtkAttributeSet(attributeSet, "toolkit", "WebKitEfl");
mario@webkit.org8c5dd902012-11-09 19:47:40 +0000460#endif
eric@webkit.orgf4efc402010-05-15 09:55:52 +0000461
commit-queue@webkit.orga9398dd2010-10-26 19:19:29 +0000462 AccessibilityObject* coreObject = core(object);
463 if (!coreObject)
464 return attributeSet;
465
commit-queue@webkit.org357dc222013-01-10 00:09:41 +0000466 // Hack needed for WebKit2 tests because obtaining an element by its ID
467 // cannot be done from the UIProcess. Assistive technologies have no need
468 // for this information.
469 Node* node = coreObject->node();
470 if (node && node->isElementNode()) {
471 String id = toElement(node)->getIdAttribute().string();
472 if (!id.isEmpty())
473 attributeSet = addToAtkAttributeSet(attributeSet, "html-id", id.utf8().data());
474 }
475
commit-queue@webkit.orga9398dd2010-10-26 19:19:29 +0000476 int headingLevel = coreObject->headingLevel();
eric@webkit.org84496192009-10-17 20:00:33 +0000477 if (headingLevel) {
478 String value = String::number(headingLevel);
mario@webkit.orgf8344ff2012-01-24 11:40:44 +0000479 attributeSet = addToAtkAttributeSet(attributeSet, "level", value.utf8().data());
eric@webkit.org84496192009-10-17 20:00:33 +0000480 }
commit-queue@webkit.orga9398dd2010-10-26 19:19:29 +0000481
482 // Set the 'layout-guess' attribute to help Assistive
483 // Technologies know when an exposed table is not data table.
484 if (coreObject->isAccessibilityTable() && !coreObject->isDataTable())
mario@webkit.orgf8344ff2012-01-24 11:40:44 +0000485 attributeSet = addToAtkAttributeSet(attributeSet, "layout-guess", "true");
commit-queue@webkit.orga9398dd2010-10-26 19:19:29 +0000486
commit-queue@webkit.org2db4aaa2012-12-10 01:09:32 +0000487 String placeholder = coreObject->placeholderValue();
488 if (!placeholder.isEmpty())
489 attributeSet = addToAtkAttributeSet(attributeSet, "placeholder-text", placeholder.utf8().data());
490
commit-queue@webkit.org5945f762013-07-16 12:55:56 +0000491 if (coreObject->ariaHasPopup())
mario@webkit.orge2f7e782013-09-18 08:28:59 +0000492 attributeSet = addToAtkAttributeSet(attributeSet, "haspopup", "true");
commit-queue@webkit.org5945f762013-07-16 12:55:56 +0000493
commit-queue@webkit.orge084cca2013-09-02 15:44:30 +0000494 String invalidStatus = coreObject->invalidStatus().string();
495 if (!invalidStatus.isEmpty() && invalidStatus != "false")
496 attributeSet = addToAtkAttributeSet(attributeSet, "aria-invalid", coreObject->invalidStatus().string().utf8().data());
497
commit-queue@webkit.org88d00b62013-09-03 11:29:33 +0000498 String helpText = coreObject->helpText();
499 if (!helpText.isEmpty())
500 attributeSet = addToAtkAttributeSet(attributeSet, "aria-help", helpText.utf8().data());
501
commit-queue@webkit.org88d00b62013-09-03 11:29:33 +0000502 AccessibilitySortDirection sortDirection = coreObject->sortDirection();
mario@webkit.orge2f7e782013-09-18 08:28:59 +0000503 if (sortDirection != SortDirectionNone) {
504 attributeSet = addToAtkAttributeSet(attributeSet, "sort",
505 sortDirection == SortDirectionAscending ? "ascending" : "descending");
506 }
commit-queue@webkit.org88d00b62013-09-03 11:29:33 +0000507
eric@webkit.org84496192009-10-17 20:00:33 +0000508 return attributeSet;
509}
510
alp@webkit.org3a5e7ca2008-05-20 01:12:20 +0000511static AtkRole atkRole(AccessibilityRole role)
512{
513 switch (role) {
xan@webkit.org0ea2f732009-04-27 21:56:05 +0000514 case UnknownRole:
515 return ATK_ROLE_UNKNOWN;
516 case ButtonRole:
alp@webkit.org3a5e7ca2008-05-20 01:12:20 +0000517 return ATK_ROLE_PUSH_BUTTON;
commit-queue@webkit.org6152cb92012-08-23 00:36:06 +0000518 case ToggleButtonRole:
519 return ATK_ROLE_TOGGLE_BUTTON;
xan@webkit.org0ea2f732009-04-27 21:56:05 +0000520 case RadioButtonRole:
alp@webkit.org3a5e7ca2008-05-20 01:12:20 +0000521 return ATK_ROLE_RADIO_BUTTON;
xan@webkit.org0ea2f732009-04-27 21:56:05 +0000522 case CheckBoxRole:
alp@webkit.org3a5e7ca2008-05-20 01:12:20 +0000523 return ATK_ROLE_CHECK_BOX;
xan@webkit.org0ea2f732009-04-27 21:56:05 +0000524 case SliderRole:
alp@webkit.org3a5e7ca2008-05-20 01:12:20 +0000525 return ATK_ROLE_SLIDER;
xan@webkit.org0ea2f732009-04-27 21:56:05 +0000526 case TabGroupRole:
commit-queue@webkit.org52553892012-12-13 15:43:38 +0000527 case TabListRole:
alp@webkit.org3a5e7ca2008-05-20 01:12:20 +0000528 return ATK_ROLE_PAGE_TAB_LIST;
xan@webkit.org0ea2f732009-04-27 21:56:05 +0000529 case TextFieldRole:
530 case TextAreaRole:
alp@webkit.org3a5e7ca2008-05-20 01:12:20 +0000531 return ATK_ROLE_ENTRY;
xan@webkit.org0ea2f732009-04-27 21:56:05 +0000532 case StaticTextRole:
533 return ATK_ROLE_TEXT;
534 case OutlineRole:
alp@webkit.org3a5e7ca2008-05-20 01:12:20 +0000535 return ATK_ROLE_TREE;
xan@webkit.org0ea2f732009-04-27 21:56:05 +0000536 case MenuBarRole:
537 return ATK_ROLE_MENU_BAR;
eric@webkit.orga0669292010-04-22 14:36:19 +0000538 case MenuListPopupRole:
xan@webkit.org0ea2f732009-04-27 21:56:05 +0000539 case MenuRole:
540 return ATK_ROLE_MENU;
eric@webkit.orga0669292010-04-22 14:36:19 +0000541 case MenuListOptionRole:
xan@webkit.org0ea2f732009-04-27 21:56:05 +0000542 case MenuItemRole:
543 return ATK_ROLE_MENU_ITEM;
commit-queue@webkit.org413f75b2013-09-11 11:31:14 +0000544 case MenuItemRadioRole:
545 return ATK_ROLE_RADIO_MENU_ITEM;
xan@webkit.org0ea2f732009-04-27 21:56:05 +0000546 case ColumnRole:
mario@webkit.org9adab662012-01-23 10:21:03 +0000547 // return ATK_ROLE_TABLE_COLUMN_HEADER; // Is this right?
xan@webkit.org0ea2f732009-04-27 21:56:05 +0000548 return ATK_ROLE_UNKNOWN; // Matches Mozilla
549 case RowRole:
mario@webkit.org9adab662012-01-23 10:21:03 +0000550 // return ATK_ROLE_TABLE_ROW_HEADER; // Is this right?
xan@webkit.org0ea2f732009-04-27 21:56:05 +0000551 return ATK_ROLE_LIST_ITEM; // Matches Mozilla
552 case ToolbarRole:
553 return ATK_ROLE_TOOL_BAR;
554 case BusyIndicatorRole:
555 return ATK_ROLE_PROGRESS_BAR; // Is this right?
556 case ProgressIndicatorRole:
mario@webkit.org9adab662012-01-23 10:21:03 +0000557 // return ATK_ROLE_SPIN_BUTTON; // Some confusion about this role in AccessibilityRenderObject.cpp
xan@webkit.org0ea2f732009-04-27 21:56:05 +0000558 return ATK_ROLE_PROGRESS_BAR;
559 case WindowRole:
560 return ATK_ROLE_WINDOW;
eric@webkit.orga0669292010-04-22 14:36:19 +0000561 case PopUpButtonRole:
xan@webkit.org0ea2f732009-04-27 21:56:05 +0000562 case ComboBoxRole:
563 return ATK_ROLE_COMBO_BOX;
564 case SplitGroupRole:
565 return ATK_ROLE_SPLIT_PANE;
566 case SplitterRole:
dmazzoni@google.comf3cf2c42012-09-07 23:46:45 +0000567 return ATK_ROLE_UNKNOWN;
xan@webkit.org0ea2f732009-04-27 21:56:05 +0000568 case ColorWellRole:
569 return ATK_ROLE_COLOR_CHOOSER;
570 case ListRole:
alp@webkit.org3a5e7ca2008-05-20 01:12:20 +0000571 return ATK_ROLE_LIST;
xan@webkit.org0ea2f732009-04-27 21:56:05 +0000572 case ScrollBarRole:
573 return ATK_ROLE_SCROLL_BAR;
mario@webkit.org86390a12011-01-07 17:34:02 +0000574 case ScrollAreaRole:
575 return ATK_ROLE_SCROLL_PANE;
commit-queue@webkit.org777f3cc2011-04-01 18:05:04 +0000576 case GridRole: // Is this right?
xan@webkit.org0ea2f732009-04-27 21:56:05 +0000577 case TableRole:
alp@webkit.org3a5e7ca2008-05-20 01:12:20 +0000578 return ATK_ROLE_TABLE;
xan@webkit.org0ea2f732009-04-27 21:56:05 +0000579 case ApplicationRole:
580 return ATK_ROLE_APPLICATION;
xan@webkit.org0ea2f732009-04-27 21:56:05 +0000581 case GroupRole:
582 case RadioGroupRole:
commit-queue@webkit.org52553892012-12-13 15:43:38 +0000583 case TabPanelRole:
xan@webkit.org0ea2f732009-04-27 21:56:05 +0000584 return ATK_ROLE_PANEL;
mario@webkit.org47a021b2011-04-11 17:39:11 +0000585 case RowHeaderRole: // Row headers are cells after all.
586 case ColumnHeaderRole: // Column headers are cells after all.
xan@webkit.org0ea2f732009-04-27 21:56:05 +0000587 case CellRole:
588 return ATK_ROLE_TABLE_CELL;
589 case LinkRole:
590 case WebCoreLinkRole:
591 case ImageMapLinkRole:
alp@webkit.org3a5e7ca2008-05-20 01:12:20 +0000592 return ATK_ROLE_LINK;
xan@webkit.org0ea2f732009-04-27 21:56:05 +0000593 case ImageMapRole:
594 case ImageRole:
alp@webkit.org3a5e7ca2008-05-20 01:12:20 +0000595 return ATK_ROLE_IMAGE;
xan@webkit.org0ea2f732009-04-27 21:56:05 +0000596 case ListMarkerRole:
jmalonzo@webkit.orgad9783b2009-05-23 22:22:52 +0000597 return ATK_ROLE_TEXT;
xan@webkit.org0ea2f732009-04-27 21:56:05 +0000598 case WebAreaRole:
mario@webkit.org9adab662012-01-23 10:21:03 +0000599 // return ATK_ROLE_HTML_CONTAINER; // Is this right?
xan@webkit.org0ea2f732009-04-27 21:56:05 +0000600 return ATK_ROLE_DOCUMENT_FRAME;
601 case HeadingRole:
602 return ATK_ROLE_HEADING;
603 case ListBoxRole:
604 return ATK_ROLE_LIST;
cfleizach@apple.com432ee572010-06-15 06:17:18 +0000605 case ListItemRole:
xan@webkit.org0ea2f732009-04-27 21:56:05 +0000606 case ListBoxOptionRole:
607 return ATK_ROLE_LIST_ITEM;
mario@webkit.org56f6bf22011-03-30 16:51:08 +0000608 case ParagraphRole:
609 return ATK_ROLE_PARAGRAPH;
610 case LabelRole:
commit-queue@webkit.org802c0122012-09-11 01:41:27 +0000611 case LegendRole:
mario@webkit.org56f6bf22011-03-30 16:51:08 +0000612 return ATK_ROLE_LABEL;
613 case DivRole:
614 return ATK_ROLE_SECTION;
615 case FormRole:
616 return ATK_ROLE_FORM;
commit-queue@webkit.org52f67612012-09-07 05:09:04 +0000617 case CanvasRole:
618 return ATK_ROLE_CANVAS;
dmazzoni@google.comf3cf2c42012-09-07 23:46:45 +0000619 case HorizontalRuleRole:
620 return ATK_ROLE_SEPARATOR;
commit-queue@webkit.orgc985400e2012-09-21 20:03:57 +0000621 case SpinButtonRole:
622 return ATK_ROLE_SPIN_BUTTON;
commit-queue@webkit.org52553892012-12-13 15:43:38 +0000623 case TabRole:
624 return ATK_ROLE_PAGE_TAB;
alp@webkit.org3a5e7ca2008-05-20 01:12:20 +0000625 default:
626 return ATK_ROLE_UNKNOWN;
627 }
628}
629
mario@webkit.org5c966772012-01-24 18:48:50 +0000630static AtkRole webkitAccessibleGetRole(AtkObject* object)
alp@webkit.org3a5e7ca2008-05-20 01:12:20 +0000631{
mario@webkit.org56f6bf22011-03-30 16:51:08 +0000632 AccessibilityObject* coreObject = core(object);
jmalonzo@webkit.org7ce37142009-05-20 11:16:01 +0000633
mario@webkit.org56f6bf22011-03-30 16:51:08 +0000634 if (!coreObject)
jmalonzo@webkit.org7ce37142009-05-20 11:16:01 +0000635 return ATK_ROLE_UNKNOWN;
636
637 // Note: Why doesn't WebCore have a password field for this
mario@webkit.org56f6bf22011-03-30 16:51:08 +0000638 if (coreObject->isPasswordField())
jmalonzo@webkit.org7ce37142009-05-20 11:16:01 +0000639 return ATK_ROLE_PASSWORD_TEXT;
640
mario@webkit.org56f6bf22011-03-30 16:51:08 +0000641 return atkRole(coreObject->roleValue());
alp@webkit.org3a5e7ca2008-05-20 01:12:20 +0000642}
643
mario@webkit.org6a24ba12010-12-14 15:35:22 +0000644static bool isTextWithCaret(AccessibilityObject* coreObject)
645{
646 if (!coreObject || !coreObject->isAccessibilityRenderObject())
647 return false;
648
649 Document* document = coreObject->document();
650 if (!document)
651 return false;
652
653 Frame* frame = document->frame();
654 if (!frame)
655 return false;
656
akling@apple.com17523502013-08-17 10:58:40 +0000657 if (!frame->settings().caretBrowsingEnabled())
mario@webkit.org6a24ba12010-12-14 15:35:22 +0000658 return false;
659
660 // Check text objects and paragraphs only.
661 AtkObject* axObject = coreObject->wrapper();
662 AtkRole role = axObject ? atk_object_get_role(axObject) : ATK_ROLE_INVALID;
663 if (role != ATK_ROLE_TEXT && role != ATK_ROLE_PARAGRAPH)
664 return false;
665
666 // Finally, check whether the caret is set in the current object.
667 VisibleSelection selection = coreObject->selection();
668 if (!selection.isCaret())
669 return false;
670
671 return selectionBelongsToObject(coreObject, selection);
672}
673
xan@webkit.org8be15d62009-04-09 11:20:57 +0000674static void setAtkStateSetFromCoreObject(AccessibilityObject* coreObject, AtkStateSet* stateSet)
675{
eric@webkit.org2dd47342009-10-26 11:42:42 +0000676 AccessibilityObject* parent = coreObject->parentObject();
677 bool isListBoxOption = parent && parent->isListBox();
xan@webkit.org8be15d62009-04-09 11:20:57 +0000678
eric@webkit.org2dd47342009-10-26 11:42:42 +0000679 // Please keep the state list in alphabetical order
xan@webkit.org8be15d62009-04-09 11:20:57 +0000680 if (coreObject->isChecked())
681 atk_state_set_add_state(stateSet, ATK_STATE_CHECKED);
682
xan@webkit.orgdf7d13a2009-06-26 09:12:44 +0000683 // FIXME: isReadOnly does not seem to do the right thing for
eric@webkit.org2dd47342009-10-26 11:42:42 +0000684 // controls, so check explicitly for them. In addition, because
685 // isReadOnly is false for listBoxOptions, we need to add one
686 // more check so that we do not present them as being "editable".
mario@webkit.org9adab662012-01-23 10:21:03 +0000687 if ((!coreObject->isReadOnly()
mario@webkit.orgf88ffcb2012-11-16 13:47:03 +0000688 || (coreObject->isControl() && coreObject->canSetValueAttribute()))
mario@webkit.org9adab662012-01-23 10:21:03 +0000689 && !isListBoxOption)
xan@webkit.org8be15d62009-04-09 11:20:57 +0000690 atk_state_set_add_state(stateSet, ATK_STATE_EDITABLE);
691
xan@webkit.orgdf7d13a2009-06-26 09:12:44 +0000692 // FIXME: Put both ENABLED and SENSITIVE together here for now
693 if (coreObject->isEnabled()) {
xan@webkit.org8be15d62009-04-09 11:20:57 +0000694 atk_state_set_add_state(stateSet, ATK_STATE_ENABLED);
xan@webkit.orgdf7d13a2009-06-26 09:12:44 +0000695 atk_state_set_add_state(stateSet, ATK_STATE_SENSITIVE);
696 }
xan@webkit.org8be15d62009-04-09 11:20:57 +0000697
mario@webkit.org8ce6a152010-11-30 21:16:14 +0000698 if (coreObject->canSetExpandedAttribute())
699 atk_state_set_add_state(stateSet, ATK_STATE_EXPANDABLE);
700
701 if (coreObject->isExpanded())
702 atk_state_set_add_state(stateSet, ATK_STATE_EXPANDED);
703
xan@webkit.org8be15d62009-04-09 11:20:57 +0000704 if (coreObject->canSetFocusAttribute())
705 atk_state_set_add_state(stateSet, ATK_STATE_FOCUSABLE);
706
mario@webkit.org6a24ba12010-12-14 15:35:22 +0000707 if (coreObject->isFocused() || isTextWithCaret(coreObject))
xan@webkit.org8be15d62009-04-09 11:20:57 +0000708 atk_state_set_add_state(stateSet, ATK_STATE_FOCUSED);
709
commit-queue@webkit.org471dff52013-03-20 08:12:15 +0000710 if (coreObject->orientation() == AccessibilityOrientationHorizontal)
711 atk_state_set_add_state(stateSet, ATK_STATE_HORIZONTAL);
712 else if (coreObject->orientation() == AccessibilityOrientationVertical)
713 atk_state_set_add_state(stateSet, ATK_STATE_VERTICAL);
xan@webkit.org8be15d62009-04-09 11:20:57 +0000714
715 if (coreObject->isIndeterminate())
716 atk_state_set_add_state(stateSet, ATK_STATE_INDETERMINATE);
717
jhoneycutt@apple.com8acea082010-01-14 01:16:15 +0000718 if (coreObject->isMultiSelectable())
xan@webkit.org8be15d62009-04-09 11:20:57 +0000719 atk_state_set_add_state(stateSet, ATK_STATE_MULTISELECTABLE);
720
721 // TODO: ATK_STATE_OPAQUE
722
723 if (coreObject->isPressed())
724 atk_state_set_add_state(stateSet, ATK_STATE_PRESSED);
725
commit-queue@webkit.org60f8d312013-06-21 10:33:34 +0000726 if (coreObject->isRequired())
727 atk_state_set_add_state(stateSet, ATK_STATE_REQUIRED);
728
xan@webkit.org8be15d62009-04-09 11:20:57 +0000729 // TODO: ATK_STATE_SELECTABLE_TEXT
730
eric@webkit.org2dd47342009-10-26 11:42:42 +0000731 if (coreObject->canSetSelectedAttribute()) {
732 atk_state_set_add_state(stateSet, ATK_STATE_SELECTABLE);
mario@webkit.org8c5dd902012-11-09 19:47:40 +0000733 // Items in focusable lists have both STATE_SELECT{ABLE,ED}
734 // and STATE_FOCUS{ABLE,ED}. We'll fake the latter based on
735 // the former.
eric@webkit.org2dd47342009-10-26 11:42:42 +0000736 if (isListBoxOption)
737 atk_state_set_add_state(stateSet, ATK_STATE_FOCUSABLE);
738 }
739
740 if (coreObject->isSelected()) {
xan@webkit.org8be15d62009-04-09 11:20:57 +0000741 atk_state_set_add_state(stateSet, ATK_STATE_SELECTED);
mario@webkit.org8c5dd902012-11-09 19:47:40 +0000742 // Items in focusable lists have both STATE_SELECT{ABLE,ED}
eric@webkit.org2dd47342009-10-26 11:42:42 +0000743 // and STATE_FOCUS{ABLE,ED}. We'll fake the latter based on the
744 // former.
745 if (isListBoxOption)
746 atk_state_set_add_state(stateSet, ATK_STATE_FOCUSED);
747 }
xan@webkit.org8be15d62009-04-09 11:20:57 +0000748
xan@webkit.orgdf7d13a2009-06-26 09:12:44 +0000749 // FIXME: Group both SHOWING and VISIBLE here for now
750 // Not sure how to handle this in WebKit, see bug
751 // http://bugzilla.gnome.org/show_bug.cgi?id=509650 for other
mario@webkit.org8c5dd902012-11-09 19:47:40 +0000752 // issues with SHOWING vs VISIBLE.
xan@webkit.orgdf7d13a2009-06-26 09:12:44 +0000753 if (!coreObject->isOffScreen()) {
xan@webkit.org8be15d62009-04-09 11:20:57 +0000754 atk_state_set_add_state(stateSet, ATK_STATE_SHOWING);
xan@webkit.orgdf7d13a2009-06-26 09:12:44 +0000755 atk_state_set_add_state(stateSet, ATK_STATE_VISIBLE);
756 }
xan@webkit.org8be15d62009-04-09 11:20:57 +0000757
758 // Mutually exclusive, so we group these two
759 if (coreObject->roleValue() == TextFieldRole)
760 atk_state_set_add_state(stateSet, ATK_STATE_SINGLE_LINE);
761 else if (coreObject->roleValue() == TextAreaRole)
762 atk_state_set_add_state(stateSet, ATK_STATE_MULTI_LINE);
763
764 // TODO: ATK_STATE_SENSITIVE
765
xan@webkit.org8be15d62009-04-09 11:20:57 +0000766 if (coreObject->isVisited())
767 atk_state_set_add_state(stateSet, ATK_STATE_VISITED);
768}
769
mario@webkit.org5c966772012-01-24 18:48:50 +0000770static AtkStateSet* webkitAccessibleRefStateSet(AtkObject* object)
xan@webkit.org8be15d62009-04-09 11:20:57 +0000771{
mario@webkit.orgf88ffcb2012-11-16 13:47:03 +0000772 AtkStateSet* stateSet = ATK_OBJECT_CLASS(webkitAccessibleParentClass)->ref_state_set(object);
xan@webkit.org8be15d62009-04-09 11:20:57 +0000773 AccessibilityObject* coreObject = core(object);
774
775 if (coreObject == fallbackObject()) {
776 atk_state_set_add_state(stateSet, ATK_STATE_DEFUNCT);
777 return stateSet;
778 }
779
mario@webkit.org6a24ba12010-12-14 15:35:22 +0000780 // Text objects must be focusable.
781 AtkRole role = atk_object_get_role(object);
782 if (role == ATK_ROLE_TEXT || role == ATK_ROLE_PARAGRAPH)
783 atk_state_set_add_state(stateSet, ATK_STATE_FOCUSABLE);
xan@webkit.org8be15d62009-04-09 11:20:57 +0000784
mario@webkit.org6a24ba12010-12-14 15:35:22 +0000785 setAtkStateSetFromCoreObject(coreObject, stateSet);
xan@webkit.org8be15d62009-04-09 11:20:57 +0000786 return stateSet;
787}
788
mario@webkit.org5c966772012-01-24 18:48:50 +0000789static AtkRelationSet* webkitAccessibleRefRelationSet(AtkObject* object)
eric@webkit.org0f6cb452009-10-22 23:07:56 +0000790{
mario@webkit.orgf88ffcb2012-11-16 13:47:03 +0000791 AtkRelationSet* relationSet = ATK_OBJECT_CLASS(webkitAccessibleParentClass)->ref_relation_set(object);
eric@webkit.org0f6cb452009-10-22 23:07:56 +0000792 AccessibilityObject* coreObject = core(object);
793
794 setAtkRelationSetFromCoreObject(coreObject, relationSet);
795
796 return relationSet;
797}
798
mario@webkit.org5c966772012-01-24 18:48:50 +0000799static void webkitAccessibleInit(AtkObject* object, gpointer data)
xan@webkit.orge4387102009-04-09 11:08:48 +0000800{
mario@webkit.orgf88ffcb2012-11-16 13:47:03 +0000801 if (ATK_OBJECT_CLASS(webkitAccessibleParentClass)->initialize)
802 ATK_OBJECT_CLASS(webkitAccessibleParentClass)->initialize(object, data);
xan@webkit.orge4387102009-04-09 11:08:48 +0000803
mario@webkit.orgfecbacd2013-03-04 17:06:15 +0000804 WebKitAccessible* accessible = WEBKIT_ACCESSIBLE(object);
805 accessible->m_object = reinterpret_cast<AccessibilityObject*>(data);
806 accessible->priv = WEBKIT_ACCESSIBLE_GET_PRIVATE(accessible);
xan@webkit.orge4387102009-04-09 11:08:48 +0000807}
808
commit-queue@webkit.org120f5652013-06-12 21:39:42 +0000809static const gchar* webkitAccessibleGetObjectLocale(AtkObject* object)
810{
811 if (ATK_IS_DOCUMENT(object)) {
812 AccessibilityObject* coreObject = core(object);
813 if (!coreObject)
814 return 0;
815
816 // TODO: Should we fall back on lang xml:lang when the following comes up empty?
817 String language = coreObject->language();
818 if (!language.isEmpty())
819 return cacheAndReturnAtkProperty(object, AtkCachedDocumentLocale, language);
820
821 } else if (ATK_IS_TEXT(object)) {
822 const gchar* locale = 0;
823
824 AtkAttributeSet* textAttributes = atk_text_get_default_attributes(ATK_TEXT(object));
commit-queue@webkit.org44a695d2013-07-04 12:10:45 +0000825 for (AtkAttributeSet* attributes = textAttributes; attributes; attributes = attributes->next) {
commit-queue@webkit.org120f5652013-06-12 21:39:42 +0000826 AtkAttribute* atkAttribute = static_cast<AtkAttribute*>(attributes->data);
827 if (!strcmp(atkAttribute->name, atk_text_attribute_get_name(ATK_TEXT_ATTR_LANGUAGE))) {
828 locale = cacheAndReturnAtkProperty(object, AtkCachedDocumentLocale, String::fromUTF8(atkAttribute->value));
829 break;
830 }
831 }
commit-queue@webkit.org120f5652013-06-12 21:39:42 +0000832 atk_attribute_set_free(textAttributes);
833
834 return locale;
835 }
836
837 return 0;
838}
839
mario@webkit.org5c966772012-01-24 18:48:50 +0000840static void webkitAccessibleFinalize(GObject* object)
xan@webkit.orge4387102009-04-09 11:08:48 +0000841{
mario@webkit.orgf88ffcb2012-11-16 13:47:03 +0000842 G_OBJECT_CLASS(webkitAccessibleParentClass)->finalize(object);
xan@webkit.orge4387102009-04-09 11:08:48 +0000843}
844
mario@webkit.orgf88ffcb2012-11-16 13:47:03 +0000845static void webkitAccessibleClassInit(AtkObjectClass* klass)
alp@webkit.org3a5e7ca2008-05-20 01:12:20 +0000846{
xan@webkit.orge4387102009-04-09 11:08:48 +0000847 GObjectClass* gobjectClass = G_OBJECT_CLASS(klass);
alp@webkit.org3a5e7ca2008-05-20 01:12:20 +0000848
mario@webkit.orgf88ffcb2012-11-16 13:47:03 +0000849 webkitAccessibleParentClass = g_type_class_peek_parent(klass);
xan@webkit.orge4387102009-04-09 11:08:48 +0000850
mario@webkit.org5c966772012-01-24 18:48:50 +0000851 gobjectClass->finalize = webkitAccessibleFinalize;
alp@webkit.org3a5e7ca2008-05-20 01:12:20 +0000852
mario@webkit.org5c966772012-01-24 18:48:50 +0000853 klass->initialize = webkitAccessibleInit;
854 klass->get_name = webkitAccessibleGetName;
855 klass->get_description = webkitAccessibleGetDescription;
856 klass->get_parent = webkitAccessibleGetParent;
857 klass->get_n_children = webkitAccessibleGetNChildren;
858 klass->ref_child = webkitAccessibleRefChild;
859 klass->get_role = webkitAccessibleGetRole;
860 klass->ref_state_set = webkitAccessibleRefStateSet;
861 klass->get_index_in_parent = webkitAccessibleGetIndexInParent;
862 klass->get_attributes = webkitAccessibleGetAttributes;
863 klass->ref_relation_set = webkitAccessibleRefRelationSet;
commit-queue@webkit.org120f5652013-06-12 21:39:42 +0000864 klass->get_object_locale = webkitAccessibleGetObjectLocale;
mario@webkit.orgfecbacd2013-03-04 17:06:15 +0000865
866 g_type_class_add_private(klass, sizeof(WebKitAccessiblePrivate));
xan@webkit.orge4387102009-04-09 11:08:48 +0000867}
868
869GType
mario@webkit.orgf88ffcb2012-11-16 13:47:03 +0000870webkitAccessibleGetType(void)
xan@webkit.orge4387102009-04-09 11:08:48 +0000871{
mario@webkit.orgf88ffcb2012-11-16 13:47:03 +0000872 static volatile gsize typeVolatile = 0;
xan@webkit.orge4387102009-04-09 11:08:48 +0000873
mario@webkit.orgf88ffcb2012-11-16 13:47:03 +0000874 if (g_once_init_enter(&typeVolatile)) {
xan@webkit.orge4387102009-04-09 11:08:48 +0000875 static const GTypeInfo tinfo = {
876 sizeof(WebKitAccessibleClass),
eric@webkit.org4ff6fd72009-11-10 09:53:33 +0000877 (GBaseInitFunc) 0,
878 (GBaseFinalizeFunc) 0,
mario@webkit.orgf88ffcb2012-11-16 13:47:03 +0000879 (GClassInitFunc) webkitAccessibleClassInit,
eric@webkit.org4ff6fd72009-11-10 09:53:33 +0000880 (GClassFinalizeFunc) 0,
881 0, /* class data */
xan@webkit.orge4387102009-04-09 11:08:48 +0000882 sizeof(WebKitAccessible), /* instance size */
883 0, /* nb preallocs */
eric@webkit.org4ff6fd72009-11-10 09:53:33 +0000884 (GInstanceInitFunc) 0,
885 0 /* value table */
xan@webkit.orge4387102009-04-09 11:08:48 +0000886 };
887
mario@webkit.orgf88ffcb2012-11-16 13:47:03 +0000888 GType type = g_type_register_static(ATK_TYPE_OBJECT, "WebKitAccessible", &tinfo, GTypeFlags(0));
889 g_once_init_leave(&typeVolatile, type);
xan@webkit.orge4387102009-04-09 11:08:48 +0000890 }
891
mario@webkit.orgf88ffcb2012-11-16 13:47:03 +0000892 return typeVolatile;
alp@webkit.orgc7738992008-05-27 02:48:14 +0000893}
894
xan@webkit.orge4387102009-04-09 11:08:48 +0000895static const GInterfaceInfo AtkInterfacesInitFunctions[] = {
mario@webkit.orgdeec8392012-01-23 14:45:23 +0000896 {reinterpret_cast<GInterfaceInitFunc>(webkitAccessibleActionInterfaceInit), 0, 0},
mario@webkit.org7e5931d2012-01-24 12:25:13 +0000897 {reinterpret_cast<GInterfaceInitFunc>(webkitAccessibleSelectionInterfaceInit), 0, 0},
mario@webkit.orgfc51ca62012-01-24 11:47:51 +0000898 {reinterpret_cast<GInterfaceInitFunc>(webkitAccessibleEditableTextInterfaceInit), 0, 0},
mario@webkit.org987d7372012-01-24 18:02:08 +0000899 {reinterpret_cast<GInterfaceInitFunc>(webkitAccessibleTextInterfaceInit), 0, 0},
mario@webkit.orgbe1ce552012-01-24 11:03:51 +0000900 {reinterpret_cast<GInterfaceInitFunc>(webkitAccessibleComponentInterfaceInit), 0, 0},
mario@webkit.orgda3e6082012-01-24 12:04:16 +0000901 {reinterpret_cast<GInterfaceInitFunc>(webkitAccessibleImageInterfaceInit), 0, 0},
mario@webkit.orgcd9f1b32012-01-24 18:28:22 +0000902 {reinterpret_cast<GInterfaceInitFunc>(webkitAccessibleTableInterfaceInit), 0, 0},
mario@webkit.org70243532012-01-24 11:58:52 +0000903 {reinterpret_cast<GInterfaceInitFunc>(webkitAccessibleHypertextInterfaceInit), 0, 0},
mario@webkit.org4dbd9822012-01-24 11:55:18 +0000904 {reinterpret_cast<GInterfaceInitFunc>(webkitAccessibleHyperlinkImplInterfaceInit), 0, 0},
mario@webkit.orgf8344ff2012-01-24 11:40:44 +0000905 {reinterpret_cast<GInterfaceInitFunc>(webkitAccessibleDocumentInterfaceInit), 0, 0},
mario@webkit.org980269e2012-01-24 16:22:57 +0000906 {reinterpret_cast<GInterfaceInitFunc>(webkitAccessibleValueInterfaceInit), 0, 0}
xan@webkit.orge4387102009-04-09 11:08:48 +0000907};
908
909enum WAIType {
910 WAI_ACTION,
eric@webkit.orgf84ff632009-10-29 17:34:39 +0000911 WAI_SELECTION,
xan@webkit.orge4387102009-04-09 11:08:48 +0000912 WAI_EDITABLE_TEXT,
xan@webkit.org92b91692009-04-21 07:02:17 +0000913 WAI_TEXT,
xan@webkit.orge43e70b2009-04-27 21:33:55 +0000914 WAI_COMPONENT,
xan@webkit.org45b26ac2009-10-27 12:20:35 +0000915 WAI_IMAGE,
eric@webkit.org65e12ba2009-11-01 21:22:30 +0000916 WAI_TABLE,
mario@webkit.org7f95c622010-11-01 15:05:36 +0000917 WAI_HYPERTEXT,
918 WAI_HYPERLINK,
mario@webkit.org04e38e92011-03-28 10:16:38 +0000919 WAI_DOCUMENT,
920 WAI_VALUE,
xan@webkit.orge4387102009-04-09 11:08:48 +0000921};
922
923static GType GetAtkInterfaceTypeFromWAIType(WAIType type)
alp@webkit.org3a5e7ca2008-05-20 01:12:20 +0000924{
mario@webkit.org7f95c622010-11-01 15:05:36 +0000925 switch (type) {
926 case WAI_ACTION:
927 return ATK_TYPE_ACTION;
928 case WAI_SELECTION:
929 return ATK_TYPE_SELECTION;
930 case WAI_EDITABLE_TEXT:
931 return ATK_TYPE_EDITABLE_TEXT;
932 case WAI_TEXT:
933 return ATK_TYPE_TEXT;
934 case WAI_COMPONENT:
935 return ATK_TYPE_COMPONENT;
936 case WAI_IMAGE:
937 return ATK_TYPE_IMAGE;
938 case WAI_TABLE:
939 return ATK_TYPE_TABLE;
940 case WAI_HYPERTEXT:
941 return ATK_TYPE_HYPERTEXT;
942 case WAI_HYPERLINK:
943 return ATK_TYPE_HYPERLINK_IMPL;
944 case WAI_DOCUMENT:
945 return ATK_TYPE_DOCUMENT;
mario@webkit.org04e38e92011-03-28 10:16:38 +0000946 case WAI_VALUE:
947 return ATK_TYPE_VALUE;
mario@webkit.org7f95c622010-11-01 15:05:36 +0000948 }
alp@webkit.org3a5e7ca2008-05-20 01:12:20 +0000949
mario@webkit.org7f95c622010-11-01 15:05:36 +0000950 return G_TYPE_INVALID;
xan@webkit.orge4387102009-04-09 11:08:48 +0000951}
alp@webkit.org3a5e7ca2008-05-20 01:12:20 +0000952
commit-queue@webkit.orgca1b1d22012-08-22 01:37:04 +0000953static bool roleIsTextType(AccessibilityRole role)
954{
zandobersek@gmail.com2b2075f2012-09-01 08:53:50 +0000955 return role == ParagraphRole || role == HeadingRole || role == DivRole || role == CellRole || role == ListItemRole;
commit-queue@webkit.orgca1b1d22012-08-22 01:37:04 +0000956}
957
xan@webkit.orge4387102009-04-09 11:08:48 +0000958static guint16 getInterfaceMaskFromObject(AccessibilityObject* coreObject)
959{
960 guint16 interfaceMask = 0;
alp@webkit.orgc7738992008-05-27 02:48:14 +0000961
xan@webkit.org92b91692009-04-21 07:02:17 +0000962 // Component interface is always supported
963 interfaceMask |= 1 << WAI_COMPONENT;
964
mario@webkit.org7f95c622010-11-01 15:05:36 +0000965 AccessibilityRole role = coreObject->roleValue();
966
xan@webkit.orgb3c59bf2009-04-21 07:01:27 +0000967 // Action
mario@webkit.org8ce6a152010-11-30 21:16:14 +0000968 // As the implementation of the AtkAction interface is a very
969 // basic one (just relays in executing the default action for each
970 // object, and only supports having one action per object), it is
971 // better just to implement this interface for every instance of
972 // the WebKitAccessible class and let WebCore decide what to do.
973 interfaceMask |= 1 << WAI_ACTION;
xan@webkit.orgb3c59bf2009-04-21 07:01:27 +0000974
eric@webkit.orgf84ff632009-10-29 17:34:39 +0000975 // Selection
mario@webkit.org8ce6a152010-11-30 21:16:14 +0000976 if (coreObject->isListBox() || coreObject->isMenuList())
eric@webkit.orgf84ff632009-10-29 17:34:39 +0000977 interfaceMask |= 1 << WAI_SELECTION;
978
mario@webkit.org9f0aee12011-04-11 21:02:23 +0000979 // Get renderer if available.
980 RenderObject* renderer = 0;
981 if (coreObject->isAccessibilityRenderObject())
982 renderer = coreObject->renderer();
983
984 // Hyperlink (links and embedded objects).
985 if (coreObject->isLink() || (renderer && renderer->isReplaced()))
986 interfaceMask |= 1 << WAI_HYPERLINK;
987
xan@webkit.orge4387102009-04-09 11:08:48 +0000988 // Text & Editable Text
mario@webkit.org8ce6a152010-11-30 21:16:14 +0000989 if (role == StaticTextRole || coreObject->isMenuListOption())
xan@webkit.org7a07be52009-04-14 15:56:10 +0000990 interfaceMask |= 1 << WAI_TEXT;
mario@webkit.org9f0aee12011-04-11 21:02:23 +0000991 else {
eric@webkit.org10e02f82010-01-07 02:05:01 +0000992 if (coreObject->isTextControl()) {
993 interfaceMask |= 1 << WAI_TEXT;
994 if (!coreObject->isReadOnly())
995 interfaceMask |= 1 << WAI_EDITABLE_TEXT;
commit-queue@webkit.org1f3aafe2010-09-22 07:49:34 +0000996 } else {
mario@webkit.org7f95c622010-11-01 15:05:36 +0000997 if (role != TableRole) {
998 interfaceMask |= 1 << WAI_HYPERTEXT;
commit-queue@webkit.orgca1b1d22012-08-22 01:37:04 +0000999 if ((renderer && renderer->childrenInline()) || roleIsTextType(role))
mario@webkit.org7f95c622010-11-01 15:05:36 +00001000 interfaceMask |= 1 << WAI_TEXT;
1001 }
1002
1003 // Add the TEXT interface for list items whose
1004 // first accessible child has a text renderer
1005 if (role == ListItemRole) {
mario@webkit.org7e9f2412011-04-06 16:50:25 +00001006 AccessibilityObject::AccessibilityChildrenVector children = coreObject->children();
commit-queue@webkit.org1f3aafe2010-09-22 07:49:34 +00001007 if (children.size()) {
1008 AccessibilityObject* axRenderChild = children.at(0).get();
1009 interfaceMask |= getInterfaceMaskFromObject(axRenderChild);
1010 }
1011 }
1012 }
mario@webkit.org7f95c622010-11-01 15:05:36 +00001013 }
xan@webkit.orge4387102009-04-09 11:08:48 +00001014
xan@webkit.orge43e70b2009-04-27 21:33:55 +00001015 // Image
1016 if (coreObject->isImage())
1017 interfaceMask |= 1 << WAI_IMAGE;
1018
xan@webkit.org45b26ac2009-10-27 12:20:35 +00001019 // Table
1020 if (role == TableRole)
1021 interfaceMask |= 1 << WAI_TABLE;
1022
eric@webkit.org65e12ba2009-11-01 21:22:30 +00001023 // Document
1024 if (role == WebAreaRole)
1025 interfaceMask |= 1 << WAI_DOCUMENT;
1026
mario@webkit.org04e38e92011-03-28 10:16:38 +00001027 // Value
commit-queue@webkit.org35af6cf2013-09-16 14:00:35 +00001028 if (role == SliderRole || role == SpinButtonRole || role == ScrollBarRole || role == ProgressIndicatorRole)
mario@webkit.org04e38e92011-03-28 10:16:38 +00001029 interfaceMask |= 1 << WAI_VALUE;
1030
commit-queue@webkit.orgfd70fd32013-09-04 14:19:02 +00001031#if ENABLE(INPUT_TYPE_COLOR)
1032 // Color type.
1033 if (role == ColorWellRole)
1034 interfaceMask |= 1 << WAI_TEXT;
1035#endif
1036
xan@webkit.orge4387102009-04-09 11:08:48 +00001037 return interfaceMask;
1038}
1039
1040static const char* getUniqueAccessibilityTypeName(guint16 interfaceMask)
1041{
1042#define WAI_TYPE_NAME_LEN (30) /* Enough for prefix + 5 hex characters (max) */
1043 static char name[WAI_TYPE_NAME_LEN + 1];
eric@webkit.org4ff6fd72009-11-10 09:53:33 +00001044
xan@webkit.orge4387102009-04-09 11:08:48 +00001045 g_sprintf(name, "WAIType%x", interfaceMask);
1046 name[WAI_TYPE_NAME_LEN] = '\0';
eric@webkit.org4ff6fd72009-11-10 09:53:33 +00001047
xan@webkit.orge4387102009-04-09 11:08:48 +00001048 return name;
1049}
1050
1051static GType getAccessibilityTypeFromObject(AccessibilityObject* coreObject)
1052{
1053 static const GTypeInfo typeInfo = {
1054 sizeof(WebKitAccessibleClass),
eric@webkit.org4ff6fd72009-11-10 09:53:33 +00001055 (GBaseInitFunc) 0,
1056 (GBaseFinalizeFunc) 0,
1057 (GClassInitFunc) 0,
1058 (GClassFinalizeFunc) 0,
1059 0, /* class data */
xan@webkit.orge4387102009-04-09 11:08:48 +00001060 sizeof(WebKitAccessible), /* instance size */
1061 0, /* nb preallocs */
eric@webkit.org4ff6fd72009-11-10 09:53:33 +00001062 (GInstanceInitFunc) 0,
1063 0 /* value table */
xan@webkit.orge4387102009-04-09 11:08:48 +00001064 };
1065
1066 guint16 interfaceMask = getInterfaceMaskFromObject(coreObject);
1067 const char* atkTypeName = getUniqueAccessibilityTypeName(interfaceMask);
1068 GType type = g_type_from_name(atkTypeName);
1069 if (type)
1070 return type;
1071
mario@webkit.orgf88ffcb2012-11-16 13:47:03 +00001072 type = g_type_register_static(WEBKIT_TYPE_ACCESSIBLE, atkTypeName, &typeInfo, GTypeFlags(0));
xan@webkit.orge4387102009-04-09 11:08:48 +00001073 for (guint i = 0; i < G_N_ELEMENTS(AtkInterfacesInitFunctions); i++) {
1074 if (interfaceMask & (1 << i))
1075 g_type_add_interface_static(type,
mario@webkit.orgf88ffcb2012-11-16 13:47:03 +00001076 GetAtkInterfaceTypeFromWAIType(static_cast<WAIType>(i)),
1077 &AtkInterfacesInitFunctions[i]);
xan@webkit.orge4387102009-04-09 11:08:48 +00001078 }
1079
alp@webkit.org3a5e7ca2008-05-20 01:12:20 +00001080 return type;
1081}
1082
mario@webkit.orgaacb2172012-01-23 11:43:28 +00001083WebKitAccessible* webkitAccessibleNew(AccessibilityObject* coreObject)
alp@webkit.org3a5e7ca2008-05-20 01:12:20 +00001084{
xan@webkit.orge4387102009-04-09 11:08:48 +00001085 GType type = getAccessibilityTypeFromObject(coreObject);
eric@webkit.org4ff6fd72009-11-10 09:53:33 +00001086 AtkObject* object = static_cast<AtkObject*>(g_object_new(type, 0));
xan@webkit.orge4387102009-04-09 11:08:48 +00001087
alp@webkit.org3a5e7ca2008-05-20 01:12:20 +00001088 atk_object_initialize(object, coreObject);
xan@webkit.orge4387102009-04-09 11:08:48 +00001089
alp@webkit.org3a5e7ca2008-05-20 01:12:20 +00001090 return WEBKIT_ACCESSIBLE(object);
1091}
1092
mario@webkit.orgaacb2172012-01-23 11:43:28 +00001093AccessibilityObject* webkitAccessibleGetAccessibilityObject(WebKitAccessible* accessible)
alp@webkit.org3a5e7ca2008-05-20 01:12:20 +00001094{
1095 return accessible->m_object;
1096}
1097
mario@webkit.orgaacb2172012-01-23 11:43:28 +00001098void webkitAccessibleDetach(WebKitAccessible* accessible)
alp@webkit.org3a5e7ca2008-05-20 01:12:20 +00001099{
alp@webkit.orgc7738992008-05-27 02:48:14 +00001100 ASSERT(accessible->m_object);
1101
mario@webkit.org92daa812011-02-16 17:12:38 +00001102 if (core(accessible)->roleValue() == WebAreaRole)
1103 g_signal_emit_by_name(accessible, "state-change", "defunct", true);
1104
alp@webkit.orgc7738992008-05-27 02:48:14 +00001105 // We replace the WebCore AccessibilityObject with a fallback object that
1106 // provides default implementations to avoid repetitive null-checking after
1107 // detachment.
xan@webkit.orgc886cc62009-04-09 11:17:48 +00001108 accessible->m_object = fallbackObject();
alp@webkit.org3a5e7ca2008-05-20 01:12:20 +00001109}
1110
mario@webkit.orgaacb2172012-01-23 11:43:28 +00001111AtkObject* webkitAccessibleGetFocusedElement(WebKitAccessible* accessible)
jmalonzo@webkit.orgaaaea3b2009-08-08 07:39:48 +00001112{
1113 if (!accessible->m_object)
1114 return 0;
1115
1116 RefPtr<AccessibilityObject> focusedObj = accessible->m_object->focusedUIElement();
1117 if (!focusedObj)
1118 return 0;
1119
1120 return focusedObj->wrapper();
1121}
1122
mario@webkit.org4c778d72012-01-09 10:16:20 +00001123AccessibilityObject* objectFocusedAndCaretOffsetUnignored(AccessibilityObject* referenceObject, int& offset)
eric@webkit.org2fade492010-01-11 12:03:36 +00001124{
eric@webkit.org9cf227c2010-01-19 21:31:01 +00001125 // Indication that something bogus has transpired.
1126 offset = -1;
eric@webkit.org2fade492010-01-11 12:03:36 +00001127
mario@webkit.org4c778d72012-01-09 10:16:20 +00001128 Document* document = referenceObject->document();
1129 if (!document)
mrobinson@webkit.orgbc2c5f22010-09-29 17:58:13 +00001130 return 0;
eric@webkit.org2fade492010-01-11 12:03:36 +00001131
mario@webkit.org4c778d72012-01-09 10:16:20 +00001132 Node* focusedNode = referenceObject->selection().end().containerNode();
1133 if (!focusedNode)
mrobinson@webkit.orgbc2c5f22010-09-29 17:58:13 +00001134 return 0;
eric@webkit.org2fade492010-01-11 12:03:36 +00001135
mario@webkit.org4c778d72012-01-09 10:16:20 +00001136 RenderObject* focusedRenderer = focusedNode->renderer();
1137 if (!focusedRenderer)
1138 return 0;
mario@webkit.org6698d352011-02-01 09:49:25 +00001139
mario@webkit.org4c778d72012-01-09 10:16:20 +00001140 AccessibilityObject* focusedObject = document->axObjectCache()->getOrCreate(focusedRenderer);
1141 if (!focusedObject)
1142 return 0;
mario@webkit.org6698d352011-02-01 09:49:25 +00001143
mario@webkit.org4c778d72012-01-09 10:16:20 +00001144 // Look for the actual (not ignoring accessibility) selected object.
mario@webkit.orgd048f762012-01-22 19:29:04 +00001145 AccessibilityObject* firstUnignoredParent = focusedObject;
1146 if (firstUnignoredParent->accessibilityIsIgnored())
1147 firstUnignoredParent = firstUnignoredParent->parentObjectUnignored();
1148 if (!firstUnignoredParent)
mario@webkit.org4c778d72012-01-09 10:16:20 +00001149 return 0;
1150
1151 // Don't ignore links if the offset is being requested for a link.
mario@webkit.orgd048f762012-01-22 19:29:04 +00001152 if (!referenceObject->isLink() && firstUnignoredParent->isLink())
1153 firstUnignoredParent = firstUnignoredParent->parentObjectUnignored();
1154 if (!firstUnignoredParent)
mario@webkit.org4c778d72012-01-09 10:16:20 +00001155 return 0;
1156
mario@webkit.orgd048f762012-01-22 19:29:04 +00001157 // The reference object must either coincide with the focused
1158 // object being considered, or be a descendant of it.
1159 if (referenceObject->isDescendantOfObject(firstUnignoredParent))
1160 referenceObject = firstUnignoredParent;
1161
mario@webkit.org4c778d72012-01-09 10:16:20 +00001162 Node* startNode = 0;
mario@webkit.orgd048f762012-01-22 19:29:04 +00001163 if (firstUnignoredParent != referenceObject || firstUnignoredParent->isTextControl()) {
mario@webkit.org4c778d72012-01-09 10:16:20 +00001164 // We need to use the first child's node of the reference
1165 // object as the start point to calculate the caret offset
1166 // because we want it to be relative to the object of
1167 // reference, not just to the focused object (which could have
1168 // previous siblings which should be taken into account too).
1169 AccessibilityObject* axFirstChild = referenceObject->firstChild();
1170 if (axFirstChild)
1171 startNode = axFirstChild->node();
1172 }
commit-queue@webkit.orgfb3e9152013-02-01 00:37:22 +00001173 // Getting the Position of a PseudoElement now triggers an assertion.
1174 // This can occur when clicking on empty space in a render block.
1175 if (!startNode || startNode->isPseudoElement())
mario@webkit.orgd048f762012-01-22 19:29:04 +00001176 startNode = firstUnignoredParent->node();
mario@webkit.org4c778d72012-01-09 10:16:20 +00001177
mario@webkit.orgb311bdb2012-02-01 17:58:57 +00001178 // Check if the node for the first parent object not ignoring
1179 // accessibility is null again before using it. This might happen
1180 // with certain kind of accessibility objects, such as the root
1181 // one (the scroller containing the webArea object).
1182 if (!startNode)
1183 return 0;
1184
mario@webkit.orgd048f762012-01-22 19:29:04 +00001185 VisiblePosition startPosition = VisiblePosition(positionBeforeNode(startNode), DOWNSTREAM);
1186 VisiblePosition endPosition = firstUnignoredParent->selection().visibleEnd();
mario@webkit.org4c778d72012-01-09 10:16:20 +00001187
1188 if (startPosition == endPosition)
1189 offset = 0;
1190 else if (!isStartOfLine(endPosition)) {
1191 RefPtr<Range> range = makeRange(startPosition, endPosition.previous());
1192 offset = TextIterator::rangeLength(range.get(), true) + 1;
1193 } else {
1194 RefPtr<Range> range = makeRange(startPosition, endPosition);
1195 offset = TextIterator::rangeLength(range.get(), true);
eric@webkit.org9cf227c2010-01-19 21:31:01 +00001196 }
mario@webkit.orgff7fd422011-01-27 19:38:03 +00001197
mario@webkit.orgd048f762012-01-22 19:29:04 +00001198 return firstUnignoredParent;
eric@webkit.org2fade492010-01-11 12:03:36 +00001199}
1200
mario@webkit.orgfecbacd2013-03-04 17:06:15 +00001201const char* cacheAndReturnAtkProperty(AtkObject* object, AtkCachedProperty property, String value)
1202{
1203 WebKitAccessiblePrivate* priv = WEBKIT_ACCESSIBLE(object)->priv;
1204 CString* propertyPtr = 0;
1205
1206 switch (property) {
1207 case AtkCachedAccessibleName:
1208 propertyPtr = &priv->accessibleName;
1209 break;
1210
1211 case AtkCachedAccessibleDescription:
1212 propertyPtr = &priv->accessibleDescription;
1213 break;
1214
1215 case AtkCachedActionName:
1216 propertyPtr = &priv->actionName;
1217 break;
1218
1219 case AtkCachedActionKeyBinding:
1220 propertyPtr = &priv->actionKeyBinding;
1221 break;
1222
1223 case AtkCachedDocumentLocale:
1224 propertyPtr = &priv->documentLocale;
1225 break;
1226
1227 case AtkCachedDocumentType:
1228 propertyPtr = &priv->documentType;
1229 break;
1230
1231 case AtkCachedDocumentEncoding:
1232 propertyPtr = &priv->documentEncoding;
1233 break;
1234
1235 case AtkCachedDocumentURI:
1236 propertyPtr = &priv->documentURI;
1237 break;
1238
1239 case AtkCachedImageDescription:
1240 propertyPtr = &priv->imageDescription;
1241 break;
1242
1243 default:
1244 ASSERT_NOT_REACHED();
1245 }
1246
1247 // Don't invalidate old memory if not stricly needed, since other
1248 // callers might be still holding on to it.
1249 if (*propertyPtr != value.utf8())
1250 *propertyPtr = value.utf8();
1251
1252 return (*propertyPtr).data();
1253}
1254
ddkilzer@apple.com8d878632008-11-09 19:50:37 +00001255#endif // HAVE(ACCESSIBILITY)