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