1 /*
2 * Copyright (C) 2010 Igalia S.L.
3 *
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Library General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) any later version.
8 *
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Library General Public License for more details.
13 *
14 * You should have received a copy of the GNU Library General Public License
15 * along with this library; see the file COPYING.LIB. If not, write to
16 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
17 * Boston, MA 02110-1301, USA.
18 */
19
20 #include "config.h"
21 #include "WebKitAccessibleHyperlink.h"
22
23 #if HAVE(ACCESSIBILITY)
24
25 #include "AXObjectCache.h"
26 #include "AccessibilityObject.h"
27 #include "AccessibilityObjectWrapperAtk.h"
28 #include "NotImplemented.h"
29 #include "Position.h"
30 #include "Range.h"
31 #include "RenderListMarker.h"
32 #include "RenderObject.h"
33 #include "TextIterator.h"
34 #include "htmlediting.h"
35
36 #include <atk/atk.h>
37 #include <glib.h>
38
39 using namespace WebCore;
40
41 struct _WebKitAccessibleHyperlinkPrivate {
42 WebKitAccessible* hyperlinkImpl;
43 };
44
45 #define WEBKIT_ACCESSIBLE_HYPERLINK_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE((obj), WEBKIT_TYPE_ACCESSIBLE_HYPERLINK, WebKitAccessibleHyperlinkPrivate))
46
47 enum {
48 PROP_0,
49
50 PROP_HYPERLINK_IMPL
51 };
52
53 static gpointer webkitAccessibleHyperlinkParentClass = 0;
54
55 // Used to provide const char* returns.
returnString(const String & str)56 static const char* returnString(const String& str)
57 {
58 static CString returnedString;
59 returnedString = str.utf8();
60 return returnedString.data();
61 }
62
core(WebKitAccessible * accessible)63 static AccessibilityObject* core(WebKitAccessible* accessible)
64 {
65 if (!accessible || !WEBKIT_IS_ACCESSIBLE(accessible))
66 return 0;
67
68 return webkit_accessible_get_accessibility_object(accessible);
69 }
70
core(WebKitAccessibleHyperlink * link)71 static AccessibilityObject* core(WebKitAccessibleHyperlink* link)
72 {
73 if (!link)
74 return 0;
75
76 return core(link->priv->hyperlinkImpl);
77 }
78
core(AtkHyperlink * link)79 static AccessibilityObject* core(AtkHyperlink* link)
80 {
81 if (!WEBKIT_IS_ACCESSIBLE_HYPERLINK(link))
82 return 0;
83
84 return core(WEBKIT_ACCESSIBLE_HYPERLINK(link));
85 }
86
core(AtkAction * action)87 static AccessibilityObject* core(AtkAction* action)
88 {
89 return core(WEBKIT_ACCESSIBLE_HYPERLINK(action));
90 }
91
92
webkitAccessibleHyperlinkActionDoAction(AtkAction * action,gint index)93 static gboolean webkitAccessibleHyperlinkActionDoAction(AtkAction* action, gint index)
94 {
95 g_return_val_if_fail(WEBKIT_IS_ACCESSIBLE_HYPERLINK(action), FALSE);
96 g_return_val_if_fail(WEBKIT_ACCESSIBLE_HYPERLINK(action)->priv->hyperlinkImpl, FALSE);
97 g_return_val_if_fail(!index, FALSE);
98
99 if (!ATK_IS_ACTION(WEBKIT_ACCESSIBLE_HYPERLINK(action)->priv->hyperlinkImpl))
100 return FALSE;
101
102 AccessibilityObject* coreObject = core(action);
103 if (!coreObject)
104 return FALSE;
105
106 return coreObject->performDefaultAction();
107 }
108
webkitAccessibleHyperlinkActionGetNActions(AtkAction * action)109 static gint webkitAccessibleHyperlinkActionGetNActions(AtkAction* action)
110 {
111 g_return_val_if_fail(WEBKIT_IS_ACCESSIBLE_HYPERLINK(action), 0);
112 g_return_val_if_fail(WEBKIT_ACCESSIBLE_HYPERLINK(action)->priv->hyperlinkImpl, 0);
113
114 if (!ATK_IS_ACTION(WEBKIT_ACCESSIBLE_HYPERLINK(action)->priv->hyperlinkImpl))
115 return 0;
116
117 return 1;
118 }
119
webkitAccessibleHyperlinkActionGetDescription(AtkAction * action,gint index)120 static const gchar* webkitAccessibleHyperlinkActionGetDescription(AtkAction* action, gint index)
121 {
122 g_return_val_if_fail(WEBKIT_IS_ACCESSIBLE_HYPERLINK(action), 0);
123 g_return_val_if_fail(WEBKIT_ACCESSIBLE_HYPERLINK(action)->priv->hyperlinkImpl, 0);
124 g_return_val_if_fail(!index, 0);
125
126 // TODO: Need a way to provide/localize action descriptions.
127 notImplemented();
128 return "";
129 }
130
webkitAccessibleHyperlinkActionGetKeybinding(AtkAction * action,gint index)131 static const gchar* webkitAccessibleHyperlinkActionGetKeybinding(AtkAction* action, gint index)
132 {
133 g_return_val_if_fail(WEBKIT_IS_ACCESSIBLE_HYPERLINK(action), 0);
134 g_return_val_if_fail(WEBKIT_ACCESSIBLE_HYPERLINK(action)->priv->hyperlinkImpl, 0);
135 g_return_val_if_fail(!index, 0);
136
137 if (!ATK_IS_ACTION(WEBKIT_ACCESSIBLE_HYPERLINK(action)->priv->hyperlinkImpl))
138 return 0;
139
140 AccessibilityObject* coreObject = core(action);
141 if (!coreObject)
142 return 0;
143
144 return returnString(coreObject->accessKey().string());
145 }
146
webkitAccessibleHyperlinkActionGetName(AtkAction * action,gint index)147 static const gchar* webkitAccessibleHyperlinkActionGetName(AtkAction* action, gint index)
148 {
149 g_return_val_if_fail(WEBKIT_IS_ACCESSIBLE_HYPERLINK(action), 0);
150 g_return_val_if_fail(WEBKIT_ACCESSIBLE_HYPERLINK(action)->priv->hyperlinkImpl, 0);
151 g_return_val_if_fail(!index, 0);
152
153 if (!ATK_IS_ACTION(WEBKIT_ACCESSIBLE_HYPERLINK(action)->priv->hyperlinkImpl))
154 return 0;
155
156 AccessibilityObject* coreObject = core(action);
157 if (!coreObject)
158 return 0;
159
160 return returnString(coreObject->actionVerb());
161 }
162
atkActionInterfaceInit(AtkActionIface * iface)163 static void atkActionInterfaceInit(AtkActionIface* iface)
164 {
165 iface->do_action = webkitAccessibleHyperlinkActionDoAction;
166 iface->get_n_actions = webkitAccessibleHyperlinkActionGetNActions;
167 iface->get_description = webkitAccessibleHyperlinkActionGetDescription;
168 iface->get_keybinding = webkitAccessibleHyperlinkActionGetKeybinding;
169 iface->get_name = webkitAccessibleHyperlinkActionGetName;
170 }
171
webkitAccessibleHyperlinkGetURI(AtkHyperlink * link,gint index)172 static gchar* webkitAccessibleHyperlinkGetURI(AtkHyperlink* link, gint index)
173 {
174 g_return_val_if_fail(WEBKIT_IS_ACCESSIBLE_HYPERLINK(link), 0);
175 // FIXME: Do NOT support more than one instance of an AtkObject
176 // implementing AtkHyperlinkImpl in every instance of AtkHyperLink
177 g_return_val_if_fail(!index, 0);
178
179 AccessibilityObject* coreObject = core(link);
180 if (!coreObject || coreObject->url().isNull())
181 return 0;
182
183 return g_strdup(returnString(coreObject->url().string()));
184 }
185
webkitAccessibleHyperlinkGetObject(AtkHyperlink * link,gint index)186 static AtkObject* webkitAccessibleHyperlinkGetObject(AtkHyperlink* link, gint index)
187 {
188 g_return_val_if_fail(WEBKIT_IS_ACCESSIBLE_HYPERLINK(link), 0);
189 g_return_val_if_fail(WEBKIT_ACCESSIBLE_HYPERLINK(link)->priv->hyperlinkImpl, 0);
190
191 // FIXME: Do NOT support more than one instance of an AtkObject
192 // implementing AtkHyperlinkImpl in every instance of AtkHyperLink
193 g_return_val_if_fail(!index, 0);
194
195 return ATK_OBJECT(WEBKIT_ACCESSIBLE_HYPERLINK(link)->priv->hyperlinkImpl);
196 }
197
getRangeLengthForObject(AccessibilityObject * obj,Range * range)198 static gint getRangeLengthForObject(AccessibilityObject* obj, Range* range)
199 {
200 // This is going to be the actual length in most of the cases
201 int baseLength = TextIterator::rangeLength(range, true);
202
203 // Check whether the current hyperlink belongs to a list item.
204 // If so, we need to consider the length of the item's marker
205 AccessibilityObject* parent = obj->parentObjectUnignored();
206 if (!parent || !parent->isAccessibilityRenderObject() || !parent->isListItem())
207 return baseLength;
208
209 // Even if we don't expose list markers to Assistive
210 // Technologies, we need to have a way to measure their length
211 // for those cases when it's needed to take it into account
212 // separately (as in getAccessibilityObjectForOffset)
213 AccessibilityObject* markerObj = parent->firstChild();
214 if (!markerObj)
215 return baseLength;
216
217 RenderObject* renderer = markerObj->renderer();
218 if (!renderer || !renderer->isListMarker())
219 return baseLength;
220
221 RenderListMarker* marker = toRenderListMarker(renderer);
222 return baseLength + marker->text().length() + marker->suffix().length();
223 }
224
webkitAccessibleHyperlinkGetStartIndex(AtkHyperlink * link)225 static gint webkitAccessibleHyperlinkGetStartIndex(AtkHyperlink* link)
226 {
227 g_return_val_if_fail(WEBKIT_IS_ACCESSIBLE_HYPERLINK(link), 0);
228
229 AccessibilityObject* coreObject = core(link);
230 if (!coreObject)
231 return 0;
232
233 AccessibilityObject* parentUnignored = coreObject->parentObjectUnignored();
234 if (!parentUnignored)
235 return 0;
236
237 Node* node = coreObject->node();
238 if (!node)
239 return 0;
240
241 Node* parentNode = parentUnignored->node();
242 if (!parentNode)
243 return 0;
244
245 RefPtr<Range> range = Range::create(node->document(), firstPositionInOrBeforeNode(parentNode), firstPositionInOrBeforeNode(node));
246 return getRangeLengthForObject(coreObject, range.get());
247 }
248
webkitAccessibleHyperlinkGetEndIndex(AtkHyperlink * link)249 static gint webkitAccessibleHyperlinkGetEndIndex(AtkHyperlink* link)
250 {
251 g_return_val_if_fail(WEBKIT_IS_ACCESSIBLE_HYPERLINK(link), 0);
252
253 AccessibilityObject* coreObject = core(link);
254 if (!coreObject)
255 return 0;
256
257 AccessibilityObject* parentUnignored = coreObject->parentObjectUnignored();
258 if (!parentUnignored)
259 return 0;
260
261 Node* node = coreObject->node();
262 if (!node)
263 return 0;
264
265 Node* parentNode = parentUnignored->node();
266 if (!parentNode)
267 return 0;
268
269 RefPtr<Range> range = Range::create(node->document(), firstPositionInOrBeforeNode(parentNode), lastPositionInOrAfterNode(node));
270 return getRangeLengthForObject(coreObject, range.get());
271 }
272
webkitAccessibleHyperlinkIsValid(AtkHyperlink * link)273 static gboolean webkitAccessibleHyperlinkIsValid(AtkHyperlink* link)
274 {
275 g_return_val_if_fail(WEBKIT_IS_ACCESSIBLE_HYPERLINK(link), 0);
276 g_return_val_if_fail(WEBKIT_ACCESSIBLE_HYPERLINK(link)->priv->hyperlinkImpl, FALSE);
277
278 // Link is valid for the whole object's lifetime
279 return TRUE;
280 }
281
webkitAccessibleHyperlinkGetNAnchors(AtkHyperlink * link)282 static gint webkitAccessibleHyperlinkGetNAnchors(AtkHyperlink* link)
283 {
284 // FIXME Do NOT support more than one instance of an AtkObject
285 // implementing AtkHyperlinkImpl in every instance of AtkHyperLink
286 g_return_val_if_fail(WEBKIT_IS_ACCESSIBLE_HYPERLINK(link), 0);
287 g_return_val_if_fail(WEBKIT_ACCESSIBLE_HYPERLINK(link)->priv->hyperlinkImpl, 0);
288 return 1;
289 }
290
webkitAccessibleHyperlinkIsSelectedLink(AtkHyperlink * link)291 static gboolean webkitAccessibleHyperlinkIsSelectedLink(AtkHyperlink* link)
292 {
293 // Not implemented: this function is deprecated in ATK now
294 notImplemented();
295 return FALSE;
296 }
297
webkitAccessibleHyperlinkGetProperty(GObject * object,guint propId,GValue * value,GParamSpec * pspec)298 static void webkitAccessibleHyperlinkGetProperty(GObject* object, guint propId, GValue* value, GParamSpec* pspec)
299 {
300 switch (propId) {
301 case PROP_HYPERLINK_IMPL:
302 g_value_set_object(value, WEBKIT_ACCESSIBLE_HYPERLINK(object)->priv->hyperlinkImpl);
303 break;
304 default:
305 G_OBJECT_WARN_INVALID_PROPERTY_ID(object, propId, pspec);
306 }
307 }
308
webkitAccessibleHyperlinkSetProperty(GObject * object,guint propId,const GValue * value,GParamSpec * pspec)309 static void webkitAccessibleHyperlinkSetProperty(GObject* object, guint propId, const GValue* value, GParamSpec* pspec)
310 {
311 WebKitAccessibleHyperlinkPrivate* priv = WEBKIT_ACCESSIBLE_HYPERLINK(object)->priv;
312
313 switch (propId) {
314 case PROP_HYPERLINK_IMPL:
315 // No need to check and unref previous values of
316 // priv->hyperlinkImpl as this is a CONSTRUCT ONLY property
317 priv->hyperlinkImpl = WEBKIT_ACCESSIBLE(g_value_get_object(value));
318 g_object_weak_ref(G_OBJECT(priv->hyperlinkImpl), (GWeakNotify)g_object_unref, object);
319 break;
320 default:
321 G_OBJECT_WARN_INVALID_PROPERTY_ID(object, propId, pspec);
322 }
323 }
324
webkitAccessibleHyperlinkFinalize(GObject * object)325 static void webkitAccessibleHyperlinkFinalize(GObject* object)
326 {
327 G_OBJECT_CLASS(webkitAccessibleHyperlinkParentClass)->finalize(object);
328 }
329
webkitAccessibleHyperlinkClassInit(AtkHyperlinkClass * klass)330 static void webkitAccessibleHyperlinkClassInit(AtkHyperlinkClass* klass)
331 {
332 GObjectClass* gobjectClass = G_OBJECT_CLASS(klass);
333
334 webkitAccessibleHyperlinkParentClass = g_type_class_peek_parent(klass);
335
336 gobjectClass->finalize = webkitAccessibleHyperlinkFinalize;
337 gobjectClass->set_property = webkitAccessibleHyperlinkSetProperty;
338 gobjectClass->get_property = webkitAccessibleHyperlinkGetProperty;
339
340 klass->get_uri = webkitAccessibleHyperlinkGetURI;
341 klass->get_object = webkitAccessibleHyperlinkGetObject;
342 klass->get_start_index = webkitAccessibleHyperlinkGetStartIndex;
343 klass->get_end_index = webkitAccessibleHyperlinkGetEndIndex;
344 klass->is_valid = webkitAccessibleHyperlinkIsValid;
345 klass->get_n_anchors = webkitAccessibleHyperlinkGetNAnchors;
346 klass->is_selected_link = webkitAccessibleHyperlinkIsSelectedLink;
347
348 g_object_class_install_property(gobjectClass, PROP_HYPERLINK_IMPL,
349 g_param_spec_object("hyperlink-impl",
350 "Hyperlink implementation",
351 "The associated WebKitAccessible instance.",
352 WEBKIT_TYPE_ACCESSIBLE,
353 (GParamFlags)(G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS)));
354
355 g_type_class_add_private(gobjectClass, sizeof(WebKitAccessibleHyperlinkPrivate));
356 }
357
webkitAccessibleHyperlinkInit(AtkHyperlink * link)358 static void webkitAccessibleHyperlinkInit(AtkHyperlink* link)
359 {
360 WEBKIT_ACCESSIBLE_HYPERLINK(link)->priv = WEBKIT_ACCESSIBLE_HYPERLINK_GET_PRIVATE(link);
361 WEBKIT_ACCESSIBLE_HYPERLINK(link)->priv->hyperlinkImpl = 0;
362 }
363
webkitAccessibleHyperlinkGetType()364 GType webkitAccessibleHyperlinkGetType()
365 {
366 static volatile gsize typeVolatile = 0;
367
368 if (g_once_init_enter(&typeVolatile)) {
369 static const GTypeInfo tinfo = {
370 sizeof(WebKitAccessibleHyperlinkClass),
371 (GBaseInitFunc) 0,
372 (GBaseFinalizeFunc) 0,
373 (GClassInitFunc) webkitAccessibleHyperlinkClassInit,
374 (GClassFinalizeFunc) 0,
375 0, /* class data */
376 sizeof(WebKitAccessibleHyperlink), /* instance size */
377 0, /* nb preallocs */
378 (GInstanceInitFunc) webkitAccessibleHyperlinkInit,
379 0 /* value table */
380 };
381
382 static const GInterfaceInfo actionInfo = {
383 (GInterfaceInitFunc)(GInterfaceInitFunc)atkActionInterfaceInit,
384 (GInterfaceFinalizeFunc) 0, 0
385 };
386
387 GType type = g_type_register_static(ATK_TYPE_HYPERLINK, "WebKitAccessibleHyperlink", &tinfo, GTypeFlags(0));
388 g_type_add_interface_static(type, ATK_TYPE_ACTION, &actionInfo);
389
390 g_once_init_leave(&typeVolatile, type);
391 }
392
393 return typeVolatile;
394 }
395
webkitAccessibleHyperlinkNew(AtkHyperlinkImpl * hyperlinkImpl)396 WebKitAccessibleHyperlink* webkitAccessibleHyperlinkNew(AtkHyperlinkImpl* hyperlinkImpl)
397 {
398 g_return_val_if_fail(ATK_IS_HYPERLINK_IMPL(hyperlinkImpl), 0);
399 return WEBKIT_ACCESSIBLE_HYPERLINK(g_object_new(WEBKIT_TYPE_ACCESSIBLE_HYPERLINK, "hyperlink-impl", hyperlinkImpl, 0));
400 }
401
webkitAccessibleHyperlinkGetAccessibilityObject(WebKitAccessibleHyperlink * link)402 WebCore::AccessibilityObject* webkitAccessibleHyperlinkGetAccessibilityObject(WebKitAccessibleHyperlink* link)
403 {
404 g_return_val_if_fail(WEBKIT_IS_ACCESSIBLE_HYPERLINK(link), 0);
405 return core(link);
406 }
407
408 #endif // HAVE(ACCESSIBILITY)
409