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(const WebHitTestResult::Data& hitTestResult)
225{
226    unsigned context = WEBKIT_HIT_TEST_RESULT_CONTEXT_DOCUMENT;
227
228    if (!hitTestResult.absoluteLinkURL.isEmpty())
229        context |= WEBKIT_HIT_TEST_RESULT_CONTEXT_LINK;
230
231    if (!hitTestResult.absoluteImageURL.isEmpty())
232        context |= WEBKIT_HIT_TEST_RESULT_CONTEXT_IMAGE;
233
234    if (!hitTestResult.absoluteMediaURL.isEmpty())
235        context |= WEBKIT_HIT_TEST_RESULT_CONTEXT_MEDIA;
236
237    if (hitTestResult.isContentEditable)
238        context |= WEBKIT_HIT_TEST_RESULT_CONTEXT_EDITABLE;
239
240    if (hitTestResult.isScrollbar)
241        context |= WEBKIT_HIT_TEST_RESULT_CONTEXT_SCROLLBAR;
242
243    return WEBKIT_HIT_TEST_RESULT(g_object_new(WEBKIT_TYPE_HIT_TEST_RESULT,
244        "context", context,
245        "link-uri", context & WEBKIT_HIT_TEST_RESULT_CONTEXT_LINK ? hitTestResult.absoluteLinkURL.utf8().data() : nullptr,
246        "image-uri", context & WEBKIT_HIT_TEST_RESULT_CONTEXT_IMAGE ? hitTestResult.absoluteImageURL.utf8().data() : nullptr,
247        "media-uri", context & WEBKIT_HIT_TEST_RESULT_CONTEXT_MEDIA ? hitTestResult.absoluteMediaURL.utf8().data() : nullptr,
248        "link-title", !hitTestResult.linkTitle.isEmpty() ? hitTestResult.linkTitle.utf8().data() : nullptr,
249        "link-label", !hitTestResult.linkLabel.isEmpty() ? hitTestResult.linkLabel.utf8().data() : nullptr,
250        nullptr));
251}
252
253static bool stringIsEqualToCString(const String& string, const CString& cString)
254{
255    return ((string.isEmpty() && cString.isNull()) || (string.utf8() == cString));
256}
257
258bool webkitHitTestResultCompare(WebKitHitTestResult* hitTestResult, const WebHitTestResult::Data& webHitTestResult)
259{
260    WebKitHitTestResultPrivate* priv = hitTestResult->priv;
261    return webHitTestResult.isContentEditable == webkit_hit_test_result_context_is_editable(hitTestResult)
262        && webHitTestResult.isScrollbar == webkit_hit_test_result_context_is_scrollbar(hitTestResult)
263        && stringIsEqualToCString(webHitTestResult.absoluteLinkURL, priv->linkURI)
264        && stringIsEqualToCString(webHitTestResult.linkTitle, priv->linkTitle)
265        && stringIsEqualToCString(webHitTestResult.linkLabel, priv->linkLabel)
266        && stringIsEqualToCString(webHitTestResult.absoluteImageURL, priv->imageURI)
267        && stringIsEqualToCString(webHitTestResult.absoluteMediaURL, priv->mediaURI);
268}
269
270/**
271 * webkit_hit_test_result_get_context:
272 * @hit_test_result: a #WebKitHitTestResult
273 *
274 * Gets the value of the #WebKitHitTestResult:context property.
275 *
276 * Returns: a bitmask of #WebKitHitTestResultContext flags
277 */
278guint webkit_hit_test_result_get_context(WebKitHitTestResult* hitTestResult)
279{
280    g_return_val_if_fail(WEBKIT_IS_HIT_TEST_RESULT(hitTestResult), 0);
281
282    return hitTestResult->priv->context;
283}
284
285/**
286 * webkit_hit_test_result_context_is_link:
287 * @hit_test_result: a #WebKitHitTestResult
288 *
289 * Gets whether %WEBKIT_HIT_TEST_RESULT_CONTEXT_LINK flag is present in
290 * #WebKitHitTestResult:context.
291 *
292 * Returns: %TRUE if there's a link element in the coordinates of the Hit Test,
293 *    or %FALSE otherwise
294 */
295gboolean webkit_hit_test_result_context_is_link(WebKitHitTestResult* hitTestResult)
296{
297    g_return_val_if_fail(WEBKIT_IS_HIT_TEST_RESULT(hitTestResult), FALSE);
298
299    return hitTestResult->priv->context & WEBKIT_HIT_TEST_RESULT_CONTEXT_LINK;
300}
301
302/**
303 * webkit_hit_test_result_context_is_image:
304 * @hit_test_result: a #WebKitHitTestResult
305 *
306 * Gets whether %WEBKIT_HIT_TEST_RESULT_CONTEXT_IMAGE flag is present in
307 * #WebKitHitTestResult:context.
308 *
309 * Returns: %TRUE if there's an image element in the coordinates of the Hit Test,
310 *    or %FALSE otherwise
311 */
312gboolean webkit_hit_test_result_context_is_image(WebKitHitTestResult* hitTestResult)
313{
314    g_return_val_if_fail(WEBKIT_IS_HIT_TEST_RESULT(hitTestResult), FALSE);
315
316    return hitTestResult->priv->context & WEBKIT_HIT_TEST_RESULT_CONTEXT_IMAGE;
317}
318
319/**
320 * webkit_hit_test_result_context_is_media:
321 * @hit_test_result: a #WebKitHitTestResult
322 *
323 * Gets whether %WEBKIT_HIT_TEST_RESULT_CONTEXT_MEDIA flag is present in
324 * #WebKitHitTestResult:context.
325 *
326 * Returns: %TRUE if there's a media element in the coordinates of the Hit Test,
327 *    or %FALSE otherwise
328 */
329gboolean webkit_hit_test_result_context_is_media(WebKitHitTestResult* hitTestResult)
330{
331    g_return_val_if_fail(WEBKIT_IS_HIT_TEST_RESULT(hitTestResult), FALSE);
332
333    return hitTestResult->priv->context & WEBKIT_HIT_TEST_RESULT_CONTEXT_MEDIA;
334}
335
336/**
337 * webkit_hit_test_result_context_is_editable:
338 * @hit_test_result: a #WebKitHitTestResult
339 *
340 * Gets whether %WEBKIT_HIT_TEST_RESULT_CONTEXT_EDITABLE flag is present in
341 * #WebKitHitTestResult:context.
342 *
343 * Returns: %TRUE if there's an editable element at the coordinates of the @hit_test_result,
344 *    or %FALSE otherwise
345 */
346gboolean webkit_hit_test_result_context_is_editable(WebKitHitTestResult* hitTestResult)
347{
348    g_return_val_if_fail(WEBKIT_IS_HIT_TEST_RESULT(hitTestResult), FALSE);
349
350    return hitTestResult->priv->context & WEBKIT_HIT_TEST_RESULT_CONTEXT_EDITABLE;
351}
352
353/**
354 * webkit_hit_test_result_get_link_uri:
355 * @hit_test_result: a #WebKitHitTestResult
356 *
357 * Gets the value of the #WebKitHitTestResult:link-uri property.
358 *
359 * Returns: the URI of the link element in the coordinates of the Hit Test,
360 *    or %NULL if there ins't a link element in @hit_test_result context
361 */
362const gchar* webkit_hit_test_result_get_link_uri(WebKitHitTestResult* hitTestResult)
363{
364    g_return_val_if_fail(WEBKIT_IS_HIT_TEST_RESULT(hitTestResult), 0);
365
366    return hitTestResult->priv->linkURI.data();
367}
368
369/**
370 * webkit_hit_test_result_get_link_title:
371 * @hit_test_result: a #WebKitHitTestResult
372 *
373 * Gets the value of the #WebKitHitTestResult:link-title property.
374 *
375 * Returns: the title of the link element in the coordinates of the Hit Test,
376 *    or %NULL if there ins't a link element in @hit_test_result context or the
377 *    link element doesn't have a title
378 */
379const gchar* webkit_hit_test_result_get_link_title(WebKitHitTestResult* hitTestResult)
380{
381    g_return_val_if_fail(WEBKIT_IS_HIT_TEST_RESULT(hitTestResult), 0);
382
383    return hitTestResult->priv->linkTitle.data();
384}
385
386/**
387 * webkit_hit_test_result_get_link_label:
388 * @hit_test_result: a #WebKitHitTestResult
389 *
390 * Gets the value of the #WebKitHitTestResult:link-label property.
391 *
392 * Returns: the label of the link element in the coordinates of the Hit Test,
393 *    or %NULL if there ins't a link element in @hit_test_result context or the
394 *    link element doesn't have a label
395 */
396const gchar* webkit_hit_test_result_get_link_label(WebKitHitTestResult* hitTestResult)
397{
398    g_return_val_if_fail(WEBKIT_IS_HIT_TEST_RESULT(hitTestResult), 0);
399
400    return hitTestResult->priv->linkLabel.data();
401}
402
403/**
404 * webkit_hit_test_result_get_image_uri:
405 * @hit_test_result: a #WebKitHitTestResult
406 *
407 * Gets the value of the #WebKitHitTestResult:image-uri property.
408 *
409 * Returns: the URI of the image element in the coordinates of the Hit Test,
410 *    or %NULL if there ins't an image element in @hit_test_result context
411 */
412const gchar* webkit_hit_test_result_get_image_uri(WebKitHitTestResult* hitTestResult)
413{
414    g_return_val_if_fail(WEBKIT_IS_HIT_TEST_RESULT(hitTestResult), 0);
415
416    return hitTestResult->priv->imageURI.data();
417}
418
419/**
420 * webkit_hit_test_result_get_media_uri:
421 * @hit_test_result: a #WebKitHitTestResult
422 *
423 * Gets the value of the #WebKitHitTestResult:media-uri property.
424 *
425 * Returns: the URI of the media element in the coordinates of the Hit Test,
426 *    or %NULL if there ins't a media element in @hit_test_result context
427 */
428const gchar* webkit_hit_test_result_get_media_uri(WebKitHitTestResult* hitTestResult)
429{
430    g_return_val_if_fail(WEBKIT_IS_HIT_TEST_RESULT(hitTestResult), 0);
431
432    return hitTestResult->priv->mediaURI.data();
433}
434
435/**
436 * webkit_hit_test_result_context_is_scrollbar:
437 * @hit_test_result: a #WebKitHitTestResult
438 *
439 * Gets whether %WEBKIT_HIT_TEST_RESULT_CONTEXT_SCROLLBAR flag is present in
440 * #WebKitHitTestResult:context.
441 *
442 * Returns: %TRUE if there's a scrollbar element at the coordinates of the @hit_test_result,
443 *    or %FALSE otherwise
444 */
445gboolean webkit_hit_test_result_context_is_scrollbar(WebKitHitTestResult* hitTestResult)
446{
447    g_return_val_if_fail(WEBKIT_IS_HIT_TEST_RESULT(hitTestResult), FALSE);
448
449    return hitTestResult->priv->context & WEBKIT_HIT_TEST_RESULT_CONTEXT_SCROLLBAR;
450}
451