Bug 794693 - Crash when trying to view email message source
[evolution.git] / src / e-util / e-web-view.c
blob679a91f5367b6d444ffde46c4e7c4708c93f4acd
1 /*
2 * e-web-view.c
4 * This program is free software; you can redistribute it and/or modify it
5 * under the terms of the GNU Lesser General Public License as published by
6 * the Free Software Foundation.
8 * This program is distributed in the hope that it will be useful, but
9 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
10 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
11 * for more details.
13 * You should have received a copy of the GNU Lesser General Public License
14 * along with this program; if not, see <http://www.gnu.org/licenses/>.
18 #include "evolution-config.h"
20 #include <glib/gi18n-lib.h>
22 #include <math.h>
23 #include <stdlib.h>
24 #include <string.h>
26 #include <pango/pango.h>
28 #include <camel/camel.h>
29 #include <libebackend/libebackend.h>
31 #include <libsoup/soup.h>
33 #include "e-alert-dialog.h"
34 #include "e-alert-sink.h"
35 #include "e-file-request.h"
36 #include "e-misc-utils.h"
37 #include "e-plugin-ui.h"
38 #include "e-popup-action.h"
39 #include "e-selectable.h"
40 #include "e-stock-request.h"
42 #include "web-extensions/e-web-extension-names.h"
44 #include "e-web-view.h"
46 #define E_WEB_VIEW_GET_PRIVATE(obj) \
47 (G_TYPE_INSTANCE_GET_PRIVATE \
48 ((obj), E_TYPE_WEB_VIEW, EWebViewPrivate))
50 typedef struct _AsyncContext AsyncContext;
52 typedef struct _ElementClickedData {
53 EWebViewElementClickedFunc callback;
54 gpointer user_data;
55 } ElementClickedData;
57 struct _EWebViewPrivate {
58 GtkUIManager *ui_manager;
59 gchar *selected_uri;
60 gchar *cursor_image_src;
62 GQueue highlights;
64 GtkAction *open_proxy;
65 GtkAction *print_proxy;
66 GtkAction *save_as_proxy;
68 /* Lockdown Options */
69 gboolean disable_printing;
70 gboolean disable_save_to_disk;
72 gboolean caret_mode;
74 GSettings *font_settings;
75 gulong font_name_changed_handler_id;
76 gulong monospace_font_name_changed_handler_id;
78 GSettings *aliasing_settings;
79 gulong antialiasing_changed_handler_id;
81 GHashTable *old_settings;
83 GDBusProxy *web_extension;
84 guint web_extension_watch_name_id;
86 WebKitFindController *find_controller;
87 gulong found_text_handler_id;
88 gulong failed_to_find_text_handler_id;
90 gboolean has_hover_link;
92 GSList *content_requests; /* EContentRequest * */
94 GHashTable *element_clicked_cbs; /* gchar *element_class ~> GPtrArray {ElementClickedData} */
95 guint web_extension_element_clicked_signal_id;
97 guint32 clipboard_flags;
98 guint web_extension_clipboard_flags_changed_signal_id;
100 gboolean need_input;
101 guint web_extension_need_input_changed_signal_id;
103 GCancellable *load_cancellable;
106 struct _AsyncContext {
107 EActivity *activity;
108 GFile *destination;
109 GInputStream *input_stream;
110 EContentRequest *content_request;
111 gchar *uri;
114 enum {
115 PROP_0,
116 PROP_CARET_MODE,
117 PROP_CLIPBOARD_FLAGS,
118 PROP_COPY_TARGET_LIST,
119 PROP_CURSOR_IMAGE_SRC,
120 PROP_DISABLE_PRINTING,
121 PROP_DISABLE_SAVE_TO_DISK,
122 PROP_NEED_INPUT,
123 PROP_OPEN_PROXY,
124 PROP_PASTE_TARGET_LIST,
125 PROP_PRINT_PROXY,
126 PROP_SAVE_AS_PROXY,
127 PROP_SELECTED_URI
130 enum {
131 NEW_ACTIVITY,
132 POPUP_EVENT,
133 STATUS_MESSAGE,
134 STOP_LOADING,
135 UPDATE_ACTIONS,
136 PROCESS_MAILTO,
137 URI_REQUESTED,
138 LAST_SIGNAL
141 static guint signals[LAST_SIGNAL];
143 static const gchar *ui =
144 "<ui>"
145 " <popup name='context'>"
146 " <menuitem action='copy-clipboard'/>"
147 " <separator/>"
148 " <placeholder name='custom-actions-1'>"
149 " <menuitem action='open'/>"
150 " <menuitem action='save-as'/>"
151 " <menuitem action='http-open'/>"
152 " <menuitem action='send-message'/>"
153 " <menuitem action='print'/>"
154 " </placeholder>"
155 " <placeholder name='custom-actions-2'>"
156 " <menuitem action='uri-copy'/>"
157 " <menuitem action='mailto-copy'/>"
158 " <menuitem action='mailto-copy-raw'/>"
159 " <menuitem action='image-copy'/>"
160 " <menuitem action='image-save'/>"
161 " </placeholder>"
162 " <placeholder name='custom-actions-3'/>"
163 " <separator/>"
164 " <menuitem action='select-all'/>"
165 " <placeholder name='inspect-menu' />"
166 " </popup>"
167 "</ui>";
169 /* Forward Declarations */
170 static void e_web_view_alert_sink_init (EAlertSinkInterface *iface);
171 static void e_web_view_selectable_init (ESelectableInterface *iface);
173 G_DEFINE_TYPE_WITH_CODE (
174 EWebView,
175 e_web_view,
176 WEBKIT_TYPE_WEB_VIEW,
177 G_IMPLEMENT_INTERFACE (
178 E_TYPE_EXTENSIBLE, NULL)
179 G_IMPLEMENT_INTERFACE (
180 E_TYPE_ALERT_SINK,
181 e_web_view_alert_sink_init)
182 G_IMPLEMENT_INTERFACE (
183 E_TYPE_SELECTABLE,
184 e_web_view_selectable_init))
186 static void
187 async_context_free (gpointer ptr)
189 AsyncContext *async_context = ptr;
191 if (!async_context)
192 return;
194 g_clear_object (&async_context->activity);
195 g_clear_object (&async_context->destination);
196 g_clear_object (&async_context->input_stream);
197 g_clear_object (&async_context->content_request);
198 g_free (async_context->uri);
200 g_slice_free (AsyncContext, async_context);
203 static void
204 action_copy_clipboard_cb (GtkAction *action,
205 EWebView *web_view)
207 e_web_view_copy_clipboard (web_view);
210 static void
211 action_http_open_cb (GtkAction *action,
212 EWebView *web_view)
214 const gchar *uri;
215 gpointer parent;
217 parent = gtk_widget_get_toplevel (GTK_WIDGET (web_view));
218 parent = gtk_widget_is_toplevel (parent) ? parent : NULL;
220 uri = e_web_view_get_selected_uri (web_view);
221 g_return_if_fail (uri != NULL);
223 e_show_uri (parent, uri);
226 static void
227 webview_mailto_copy (EWebView *web_view,
228 gboolean only_email_address)
230 CamelURL *curl;
231 CamelInternetAddress *inet_addr;
232 GtkClipboard *clipboard;
233 const gchar *uri, *name = NULL, *email = NULL;
234 gchar *text;
236 uri = e_web_view_get_selected_uri (web_view);
237 g_return_if_fail (uri != NULL);
239 /* This should work because we checked it in update_actions(). */
240 curl = camel_url_new (uri, NULL);
241 g_return_if_fail (curl != NULL);
243 inet_addr = camel_internet_address_new ();
244 camel_address_decode (CAMEL_ADDRESS (inet_addr), curl->path);
245 if (only_email_address &&
246 camel_internet_address_get (inet_addr, 0, &name, &email) &&
247 email && *email) {
248 text = g_strdup (email);
249 } else {
250 text = camel_address_format (CAMEL_ADDRESS (inet_addr));
251 if (text == NULL || *text == '\0')
252 text = g_strdup (uri + strlen ("mailto:"));
255 g_object_unref (inet_addr);
256 camel_url_free (curl);
258 clipboard = gtk_clipboard_get (GDK_SELECTION_PRIMARY);
259 gtk_clipboard_set_text (clipboard, text, -1);
260 gtk_clipboard_store (clipboard);
262 clipboard = gtk_clipboard_get (GDK_SELECTION_CLIPBOARD);
263 gtk_clipboard_set_text (clipboard, text, -1);
264 gtk_clipboard_store (clipboard);
266 g_free (text);
269 static void
270 action_mailto_copy_cb (GtkAction *action,
271 EWebView *web_view)
273 webview_mailto_copy (web_view, FALSE);
276 static void
277 action_mailto_copy_raw_cb (GtkAction *action,
278 EWebView *web_view)
280 webview_mailto_copy (web_view, TRUE);
283 static void
284 action_select_all_cb (GtkAction *action,
285 EWebView *web_view)
287 e_web_view_select_all (web_view);
290 static void
291 action_send_message_cb (GtkAction *action,
292 EWebView *web_view)
294 const gchar *uri;
295 gpointer parent;
296 gboolean handled;
298 parent = gtk_widget_get_toplevel (GTK_WIDGET (web_view));
299 parent = gtk_widget_is_toplevel (parent) ? parent : NULL;
301 uri = e_web_view_get_selected_uri (web_view);
302 g_return_if_fail (uri != NULL);
304 handled = FALSE;
305 g_signal_emit (web_view, signals[PROCESS_MAILTO], 0, uri, &handled);
307 if (!handled)
308 e_show_uri (parent, uri);
311 static void
312 action_uri_copy_cb (GtkAction *action,
313 EWebView *web_view)
315 GtkClipboard *clipboard;
316 const gchar *uri;
318 uri = e_web_view_get_selected_uri (web_view);
319 g_return_if_fail (uri != NULL);
321 clipboard = gtk_clipboard_get (GDK_SELECTION_PRIMARY);
322 gtk_clipboard_set_text (clipboard, uri, -1);
323 gtk_clipboard_store (clipboard);
325 clipboard = gtk_clipboard_get (GDK_SELECTION_CLIPBOARD);
326 gtk_clipboard_set_text (clipboard, uri, -1);
327 gtk_clipboard_store (clipboard);
330 static void
331 action_image_copy_cb (GtkAction *action,
332 EWebView *web_view)
334 e_web_view_cursor_image_copy (web_view);
337 static void
338 action_image_save_cb (GtkAction *action,
339 EWebView *web_view)
341 e_web_view_cursor_image_save (web_view);
344 static GtkActionEntry uri_entries[] = {
346 { "uri-copy",
347 "edit-copy",
348 N_("_Copy Link Location"),
349 "<Control>c",
350 N_("Copy the link to the clipboard"),
351 G_CALLBACK (action_uri_copy_cb) }
354 static GtkActionEntry http_entries[] = {
356 { "http-open",
357 "emblem-web",
358 N_("_Open Link in Browser"),
359 NULL,
360 N_("Open the link in a web browser"),
361 G_CALLBACK (action_http_open_cb) }
364 static GtkActionEntry mailto_entries[] = {
366 { "mailto-copy",
367 "edit-copy",
368 N_("_Copy Email Address"),
369 "<Control>c",
370 N_("Copy the email address to the clipboard"),
371 G_CALLBACK (action_mailto_copy_cb) },
373 { "mailto-copy-raw",
374 "edit-copy",
375 N_("Copy _Raw Email Address"),
376 NULL,
377 N_("Copy the raw email address to the clipboard"),
378 G_CALLBACK (action_mailto_copy_raw_cb) },
380 { "send-message",
381 "mail-message-new",
382 N_("_Send New Message To..."),
383 NULL,
384 N_("Send a mail message to this address"),
385 G_CALLBACK (action_send_message_cb) }
388 static GtkActionEntry image_entries[] = {
390 { "image-copy",
391 "edit-copy",
392 N_("_Copy Image"),
393 "<Control>c",
394 N_("Copy the image to the clipboard"),
395 G_CALLBACK (action_image_copy_cb) },
397 { "image-save",
398 "document-save",
399 N_("Save _Image..."),
400 "<Control>s",
401 N_("Save the image to a file"),
402 G_CALLBACK (action_image_save_cb) }
405 static GtkActionEntry selection_entries[] = {
407 { "copy-clipboard",
408 "edit-copy",
409 N_("_Copy"),
410 "<Control>c",
411 N_("Copy the selection"),
412 G_CALLBACK (action_copy_clipboard_cb) },
415 static GtkActionEntry standard_entries[] = {
417 { "select-all",
418 "edit-select-all",
419 N_("Select _All"),
420 NULL,
421 N_("Select all text and images"),
422 G_CALLBACK (action_select_all_cb) }
425 static void
426 web_view_menu_item_select_cb (EWebView *web_view,
427 GtkWidget *widget)
429 GtkAction *action;
430 GtkActivatable *activatable;
431 const gchar *tooltip;
433 activatable = GTK_ACTIVATABLE (widget);
434 action = gtk_activatable_get_related_action (activatable);
435 tooltip = gtk_action_get_tooltip (action);
437 if (tooltip == NULL)
438 return;
440 e_web_view_status_message (web_view, tooltip);
443 static void
444 webkit_find_controller_found_text_cb (WebKitFindController *find_controller,
445 guint match_count,
446 EWebView *web_view)
450 static void
451 webkit_find_controller_failed_to_found_text_cb (WebKitFindController *find_controller,
452 EWebView *web_view)
456 static void
457 web_view_set_find_controller (EWebView *web_view)
459 WebKitFindController *find_controller;
461 find_controller =
462 webkit_web_view_get_find_controller (WEBKIT_WEB_VIEW (web_view));
464 web_view->priv->found_text_handler_id = g_signal_connect (
465 find_controller, "found-text",
466 G_CALLBACK (webkit_find_controller_found_text_cb), web_view);
468 web_view->priv->failed_to_find_text_handler_id = g_signal_connect (
469 find_controller, "failed-to-find-text",
470 G_CALLBACK (webkit_find_controller_failed_to_found_text_cb), web_view);
472 web_view->priv->find_controller = find_controller;
475 static void
476 web_view_update_document_highlights (EWebView *web_view)
478 GList *head, *link;
480 head = g_queue_peek_head_link (&web_view->priv->highlights);
482 for (link = head; link != NULL; link = g_list_next (link)) {
483 webkit_find_controller_search (
484 web_view->priv->find_controller,
485 link->data,
486 WEBKIT_FIND_OPTIONS_NONE,
487 G_MAXUINT);
491 static void
492 web_view_menu_item_deselect_cb (EWebView *web_view)
494 e_web_view_status_message (web_view, NULL);
497 static void
498 web_view_connect_proxy_cb (EWebView *web_view,
499 GtkAction *action,
500 GtkWidget *proxy)
502 if (!GTK_IS_MENU_ITEM (proxy))
503 return;
505 g_signal_connect_swapped (
506 proxy, "select",
507 G_CALLBACK (web_view_menu_item_select_cb), web_view);
509 g_signal_connect_swapped (
510 proxy, "deselect",
511 G_CALLBACK (web_view_menu_item_deselect_cb), web_view);
514 static gboolean
515 web_view_context_menu_cb (WebKitWebView *webkit_web_view,
516 WebKitContextMenu *context_menu,
517 GdkEvent *event,
518 WebKitHitTestResult *hit_test_result,
519 gpointer user_data)
521 WebKitHitTestResultContext context;
522 EWebView *web_view;
523 gboolean event_handled = FALSE;
524 gchar *link_uri = NULL;
526 web_view = E_WEB_VIEW (webkit_web_view);
528 g_free (web_view->priv->cursor_image_src);
529 web_view->priv->cursor_image_src = NULL;
531 if (hit_test_result == NULL)
532 return FALSE;
534 context = webkit_hit_test_result_get_context (hit_test_result);
536 if (context & WEBKIT_HIT_TEST_RESULT_CONTEXT_IMAGE) {
537 gchar *image_uri = NULL;
539 g_object_get (hit_test_result, "image-uri", &image_uri, NULL);
541 if (image_uri != NULL) {
542 g_free (web_view->priv->cursor_image_src);
543 web_view->priv->cursor_image_src = image_uri;
547 if (context & WEBKIT_HIT_TEST_RESULT_CONTEXT_LINK)
548 g_object_get (hit_test_result, "link-uri", &link_uri, NULL);
550 g_signal_emit (
551 web_view,
552 signals[POPUP_EVENT], 0,
553 link_uri, event, &event_handled);
555 g_free (link_uri);
557 return event_handled;
560 static void
561 web_view_mouse_target_changed_cb (EWebView *web_view,
562 WebKitHitTestResult *hit_test_result,
563 guint modifiers,
564 gpointer user_data)
566 EWebViewClass *class;
567 const gchar *title, *uri;
569 title = webkit_hit_test_result_get_link_title (hit_test_result);
570 uri = webkit_hit_test_result_get_link_uri (hit_test_result);
572 web_view->priv->has_hover_link = uri && *uri;
574 /* XXX WebKitWebView does not provide a class method for
575 * this signal, so we do so we can override the default
576 * behavior from subclasses for special URI types. */
578 class = E_WEB_VIEW_GET_CLASS (web_view);
579 g_return_if_fail (class->hovering_over_link != NULL);
581 class->hovering_over_link (web_view, title, uri);
584 static gboolean
585 web_view_decide_policy_cb (EWebView *web_view,
586 WebKitPolicyDecision *decision,
587 WebKitPolicyDecisionType type)
589 EWebViewClass *class;
590 WebKitNavigationPolicyDecision *navigation_decision;
591 WebKitNavigationAction *navigation_action;
592 WebKitNavigationType navigation_type;
593 WebKitURIRequest *request;
594 const gchar *uri, *view_uri;
596 if (type != WEBKIT_POLICY_DECISION_TYPE_NAVIGATION_ACTION &&
597 type != WEBKIT_POLICY_DECISION_TYPE_NEW_WINDOW_ACTION)
598 return FALSE;
600 navigation_decision = WEBKIT_NAVIGATION_POLICY_DECISION (decision);
601 navigation_action = webkit_navigation_policy_decision_get_navigation_action (navigation_decision);
602 navigation_type = webkit_navigation_action_get_navigation_type (navigation_action);
604 if (navigation_type != WEBKIT_NAVIGATION_TYPE_LINK_CLICKED)
605 return FALSE;
607 request = webkit_navigation_action_get_request (navigation_action);
608 uri = webkit_uri_request_get_uri (request);
609 view_uri = webkit_web_view_get_uri (WEBKIT_WEB_VIEW (web_view));
611 /* Allow navigation through fragments in page */
612 if (uri && *uri && view_uri && *view_uri) {
613 SoupURI *uri_link, *uri_view;
615 uri_link = soup_uri_new (uri);
616 uri_view = soup_uri_new (view_uri);
617 if (uri_link && uri_view) {
618 const gchar *tmp1, *tmp2;
620 tmp1 = soup_uri_get_scheme (uri_link);
621 tmp2 = soup_uri_get_scheme (uri_view);
623 /* The scheme on both URIs should be the same */
624 if (tmp1 && tmp2 && g_ascii_strcasecmp (tmp1, tmp2) != 0)
625 goto free_uris;
627 tmp1 = soup_uri_get_host (uri_link);
628 tmp2 = soup_uri_get_host (uri_view);
630 /* The host on both URIs should be the same */
631 if (tmp1 && tmp2 && g_ascii_strcasecmp (tmp1, tmp2) != 0)
632 goto free_uris;
634 /* URI from link should have fragment set - could be empty */
635 if (soup_uri_get_fragment (uri_link)) {
636 soup_uri_free (uri_link);
637 soup_uri_free (uri_view);
638 webkit_policy_decision_use (decision);
639 return TRUE;
643 free_uris:
644 if (uri_link)
645 soup_uri_free (uri_link);
646 if (uri_view)
647 soup_uri_free (uri_view);
650 /* XXX WebKitWebView does not provide a class method for
651 * this signal, so we do so we can override the default
652 * behavior from subclasses for special URI types. */
654 class = E_WEB_VIEW_GET_CLASS (web_view);
655 g_return_val_if_fail (class->link_clicked != NULL, FALSE);
657 webkit_policy_decision_ignore (decision);
659 class->link_clicked (web_view, uri);
661 return TRUE;
664 static void
665 e_web_view_ensure_body_class (EWebView *web_view)
667 GDBusProxy *web_extension;
669 g_return_if_fail (E_IS_WEB_VIEW (web_view));
671 web_extension = e_web_view_get_web_extension_proxy (web_view);
672 if (!web_extension)
673 return;
675 e_util_invoke_g_dbus_proxy_call_with_error_check (
676 web_extension,
677 "EWebViewEnsureBodyClass",
678 g_variant_new (
679 "(ts)",
680 webkit_web_view_get_page_id (WEBKIT_WEB_VIEW (web_view)),
681 "-e-web-view-background-color -e-web-view-text-color"),
682 NULL);
685 static void
686 style_updated_cb (EWebView *web_view)
688 GdkRGBA color;
689 gchar *color_value;
690 gchar *style;
691 GtkStyleContext *style_context;
693 style_context = gtk_widget_get_style_context (GTK_WIDGET (web_view));
695 if (gtk_style_context_lookup_color (style_context, "theme_base_color", &color))
696 color_value = g_strdup_printf ("#%06x", e_rgba_to_value (&color));
697 else {
698 color_value = g_strdup (E_UTILS_DEFAULT_THEME_BASE_COLOR);
699 if (!gdk_rgba_parse (&color, color_value)) {
700 color.red = 1.0;
701 color.green = 1.0;
702 color.blue = 1.0;
703 color.alpha = 1.0;
707 style = g_strconcat ("background-color: ", color_value, ";", NULL);
709 webkit_web_view_set_background_color (WEBKIT_WEB_VIEW (web_view), &color);
711 e_web_view_add_css_rule_into_style_sheet (
712 web_view,
713 "-e-web-view-style-sheet",
714 ".-e-web-view-background-color",
715 style);
717 g_free (color_value);
718 g_free (style);
720 if (gtk_style_context_lookup_color (style_context, "theme_fg_color", &color))
721 color_value = g_strdup_printf ("#%06x", e_rgba_to_value (&color));
722 else
723 color_value = g_strdup (E_UTILS_DEFAULT_THEME_FG_COLOR);
725 style = g_strconcat ("color: ", color_value, ";", NULL);
727 e_web_view_add_css_rule_into_style_sheet (
728 web_view,
729 "-e-web-view-style-sheet",
730 ".-e-web-view-text-color",
731 style);
733 g_free (color_value);
734 g_free (style);
737 static void
738 web_view_load_changed_cb (WebKitWebView *webkit_web_view,
739 WebKitLoadEvent load_event,
740 gpointer user_data)
742 EWebView *web_view;
744 web_view = E_WEB_VIEW (webkit_web_view);
746 if (load_event == WEBKIT_LOAD_STARTED)
747 g_hash_table_remove_all (web_view->priv->element_clicked_cbs);
749 if (load_event != WEBKIT_LOAD_FINISHED)
750 return;
752 e_web_view_ensure_body_class (web_view);
753 style_updated_cb (web_view);
755 web_view_update_document_highlights (web_view);
758 static GObjectConstructParam*
759 find_property (guint n_properties,
760 GObjectConstructParam* properties,
761 GParamSpec* param_spec)
763 while (n_properties--) {
764 if (properties->pspec == param_spec)
765 return properties;
766 properties++;
769 return NULL;
772 static GObject*
773 web_view_constructor (GType type,
774 guint n_construct_properties,
775 GObjectConstructParam *construct_properties)
777 GObjectClass* object_class;
778 GParamSpec* param_spec;
779 GObjectConstructParam *param = NULL;
781 object_class = G_OBJECT_CLASS (g_type_class_ref(type));
782 g_return_val_if_fail (object_class != NULL, NULL);
784 if (construct_properties && n_construct_properties != 0) {
785 param_spec = g_object_class_find_property (object_class, "settings");
786 if ((param = find_property (n_construct_properties, construct_properties, param_spec)))
787 g_value_take_object (param->value, e_web_view_get_default_webkit_settings ());
788 param_spec = g_object_class_find_property(object_class, "user-content-manager");
789 if ((param = find_property (n_construct_properties, construct_properties, param_spec)))
790 g_value_take_object (param->value, webkit_user_content_manager_new ());
793 g_type_class_unref (object_class);
795 return G_OBJECT_CLASS (e_web_view_parent_class)->constructor(type, n_construct_properties, construct_properties);
798 static void
799 web_view_set_property (GObject *object,
800 guint property_id,
801 const GValue *value,
802 GParamSpec *pspec)
804 switch (property_id) {
805 case PROP_CARET_MODE:
806 e_web_view_set_caret_mode (
807 E_WEB_VIEW (object),
808 g_value_get_boolean (value));
809 return;
811 case PROP_CLIPBOARD_FLAGS:
812 e_web_view_set_clipboard_flags (
813 E_WEB_VIEW (object),
814 g_value_get_uint (value));
815 return;
817 case PROP_COPY_TARGET_LIST:
818 /* This is a fake property. */
819 g_warning ("%s: EWebView::copy-target-list not used", G_STRFUNC);
820 return;
822 case PROP_CURSOR_IMAGE_SRC:
823 e_web_view_set_cursor_image_src (
824 E_WEB_VIEW (object),
825 g_value_get_string (value));
826 return;
828 case PROP_DISABLE_PRINTING:
829 e_web_view_set_disable_printing (
830 E_WEB_VIEW (object),
831 g_value_get_boolean (value));
832 return;
834 case PROP_DISABLE_SAVE_TO_DISK:
835 e_web_view_set_disable_save_to_disk (
836 E_WEB_VIEW (object),
837 g_value_get_boolean (value));
838 return;
840 case PROP_NEED_INPUT:
841 e_web_view_set_need_input (
842 E_WEB_VIEW (object),
843 g_value_get_boolean (value));
844 return;
846 case PROP_OPEN_PROXY:
847 e_web_view_set_open_proxy (
848 E_WEB_VIEW (object),
849 g_value_get_object (value));
850 return;
852 case PROP_PASTE_TARGET_LIST:
853 /* This is a fake property. */
854 g_warning ("%s: EWebView::paste-target-list not used", G_STRFUNC);
855 return;
857 case PROP_PRINT_PROXY:
858 e_web_view_set_print_proxy (
859 E_WEB_VIEW (object),
860 g_value_get_object (value));
861 return;
863 case PROP_SAVE_AS_PROXY:
864 e_web_view_set_save_as_proxy (
865 E_WEB_VIEW (object),
866 g_value_get_object (value));
867 return;
869 case PROP_SELECTED_URI:
870 e_web_view_set_selected_uri (
871 E_WEB_VIEW (object),
872 g_value_get_string (value));
873 return;
875 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
878 static void
879 web_view_get_property (GObject *object,
880 guint property_id,
881 GValue *value,
882 GParamSpec *pspec)
884 switch (property_id) {
885 case PROP_CARET_MODE:
886 g_value_set_boolean (
887 value, e_web_view_get_caret_mode (
888 E_WEB_VIEW (object)));
889 return;
891 case PROP_CLIPBOARD_FLAGS:
892 g_value_set_uint (
893 value, e_web_view_get_clipboard_flags (
894 E_WEB_VIEW (object)));
895 return;
897 case PROP_COPY_TARGET_LIST:
898 /* This is a fake property. */
899 g_value_set_boxed (value, NULL);
900 return;
902 case PROP_CURSOR_IMAGE_SRC:
903 g_value_set_string (
904 value, e_web_view_get_cursor_image_src (
905 E_WEB_VIEW (object)));
906 return;
908 case PROP_DISABLE_PRINTING:
909 g_value_set_boolean (
910 value, e_web_view_get_disable_printing (
911 E_WEB_VIEW (object)));
912 return;
914 case PROP_DISABLE_SAVE_TO_DISK:
915 g_value_set_boolean (
916 value, e_web_view_get_disable_save_to_disk (
917 E_WEB_VIEW (object)));
918 return;
920 case PROP_NEED_INPUT:
921 g_value_set_boolean (
922 value, e_web_view_get_need_input (
923 E_WEB_VIEW (object)));
924 return;
926 case PROP_OPEN_PROXY:
927 g_value_set_object (
928 value, e_web_view_get_open_proxy (
929 E_WEB_VIEW (object)));
930 return;
932 case PROP_PASTE_TARGET_LIST:
933 /* This is a fake property. */
934 g_value_set_boxed (value, NULL);
935 return;
937 case PROP_PRINT_PROXY:
938 g_value_set_object (
939 value, e_web_view_get_print_proxy (
940 E_WEB_VIEW (object)));
941 return;
943 case PROP_SAVE_AS_PROXY:
944 g_value_set_object (
945 value, e_web_view_get_save_as_proxy (
946 E_WEB_VIEW (object)));
947 return;
949 case PROP_SELECTED_URI:
950 g_value_set_string (
951 value, e_web_view_get_selected_uri (
952 E_WEB_VIEW (object)));
953 return;
957 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
960 static void
961 web_view_dispose (GObject *object)
963 EWebViewPrivate *priv;
965 priv = E_WEB_VIEW_GET_PRIVATE (object);
967 if (priv->load_cancellable) {
968 g_cancellable_cancel (priv->load_cancellable);
969 g_clear_object (&priv->load_cancellable);
972 if (priv->font_name_changed_handler_id > 0) {
973 g_signal_handler_disconnect (
974 priv->font_settings,
975 priv->font_name_changed_handler_id);
976 priv->font_name_changed_handler_id = 0;
979 if (priv->monospace_font_name_changed_handler_id > 0) {
980 g_signal_handler_disconnect (
981 priv->font_settings,
982 priv->monospace_font_name_changed_handler_id);
983 priv->monospace_font_name_changed_handler_id = 0;
986 if (priv->antialiasing_changed_handler_id > 0) {
987 g_signal_handler_disconnect (
988 priv->aliasing_settings,
989 priv->antialiasing_changed_handler_id);
990 priv->antialiasing_changed_handler_id = 0;
993 if (priv->web_extension_watch_name_id > 0) {
994 g_bus_unwatch_name (priv->web_extension_watch_name_id);
995 priv->web_extension_watch_name_id = 0;
998 if (priv->found_text_handler_id > 0) {
999 g_signal_handler_disconnect (
1000 priv->find_controller,
1001 priv->found_text_handler_id);
1002 priv->found_text_handler_id = 0;
1005 if (priv->failed_to_find_text_handler_id > 0) {
1006 g_signal_handler_disconnect (
1007 priv->find_controller,
1008 priv->failed_to_find_text_handler_id);
1009 priv->failed_to_find_text_handler_id = 0;
1012 if (priv->web_extension && priv->web_extension_clipboard_flags_changed_signal_id) {
1013 g_dbus_connection_signal_unsubscribe (
1014 g_dbus_proxy_get_connection (priv->web_extension),
1015 priv->web_extension_clipboard_flags_changed_signal_id);
1016 priv->web_extension_clipboard_flags_changed_signal_id = 0;
1019 if (priv->web_extension && priv->web_extension_need_input_changed_signal_id) {
1020 g_dbus_connection_signal_unsubscribe (
1021 g_dbus_proxy_get_connection (priv->web_extension),
1022 priv->web_extension_need_input_changed_signal_id);
1023 priv->web_extension_need_input_changed_signal_id = 0;
1026 if (priv->web_extension && priv->web_extension_element_clicked_signal_id) {
1027 g_dbus_connection_signal_unsubscribe (
1028 g_dbus_proxy_get_connection (priv->web_extension),
1029 priv->web_extension_element_clicked_signal_id);
1030 priv->web_extension_element_clicked_signal_id = 0;
1033 g_hash_table_remove_all (priv->element_clicked_cbs);
1035 g_slist_free_full (priv->content_requests, g_object_unref);
1036 priv->content_requests = NULL;
1038 g_clear_object (&priv->ui_manager);
1039 g_clear_object (&priv->open_proxy);
1040 g_clear_object (&priv->print_proxy);
1041 g_clear_object (&priv->save_as_proxy);
1042 g_clear_object (&priv->aliasing_settings);
1043 g_clear_object (&priv->font_settings);
1044 g_clear_object (&priv->web_extension);
1046 /* Chain up to parent's dispose() method. */
1047 G_OBJECT_CLASS (e_web_view_parent_class)->dispose (object);
1050 static void
1051 web_view_finalize (GObject *object)
1053 EWebViewPrivate *priv;
1055 priv = E_WEB_VIEW_GET_PRIVATE (object);
1057 g_free (priv->selected_uri);
1058 g_free (priv->cursor_image_src);
1060 while (!g_queue_is_empty (&priv->highlights))
1061 g_free (g_queue_pop_head (&priv->highlights));
1063 if (priv->old_settings) {
1064 g_hash_table_destroy (priv->old_settings);
1065 priv->old_settings = NULL;
1068 g_hash_table_destroy (priv->element_clicked_cbs);
1070 /* Chain up to parent's finalize() method. */
1071 G_OBJECT_CLASS (e_web_view_parent_class)->finalize (object);
1075 static void
1076 web_view_uri_request_done_cb (GObject *source_object,
1077 GAsyncResult *result,
1078 gpointer user_data)
1080 WebKitURISchemeRequest *request = user_data;
1081 GInputStream *stream = NULL;
1082 gint64 stream_length = -1;
1083 gchar *mime_type = NULL;
1084 GError *error = NULL;
1086 g_return_if_fail (E_IS_CONTENT_REQUEST (source_object));
1087 g_return_if_fail (WEBKIT_IS_URI_SCHEME_REQUEST (request));
1089 if (!e_content_request_process_finish (E_CONTENT_REQUEST (source_object),
1090 result, &stream, &stream_length, &mime_type, &error)) {
1091 webkit_uri_scheme_request_finish_error (request, error);
1092 g_clear_error (&error);
1093 } else {
1094 webkit_uri_scheme_request_finish (request, stream, stream_length, mime_type);
1096 g_clear_object (&stream);
1097 g_free (mime_type);
1100 g_object_unref (request);
1103 static void
1104 web_view_process_uri_request_cb (WebKitURISchemeRequest *request,
1105 gpointer user_data)
1107 EWebView *web_view = NULL;
1108 EContentRequest *content_request = user_data;
1109 const gchar *uri;
1110 gchar *redirect_to_uri = NULL;
1111 GObject *requester;
1113 g_return_if_fail (WEBKIT_IS_URI_SCHEME_REQUEST (request));
1114 g_return_if_fail (E_IS_CONTENT_REQUEST (content_request));
1116 uri = webkit_uri_scheme_request_get_uri (request);
1117 requester = G_OBJECT (webkit_uri_scheme_request_get_web_view (request));
1119 if (!requester) {
1120 GError *error;
1122 error = g_error_new_literal (G_IO_ERROR, G_IO_ERROR_CANCELLED, "Cancelled");
1123 webkit_uri_scheme_request_finish_error (request, error);
1124 g_clear_error (&error);
1126 return;
1129 g_return_if_fail (e_content_request_can_process_uri (content_request, uri));
1131 if (E_IS_WEB_VIEW (requester)) {
1132 /* Expects an empty string to abandon the request,
1133 or NULL to keep the passed-in uri,
1134 or a new uri to load instead. */
1135 g_signal_emit (requester, signals[URI_REQUESTED], 0, uri, &redirect_to_uri);
1137 if (redirect_to_uri && *redirect_to_uri) {
1138 uri = redirect_to_uri;
1139 } else if (redirect_to_uri) {
1140 GError *error;
1142 g_free (redirect_to_uri);
1144 error = g_error_new_literal (G_IO_ERROR, G_IO_ERROR_CANCELLED, "Cancelled");
1146 webkit_uri_scheme_request_finish_error (request, error);
1147 g_clear_error (&error);
1149 return;
1152 web_view = E_WEB_VIEW (requester);
1155 e_content_request_process (content_request, uri, requester, web_view ? web_view->priv->load_cancellable : NULL,
1156 web_view_uri_request_done_cb, g_object_ref (request));
1158 g_free (redirect_to_uri);
1161 /* 'scheme' is like "file", not "file:" */
1162 void
1163 e_web_view_register_content_request_for_scheme (EWebView *web_view,
1164 const gchar *scheme,
1165 EContentRequest *content_request)
1167 WebKitWebContext *web_context;
1169 g_return_if_fail (E_IS_WEB_VIEW (web_view));
1170 g_return_if_fail (E_IS_CONTENT_REQUEST (content_request));
1171 g_return_if_fail (scheme != NULL);
1173 web_context = webkit_web_view_get_context (WEBKIT_WEB_VIEW (web_view));
1175 webkit_web_context_register_uri_scheme (web_context, scheme, web_view_process_uri_request_cb,
1176 g_object_ref (content_request), g_object_unref);
1178 if (!g_slist_find (web_view->priv->content_requests, content_request)) {
1179 web_view->priv->content_requests = g_slist_prepend (
1180 web_view->priv->content_requests,
1181 g_object_ref (content_request));
1185 static void
1186 web_view_initialize (WebKitWebView *web_view)
1188 WebKitWebContext *web_context;
1189 EContentRequest *content_request;
1190 const gchar *id = "org.gnome.settings-daemon.plugins.xsettings";
1191 GSettings *settings = NULL, *font_settings;
1192 GSettingsSchema *settings_schema;
1194 web_context = webkit_web_view_get_context (web_view);
1196 webkit_web_context_set_cache_model (web_context, WEBKIT_CACHE_MODEL_DOCUMENT_VIEWER);
1198 content_request = e_file_request_new ();
1199 e_web_view_register_content_request_for_scheme (E_WEB_VIEW (web_view), "evo-file", content_request);
1200 g_object_unref (content_request);
1202 content_request = e_stock_request_new ();
1203 e_web_view_register_content_request_for_scheme (E_WEB_VIEW (web_view), "gtk-stock", content_request);
1204 g_object_unref (content_request);
1206 /* Optional schema */
1207 settings_schema = g_settings_schema_source_lookup (
1208 g_settings_schema_source_get_default (), id, FALSE);
1210 if (settings_schema)
1211 settings = e_util_ref_settings (id);
1213 font_settings = e_util_ref_settings ("org.gnome.desktop.interface");
1214 e_web_view_update_fonts_settings (
1215 font_settings, settings, NULL, NULL, GTK_WIDGET (web_view));
1217 g_object_unref (font_settings);
1218 if (settings)
1219 g_object_unref (settings);
1223 static void
1224 web_view_constructed (GObject *object)
1226 WebKitSettings *web_settings;
1227 #ifndef G_OS_WIN32
1228 GSettings *settings;
1230 settings = e_util_ref_settings ("org.gnome.desktop.lockdown");
1232 g_settings_bind (
1233 settings, "disable-printing",
1234 object, "disable-printing",
1235 G_SETTINGS_BIND_GET);
1237 g_settings_bind (
1238 settings, "disable-save-to-disk",
1239 object, "disable-save-to-disk",
1240 G_SETTINGS_BIND_GET);
1242 g_object_unref (settings);
1243 #endif
1245 e_extensible_load_extensions (E_EXTENSIBLE (object));
1247 /* Chain up to parent's constructed() method. */
1248 G_OBJECT_CLASS (e_web_view_parent_class)->constructed (object);
1250 web_settings = webkit_web_view_get_settings (WEBKIT_WEB_VIEW (object));
1252 g_object_set (
1253 G_OBJECT (web_settings),
1254 "default-charset", "UTF-8",
1255 NULL);
1257 e_binding_bind_property (
1258 web_settings, "enable-caret-browsing",
1259 E_WEB_VIEW (object), "caret-mode",
1260 G_BINDING_BIDIRECTIONAL |
1261 G_BINDING_SYNC_CREATE);
1263 web_view_initialize (WEBKIT_WEB_VIEW (object));
1265 web_view_set_find_controller (E_WEB_VIEW (object));
1268 static void
1269 e_web_view_replace_load_cancellable (EWebView *web_view,
1270 gboolean create_new)
1272 g_return_if_fail (E_IS_WEB_VIEW (web_view));
1274 if (web_view->priv->load_cancellable) {
1275 g_cancellable_cancel (web_view->priv->load_cancellable);
1276 g_clear_object (&web_view->priv->load_cancellable);
1279 if (create_new)
1280 web_view->priv->load_cancellable = g_cancellable_new ();
1283 static gboolean
1284 web_view_scroll_event (GtkWidget *widget,
1285 GdkEventScroll *event)
1287 if (event->state & GDK_CONTROL_MASK) {
1288 GdkScrollDirection direction = event->direction;
1290 if (direction == GDK_SCROLL_SMOOTH) {
1291 static gdouble total_delta_y = 0.0;
1293 total_delta_y += event->delta_y;
1295 if (total_delta_y >= 1.0) {
1296 total_delta_y = 0.0;
1297 direction = GDK_SCROLL_DOWN;
1298 } else if (total_delta_y <= -1.0) {
1299 total_delta_y = 0.0;
1300 direction = GDK_SCROLL_UP;
1301 } else if (event->delta_y >= 1e-9 || event->delta_y <= -1e-9) {
1302 return TRUE;
1303 } else {
1304 return FALSE;
1308 switch (direction) {
1309 case GDK_SCROLL_UP:
1310 e_web_view_zoom_in (E_WEB_VIEW (widget));
1311 return TRUE;
1312 case GDK_SCROLL_DOWN:
1313 e_web_view_zoom_out (E_WEB_VIEW (widget));
1314 return TRUE;
1315 default:
1316 break;
1320 return GTK_WIDGET_CLASS (e_web_view_parent_class)->
1321 scroll_event (widget, event);
1324 static gboolean
1325 web_view_drag_motion (GtkWidget *widget,
1326 GdkDragContext *context,
1327 gint x,
1328 gint y,
1329 guint time_)
1331 /* Made this way to not pretend that this is a drop zone,
1332 when only FALSE had been returned, even it is supposed
1333 to be enough. Remove web_view_drag_leave() once fixed. */
1334 gdk_drag_status (context, 0, time_);
1336 return TRUE;
1339 static gboolean
1340 web_view_drag_drop (GtkWidget *widget,
1341 GdkDragContext *context,
1342 gint x,
1343 gint y,
1344 guint time_)
1346 /* Defined to avoid crash in WebKit */
1347 return FALSE;
1350 static void
1351 web_view_drag_leave (GtkWidget *widget,
1352 GdkDragContext *context,
1353 guint time_)
1355 /* Defined to avoid crash in WebKit, when the web_view_drag_motion()
1356 uses the other way around. */
1359 static void
1360 web_view_hovering_over_link (EWebView *web_view,
1361 const gchar *title,
1362 const gchar *uri)
1364 CamelInternetAddress *address;
1365 CamelURL *curl;
1366 const gchar *format = NULL;
1367 gchar *message = NULL;
1368 gchar *who;
1370 if (uri == NULL || *uri == '\0')
1371 goto exit;
1373 if (g_str_has_prefix (uri, "mailto:"))
1374 format = _("Click to mail %s");
1375 else if (g_str_has_prefix (uri, "callto:") ||
1376 g_str_has_prefix (uri, "h323:") ||
1377 g_str_has_prefix (uri, "sip:") ||
1378 g_str_has_prefix (uri, "tel:"))
1379 format = _("Click to call %s");
1380 else if (g_str_has_prefix (uri, "##"))
1381 message = g_strdup (_("Click to hide/unhide addresses"));
1382 else if (g_str_has_prefix (uri, "mail:")) {
1383 const gchar *fragment;
1384 SoupURI *soup_uri;
1386 soup_uri = soup_uri_new (uri);
1387 if (!soup_uri)
1388 goto exit;
1390 fragment = soup_uri_get_fragment (soup_uri);
1391 if (fragment && *fragment)
1392 message = g_strdup_printf (_("Go to the section %s of the message"), fragment);
1393 else
1394 message = g_strdup (_("Go to the beginning of the message"));
1396 soup_uri_free (soup_uri);
1397 } else
1398 message = g_strdup_printf (_("Click to open %s"), uri);
1400 if (format == NULL)
1401 goto exit;
1403 /* XXX Use something other than Camel here. Surely
1404 * there's other APIs around that can do this. */
1405 curl = camel_url_new (uri, NULL);
1406 address = camel_internet_address_new ();
1407 camel_address_decode (CAMEL_ADDRESS (address), curl->path);
1408 who = camel_address_format (CAMEL_ADDRESS (address));
1409 g_object_unref (address);
1410 camel_url_free (curl);
1412 if (who == NULL)
1413 who = g_strdup (strchr (uri, ':') + 1);
1415 message = g_strdup_printf (format, who);
1417 g_free (who);
1419 exit:
1420 e_web_view_status_message (web_view, message);
1422 g_free (message);
1425 static void
1426 web_view_link_clicked (EWebView *web_view,
1427 const gchar *uri)
1429 gpointer parent;
1431 if (uri && g_ascii_strncasecmp (uri, "mailto:", 7) == 0) {
1432 gboolean handled = FALSE;
1434 g_signal_emit (
1435 web_view, signals[PROCESS_MAILTO], 0, uri, &handled);
1437 if (handled)
1438 return;
1441 parent = gtk_widget_get_toplevel (GTK_WIDGET (web_view));
1442 parent = gtk_widget_is_toplevel (parent) ? parent : NULL;
1444 e_show_uri (parent, uri);
1447 static void
1448 web_view_load_string (EWebView *web_view,
1449 const gchar *string)
1451 if (string == NULL)
1452 string = "";
1454 webkit_web_view_load_html (
1455 WEBKIT_WEB_VIEW (web_view), string, "evo-file:///");
1458 static void
1459 web_view_load_uri (EWebView *web_view,
1460 const gchar *uri)
1462 if (uri == NULL)
1463 uri = "about:blank";
1465 webkit_web_view_load_uri (WEBKIT_WEB_VIEW (web_view), uri);
1468 static gchar *
1469 web_view_suggest_filename (EWebView *web_view,
1470 const gchar *uri)
1472 const gchar *cp;
1474 /* Try to derive a filename from the last path segment. */
1476 cp = strrchr (uri, '/');
1477 if (cp != NULL) {
1478 if (strchr (cp, '?') == NULL)
1479 cp++;
1480 else
1481 cp = NULL;
1484 return g_strdup (cp);
1487 static gboolean
1488 web_view_popup_event (EWebView *web_view,
1489 const gchar *uri,
1490 GdkEvent *event)
1492 e_web_view_set_selected_uri (web_view, uri);
1493 e_web_view_show_popup_menu (web_view, event);
1495 return TRUE;
1498 static void
1499 web_view_stop_loading (EWebView *web_view)
1501 e_web_view_replace_load_cancellable (web_view, FALSE);
1502 webkit_web_view_stop_loading (WEBKIT_WEB_VIEW (web_view));
1505 static void
1506 web_view_register_element_clicked_hfunc (gpointer key,
1507 gpointer value,
1508 gpointer user_data)
1510 const gchar *elem_class = key;
1511 EWebView *web_view = user_data;
1513 g_return_if_fail (elem_class && *elem_class);
1514 g_return_if_fail (E_IS_WEB_VIEW (web_view));
1516 if (!web_view->priv->web_extension)
1517 return;
1519 e_util_invoke_g_dbus_proxy_call_with_error_check (
1520 web_view->priv->web_extension,
1521 "RegisterElementClicked",
1522 g_variant_new (
1523 "(ts)",
1524 webkit_web_view_get_page_id (WEBKIT_WEB_VIEW (web_view)),
1525 elem_class),
1526 NULL);
1529 static void
1530 web_view_need_input_changed_signal_cb (GDBusConnection *connection,
1531 const gchar *sender_name,
1532 const gchar *object_path,
1533 const gchar *interface_name,
1534 const gchar *signal_name,
1535 GVariant *parameters,
1536 gpointer user_data)
1538 EWebView *web_view = user_data;
1539 guint64 page_id = 0;
1540 gboolean need_input = FALSE;
1542 if (g_strcmp0 (signal_name, "NeedInputChanged") != 0)
1543 return;
1545 g_return_if_fail (E_IS_WEB_VIEW (web_view));
1547 if (!parameters)
1548 return;
1550 g_variant_get (parameters, "(tb)", &page_id, &need_input);
1552 if (page_id == webkit_web_view_get_page_id (WEBKIT_WEB_VIEW (web_view)))
1553 e_web_view_set_need_input (web_view, need_input);
1556 static void
1557 web_view_clipboard_flags_changed_signal_cb (GDBusConnection *connection,
1558 const gchar *sender_name,
1559 const gchar *object_path,
1560 const gchar *interface_name,
1561 const gchar *signal_name,
1562 GVariant *parameters,
1563 gpointer user_data)
1565 EWebView *web_view = user_data;
1566 guint64 page_id = 0;
1567 guint32 clipboard_flags = 0;
1569 if (g_strcmp0 (signal_name, "ClipboardFlagsChanged") != 0)
1570 return;
1572 g_return_if_fail (E_IS_WEB_VIEW (web_view));
1574 if (!parameters)
1575 return;
1577 g_variant_get (parameters, "(tu)", &page_id, &clipboard_flags);
1579 if (page_id == webkit_web_view_get_page_id (WEBKIT_WEB_VIEW (web_view)))
1580 e_web_view_set_clipboard_flags (web_view, clipboard_flags);
1583 static void
1584 web_view_element_clicked_signal_cb (GDBusConnection *connection,
1585 const gchar *sender_name,
1586 const gchar *object_path,
1587 const gchar *interface_name,
1588 const gchar *signal_name,
1589 GVariant *parameters,
1590 gpointer user_data)
1592 EWebView *web_view = user_data;
1593 const gchar *elem_class = NULL, *elem_value = NULL;
1594 GtkAllocation elem_position;
1595 guint64 page_id = 0;
1596 gint position_left = 0, position_top = 0, position_width = 0, position_height = 0;
1597 GPtrArray *listeners;
1599 if (g_strcmp0 (signal_name, "ElementClicked") != 0)
1600 return;
1602 g_return_if_fail (E_IS_WEB_VIEW (web_view));
1604 if (!parameters)
1605 return;
1607 g_variant_get (parameters, "(t&s&siiii)", &page_id, &elem_class, &elem_value, &position_left, &position_top, &position_width, &position_height);
1609 if (!elem_class || !*elem_class || page_id != webkit_web_view_get_page_id (WEBKIT_WEB_VIEW (web_view)))
1610 return;
1612 elem_position.x = position_left;
1613 elem_position.y = position_top;
1614 elem_position.width = position_width;
1615 elem_position.height = position_height;
1617 listeners = g_hash_table_lookup (web_view->priv->element_clicked_cbs, elem_class);
1618 if (listeners) {
1619 guint ii;
1621 for (ii = 0; ii <listeners->len; ii++) {
1622 ElementClickedData *ecd = g_ptr_array_index (listeners, ii);
1624 if (ecd && ecd->callback)
1625 ecd->callback (web_view, elem_class, elem_value, &elem_position, ecd->user_data);
1630 static void
1631 web_extension_proxy_created_cb (GDBusProxy *proxy,
1632 GAsyncResult *result,
1633 GWeakRef *web_view_ref)
1635 EWebView *web_view;
1636 GError *error = NULL;
1638 g_return_if_fail (web_view_ref != NULL);
1640 web_view = g_weak_ref_get (web_view_ref);
1642 if (!web_view) {
1643 e_weak_ref_free (web_view_ref);
1644 return;
1647 web_view->priv->web_extension = g_dbus_proxy_new_finish (result, &error);
1648 if (!web_view->priv->web_extension) {
1649 g_warning ("Error creating web extension proxy: %s\n", error->message);
1650 g_error_free (error);
1651 } else {
1652 web_view->priv->web_extension_clipboard_flags_changed_signal_id =
1653 g_dbus_connection_signal_subscribe (
1654 g_dbus_proxy_get_connection (web_view->priv->web_extension),
1655 g_dbus_proxy_get_name (web_view->priv->web_extension),
1656 E_WEB_EXTENSION_INTERFACE,
1657 "ClipboardFlagsChanged",
1658 E_WEB_EXTENSION_OBJECT_PATH,
1659 NULL,
1660 G_DBUS_SIGNAL_FLAGS_NONE,
1661 web_view_clipboard_flags_changed_signal_cb,
1662 web_view,
1663 NULL);
1665 web_view->priv->web_extension_need_input_changed_signal_id =
1666 g_dbus_connection_signal_subscribe (
1667 g_dbus_proxy_get_connection (web_view->priv->web_extension),
1668 g_dbus_proxy_get_name (web_view->priv->web_extension),
1669 E_WEB_EXTENSION_INTERFACE,
1670 "NeedInputChanged",
1671 E_WEB_EXTENSION_OBJECT_PATH,
1672 NULL,
1673 G_DBUS_SIGNAL_FLAGS_NONE,
1674 web_view_need_input_changed_signal_cb,
1675 web_view,
1676 NULL);
1678 web_view->priv->web_extension_element_clicked_signal_id =
1679 g_dbus_connection_signal_subscribe (
1680 g_dbus_proxy_get_connection (web_view->priv->web_extension),
1681 g_dbus_proxy_get_name (web_view->priv->web_extension),
1682 E_WEB_EXTENSION_INTERFACE,
1683 "ElementClicked",
1684 E_WEB_EXTENSION_OBJECT_PATH,
1685 NULL,
1686 G_DBUS_SIGNAL_FLAGS_NONE,
1687 web_view_element_clicked_signal_cb,
1688 web_view,
1689 NULL);
1691 g_hash_table_foreach (web_view->priv->element_clicked_cbs, web_view_register_element_clicked_hfunc, web_view);
1693 e_web_view_ensure_body_class (web_view);
1694 style_updated_cb (web_view);
1697 g_clear_object (&web_view);
1698 e_weak_ref_free (web_view_ref);
1701 static void
1702 web_extension_appeared_cb (GDBusConnection *connection,
1703 const gchar *name,
1704 const gchar *name_owner,
1705 GWeakRef *web_view_ref)
1707 EWebView *web_view;
1709 g_return_if_fail (web_view_ref != NULL);
1711 web_view = g_weak_ref_get (web_view_ref);
1713 if (!web_view)
1714 return;
1716 g_dbus_proxy_new (
1717 connection,
1718 G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START |
1719 G_DBUS_PROXY_FLAGS_DO_NOT_CONNECT_SIGNALS,
1720 NULL,
1721 name,
1722 E_WEB_EXTENSION_OBJECT_PATH,
1723 E_WEB_EXTENSION_INTERFACE,
1724 NULL,
1725 (GAsyncReadyCallback) web_extension_proxy_created_cb,
1726 e_weak_ref_new (web_view));
1728 g_clear_object (&web_view);
1731 static void
1732 web_extension_vanished_cb (GDBusConnection *connection,
1733 const gchar *name,
1734 GWeakRef *web_view_ref)
1736 EWebView *web_view;
1738 g_return_if_fail (web_view_ref != NULL);
1740 web_view = g_weak_ref_get (web_view_ref);
1742 if (!web_view)
1743 return;
1745 g_clear_object (&web_view->priv->web_extension);
1746 g_clear_object (&web_view);
1749 static void
1750 web_view_watch_web_extension (EWebView *web_view)
1752 web_view->priv->web_extension_watch_name_id =
1753 g_bus_watch_name (
1754 G_BUS_TYPE_SESSION,
1755 E_WEB_EXTENSION_SERVICE_NAME,
1756 G_BUS_NAME_WATCHER_FLAGS_NONE,
1757 (GBusNameAppearedCallback) web_extension_appeared_cb,
1758 (GBusNameVanishedCallback) web_extension_vanished_cb,
1759 e_weak_ref_new (web_view),
1760 (GDestroyNotify) e_weak_ref_free);
1763 GDBusProxy *
1764 e_web_view_get_web_extension_proxy (EWebView *web_view)
1766 g_return_val_if_fail (E_IS_WEB_VIEW (web_view), NULL);
1768 return web_view->priv->web_extension;
1771 static void
1772 web_view_update_actions (EWebView *web_view)
1774 GtkActionGroup *action_group;
1775 gboolean can_copy;
1776 gboolean scheme_is_http = FALSE;
1777 gboolean scheme_is_mailto = FALSE;
1778 gboolean uri_is_valid = FALSE;
1779 gboolean visible;
1780 const gchar *cursor_image_src;
1781 const gchar *group_name;
1782 const gchar *uri;
1784 g_return_if_fail (E_IS_WEB_VIEW (web_view));
1786 uri = e_web_view_get_selected_uri (web_view);
1787 can_copy = (e_web_view_get_clipboard_flags (web_view) & E_CLIPBOARD_CAN_COPY) != 0;
1788 cursor_image_src = e_web_view_get_cursor_image_src (web_view);
1790 /* Parse the URI early so we know if the actions will work. */
1791 if (uri != NULL) {
1792 CamelURL *curl;
1794 curl = camel_url_new (uri, NULL);
1795 uri_is_valid = (curl != NULL);
1796 camel_url_free (curl);
1798 scheme_is_http =
1799 (g_ascii_strncasecmp (uri, "http:", 5) == 0) ||
1800 (g_ascii_strncasecmp (uri, "https:", 6) == 0);
1802 scheme_is_mailto =
1803 (g_ascii_strncasecmp (uri, "mailto:", 7) == 0);
1806 /* Allow copying the URI even if it's malformed. */
1807 group_name = "uri";
1808 visible = (uri != NULL) && !scheme_is_mailto;
1809 action_group = e_web_view_get_action_group (web_view, group_name);
1810 gtk_action_group_set_visible (action_group, visible);
1812 group_name = "http";
1813 visible = uri_is_valid && scheme_is_http;
1814 action_group = e_web_view_get_action_group (web_view, group_name);
1815 gtk_action_group_set_visible (action_group, visible);
1817 group_name = "mailto";
1818 visible = uri_is_valid && scheme_is_mailto;
1819 action_group = e_web_view_get_action_group (web_view, group_name);
1820 gtk_action_group_set_visible (action_group, visible);
1822 if (visible) {
1823 CamelURL *curl;
1825 curl = camel_url_new (uri, NULL);
1826 if (curl) {
1827 CamelInternetAddress *inet_addr;
1828 const gchar *name = NULL, *email = NULL;
1829 GtkAction *action;
1831 inet_addr = camel_internet_address_new ();
1832 camel_address_decode (CAMEL_ADDRESS (inet_addr), curl->path);
1834 action = gtk_action_group_get_action (action_group, "mailto-copy-raw");
1835 gtk_action_set_visible (action,
1836 camel_internet_address_get (inet_addr, 0, &name, &email) &&
1837 name && *name && email && *email);
1839 g_object_unref (inet_addr);
1840 camel_url_free (curl);
1844 group_name = "image";
1845 visible = (cursor_image_src != NULL);
1846 action_group = e_web_view_get_action_group (web_view, group_name);
1847 gtk_action_group_set_visible (action_group, visible);
1849 group_name = "selection";
1850 visible = can_copy;
1851 action_group = e_web_view_get_action_group (web_view, group_name);
1852 gtk_action_group_set_visible (action_group, visible);
1854 group_name = "standard";
1855 visible = (uri == NULL);
1856 action_group = e_web_view_get_action_group (web_view, group_name);
1857 gtk_action_group_set_visible (action_group, visible);
1859 group_name = "lockdown-printing";
1860 visible = (uri == NULL) && !web_view->priv->disable_printing;
1861 action_group = e_web_view_get_action_group (web_view, group_name);
1862 gtk_action_group_set_visible (action_group, visible);
1864 group_name = "lockdown-save-to-disk";
1865 visible = (uri == NULL) && !web_view->priv->disable_save_to_disk;
1866 action_group = e_web_view_get_action_group (web_view, group_name);
1867 gtk_action_group_set_visible (action_group, visible);
1870 static void
1871 web_view_submit_alert (EAlertSink *alert_sink,
1872 EAlert *alert)
1874 EWebView *web_view;
1875 GtkWidget *dialog;
1876 GString *buffer;
1877 const gchar *icon_name = NULL;
1878 const gchar *primary_text;
1879 const gchar *secondary_text;
1880 gint icon_width, icon_height;
1881 gpointer parent;
1883 web_view = E_WEB_VIEW (alert_sink);
1885 parent = gtk_widget_get_toplevel (GTK_WIDGET (web_view));
1886 parent = gtk_widget_is_toplevel (parent) ? parent : NULL;
1888 switch (e_alert_get_message_type (alert)) {
1889 case GTK_MESSAGE_INFO:
1890 icon_name = "dialog-information";
1891 break;
1893 case GTK_MESSAGE_WARNING:
1894 icon_name = "dialog-warning";
1895 break;
1897 case GTK_MESSAGE_ERROR:
1898 icon_name = "dialog-error";
1899 break;
1901 default:
1902 dialog = e_alert_dialog_new (parent, alert);
1903 gtk_dialog_run (GTK_DIALOG (dialog));
1904 gtk_widget_destroy (dialog);
1905 return;
1908 /* Primary text is required. */
1909 primary_text = e_alert_get_primary_text (alert);
1910 g_return_if_fail (primary_text != NULL);
1912 /* Secondary text is optional. */
1913 secondary_text = e_alert_get_secondary_text (alert);
1914 if (secondary_text == NULL)
1915 secondary_text = "";
1917 if (!gtk_icon_size_lookup (GTK_ICON_SIZE_DIALOG, &icon_width, &icon_height)) {
1918 icon_width = 48;
1919 icon_height = 48;
1922 buffer = g_string_sized_new (512);
1924 g_string_append (
1925 buffer,
1926 "<html>"
1927 "<head>"
1928 "<meta http-equiv=\"content-type\""
1929 " content=\"text/html; charset=utf-8\">"
1930 "</head>"
1931 "<body>");
1933 g_string_append (
1934 buffer,
1935 "<table bgcolor='#000000' width='100%'"
1936 " cellpadding='1' cellspacing='0'>"
1937 "<tr>"
1938 "<td>"
1939 "<table bgcolor='#dddddd' width='100%' cellpadding='6'>"
1940 "<tr>");
1942 g_string_append_printf (
1943 buffer,
1944 "<tr>"
1945 "<td valign='top'>"
1946 "<img src='gtk-stock://%s/?size=%d' width=\"%dpx\" height=\"%dpx\"/>"
1947 "</td>"
1948 "<td align='left' width='100%%'>"
1949 "<h3>%s</h3>"
1950 "%s"
1951 "</td>"
1952 "</tr>",
1953 icon_name,
1954 GTK_ICON_SIZE_DIALOG,
1955 icon_width,
1956 icon_height,
1957 primary_text,
1958 secondary_text);
1960 g_string_append (
1961 buffer,
1962 "</table>"
1963 "</td>"
1964 "</tr>"
1965 "</table>"
1966 "</body>"
1967 "</html>");
1969 e_web_view_load_string (web_view, buffer->str);
1971 g_string_free (buffer, TRUE);
1974 static void
1975 web_view_can_execute_editing_command_cb (WebKitWebView *webkit_web_view,
1976 GAsyncResult *result,
1977 GtkAction *action)
1979 gboolean can_do_command;
1981 can_do_command = webkit_web_view_can_execute_editing_command_finish (
1982 webkit_web_view, result, NULL);
1984 gtk_action_set_sensitive (action, can_do_command);
1985 g_object_unref (action);
1988 static void
1989 web_view_selectable_update_actions (ESelectable *selectable,
1990 EFocusTracker *focus_tracker,
1991 GdkAtom *clipboard_targets,
1992 gint n_clipboard_targets)
1994 EWebView *web_view;
1995 GtkAction *action;
1996 gboolean can_copy = FALSE;
1998 web_view = E_WEB_VIEW (selectable);
2000 can_copy = (e_web_view_get_clipboard_flags (web_view) & E_CLIPBOARD_CAN_COPY) != 0;
2002 action = e_focus_tracker_get_copy_clipboard_action (focus_tracker);
2003 gtk_action_set_sensitive (action, can_copy);
2004 gtk_action_set_tooltip (action, _("Copy the selection"));
2006 action = e_focus_tracker_get_cut_clipboard_action (focus_tracker);
2007 webkit_web_view_can_execute_editing_command (
2008 WEBKIT_WEB_VIEW (web_view),
2009 WEBKIT_EDITING_COMMAND_CUT,
2010 NULL, /* cancellable */
2011 (GAsyncReadyCallback) web_view_can_execute_editing_command_cb,
2012 g_object_ref (action));
2013 gtk_action_set_tooltip (action, _("Cut the selection"));
2015 action = e_focus_tracker_get_paste_clipboard_action (focus_tracker);
2016 webkit_web_view_can_execute_editing_command (
2017 WEBKIT_WEB_VIEW (web_view),
2018 WEBKIT_EDITING_COMMAND_PASTE,
2019 NULL, /* cancellable */
2020 (GAsyncReadyCallback) web_view_can_execute_editing_command_cb,
2021 g_object_ref (action));
2022 gtk_action_set_tooltip (action, _("Paste the clipboard"));
2024 action = e_focus_tracker_get_select_all_action (focus_tracker);
2025 gtk_action_set_sensitive (action, TRUE);
2026 gtk_action_set_tooltip (action, _("Select all text and images"));
2029 static void
2030 web_view_selectable_cut_clipboard (ESelectable *selectable)
2032 e_web_view_cut_clipboard (E_WEB_VIEW (selectable));
2035 static void
2036 web_view_selectable_copy_clipboard (ESelectable *selectable)
2038 e_web_view_copy_clipboard (E_WEB_VIEW (selectable));
2041 static void
2042 web_view_selectable_paste_clipboard (ESelectable *selectable)
2044 e_web_view_paste_clipboard (E_WEB_VIEW (selectable));
2047 static void
2048 web_view_selectable_select_all (ESelectable *selectable)
2050 e_web_view_select_all (E_WEB_VIEW (selectable));
2053 static void
2054 e_web_view_test_change_and_update_fonts_cb (EWebView *web_view,
2055 const gchar *key,
2056 GSettings *settings)
2058 GVariant *new_value, *old_value;
2060 new_value = g_settings_get_value (settings, key);
2061 old_value = g_hash_table_lookup (web_view->priv->old_settings, key);
2063 if (!new_value || !old_value || !g_variant_equal (new_value, old_value)) {
2064 if (new_value)
2065 g_hash_table_insert (web_view->priv->old_settings, g_strdup (key), new_value);
2066 else
2067 g_hash_table_remove (web_view->priv->old_settings, key);
2069 e_web_view_update_fonts (web_view);
2070 } else if (new_value) {
2071 g_variant_unref (new_value);
2075 static void
2076 web_view_toplevel_event_after_cb (GtkWidget *widget,
2077 GdkEvent *event,
2078 EWebView *web_view)
2080 if (event && event->type == GDK_MOTION_NOTIFY && web_view->priv->has_hover_link) {
2081 GdkEventMotion *motion_event = (GdkEventMotion *) event;
2083 if (gdk_event_get_window (event) != gtk_widget_get_window (GTK_WIDGET (web_view))) {
2084 GdkEventMotion fake_motion_event;
2085 gboolean result = FALSE;
2087 fake_motion_event = *motion_event;
2088 fake_motion_event.x = -1.0;
2089 fake_motion_event.y = -1.0;
2090 fake_motion_event.window = gtk_widget_get_window (GTK_WIDGET (web_view));
2092 /* Use a fake event instead of the call to unset the status message, because
2093 WebKit caches which link it stays on and doesn't emit the signal when still
2094 moving about the same link, thus this will unset the link also for the WebKit. */
2095 g_signal_emit_by_name (web_view, "motion-notify-event", &fake_motion_event, &result);
2097 web_view->priv->has_hover_link = FALSE;
2102 static void
2103 web_view_map (GtkWidget *widget)
2105 GtkWidget *toplevel;
2107 toplevel = gtk_widget_get_toplevel (widget);
2109 g_signal_connect (toplevel, "event-after", G_CALLBACK (web_view_toplevel_event_after_cb), widget);
2111 GTK_WIDGET_CLASS (e_web_view_parent_class)->map (widget);
2114 static void
2115 web_view_unmap (GtkWidget *widget)
2117 GtkWidget *toplevel;
2119 toplevel = gtk_widget_get_toplevel (widget);
2121 g_signal_handlers_disconnect_by_func (toplevel, G_CALLBACK (web_view_toplevel_event_after_cb), widget);
2123 GTK_WIDGET_CLASS (e_web_view_parent_class)->unmap (widget);
2126 static void
2127 e_web_view_class_init (EWebViewClass *class)
2129 GObjectClass *object_class;
2130 GtkWidgetClass *widget_class;
2132 g_type_class_add_private (class, sizeof (EWebViewPrivate));
2134 object_class = G_OBJECT_CLASS (class);
2135 object_class->constructor = web_view_constructor;
2136 object_class->set_property = web_view_set_property;
2137 object_class->get_property = web_view_get_property;
2138 object_class->dispose = web_view_dispose;
2139 object_class->finalize = web_view_finalize;
2140 object_class->constructed = web_view_constructed;
2142 widget_class = GTK_WIDGET_CLASS (class);
2143 widget_class->scroll_event = web_view_scroll_event;
2144 widget_class->drag_motion = web_view_drag_motion;
2145 widget_class->drag_drop = web_view_drag_drop;
2146 widget_class->drag_leave = web_view_drag_leave;
2147 widget_class->map = web_view_map;
2148 widget_class->unmap = web_view_unmap;
2150 class->hovering_over_link = web_view_hovering_over_link;
2151 class->link_clicked = web_view_link_clicked;
2152 class->load_string = web_view_load_string;
2153 class->load_uri = web_view_load_uri;
2154 class->suggest_filename = web_view_suggest_filename;
2155 class->popup_event = web_view_popup_event;
2156 class->stop_loading = web_view_stop_loading;
2157 class->update_actions = web_view_update_actions;
2159 g_object_class_install_property (
2160 object_class,
2161 PROP_CARET_MODE,
2162 g_param_spec_boolean (
2163 "caret-mode",
2164 "Caret Mode",
2165 NULL,
2166 FALSE,
2167 G_PARAM_READWRITE));
2169 g_object_class_install_property (
2170 object_class,
2171 PROP_CLIPBOARD_FLAGS,
2172 g_param_spec_uint (
2173 "clipboard-flags",
2174 "Clipboard Flags",
2175 NULL,
2176 0, G_MAXUINT, 0,
2177 G_PARAM_READWRITE |
2178 G_PARAM_CONSTRUCT));
2180 /* Inherited from ESelectableInterface; just a fake property here */
2181 g_object_class_override_property (
2182 object_class,
2183 PROP_COPY_TARGET_LIST,
2184 "copy-target-list");
2186 /* Inherited from ESelectableInterface; just a fake property here */
2187 g_object_class_override_property (
2188 object_class,
2189 PROP_PASTE_TARGET_LIST,
2190 "paste-target-list");
2192 g_object_class_install_property (
2193 object_class,
2194 PROP_CURSOR_IMAGE_SRC,
2195 g_param_spec_string (
2196 "cursor-image-src",
2197 "Image source uri at the mouse cursor",
2198 NULL,
2199 NULL,
2200 G_PARAM_READWRITE));
2202 g_object_class_install_property (
2203 object_class,
2204 PROP_DISABLE_PRINTING,
2205 g_param_spec_boolean (
2206 "disable-printing",
2207 "Disable Printing",
2208 NULL,
2209 FALSE,
2210 G_PARAM_READWRITE |
2211 G_PARAM_CONSTRUCT));
2213 g_object_class_install_property (
2214 object_class,
2215 PROP_DISABLE_SAVE_TO_DISK,
2216 g_param_spec_boolean (
2217 "disable-save-to-disk",
2218 "Disable Save-to-Disk",
2219 NULL,
2220 FALSE,
2221 G_PARAM_READWRITE |
2222 G_PARAM_CONSTRUCT));
2224 g_object_class_install_property (
2225 object_class,
2226 PROP_NEED_INPUT,
2227 g_param_spec_boolean (
2228 "need-input",
2229 "Need Input",
2230 NULL,
2231 FALSE,
2232 G_PARAM_READWRITE |
2233 G_PARAM_CONSTRUCT));
2235 g_object_class_install_property (
2236 object_class,
2237 PROP_OPEN_PROXY,
2238 g_param_spec_object (
2239 "open-proxy",
2240 "Open Proxy",
2241 NULL,
2242 GTK_TYPE_ACTION,
2243 G_PARAM_READWRITE));
2245 g_object_class_install_property (
2246 object_class,
2247 PROP_PRINT_PROXY,
2248 g_param_spec_object (
2249 "print-proxy",
2250 "Print Proxy",
2251 NULL,
2252 GTK_TYPE_ACTION,
2253 G_PARAM_READWRITE));
2255 g_object_class_install_property (
2256 object_class,
2257 PROP_SAVE_AS_PROXY,
2258 g_param_spec_object (
2259 "save-as-proxy",
2260 "Save As Proxy",
2261 NULL,
2262 GTK_TYPE_ACTION,
2263 G_PARAM_READWRITE));
2265 g_object_class_install_property (
2266 object_class,
2267 PROP_SELECTED_URI,
2268 g_param_spec_string (
2269 "selected-uri",
2270 "Selected URI",
2271 NULL,
2272 NULL,
2273 G_PARAM_READWRITE));
2275 signals[NEW_ACTIVITY] = g_signal_new (
2276 "new-activity",
2277 G_TYPE_FROM_CLASS (class),
2278 G_SIGNAL_RUN_LAST,
2279 G_STRUCT_OFFSET (EWebViewClass, new_activity),
2280 NULL, NULL,
2281 g_cclosure_marshal_VOID__OBJECT,
2282 G_TYPE_NONE, 1,
2283 E_TYPE_ACTIVITY);
2285 signals[POPUP_EVENT] = g_signal_new (
2286 "popup-event",
2287 G_TYPE_FROM_CLASS (class),
2288 G_SIGNAL_RUN_LAST,
2289 G_STRUCT_OFFSET (EWebViewClass, popup_event),
2290 g_signal_accumulator_true_handled, NULL,
2291 NULL,
2292 G_TYPE_BOOLEAN, 2, G_TYPE_STRING, GDK_TYPE_EVENT | G_SIGNAL_TYPE_STATIC_SCOPE);
2294 signals[STATUS_MESSAGE] = g_signal_new (
2295 "status-message",
2296 G_TYPE_FROM_CLASS (class),
2297 G_SIGNAL_RUN_LAST,
2298 G_STRUCT_OFFSET (EWebViewClass, status_message),
2299 NULL, NULL,
2300 g_cclosure_marshal_VOID__STRING,
2301 G_TYPE_NONE, 1,
2302 G_TYPE_STRING);
2304 signals[STOP_LOADING] = g_signal_new (
2305 "stop-loading",
2306 G_TYPE_FROM_CLASS (class),
2307 G_SIGNAL_RUN_LAST,
2308 G_STRUCT_OFFSET (EWebViewClass, stop_loading),
2309 NULL, NULL,
2310 g_cclosure_marshal_VOID__VOID,
2311 G_TYPE_NONE, 0);
2313 signals[UPDATE_ACTIONS] = g_signal_new (
2314 "update-actions",
2315 G_TYPE_FROM_CLASS (class),
2316 G_SIGNAL_RUN_LAST,
2317 G_STRUCT_OFFSET (EWebViewClass, update_actions),
2318 NULL, NULL,
2319 g_cclosure_marshal_VOID__VOID,
2320 G_TYPE_NONE, 0);
2322 /* return TRUE when a signal handler processed the mailto URI */
2323 signals[PROCESS_MAILTO] = g_signal_new (
2324 "process-mailto",
2325 G_TYPE_FROM_CLASS (class),
2326 G_SIGNAL_RUN_LAST,
2327 G_STRUCT_OFFSET (EWebViewClass, process_mailto),
2328 NULL, NULL,
2329 e_marshal_BOOLEAN__STRING,
2330 G_TYPE_BOOLEAN, 1, G_TYPE_STRING);
2332 /* Expects an empty string to abandon the request,
2333 or NULL to keep the passed-in uri,
2334 or a new uri to load instead. */
2335 signals[URI_REQUESTED] = g_signal_new (
2336 "uri-requested",
2337 G_TYPE_FROM_CLASS (class),
2338 G_SIGNAL_RUN_LAST,
2339 G_STRUCT_OFFSET (EWebViewClass, uri_requested),
2340 NULL, NULL, NULL,
2341 G_TYPE_NONE, 2, G_TYPE_STRING, G_TYPE_POINTER);
2344 static void
2345 e_web_view_alert_sink_init (EAlertSinkInterface *iface)
2347 iface->submit_alert = web_view_submit_alert;
2350 static void
2351 e_web_view_selectable_init (ESelectableInterface *iface)
2353 iface->update_actions = web_view_selectable_update_actions;
2354 iface->cut_clipboard = web_view_selectable_cut_clipboard;
2355 iface->copy_clipboard = web_view_selectable_copy_clipboard;
2356 iface->paste_clipboard = web_view_selectable_paste_clipboard;
2357 iface->select_all = web_view_selectable_select_all;
2360 static void
2361 initialize_web_extensions_cb (WebKitWebContext *web_context)
2363 /* Set the web extensions dir before the process is launched */
2364 webkit_web_context_set_web_extensions_directory (
2365 web_context, EVOLUTION_WEB_EXTENSIONS_DIR);
2368 static void
2369 e_web_view_init (EWebView *web_view)
2371 GtkUIManager *ui_manager;
2372 GtkActionGroup *action_group;
2373 EPopupAction *popup_action;
2374 GSettingsSchema *settings_schema;
2375 GSettings *settings;
2376 const gchar *domain = GETTEXT_PACKAGE;
2377 const gchar *id;
2378 gulong handler_id;
2379 GError *error = NULL;
2381 web_view->priv = E_WEB_VIEW_GET_PRIVATE (web_view);
2383 web_view->priv->old_settings = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, (GDestroyNotify) g_variant_unref);
2385 g_signal_connect (
2386 web_view, "context-menu",
2387 G_CALLBACK (web_view_context_menu_cb), NULL);
2389 g_signal_connect (
2390 web_view, "mouse-target-changed",
2391 G_CALLBACK (web_view_mouse_target_changed_cb), NULL);
2393 g_signal_connect (
2394 web_view, "decide-policy",
2395 G_CALLBACK (web_view_decide_policy_cb),
2396 NULL);
2398 g_signal_connect (
2399 webkit_web_context_get_default (), "initialize-web-extensions",
2400 G_CALLBACK (initialize_web_extensions_cb), NULL);
2402 g_signal_connect (
2403 web_view, "load-changed",
2404 G_CALLBACK (web_view_load_changed_cb), NULL);
2406 g_signal_connect (
2407 web_view, "style-updated",
2408 G_CALLBACK (style_updated_cb), NULL);
2410 g_signal_connect (
2411 web_view, "state-flags-changed",
2412 G_CALLBACK (style_updated_cb), NULL);
2414 ui_manager = gtk_ui_manager_new ();
2415 web_view->priv->ui_manager = ui_manager;
2417 g_signal_connect_swapped (
2418 ui_manager, "connect-proxy",
2419 G_CALLBACK (web_view_connect_proxy_cb), web_view);
2421 web_view_watch_web_extension (web_view);
2423 settings = e_util_ref_settings ("org.gnome.desktop.interface");
2424 web_view->priv->font_settings = g_object_ref (settings);
2425 handler_id = g_signal_connect_swapped (
2426 settings, "changed::font-name",
2427 G_CALLBACK (e_web_view_test_change_and_update_fonts_cb), web_view);
2428 web_view->priv->font_name_changed_handler_id = handler_id;
2429 handler_id = g_signal_connect_swapped (
2430 settings, "changed::monospace-font-name",
2431 G_CALLBACK (e_web_view_test_change_and_update_fonts_cb), web_view);
2432 web_view->priv->monospace_font_name_changed_handler_id = handler_id;
2433 g_object_unref (settings);
2435 /* This schema is optional. Use if available. */
2436 id = "org.gnome.settings-daemon.plugins.xsettings";
2437 settings_schema = g_settings_schema_source_lookup (
2438 g_settings_schema_source_get_default (), id, FALSE);
2439 if (settings_schema != NULL) {
2440 settings = e_util_ref_settings (id);
2441 web_view->priv->aliasing_settings = g_object_ref (settings);
2442 handler_id = g_signal_connect_swapped (
2443 settings, "changed::antialiasing",
2444 G_CALLBACK (e_web_view_test_change_and_update_fonts_cb), web_view);
2445 web_view->priv->antialiasing_changed_handler_id = handler_id;
2446 g_object_unref (settings);
2447 g_settings_schema_unref (settings_schema);
2450 action_group = gtk_action_group_new ("uri");
2451 gtk_action_group_set_translation_domain (action_group, domain);
2452 gtk_ui_manager_insert_action_group (ui_manager, action_group, 0);
2453 g_object_unref (action_group);
2455 gtk_action_group_add_actions (
2456 action_group, uri_entries,
2457 G_N_ELEMENTS (uri_entries), web_view);
2459 action_group = gtk_action_group_new ("http");
2460 gtk_action_group_set_translation_domain (action_group, domain);
2461 gtk_ui_manager_insert_action_group (ui_manager, action_group, 0);
2462 g_object_unref (action_group);
2464 gtk_action_group_add_actions (
2465 action_group, http_entries,
2466 G_N_ELEMENTS (http_entries), web_view);
2468 action_group = gtk_action_group_new ("mailto");
2469 gtk_action_group_set_translation_domain (action_group, domain);
2470 gtk_ui_manager_insert_action_group (ui_manager, action_group, 0);
2471 g_object_unref (action_group);
2473 gtk_action_group_add_actions (
2474 action_group, mailto_entries,
2475 G_N_ELEMENTS (mailto_entries), web_view);
2477 action_group = gtk_action_group_new ("image");
2478 gtk_action_group_set_translation_domain (action_group, domain);
2479 gtk_ui_manager_insert_action_group (ui_manager, action_group, 0);
2480 g_object_unref (action_group);
2482 gtk_action_group_add_actions (
2483 action_group, image_entries,
2484 G_N_ELEMENTS (image_entries), web_view);
2486 action_group = gtk_action_group_new ("selection");
2487 gtk_action_group_set_translation_domain (action_group, domain);
2488 gtk_ui_manager_insert_action_group (ui_manager, action_group, 0);
2489 g_object_unref (action_group);
2491 gtk_action_group_add_actions (
2492 action_group, selection_entries,
2493 G_N_ELEMENTS (selection_entries), web_view);
2495 action_group = gtk_action_group_new ("standard");
2496 gtk_action_group_set_translation_domain (action_group, domain);
2497 gtk_ui_manager_insert_action_group (ui_manager, action_group, 0);
2498 g_object_unref (action_group);
2500 gtk_action_group_add_actions (
2501 action_group, standard_entries,
2502 G_N_ELEMENTS (standard_entries), web_view);
2504 popup_action = e_popup_action_new ("open");
2505 gtk_action_group_add_action (action_group, GTK_ACTION (popup_action));
2506 g_object_unref (popup_action);
2508 e_binding_bind_property (
2509 web_view, "open-proxy",
2510 popup_action, "related-action",
2511 G_BINDING_BIDIRECTIONAL |
2512 G_BINDING_SYNC_CREATE);
2514 /* Support lockdown. */
2516 action_group = gtk_action_group_new ("lockdown-printing");
2517 gtk_action_group_set_translation_domain (action_group, domain);
2518 gtk_ui_manager_insert_action_group (ui_manager, action_group, 0);
2519 g_object_unref (action_group);
2521 popup_action = e_popup_action_new ("print");
2522 gtk_action_group_add_action (action_group, GTK_ACTION (popup_action));
2523 g_object_unref (popup_action);
2525 e_binding_bind_property (
2526 web_view, "print-proxy",
2527 popup_action, "related-action",
2528 G_BINDING_BIDIRECTIONAL |
2529 G_BINDING_SYNC_CREATE);
2531 action_group = gtk_action_group_new ("lockdown-save-to-disk");
2532 gtk_action_group_set_translation_domain (action_group, domain);
2533 gtk_ui_manager_insert_action_group (ui_manager, action_group, 0);
2534 g_object_unref (action_group);
2536 popup_action = e_popup_action_new ("save-as");
2537 gtk_action_group_add_action (action_group, GTK_ACTION (popup_action));
2538 g_object_unref (popup_action);
2540 e_binding_bind_property (
2541 web_view, "save-as-proxy",
2542 popup_action, "related-action",
2543 G_BINDING_BIDIRECTIONAL |
2544 G_BINDING_SYNC_CREATE);
2546 /* Because we are loading from a hard-coded string, there is
2547 * no chance of I/O errors. Failure here implies a malformed
2548 * UI definition. Full stop. */
2549 gtk_ui_manager_add_ui_from_string (ui_manager, ui, -1, &error);
2550 if (error != NULL)
2551 g_error ("%s", error->message);
2553 id = "org.gnome.evolution.webview";
2554 e_plugin_ui_register_manager (ui_manager, id, web_view);
2555 e_plugin_ui_enable_manager (ui_manager, id);
2557 web_view->priv->element_clicked_cbs = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, (GDestroyNotify) g_ptr_array_unref);
2559 web_view->priv->load_cancellable = NULL;
2562 GtkWidget *
2563 e_web_view_new (void)
2565 return g_object_new (
2566 E_TYPE_WEB_VIEW,
2567 NULL);
2570 void
2571 e_web_view_clear (EWebView *web_view)
2573 g_return_if_fail (E_IS_WEB_VIEW (web_view));
2575 e_web_view_replace_load_cancellable (web_view, FALSE);
2577 webkit_web_view_load_html (
2578 WEBKIT_WEB_VIEW (web_view),
2579 "<html>"
2580 "<head></head>"
2581 "<body class=\"-e-web-view-background-color -e-web-view-text-color\"></body>"
2582 "</html>",
2583 NULL);
2586 void
2587 e_web_view_load_string (EWebView *web_view,
2588 const gchar *string)
2590 EWebViewClass *class;
2592 g_return_if_fail (E_IS_WEB_VIEW (web_view));
2594 class = E_WEB_VIEW_GET_CLASS (web_view);
2595 g_return_if_fail (class->load_string != NULL);
2597 e_web_view_replace_load_cancellable (web_view, TRUE);
2599 class->load_string (web_view, string);
2602 void
2603 e_web_view_load_uri (EWebView *web_view,
2604 const gchar *uri)
2606 EWebViewClass *class;
2608 g_return_if_fail (E_IS_WEB_VIEW (web_view));
2610 class = E_WEB_VIEW_GET_CLASS (web_view);
2611 g_return_if_fail (class->load_uri != NULL);
2613 e_web_view_replace_load_cancellable (web_view, TRUE);
2615 class->load_uri (web_view, uri);
2619 * e_web_view_suggest_filename:
2620 * @web_view: an #EWebView
2621 * @uri: a URI string
2623 * Attempts to derive a suggested filename from the @uri for use in a
2624 * "Save As" dialog.
2626 * By default the suggested filename is the last path segment of the @uri
2627 * (unless @uri looks like a query), but subclasses can use other mechanisms
2628 * for custom URI schemes. For example, "cid:" URIs in an email message may
2629 * refer to a MIME part with a suggested filename in its Content-Disposition
2630 * header.
2632 * The returned string should be freed with g_free() when finished with it,
2633 * but callers should also be prepared for the function to return %NULL if
2634 * a filename cannot be determined.
2636 * Returns: a suggested filename, or %NULL
2638 gchar *
2639 e_web_view_suggest_filename (EWebView *web_view,
2640 const gchar *uri)
2642 EWebViewClass *class;
2643 gchar *filename;
2645 g_return_val_if_fail (E_IS_WEB_VIEW (web_view), NULL);
2646 g_return_val_if_fail (uri != NULL, NULL);
2648 class = E_WEB_VIEW_GET_CLASS (web_view);
2649 g_return_val_if_fail (class->suggest_filename != NULL, NULL);
2651 filename = class->suggest_filename (web_view, uri);
2653 if (filename != NULL)
2654 e_filename_make_safe (filename);
2656 return filename;
2659 void
2660 e_web_view_reload (EWebView *web_view)
2662 g_return_if_fail (E_IS_WEB_VIEW (web_view));
2664 e_web_view_replace_load_cancellable (web_view, TRUE);
2666 webkit_web_view_reload (WEBKIT_WEB_VIEW (web_view));
2669 static void
2670 get_document_content_html_cb (GObject *source_object,
2671 GAsyncResult *result,
2672 gpointer user_data)
2674 GDBusProxy *web_extension;
2675 GTask *task = user_data;
2676 GVariant *result_variant;
2677 gchar *html_content = NULL;
2678 GError *error = NULL;
2680 g_return_if_fail (G_IS_DBUS_PROXY (source_object));
2681 g_return_if_fail (G_IS_TASK (task));
2683 web_extension = G_DBUS_PROXY (source_object);
2685 result_variant = g_dbus_proxy_call_finish (web_extension, result, &error);
2686 if (result_variant)
2687 g_variant_get (result_variant, "(s)", &html_content);
2688 g_variant_unref (result_variant);
2690 g_task_return_pointer (task, html_content, g_free);
2691 g_object_unref (task);
2693 if (error)
2694 g_dbus_error_strip_remote_error (error);
2696 e_util_claim_dbus_proxy_call_error (web_extension, "GetDocumentContentHTML", error);
2697 g_clear_error (&error);
2700 void
2701 e_web_view_get_content_html (EWebView *web_view,
2702 GCancellable *cancellable,
2703 GAsyncReadyCallback callback,
2704 gpointer user_data)
2706 GDBusProxy *web_extension;
2707 GTask *task;
2709 g_return_if_fail (E_IS_WEB_VIEW (web_view));
2711 task = g_task_new (web_view, cancellable, callback, user_data);
2713 web_extension = e_web_view_get_web_extension_proxy (web_view);
2714 if (web_extension) {
2715 g_dbus_proxy_call (
2716 web_extension,
2717 "GetDocumentContentHTML",
2718 g_variant_new (
2719 "(t)",
2720 webkit_web_view_get_page_id (
2721 WEBKIT_WEB_VIEW (web_view))),
2722 G_DBUS_CALL_FLAGS_NONE,
2724 cancellable,
2725 get_document_content_html_cb,
2726 g_object_ref (task));
2727 } else
2728 g_task_return_pointer (task, NULL, NULL);
2731 gchar *
2732 e_web_view_get_content_html_finish (EWebView *web_view,
2733 GAsyncResult *result,
2734 GError **error)
2736 g_return_val_if_fail (E_IS_WEB_VIEW (web_view), NULL);
2737 g_return_val_if_fail (g_task_is_valid (result, web_view), FALSE);
2739 return g_task_propagate_pointer (G_TASK (result), error);
2742 gchar *
2743 e_web_view_get_content_html_sync (EWebView *web_view,
2744 GCancellable *cancellable,
2745 GError **error)
2747 GDBusProxy *web_extension;
2749 g_return_val_if_fail (E_IS_WEB_VIEW (web_view), NULL);
2751 web_extension = e_web_view_get_web_extension_proxy (web_view);
2752 if (web_extension) {
2753 GVariant *result;
2755 result = e_util_invoke_g_dbus_proxy_call_sync_wrapper_full (
2756 web_extension,
2757 "GetDocumentContentHTML",
2758 g_variant_new (
2759 "(t)",
2760 webkit_web_view_get_page_id (
2761 WEBKIT_WEB_VIEW (web_view))),
2762 G_DBUS_CALL_FLAGS_NONE,
2764 cancellable,
2765 error);
2767 if (result) {
2768 gchar *html_content = NULL;
2770 g_variant_get (result, "(s)", &html_content);
2771 g_variant_unref (result);
2773 return html_content;
2777 return NULL;
2780 gboolean
2781 e_web_view_get_caret_mode (EWebView *web_view)
2783 g_return_val_if_fail (E_IS_WEB_VIEW (web_view), FALSE);
2785 return web_view->priv->caret_mode;
2788 void
2789 e_web_view_set_caret_mode (EWebView *web_view,
2790 gboolean caret_mode)
2792 g_return_if_fail (E_IS_WEB_VIEW (web_view));
2794 if (web_view->priv->caret_mode == caret_mode)
2795 return;
2797 web_view->priv->caret_mode = caret_mode;
2799 g_object_notify (G_OBJECT (web_view), "caret-mode");
2802 GtkTargetList *
2803 e_web_view_get_copy_target_list (EWebView *web_view)
2805 g_return_val_if_fail (E_IS_WEB_VIEW (web_view), NULL);
2807 return NULL;
2808 /* FIXME WK2 */
2809 /*return webkit_web_view_get_copy_target_list (
2810 WEBKIT_WEB_VIEW (web_view));*/
2813 gboolean
2814 e_web_view_get_disable_printing (EWebView *web_view)
2816 g_return_val_if_fail (E_IS_WEB_VIEW (web_view), FALSE);
2818 return web_view->priv->disable_printing;
2821 void
2822 e_web_view_set_disable_printing (EWebView *web_view,
2823 gboolean disable_printing)
2825 g_return_if_fail (E_IS_WEB_VIEW (web_view));
2827 if (web_view->priv->disable_printing == disable_printing)
2828 return;
2830 web_view->priv->disable_printing = disable_printing;
2832 g_object_notify (G_OBJECT (web_view), "disable-printing");
2835 gboolean
2836 e_web_view_get_disable_save_to_disk (EWebView *web_view)
2838 g_return_val_if_fail (E_IS_WEB_VIEW (web_view), FALSE);
2840 return web_view->priv->disable_save_to_disk;
2843 void
2844 e_web_view_set_disable_save_to_disk (EWebView *web_view,
2845 gboolean disable_save_to_disk)
2847 g_return_if_fail (E_IS_WEB_VIEW (web_view));
2849 if (web_view->priv->disable_save_to_disk == disable_save_to_disk)
2850 return;
2852 web_view->priv->disable_save_to_disk = disable_save_to_disk;
2854 g_object_notify (G_OBJECT (web_view), "disable-save-to-disk");
2857 gboolean
2858 e_web_view_get_editable (EWebView *web_view)
2860 g_return_val_if_fail (E_IS_WEB_VIEW (web_view), FALSE);
2862 return webkit_web_view_is_editable (WEBKIT_WEB_VIEW (web_view));
2865 void
2866 e_web_view_set_editable (EWebView *web_view,
2867 gboolean editable)
2869 g_return_if_fail (E_IS_WEB_VIEW (web_view));
2871 webkit_web_view_set_editable (WEBKIT_WEB_VIEW (web_view), editable);
2874 guint32
2875 e_web_view_get_clipboard_flags (EWebView *web_view)
2877 g_return_val_if_fail (E_IS_WEB_VIEW (web_view), 0);
2879 return web_view->priv->clipboard_flags;
2882 void
2883 e_web_view_set_clipboard_flags (EWebView *web_view,
2884 guint32 clipboard_flags)
2886 g_return_if_fail (E_IS_WEB_VIEW (web_view));
2888 if (web_view->priv->clipboard_flags == clipboard_flags)
2889 return;
2891 web_view->priv->clipboard_flags = clipboard_flags;
2893 g_object_notify (G_OBJECT (web_view), "clipboard-flags");
2896 gboolean
2897 e_web_view_get_need_input (EWebView *web_view)
2899 g_return_val_if_fail (E_IS_WEB_VIEW (web_view), FALSE);
2901 return web_view->priv->need_input;
2904 void
2905 e_web_view_set_need_input (EWebView *web_view,
2906 gboolean need_input)
2908 g_return_if_fail (E_IS_WEB_VIEW (web_view));
2910 if ((!web_view->priv->need_input) == (!need_input))
2911 return;
2913 web_view->priv->need_input = need_input;
2915 g_object_notify (G_OBJECT (web_view), "need-input");
2918 const gchar *
2919 e_web_view_get_selected_uri (EWebView *web_view)
2921 g_return_val_if_fail (E_IS_WEB_VIEW (web_view), NULL);
2923 return web_view->priv->selected_uri;
2926 void
2927 e_web_view_set_selected_uri (EWebView *web_view,
2928 const gchar *selected_uri)
2930 g_return_if_fail (E_IS_WEB_VIEW (web_view));
2932 if (g_strcmp0 (web_view->priv->selected_uri, selected_uri) == 0)
2933 return;
2935 g_free (web_view->priv->selected_uri);
2936 web_view->priv->selected_uri = g_strdup (selected_uri);
2938 g_object_notify (G_OBJECT (web_view), "selected-uri");
2941 const gchar *
2942 e_web_view_get_cursor_image_src (EWebView *web_view)
2944 g_return_val_if_fail (E_IS_WEB_VIEW (web_view), NULL);
2946 return web_view->priv->cursor_image_src;
2949 void
2950 e_web_view_set_cursor_image_src (EWebView *web_view,
2951 const gchar *src_uri)
2953 g_return_if_fail (E_IS_WEB_VIEW (web_view));
2955 if (g_strcmp0 (web_view->priv->cursor_image_src, src_uri) == 0)
2956 return;
2958 g_free (web_view->priv->cursor_image_src);
2959 web_view->priv->cursor_image_src = g_strdup (src_uri);
2961 g_object_notify (G_OBJECT (web_view), "cursor-image-src");
2964 GtkAction *
2965 e_web_view_get_open_proxy (EWebView *web_view)
2967 g_return_val_if_fail (E_IS_WEB_VIEW (web_view), NULL);
2969 return web_view->priv->open_proxy;
2972 void
2973 e_web_view_set_open_proxy (EWebView *web_view,
2974 GtkAction *open_proxy)
2976 g_return_if_fail (E_IS_WEB_VIEW (web_view));
2978 if (web_view->priv->open_proxy == open_proxy)
2979 return;
2981 if (open_proxy != NULL) {
2982 g_return_if_fail (GTK_IS_ACTION (open_proxy));
2983 g_object_ref (open_proxy);
2986 if (web_view->priv->open_proxy != NULL)
2987 g_object_unref (web_view->priv->open_proxy);
2989 web_view->priv->open_proxy = open_proxy;
2991 g_object_notify (G_OBJECT (web_view), "open-proxy");
2994 GtkTargetList *
2995 e_web_view_get_paste_target_list (EWebView *web_view)
2997 g_return_val_if_fail (E_IS_WEB_VIEW (web_view), NULL);
2999 /* FIXME WK2
3000 return webkit_web_view_get_paste_target_list (
3001 WEBKIT_WEB_VIEW (web_view)); */
3002 return NULL;
3005 GtkAction *
3006 e_web_view_get_print_proxy (EWebView *web_view)
3008 g_return_val_if_fail (E_IS_WEB_VIEW (web_view), NULL);
3010 return web_view->priv->print_proxy;
3013 void
3014 e_web_view_set_print_proxy (EWebView *web_view,
3015 GtkAction *print_proxy)
3017 g_return_if_fail (E_IS_WEB_VIEW (web_view));
3019 if (web_view->priv->print_proxy == print_proxy)
3020 return;
3022 if (print_proxy != NULL) {
3023 g_return_if_fail (GTK_IS_ACTION (print_proxy));
3024 g_object_ref (print_proxy);
3027 if (web_view->priv->print_proxy != NULL)
3028 g_object_unref (web_view->priv->print_proxy);
3030 web_view->priv->print_proxy = print_proxy;
3032 g_object_notify (G_OBJECT (web_view), "print-proxy");
3035 GtkAction *
3036 e_web_view_get_save_as_proxy (EWebView *web_view)
3038 g_return_val_if_fail (E_IS_WEB_VIEW (web_view), NULL);
3040 return web_view->priv->save_as_proxy;
3043 void
3044 e_web_view_set_save_as_proxy (EWebView *web_view,
3045 GtkAction *save_as_proxy)
3047 g_return_if_fail (E_IS_WEB_VIEW (web_view));
3049 if (web_view->priv->save_as_proxy == save_as_proxy)
3050 return;
3052 if (save_as_proxy != NULL) {
3053 g_return_if_fail (GTK_IS_ACTION (save_as_proxy));
3054 g_object_ref (save_as_proxy);
3057 if (web_view->priv->save_as_proxy != NULL)
3058 g_object_unref (web_view->priv->save_as_proxy);
3060 web_view->priv->save_as_proxy = save_as_proxy;
3062 g_object_notify (G_OBJECT (web_view), "save-as-proxy");
3065 void
3066 e_web_view_add_highlight (EWebView *web_view,
3067 const gchar *highlight)
3069 g_return_if_fail (E_IS_WEB_VIEW (web_view));
3070 g_return_if_fail (highlight && *highlight);
3072 g_queue_push_tail (
3073 &web_view->priv->highlights,
3074 g_strdup (highlight));
3076 webkit_find_controller_search (
3077 web_view->priv->find_controller,
3078 highlight,
3079 WEBKIT_FIND_OPTIONS_NONE,
3080 G_MAXUINT);
3083 void
3084 e_web_view_clear_highlights (EWebView *web_view)
3086 g_return_if_fail (E_IS_WEB_VIEW (web_view));
3088 webkit_find_controller_search_finish (web_view->priv->find_controller);
3090 while (!g_queue_is_empty (&web_view->priv->highlights))
3091 g_free (g_queue_pop_head (&web_view->priv->highlights));
3094 void
3095 e_web_view_update_highlights (EWebView *web_view)
3097 g_return_if_fail (E_IS_WEB_VIEW (web_view));
3099 web_view_update_document_highlights (web_view);
3102 GtkAction *
3103 e_web_view_get_action (EWebView *web_view,
3104 const gchar *action_name)
3106 GtkUIManager *ui_manager;
3108 g_return_val_if_fail (E_IS_WEB_VIEW (web_view), NULL);
3109 g_return_val_if_fail (action_name != NULL, NULL);
3111 ui_manager = e_web_view_get_ui_manager (web_view);
3113 return e_lookup_action (ui_manager, action_name);
3116 GtkActionGroup *
3117 e_web_view_get_action_group (EWebView *web_view,
3118 const gchar *group_name)
3120 GtkUIManager *ui_manager;
3122 g_return_val_if_fail (E_IS_WEB_VIEW (web_view), NULL);
3123 g_return_val_if_fail (group_name != NULL, NULL);
3125 ui_manager = e_web_view_get_ui_manager (web_view);
3127 return e_lookup_action_group (ui_manager, group_name);
3130 void
3131 e_web_view_copy_clipboard (EWebView *web_view)
3133 g_return_if_fail (E_IS_WEB_VIEW (web_view));
3135 webkit_web_view_execute_editing_command (
3136 WEBKIT_WEB_VIEW (web_view), WEBKIT_EDITING_COMMAND_COPY);
3139 void
3140 e_web_view_cut_clipboard (EWebView *web_view)
3142 g_return_if_fail (E_IS_WEB_VIEW (web_view));
3144 webkit_web_view_execute_editing_command (
3145 WEBKIT_WEB_VIEW (web_view), WEBKIT_EDITING_COMMAND_CUT);
3148 gboolean
3149 e_web_view_is_selection_active (EWebView *web_view)
3151 GDBusProxy *web_extension;
3153 g_return_val_if_fail (E_IS_WEB_VIEW (web_view), FALSE);
3155 web_extension = e_web_view_get_web_extension_proxy (web_view);
3156 if (web_extension) {
3157 GVariant *result;
3159 result = e_util_invoke_g_dbus_proxy_call_sync_wrapper_with_error_check (
3160 web_extension,
3161 "DocumentHasSelection",
3162 g_variant_new (
3163 "(t)",
3164 webkit_web_view_get_page_id (
3165 WEBKIT_WEB_VIEW (web_view))),
3166 NULL);
3168 if (result) {
3169 gboolean value = FALSE;
3171 g_variant_get (result, "(b)", &value);
3172 g_variant_unref (result);
3173 return value;
3177 return FALSE;
3180 void
3181 e_web_view_paste_clipboard (EWebView *web_view)
3183 g_return_if_fail (E_IS_WEB_VIEW (web_view));
3185 webkit_web_view_execute_editing_command (
3186 WEBKIT_WEB_VIEW (web_view), WEBKIT_EDITING_COMMAND_PASTE);
3189 gboolean
3190 e_web_view_scroll_forward (EWebView *web_view)
3192 g_return_val_if_fail (E_IS_WEB_VIEW (web_view), FALSE);
3193 /* FIXME WK2
3194 webkit_web_view_move_cursor (
3195 WEBKIT_WEB_VIEW (web_view), GTK_MOVEMENT_PAGES, 1);
3197 return TRUE; /* XXX This means nothing. */
3200 gboolean
3201 e_web_view_scroll_backward (EWebView *web_view)
3203 g_return_val_if_fail (E_IS_WEB_VIEW (web_view), FALSE);
3204 /* FIXME WK2
3205 webkit_web_view_move_cursor (
3206 WEBKIT_WEB_VIEW (web_view), GTK_MOVEMENT_PAGES, -1);
3208 return TRUE; /* XXX This means nothing. */
3211 void
3212 e_web_view_select_all (EWebView *web_view)
3214 g_return_if_fail (E_IS_WEB_VIEW (web_view));
3216 webkit_web_view_execute_editing_command (
3217 WEBKIT_WEB_VIEW (web_view), WEBKIT_EDITING_COMMAND_SELECT_ALL);
3220 void
3221 e_web_view_unselect_all (EWebView *web_view)
3223 #if 0 /* WEBKIT */
3224 g_return_if_fail (E_IS_WEB_VIEW (web_view));
3226 gtk_html_command (GTK_HTML (web_view), "unselect-all");
3227 #endif
3230 void
3231 e_web_view_zoom_100 (EWebView *web_view)
3233 g_return_if_fail (E_IS_WEB_VIEW (web_view));
3235 webkit_web_view_set_zoom_level (WEBKIT_WEB_VIEW (web_view), 1.0);
3238 void
3239 e_web_view_zoom_in (EWebView *web_view)
3241 gdouble zoom_level;
3243 g_return_if_fail (E_IS_WEB_VIEW (web_view));
3245 /* There is no webkit_web_view_zoom_in function in WK2, so emulate it */
3246 zoom_level = webkit_web_view_get_zoom_level (WEBKIT_WEB_VIEW (web_view));
3247 /* zoom-step in WK1 was 0.1 */
3248 zoom_level += 0.1;
3249 if (zoom_level < 4.9999)
3250 webkit_web_view_set_zoom_level (WEBKIT_WEB_VIEW (web_view), zoom_level);
3253 void
3254 e_web_view_zoom_out (EWebView *web_view)
3256 gdouble zoom_level;
3258 g_return_if_fail (E_IS_WEB_VIEW (web_view));
3260 /* There is no webkit_web_view_zoom_out function in WK2, so emulate it */
3261 zoom_level = webkit_web_view_get_zoom_level (WEBKIT_WEB_VIEW (web_view));
3262 /* zoom-step in WK1 was 0.1 */
3263 zoom_level -= 0.1;
3264 if (zoom_level > 0.7999)
3265 webkit_web_view_set_zoom_level (WEBKIT_WEB_VIEW (web_view), zoom_level);
3268 GtkUIManager *
3269 e_web_view_get_ui_manager (EWebView *web_view)
3271 g_return_val_if_fail (E_IS_WEB_VIEW (web_view), NULL);
3273 return web_view->priv->ui_manager;
3276 static void
3277 e_web_view_popup_menu_deactivate_cb (GtkMenu *popup_menu,
3278 GtkWidget *web_view)
3280 g_return_if_fail (GTK_IS_MENU (popup_menu));
3282 g_signal_handlers_disconnect_by_func (popup_menu, e_web_view_popup_menu_deactivate_cb, web_view);
3283 gtk_menu_detach (popup_menu);
3286 GtkWidget *
3287 e_web_view_get_popup_menu (EWebView *web_view)
3289 GtkUIManager *ui_manager;
3290 GtkWidget *menu;
3292 g_return_val_if_fail (E_IS_WEB_VIEW (web_view), NULL);
3294 ui_manager = e_web_view_get_ui_manager (web_view);
3295 menu = gtk_ui_manager_get_widget (ui_manager, "/context");
3296 g_return_val_if_fail (GTK_IS_MENU (menu), NULL);
3298 g_warn_if_fail (!gtk_menu_get_attach_widget (GTK_MENU (menu)));
3300 gtk_menu_attach_to_widget (GTK_MENU (menu),
3301 GTK_WIDGET (web_view),
3302 NULL);
3304 g_signal_connect (
3305 menu, "deactivate",
3306 G_CALLBACK (e_web_view_popup_menu_deactivate_cb), web_view);
3308 return menu;
3311 void
3312 e_web_view_show_popup_menu (EWebView *web_view,
3313 GdkEvent *event)
3315 GtkWidget *menu;
3317 g_return_if_fail (E_IS_WEB_VIEW (web_view));
3319 e_web_view_update_actions (web_view);
3321 menu = e_web_view_get_popup_menu (web_view);
3323 gtk_menu_popup_at_pointer (GTK_MENU (menu), event);
3327 * e_web_view_new_activity:
3328 * @web_view: an #EWebView
3330 * Returns a new #EActivity for an #EWebView-related asynchronous operation,
3331 * and emits the #EWebView::new-activity signal. By default the #EActivity
3332 * comes loaded with a #GCancellable and sets the @web_view itself as the
3333 * #EActivity:alert-sink (which means alerts are displayed directly in the
3334 * content area). The signal emission allows the #EActivity to be further
3335 * customized and/or tracked by the application.
3337 * Returns: an #EActivity
3339 EActivity *
3340 e_web_view_new_activity (EWebView *web_view)
3342 EActivity *activity;
3343 EAlertSink *alert_sink;
3344 GCancellable *cancellable;
3346 g_return_val_if_fail (E_IS_WEB_VIEW (web_view), NULL);
3348 activity = e_activity_new ();
3350 alert_sink = E_ALERT_SINK (web_view);
3351 e_activity_set_alert_sink (activity, alert_sink);
3353 cancellable = g_cancellable_new ();
3354 e_activity_set_cancellable (activity, cancellable);
3355 g_object_unref (cancellable);
3357 g_signal_emit (web_view, signals[NEW_ACTIVITY], 0, activity);
3359 return activity;
3362 void
3363 e_web_view_status_message (EWebView *web_view,
3364 const gchar *status_message)
3366 g_return_if_fail (E_IS_WEB_VIEW (web_view));
3368 g_signal_emit (web_view, signals[STATUS_MESSAGE], 0, status_message);
3371 void
3372 e_web_view_stop_loading (EWebView *web_view)
3374 g_return_if_fail (E_IS_WEB_VIEW (web_view));
3376 g_signal_emit (web_view, signals[STOP_LOADING], 0);
3379 void
3380 e_web_view_update_actions (EWebView *web_view)
3382 g_return_if_fail (E_IS_WEB_VIEW (web_view));
3384 g_signal_emit (web_view, signals[UPDATE_ACTIONS], 0);
3387 static void
3388 get_selection_content_html_cb (GObject *source_object,
3389 GAsyncResult *result,
3390 gpointer user_data)
3392 GDBusProxy *web_extension;
3393 GTask *task = user_data;
3394 GVariant *result_variant;
3395 gchar *html_content = NULL;
3396 GError *error = NULL;
3398 g_return_if_fail (G_IS_DBUS_PROXY (source_object));
3399 g_return_if_fail (G_IS_TASK (task));
3401 web_extension = G_DBUS_PROXY (source_object);
3403 result_variant = g_dbus_proxy_call_finish (web_extension, result, &error);
3404 if (result_variant)
3405 g_variant_get (result_variant, "(s)", &html_content);
3406 g_variant_unref (result_variant);
3408 g_task_return_pointer (task, html_content, g_free);
3409 g_object_unref (task);
3411 if (error)
3412 g_dbus_error_strip_remote_error (error);
3414 e_util_claim_dbus_proxy_call_error (web_extension, "GetSelectionContentHTML", error);
3415 g_clear_error (&error);
3418 void
3419 e_web_view_get_selection_content_html (EWebView *web_view,
3420 GCancellable *cancellable,
3421 GAsyncReadyCallback callback,
3422 gpointer user_data)
3424 GDBusProxy *web_extension;
3425 GTask *task;
3427 g_return_if_fail (E_IS_WEB_VIEW (web_view));
3429 task = g_task_new (web_view, cancellable, callback, user_data);
3431 web_extension = e_web_view_get_web_extension_proxy (web_view);
3432 if (web_extension) {
3433 g_dbus_proxy_call (
3434 web_extension,
3435 "GetSelectionContentHTML",
3436 g_variant_new (
3437 "(t)",
3438 webkit_web_view_get_page_id (
3439 WEBKIT_WEB_VIEW (web_view))),
3440 G_DBUS_CALL_FLAGS_NONE,
3442 cancellable,
3443 get_selection_content_html_cb,
3444 g_object_ref (task));
3445 } else
3446 g_task_return_pointer (task, NULL, NULL);
3449 gchar *
3450 e_web_view_get_selection_content_html_finish (EWebView *web_view,
3451 GAsyncResult *result,
3452 GError **error)
3454 g_return_val_if_fail (E_IS_WEB_VIEW (web_view), NULL);
3455 g_return_val_if_fail (g_task_is_valid (result, web_view), FALSE);
3457 return g_task_propagate_pointer (G_TASK (result), error);
3460 gchar *
3461 e_web_view_get_selection_content_html_sync (EWebView *web_view,
3462 GCancellable *cancellable,
3463 GError **error)
3465 GDBusProxy *web_extension;
3467 g_return_val_if_fail (E_IS_WEB_VIEW (web_view), NULL);
3469 web_extension = e_web_view_get_web_extension_proxy (web_view);
3470 if (web_extension) {
3471 GVariant *result;
3473 result = e_util_invoke_g_dbus_proxy_call_sync_wrapper_full (
3474 web_extension,
3475 "GetSelectionContentHTML",
3476 g_variant_new (
3477 "(t)",
3478 webkit_web_view_get_page_id (
3479 WEBKIT_WEB_VIEW (web_view))),
3480 G_DBUS_CALL_FLAGS_NONE,
3482 cancellable,
3483 error);
3485 if (result) {
3486 gchar *html_content = NULL;
3488 g_variant_get (result, "(s)", &html_content);
3489 g_variant_unref (result);
3490 return html_content;
3494 return NULL;
3497 const gchar *
3498 e_web_view_get_citation_color_for_level (gint level)
3500 /* Block quote border colors are borrowed from Thunderbird. */
3501 static const gchar *citation_color_levels[5] = {
3502 "rgb(233,185,110)", /* level 5 - Chocolate 1 */
3503 "rgb(114,159,207)", /* level 1 - Sky Blue 1 */
3504 "rgb(173,127,168)", /* level 2 - Plum 1 */
3505 "rgb(138,226,52)", /* level 3 - Chameleon 1 */
3506 "rgb(252,175,62)", /* level 4 - Orange 1 */
3509 g_return_val_if_fail (level > 0, citation_color_levels[1]);
3511 return citation_color_levels[level % 5];
3514 void
3515 e_web_view_update_fonts_settings (GSettings *font_settings,
3516 GSettings *aliasing_settings,
3517 PangoFontDescription *ms_font,
3518 PangoFontDescription *vw_font,
3519 GtkWidget *view_widget)
3521 gboolean clean_ms = FALSE, clean_vw = FALSE;
3522 gchar *aa = NULL;
3523 const gchar *styles[] = { "normal", "oblique", "italic" };
3524 const gchar *smoothing = NULL;
3525 GdkColor *link = NULL;
3526 GdkColor *visited = NULL;
3527 GString *stylesheet;
3528 GtkStyleContext *context;
3529 PangoFontDescription *min_size, *ms, *vw;
3530 WebKitSettings *wk_settings;
3531 WebKitUserContentManager *manager;
3532 WebKitUserStyleSheet *style_sheet;
3534 if (!ms_font) {
3535 gchar *font;
3537 font = g_settings_get_string (
3538 font_settings,
3539 "monospace-font-name");
3541 ms = pango_font_description_from_string (
3542 (font != NULL) ? font : "monospace 10");
3544 clean_ms = TRUE;
3546 g_free (font);
3547 } else
3548 ms = ms_font;
3550 if (!vw_font) {
3551 gchar *font;
3553 font = g_settings_get_string (
3554 font_settings,
3555 "font-name");
3557 vw = pango_font_description_from_string (
3558 (font != NULL) ? font : "serif 10");
3560 clean_vw = TRUE;
3562 g_free (font);
3563 } else
3564 vw = vw_font;
3566 if (pango_font_description_get_size (ms) < pango_font_description_get_size (vw))
3567 min_size = ms;
3568 else
3569 min_size = vw;
3571 stylesheet = g_string_new ("");
3572 g_string_append_printf (
3573 stylesheet,
3574 "body {\n"
3575 " font-family: '%s';\n"
3576 " font-size: %dpt;\n"
3577 " font-weight: %d;\n"
3578 " font-style: %s;\n",
3579 pango_font_description_get_family (vw),
3580 pango_font_description_get_size (vw) / PANGO_SCALE,
3581 pango_font_description_get_weight (vw),
3582 styles[pango_font_description_get_style (vw)]);
3584 if (aliasing_settings != NULL)
3585 aa = g_settings_get_string (
3586 aliasing_settings, "antialiasing");
3588 if (g_strcmp0 (aa, "none") == 0)
3589 smoothing = "none";
3590 else if (g_strcmp0 (aa, "grayscale") == 0)
3591 smoothing = "antialiased";
3592 else if (g_strcmp0 (aa, "rgba") == 0)
3593 smoothing = "subpixel-antialiased";
3595 if (smoothing != NULL)
3596 g_string_append_printf (
3597 stylesheet,
3598 " -webkit-font-smoothing: %s;\n",
3599 smoothing);
3601 g_free (aa);
3603 g_string_append (stylesheet, "}\n");
3605 g_string_append_printf (
3606 stylesheet,
3607 "pre,code,.pre {\n"
3608 " font-family: '%s';\n"
3609 " font-size: %dpt;\n"
3610 " font-weight: %d;\n"
3611 " font-style: %s;\n"
3612 " margin: 0px;\n"
3613 "}\n",
3614 pango_font_description_get_family (ms),
3615 pango_font_description_get_size (ms) / PANGO_SCALE,
3616 pango_font_description_get_weight (ms),
3617 styles[pango_font_description_get_style (ms)]);
3619 if (view_widget) {
3620 context = gtk_widget_get_style_context (view_widget);
3621 gtk_style_context_get_style (
3622 context,
3623 "link-color", &link,
3624 "visited-link-color", &visited,
3625 NULL);
3627 if (link == NULL) {
3628 GdkRGBA rgba;
3629 GtkStateFlags state;
3631 link = g_slice_new0 (GdkColor);
3632 link->blue = G_MAXINT16;
3634 rgba.alpha = 1;
3635 rgba.red = 0;
3636 rgba.green = 0;
3637 rgba.blue = 1;
3639 state = gtk_style_context_get_state (context);
3640 state = state & (~(GTK_STATE_FLAG_VISITED | GTK_STATE_FLAG_LINK));
3641 state = state | GTK_STATE_FLAG_LINK;
3643 gtk_style_context_save (context);
3644 gtk_style_context_set_state (context, state);
3645 gtk_style_context_get_color (context, state, &rgba);
3646 gtk_style_context_restore (context);
3648 e_rgba_to_color (&rgba, link);
3651 if (visited == NULL) {
3652 GdkRGBA rgba;
3653 GtkStateFlags state;
3655 visited = g_slice_new0 (GdkColor);
3656 visited->red = G_MAXINT16;
3658 rgba.alpha = 1;
3659 rgba.red = 1;
3660 rgba.green = 0;
3661 rgba.blue = 0;
3663 state = gtk_style_context_get_state (context);
3664 state = state & (~(GTK_STATE_FLAG_VISITED | GTK_STATE_FLAG_LINK));
3665 state = state | GTK_STATE_FLAG_VISITED;
3667 gtk_style_context_save (context);
3668 gtk_style_context_set_state (context, state);
3669 gtk_style_context_get_color (context, state, &rgba);
3670 gtk_style_context_restore (context);
3672 e_rgba_to_color (&rgba, visited);
3675 g_string_append_printf (
3676 stylesheet,
3677 "a {\n"
3678 " color: #%06x;\n"
3679 "}\n"
3680 "a:visited {\n"
3681 " color: #%06x;\n"
3682 "}\n",
3683 e_color_to_value (link),
3684 e_color_to_value (visited));
3686 gdk_color_free (link);
3687 gdk_color_free (visited);
3689 g_string_append (
3690 stylesheet,
3691 "blockquote[type=cite]:not(.-x-evo-plaintext-quoted) "
3692 "{\n"
3693 " padding: 0ch 1ch 0ch 1ch;\n"
3694 " margin: 0ch;\n"
3695 " border-width: 0px 2px 0px 2px;\n"
3696 " border-style: none solid none solid;\n"
3697 " border-radius: 2px;\n"
3698 "}\n");
3700 g_string_append_printf (
3701 stylesheet,
3702 "blockquote[type=cite]:not(.-x-evo-plaintext-quoted) "
3703 "{\n"
3704 " border-color: %s;\n"
3705 "}\n",
3706 e_web_view_get_citation_color_for_level (1));
3708 g_string_append_printf (
3709 stylesheet,
3710 "blockquote[type=cite]:not(.-x-evo-plaintext-quoted) "
3711 "blockquote[type=cite]:not(.-x-evo-plaintext-quoted) "
3712 "{\n"
3713 " border-color: %s;\n"
3714 "}\n",
3715 e_web_view_get_citation_color_for_level (2));
3717 g_string_append_printf (
3718 stylesheet,
3719 "blockquote[type=cite]:not(.-x-evo-plaintext-quoted) "
3720 "blockquote[type=cite]:not(.-x-evo-plaintext-quoted) "
3721 "blockquote[type=cite]:not(.-x-evo-plaintext-quoted) "
3722 "{\n"
3723 " border-color: %s;\n"
3724 "}\n",
3725 e_web_view_get_citation_color_for_level (3));
3727 g_string_append_printf (
3728 stylesheet,
3729 "blockquote[type=cite]:not(.-x-evo-plaintext-quoted) "
3730 "blockquote[type=cite]:not(.-x-evo-plaintext-quoted) "
3731 "blockquote[type=cite]:not(.-x-evo-plaintext-quoted) "
3732 "blockquote[type=cite]:not(.-x-evo-plaintext-quoted) "
3733 "{\n"
3734 " border-color: %s;\n"
3735 "}\n",
3736 e_web_view_get_citation_color_for_level (4));
3738 g_string_append_printf (
3739 stylesheet,
3740 "blockquote[type=cite]:not(.-x-evo-plaintext-quoted) "
3741 "blockquote[type=cite]:not(.-x-evo-plaintext-quoted) "
3742 "blockquote[type=cite]:not(.-x-evo-plaintext-quoted) "
3743 "blockquote[type=cite]:not(.-x-evo-plaintext-quoted) "
3744 "blockquote[type=cite]:not(.-x-evo-plaintext-quoted) "
3745 "{\n"
3746 " border-color: %s;\n"
3747 "}\n",
3748 e_web_view_get_citation_color_for_level (5));
3751 wk_settings = webkit_web_view_get_settings (WEBKIT_WEB_VIEW (view_widget));
3753 g_object_set (
3754 wk_settings,
3755 "default-font-size",
3756 e_util_normalize_font_size (
3757 view_widget, pango_font_description_get_size (vw) / PANGO_SCALE),
3758 "default-font-family",
3759 pango_font_description_get_family (vw),
3760 "monospace-font-family",
3761 pango_font_description_get_family (ms),
3762 "default-monospace-font-size",
3763 e_util_normalize_font_size (
3764 view_widget, pango_font_description_get_size (ms) / PANGO_SCALE),
3765 "minimum-font-size",
3766 e_util_normalize_font_size (
3767 view_widget, pango_font_description_get_size (min_size) / PANGO_SCALE),
3768 NULL);
3770 manager = webkit_web_view_get_user_content_manager (WEBKIT_WEB_VIEW (view_widget));
3771 webkit_user_content_manager_remove_all_style_sheets (manager);
3773 style_sheet = webkit_user_style_sheet_new (
3774 stylesheet->str,
3775 WEBKIT_USER_CONTENT_INJECT_ALL_FRAMES,
3776 WEBKIT_USER_STYLE_LEVEL_USER,
3777 NULL,
3778 NULL);
3780 webkit_user_content_manager_add_style_sheet (manager, style_sheet);
3782 webkit_user_style_sheet_unref (style_sheet);
3784 g_string_free (stylesheet, TRUE);
3786 if (clean_ms)
3787 pango_font_description_free (ms);
3788 if (clean_vw)
3789 pango_font_description_free (vw);
3792 WebKitSettings *
3793 e_web_view_get_default_webkit_settings (void)
3795 return webkit_settings_new_with_settings (
3796 "auto-load-images", TRUE,
3797 "default-charset", "utf-8",
3798 "enable-html5-database", FALSE,
3799 "enable-dns-prefetching", FALSE,
3800 "enable-html5-local-storage", FALSE,
3801 "enable-java", FALSE,
3802 "enable-javascript", FALSE,
3803 "enable-offline-web-application-cache", FALSE,
3804 "enable-page-cache", FALSE,
3805 "enable-plugins", FALSE,
3806 "enable-smooth-scrolling", FALSE,
3807 "media-playback-allows-inline", FALSE,
3808 NULL);
3811 void
3812 e_web_view_update_fonts (EWebView *web_view)
3814 EWebViewClass *class;
3815 PangoFontDescription *ms = NULL, *vw = NULL;
3817 g_return_if_fail (E_IS_WEB_VIEW (web_view));
3819 class = E_WEB_VIEW_GET_CLASS (web_view);
3820 if (class->set_fonts != NULL)
3821 class->set_fonts (web_view, &ms, &vw);
3823 e_web_view_update_fonts_settings (
3824 web_view->priv->font_settings,
3825 web_view->priv->aliasing_settings,
3826 ms, vw, GTK_WIDGET (web_view));
3828 pango_font_description_free (ms);
3829 pango_font_description_free (vw);
3832 /* Helper for e_web_view_cursor_image_copy() */
3833 static void
3834 web_view_cursor_image_copy_pixbuf_cb (GObject *source_object,
3835 GAsyncResult *result,
3836 gpointer user_data)
3838 EActivity *activity;
3839 EAlertSink *alert_sink;
3840 GdkPixbuf *pixbuf;
3841 GError *local_error = NULL;
3843 activity = E_ACTIVITY (user_data);
3844 alert_sink = e_activity_get_alert_sink (activity);
3846 pixbuf = gdk_pixbuf_new_from_stream_finish (result, &local_error);
3848 /* Sanity check. */
3849 g_return_if_fail (
3850 ((pixbuf != NULL) && (local_error == NULL)) ||
3851 ((pixbuf == NULL) && (local_error != NULL)));
3853 if (e_activity_handle_cancellation (activity, local_error)) {
3854 g_error_free (local_error);
3856 } else if (local_error != NULL) {
3857 e_alert_submit (
3858 alert_sink,
3859 "widgets:no-image-copy",
3860 local_error->message, NULL);
3861 g_error_free (local_error);
3863 } else {
3864 GtkClipboard *clipboard;
3866 clipboard = gtk_clipboard_get (GDK_SELECTION_CLIPBOARD);
3867 gtk_clipboard_set_image (clipboard, pixbuf);
3868 gtk_clipboard_store (clipboard);
3870 e_activity_set_state (activity, E_ACTIVITY_COMPLETED);
3873 g_clear_object (&activity);
3874 g_clear_object (&pixbuf);
3877 /* Helper for e_web_view_cursor_image_copy() */
3878 static void
3879 web_view_cursor_image_copy_request_cb (GObject *source_object,
3880 GAsyncResult *result,
3881 gpointer user_data)
3883 EActivity *activity;
3884 EAlertSink *alert_sink;
3885 GCancellable *cancellable;
3886 GInputStream *input_stream;
3887 GError *local_error = NULL;
3889 activity = E_ACTIVITY (user_data);
3890 alert_sink = e_activity_get_alert_sink (activity);
3891 cancellable = e_activity_get_cancellable (activity);
3893 input_stream = e_web_view_request_finish (
3894 E_WEB_VIEW (source_object), result, &local_error);
3896 /* Sanity check. */
3897 g_return_if_fail (
3898 ((input_stream != NULL) && (local_error == NULL)) ||
3899 ((input_stream == NULL) && (local_error != NULL)));
3901 if (e_activity_handle_cancellation (activity, local_error)) {
3902 g_error_free (local_error);
3904 } else if (local_error != NULL) {
3905 e_alert_submit (
3906 alert_sink,
3907 "widgets:no-image-copy",
3908 local_error->message, NULL);
3909 g_error_free (local_error);
3911 } else {
3912 gdk_pixbuf_new_from_stream_async (
3913 input_stream, cancellable,
3914 web_view_cursor_image_copy_pixbuf_cb,
3915 g_object_ref (activity));
3918 g_clear_object (&activity);
3919 g_clear_object (&input_stream);
3923 * e_web_view_cursor_image_copy:
3924 * @web_view: an #EWebView
3926 * Asynchronously copies the image under the cursor to the clipboard.
3928 * This function triggers a #EWebView::new-activity signal emission so
3929 * the asynchronous operation can be tracked and/or cancelled.
3931 void
3932 e_web_view_cursor_image_copy (EWebView *web_view)
3934 g_return_if_fail (E_IS_WEB_VIEW (web_view));
3936 if (web_view->priv->cursor_image_src != NULL) {
3937 EActivity *activity;
3938 GCancellable *cancellable;
3939 const gchar *text;
3941 activity = e_web_view_new_activity (web_view);
3942 cancellable = e_activity_get_cancellable (activity);
3944 text = _("Copying image to clipboard");
3945 e_activity_set_text (activity, text);
3947 e_web_view_request (
3948 web_view,
3949 web_view->priv->cursor_image_src,
3950 cancellable,
3951 web_view_cursor_image_copy_request_cb,
3952 g_object_ref (activity));
3954 g_object_unref (activity);
3958 /* Helper for e_web_view_cursor_image_save() */
3959 static void
3960 web_view_cursor_image_save_splice_cb (GObject *source_object,
3961 GAsyncResult *result,
3962 gpointer user_data)
3964 EActivity *activity;
3965 EAlertSink *alert_sink;
3966 AsyncContext *async_context;
3967 GError *local_error = NULL;
3969 async_context = (AsyncContext *) user_data;
3971 activity = async_context->activity;
3972 alert_sink = e_activity_get_alert_sink (activity);
3974 g_output_stream_splice_finish (
3975 G_OUTPUT_STREAM (source_object), result, &local_error);
3977 if (e_activity_handle_cancellation (activity, local_error)) {
3978 g_error_free (local_error);
3980 } else if (local_error != NULL) {
3981 e_alert_submit (
3982 alert_sink,
3983 "widgets:no-image-save",
3984 local_error->message, NULL);
3985 g_error_free (local_error);
3987 } else {
3988 e_activity_set_state (activity, E_ACTIVITY_COMPLETED);
3991 async_context_free (async_context);
3994 /* Helper for e_web_view_cursor_image_save() */
3995 static void
3996 web_view_cursor_image_save_replace_cb (GObject *source_object,
3997 GAsyncResult *result,
3998 gpointer user_data)
4000 EActivity *activity;
4001 EAlertSink *alert_sink;
4002 GCancellable *cancellable;
4003 GFileOutputStream *output_stream;
4004 AsyncContext *async_context;
4005 GError *local_error = NULL;
4007 async_context = (AsyncContext *) user_data;
4009 activity = async_context->activity;
4010 alert_sink = e_activity_get_alert_sink (activity);
4011 cancellable = e_activity_get_cancellable (activity);
4013 output_stream = g_file_replace_finish (
4014 G_FILE (source_object), result, &local_error);
4016 /* Sanity check. */
4017 g_return_if_fail (
4018 ((output_stream != NULL) && (local_error == NULL)) ||
4019 ((output_stream == NULL) && (local_error != NULL)));
4021 if (e_activity_handle_cancellation (activity, local_error)) {
4022 g_error_free (local_error);
4023 async_context_free (async_context);
4025 } else if (local_error != NULL) {
4026 e_alert_submit (
4027 alert_sink,
4028 "widgets:no-image-save",
4029 local_error->message, NULL);
4030 g_error_free (local_error);
4031 async_context_free (async_context);
4033 } else {
4034 g_output_stream_splice_async (
4035 G_OUTPUT_STREAM (output_stream),
4036 async_context->input_stream,
4037 G_OUTPUT_STREAM_SPLICE_CLOSE_SOURCE |
4038 G_OUTPUT_STREAM_SPLICE_CLOSE_TARGET,
4039 G_PRIORITY_DEFAULT,
4040 cancellable,
4041 web_view_cursor_image_save_splice_cb,
4042 async_context);
4045 g_clear_object (&output_stream);
4048 /* Helper for e_web_view_cursor_image_save() */
4049 static void
4050 web_view_cursor_image_save_request_cb (GObject *source_object,
4051 GAsyncResult *result,
4052 gpointer user_data)
4054 EActivity *activity;
4055 EAlertSink *alert_sink;
4056 GCancellable *cancellable;
4057 GInputStream *input_stream;
4058 AsyncContext *async_context;
4059 GError *local_error = NULL;
4061 async_context = (AsyncContext *) user_data;
4063 activity = async_context->activity;
4064 alert_sink = e_activity_get_alert_sink (activity);
4065 cancellable = e_activity_get_cancellable (activity);
4067 input_stream = e_web_view_request_finish (
4068 E_WEB_VIEW (source_object), result, &local_error);
4070 /* Sanity check. */
4071 g_return_if_fail (
4072 ((input_stream != NULL) && (local_error == NULL)) ||
4073 ((input_stream == NULL) && (local_error != NULL)));
4075 if (e_activity_handle_cancellation (activity, local_error)) {
4076 g_error_free (local_error);
4077 async_context_free (async_context);
4079 } else if (local_error != NULL) {
4080 e_alert_submit (
4081 alert_sink,
4082 "widgets:no-image-save",
4083 local_error->message, NULL);
4084 g_error_free (local_error);
4085 async_context_free (async_context);
4087 } else {
4088 async_context->input_stream = g_object_ref (input_stream);
4090 /* Open an output stream to the destination file. */
4091 g_file_replace_async (
4092 async_context->destination,
4093 NULL, FALSE,
4094 G_FILE_CREATE_REPLACE_DESTINATION,
4095 G_PRIORITY_DEFAULT,
4096 cancellable,
4097 web_view_cursor_image_save_replace_cb,
4098 async_context);
4101 g_clear_object (&input_stream);
4105 * e_web_view_cursor_image_save:
4106 * @web_view: an #EWebView
4108 * Prompts the user to choose a destination file and then asynchronously
4109 * saves the image under the cursor to the destination file.
4111 * This function triggers a #EWebView::new-activity signal emission so
4112 * the asynchronous operation can be tracked and/or cancelled.
4114 void
4115 e_web_view_cursor_image_save (EWebView *web_view)
4117 GtkFileChooser *file_chooser;
4118 GFile *destination = NULL;
4119 GtkWidget *dialog;
4120 gchar *suggestion;
4121 gpointer toplevel;
4123 g_return_if_fail (E_IS_WEB_VIEW (web_view));
4125 if (web_view->priv->cursor_image_src == NULL)
4126 return;
4128 toplevel = gtk_widget_get_toplevel (GTK_WIDGET (web_view));
4129 toplevel = gtk_widget_is_toplevel (toplevel) ? toplevel : NULL;
4131 dialog = gtk_file_chooser_dialog_new (
4132 _("Save Image"), toplevel,
4133 GTK_FILE_CHOOSER_ACTION_SAVE,
4134 _("_Cancel"), GTK_RESPONSE_CANCEL,
4135 _("_Save"), GTK_RESPONSE_ACCEPT, NULL);
4137 gtk_dialog_set_default_response (
4138 GTK_DIALOG (dialog), GTK_RESPONSE_ACCEPT);
4140 file_chooser = GTK_FILE_CHOOSER (dialog);
4141 gtk_file_chooser_set_local_only (file_chooser, FALSE);
4142 gtk_file_chooser_set_do_overwrite_confirmation (file_chooser, TRUE);
4144 suggestion = e_web_view_suggest_filename (
4145 web_view, web_view->priv->cursor_image_src);
4147 if (suggestion != NULL) {
4148 gtk_file_chooser_set_current_name (file_chooser, suggestion);
4149 g_free (suggestion);
4152 e_util_load_file_chooser_folder (file_chooser);
4154 if (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_ACCEPT) {
4155 e_util_save_file_chooser_folder (file_chooser);
4157 destination = gtk_file_chooser_get_file (file_chooser);
4160 gtk_widget_destroy (dialog);
4162 if (destination != NULL) {
4163 EActivity *activity;
4164 GCancellable *cancellable;
4165 AsyncContext *async_context;
4166 gchar *text;
4167 gchar *uri;
4169 activity = e_web_view_new_activity (web_view);
4170 cancellable = e_activity_get_cancellable (activity);
4172 uri = g_file_get_uri (destination);
4173 text = g_strdup_printf (_("Saving image to “%s”"), uri);
4174 e_activity_set_text (activity, text);
4175 g_free (text);
4176 g_free (uri);
4178 async_context = g_slice_new0 (AsyncContext);
4179 async_context->activity = g_object_ref (activity);
4180 async_context->destination = g_object_ref (destination);
4182 e_web_view_request (
4183 web_view,
4184 web_view->priv->cursor_image_src,
4185 cancellable,
4186 web_view_cursor_image_save_request_cb,
4187 async_context);
4189 g_object_unref (activity);
4190 g_object_unref (destination);
4194 /* Helper for e_web_view_request() */
4195 static void
4196 web_view_request_process_thread (GTask *task,
4197 gpointer source_object,
4198 gpointer task_data,
4199 GCancellable *cancellable)
4201 AsyncContext *async_context = task_data;
4202 gint64 stream_length = -1;
4203 gchar *mime_type = NULL;
4204 GError *local_error = NULL;
4206 if (!e_content_request_process_sync (async_context->content_request,
4207 async_context->uri, source_object, &async_context->input_stream,
4208 &stream_length, &mime_type, cancellable, &local_error)) {
4209 g_task_return_error (task, local_error);
4210 } else {
4211 g_task_return_boolean (task, TRUE);
4214 g_free (mime_type);
4218 * e_web_view_request:
4219 * @web_view: an #EWebView
4220 * @uri: the URI to load
4221 * @cancellable: optional #GCancellable object, or %NULL
4222 * @callback: a #GAsyncReadyCallback to call when the request is satisfied
4223 * @user_data: data to pass to the callback function
4225 * Asynchronously requests data at @uri as displaed in the @web_view.
4227 * When the operation is finished, @callback will be called. You can then
4228 * call e_web_view_request_finish() to get the result of the operation.
4230 void
4231 e_web_view_request (EWebView *web_view,
4232 const gchar *uri,
4233 GCancellable *cancellable,
4234 GAsyncReadyCallback callback,
4235 gpointer user_data)
4237 EContentRequest *content_request = NULL;
4238 AsyncContext *async_context;
4239 GSList *link;
4240 GTask *task;
4242 g_return_if_fail (E_IS_WEB_VIEW (web_view));
4243 g_return_if_fail (uri != NULL);
4245 for (link = web_view->priv->content_requests; link; link = g_slist_next (link)) {
4246 EContentRequest *adept = link->data;
4248 if (!E_IS_CONTENT_REQUEST (adept) ||
4249 !e_content_request_can_process_uri (adept, uri))
4250 continue;
4252 content_request = adept;
4253 break;
4256 async_context = g_slice_new0 (AsyncContext);
4257 async_context->uri = g_strdup (uri);
4259 task = g_task_new (web_view, cancellable, callback, user_data);
4260 g_task_set_task_data (task, async_context, async_context_free);
4261 g_task_set_check_cancellable (task, TRUE);
4263 if (content_request) {
4264 async_context->content_request = g_object_ref (content_request);
4265 g_task_run_in_thread (task, web_view_request_process_thread);
4266 } else {
4267 g_task_return_new_error (task, G_IO_ERROR, G_IO_ERROR_FAILED,
4268 _("Cannot get URI “%s”, do not know how to download it."), uri);
4271 g_object_unref (task);
4275 * e_web_view_request_finish:
4276 * @web_view: an #EWebView
4277 * @result: a #GAsyncResult
4278 * @error: return location for a #GError, or %NULL
4280 * Finishes the operation started with e_web_view_request().
4282 * Unreference the returned #GInputStream with g_object_unref() when finished
4283 * with it. If an error occurred, the function will set @error and return
4284 * %NULL.
4286 * Returns: a #GInputStream, or %NULL
4288 GInputStream *
4289 e_web_view_request_finish (EWebView *web_view,
4290 GAsyncResult *result,
4291 GError **error)
4293 AsyncContext *async_context;
4295 g_return_val_if_fail (g_task_is_valid (result, web_view), NULL);
4297 if (!g_task_propagate_boolean (G_TASK (result), error))
4298 return NULL;
4300 async_context = g_task_get_task_data (G_TASK (result));
4302 g_return_val_if_fail (async_context->input_stream != NULL, NULL);
4304 return g_object_ref (async_context->input_stream);
4308 * e_web_view_create_and_add_css_style_sheet:
4309 * @web_view: an #EWebView
4310 * @style_sheet_id: CSS style sheet's id
4312 * Creates new CSS style sheet with given @style_sheel_id and inserts
4313 * it into given @web_view document.
4315 void
4316 e_web_view_create_and_add_css_style_sheet (EWebView *web_view,
4317 const gchar *style_sheet_id)
4319 GDBusProxy *web_extension;
4321 g_return_if_fail (E_IS_WEB_VIEW (web_view));
4322 g_return_if_fail (style_sheet_id && *style_sheet_id);
4324 web_extension = e_web_view_get_web_extension_proxy (web_view);
4325 if (web_extension) {
4326 e_util_invoke_g_dbus_proxy_call_with_error_check (
4327 web_extension,
4328 "CreateAndAddCSSStyleSheet",
4329 g_variant_new (
4330 "(ts)",
4331 webkit_web_view_get_page_id (
4332 WEBKIT_WEB_VIEW (web_view)),
4333 style_sheet_id),
4334 NULL);
4339 * e_web_view_add_css_rule_into_style_sheet:
4340 * @web_view: an #EWebView
4341 * @style_sheet_id: CSS style sheet's id
4342 * @selector: CSS selector
4343 * @style: style for given selector
4345 * Insert new CSS rule (defined with @selector and @style) into CSS style sheet
4346 * with given @style_sheet_id. If style sheet doesn't exist, it's created.
4348 * The rule is inserted to every DOM document that is in page. That means also
4349 * into DOM documents inside iframe elements.
4351 void
4352 e_web_view_add_css_rule_into_style_sheet (EWebView *web_view,
4353 const gchar *style_sheet_id,
4354 const gchar *selector,
4355 const gchar *style)
4357 GDBusProxy *web_extension;
4359 g_return_if_fail (E_IS_WEB_VIEW (web_view));
4360 g_return_if_fail (style_sheet_id && *style_sheet_id);
4361 g_return_if_fail (selector && *selector);
4362 g_return_if_fail (style && *style);
4364 web_extension = e_web_view_get_web_extension_proxy (web_view);
4365 if (web_extension) {
4366 e_util_invoke_g_dbus_proxy_call_with_error_check (
4367 web_extension,
4368 "AddCSSRuleIntoStyleSheet",
4369 g_variant_new (
4370 "(tsss)",
4371 webkit_web_view_get_page_id (
4372 WEBKIT_WEB_VIEW (web_view)),
4373 style_sheet_id,
4374 selector,
4375 style),
4376 NULL);
4381 * e_web_view_get_document_uri_from_point:
4382 * @web_view: an #EWebView
4383 * @x: x-coordinate
4384 * @y: y-coordinate
4386 * Returns: A document URI which is under the @x, @y coordinates or %NULL,
4387 * if there is none. Free the returned pointer with g_free() when done with it.
4389 * Since: 3.22
4391 gchar *
4392 e_web_view_get_document_uri_from_point (EWebView *web_view,
4393 gint32 x,
4394 gint32 y)
4396 GDBusProxy *web_extension;
4397 GVariant *result;
4398 GError *local_error = NULL;
4400 g_return_val_if_fail (E_IS_WEB_VIEW (web_view), NULL);
4402 web_extension = e_web_view_get_web_extension_proxy (web_view);
4403 if (!web_extension)
4404 return NULL;
4406 result = e_util_invoke_g_dbus_proxy_call_sync_wrapper_full (
4407 web_extension,
4408 "GetDocumentURIFromPoint",
4409 g_variant_new (
4410 "(tii)",
4411 webkit_web_view_get_page_id (WEBKIT_WEB_VIEW (web_view)),
4414 G_DBUS_CALL_FLAGS_NONE,
4416 NULL,
4417 &local_error);
4419 if (local_error)
4420 g_dbus_error_strip_remote_error (local_error);
4422 e_util_claim_dbus_proxy_call_error (web_extension, "GetDocumentURIFromPoint", local_error);
4423 g_clear_error (&local_error);
4425 if (result) {
4426 gchar *uri = NULL;
4428 g_variant_get (result, "(s)", &uri);
4429 g_variant_unref (result);
4431 if (g_strcmp0 (uri, "") == 0) {
4432 g_free (uri);
4433 uri = NULL;
4436 return uri;
4439 return NULL;
4443 * e_web_view_set_document_iframe_src:
4444 * @web_view: an #EWebView
4445 * @document_uri: a document URI for whose IFrame change the source
4446 * @new_iframe_src: the source to change the IFrame to
4448 * Change IFrame source for the given @document_uri IFrame
4449 * to the @new_iframe_src.
4451 * Since: 3.22
4453 void
4454 e_web_view_set_document_iframe_src (EWebView *web_view,
4455 const gchar *document_uri,
4456 const gchar *new_iframe_src)
4458 GDBusProxy *web_extension;
4460 g_return_if_fail (E_IS_WEB_VIEW (web_view));
4462 web_extension = e_web_view_get_web_extension_proxy (web_view);
4463 if (!web_extension)
4464 return;
4466 /* Cannot call this synchronously, blocking the local main loop, because the reload
4467 can on the WebProcess side can be asking for a redirection policy, waiting
4468 for a response which may be waiting in the blocked main loop. */
4469 e_util_invoke_g_dbus_proxy_call_with_error_check (
4470 web_extension,
4471 "SetDocumentIFrameSrc",
4472 g_variant_new (
4473 "(tss)",
4474 webkit_web_view_get_page_id (WEBKIT_WEB_VIEW (web_view)),
4475 document_uri,
4476 new_iframe_src),
4477 NULL);
4481 * EWebViewElementClickedFunc:
4482 * @web_view: an #EWebView
4483 * @element_class: an element class, as set on the element which had been clicked
4484 * @element_value: a 'value' attribute content of the clicked element
4485 * @element_position: a #GtkAllocation with the position of the clicked element
4486 * @user_data: user data as provided in the e_web_view_register_element_clicked() call
4488 * The callback is called whenever an element of class @element_class is clicked.
4489 * The @element_value is a content of the 'value' attribute of the clicked element.
4490 * The @element_position is the place of the element within the web page, already
4491 * accounting scrollbar positions.
4493 * See: e_web_view_register_element_clicked, e_web_view_unregister_element_clicked
4495 * Since: 3.22
4499 * e_web_view_register_element_clicked:
4500 * @web_view: an #EWebView
4501 * @element_class: an element class on which to listen for clicking
4502 * @callback: an #EWebViewElementClickedFunc to call, when the element is clicked
4503 * @user_data: user data to pass to @callback
4505 * Registers a @callback to be called when any element of the class @element_class
4506 * is clicked. If the element contains a 'value' attribute, then it is passed to
4507 * the @callback too. These callback are valid until a new content of the @web_view
4508 * is loaded, after which all the registered callbacks are forgotten.
4510 * Since: 3.22
4512 void
4513 e_web_view_register_element_clicked (EWebView *web_view,
4514 const gchar *element_class,
4515 EWebViewElementClickedFunc callback,
4516 gpointer user_data)
4518 ElementClickedData *ecd;
4519 GPtrArray *cbs;
4520 guint ii;
4522 g_return_if_fail (E_IS_WEB_VIEW (web_view));
4523 g_return_if_fail (element_class != NULL);
4524 g_return_if_fail (callback != NULL);
4526 cbs = g_hash_table_lookup (web_view->priv->element_clicked_cbs, element_class);
4527 if (cbs) {
4528 for (ii = 0; ii < cbs->len; ii++) {
4529 ecd = g_ptr_array_index (cbs, ii);
4531 if (ecd && ecd->callback == callback && ecd->user_data == user_data) {
4532 /* Callback is already registered, but re-register it, in case the page
4533 was changed dynamically and new elements with the given call are added. */
4534 web_view_register_element_clicked_hfunc ((gpointer) element_class, cbs, web_view);
4535 return;
4540 ecd = g_new0 (ElementClickedData, 1);
4541 ecd->callback = callback;
4542 ecd->user_data = user_data;
4544 if (!cbs) {
4545 cbs = g_ptr_array_new_full (1, g_free);
4546 g_ptr_array_add (cbs, ecd);
4548 g_hash_table_insert (web_view->priv->element_clicked_cbs, g_strdup (element_class), cbs);
4549 } else {
4550 g_ptr_array_add (cbs, ecd);
4553 /* Dynamically changing page can call this multiple times; re-register all classes */
4554 g_hash_table_foreach (web_view->priv->element_clicked_cbs, web_view_register_element_clicked_hfunc, web_view);
4558 * e_web_view_unregister_element_clicked:
4559 * @web_view: an #EWebView
4560 * @element_class: an element class on which to listen for clicking
4561 * @callback: an #EWebViewElementClickedFunc to call, when the element is clicked
4562 * @user_data: user data to pass to @callback
4564 * Unregisters the @callback for the @element_class with the given @user_data, which
4565 * should be previously registered with e_web_view_register_element_clicked(). This
4566 * unregister is usually not needed, because the @web_view unregisters all callbacks
4567 * when a new content is loaded.
4569 * Since: 3.22
4571 void
4572 e_web_view_unregister_element_clicked (EWebView *web_view,
4573 const gchar *element_class,
4574 EWebViewElementClickedFunc callback,
4575 gpointer user_data)
4577 ElementClickedData *ecd;
4578 GPtrArray *cbs;
4579 guint ii;
4581 g_return_if_fail (E_IS_WEB_VIEW (web_view));
4582 g_return_if_fail (element_class != NULL);
4583 g_return_if_fail (callback != NULL);
4585 cbs = g_hash_table_lookup (web_view->priv->element_clicked_cbs, element_class);
4586 if (!cbs)
4587 return;
4589 for (ii = 0; ii < cbs->len; ii++) {
4590 ecd = g_ptr_array_index (cbs, ii);
4592 if (ecd && ecd->callback == callback && ecd->user_data == user_data) {
4593 g_ptr_array_remove (cbs, ecd);
4594 if (!cbs->len)
4595 g_hash_table_remove (web_view->priv->element_clicked_cbs, element_class);
4596 break;
4601 void
4602 e_web_view_set_element_hidden (EWebView *web_view,
4603 const gchar *element_id,
4604 gboolean hidden)
4606 GDBusProxy *web_extension;
4608 g_return_if_fail (E_IS_WEB_VIEW (web_view));
4609 g_return_if_fail (element_id && *element_id);
4611 web_extension = e_web_view_get_web_extension_proxy (web_view);
4612 if (!web_extension)
4613 return;
4615 e_util_invoke_g_dbus_proxy_call_with_error_check (
4616 web_extension,
4617 "SetElementHidden",
4618 g_variant_new (
4619 "(tsb)",
4620 webkit_web_view_get_page_id (WEBKIT_WEB_VIEW (web_view)),
4621 element_id,
4622 hidden),
4623 NULL);
4626 void
4627 e_web_view_set_element_style_property (EWebView *web_view,
4628 const gchar *element_id,
4629 const gchar *property_name,
4630 const gchar *value,
4631 const gchar *priority)
4633 GDBusProxy *web_extension;
4635 g_return_if_fail (E_IS_WEB_VIEW (web_view));
4636 g_return_if_fail (element_id && *element_id);
4637 g_return_if_fail (property_name && *property_name);
4639 web_extension = e_web_view_get_web_extension_proxy (web_view);
4640 if (!web_extension)
4641 return;
4643 e_util_invoke_g_dbus_proxy_call_with_error_check (
4644 web_extension,
4645 "SetElementStyleProperty",
4646 g_variant_new (
4647 "(tssss)",
4648 webkit_web_view_get_page_id (WEBKIT_WEB_VIEW (web_view)),
4649 element_id,
4650 property_name,
4651 value ? value : "",
4652 priority ? priority : ""),
4653 NULL);
4656 void
4657 e_web_view_set_element_attribute (EWebView *web_view,
4658 const gchar *element_id,
4659 const gchar *namespace_uri,
4660 const gchar *qualified_name,
4661 const gchar *value)
4663 GDBusProxy *web_extension;
4665 g_return_if_fail (E_IS_WEB_VIEW (web_view));
4666 g_return_if_fail (element_id && *element_id);
4667 g_return_if_fail (qualified_name && *qualified_name);
4669 web_extension = e_web_view_get_web_extension_proxy (web_view);
4670 if (!web_extension)
4671 return;
4673 e_util_invoke_g_dbus_proxy_call_with_error_check (
4674 web_extension,
4675 "SetElementAttribute",
4676 g_variant_new (
4677 "(tssss)",
4678 webkit_web_view_get_page_id (WEBKIT_WEB_VIEW (web_view)),
4679 element_id,
4680 namespace_uri ? namespace_uri : "",
4681 qualified_name,
4682 value ? value : ""),
4683 NULL);