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