1/*
2 * Copyright (C) 2012 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 "WebKitHitTestResult.h"
22
23#include "WebHitTestResultData.h"
24#include "WebKitHitTestResultPrivate.h"
25#include <glib/gi18n-lib.h>
26#include <wtf/glib/WTFGType.h>
27#include <wtf/text/CString.h>
28
29using namespace WebKit;
30
31/**
32 * SECTION: WebKitHitTestResult
33 * @Short_description: Result of a Hit Test
34 * @Title: WebKitHitTestResult
35 * @See_also: #WebKitWebView
36 *
37 * A Hit Test is an operation to get context information about a given
38 * point in a #WebKitWebView. #WebKitHitTestResult represents the
39 * result of a Hit Test. It provides context information about what is
40 * at the coordinates of the Hit Test, such as if there's a link,
41 * an image or a media.
42 *
43 * You can get the context of the HitTestResult with
44 * webkit_hit_test_result_get_context() that returns a bitmask of
45 * #WebKitHitTestResultContext flags. You can also use
46 * webkit_hit_test_result_context_is_link(), webkit_hit_test_result_context_is_image() and
47 * webkit_hit_test_result_context_is_media() to determine whether there's
48 * a link, image or a media element at the coordinates of the Hit Test.
49 * Note that it's possible that several #WebKitHitTestResultContext flags
50 * are active at the same time, for example if there's a link containing an image.
51 *
52 * When the mouse is moved over a #WebKitWebView a Hit Test is performed
53 * for the mouse coordinates and #WebKitWebView::mouse-target-changed
54 * signal is emitted with a #WebKitHitTestResult.
55 *
56 */
57
58enum {
59 PROP_0,
60
61 PROP_CONTEXT,
62 PROP_LINK_URI,
63 PROP_LINK_TITLE,
64 PROP_LINK_LABEL,
65 PROP_IMAGE_URI,
66 PROP_MEDIA_URI
67};
68
69struct _WebKitHitTestResultPrivate {
70 unsigned int context;
71 CString linkURI;
72 CString linkTitle;
73 CString linkLabel;
74 CString imageURI;
75 CString mediaURI;
76};
77
78WEBKIT_DEFINE_TYPE(WebKitHitTestResult, webkit_hit_test_result, G_TYPE_OBJECT)
79
80static void webkitHitTestResultGetProperty(GObject* object, guint propId, GValue* value, GParamSpec* paramSpec)
81{
82 WebKitHitTestResult* hitTestResult = WEBKIT_HIT_TEST_RESULT(object);
83
84 switch (propId) {
85 case PROP_CONTEXT:
86 g_value_set_uint(value, webkit_hit_test_result_get_context(hitTestResult));
87 break;
88 case PROP_LINK_URI:
89 g_value_set_string(value, webkit_hit_test_result_get_link_uri(hitTestResult));
90 break;
91 case PROP_LINK_TITLE:
92 g_value_set_string(value, webkit_hit_test_result_get_link_title(hitTestResult));
93 break;
94 case PROP_LINK_LABEL:
95 g_value_set_string(value, webkit_hit_test_result_get_link_label(hitTestResult));
96 break;
97 case PROP_IMAGE_URI:
98 g_value_set_string(value, webkit_hit_test_result_get_image_uri(hitTestResult));
99 break;
100 case PROP_MEDIA_URI:
101 g_value_set_string(value, webkit_hit_test_result_get_media_uri(hitTestResult));
102 break;
103 default:
104 G_OBJECT_WARN_INVALID_PROPERTY_ID(object, propId, paramSpec);
105 }
106}
107
108static void webkitHitTestResultSetProperty(GObject* object, guint propId, const GValue* value, GParamSpec* paramSpec)
109{
110 WebKitHitTestResult* hitTestResult = WEBKIT_HIT_TEST_RESULT(object);
111
112 switch (propId) {
113 case PROP_CONTEXT:
114 hitTestResult->priv->context = g_value_get_uint(value);
115 break;
116 case PROP_LINK_URI:
117 hitTestResult->priv->linkURI = g_value_get_string(value);
118 break;
119 case PROP_LINK_TITLE:
120 hitTestResult->priv->linkTitle = g_value_get_string(value);
121 break;
122 case PROP_LINK_LABEL:
123 hitTestResult->priv->linkLabel = g_value_get_string(value);
124 break;
125 case PROP_IMAGE_URI:
126 hitTestResult->priv->imageURI = g_value_get_string(value);
127 break;
128 case PROP_MEDIA_URI:
129 hitTestResult->priv->mediaURI = g_value_get_string(value);
130 break;
131 default:
132 G_OBJECT_WARN_INVALID_PROPERTY_ID(object, propId, paramSpec);
133 }
134}
135
136static void webkit_hit_test_result_class_init(WebKitHitTestResultClass* hitTestResultClass)
137{
138 GObjectClass* objectClass = G_OBJECT_CLASS(hitTestResultClass);
139 objectClass->get_property = webkitHitTestResultGetProperty;
140 objectClass->set_property = webkitHitTestResultSetProperty;
141
142 GParamFlags paramFlags = static_cast<GParamFlags>(WEBKIT_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY);
143
144 /**
145 * WebKitHitTestResult:context:
146 *
147 * Bitmask of #WebKitHitTestResultContext flags representing
148 * the context of the #WebKitHitTestResult.
149 */
150 g_object_class_install_property(objectClass,
151 PROP_CONTEXT,
152 g_param_spec_uint("context",
153 _("Context"),
154 _("Flags with the context of the WebKitHitTestResult"),
155 0, G_MAXUINT, 0,
156 paramFlags));
157
158 /**
159 * WebKitHitTestResult:link-uri:
160 *
161 * The URI of the link if flag %WEBKIT_HIT_TEST_RESULT_CONTEXT_LINK
162 * is present in #WebKitHitTestResult:context
163 */
164 g_object_class_install_property(objectClass,
165 PROP_LINK_URI,
166 g_param_spec_string("link-uri",
167 _("Link URI"),
168 _("The link URI"),
169 0,
170 paramFlags));
171 /**
172 * WebKitHitTestResult:link-title:
173 *
174 * The title of the link if flag %WEBKIT_HIT_TEST_RESULT_CONTEXT_LINK
175 * is present in #WebKitHitTestResult:context
176 */
177 g_object_class_install_property(objectClass,
178 PROP_LINK_TITLE,
179 g_param_spec_string("link-title",
180 _("Link Title"),
181 _("The link title"),
182 0,
183 paramFlags));
184 /**
185 * WebKitHitTestResult:link-label:
186 *
187 * The label of the link if flag %WEBKIT_HIT_TEST_RESULT_CONTEXT_LINK
188 * is present in #WebKitHitTestResult:context
189 */
190 g_object_class_install_property(objectClass,
191 PROP_LINK_LABEL,
192 g_param_spec_string("link-label",
193 _("Link Label"),
194 _("The link label"),
195 0,
196 paramFlags));
197 /**
198 * WebKitHitTestResult:image-uri:
199 *
200 * The URI of the image if flag %WEBKIT_HIT_TEST_RESULT_CONTEXT_IMAGE
201 * is present in #WebKitHitTestResult:context
202 */
203 g_object_class_install_property(objectClass,
204 PROP_IMAGE_URI,
205 g_param_spec_string("image-uri",
206 _("Image URI"),
207 _("The image URI"),
208 0,
209 paramFlags));
210 /**
211 * WebKitHitTestResult:media-uri:
212 *
213 * The URI of the media if flag %WEBKIT_HIT_TEST_RESULT_CONTEXT_MEDIA
214 * is present in #WebKitHitTestResult:context
215 */
216 g_object_class_install_property(objectClass,
217 PROP_MEDIA_URI,
218 g_param_spec_string("media-uri",
219 _("Media URI"),
220 _("The media URI"),
221 0,
222 paramFlags));
223}
224
225WebKitHitTestResult* webkitHitTestResultCreate(const WebHitTestResultData& hitTestResult)
226{
227 unsigned context = WEBKIT_HIT_TEST_RESULT_CONTEXT_DOCUMENT;
228
229 if (!hitTestResult.absoluteLinkURL.isEmpty())
230 context |= WEBKIT_HIT_TEST_RESULT_CONTEXT_LINK;
231
232 if (!hitTestResult.absoluteImageURL.isEmpty())
233 context |= WEBKIT_HIT_TEST_RESULT_CONTEXT_IMAGE;
234
235 if (!hitTestResult.absoluteMediaURL.isEmpty())
236 context |= WEBKIT_HIT_TEST_RESULT_CONTEXT_MEDIA;
237
238 if (hitTestResult.isContentEditable)
239 context |= WEBKIT_HIT_TEST_RESULT_CONTEXT_EDITABLE;
240
241 if (hitTestResult.isScrollbar)
242 context |= WEBKIT_HIT_TEST_RESULT_CONTEXT_SCROLLBAR;
243
244 if (hitTestResult.isSelected)
245 context |= WEBKIT_HIT_TEST_RESULT_CONTEXT_SELECTION;
246
247 return WEBKIT_HIT_TEST_RESULT(g_object_new(WEBKIT_TYPE_HIT_TEST_RESULT,
248 "context", context,
249 "link-uri", context & WEBKIT_HIT_TEST_RESULT_CONTEXT_LINK ? hitTestResult.absoluteLinkURL.utf8().data() : nullptr,
250 "image-uri", context & WEBKIT_HIT_TEST_RESULT_CONTEXT_IMAGE ? hitTestResult.absoluteImageURL.utf8().data() : nullptr,
251 "media-uri", context & WEBKIT_HIT_TEST_RESULT_CONTEXT_MEDIA ? hitTestResult.absoluteMediaURL.utf8().data() : nullptr,
252 "link-title", !hitTestResult.linkTitle.isEmpty() ? hitTestResult.linkTitle.utf8().data() : nullptr,
253 "link-label", !hitTestResult.linkLabel.isEmpty() ? hitTestResult.linkLabel.utf8().data() : nullptr,
254 nullptr));
255}
256
257static bool stringIsEqualToCString(const String& string, const CString& cString)
258{
259 return ((string.isEmpty() && cString.isNull()) || (string.utf8() == cString));
260}
261
262bool webkitHitTestResultCompare(WebKitHitTestResult* hitTestResult, const WebHitTestResultData& webHitTestResult)
263{
264 WebKitHitTestResultPrivate* priv = hitTestResult->priv;
265 return webHitTestResult.isContentEditable == webkit_hit_test_result_context_is_editable(hitTestResult)
266 && webHitTestResult.isScrollbar == webkit_hit_test_result_context_is_scrollbar(hitTestResult)
267 && webHitTestResult.isSelected == webkit_hit_test_result_context_is_selection(hitTestResult)
268 && stringIsEqualToCString(webHitTestResult.absoluteLinkURL, priv->linkURI)
269 && stringIsEqualToCString(webHitTestResult.linkTitle, priv->linkTitle)
270 && stringIsEqualToCString(webHitTestResult.linkLabel, priv->linkLabel)
271 && stringIsEqualToCString(webHitTestResult.absoluteImageURL, priv->imageURI)
272 && stringIsEqualToCString(webHitTestResult.absoluteMediaURL, priv->mediaURI);
273}
274
275/**
276 * webkit_hit_test_result_get_context:
277 * @hit_test_result: a #WebKitHitTestResult
278 *
279 * Gets the value of the #WebKitHitTestResult:context property.
280 *
281 * Returns: a bitmask of #WebKitHitTestResultContext flags
282 */
283guint webkit_hit_test_result_get_context(WebKitHitTestResult* hitTestResult)
284{
285 g_return_val_if_fail(WEBKIT_IS_HIT_TEST_RESULT(hitTestResult), 0);
286
287 return hitTestResult->priv->context;
288}
289
290/**
291 * webkit_hit_test_result_context_is_link:
292 * @hit_test_result: a #WebKitHitTestResult
293 *
294 * Gets whether %WEBKIT_HIT_TEST_RESULT_CONTEXT_LINK flag is present in
295 * #WebKitHitTestResult:context.
296 *
297 * Returns: %TRUE if there's a link element in the coordinates of the Hit Test,
298 * or %FALSE otherwise
299 */
300gboolean webkit_hit_test_result_context_is_link(WebKitHitTestResult* hitTestResult)
301{
302 g_return_val_if_fail(WEBKIT_IS_HIT_TEST_RESULT(hitTestResult), FALSE);
303
304 return hitTestResult->priv->context & WEBKIT_HIT_TEST_RESULT_CONTEXT_LINK;
305}
306
307/**
308 * webkit_hit_test_result_context_is_image:
309 * @hit_test_result: a #WebKitHitTestResult
310 *
311 * Gets whether %WEBKIT_HIT_TEST_RESULT_CONTEXT_IMAGE flag is present in
312 * #WebKitHitTestResult:context.
313 *
314 * Returns: %TRUE if there's an image element in the coordinates of the Hit Test,
315 * or %FALSE otherwise
316 */
317gboolean webkit_hit_test_result_context_is_image(WebKitHitTestResult* hitTestResult)
318{
319 g_return_val_if_fail(WEBKIT_IS_HIT_TEST_RESULT(hitTestResult), FALSE);
320
321 return hitTestResult->priv->context & WEBKIT_HIT_TEST_RESULT_CONTEXT_IMAGE;
322}
323
324/**
325 * webkit_hit_test_result_context_is_media:
326 * @hit_test_result: a #WebKitHitTestResult
327 *
328 * Gets whether %WEBKIT_HIT_TEST_RESULT_CONTEXT_MEDIA flag is present in
329 * #WebKitHitTestResult:context.
330 *
331 * Returns: %TRUE if there's a media element in the coordinates of the Hit Test,
332 * or %FALSE otherwise
333 */
334gboolean webkit_hit_test_result_context_is_media(WebKitHitTestResult* hitTestResult)
335{
336 g_return_val_if_fail(WEBKIT_IS_HIT_TEST_RESULT(hitTestResult), FALSE);
337
338 return hitTestResult->priv->context & WEBKIT_HIT_TEST_RESULT_CONTEXT_MEDIA;
339}
340
341/**
342 * webkit_hit_test_result_context_is_editable:
343 * @hit_test_result: a #WebKitHitTestResult
344 *
345 * Gets whether %WEBKIT_HIT_TEST_RESULT_CONTEXT_EDITABLE flag is present in
346 * #WebKitHitTestResult:context.
347 *
348 * Returns: %TRUE if there's an editable element at the coordinates of the @hit_test_result,
349 * or %FALSE otherwise
350 */
351gboolean webkit_hit_test_result_context_is_editable(WebKitHitTestResult* hitTestResult)
352{
353 g_return_val_if_fail(WEBKIT_IS_HIT_TEST_RESULT(hitTestResult), FALSE);
354
355 return hitTestResult->priv->context & WEBKIT_HIT_TEST_RESULT_CONTEXT_EDITABLE;
356}
357
358/**
359 * webkit_hit_test_result_context_is_selection:
360 * @hit_test_result: a #WebKitHitTestResult
361 *
362 * Gets whether %WEBKIT_HIT_TEST_RESULT_CONTEXT_SELECTION flag is present in
363 * #WebKitHitTestResult:context.
364 *
365 * Returns: %TRUE if there's a selected element at the coordinates of the @hit_test_result,
366 * or %FALSE otherwise
367 *
368 * Since: 2.8
369 */
370gboolean webkit_hit_test_result_context_is_selection(WebKitHitTestResult* hitTestResult)
371{
372 g_return_val_if_fail(WEBKIT_IS_HIT_TEST_RESULT(hitTestResult), FALSE);
373
374 return hitTestResult->priv->context & WEBKIT_HIT_TEST_RESULT_CONTEXT_SELECTION;
375}
376
377/**
378 * webkit_hit_test_result_get_link_uri:
379 * @hit_test_result: a #WebKitHitTestResult
380 *
381 * Gets the value of the #WebKitHitTestResult:link-uri property.
382 *
383 * Returns: the URI of the link element in the coordinates of the Hit Test,
384 * or %NULL if there isn't a link element in @hit_test_result context
385 */
386const gchar* webkit_hit_test_result_get_link_uri(WebKitHitTestResult* hitTestResult)
387{
388 g_return_val_if_fail(WEBKIT_IS_HIT_TEST_RESULT(hitTestResult), 0);
389
390 return hitTestResult->priv->linkURI.data();
391}
392
393/**
394 * webkit_hit_test_result_get_link_title:
395 * @hit_test_result: a #WebKitHitTestResult
396 *
397 * Gets the value of the #WebKitHitTestResult:link-title property.
398 *
399 * Returns: the title of the link element in the coordinates of the Hit Test,
400 * or %NULL if there isn't a link element in @hit_test_result context or the
401 * link element doesn't have a title
402 */
403const gchar* webkit_hit_test_result_get_link_title(WebKitHitTestResult* hitTestResult)
404{
405 g_return_val_if_fail(WEBKIT_IS_HIT_TEST_RESULT(hitTestResult), 0);
406
407 return hitTestResult->priv->linkTitle.data();
408}
409
410/**
411 * webkit_hit_test_result_get_link_label:
412 * @hit_test_result: a #WebKitHitTestResult
413 *
414 * Gets the value of the #WebKitHitTestResult:link-label property.
415 *
416 * Returns: the label of the link element in the coordinates of the Hit Test,
417 * or %NULL if there isn't a link element in @hit_test_result context or the
418 * link element doesn't have a label
419 */
420const gchar* webkit_hit_test_result_get_link_label(WebKitHitTestResult* hitTestResult)
421{
422 g_return_val_if_fail(WEBKIT_IS_HIT_TEST_RESULT(hitTestResult), 0);
423
424 return hitTestResult->priv->linkLabel.data();
425}
426
427/**
428 * webkit_hit_test_result_get_image_uri:
429 * @hit_test_result: a #WebKitHitTestResult
430 *
431 * Gets the value of the #WebKitHitTestResult:image-uri property.
432 *
433 * Returns: the URI of the image element in the coordinates of the Hit Test,
434 * or %NULL if there isn't an image element in @hit_test_result context
435 */
436const gchar* webkit_hit_test_result_get_image_uri(WebKitHitTestResult* hitTestResult)
437{
438 g_return_val_if_fail(WEBKIT_IS_HIT_TEST_RESULT(hitTestResult), 0);
439
440 return hitTestResult->priv->imageURI.data();
441}
442
443/**
444 * webkit_hit_test_result_get_media_uri:
445 * @hit_test_result: a #WebKitHitTestResult
446 *
447 * Gets the value of the #WebKitHitTestResult:media-uri property.
448 *
449 * Returns: the URI of the media element in the coordinates of the Hit Test,
450 * or %NULL if there isn't a media element in @hit_test_result context
451 */
452const gchar* webkit_hit_test_result_get_media_uri(WebKitHitTestResult* hitTestResult)
453{
454 g_return_val_if_fail(WEBKIT_IS_HIT_TEST_RESULT(hitTestResult), 0);
455
456 return hitTestResult->priv->mediaURI.data();
457}
458
459/**
460 * webkit_hit_test_result_context_is_scrollbar:
461 * @hit_test_result: a #WebKitHitTestResult
462 *
463 * Gets whether %WEBKIT_HIT_TEST_RESULT_CONTEXT_SCROLLBAR flag is present in
464 * #WebKitHitTestResult:context.
465 *
466 * Returns: %TRUE if there's a scrollbar element at the coordinates of the @hit_test_result,
467 * or %FALSE otherwise
468 */
469gboolean webkit_hit_test_result_context_is_scrollbar(WebKitHitTestResult* hitTestResult)
470{
471 g_return_val_if_fail(WEBKIT_IS_HIT_TEST_RESULT(hitTestResult), FALSE);
472
473 return hitTestResult->priv->context & WEBKIT_HIT_TEST_RESULT_CONTEXT_SCROLLBAR;
474}
475