blob: b43ecf8be19fff4debe36cd733220ed58b69d3ac [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"
mario.prada@samsung.com4fb934b2013-12-17 18:08:34 +000038#include "AccessibilityList.h"
akling@apple.com88e05f62013-09-16 15:17:24 +000039#include "AccessibilityListBoxOption.h"
alp@webkit.orgc7738992008-05-27 02:48:14 +000040#include "Document.h"
alp@webkit.orgc7738992008-05-27 02:48:14 +000041#include "Frame.h"
42#include "FrameView.h"
xan@webkit.org9561b2c2009-05-20 14:33:19 +000043#include "HTMLNames.h"
xan@webkit.org45b26ac2009-10-27 12:20:35 +000044#include "HTMLTableElement.h"
mario@webkit.org9adab662012-01-23 10:21:03 +000045#include "HostWindow.h"
mario@webkit.org970eaf32012-01-24 18:37:45 +000046#include "RenderObject.h"
mario@webkit.org6a24ba12010-12-14 15:35:22 +000047#include "Settings.h"
commit-queue@webkit.org74bd2e82010-09-12 11:16:14 +000048#include "TextIterator.h"
tkent@chromium.org8c35c122013-03-06 13:00:14 +000049#include "VisibleUnits.h"
mario@webkit.org7f95c622010-11-01 15:05:36 +000050#include "WebKitAccessibleHyperlink.h"
mario@webkit.orgdeec8392012-01-23 14:45:23 +000051#include "WebKitAccessibleInterfaceAction.h"
mario@webkit.orgbe1ce552012-01-24 11:03:51 +000052#include "WebKitAccessibleInterfaceComponent.h"
mario@webkit.orgf8344ff2012-01-24 11:40:44 +000053#include "WebKitAccessibleInterfaceDocument.h"
mario@webkit.orgfc51ca62012-01-24 11:47:51 +000054#include "WebKitAccessibleInterfaceEditableText.h"
mario@webkit.org4dbd9822012-01-24 11:55:18 +000055#include "WebKitAccessibleInterfaceHyperlinkImpl.h"
mario@webkit.org70243532012-01-24 11:58:52 +000056#include "WebKitAccessibleInterfaceHypertext.h"
mario@webkit.orgda3e6082012-01-24 12:04:16 +000057#include "WebKitAccessibleInterfaceImage.h"
mario@webkit.org7e5931d2012-01-24 12:25:13 +000058#include "WebKitAccessibleInterfaceSelection.h"
mario@webkit.orgcd9f1b32012-01-24 18:28:22 +000059#include "WebKitAccessibleInterfaceTable.h"
mario@webkit.org987d7372012-01-24 18:02:08 +000060#include "WebKitAccessibleInterfaceText.h"
mario@webkit.org980269e2012-01-24 16:22:57 +000061#include "WebKitAccessibleInterfaceValue.h"
mario@webkit.org7ceffa42012-01-23 11:55:01 +000062#include "WebKitAccessibleUtil.h"
mario@webkit.orgba16aea2011-04-13 16:33:02 +000063#include "htmlediting.h"
xan@webkit.orge4387102009-04-09 11:08:48 +000064#include <glib/gprintf.h>
benjamin@webkit.org9d72cb02013-04-22 22:52:23 +000065#include <wtf/text/CString.h>
mario@webkit.org8c5dd902012-11-09 19:47:40 +000066
67#if PLATFORM(GTK)
mario@webkit.org970eaf32012-01-24 18:37:45 +000068#include <gtk/gtk.h>
mario@webkit.org8c5dd902012-11-09 19:47:40 +000069#endif
alp@webkit.org3a5e7ca2008-05-20 01:12:20 +000070
alp@webkit.org3a5e7ca2008-05-20 01:12:20 +000071using namespace WebCore;
72
mario@webkit.orgfecbacd2013-03-04 17:06:15 +000073struct _WebKitAccessiblePrivate {
74 // Cached data for AtkObject.
75 CString accessibleName;
76 CString accessibleDescription;
77
78 // Cached data for AtkAction.
79 CString actionName;
80 CString actionKeyBinding;
81
82 // Cached data for AtkDocument.
83 CString documentLocale;
84 CString documentType;
85 CString documentEncoding;
86 CString documentURI;
87
88 // Cached data for AtkImage.
89 CString imageDescription;
90};
91
92#define WEBKIT_ACCESSIBLE_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE((obj), WEBKIT_TYPE_ACCESSIBLE, WebKitAccessiblePrivate))
93
xan@webkit.orgc886cc62009-04-09 11:17:48 +000094static AccessibilityObject* fallbackObject()
95{
akling@apple.com88e05f62013-09-16 15:17:24 +000096 static AccessibilityObject* object = AccessibilityListBoxOption::create().leakRef();
xan@webkit.orgc886cc62009-04-09 11:17:48 +000097 return object;
98}
99
alp@webkit.orgc7738992008-05-27 02:48:14 +0000100static AccessibilityObject* core(AtkObject* object)
101{
102 if (!WEBKIT_IS_ACCESSIBLE(object))
103 return 0;
104
mario@webkit.orgff8cf0f2013-09-27 09:44:20 +0000105 return webkitAccessibleGetAccessibilityObject(WEBKIT_ACCESSIBLE(object));
alp@webkit.orgc7738992008-05-27 02:48:14 +0000106}
107
mario@webkit.org5c966772012-01-24 18:48:50 +0000108static const gchar* webkitAccessibleGetName(AtkObject* object)
alp@webkit.org3a5e7ca2008-05-20 01:12:20 +0000109{
mario@webkit.orgff8cf0f2013-09-27 09:44:20 +0000110 g_return_val_if_fail(WEBKIT_IS_ACCESSIBLE(object), 0);
111 returnValIfWebKitAccessibleIsInvalid(WEBKIT_ACCESSIBLE(object), 0);
112
eric@webkit.org0f6cb452009-10-22 23:07:56 +0000113 AccessibilityObject* coreObject = core(object);
mario@webkit.orgff8cf0f2013-09-27 09:44:20 +0000114
eric@webkit.org5a5bba52009-12-07 14:27:44 +0000115 if (!coreObject->isAccessibilityRenderObject())
mario@webkit.orgfecbacd2013-03-04 17:06:15 +0000116 return cacheAndReturnAtkProperty(object, AtkCachedAccessibleName, coreObject->stringValue());
eric@webkit.org5a5bba52009-12-07 14:27:44 +0000117
commit-queue@webkit.org802c0122012-09-11 01:41:27 +0000118 if (coreObject->isFieldset()) {
119 AccessibilityObject* label = coreObject->titleUIElement();
120 if (label) {
121 AtkObject* atkObject = label->wrapper();
122 if (ATK_IS_TEXT(atkObject))
123 return atk_text_get_text(ATK_TEXT(atkObject), 0, -1);
124 }
125 }
126
eric@webkit.org0f6cb452009-10-22 23:07:56 +0000127 if (coreObject->isControl()) {
mario@webkit.org7e9f2412011-04-06 16:50:25 +0000128 AccessibilityObject* label = coreObject->correspondingLabelForControlElement();
commit-queue@webkit.orgc5b95312010-06-24 03:18:17 +0000129 if (label) {
130 AtkObject* atkObject = label->wrapper();
131 if (ATK_IS_TEXT(atkObject))
mario@webkit.orgadc13a82012-01-24 21:18:36 +0000132 return atk_text_get_text(ATK_TEXT(atkObject), 0, -1);
commit-queue@webkit.orgc5b95312010-06-24 03:18:17 +0000133 }
commit-queue@webkit.orgee601242010-08-04 18:29:10 +0000134
mario@webkit.org92daa812011-02-16 17:12:38 +0000135 // Try text under the node.
mario@webkit.org7e9f2412011-04-06 16:50:25 +0000136 String textUnder = coreObject->textUnderElement();
commit-queue@webkit.orgee601242010-08-04 18:29:10 +0000137 if (textUnder.length())
mario@webkit.orgfecbacd2013-03-04 17:06:15 +0000138 return cacheAndReturnAtkProperty(object, AtkCachedAccessibleName, textUnder);
eric@webkit.org0f6cb452009-10-22 23:07:56 +0000139 }
eric@webkit.org5a5bba52009-12-07 14:27:44 +0000140
mario@webkit.org7e9f2412011-04-06 16:50:25 +0000141 if (coreObject->isImage() || coreObject->isInputImage()) {
142 Node* node = coreObject->node();
eric@webkit.org5a5bba52009-12-07 14:27:44 +0000143 if (node && node->isHTMLElement()) {
144 // Get the attribute rather than altText String so as not to fall back on title.
commit-queue@webkit.org03477c82011-09-02 17:07:51 +0000145 String alt = toHTMLElement(node)->getAttribute(HTMLNames::altAttr);
eric@webkit.org5a5bba52009-12-07 14:27:44 +0000146 if (!alt.isEmpty())
mario@webkit.orgfecbacd2013-03-04 17:06:15 +0000147 return cacheAndReturnAtkProperty(object, AtkCachedAccessibleName, alt);
eric@webkit.org5a5bba52009-12-07 14:27:44 +0000148 }
149 }
150
mario@webkit.org92daa812011-02-16 17:12:38 +0000151 // Fallback for the webArea object: just return the document's title.
mario@webkit.org7e9f2412011-04-06 16:50:25 +0000152 if (coreObject->isWebArea()) {
mario@webkit.org92daa812011-02-16 17:12:38 +0000153 Document* document = coreObject->document();
154 if (document)
mario@webkit.orgfecbacd2013-03-04 17:06:15 +0000155 return cacheAndReturnAtkProperty(object, AtkCachedAccessibleName, document->title());
mario@webkit.org92daa812011-02-16 17:12:38 +0000156 }
157
mario@webkit.orgd684f512011-11-29 11:00:58 +0000158 // Nothing worked so far, try with the AccessibilityObject's
159 // title() before going ahead with stringValue().
mario@webkit.org99e78ca2012-10-22 17:32:52 +0000160 String axTitle = accessibilityTitle(coreObject);
mario@webkit.orgd684f512011-11-29 11:00:58 +0000161 if (!axTitle.isEmpty())
mario@webkit.orgfecbacd2013-03-04 17:06:15 +0000162 return cacheAndReturnAtkProperty(object, AtkCachedAccessibleName, axTitle);
mario@webkit.orgd684f512011-11-29 11:00:58 +0000163
mario@webkit.orgfecbacd2013-03-04 17:06:15 +0000164 return cacheAndReturnAtkProperty(object, AtkCachedAccessibleName, coreObject->stringValue());
alp@webkit.org3a5e7ca2008-05-20 01:12:20 +0000165}
166
mario@webkit.org5c966772012-01-24 18:48:50 +0000167static const gchar* webkitAccessibleGetDescription(AtkObject* object)
alp@webkit.org3a5e7ca2008-05-20 01:12:20 +0000168{
mario@webkit.orgff8cf0f2013-09-27 09:44:20 +0000169 g_return_val_if_fail(WEBKIT_IS_ACCESSIBLE(object), 0);
170 returnValIfWebKitAccessibleIsInvalid(WEBKIT_ACCESSIBLE(object), 0);
171
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();
commit-queue@webkit.org79c0a1d2013-10-23 12:13:23 +0000176 if (!node || !node->isHTMLElement() || coreObject->ariaRoleAttribute() != UnknownRole || coreObject->isImage())
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
m.pakula@samsung.come026b6a2013-11-06 18:17:03 +0000195static void removeAtkRelationByType(AtkRelationSet* relationSet, AtkRelationType relationType)
196{
197 int count = atk_relation_set_get_n_relations(relationSet);
198 for (int i = 0; i < count; i++) {
199 AtkRelation* relation = atk_relation_set_get_relation(relationSet, i);
200 if (atk_relation_get_relation_type(relation) == relationType) {
201 atk_relation_set_remove(relationSet, relation);
202 break;
203 }
204 }
205}
206
eric@webkit.org0f6cb452009-10-22 23:07:56 +0000207static void setAtkRelationSetFromCoreObject(AccessibilityObject* coreObject, AtkRelationSet* relationSet)
208{
commit-queue@webkit.org802c0122012-09-11 01:41:27 +0000209 if (coreObject->isFieldset()) {
210 AccessibilityObject* label = coreObject->titleUIElement();
m.pakula@samsung.come026b6a2013-11-06 18:17:03 +0000211 if (label) {
212 removeAtkRelationByType(relationSet, ATK_RELATION_LABELLED_BY);
commit-queue@webkit.org802c0122012-09-11 01:41:27 +0000213 atk_relation_set_add_relation_by_type(relationSet, ATK_RELATION_LABELLED_BY, label->wrapper());
m.pakula@samsung.come026b6a2013-11-06 18:17:03 +0000214 }
commit-queue@webkit.org802c0122012-09-11 01:41:27 +0000215 return;
216 }
217
218 if (coreObject->roleValue() == LegendRole) {
219 for (AccessibilityObject* parent = coreObject->parentObjectUnignored(); parent; parent = parent->parentObjectUnignored()) {
220 if (parent->isFieldset()) {
221 atk_relation_set_add_relation_by_type(relationSet, ATK_RELATION_LABEL_FOR, parent->wrapper());
222 break;
223 }
224 }
225 return;
226 }
227
mario@webkit.org7e9f2412011-04-06 16:50:25 +0000228 if (coreObject->isControl()) {
229 AccessibilityObject* label = coreObject->correspondingLabelForControlElement();
m.pakula@samsung.come026b6a2013-11-06 18:17:03 +0000230 if (label) {
231 removeAtkRelationByType(relationSet, ATK_RELATION_LABELLED_BY);
eric@webkit.org0f6cb452009-10-22 23:07:56 +0000232 atk_relation_set_add_relation_by_type(relationSet, ATK_RELATION_LABELLED_BY, label->wrapper());
m.pakula@samsung.come026b6a2013-11-06 18:17:03 +0000233 }
eric@webkit.org0f6cb452009-10-22 23:07:56 +0000234 } else {
mario@webkit.org7e9f2412011-04-06 16:50:25 +0000235 AccessibilityObject* control = coreObject->correspondingControlForLabelElement();
eric@webkit.org0f6cb452009-10-22 23:07:56 +0000236 if (control)
237 atk_relation_set_add_relation_by_type(relationSet, ATK_RELATION_LABEL_FOR, control->wrapper());
238 }
k.czech@samsung.com22545812014-01-21 09:10:25 +0000239
240 // Check whether object supports aria-flowto
241 if (coreObject->supportsARIAFlowTo()) {
242 removeAtkRelationByType(relationSet, ATK_RELATION_FLOWS_TO);
243 AccessibilityObject::AccessibilityChildrenVector ariaFlowToElements;
244 coreObject->ariaFlowToElements(ariaFlowToElements);
245 for (const auto& accessibilityObject : ariaFlowToElements)
246 atk_relation_set_add_relation_by_type(relationSet, ATK_RELATION_FLOWS_TO, accessibilityObject->wrapper());
247 }
k.czech@samsung.com7e9868e2014-01-29 13:16:27 +0000248
249 // Check whether object supports aria-describedby. It provides an additional information for the user.
250 if (coreObject->supportsARIADescribedBy()) {
251 removeAtkRelationByType(relationSet, ATK_RELATION_DESCRIBED_BY);
252 AccessibilityObject::AccessibilityChildrenVector ariaDescribedByElements;
253 coreObject->ariaDescribedByElements(ariaDescribedByElements);
254 for (const auto& accessibilityObject : ariaDescribedByElements)
255 atk_relation_set_add_relation_by_type(relationSet, ATK_RELATION_DESCRIBED_BY, accessibilityObject->wrapper());
256 }
k.czech@samsung.comb8ad4bf2014-02-03 09:16:54 +0000257
258 // Check whether object supports aria-controls. It provides information about elements that are controlled by the current object.
259 if (coreObject->supportsARIAControls()) {
260 removeAtkRelationByType(relationSet, ATK_RELATION_CONTROLLER_FOR);
261 AccessibilityObject::AccessibilityChildrenVector ariaControls;
262 coreObject->ariaControlsElements(ariaControls);
263 for (const auto& accessibilityObject : ariaControls)
264 atk_relation_set_add_relation_by_type(relationSet, ATK_RELATION_CONTROLLER_FOR, accessibilityObject->wrapper());
265 }
eric@webkit.org0f6cb452009-10-22 23:07:56 +0000266}
267
mario@webkit.orgf88ffcb2012-11-16 13:47:03 +0000268static gpointer webkitAccessibleParentClass = 0;
eric@webkit.orgb84b4742009-10-20 21:20:50 +0000269
mario@webkit.org86390a12011-01-07 17:34:02 +0000270static bool isRootObject(AccessibilityObject* coreObject)
alp@webkit.org3a5e7ca2008-05-20 01:12:20 +0000271{
mario@webkit.org86390a12011-01-07 17:34:02 +0000272 // The root accessible object in WebCore is always an object with
273 // the ScrolledArea role with one child with the WebArea role.
274 if (!coreObject || !coreObject->isScrollView())
275 return false;
alp@webkit.org3a5e7ca2008-05-20 01:12:20 +0000276
mario@webkit.org86390a12011-01-07 17:34:02 +0000277 AccessibilityObject* firstChild = coreObject->firstChild();
278 if (!firstChild || !firstChild->isWebArea())
279 return false;
280
281 return true;
282}
283
284static AtkObject* atkParentOfRootObject(AtkObject* object)
285{
286 AccessibilityObject* coreObject = core(object);
287 AccessibilityObject* coreParent = coreObject->parentObjectUnignored();
288
289 // The top level object claims to not have a parent. This makes it
eric@webkit.org03b220b2009-10-19 11:58:38 +0000290 // impossible for assistive technologies to ascend the accessible
291 // hierarchy all the way to the application. (Bug 30489)
mario@webkit.org86390a12011-01-07 17:34:02 +0000292 if (!coreParent && isRootObject(coreObject)) {
mario@webkit.org8d00fa72011-04-13 16:27:23 +0000293 Document* document = coreObject->document();
294 if (!document)
295 return 0;
296
mario@webkit.org8c5dd902012-11-09 19:47:40 +0000297#if PLATFORM(GTK)
mario@webkit.org8d00fa72011-04-13 16:27:23 +0000298 HostWindow* hostWindow = document->view()->hostWindow();
eric@webkit.org03b220b2009-10-19 11:58:38 +0000299 if (hostWindow) {
mario@webkit.org86390a12011-01-07 17:34:02 +0000300 PlatformPageClient scrollView = hostWindow->platformPageClient();
301 if (scrollView) {
302 GtkWidget* scrollViewParent = gtk_widget_get_parent(scrollView);
303 if (scrollViewParent)
304 return gtk_widget_get_accessible(scrollViewParent);
eric@webkit.org03b220b2009-10-19 11:58:38 +0000305 }
306 }
mario@webkit.org8c5dd902012-11-09 19:47:40 +0000307#endif // PLATFORM(GTK)
eric@webkit.org03b220b2009-10-19 11:58:38 +0000308 }
309
alp@webkit.org3a5e7ca2008-05-20 01:12:20 +0000310 if (!coreParent)
eric@webkit.org1f5f7fe2009-11-04 10:35:56 +0000311 return 0;
312
313 return coreParent->wrapper();
314}
315
mario@webkit.org5c966772012-01-24 18:48:50 +0000316static AtkObject* webkitAccessibleGetParent(AtkObject* object)
eric@webkit.org1f5f7fe2009-11-04 10:35:56 +0000317{
mario@webkit.orgff8cf0f2013-09-27 09:44:20 +0000318 g_return_val_if_fail(WEBKIT_IS_ACCESSIBLE(object), 0);
319 returnValIfWebKitAccessibleIsInvalid(WEBKIT_ACCESSIBLE(object), 0);
320
mario@webkit.org46e9b262011-11-17 11:50:07 +0000321 // Check first if the parent has been already set.
mario@webkit.orgf88ffcb2012-11-16 13:47:03 +0000322 AtkObject* accessibleParent = ATK_OBJECT_CLASS(webkitAccessibleParentClass)->get_parent(object);
mario@webkit.org46e9b262011-11-17 11:50:07 +0000323 if (accessibleParent)
324 return accessibleParent;
325
326 // Parent not set yet, so try to find it in the hierarchy.
mario@webkit.org86390a12011-01-07 17:34:02 +0000327 AccessibilityObject* coreObject = core(object);
mario@webkit.org868b5ea2011-06-24 09:06:32 +0000328 if (!coreObject)
329 return 0;
330
mario@webkit.org86390a12011-01-07 17:34:02 +0000331 AccessibilityObject* coreParent = coreObject->parentObjectUnignored();
mario@webkit.org868b5ea2011-06-24 09:06:32 +0000332
mario@webkit.org86390a12011-01-07 17:34:02 +0000333 if (!coreParent && isRootObject(coreObject))
334 return atkParentOfRootObject(object);
eric@webkit.org1f5f7fe2009-11-04 10:35:56 +0000335
336 if (!coreParent)
337 return 0;
alp@webkit.org3a5e7ca2008-05-20 01:12:20 +0000338
mario@webkit.org8c5dd902012-11-09 19:47:40 +0000339 // We don't expose table rows to Assistive technologies, but we
mario@webkit.org868b5ea2011-06-24 09:06:32 +0000340 // need to have them anyway in the hierarchy from WebCore to
341 // properly perform coordinates calculations when requested.
342 if (coreParent->isTableRow() && coreObject->isTableCell())
343 coreParent = coreParent->parentObjectUnignored();
344
alp@webkit.org3a5e7ca2008-05-20 01:12:20 +0000345 return coreParent->wrapper();
346}
347
mario@webkit.org868b5ea2011-06-24 09:06:32 +0000348static gint getNChildrenForTable(AccessibilityObject* coreObject)
349{
zandobersek@gmail.comc60dc0a2014-01-13 11:19:38 +0000350 const AccessibilityObject::AccessibilityChildrenVector& tableChildren = coreObject->children();
mario@webkit.org868b5ea2011-06-24 09:06:32 +0000351 size_t cellsCount = 0;
352
353 // Look for the actual index of the cell inside the table.
zandobersek@gmail.comf4216bb2014-01-17 11:02:02 +0000354 for (const auto& tableChild : tableChildren) {
355 if (tableChild->isTableRow())
356 cellsCount += tableChild->children().size();
357 else
mario@webkit.org868b5ea2011-06-24 09:06:32 +0000358 cellsCount++;
359 }
360
361 return cellsCount;
362}
363
mario@webkit.org5c966772012-01-24 18:48:50 +0000364static gint webkitAccessibleGetNChildren(AtkObject* object)
alp@webkit.org3a5e7ca2008-05-20 01:12:20 +0000365{
mario@webkit.orgff8cf0f2013-09-27 09:44:20 +0000366 g_return_val_if_fail(WEBKIT_IS_ACCESSIBLE(object), 0);
367 returnValIfWebKitAccessibleIsInvalid(WEBKIT_ACCESSIBLE(object), 0);
368
mario@webkit.org868b5ea2011-06-24 09:06:32 +0000369 AccessibilityObject* coreObject = core(object);
370
371 // Tables should be treated in a different way because rows should
mario@webkit.org8c5dd902012-11-09 19:47:40 +0000372 // be bypassed when exposing the accessible hierarchy.
mario@webkit.org868b5ea2011-06-24 09:06:32 +0000373 if (coreObject->isAccessibilityTable())
374 return getNChildrenForTable(coreObject);
375
376 return coreObject->children().size();
377}
378
379static AccessibilityObject* getChildForTable(AccessibilityObject* coreObject, gint index)
380{
zandobersek@gmail.comc60dc0a2014-01-13 11:19:38 +0000381 const AccessibilityObject::AccessibilityChildrenVector& tableChildren = coreObject->children();
mario@webkit.org868b5ea2011-06-24 09:06:32 +0000382 size_t cellsCount = 0;
mario@webkit.org868b5ea2011-06-24 09:06:32 +0000383
384 // Look for the actual index of the cell inside the table.
385 size_t current = static_cast<size_t>(index);
zandobersek@gmail.comf4216bb2014-01-17 11:02:02 +0000386 for (const auto& tableChild : tableChildren) {
387 if (tableChild->isTableRow()) {
388 const AccessibilityObject::AccessibilityChildrenVector& rowChildren = tableChild->children();
mario@webkit.org868b5ea2011-06-24 09:06:32 +0000389 size_t rowChildrenCount = rowChildren.size();
390 if (current < cellsCount + rowChildrenCount)
391 return rowChildren.at(current - cellsCount).get();
392 cellsCount += rowChildrenCount;
393 } else if (cellsCount == current)
zandobersek@gmail.comf4216bb2014-01-17 11:02:02 +0000394 return tableChild.get();
mario@webkit.org868b5ea2011-06-24 09:06:32 +0000395 else
396 cellsCount++;
397 }
398
399 // Shouldn't reach if the child was found.
400 return 0;
alp@webkit.org3a5e7ca2008-05-20 01:12:20 +0000401}
402
mario@webkit.org5c966772012-01-24 18:48:50 +0000403static AtkObject* webkitAccessibleRefChild(AtkObject* object, gint index)
alp@webkit.org3a5e7ca2008-05-20 01:12:20 +0000404{
mario@webkit.orgff8cf0f2013-09-27 09:44:20 +0000405 g_return_val_if_fail(WEBKIT_IS_ACCESSIBLE(object), 0);
406 returnValIfWebKitAccessibleIsInvalid(WEBKIT_ACCESSIBLE(object), 0);
407
mario@webkit.org868b5ea2011-06-24 09:06:32 +0000408 if (index < 0)
eric@webkit.orgdbd4d402009-11-04 09:31:06 +0000409 return 0;
alp@webkit.org3a5e7ca2008-05-20 01:12:20 +0000410
mario@webkit.org868b5ea2011-06-24 09:06:32 +0000411 AccessibilityObject* coreObject = core(object);
412 AccessibilityObject* coreChild = 0;
413
mario@webkit.org8c5dd902012-11-09 19:47:40 +0000414 // Tables are special cases because rows should be bypassed, but
415 // still taking their cells into account.
mario@webkit.org868b5ea2011-06-24 09:06:32 +0000416 if (coreObject->isAccessibilityTable())
417 coreChild = getChildForTable(coreObject, index);
418 else {
zandobersek@gmail.comc60dc0a2014-01-13 11:19:38 +0000419 const AccessibilityObject::AccessibilityChildrenVector& children = coreObject->children();
mario@webkit.org868b5ea2011-06-24 09:06:32 +0000420 if (static_cast<unsigned>(index) >= children.size())
421 return 0;
422 coreChild = children.at(index).get();
423 }
alp@webkit.org3a5e7ca2008-05-20 01:12:20 +0000424
425 if (!coreChild)
eric@webkit.org4ff6fd72009-11-10 09:53:33 +0000426 return 0;
alp@webkit.org3a5e7ca2008-05-20 01:12:20 +0000427
428 AtkObject* child = coreChild->wrapper();
eric@webkit.orgb84b4742009-10-20 21:20:50 +0000429 atk_object_set_parent(child, object);
alp@webkit.org3a5e7ca2008-05-20 01:12:20 +0000430 g_object_ref(child);
431
432 return child;
433}
434
mario@webkit.org868b5ea2011-06-24 09:06:32 +0000435static gint getIndexInParentForCellInRow(AccessibilityObject* coreObject)
436{
437 AccessibilityObject* parent = coreObject->parentObjectUnignored();
438 if (!parent)
439 return -1;
440
441 AccessibilityObject* grandParent = parent->parentObjectUnignored();
442 if (!grandParent)
443 return -1;
444
zandobersek@gmail.comc60dc0a2014-01-13 11:19:38 +0000445 const AccessibilityObject::AccessibilityChildrenVector& rows = grandParent->children();
mario@webkit.org868b5ea2011-06-24 09:06:32 +0000446 size_t previousCellsCount = 0;
447
448 // Look for the actual index of the cell inside the table.
zandobersek@gmail.comf4216bb2014-01-17 11:02:02 +0000449 for (const auto& row : rows) {
450 if (!row->isTableRow())
mario@webkit.org868b5ea2011-06-24 09:06:32 +0000451 continue;
452
zandobersek@gmail.comf4216bb2014-01-17 11:02:02 +0000453 const AccessibilityObject::AccessibilityChildrenVector& cells = row->children();
mario@webkit.org868b5ea2011-06-24 09:06:32 +0000454 size_t cellsCount = cells.size();
455
zandobersek@gmail.comf4216bb2014-01-17 11:02:02 +0000456 if (row == parent) {
mario@webkit.org868b5ea2011-06-24 09:06:32 +0000457 for (unsigned j = 0; j < cellsCount; ++j) {
458 if (cells[j] == coreObject)
459 return previousCellsCount + j;
460 }
461 }
462
463 previousCellsCount += cellsCount;
464 }
465
466 return -1;
467}
468
mario@webkit.org5c966772012-01-24 18:48:50 +0000469static gint webkitAccessibleGetIndexInParent(AtkObject* object)
alp@webkit.org3a5e7ca2008-05-20 01:12:20 +0000470{
mario@webkit.orgff8cf0f2013-09-27 09:44:20 +0000471 g_return_val_if_fail(WEBKIT_IS_ACCESSIBLE(object), -1);
472 returnValIfWebKitAccessibleIsInvalid(WEBKIT_ACCESSIBLE(object), -1);
473
xan@webkit.org1f5349a2009-10-27 09:20:21 +0000474 AccessibilityObject* coreObject = core(object);
475 AccessibilityObject* parent = coreObject->parentObjectUnignored();
476
mario@webkit.org86390a12011-01-07 17:34:02 +0000477 if (!parent && isRootObject(coreObject)) {
478 AtkObject* atkParent = atkParentOfRootObject(object);
eric@webkit.org1f5f7fe2009-11-04 10:35:56 +0000479 if (!atkParent)
480 return -1;
481
482 unsigned count = atk_object_get_n_accessible_children(atkParent);
483 for (unsigned i = 0; i < count; ++i) {
484 AtkObject* child = atk_object_ref_accessible_child(atkParent, i);
485 bool childIsObject = child == object;
486 g_object_unref(child);
487 if (childIsObject)
488 return i;
489 }
490 }
xan@webkit.org1f5349a2009-10-27 09:20:21 +0000491
mario@webkit.org868b5ea2011-06-24 09:06:32 +0000492 // Need to calculate the index of the cell in the table, as
mario@webkit.org8c5dd902012-11-09 19:47:40 +0000493 // rows won't be exposed to assistive technologies.
mario@webkit.org868b5ea2011-06-24 09:06:32 +0000494 if (parent && parent->isTableRow() && coreObject->isTableCell())
495 return getIndexInParentForCellInRow(coreObject);
496
commit-queue@webkit.org54a624e2013-01-16 18:00:17 +0000497 if (!parent)
498 return -1;
499
commit-queue@webkit.orgbf183552012-02-22 09:28:57 +0000500 size_t index = parent->children().find(coreObject);
501 return (index == WTF::notFound) ? -1 : index;
alp@webkit.org3a5e7ca2008-05-20 01:12:20 +0000502}
503
mario@webkit.org5c966772012-01-24 18:48:50 +0000504static AtkAttributeSet* webkitAccessibleGetAttributes(AtkObject* object)
eric@webkit.org84496192009-10-17 20:00:33 +0000505{
mario@webkit.orgff8cf0f2013-09-27 09:44:20 +0000506 g_return_val_if_fail(WEBKIT_IS_ACCESSIBLE(object), 0);
507 returnValIfWebKitAccessibleIsInvalid(WEBKIT_ACCESSIBLE(object), 0);
508
eric@webkit.org4ff6fd72009-11-10 09:53:33 +0000509 AtkAttributeSet* attributeSet = 0;
mario@webkit.org8c5dd902012-11-09 19:47:40 +0000510#if PLATFORM(GTK)
mario@webkit.orgf8344ff2012-01-24 11:40:44 +0000511 attributeSet = addToAtkAttributeSet(attributeSet, "toolkit", "WebKitGtk");
commit-queue@webkit.orgd9947352012-12-05 17:56:12 +0000512#elif PLATFORM(EFL)
513 attributeSet = addToAtkAttributeSet(attributeSet, "toolkit", "WebKitEfl");
mario@webkit.org8c5dd902012-11-09 19:47:40 +0000514#endif
eric@webkit.orgf4efc402010-05-15 09:55:52 +0000515
commit-queue@webkit.orga9398dd2010-10-26 19:19:29 +0000516 AccessibilityObject* coreObject = core(object);
517 if (!coreObject)
518 return attributeSet;
519
commit-queue@webkit.org357dc222013-01-10 00:09:41 +0000520 // Hack needed for WebKit2 tests because obtaining an element by its ID
521 // cannot be done from the UIProcess. Assistive technologies have no need
522 // for this information.
523 Node* node = coreObject->node();
524 if (node && node->isElementNode()) {
525 String id = toElement(node)->getIdAttribute().string();
526 if (!id.isEmpty())
527 attributeSet = addToAtkAttributeSet(attributeSet, "html-id", id.utf8().data());
528 }
529
commit-queue@webkit.orga9398dd2010-10-26 19:19:29 +0000530 int headingLevel = coreObject->headingLevel();
eric@webkit.org84496192009-10-17 20:00:33 +0000531 if (headingLevel) {
532 String value = String::number(headingLevel);
mario@webkit.orgf8344ff2012-01-24 11:40:44 +0000533 attributeSet = addToAtkAttributeSet(attributeSet, "level", value.utf8().data());
eric@webkit.org84496192009-10-17 20:00:33 +0000534 }
commit-queue@webkit.orga9398dd2010-10-26 19:19:29 +0000535
536 // Set the 'layout-guess' attribute to help Assistive
537 // Technologies know when an exposed table is not data table.
538 if (coreObject->isAccessibilityTable() && !coreObject->isDataTable())
mario@webkit.orgf8344ff2012-01-24 11:40:44 +0000539 attributeSet = addToAtkAttributeSet(attributeSet, "layout-guess", "true");
commit-queue@webkit.orga9398dd2010-10-26 19:19:29 +0000540
commit-queue@webkit.org2db4aaa2012-12-10 01:09:32 +0000541 String placeholder = coreObject->placeholderValue();
542 if (!placeholder.isEmpty())
543 attributeSet = addToAtkAttributeSet(attributeSet, "placeholder-text", placeholder.utf8().data());
544
commit-queue@webkit.org5945f762013-07-16 12:55:56 +0000545 if (coreObject->ariaHasPopup())
mario@webkit.orge2f7e782013-09-18 08:28:59 +0000546 attributeSet = addToAtkAttributeSet(attributeSet, "haspopup", "true");
commit-queue@webkit.org5945f762013-07-16 12:55:56 +0000547
commit-queue@webkit.org88d00b62013-09-03 11:29:33 +0000548 AccessibilitySortDirection sortDirection = coreObject->sortDirection();
mario@webkit.orge2f7e782013-09-18 08:28:59 +0000549 if (sortDirection != SortDirectionNone) {
mario@webkit.org9bf82612013-09-27 08:58:08 +0000550 // WAI-ARIA spec says to translate the value as is from the attribute.
551 const AtomicString& sortAttribute = coreObject->getAttribute(HTMLNames::aria_sortAttr);
552 attributeSet = addToAtkAttributeSet(attributeSet, "sort", sortAttribute.string().utf8().data());
mario@webkit.orge2f7e782013-09-18 08:28:59 +0000553 }
commit-queue@webkit.org88d00b62013-09-03 11:29:33 +0000554
mario.prada@samsung.come10ceaa2013-12-11 19:49:39 +0000555 // Landmarks will be exposed with xml-roles object attributes, with the exception
556 // of LandmarkApplicationRole, which will be exposed with ATK_ROLE_EMBEDDED.
557 AccessibilityRole role = coreObject->roleValue();
558 switch (role) {
559 case LandmarkBannerRole:
560 attributeSet = addToAtkAttributeSet(attributeSet, "xml-roles", "banner");
561 break;
562 case LandmarkComplementaryRole:
563 attributeSet = addToAtkAttributeSet(attributeSet, "xml-roles", "complementary");
564 break;
565 case LandmarkContentInfoRole:
566 attributeSet = addToAtkAttributeSet(attributeSet, "xml-roles", "contentinfo");
567 break;
568 case LandmarkMainRole:
569 attributeSet = addToAtkAttributeSet(attributeSet, "xml-roles", "main");
570 break;
571 case LandmarkNavigationRole:
572 attributeSet = addToAtkAttributeSet(attributeSet, "xml-roles", "navigation");
573 break;
574 case LandmarkSearchRole:
575 attributeSet = addToAtkAttributeSet(attributeSet, "xml-roles", "search");
576 break;
577 default:
578 break;
579 }
580
eric@webkit.org84496192009-10-17 20:00:33 +0000581 return attributeSet;
582}
583
mario.prada@samsung.com4fb934b2013-12-17 18:08:34 +0000584static AtkRole atkRole(AccessibilityObject* coreObject)
alp@webkit.org3a5e7ca2008-05-20 01:12:20 +0000585{
mario.prada@samsung.com4fb934b2013-12-17 18:08:34 +0000586 AccessibilityRole role = coreObject->roleValue();
alp@webkit.org3a5e7ca2008-05-20 01:12:20 +0000587 switch (role) {
mario.prada@samsung.com810148f2013-12-10 20:11:18 +0000588 case ApplicationAlertDialogRole:
commit-queue@webkit.orgb68dfdf2013-12-06 13:58:38 +0000589 case ApplicationAlertRole:
590 return ATK_ROLE_ALERT;
591 case ApplicationDialogRole:
commit-queue@webkit.orgb68dfdf2013-12-06 13:58:38 +0000592 return ATK_ROLE_DIALOG;
593 case ApplicationStatusRole:
594 return ATK_ROLE_STATUSBAR;
xan@webkit.org0ea2f732009-04-27 21:56:05 +0000595 case UnknownRole:
596 return ATK_ROLE_UNKNOWN;
mario@webkit.org53239522013-11-07 11:12:23 +0000597 case AudioRole:
598 case VideoRole:
599 return ATK_ROLE_EMBEDDED;
xan@webkit.org0ea2f732009-04-27 21:56:05 +0000600 case ButtonRole:
alp@webkit.org3a5e7ca2008-05-20 01:12:20 +0000601 return ATK_ROLE_PUSH_BUTTON;
commit-queue@webkit.org6152cb92012-08-23 00:36:06 +0000602 case ToggleButtonRole:
603 return ATK_ROLE_TOGGLE_BUTTON;
xan@webkit.org0ea2f732009-04-27 21:56:05 +0000604 case RadioButtonRole:
alp@webkit.org3a5e7ca2008-05-20 01:12:20 +0000605 return ATK_ROLE_RADIO_BUTTON;
xan@webkit.org0ea2f732009-04-27 21:56:05 +0000606 case CheckBoxRole:
alp@webkit.org3a5e7ca2008-05-20 01:12:20 +0000607 return ATK_ROLE_CHECK_BOX;
xan@webkit.org0ea2f732009-04-27 21:56:05 +0000608 case SliderRole:
alp@webkit.org3a5e7ca2008-05-20 01:12:20 +0000609 return ATK_ROLE_SLIDER;
xan@webkit.org0ea2f732009-04-27 21:56:05 +0000610 case TabGroupRole:
commit-queue@webkit.org52553892012-12-13 15:43:38 +0000611 case TabListRole:
alp@webkit.org3a5e7ca2008-05-20 01:12:20 +0000612 return ATK_ROLE_PAGE_TAB_LIST;
xan@webkit.org0ea2f732009-04-27 21:56:05 +0000613 case TextFieldRole:
614 case TextAreaRole:
alp@webkit.org3a5e7ca2008-05-20 01:12:20 +0000615 return ATK_ROLE_ENTRY;
xan@webkit.org0ea2f732009-04-27 21:56:05 +0000616 case StaticTextRole:
617 return ATK_ROLE_TEXT;
618 case OutlineRole:
commit-queue@webkit.orgb68dfdf2013-12-06 13:58:38 +0000619 case TreeRole:
alp@webkit.org3a5e7ca2008-05-20 01:12:20 +0000620 return ATK_ROLE_TREE;
commit-queue@webkit.orgb68dfdf2013-12-06 13:58:38 +0000621 case TreeItemRole:
622 return ATK_ROLE_TREE_ITEM;
xan@webkit.org0ea2f732009-04-27 21:56:05 +0000623 case MenuBarRole:
624 return ATK_ROLE_MENU_BAR;
eric@webkit.orga0669292010-04-22 14:36:19 +0000625 case MenuListPopupRole:
xan@webkit.org0ea2f732009-04-27 21:56:05 +0000626 case MenuRole:
627 return ATK_ROLE_MENU;
eric@webkit.orga0669292010-04-22 14:36:19 +0000628 case MenuListOptionRole:
xan@webkit.org0ea2f732009-04-27 21:56:05 +0000629 case MenuItemRole:
630 return ATK_ROLE_MENU_ITEM;
mario.prada@samsung.com092b72a2013-12-16 15:58:34 +0000631 case MenuItemCheckboxRole:
632 return ATK_ROLE_CHECK_MENU_ITEM;
commit-queue@webkit.org413f75b2013-09-11 11:31:14 +0000633 case MenuItemRadioRole:
634 return ATK_ROLE_RADIO_MENU_ITEM;
xan@webkit.org0ea2f732009-04-27 21:56:05 +0000635 case ColumnRole:
mario@webkit.org9adab662012-01-23 10:21:03 +0000636 // return ATK_ROLE_TABLE_COLUMN_HEADER; // Is this right?
xan@webkit.org0ea2f732009-04-27 21:56:05 +0000637 return ATK_ROLE_UNKNOWN; // Matches Mozilla
638 case RowRole:
mario@webkit.org9adab662012-01-23 10:21:03 +0000639 // return ATK_ROLE_TABLE_ROW_HEADER; // Is this right?
xan@webkit.org0ea2f732009-04-27 21:56:05 +0000640 return ATK_ROLE_LIST_ITEM; // Matches Mozilla
641 case ToolbarRole:
642 return ATK_ROLE_TOOL_BAR;
643 case BusyIndicatorRole:
644 return ATK_ROLE_PROGRESS_BAR; // Is this right?
645 case ProgressIndicatorRole:
mario@webkit.org9adab662012-01-23 10:21:03 +0000646 // return ATK_ROLE_SPIN_BUTTON; // Some confusion about this role in AccessibilityRenderObject.cpp
xan@webkit.org0ea2f732009-04-27 21:56:05 +0000647 return ATK_ROLE_PROGRESS_BAR;
648 case WindowRole:
649 return ATK_ROLE_WINDOW;
eric@webkit.orga0669292010-04-22 14:36:19 +0000650 case PopUpButtonRole:
xan@webkit.org0ea2f732009-04-27 21:56:05 +0000651 case ComboBoxRole:
652 return ATK_ROLE_COMBO_BOX;
653 case SplitGroupRole:
654 return ATK_ROLE_SPLIT_PANE;
655 case SplitterRole:
mario.prada@samsung.comae97a652013-12-10 20:12:56 +0000656 return ATK_ROLE_SEPARATOR;
xan@webkit.org0ea2f732009-04-27 21:56:05 +0000657 case ColorWellRole:
658 return ATK_ROLE_COLOR_CHOOSER;
659 case ListRole:
alp@webkit.org3a5e7ca2008-05-20 01:12:20 +0000660 return ATK_ROLE_LIST;
xan@webkit.org0ea2f732009-04-27 21:56:05 +0000661 case ScrollBarRole:
662 return ATK_ROLE_SCROLL_BAR;
mario@webkit.org86390a12011-01-07 17:34:02 +0000663 case ScrollAreaRole:
664 return ATK_ROLE_SCROLL_PANE;
commit-queue@webkit.org777f3cc2011-04-01 18:05:04 +0000665 case GridRole: // Is this right?
xan@webkit.org0ea2f732009-04-27 21:56:05 +0000666 case TableRole:
alp@webkit.org3a5e7ca2008-05-20 01:12:20 +0000667 return ATK_ROLE_TABLE;
xan@webkit.org0ea2f732009-04-27 21:56:05 +0000668 case ApplicationRole:
669 return ATK_ROLE_APPLICATION;
xan@webkit.org0ea2f732009-04-27 21:56:05 +0000670 case GroupRole:
671 case RadioGroupRole:
commit-queue@webkit.org52553892012-12-13 15:43:38 +0000672 case TabPanelRole:
xan@webkit.org0ea2f732009-04-27 21:56:05 +0000673 return ATK_ROLE_PANEL;
mario@webkit.org47a021b2011-04-11 17:39:11 +0000674 case RowHeaderRole: // Row headers are cells after all.
675 case ColumnHeaderRole: // Column headers are cells after all.
xan@webkit.org0ea2f732009-04-27 21:56:05 +0000676 case CellRole:
677 return ATK_ROLE_TABLE_CELL;
678 case LinkRole:
679 case WebCoreLinkRole:
680 case ImageMapLinkRole:
alp@webkit.org3a5e7ca2008-05-20 01:12:20 +0000681 return ATK_ROLE_LINK;
xan@webkit.org0ea2f732009-04-27 21:56:05 +0000682 case ImageMapRole:
683 case ImageRole:
alp@webkit.org3a5e7ca2008-05-20 01:12:20 +0000684 return ATK_ROLE_IMAGE;
xan@webkit.org0ea2f732009-04-27 21:56:05 +0000685 case ListMarkerRole:
jmalonzo@webkit.orgad9783b2009-05-23 22:22:52 +0000686 return ATK_ROLE_TEXT;
commit-queue@webkit.orgb68dfdf2013-12-06 13:58:38 +0000687 case DocumentArticleRole:
mario.prada@samsung.com2a1d5622013-12-16 15:17:58 +0000688#if ATK_CHECK_VERSION(2, 11, 3)
689 return ATK_ROLE_ARTICLE;
690#endif
691 case DocumentRole:
xan@webkit.org0ea2f732009-04-27 21:56:05 +0000692 return ATK_ROLE_DOCUMENT_FRAME;
mario.prada@samsung.comc4a5eca2013-12-16 16:18:30 +0000693 case DocumentNoteRole:
694 return ATK_ROLE_COMMENT;
xan@webkit.org0ea2f732009-04-27 21:56:05 +0000695 case HeadingRole:
696 return ATK_ROLE_HEADING;
697 case ListBoxRole:
698 return ATK_ROLE_LIST;
cfleizach@apple.com432ee572010-06-15 06:17:18 +0000699 case ListItemRole:
xan@webkit.org0ea2f732009-04-27 21:56:05 +0000700 case ListBoxOptionRole:
701 return ATK_ROLE_LIST_ITEM;
mario@webkit.org56f6bf22011-03-30 16:51:08 +0000702 case ParagraphRole:
703 return ATK_ROLE_PARAGRAPH;
704 case LabelRole:
commit-queue@webkit.org802c0122012-09-11 01:41:27 +0000705 case LegendRole:
mario@webkit.org56f6bf22011-03-30 16:51:08 +0000706 return ATK_ROLE_LABEL;
707 case DivRole:
708 return ATK_ROLE_SECTION;
709 case FormRole:
710 return ATK_ROLE_FORM;
commit-queue@webkit.org52f67612012-09-07 05:09:04 +0000711 case CanvasRole:
712 return ATK_ROLE_CANVAS;
dmazzoni@google.comf3cf2c42012-09-07 23:46:45 +0000713 case HorizontalRuleRole:
714 return ATK_ROLE_SEPARATOR;
commit-queue@webkit.orgc985400e2012-09-21 20:03:57 +0000715 case SpinButtonRole:
716 return ATK_ROLE_SPIN_BUTTON;
commit-queue@webkit.org52553892012-12-13 15:43:38 +0000717 case TabRole:
718 return ATK_ROLE_PAGE_TAB;
commit-queue@webkit.orgb68dfdf2013-12-06 13:58:38 +0000719 case UserInterfaceTooltipRole:
720 return ATK_ROLE_TOOL_TIP;
721 case WebAreaRole:
722 return ATK_ROLE_DOCUMENT_WEB;
mario.prada@samsung.come10ceaa2013-12-11 19:49:39 +0000723 case LandmarkApplicationRole:
724 return ATK_ROLE_EMBEDDED;
725#if ATK_CHECK_VERSION(2, 11, 3)
mario.prada@samsung.comc4a5eca2013-12-16 16:18:30 +0000726 case ApplicationLogRole:
727 return ATK_ROLE_LOG;
728 case ApplicationMarqueeRole:
729 return ATK_ROLE_MARQUEE;
730 case ApplicationTimerRole:
731 return ATK_ROLE_TIMER;
732 case DefinitionRole:
733 return ATK_ROLE_DEFINITION;
734 case DocumentMathRole:
735 return ATK_ROLE_MATH;
mario.prada@samsung.come10ceaa2013-12-11 19:49:39 +0000736 case LandmarkBannerRole:
737 case LandmarkComplementaryRole:
738 case LandmarkContentInfoRole:
739 case LandmarkMainRole:
740 case LandmarkNavigationRole:
741 case LandmarkSearchRole:
742 return ATK_ROLE_LANDMARK;
743#endif
mario.prada@samsung.com4fb934b2013-12-17 18:08:34 +0000744#if ATK_CHECK_VERSION(2, 11, 4)
745 case DescriptionListRole:
746 return ATK_ROLE_DESCRIPTION_LIST;
747 case DescriptionListTermRole:
748 return ATK_ROLE_DESCRIPTION_TERM;
749 case DescriptionListDetailRole:
750 return ATK_ROLE_DESCRIPTION_VALUE;
751#endif
alp@webkit.org3a5e7ca2008-05-20 01:12:20 +0000752 default:
753 return ATK_ROLE_UNKNOWN;
754 }
755}
756
mario@webkit.org5c966772012-01-24 18:48:50 +0000757static AtkRole webkitAccessibleGetRole(AtkObject* object)
alp@webkit.org3a5e7ca2008-05-20 01:12:20 +0000758{
mario@webkit.orgff8cf0f2013-09-27 09:44:20 +0000759 g_return_val_if_fail(WEBKIT_IS_ACCESSIBLE(object), ATK_ROLE_UNKNOWN);
760 returnValIfWebKitAccessibleIsInvalid(WEBKIT_ACCESSIBLE(object), ATK_ROLE_UNKNOWN);
761
mario@webkit.org56f6bf22011-03-30 16:51:08 +0000762 AccessibilityObject* coreObject = core(object);
jmalonzo@webkit.org7ce37142009-05-20 11:16:01 +0000763
mario@webkit.org56f6bf22011-03-30 16:51:08 +0000764 if (!coreObject)
jmalonzo@webkit.org7ce37142009-05-20 11:16:01 +0000765 return ATK_ROLE_UNKNOWN;
766
767 // Note: Why doesn't WebCore have a password field for this
mario@webkit.org56f6bf22011-03-30 16:51:08 +0000768 if (coreObject->isPasswordField())
jmalonzo@webkit.org7ce37142009-05-20 11:16:01 +0000769 return ATK_ROLE_PASSWORD_TEXT;
770
mario.prada@samsung.com4fb934b2013-12-17 18:08:34 +0000771 return atkRole(coreObject);
alp@webkit.org3a5e7ca2008-05-20 01:12:20 +0000772}
773
mario@webkit.org6a24ba12010-12-14 15:35:22 +0000774static bool isTextWithCaret(AccessibilityObject* coreObject)
775{
776 if (!coreObject || !coreObject->isAccessibilityRenderObject())
777 return false;
778
779 Document* document = coreObject->document();
780 if (!document)
781 return false;
782
783 Frame* frame = document->frame();
784 if (!frame)
785 return false;
786
akling@apple.com17523502013-08-17 10:58:40 +0000787 if (!frame->settings().caretBrowsingEnabled())
mario@webkit.org6a24ba12010-12-14 15:35:22 +0000788 return false;
789
790 // Check text objects and paragraphs only.
791 AtkObject* axObject = coreObject->wrapper();
792 AtkRole role = axObject ? atk_object_get_role(axObject) : ATK_ROLE_INVALID;
793 if (role != ATK_ROLE_TEXT && role != ATK_ROLE_PARAGRAPH)
794 return false;
795
796 // Finally, check whether the caret is set in the current object.
797 VisibleSelection selection = coreObject->selection();
798 if (!selection.isCaret())
799 return false;
800
801 return selectionBelongsToObject(coreObject, selection);
802}
803
xan@webkit.org8be15d62009-04-09 11:20:57 +0000804static void setAtkStateSetFromCoreObject(AccessibilityObject* coreObject, AtkStateSet* stateSet)
805{
eric@webkit.org2dd47342009-10-26 11:42:42 +0000806 AccessibilityObject* parent = coreObject->parentObject();
807 bool isListBoxOption = parent && parent->isListBox();
xan@webkit.org8be15d62009-04-09 11:20:57 +0000808
eric@webkit.org2dd47342009-10-26 11:42:42 +0000809 // Please keep the state list in alphabetical order
commit-queue@webkit.orgc4299262013-12-02 10:40:07 +0000810 if (isListBoxOption && coreObject->isSelectedOptionActive())
811 atk_state_set_add_state(stateSet, ATK_STATE_ACTIVE);
812
xan@webkit.org8be15d62009-04-09 11:20:57 +0000813 if (coreObject->isChecked())
814 atk_state_set_add_state(stateSet, ATK_STATE_CHECKED);
815
xan@webkit.orgdf7d13a2009-06-26 09:12:44 +0000816 // FIXME: isReadOnly does not seem to do the right thing for
eric@webkit.org2dd47342009-10-26 11:42:42 +0000817 // controls, so check explicitly for them. In addition, because
818 // isReadOnly is false for listBoxOptions, we need to add one
819 // more check so that we do not present them as being "editable".
mario@webkit.org9adab662012-01-23 10:21:03 +0000820 if ((!coreObject->isReadOnly()
mario@webkit.orgf88ffcb2012-11-16 13:47:03 +0000821 || (coreObject->isControl() && coreObject->canSetValueAttribute()))
mario@webkit.org9adab662012-01-23 10:21:03 +0000822 && !isListBoxOption)
xan@webkit.org8be15d62009-04-09 11:20:57 +0000823 atk_state_set_add_state(stateSet, ATK_STATE_EDITABLE);
824
xan@webkit.orgdf7d13a2009-06-26 09:12:44 +0000825 // FIXME: Put both ENABLED and SENSITIVE together here for now
826 if (coreObject->isEnabled()) {
xan@webkit.org8be15d62009-04-09 11:20:57 +0000827 atk_state_set_add_state(stateSet, ATK_STATE_ENABLED);
xan@webkit.orgdf7d13a2009-06-26 09:12:44 +0000828 atk_state_set_add_state(stateSet, ATK_STATE_SENSITIVE);
829 }
xan@webkit.org8be15d62009-04-09 11:20:57 +0000830
mario@webkit.org8ce6a152010-11-30 21:16:14 +0000831 if (coreObject->canSetExpandedAttribute())
832 atk_state_set_add_state(stateSet, ATK_STATE_EXPANDABLE);
833
834 if (coreObject->isExpanded())
835 atk_state_set_add_state(stateSet, ATK_STATE_EXPANDED);
836
xan@webkit.org8be15d62009-04-09 11:20:57 +0000837 if (coreObject->canSetFocusAttribute())
838 atk_state_set_add_state(stateSet, ATK_STATE_FOCUSABLE);
839
mario@webkit.org6a24ba12010-12-14 15:35:22 +0000840 if (coreObject->isFocused() || isTextWithCaret(coreObject))
xan@webkit.org8be15d62009-04-09 11:20:57 +0000841 atk_state_set_add_state(stateSet, ATK_STATE_FOCUSED);
842
commit-queue@webkit.org471dff52013-03-20 08:12:15 +0000843 if (coreObject->orientation() == AccessibilityOrientationHorizontal)
844 atk_state_set_add_state(stateSet, ATK_STATE_HORIZONTAL);
845 else if (coreObject->orientation() == AccessibilityOrientationVertical)
846 atk_state_set_add_state(stateSet, ATK_STATE_VERTICAL);
xan@webkit.org8be15d62009-04-09 11:20:57 +0000847
848 if (coreObject->isIndeterminate())
849 atk_state_set_add_state(stateSet, ATK_STATE_INDETERMINATE);
850
k.czech@samsung.com43a89312014-01-07 11:28:04 +0000851 if (coreObject->isCheckboxOrRadio() || coreObject->isMenuItem()) {
852 if (coreObject->checkboxOrRadioValue() == ButtonStateMixed)
853 atk_state_set_add_state(stateSet, ATK_STATE_INDETERMINATE);
854 }
855
mario@webkit.orge0f00f12013-10-08 10:55:25 +0000856 if (coreObject->invalidStatus() != "false")
857 atk_state_set_add_state(stateSet, ATK_STATE_INVALID_ENTRY);
858
jhoneycutt@apple.com8acea082010-01-14 01:16:15 +0000859 if (coreObject->isMultiSelectable())
xan@webkit.org8be15d62009-04-09 11:20:57 +0000860 atk_state_set_add_state(stateSet, ATK_STATE_MULTISELECTABLE);
861
862 // TODO: ATK_STATE_OPAQUE
863
864 if (coreObject->isPressed())
865 atk_state_set_add_state(stateSet, ATK_STATE_PRESSED);
866
commit-queue@webkit.org60f8d312013-06-21 10:33:34 +0000867 if (coreObject->isRequired())
868 atk_state_set_add_state(stateSet, ATK_STATE_REQUIRED);
869
xan@webkit.org8be15d62009-04-09 11:20:57 +0000870 // TODO: ATK_STATE_SELECTABLE_TEXT
871
eric@webkit.org2dd47342009-10-26 11:42:42 +0000872 if (coreObject->canSetSelectedAttribute()) {
873 atk_state_set_add_state(stateSet, ATK_STATE_SELECTABLE);
mario@webkit.org8c5dd902012-11-09 19:47:40 +0000874 // Items in focusable lists have both STATE_SELECT{ABLE,ED}
875 // and STATE_FOCUS{ABLE,ED}. We'll fake the latter based on
876 // the former.
eric@webkit.org2dd47342009-10-26 11:42:42 +0000877 if (isListBoxOption)
878 atk_state_set_add_state(stateSet, ATK_STATE_FOCUSABLE);
879 }
880
881 if (coreObject->isSelected()) {
xan@webkit.org8be15d62009-04-09 11:20:57 +0000882 atk_state_set_add_state(stateSet, ATK_STATE_SELECTED);
mario@webkit.org8c5dd902012-11-09 19:47:40 +0000883 // Items in focusable lists have both STATE_SELECT{ABLE,ED}
eric@webkit.org2dd47342009-10-26 11:42:42 +0000884 // and STATE_FOCUS{ABLE,ED}. We'll fake the latter based on the
885 // former.
886 if (isListBoxOption)
887 atk_state_set_add_state(stateSet, ATK_STATE_FOCUSED);
888 }
xan@webkit.org8be15d62009-04-09 11:20:57 +0000889
xan@webkit.orgdf7d13a2009-06-26 09:12:44 +0000890 // FIXME: Group both SHOWING and VISIBLE here for now
891 // Not sure how to handle this in WebKit, see bug
892 // http://bugzilla.gnome.org/show_bug.cgi?id=509650 for other
mario@webkit.org8c5dd902012-11-09 19:47:40 +0000893 // issues with SHOWING vs VISIBLE.
xan@webkit.orgdf7d13a2009-06-26 09:12:44 +0000894 if (!coreObject->isOffScreen()) {
xan@webkit.org8be15d62009-04-09 11:20:57 +0000895 atk_state_set_add_state(stateSet, ATK_STATE_SHOWING);
xan@webkit.orgdf7d13a2009-06-26 09:12:44 +0000896 atk_state_set_add_state(stateSet, ATK_STATE_VISIBLE);
897 }
xan@webkit.org8be15d62009-04-09 11:20:57 +0000898
899 // Mutually exclusive, so we group these two
900 if (coreObject->roleValue() == TextFieldRole)
901 atk_state_set_add_state(stateSet, ATK_STATE_SINGLE_LINE);
902 else if (coreObject->roleValue() == TextAreaRole)
903 atk_state_set_add_state(stateSet, ATK_STATE_MULTI_LINE);
904
905 // TODO: ATK_STATE_SENSITIVE
906
xan@webkit.org8be15d62009-04-09 11:20:57 +0000907 if (coreObject->isVisited())
908 atk_state_set_add_state(stateSet, ATK_STATE_VISITED);
909}
910
mario@webkit.org5c966772012-01-24 18:48:50 +0000911static AtkStateSet* webkitAccessibleRefStateSet(AtkObject* object)
xan@webkit.org8be15d62009-04-09 11:20:57 +0000912{
mario@webkit.orgff8cf0f2013-09-27 09:44:20 +0000913 g_return_val_if_fail(WEBKIT_IS_ACCESSIBLE(object), 0);
914
mario@webkit.orgf88ffcb2012-11-16 13:47:03 +0000915 AtkStateSet* stateSet = ATK_OBJECT_CLASS(webkitAccessibleParentClass)->ref_state_set(object);
xan@webkit.org8be15d62009-04-09 11:20:57 +0000916 AccessibilityObject* coreObject = core(object);
917
mario@webkit.orgff8cf0f2013-09-27 09:44:20 +0000918 // Make sure the layout is updated to really know whether the object
919 // is defunct or not, so we can return the proper state.
920 coreObject->updateBackingStore();
921
xan@webkit.org8be15d62009-04-09 11:20:57 +0000922 if (coreObject == fallbackObject()) {
923 atk_state_set_add_state(stateSet, ATK_STATE_DEFUNCT);
924 return stateSet;
925 }
926
mario@webkit.org6a24ba12010-12-14 15:35:22 +0000927 // Text objects must be focusable.
928 AtkRole role = atk_object_get_role(object);
929 if (role == ATK_ROLE_TEXT || role == ATK_ROLE_PARAGRAPH)
930 atk_state_set_add_state(stateSet, ATK_STATE_FOCUSABLE);
xan@webkit.org8be15d62009-04-09 11:20:57 +0000931
mario@webkit.org6a24ba12010-12-14 15:35:22 +0000932 setAtkStateSetFromCoreObject(coreObject, stateSet);
xan@webkit.org8be15d62009-04-09 11:20:57 +0000933 return stateSet;
934}
935
mario@webkit.org5c966772012-01-24 18:48:50 +0000936static AtkRelationSet* webkitAccessibleRefRelationSet(AtkObject* object)
eric@webkit.org0f6cb452009-10-22 23:07:56 +0000937{
mario@webkit.orgff8cf0f2013-09-27 09:44:20 +0000938 g_return_val_if_fail(WEBKIT_IS_ACCESSIBLE(object), 0);
939 returnValIfWebKitAccessibleIsInvalid(WEBKIT_ACCESSIBLE(object), 0);
940
mario@webkit.orgf88ffcb2012-11-16 13:47:03 +0000941 AtkRelationSet* relationSet = ATK_OBJECT_CLASS(webkitAccessibleParentClass)->ref_relation_set(object);
eric@webkit.org0f6cb452009-10-22 23:07:56 +0000942 AccessibilityObject* coreObject = core(object);
943
944 setAtkRelationSetFromCoreObject(coreObject, relationSet);
945
946 return relationSet;
947}
948
mario@webkit.org5c966772012-01-24 18:48:50 +0000949static void webkitAccessibleInit(AtkObject* object, gpointer data)
xan@webkit.orge4387102009-04-09 11:08:48 +0000950{
mario@webkit.orgf88ffcb2012-11-16 13:47:03 +0000951 if (ATK_OBJECT_CLASS(webkitAccessibleParentClass)->initialize)
952 ATK_OBJECT_CLASS(webkitAccessibleParentClass)->initialize(object, data);
xan@webkit.orge4387102009-04-09 11:08:48 +0000953
mario@webkit.orgfecbacd2013-03-04 17:06:15 +0000954 WebKitAccessible* accessible = WEBKIT_ACCESSIBLE(object);
955 accessible->m_object = reinterpret_cast<AccessibilityObject*>(data);
956 accessible->priv = WEBKIT_ACCESSIBLE_GET_PRIVATE(accessible);
xan@webkit.orge4387102009-04-09 11:08:48 +0000957}
958
commit-queue@webkit.org120f5652013-06-12 21:39:42 +0000959static const gchar* webkitAccessibleGetObjectLocale(AtkObject* object)
960{
mario@webkit.orgff8cf0f2013-09-27 09:44:20 +0000961 g_return_val_if_fail(WEBKIT_IS_ACCESSIBLE(object), 0);
962 returnValIfWebKitAccessibleIsInvalid(WEBKIT_ACCESSIBLE(object), 0);
commit-queue@webkit.org120f5652013-06-12 21:39:42 +0000963
mario@webkit.orgff8cf0f2013-09-27 09:44:20 +0000964 AccessibilityObject* coreObject = core(object);
965 if (!coreObject)
966 return 0;
967
968 if (ATK_IS_DOCUMENT(object)) {
commit-queue@webkit.org120f5652013-06-12 21:39:42 +0000969 // TODO: Should we fall back on lang xml:lang when the following comes up empty?
970 String language = coreObject->language();
971 if (!language.isEmpty())
972 return cacheAndReturnAtkProperty(object, AtkCachedDocumentLocale, language);
973
974 } else if (ATK_IS_TEXT(object)) {
975 const gchar* locale = 0;
976
977 AtkAttributeSet* textAttributes = atk_text_get_default_attributes(ATK_TEXT(object));
commit-queue@webkit.org44a695d2013-07-04 12:10:45 +0000978 for (AtkAttributeSet* attributes = textAttributes; attributes; attributes = attributes->next) {
commit-queue@webkit.org120f5652013-06-12 21:39:42 +0000979 AtkAttribute* atkAttribute = static_cast<AtkAttribute*>(attributes->data);
980 if (!strcmp(atkAttribute->name, atk_text_attribute_get_name(ATK_TEXT_ATTR_LANGUAGE))) {
981 locale = cacheAndReturnAtkProperty(object, AtkCachedDocumentLocale, String::fromUTF8(atkAttribute->value));
982 break;
983 }
984 }
commit-queue@webkit.org120f5652013-06-12 21:39:42 +0000985 atk_attribute_set_free(textAttributes);
986
987 return locale;
988 }
989
990 return 0;
991}
992
mario@webkit.org5c966772012-01-24 18:48:50 +0000993static void webkitAccessibleFinalize(GObject* object)
xan@webkit.orge4387102009-04-09 11:08:48 +0000994{
mario@webkit.orgf88ffcb2012-11-16 13:47:03 +0000995 G_OBJECT_CLASS(webkitAccessibleParentClass)->finalize(object);
xan@webkit.orge4387102009-04-09 11:08:48 +0000996}
997
mario@webkit.orgf88ffcb2012-11-16 13:47:03 +0000998static void webkitAccessibleClassInit(AtkObjectClass* klass)
alp@webkit.org3a5e7ca2008-05-20 01:12:20 +0000999{
xan@webkit.orge4387102009-04-09 11:08:48 +00001000 GObjectClass* gobjectClass = G_OBJECT_CLASS(klass);
alp@webkit.org3a5e7ca2008-05-20 01:12:20 +00001001
mario@webkit.orgf88ffcb2012-11-16 13:47:03 +00001002 webkitAccessibleParentClass = g_type_class_peek_parent(klass);
xan@webkit.orge4387102009-04-09 11:08:48 +00001003
mario@webkit.org5c966772012-01-24 18:48:50 +00001004 gobjectClass->finalize = webkitAccessibleFinalize;
alp@webkit.org3a5e7ca2008-05-20 01:12:20 +00001005
mario@webkit.org5c966772012-01-24 18:48:50 +00001006 klass->initialize = webkitAccessibleInit;
1007 klass->get_name = webkitAccessibleGetName;
1008 klass->get_description = webkitAccessibleGetDescription;
1009 klass->get_parent = webkitAccessibleGetParent;
1010 klass->get_n_children = webkitAccessibleGetNChildren;
1011 klass->ref_child = webkitAccessibleRefChild;
1012 klass->get_role = webkitAccessibleGetRole;
1013 klass->ref_state_set = webkitAccessibleRefStateSet;
1014 klass->get_index_in_parent = webkitAccessibleGetIndexInParent;
1015 klass->get_attributes = webkitAccessibleGetAttributes;
1016 klass->ref_relation_set = webkitAccessibleRefRelationSet;
commit-queue@webkit.org120f5652013-06-12 21:39:42 +00001017 klass->get_object_locale = webkitAccessibleGetObjectLocale;
mario@webkit.orgfecbacd2013-03-04 17:06:15 +00001018
1019 g_type_class_add_private(klass, sizeof(WebKitAccessiblePrivate));
xan@webkit.orge4387102009-04-09 11:08:48 +00001020}
1021
1022GType
mario@webkit.orgf88ffcb2012-11-16 13:47:03 +00001023webkitAccessibleGetType(void)
xan@webkit.orge4387102009-04-09 11:08:48 +00001024{
mario@webkit.orgf88ffcb2012-11-16 13:47:03 +00001025 static volatile gsize typeVolatile = 0;
xan@webkit.orge4387102009-04-09 11:08:48 +00001026
mario@webkit.orgf88ffcb2012-11-16 13:47:03 +00001027 if (g_once_init_enter(&typeVolatile)) {
xan@webkit.orge4387102009-04-09 11:08:48 +00001028 static const GTypeInfo tinfo = {
1029 sizeof(WebKitAccessibleClass),
eric@webkit.org4ff6fd72009-11-10 09:53:33 +00001030 (GBaseInitFunc) 0,
1031 (GBaseFinalizeFunc) 0,
mario@webkit.orgf88ffcb2012-11-16 13:47:03 +00001032 (GClassInitFunc) webkitAccessibleClassInit,
eric@webkit.org4ff6fd72009-11-10 09:53:33 +00001033 (GClassFinalizeFunc) 0,
1034 0, /* class data */
xan@webkit.orge4387102009-04-09 11:08:48 +00001035 sizeof(WebKitAccessible), /* instance size */
1036 0, /* nb preallocs */
eric@webkit.org4ff6fd72009-11-10 09:53:33 +00001037 (GInstanceInitFunc) 0,
1038 0 /* value table */
xan@webkit.orge4387102009-04-09 11:08:48 +00001039 };
1040
mario@webkit.orgf88ffcb2012-11-16 13:47:03 +00001041 GType type = g_type_register_static(ATK_TYPE_OBJECT, "WebKitAccessible", &tinfo, GTypeFlags(0));
1042 g_once_init_leave(&typeVolatile, type);
xan@webkit.orge4387102009-04-09 11:08:48 +00001043 }
1044
mario@webkit.orgf88ffcb2012-11-16 13:47:03 +00001045 return typeVolatile;
alp@webkit.orgc7738992008-05-27 02:48:14 +00001046}
1047
xan@webkit.orge4387102009-04-09 11:08:48 +00001048static const GInterfaceInfo AtkInterfacesInitFunctions[] = {
mario@webkit.orgdeec8392012-01-23 14:45:23 +00001049 {reinterpret_cast<GInterfaceInitFunc>(webkitAccessibleActionInterfaceInit), 0, 0},
mario@webkit.org7e5931d2012-01-24 12:25:13 +00001050 {reinterpret_cast<GInterfaceInitFunc>(webkitAccessibleSelectionInterfaceInit), 0, 0},
mario@webkit.orgfc51ca62012-01-24 11:47:51 +00001051 {reinterpret_cast<GInterfaceInitFunc>(webkitAccessibleEditableTextInterfaceInit), 0, 0},
mario@webkit.org987d7372012-01-24 18:02:08 +00001052 {reinterpret_cast<GInterfaceInitFunc>(webkitAccessibleTextInterfaceInit), 0, 0},
mario@webkit.orgbe1ce552012-01-24 11:03:51 +00001053 {reinterpret_cast<GInterfaceInitFunc>(webkitAccessibleComponentInterfaceInit), 0, 0},
mario@webkit.orgda3e6082012-01-24 12:04:16 +00001054 {reinterpret_cast<GInterfaceInitFunc>(webkitAccessibleImageInterfaceInit), 0, 0},
mario@webkit.orgcd9f1b32012-01-24 18:28:22 +00001055 {reinterpret_cast<GInterfaceInitFunc>(webkitAccessibleTableInterfaceInit), 0, 0},
mario@webkit.org70243532012-01-24 11:58:52 +00001056 {reinterpret_cast<GInterfaceInitFunc>(webkitAccessibleHypertextInterfaceInit), 0, 0},
mario@webkit.org4dbd9822012-01-24 11:55:18 +00001057 {reinterpret_cast<GInterfaceInitFunc>(webkitAccessibleHyperlinkImplInterfaceInit), 0, 0},
mario@webkit.orgf8344ff2012-01-24 11:40:44 +00001058 {reinterpret_cast<GInterfaceInitFunc>(webkitAccessibleDocumentInterfaceInit), 0, 0},
mario@webkit.org980269e2012-01-24 16:22:57 +00001059 {reinterpret_cast<GInterfaceInitFunc>(webkitAccessibleValueInterfaceInit), 0, 0}
xan@webkit.orge4387102009-04-09 11:08:48 +00001060};
1061
1062enum WAIType {
1063 WAI_ACTION,
eric@webkit.orgf84ff632009-10-29 17:34:39 +00001064 WAI_SELECTION,
xan@webkit.orge4387102009-04-09 11:08:48 +00001065 WAI_EDITABLE_TEXT,
xan@webkit.org92b91692009-04-21 07:02:17 +00001066 WAI_TEXT,
xan@webkit.orge43e70b2009-04-27 21:33:55 +00001067 WAI_COMPONENT,
xan@webkit.org45b26ac2009-10-27 12:20:35 +00001068 WAI_IMAGE,
eric@webkit.org65e12ba2009-11-01 21:22:30 +00001069 WAI_TABLE,
mario@webkit.org7f95c622010-11-01 15:05:36 +00001070 WAI_HYPERTEXT,
1071 WAI_HYPERLINK,
mario@webkit.org04e38e92011-03-28 10:16:38 +00001072 WAI_DOCUMENT,
1073 WAI_VALUE,
xan@webkit.orge4387102009-04-09 11:08:48 +00001074};
1075
1076static GType GetAtkInterfaceTypeFromWAIType(WAIType type)
alp@webkit.org3a5e7ca2008-05-20 01:12:20 +00001077{
mario@webkit.org7f95c622010-11-01 15:05:36 +00001078 switch (type) {
1079 case WAI_ACTION:
1080 return ATK_TYPE_ACTION;
1081 case WAI_SELECTION:
1082 return ATK_TYPE_SELECTION;
1083 case WAI_EDITABLE_TEXT:
1084 return ATK_TYPE_EDITABLE_TEXT;
1085 case WAI_TEXT:
1086 return ATK_TYPE_TEXT;
1087 case WAI_COMPONENT:
1088 return ATK_TYPE_COMPONENT;
1089 case WAI_IMAGE:
1090 return ATK_TYPE_IMAGE;
1091 case WAI_TABLE:
1092 return ATK_TYPE_TABLE;
1093 case WAI_HYPERTEXT:
1094 return ATK_TYPE_HYPERTEXT;
1095 case WAI_HYPERLINK:
1096 return ATK_TYPE_HYPERLINK_IMPL;
1097 case WAI_DOCUMENT:
1098 return ATK_TYPE_DOCUMENT;
mario@webkit.org04e38e92011-03-28 10:16:38 +00001099 case WAI_VALUE:
1100 return ATK_TYPE_VALUE;
mario@webkit.org7f95c622010-11-01 15:05:36 +00001101 }
alp@webkit.org3a5e7ca2008-05-20 01:12:20 +00001102
mario@webkit.org7f95c622010-11-01 15:05:36 +00001103 return G_TYPE_INVALID;
xan@webkit.orge4387102009-04-09 11:08:48 +00001104}
alp@webkit.org3a5e7ca2008-05-20 01:12:20 +00001105
commit-queue@webkit.orgca1b1d22012-08-22 01:37:04 +00001106static bool roleIsTextType(AccessibilityRole role)
1107{
zandobersek@gmail.com2b2075f2012-09-01 08:53:50 +00001108 return role == ParagraphRole || role == HeadingRole || role == DivRole || role == CellRole || role == ListItemRole;
commit-queue@webkit.orgca1b1d22012-08-22 01:37:04 +00001109}
1110
xan@webkit.orge4387102009-04-09 11:08:48 +00001111static guint16 getInterfaceMaskFromObject(AccessibilityObject* coreObject)
1112{
1113 guint16 interfaceMask = 0;
alp@webkit.orgc7738992008-05-27 02:48:14 +00001114
xan@webkit.org92b91692009-04-21 07:02:17 +00001115 // Component interface is always supported
1116 interfaceMask |= 1 << WAI_COMPONENT;
1117
mario@webkit.org7f95c622010-11-01 15:05:36 +00001118 AccessibilityRole role = coreObject->roleValue();
1119
xan@webkit.orgb3c59bf2009-04-21 07:01:27 +00001120 // Action
mario@webkit.org8ce6a152010-11-30 21:16:14 +00001121 // As the implementation of the AtkAction interface is a very
1122 // basic one (just relays in executing the default action for each
1123 // object, and only supports having one action per object), it is
1124 // better just to implement this interface for every instance of
1125 // the WebKitAccessible class and let WebCore decide what to do.
1126 interfaceMask |= 1 << WAI_ACTION;
xan@webkit.orgb3c59bf2009-04-21 07:01:27 +00001127
eric@webkit.orgf84ff632009-10-29 17:34:39 +00001128 // Selection
mario@webkit.org8ce6a152010-11-30 21:16:14 +00001129 if (coreObject->isListBox() || coreObject->isMenuList())
eric@webkit.orgf84ff632009-10-29 17:34:39 +00001130 interfaceMask |= 1 << WAI_SELECTION;
1131
mario@webkit.org9f0aee12011-04-11 21:02:23 +00001132 // Get renderer if available.
1133 RenderObject* renderer = 0;
1134 if (coreObject->isAccessibilityRenderObject())
1135 renderer = coreObject->renderer();
1136
1137 // Hyperlink (links and embedded objects).
1138 if (coreObject->isLink() || (renderer && renderer->isReplaced()))
1139 interfaceMask |= 1 << WAI_HYPERLINK;
1140
xan@webkit.orge4387102009-04-09 11:08:48 +00001141 // Text & Editable Text
mario@webkit.org8ce6a152010-11-30 21:16:14 +00001142 if (role == StaticTextRole || coreObject->isMenuListOption())
xan@webkit.org7a07be52009-04-14 15:56:10 +00001143 interfaceMask |= 1 << WAI_TEXT;
mario@webkit.org9f0aee12011-04-11 21:02:23 +00001144 else {
eric@webkit.org10e02f82010-01-07 02:05:01 +00001145 if (coreObject->isTextControl()) {
1146 interfaceMask |= 1 << WAI_TEXT;
1147 if (!coreObject->isReadOnly())
1148 interfaceMask |= 1 << WAI_EDITABLE_TEXT;
commit-queue@webkit.org1f3aafe2010-09-22 07:49:34 +00001149 } else {
mario@webkit.org7f95c622010-11-01 15:05:36 +00001150 if (role != TableRole) {
1151 interfaceMask |= 1 << WAI_HYPERTEXT;
commit-queue@webkit.orgca1b1d22012-08-22 01:37:04 +00001152 if ((renderer && renderer->childrenInline()) || roleIsTextType(role))
mario@webkit.org7f95c622010-11-01 15:05:36 +00001153 interfaceMask |= 1 << WAI_TEXT;
1154 }
1155
1156 // Add the TEXT interface for list items whose
1157 // first accessible child has a text renderer
1158 if (role == ListItemRole) {
zandobersek@gmail.comc60dc0a2014-01-13 11:19:38 +00001159 const AccessibilityObject::AccessibilityChildrenVector& children = coreObject->children();
commit-queue@webkit.org1f3aafe2010-09-22 07:49:34 +00001160 if (children.size()) {
1161 AccessibilityObject* axRenderChild = children.at(0).get();
1162 interfaceMask |= getInterfaceMaskFromObject(axRenderChild);
1163 }
1164 }
1165 }
mario@webkit.org7f95c622010-11-01 15:05:36 +00001166 }
xan@webkit.orge4387102009-04-09 11:08:48 +00001167
xan@webkit.orge43e70b2009-04-27 21:33:55 +00001168 // Image
1169 if (coreObject->isImage())
1170 interfaceMask |= 1 << WAI_IMAGE;
1171
xan@webkit.org45b26ac2009-10-27 12:20:35 +00001172 // Table
1173 if (role == TableRole)
1174 interfaceMask |= 1 << WAI_TABLE;
1175
eric@webkit.org65e12ba2009-11-01 21:22:30 +00001176 // Document
1177 if (role == WebAreaRole)
1178 interfaceMask |= 1 << WAI_DOCUMENT;
1179
mario@webkit.org04e38e92011-03-28 10:16:38 +00001180 // Value
commit-queue@webkit.org35af6cf2013-09-16 14:00:35 +00001181 if (role == SliderRole || role == SpinButtonRole || role == ScrollBarRole || role == ProgressIndicatorRole)
mario@webkit.org04e38e92011-03-28 10:16:38 +00001182 interfaceMask |= 1 << WAI_VALUE;
1183
commit-queue@webkit.orgfd70fd32013-09-04 14:19:02 +00001184#if ENABLE(INPUT_TYPE_COLOR)
1185 // Color type.
1186 if (role == ColorWellRole)
1187 interfaceMask |= 1 << WAI_TEXT;
1188#endif
1189
xan@webkit.orge4387102009-04-09 11:08:48 +00001190 return interfaceMask;
1191}
1192
1193static const char* getUniqueAccessibilityTypeName(guint16 interfaceMask)
1194{
1195#define WAI_TYPE_NAME_LEN (30) /* Enough for prefix + 5 hex characters (max) */
1196 static char name[WAI_TYPE_NAME_LEN + 1];
eric@webkit.org4ff6fd72009-11-10 09:53:33 +00001197
xan@webkit.orge4387102009-04-09 11:08:48 +00001198 g_sprintf(name, "WAIType%x", interfaceMask);
1199 name[WAI_TYPE_NAME_LEN] = '\0';
eric@webkit.org4ff6fd72009-11-10 09:53:33 +00001200
xan@webkit.orge4387102009-04-09 11:08:48 +00001201 return name;
1202}
1203
1204static GType getAccessibilityTypeFromObject(AccessibilityObject* coreObject)
1205{
1206 static const GTypeInfo typeInfo = {
1207 sizeof(WebKitAccessibleClass),
eric@webkit.org4ff6fd72009-11-10 09:53:33 +00001208 (GBaseInitFunc) 0,
1209 (GBaseFinalizeFunc) 0,
1210 (GClassInitFunc) 0,
1211 (GClassFinalizeFunc) 0,
1212 0, /* class data */
xan@webkit.orge4387102009-04-09 11:08:48 +00001213 sizeof(WebKitAccessible), /* instance size */
1214 0, /* nb preallocs */
eric@webkit.org4ff6fd72009-11-10 09:53:33 +00001215 (GInstanceInitFunc) 0,
1216 0 /* value table */
xan@webkit.orge4387102009-04-09 11:08:48 +00001217 };
1218
1219 guint16 interfaceMask = getInterfaceMaskFromObject(coreObject);
1220 const char* atkTypeName = getUniqueAccessibilityTypeName(interfaceMask);
1221 GType type = g_type_from_name(atkTypeName);
1222 if (type)
1223 return type;
1224
mario@webkit.orgf88ffcb2012-11-16 13:47:03 +00001225 type = g_type_register_static(WEBKIT_TYPE_ACCESSIBLE, atkTypeName, &typeInfo, GTypeFlags(0));
xan@webkit.orge4387102009-04-09 11:08:48 +00001226 for (guint i = 0; i < G_N_ELEMENTS(AtkInterfacesInitFunctions); i++) {
1227 if (interfaceMask & (1 << i))
1228 g_type_add_interface_static(type,
mario@webkit.orgf88ffcb2012-11-16 13:47:03 +00001229 GetAtkInterfaceTypeFromWAIType(static_cast<WAIType>(i)),
1230 &AtkInterfacesInitFunctions[i]);
xan@webkit.orge4387102009-04-09 11:08:48 +00001231 }
1232
alp@webkit.org3a5e7ca2008-05-20 01:12:20 +00001233 return type;
1234}
1235
mario@webkit.orgaacb2172012-01-23 11:43:28 +00001236WebKitAccessible* webkitAccessibleNew(AccessibilityObject* coreObject)
alp@webkit.org3a5e7ca2008-05-20 01:12:20 +00001237{
xan@webkit.orge4387102009-04-09 11:08:48 +00001238 GType type = getAccessibilityTypeFromObject(coreObject);
eric@webkit.org4ff6fd72009-11-10 09:53:33 +00001239 AtkObject* object = static_cast<AtkObject*>(g_object_new(type, 0));
xan@webkit.orge4387102009-04-09 11:08:48 +00001240
alp@webkit.org3a5e7ca2008-05-20 01:12:20 +00001241 atk_object_initialize(object, coreObject);
xan@webkit.orge4387102009-04-09 11:08:48 +00001242
alp@webkit.org3a5e7ca2008-05-20 01:12:20 +00001243 return WEBKIT_ACCESSIBLE(object);
1244}
1245
mario@webkit.orgaacb2172012-01-23 11:43:28 +00001246AccessibilityObject* webkitAccessibleGetAccessibilityObject(WebKitAccessible* accessible)
alp@webkit.org3a5e7ca2008-05-20 01:12:20 +00001247{
1248 return accessible->m_object;
1249}
1250
mario@webkit.orgaacb2172012-01-23 11:43:28 +00001251void webkitAccessibleDetach(WebKitAccessible* accessible)
alp@webkit.org3a5e7ca2008-05-20 01:12:20 +00001252{
alp@webkit.orgc7738992008-05-27 02:48:14 +00001253 ASSERT(accessible->m_object);
1254
mario@webkit.orgff8cf0f2013-09-27 09:44:20 +00001255 if (accessible->m_object->roleValue() == WebAreaRole)
mario@webkit.orgca849062013-10-21 14:02:03 +00001256 atk_object_notify_state_change(ATK_OBJECT(accessible), ATK_STATE_DEFUNCT, true);
mario@webkit.org92daa812011-02-16 17:12:38 +00001257
alp@webkit.orgc7738992008-05-27 02:48:14 +00001258 // We replace the WebCore AccessibilityObject with a fallback object that
1259 // provides default implementations to avoid repetitive null-checking after
1260 // detachment.
xan@webkit.orgc886cc62009-04-09 11:17:48 +00001261 accessible->m_object = fallbackObject();
alp@webkit.org3a5e7ca2008-05-20 01:12:20 +00001262}
1263
mario@webkit.orgff8cf0f2013-09-27 09:44:20 +00001264bool webkitAccessibleIsDetached(WebKitAccessible* accessible)
1265{
1266 ASSERT(accessible->m_object);
1267 return accessible->m_object == fallbackObject();
1268}
1269
mario@webkit.orgaacb2172012-01-23 11:43:28 +00001270AtkObject* webkitAccessibleGetFocusedElement(WebKitAccessible* accessible)
jmalonzo@webkit.orgaaaea3b2009-08-08 07:39:48 +00001271{
1272 if (!accessible->m_object)
1273 return 0;
1274
1275 RefPtr<AccessibilityObject> focusedObj = accessible->m_object->focusedUIElement();
1276 if (!focusedObj)
1277 return 0;
1278
1279 return focusedObj->wrapper();
1280}
1281
mario@webkit.org4c778d72012-01-09 10:16:20 +00001282AccessibilityObject* objectFocusedAndCaretOffsetUnignored(AccessibilityObject* referenceObject, int& offset)
eric@webkit.org2fade492010-01-11 12:03:36 +00001283{
eric@webkit.org9cf227c2010-01-19 21:31:01 +00001284 // Indication that something bogus has transpired.
1285 offset = -1;
eric@webkit.org2fade492010-01-11 12:03:36 +00001286
mario@webkit.org4c778d72012-01-09 10:16:20 +00001287 Document* document = referenceObject->document();
1288 if (!document)
mrobinson@webkit.orgbc2c5f22010-09-29 17:58:13 +00001289 return 0;
eric@webkit.org2fade492010-01-11 12:03:36 +00001290
mario@webkit.org4c778d72012-01-09 10:16:20 +00001291 Node* focusedNode = referenceObject->selection().end().containerNode();
1292 if (!focusedNode)
mrobinson@webkit.orgbc2c5f22010-09-29 17:58:13 +00001293 return 0;
eric@webkit.org2fade492010-01-11 12:03:36 +00001294
mario@webkit.org4c778d72012-01-09 10:16:20 +00001295 RenderObject* focusedRenderer = focusedNode->renderer();
1296 if (!focusedRenderer)
1297 return 0;
mario@webkit.org6698d352011-02-01 09:49:25 +00001298
mario@webkit.org4c778d72012-01-09 10:16:20 +00001299 AccessibilityObject* focusedObject = document->axObjectCache()->getOrCreate(focusedRenderer);
1300 if (!focusedObject)
1301 return 0;
mario@webkit.org6698d352011-02-01 09:49:25 +00001302
mario@webkit.org4c778d72012-01-09 10:16:20 +00001303 // Look for the actual (not ignoring accessibility) selected object.
mario@webkit.orgd048f762012-01-22 19:29:04 +00001304 AccessibilityObject* firstUnignoredParent = focusedObject;
1305 if (firstUnignoredParent->accessibilityIsIgnored())
1306 firstUnignoredParent = firstUnignoredParent->parentObjectUnignored();
1307 if (!firstUnignoredParent)
mario@webkit.org4c778d72012-01-09 10:16:20 +00001308 return 0;
1309
1310 // Don't ignore links if the offset is being requested for a link.
mario@webkit.orgd048f762012-01-22 19:29:04 +00001311 if (!referenceObject->isLink() && firstUnignoredParent->isLink())
1312 firstUnignoredParent = firstUnignoredParent->parentObjectUnignored();
1313 if (!firstUnignoredParent)
mario@webkit.org4c778d72012-01-09 10:16:20 +00001314 return 0;
1315
mario@webkit.orgd048f762012-01-22 19:29:04 +00001316 // The reference object must either coincide with the focused
1317 // object being considered, or be a descendant of it.
1318 if (referenceObject->isDescendantOfObject(firstUnignoredParent))
1319 referenceObject = firstUnignoredParent;
1320
mario@webkit.org4c778d72012-01-09 10:16:20 +00001321 Node* startNode = 0;
mario@webkit.orgd048f762012-01-22 19:29:04 +00001322 if (firstUnignoredParent != referenceObject || firstUnignoredParent->isTextControl()) {
mario@webkit.org4c778d72012-01-09 10:16:20 +00001323 // We need to use the first child's node of the reference
1324 // object as the start point to calculate the caret offset
1325 // because we want it to be relative to the object of
1326 // reference, not just to the focused object (which could have
1327 // previous siblings which should be taken into account too).
1328 AccessibilityObject* axFirstChild = referenceObject->firstChild();
1329 if (axFirstChild)
1330 startNode = axFirstChild->node();
1331 }
commit-queue@webkit.orgfb3e9152013-02-01 00:37:22 +00001332 // Getting the Position of a PseudoElement now triggers an assertion.
1333 // This can occur when clicking on empty space in a render block.
1334 if (!startNode || startNode->isPseudoElement())
mario@webkit.orgd048f762012-01-22 19:29:04 +00001335 startNode = firstUnignoredParent->node();
mario@webkit.org4c778d72012-01-09 10:16:20 +00001336
mario@webkit.orgb311bdb2012-02-01 17:58:57 +00001337 // Check if the node for the first parent object not ignoring
1338 // accessibility is null again before using it. This might happen
1339 // with certain kind of accessibility objects, such as the root
1340 // one (the scroller containing the webArea object).
1341 if (!startNode)
1342 return 0;
1343
mario@webkit.orgd048f762012-01-22 19:29:04 +00001344 VisiblePosition startPosition = VisiblePosition(positionBeforeNode(startNode), DOWNSTREAM);
1345 VisiblePosition endPosition = firstUnignoredParent->selection().visibleEnd();
mario@webkit.org4c778d72012-01-09 10:16:20 +00001346
1347 if (startPosition == endPosition)
1348 offset = 0;
1349 else if (!isStartOfLine(endPosition)) {
1350 RefPtr<Range> range = makeRange(startPosition, endPosition.previous());
1351 offset = TextIterator::rangeLength(range.get(), true) + 1;
1352 } else {
1353 RefPtr<Range> range = makeRange(startPosition, endPosition);
1354 offset = TextIterator::rangeLength(range.get(), true);
eric@webkit.org9cf227c2010-01-19 21:31:01 +00001355 }
mario@webkit.orgff7fd422011-01-27 19:38:03 +00001356
mario@webkit.orgd048f762012-01-22 19:29:04 +00001357 return firstUnignoredParent;
eric@webkit.org2fade492010-01-11 12:03:36 +00001358}
1359
mario@webkit.orgfecbacd2013-03-04 17:06:15 +00001360const char* cacheAndReturnAtkProperty(AtkObject* object, AtkCachedProperty property, String value)
1361{
1362 WebKitAccessiblePrivate* priv = WEBKIT_ACCESSIBLE(object)->priv;
1363 CString* propertyPtr = 0;
1364
1365 switch (property) {
1366 case AtkCachedAccessibleName:
1367 propertyPtr = &priv->accessibleName;
1368 break;
1369
1370 case AtkCachedAccessibleDescription:
1371 propertyPtr = &priv->accessibleDescription;
1372 break;
1373
1374 case AtkCachedActionName:
1375 propertyPtr = &priv->actionName;
1376 break;
1377
1378 case AtkCachedActionKeyBinding:
1379 propertyPtr = &priv->actionKeyBinding;
1380 break;
1381
1382 case AtkCachedDocumentLocale:
1383 propertyPtr = &priv->documentLocale;
1384 break;
1385
1386 case AtkCachedDocumentType:
1387 propertyPtr = &priv->documentType;
1388 break;
1389
1390 case AtkCachedDocumentEncoding:
1391 propertyPtr = &priv->documentEncoding;
1392 break;
1393
1394 case AtkCachedDocumentURI:
1395 propertyPtr = &priv->documentURI;
1396 break;
1397
1398 case AtkCachedImageDescription:
1399 propertyPtr = &priv->imageDescription;
1400 break;
1401
1402 default:
1403 ASSERT_NOT_REACHED();
1404 }
1405
1406 // Don't invalidate old memory if not stricly needed, since other
1407 // callers might be still holding on to it.
1408 if (*propertyPtr != value.utf8())
1409 *propertyPtr = value.utf8();
1410
1411 return (*propertyPtr).data();
1412}
1413
ddkilzer@apple.com8d878632008-11-09 19:50:37 +00001414#endif // HAVE(ACCESSIBILITY)