/* * e-web-view.c * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with the program; if not, see * */ #ifdef HAVE_CONFIG_H #include #endif #include "e-web-view.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #define LIBSOUP_USE_UNSTABLE_REQUEST_API #include #include #include "e-popup-action.h" #include "e-selectable.h" #include #define E_WEB_VIEW_GET_PRIVATE(obj) \ (G_TYPE_INSTANCE_GET_PRIVATE \ ((obj), E_TYPE_WEB_VIEW, EWebViewPrivate)) typedef struct _EWebViewRequest EWebViewRequest; struct _EWebViewPrivate { GList *requests; GtkUIManager *ui_manager; gchar *selected_uri; GdkPixbufAnimation *cursor_image; gchar *cursor_image_src; GHashTable *js_callbacks; GSList *highlights; GtkAction *open_proxy; GtkAction *print_proxy; GtkAction *save_as_proxy; /* Lockdown Options */ guint disable_printing : 1; guint disable_save_to_disk : 1; guint caret_mode : 1; GSettings *font_settings; GSettings *aliasing_settings; }; enum { PROP_0, PROP_CARET_MODE, PROP_COPY_TARGET_LIST, PROP_CURSOR_IMAGE, PROP_CURSOR_IMAGE_SRC, PROP_DISABLE_PRINTING, PROP_DISABLE_SAVE_TO_DISK, PROP_INLINE_SPELLING, PROP_MAGIC_LINKS, PROP_MAGIC_SMILEYS, PROP_OPEN_PROXY, PROP_PRINT_PROXY, PROP_SAVE_AS_PROXY, PROP_SELECTED_URI }; enum { POPUP_EVENT, STATUS_MESSAGE, STOP_LOADING, UPDATE_ACTIONS, PROCESS_MAILTO, LAST_SIGNAL }; static gpointer parent_class; static guint signals[LAST_SIGNAL]; static const gchar *ui = "" " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " ""; /* Forward Declarations */ static void e_web_view_alert_sink_init (EAlertSinkInterface *interface); static void e_web_view_selectable_init (ESelectableInterface *interface); G_DEFINE_TYPE_WITH_CODE ( EWebView, e_web_view, WEBKIT_TYPE_WEB_VIEW, G_IMPLEMENT_INTERFACE ( E_TYPE_EXTENSIBLE, NULL) G_IMPLEMENT_INTERFACE ( E_TYPE_ALERT_SINK, e_web_view_alert_sink_init) G_IMPLEMENT_INTERFACE ( E_TYPE_SELECTABLE, e_web_view_selectable_init)) static void action_copy_clipboard_cb (GtkAction *action, EWebView *web_view) { e_web_view_copy_clipboard (web_view); } static void action_http_open_cb (GtkAction *action, EWebView *web_view) { const gchar *uri; gpointer parent; parent = gtk_widget_get_toplevel (GTK_WIDGET (web_view)); parent = gtk_widget_is_toplevel (parent) ? parent : NULL; uri = e_web_view_get_selected_uri (web_view); g_return_if_fail (uri != NULL); e_show_uri (parent, uri); } static void action_mailto_copy_cb (GtkAction *action, EWebView *web_view) { CamelURL *curl; CamelInternetAddress *inet_addr; GtkClipboard *clipboard; const gchar *uri; gchar *text; uri = e_web_view_get_selected_uri (web_view); g_return_if_fail (uri != NULL); /* This should work because we checked it in update_actions(). */ curl = camel_url_new (uri, NULL); g_return_if_fail (curl != NULL); inet_addr = camel_internet_address_new (); camel_address_decode (CAMEL_ADDRESS (inet_addr), curl->path); text = camel_address_format (CAMEL_ADDRESS (inet_addr)); if (text == NULL || *text == '\0') text = g_strdup (uri + strlen ("mailto:")); g_object_unref (inet_addr); camel_url_free (curl); clipboard = gtk_clipboard_get (GDK_SELECTION_PRIMARY); gtk_clipboard_set_text (clipboard, text, -1); gtk_clipboard_store (clipboard); clipboard = gtk_clipboard_get (GDK_SELECTION_CLIPBOARD); gtk_clipboard_set_text (clipboard, text, -1); gtk_clipboard_store (clipboard); g_free (text); } static void action_select_all_cb (GtkAction *action, EWebView *web_view) { e_web_view_select_all (web_view); } static void action_send_message_cb (GtkAction *action, EWebView *web_view) { const gchar *uri; gpointer parent; gboolean handled; parent = gtk_widget_get_toplevel (GTK_WIDGET (web_view)); parent = gtk_widget_is_toplevel (parent) ? parent : NULL; uri = e_web_view_get_selected_uri (web_view); g_return_if_fail (uri != NULL); handled = FALSE; g_signal_emit (web_view, signals[PROCESS_MAILTO], 0, uri, &handled); if (!handled) e_show_uri (parent, uri); } static void action_uri_copy_cb (GtkAction *action, EWebView *web_view) { GtkClipboard *clipboard; const gchar *uri; clipboard = gtk_clipboard_get (GDK_SELECTION_CLIPBOARD); uri = e_web_view_get_selected_uri (web_view); g_return_if_fail (uri != NULL); gtk_clipboard_set_text (clipboard, uri, -1); gtk_clipboard_store (clipboard); } static void action_image_copy_cb (GtkAction *action, EWebView *web_view) { GtkClipboard *clipboard; GdkPixbufAnimation *animation; GdkPixbuf *pixbuf; clipboard = gtk_clipboard_get (GDK_SELECTION_CLIPBOARD); animation = e_web_view_get_cursor_image (web_view); g_return_if_fail (animation != NULL); pixbuf = gdk_pixbuf_animation_get_static_image (animation); if (!pixbuf) return; gtk_clipboard_set_image (clipboard, pixbuf); gtk_clipboard_store (clipboard); } static GtkActionEntry uri_entries[] = { { "uri-copy", GTK_STOCK_COPY, N_("_Copy Link Location"), NULL, N_("Copy the link to the clipboard"), G_CALLBACK (action_uri_copy_cb) } }; static GtkActionEntry http_entries[] = { { "http-open", "emblem-web", N_("_Open Link in Browser"), NULL, N_("Open the link in a web browser"), G_CALLBACK (action_http_open_cb) } }; static GtkActionEntry mailto_entries[] = { { "mailto-copy", GTK_STOCK_COPY, N_("_Copy Email Address"), NULL, N_("Copy the email address to the clipboard"), G_CALLBACK (action_mailto_copy_cb) }, { "send-message", "mail-message-new", N_("_Send New Message To..."), NULL, N_("Send a mail message to this address"), G_CALLBACK (action_send_message_cb) } }; static GtkActionEntry image_entries[] = { { "image-copy", GTK_STOCK_COPY, N_("_Copy Image"), NULL, N_("Copy the image to the clipboard"), G_CALLBACK (action_image_copy_cb) } }; static GtkActionEntry selection_entries[] = { { "copy-clipboard", GTK_STOCK_COPY, NULL, NULL, N_("Copy the selection"), G_CALLBACK (action_copy_clipboard_cb) }, }; static GtkActionEntry standard_entries[] = { { "select-all", GTK_STOCK_SELECT_ALL, NULL, NULL, N_("Select all text and images"), G_CALLBACK (action_select_all_cb) } }; static void web_view_menu_item_select_cb (EWebView *web_view, GtkWidget *widget) { GtkAction *action; GtkActivatable *activatable; const gchar *tooltip; activatable = GTK_ACTIVATABLE (widget); action = gtk_activatable_get_related_action (activatable); tooltip = gtk_action_get_tooltip (action); if (tooltip == NULL) return; e_web_view_status_message (web_view, tooltip); } static void replace_text (WebKitDOMNode *node, const gchar *text, WebKitDOMNode *replacement) { /* NodeType 3 = TEXT_NODE */ if (webkit_dom_node_get_node_type (node) == 3) { gint text_length = strlen (text); while (node) { WebKitDOMNode *current_node, *replacement_node; const gchar *node_data, *offset; goffset split_offset; gint data_length; current_node = node; /* Don't use the WEBKIT_DOM_CHARACTER_DATA macro for * casting. WebKit lies about type of the object and * GLib will throw runtime warning about node not being * WebKitDOMCharacterData, but the function will return * correct and valid data. * IMO it's bug in the Gtk bindings and WebKit internally * handles it by the nodeType so therefor it works * event for "invalid" objects. But really, who knows..? */ node_data = webkit_dom_character_data_get_data ( (WebKitDOMCharacterData *) node); offset = strstr (node_data, text); if (!offset) { node = NULL; continue; } split_offset = offset - node_data + text_length; replacement_node = webkit_dom_node_clone_node (replacement, TRUE); data_length = webkit_dom_character_data_get_length ( (WebKitDOMCharacterData *) node); if (split_offset < data_length) { WebKitDOMNode *parent_node; node = WEBKIT_DOM_NODE ( webkit_dom_text_split_text ( (WebKitDOMText *) node, offset - node_data + text_length, NULL)); parent_node = webkit_dom_node_get_parent_node (node); webkit_dom_node_insert_before ( parent_node, replacement_node, node, NULL); } else { WebKitDOMNode *parent_node; parent_node = webkit_dom_node_get_parent_node (node); webkit_dom_node_append_child ( parent_node, replacement_node, NULL); } webkit_dom_character_data_delete_data ( (WebKitDOMCharacterData *) (current_node), offset - node_data, text_length, NULL); } } else { WebKitDOMNode *child, *next_child; /* Iframe? Let's traverse inside! */ if (WEBKIT_DOM_IS_HTML_IFRAME_ELEMENT (node)) { WebKitDOMDocument *frame_document; frame_document = webkit_dom_html_iframe_element_get_content_document ( WEBKIT_DOM_HTML_IFRAME_ELEMENT (node)); replace_text (WEBKIT_DOM_NODE (frame_document), text, replacement); } else { child = webkit_dom_node_get_first_child (node); while (child) { next_child = webkit_dom_node_get_next_sibling (child); replace_text (child, text, replacement); child = next_child; } } } } static void web_view_update_document_highlights (EWebView *web_view) { WebKitDOMDocument *document; GSList *iter; document = webkit_web_view_get_dom_document (WEBKIT_WEB_VIEW (web_view)); for (iter = web_view->priv->highlights; iter; iter = iter->next) { WebKitDOMDocumentFragment *frag; WebKitDOMElement *span; span = webkit_dom_document_create_element (document, "span", NULL); webkit_dom_html_element_set_class_name ( WEBKIT_DOM_HTML_ELEMENT (span), "__evo-highlight"); webkit_dom_html_element_set_inner_text ( WEBKIT_DOM_HTML_ELEMENT (span), iter->data, NULL); frag = webkit_dom_document_create_document_fragment (document); webkit_dom_node_append_child ( WEBKIT_DOM_NODE (frag), WEBKIT_DOM_NODE (span), NULL); replace_text (WEBKIT_DOM_NODE (document), iter->data, WEBKIT_DOM_NODE (frag)); } } static void web_view_menu_item_deselect_cb (EWebView *web_view) { e_web_view_status_message (web_view, NULL); } static void web_view_connect_proxy_cb (EWebView *web_view, GtkAction *action, GtkWidget *proxy) { if (!GTK_IS_MENU_ITEM (proxy)) return; g_signal_connect_swapped ( proxy, "select", G_CALLBACK (web_view_menu_item_select_cb), web_view); g_signal_connect_swapped ( proxy, "deselect", G_CALLBACK (web_view_menu_item_deselect_cb), web_view); } static GtkWidget * web_view_create_plugin_widget_cb (EWebView *web_view, const gchar *mime_type, const gchar *uri, GHashTable *param) { EWebViewClass *class; /* XXX WebKitWebView does not provide a class method for * this signal, so we do so we can override the default * behavior from subclasses for special URI types. */ class = E_WEB_VIEW_GET_CLASS (web_view); g_return_val_if_fail (class->create_plugin_widget != NULL, NULL); return class->create_plugin_widget (web_view, mime_type, uri, param); } static void web_view_hovering_over_link_cb (EWebView *web_view, const gchar *title, const gchar *uri) { EWebViewClass *class; /* XXX WebKitWebView does not provide a class method for * this signal, so we do so we can override the default * behavior from subclasses for special URI types. */ class = E_WEB_VIEW_GET_CLASS (web_view); g_return_if_fail (class->hovering_over_link != NULL); class->hovering_over_link (web_view, title, uri); } static gboolean web_view_navigation_policy_decision_requested_cb (EWebView *web_view, WebKitWebFrame *frame, WebKitNetworkRequest *request, WebKitWebNavigationAction *navigation_action, WebKitWebPolicyDecision *policy_decision) { EWebViewClass *class; WebKitWebNavigationReason reason; const gchar *uri; reason = webkit_web_navigation_action_get_reason (navigation_action); if (reason != WEBKIT_WEB_NAVIGATION_REASON_LINK_CLICKED) return FALSE; /* XXX WebKitWebView does not provide a class method for * this signal, so we do so we can override the default * behavior from subclasses for special URI types. */ class = E_WEB_VIEW_GET_CLASS (web_view); g_return_val_if_fail (class->link_clicked != NULL, FALSE); webkit_web_policy_decision_ignore (policy_decision); uri = webkit_network_request_get_uri (request); class->link_clicked (web_view, uri); return TRUE; } static void web_view_load_status_changed_cb (WebKitWebView *web_view, GParamSpec *pspec, gpointer user_data) { WebKitLoadStatus status; status = webkit_web_view_get_load_status (web_view); if (status != WEBKIT_LOAD_FINISHED) return; web_view_update_document_highlights (E_WEB_VIEW (web_view)); } static void web_view_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec) { switch (property_id) { case PROP_CARET_MODE: e_web_view_set_caret_mode ( E_WEB_VIEW (object), g_value_get_boolean (value)); return; case PROP_CURSOR_IMAGE: e_web_view_set_cursor_image ( E_WEB_VIEW (object), g_value_get_object (value)); return; case PROP_CURSOR_IMAGE_SRC: e_web_view_set_cursor_image_src ( E_WEB_VIEW (object), g_value_get_string (value)); return; case PROP_DISABLE_PRINTING: e_web_view_set_disable_printing ( E_WEB_VIEW (object), g_value_get_boolean (value)); return; case PROP_DISABLE_SAVE_TO_DISK: e_web_view_set_disable_save_to_disk ( E_WEB_VIEW (object), g_value_get_boolean (value)); return; case PROP_INLINE_SPELLING: e_web_view_set_inline_spelling ( E_WEB_VIEW (object), g_value_get_boolean (value)); return; case PROP_MAGIC_LINKS: e_web_view_set_magic_links ( E_WEB_VIEW (object), g_value_get_boolean (value)); return; case PROP_MAGIC_SMILEYS: e_web_view_set_magic_smileys ( E_WEB_VIEW (object), g_value_get_boolean (value)); return; case PROP_OPEN_PROXY: e_web_view_set_open_proxy ( E_WEB_VIEW (object), g_value_get_object (value)); return; case PROP_PRINT_PROXY: e_web_view_set_print_proxy ( E_WEB_VIEW (object), g_value_get_object (value)); return; case PROP_SAVE_AS_PROXY: e_web_view_set_save_as_proxy ( E_WEB_VIEW (object), g_value_get_object (value)); return; case PROP_SELECTED_URI: e_web_view_set_selected_uri ( E_WEB_VIEW (object), g_value_get_string (value)); return; } G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); } static void web_view_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec) { switch (property_id) { case PROP_CARET_MODE: g_value_set_boolean ( value, e_web_view_get_caret_mode ( E_WEB_VIEW (object))); return; case PROP_CURSOR_IMAGE: g_value_set_object ( value, e_web_view_get_cursor_image ( E_WEB_VIEW (object))); return; case PROP_CURSOR_IMAGE_SRC: g_value_set_string ( value, e_web_view_get_cursor_image_src ( E_WEB_VIEW (object))); return; case PROP_DISABLE_PRINTING: g_value_set_boolean ( value, e_web_view_get_disable_printing ( E_WEB_VIEW (object))); return; case PROP_DISABLE_SAVE_TO_DISK: g_value_set_boolean ( value, e_web_view_get_disable_save_to_disk ( E_WEB_VIEW (object))); return; case PROP_INLINE_SPELLING: g_value_set_boolean ( value, e_web_view_get_inline_spelling ( E_WEB_VIEW (object))); return; case PROP_MAGIC_LINKS: g_value_set_boolean ( value, e_web_view_get_magic_links ( E_WEB_VIEW (object))); return; case PROP_MAGIC_SMILEYS: g_value_set_boolean ( value, e_web_view_get_magic_smileys ( E_WEB_VIEW (object))); return; case PROP_OPEN_PROXY: g_value_set_object ( value, e_web_view_get_open_proxy ( E_WEB_VIEW (object))); return; case PROP_PRINT_PROXY: g_value_set_object ( value, e_web_view_get_print_proxy ( E_WEB_VIEW (object))); return; case PROP_SAVE_AS_PROXY: g_value_set_object ( value, e_web_view_get_save_as_proxy ( E_WEB_VIEW (object))); return; case PROP_SELECTED_URI: g_value_set_string ( value, e_web_view_get_selected_uri ( E_WEB_VIEW (object))); return; } G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); } static void web_view_dispose (GObject *object) { EWebViewPrivate *priv; priv = E_WEB_VIEW_GET_PRIVATE (object); if (priv->ui_manager != NULL) { g_object_unref (priv->ui_manager); priv->ui_manager = NULL; } if (priv->open_proxy != NULL) { g_object_unref (priv->open_proxy); priv->open_proxy = NULL; } if (priv->print_proxy != NULL) { g_object_unref (priv->print_proxy); priv->print_proxy = NULL; } if (priv->save_as_proxy != NULL) { g_object_unref (priv->save_as_proxy); priv->save_as_proxy = NULL; } if (priv->cursor_image != NULL) { g_object_unref (priv->cursor_image); priv->cursor_image = NULL; } if (priv->cursor_image_src != NULL) { g_free (priv->cursor_image_src); priv->cursor_image_src = NULL; } if (priv->js_callbacks != NULL) { g_hash_table_destroy (priv->js_callbacks); priv->js_callbacks = NULL; } if (priv->highlights != NULL) { g_slist_free_full (priv->highlights, g_free); priv->highlights = NULL; } if (priv->aliasing_settings != NULL) { g_object_unref (priv->aliasing_settings); priv->aliasing_settings = NULL; } if (priv->font_settings != NULL) { g_object_unref (priv->font_settings); priv->font_settings = NULL; } /* Chain up to parent's dispose() method. */ G_OBJECT_CLASS (parent_class)->dispose (object); } static void web_view_finalize (GObject *object) { EWebViewPrivate *priv; priv = E_WEB_VIEW_GET_PRIVATE (object); /* All URI requests should be complete or cancelled by now. */ if (priv->requests != NULL) g_warning ("Finalizing EWebView with active URI requests"); g_free (priv->selected_uri); /* Chain up to parent's finalize() method. */ G_OBJECT_CLASS (parent_class)->finalize (object); } static void web_view_constructed (GObject *object) { #ifndef G_OS_WIN32 GSettings *settings; settings = g_settings_new ("org.gnome.desktop.lockdown"); g_settings_bind ( settings, "disable-printing", object, "disable-printing", G_SETTINGS_BIND_GET); g_settings_bind ( settings, "disable-save-to-disk", object, "disable-save-to-disk", G_SETTINGS_BIND_GET); g_object_unref (settings); #endif /* Chain up to parent's constructed() method. */ G_OBJECT_CLASS (parent_class)->constructed (object); } static gboolean web_view_button_press_event (GtkWidget *widget, GdkEventButton *event) { GtkWidgetClass *widget_class; EWebView *web_view; gboolean event_handled = FALSE; gchar *uri; web_view = E_WEB_VIEW (widget); if (event) { WebKitHitTestResult *test; WebKitHitTestResultContext context; if (web_view->priv->cursor_image) { g_object_unref (web_view->priv->cursor_image); web_view->priv->cursor_image = NULL; } if (web_view->priv->cursor_image_src) { g_free (web_view->priv->cursor_image_src); web_view->priv->cursor_image_src = NULL; } test = webkit_web_view_get_hit_test_result (WEBKIT_WEB_VIEW (web_view), event); if (!test) goto chainup; g_object_get (G_OBJECT (test), "context", &context, NULL); if (context & WEBKIT_HIT_TEST_RESULT_CONTEXT_IMAGE) { WebKitWebDataSource *data_source; WebKitWebFrame *frame; GList *subresources, *res; g_object_get (G_OBJECT (test), "image-uri", &uri, NULL); if (!uri) goto chainup; if (web_view->priv->cursor_image_src) g_free (web_view->priv->cursor_image_src); web_view->priv->cursor_image_src = uri; /* Iterate through all resources of the loaded webpage and try to find resource with URI matching cursor_image_src */ frame = webkit_web_view_get_main_frame (WEBKIT_WEB_VIEW (web_view)); data_source = webkit_web_frame_get_data_source (frame); subresources = webkit_web_data_source_get_subresources (data_source); for (res = subresources; res; res = res->next) { WebKitWebResource *src = res->data; GdkPixbufLoader *loader; GString *data; if (g_strcmp0 (webkit_web_resource_get_uri (src), web_view->priv->cursor_image_src) != 0) continue; data = webkit_web_resource_get_data (src); if (!data) break; loader = gdk_pixbuf_loader_new (); if (!gdk_pixbuf_loader_write (loader, (guchar *) data->str, data->len, NULL)) { g_object_unref (loader); break; } gdk_pixbuf_loader_close (loader, NULL); if (web_view->priv->cursor_image) g_object_unref (web_view->priv->cursor_image); web_view->priv->cursor_image = g_object_ref (gdk_pixbuf_loader_get_animation (loader)); g_object_unref (loader); break; } } } if (event != NULL && event->button != 3) goto chainup; uri = e_web_view_extract_uri (web_view, event); if (uri != NULL && g_str_has_prefix (uri, "##")) { g_free (uri); goto chainup; } g_signal_emit ( web_view, signals[POPUP_EVENT], 0, event, uri, &event_handled); g_free (uri); if (event_handled) return TRUE; chainup: /* Chain up to parent's button_press_event() method. */ widget_class = GTK_WIDGET_CLASS (parent_class); return widget_class->button_press_event (widget, event); } static gboolean web_view_scroll_event (GtkWidget *widget, GdkEventScroll *event) { if (event->state & GDK_CONTROL_MASK) { GdkScrollDirection direction = event->direction; #if GTK_CHECK_VERSION(3,3,18) if (direction == GDK_SCROLL_SMOOTH) { static gdouble total_delta_y = 0.0; total_delta_y += event->delta_y; if (total_delta_y >= 1.0) { total_delta_y = 0.0; direction = GDK_SCROLL_DOWN; } else if (total_delta_y <= -1.0) { total_delta_y = 0.0; direction = GDK_SCROLL_UP; } else { return FALSE; } } #endif switch (direction) { case GDK_SCROLL_UP: e_web_view_zoom_in (E_WEB_VIEW (widget)); return TRUE; case GDK_SCROLL_DOWN: e_web_view_zoom_out (E_WEB_VIEW (widget)); return TRUE; default: break; } } return FALSE; } static GtkWidget * web_view_create_plugin_widget (EWebView *web_view, const gchar *mime_type, const gchar *uri, GHashTable *param) { GtkWidget *widget = NULL; if (g_strcmp0 (mime_type, "image/x-themed-icon") == 0) { GtkIconTheme *icon_theme; GdkPixbuf *pixbuf; gpointer data; glong size = 0; GError *error = NULL; icon_theme = gtk_icon_theme_get_default (); if (size == 0) { data = g_hash_table_lookup (param, "width"); if (data != NULL) size = MAX (size, strtol (data, NULL, 10)); } if (size == 0) { data = g_hash_table_lookup (param, "height"); if (data != NULL) size = MAX (size, strtol (data, NULL, 10)); } if (size == 0) size = 32; /* arbitrary default */ pixbuf = gtk_icon_theme_load_icon ( icon_theme, uri, size, 0, &error); if (pixbuf != NULL) { widget = gtk_image_new_from_pixbuf (pixbuf); g_object_unref (pixbuf); } else if (error != NULL) { g_warning ("%s", error->message); g_error_free (error); } } return widget; } static gchar * web_view_extract_uri (EWebView *web_view, GdkEventButton *event) { WebKitHitTestResult *result; WebKitHitTestResultContext context; gchar *uri = NULL; result = webkit_web_view_get_hit_test_result ( WEBKIT_WEB_VIEW (web_view), event); g_object_get (result, "context", &context, "link-uri", &uri, NULL); if (context & WEBKIT_HIT_TEST_RESULT_CONTEXT_LINK) return uri; g_free (uri); return NULL; } static void web_view_hovering_over_link (EWebView *web_view, const gchar *title, const gchar *uri) { CamelInternetAddress *address; CamelURL *curl; const gchar *format = NULL; gchar *message = NULL; gchar *who; if (uri == NULL || *uri == '\0') goto exit; if (g_str_has_prefix (uri, "mailto:")) format = _("Click to mail %s"); else if (g_str_has_prefix (uri, "callto:")) format = _("Click to call %s"); else if (g_str_has_prefix (uri, "h323:")) format = _("Click to call %s"); else if (g_str_has_prefix (uri, "sip:")) format = _("Click to call %s"); else if (g_str_has_prefix (uri, "##")) message = g_strdup (_("Click to hide/unhide addresses")); else message = g_strdup_printf (_("Click to open %s"), uri); if (format == NULL) goto exit; /* XXX Use something other than Camel here. Surely * there's other APIs around that can do this. */ curl = camel_url_new (uri, NULL); address = camel_internet_address_new (); camel_address_decode (CAMEL_ADDRESS (address), curl->path); who = camel_address_format (CAMEL_ADDRESS (address)); g_object_unref (address); camel_url_free (curl); if (who == NULL) who = g_strdup (strchr (uri, ':') + 1); message = g_strdup_printf (format, who); g_free (who); exit: e_web_view_status_message (web_view, message); g_free (message); } static void web_view_link_clicked (EWebView *web_view, const gchar *uri) { gpointer parent; if (uri && g_ascii_strncasecmp (uri, "mailto:", 7) == 0) { gboolean handled = FALSE; g_signal_emit (web_view, signals[PROCESS_MAILTO], 0, uri, &handled); if (handled) return; } parent = gtk_widget_get_toplevel (GTK_WIDGET (web_view)); parent = gtk_widget_is_toplevel (parent) ? parent : NULL; e_show_uri (parent, uri); } static void web_view_load_string (EWebView *web_view, const gchar *string) { if (string == NULL) string = ""; webkit_web_view_load_string ( WEBKIT_WEB_VIEW (web_view), string, "text/html", "UTF-8", "evo-file:///"); } static void web_view_load_uri (EWebView *web_view, const gchar *uri) { if (uri == NULL) uri = "about:blank"; webkit_web_view_load_uri ( WEBKIT_WEB_VIEW (web_view), uri); } static void web_view_frame_load_string (EWebView *web_view, const gchar *frame_name, const gchar *string) { WebKitWebFrame *main_frame, *frame; if (string == NULL) string = ""; main_frame = webkit_web_view_get_main_frame (WEBKIT_WEB_VIEW (web_view)); if (main_frame) { frame = webkit_web_frame_find_frame (main_frame, frame_name); if (frame) webkit_web_frame_load_string ( frame, string, "text/html", "UTF-8", "evo-file:///"); } } static void web_view_frame_load_uri (EWebView *web_view, const gchar *frame_name, const gchar *uri) { WebKitWebFrame *main_frame, *frame; if (uri == NULL) uri = "about:blank"; main_frame = webkit_web_view_get_main_frame (WEBKIT_WEB_VIEW (web_view)); if (main_frame) { frame = webkit_web_frame_find_frame (main_frame, frame_name); if (frame) webkit_web_frame_load_uri (frame, uri); } } static gboolean web_view_popup_event (EWebView *web_view, GdkEventButton *event, const gchar *uri) { e_web_view_set_selected_uri (web_view, uri); e_web_view_show_popup_menu (web_view, event, NULL, NULL); return TRUE; } static void web_view_stop_loading (EWebView *web_view) { webkit_web_view_stop_loading (WEBKIT_WEB_VIEW (web_view)); } static void web_view_update_actions (EWebView *web_view) { GtkActionGroup *action_group; gboolean have_selection; gboolean scheme_is_http = FALSE; gboolean scheme_is_mailto = FALSE; gboolean uri_is_valid = FALSE; gboolean has_cursor_image; gboolean visible; const gchar *group_name; const gchar *uri; uri = e_web_view_get_selected_uri (web_view); have_selection = e_web_view_is_selection_active (web_view); has_cursor_image = e_web_view_get_cursor_image (web_view) != NULL; /* Parse the URI early so we know if the actions will work. */ if (uri != NULL) { CamelURL *curl; curl = camel_url_new (uri, NULL); uri_is_valid = (curl != NULL); camel_url_free (curl); scheme_is_http = (g_ascii_strncasecmp (uri, "http:", 5) == 0) || (g_ascii_strncasecmp (uri, "https:", 6) == 0); scheme_is_mailto = (g_ascii_strncasecmp (uri, "mailto:", 7) == 0); } /* Allow copying the URI even if it's malformed. */ group_name = "uri"; visible = (uri != NULL) && !scheme_is_mailto; action_group = e_web_view_get_action_group (web_view, group_name); gtk_action_group_set_visible (action_group, visible); group_name = "http"; visible = uri_is_valid && scheme_is_http; action_group = e_web_view_get_action_group (web_view, group_name); gtk_action_group_set_visible (action_group, visible); group_name = "mailto"; visible = uri_is_valid && scheme_is_mailto; action_group = e_web_view_get_action_group (web_view, group_name); gtk_action_group_set_visible (action_group, visible); group_name = "image"; visible = has_cursor_image; action_group = e_web_view_get_action_group (web_view, group_name); gtk_action_group_set_visible (action_group, visible); group_name = "selection"; visible = have_selection; action_group = e_web_view_get_action_group (web_view, group_name); gtk_action_group_set_visible (action_group, visible); group_name = "standard"; visible = (uri == NULL); action_group = e_web_view_get_action_group (web_view, group_name); gtk_action_group_set_visible (action_group, visible); group_name = "lockdown-printing"; visible = (uri == NULL) && !web_view->priv->disable_printing; action_group = e_web_view_get_action_group (web_view, group_name); gtk_action_group_set_visible (action_group, visible); group_name = "lockdown-save-to-disk"; visible = (uri == NULL) && !web_view->priv->disable_save_to_disk; action_group = e_web_view_get_action_group (web_view, group_name); gtk_action_group_set_visible (action_group, visible); } static void web_view_submit_alert (EAlertSink *alert_sink, EAlert *alert) { EWebView *web_view; GtkWidget *dialog; GString *buffer; const gchar *icon_name = NULL; gpointer parent; web_view = E_WEB_VIEW (alert_sink); parent = gtk_widget_get_toplevel (GTK_WIDGET (web_view)); parent = gtk_widget_is_toplevel (parent) ? parent : NULL; switch (e_alert_get_message_type (alert)) { case GTK_MESSAGE_INFO: icon_name = GTK_STOCK_DIALOG_INFO; break; case GTK_MESSAGE_WARNING: icon_name = GTK_STOCK_DIALOG_WARNING; break; case GTK_MESSAGE_ERROR: icon_name = GTK_STOCK_DIALOG_ERROR; break; default: dialog = e_alert_dialog_new (parent, alert); gtk_dialog_run (GTK_DIALOG (dialog)); gtk_widget_destroy (dialog); return; } buffer = g_string_sized_new (512); g_string_append ( buffer, "" "" "" "" ""); g_string_append ( buffer, "" "" "" "" "
" "" ""); g_string_append_printf ( buffer, "" "" "" "", icon_name, GTK_ICON_SIZE_DIALOG, e_alert_get_primary_text (alert), e_alert_get_secondary_text (alert)); g_string_append ( buffer, "
" "" "" "

%s

" "%s" "
" "
" "" ""); e_web_view_load_string (web_view, buffer->str); g_string_free (buffer, TRUE); } static void web_view_selectable_update_actions (ESelectable *selectable, EFocusTracker *focus_tracker, GdkAtom *clipboard_targets, gint n_clipboard_targets) { WebKitWebView *web_view; GtkAction *action; gboolean sensitive; const gchar *tooltip; web_view = WEBKIT_WEB_VIEW (selectable); action = e_focus_tracker_get_cut_clipboard_action (focus_tracker); sensitive = webkit_web_view_can_cut_clipboard (web_view); tooltip = _("Cut the selection"); gtk_action_set_sensitive (action, sensitive); gtk_action_set_tooltip (action, tooltip); action = e_focus_tracker_get_copy_clipboard_action (focus_tracker); sensitive = webkit_web_view_can_copy_clipboard (web_view); tooltip = _("Copy the selection"); gtk_action_set_sensitive (action, sensitive); gtk_action_set_tooltip (action, tooltip); action = e_focus_tracker_get_paste_clipboard_action (focus_tracker); sensitive = webkit_web_view_can_paste_clipboard (web_view); tooltip = _("Paste the clipboard"); gtk_action_set_sensitive (action, sensitive); gtk_action_set_tooltip (action, tooltip); action = e_focus_tracker_get_select_all_action (focus_tracker); sensitive = TRUE; tooltip = _("Select all text and images"); gtk_action_set_sensitive (action, sensitive); gtk_action_set_tooltip (action, tooltip); } static void web_view_selectable_cut_clipboard (ESelectable *selectable) { e_web_view_cut_clipboard (E_WEB_VIEW (selectable)); } static void web_view_selectable_copy_clipboard (ESelectable *selectable) { e_web_view_copy_clipboard (E_WEB_VIEW (selectable)); } static void web_view_selectable_paste_clipboard (ESelectable *selectable) { e_web_view_paste_clipboard (E_WEB_VIEW (selectable)); } static void web_view_selectable_select_all (ESelectable *selectable) { e_web_view_select_all (E_WEB_VIEW (selectable)); } static void e_web_view_class_init (EWebViewClass *class) { GObjectClass *object_class; GtkWidgetClass *widget_class; #if 0 /* WEBKIT */ GtkHTMLClass *html_class; #endif parent_class = g_type_class_peek_parent (class); g_type_class_add_private (class, sizeof (EWebViewPrivate)); object_class = G_OBJECT_CLASS (class); object_class->set_property = web_view_set_property; object_class->get_property = web_view_get_property; object_class->dispose = web_view_dispose; object_class->finalize = web_view_finalize; object_class->constructed = web_view_constructed; widget_class = GTK_WIDGET_CLASS (class); widget_class->button_press_event = web_view_button_press_event; widget_class->scroll_event = web_view_scroll_event; #if 0 /* WEBKIT */ html_class = GTK_HTML_CLASS (class); html_class->url_requested = web_view_url_requested; #endif class->create_plugin_widget = web_view_create_plugin_widget; class->extract_uri = web_view_extract_uri; class->hovering_over_link = web_view_hovering_over_link; class->link_clicked = web_view_link_clicked; class->load_string = web_view_load_string; class->load_uri = web_view_load_uri; class->frame_load_string = web_view_frame_load_string; class->frame_load_uri = web_view_frame_load_uri; class->popup_event = web_view_popup_event; class->stop_loading = web_view_stop_loading; class->update_actions = web_view_update_actions; g_object_class_install_property ( object_class, PROP_CARET_MODE, g_param_spec_boolean ( "caret-mode", "Caret Mode", NULL, FALSE, G_PARAM_READWRITE)); g_object_class_install_property ( object_class, PROP_CURSOR_IMAGE, g_param_spec_object ( "cursor-image", "Image animation at the mouse cursor", NULL, GDK_TYPE_PIXBUF_ANIMATION, G_PARAM_READWRITE)); g_object_class_install_property ( object_class, PROP_CURSOR_IMAGE_SRC, g_param_spec_string ( "cursor-image-src", "Image source uri at the mouse cursor", NULL, NULL, G_PARAM_READWRITE)); g_object_class_install_property ( object_class, PROP_DISABLE_PRINTING, g_param_spec_boolean ( "disable-printing", "Disable Printing", NULL, FALSE, G_PARAM_READWRITE | G_PARAM_CONSTRUCT)); g_object_class_install_property ( object_class, PROP_DISABLE_SAVE_TO_DISK, g_param_spec_boolean ( "disable-save-to-disk", "Disable Save-to-Disk", NULL, FALSE, G_PARAM_READWRITE | G_PARAM_CONSTRUCT)); g_object_class_install_property ( object_class, PROP_INLINE_SPELLING, g_param_spec_boolean ( "inline-spelling", "Inline Spelling", NULL, FALSE, G_PARAM_READWRITE)); g_object_class_install_property ( object_class, PROP_MAGIC_LINKS, g_param_spec_boolean ( "magic-links", "Magic Links", NULL, FALSE, G_PARAM_READWRITE)); g_object_class_install_property ( object_class, PROP_MAGIC_SMILEYS, g_param_spec_boolean ( "magic-smileys", "Magic Smileys", NULL, FALSE, G_PARAM_READWRITE)); g_object_class_install_property ( object_class, PROP_OPEN_PROXY, g_param_spec_object ( "open-proxy", "Open Proxy", NULL, GTK_TYPE_ACTION, G_PARAM_READWRITE)); g_object_class_install_property ( object_class, PROP_PRINT_PROXY, g_param_spec_object ( "print-proxy", "Print Proxy", NULL, GTK_TYPE_ACTION, G_PARAM_READWRITE)); g_object_class_install_property ( object_class, PROP_SAVE_AS_PROXY, g_param_spec_object ( "save-as-proxy", "Save As Proxy", NULL, GTK_TYPE_ACTION, G_PARAM_READWRITE)); g_object_class_install_property ( object_class, PROP_SELECTED_URI, g_param_spec_string ( "selected-uri", "Selected URI", NULL, NULL, G_PARAM_READWRITE)); signals[POPUP_EVENT] = g_signal_new ( "popup-event", G_TYPE_FROM_CLASS (class), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (EWebViewClass, popup_event), g_signal_accumulator_true_handled, NULL, e_marshal_BOOLEAN__BOXED_STRING, G_TYPE_BOOLEAN, 2, GDK_TYPE_EVENT | G_SIGNAL_TYPE_STATIC_SCOPE, G_TYPE_STRING); signals[STATUS_MESSAGE] = g_signal_new ( "status-message", G_TYPE_FROM_CLASS (class), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (EWebViewClass, status_message), NULL, NULL, g_cclosure_marshal_VOID__STRING, G_TYPE_NONE, 1, G_TYPE_STRING); signals[STOP_LOADING] = g_signal_new ( "stop-loading", G_TYPE_FROM_CLASS (class), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (EWebViewClass, stop_loading), NULL, NULL, g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0); signals[UPDATE_ACTIONS] = g_signal_new ( "update-actions", G_TYPE_FROM_CLASS (class), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (EWebViewClass, update_actions), NULL, NULL, g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0); /* return TRUE when a signal handler processed the mailto URI */ signals[PROCESS_MAILTO] = g_signal_new ( "process-mailto", G_TYPE_FROM_CLASS (class), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (EWebViewClass, process_mailto), NULL, NULL, e_marshal_BOOLEAN__STRING, G_TYPE_BOOLEAN, 1, G_TYPE_STRING); } static void e_web_view_alert_sink_init (EAlertSinkInterface *interface) { interface->submit_alert = web_view_submit_alert; } static void e_web_view_selectable_init (ESelectableInterface *interface) { interface->update_actions = web_view_selectable_update_actions; interface->cut_clipboard = web_view_selectable_cut_clipboard; interface->copy_clipboard = web_view_selectable_copy_clipboard; interface->paste_clipboard = web_view_selectable_paste_clipboard; interface->select_all = web_view_selectable_select_all; } static void e_web_view_init (EWebView *web_view) { GtkUIManager *ui_manager; GtkActionGroup *action_group; EPopupAction *popup_action; WebKitWebSettings *web_settings; GSettingsSchema *settings_schema; GSettings *settings; const gchar *domain = GETTEXT_PACKAGE; const gchar *id; GError *error = NULL; web_view->priv = E_WEB_VIEW_GET_PRIVATE (web_view); web_view->priv->highlights = NULL; g_signal_connect ( web_view, "create-plugin-widget", G_CALLBACK (web_view_create_plugin_widget_cb), NULL); g_signal_connect ( web_view, "hovering-over-link", G_CALLBACK (web_view_hovering_over_link_cb), NULL); g_signal_connect ( web_view, "navigation-policy-decision-requested", G_CALLBACK (web_view_navigation_policy_decision_requested_cb), NULL); g_signal_connect ( web_view, "new-window-policy-decision-requested", G_CALLBACK (web_view_navigation_policy_decision_requested_cb), NULL); g_signal_connect ( web_view, "notify::load-status", G_CALLBACK (web_view_load_status_changed_cb), NULL); ui_manager = gtk_ui_manager_new (); web_view->priv->ui_manager = ui_manager; web_view->priv->js_callbacks = g_hash_table_new_full (g_str_hash, g_str_equal, (GDestroyNotify) g_free, NULL); g_signal_connect_swapped ( ui_manager, "connect-proxy", G_CALLBACK (web_view_connect_proxy_cb), web_view); web_settings = e_web_view_get_default_settings (); e_web_view_set_settings (web_view, web_settings); g_object_unref (web_settings); e_web_view_install_request_handler (web_view, E_TYPE_FILE_REQUEST); e_web_view_install_request_handler (web_view, E_TYPE_STOCK_REQUEST); settings = g_settings_new ("org.gnome.desktop.interface"); g_signal_connect_swapped ( settings, "changed::font-name", G_CALLBACK (e_web_view_update_fonts), web_view); g_signal_connect_swapped ( settings, "changed::monospace-font-name", G_CALLBACK (e_web_view_update_fonts), web_view); web_view->priv->font_settings = settings; /* This schema is optional. Use if available. */ id = "org.gnome.settings-daemon.plugins.xsettings"; settings_schema = g_settings_schema_source_lookup ( g_settings_schema_source_get_default (), id, FALSE); if (settings_schema != NULL) { settings = g_settings_new (id); g_signal_connect_swapped ( settings, "changed::antialiasing", G_CALLBACK (e_web_view_update_fonts), web_view); web_view->priv->aliasing_settings = settings; } e_web_view_update_fonts (web_view); action_group = gtk_action_group_new ("uri"); gtk_action_group_set_translation_domain (action_group, domain); gtk_ui_manager_insert_action_group (ui_manager, action_group, 0); g_object_unref (action_group); gtk_action_group_add_actions ( action_group, uri_entries, G_N_ELEMENTS (uri_entries), web_view); action_group = gtk_action_group_new ("http"); gtk_action_group_set_translation_domain (action_group, domain); gtk_ui_manager_insert_action_group (ui_manager, action_group, 0); g_object_unref (action_group); gtk_action_group_add_actions ( action_group, http_entries, G_N_ELEMENTS (http_entries), web_view); action_group = gtk_action_group_new ("mailto"); gtk_action_group_set_translation_domain (action_group, domain); gtk_ui_manager_insert_action_group (ui_manager, action_group, 0); g_object_unref (action_group); gtk_action_group_add_actions ( action_group, mailto_entries, G_N_ELEMENTS (mailto_entries), web_view); action_group = gtk_action_group_new ("image"); gtk_action_group_set_translation_domain (action_group, domain); gtk_ui_manager_insert_action_group (ui_manager, action_group, 0); g_object_unref (action_group); gtk_action_group_add_actions ( action_group, image_entries, G_N_ELEMENTS (image_entries), web_view); action_group = gtk_action_group_new ("selection"); gtk_action_group_set_translation_domain (action_group, domain); gtk_ui_manager_insert_action_group (ui_manager, action_group, 0); g_object_unref (action_group); gtk_action_group_add_actions ( action_group, selection_entries, G_N_ELEMENTS (selection_entries), web_view); action_group = gtk_action_group_new ("standard"); gtk_action_group_set_translation_domain (action_group, domain); gtk_ui_manager_insert_action_group (ui_manager, action_group, 0); g_object_unref (action_group); gtk_action_group_add_actions ( action_group, standard_entries, G_N_ELEMENTS (standard_entries), web_view); popup_action = e_popup_action_new ("open"); gtk_action_group_add_action (action_group, GTK_ACTION (popup_action)); g_object_unref (popup_action); g_object_bind_property ( web_view, "open-proxy", popup_action, "related-action", G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE); /* Support lockdown. */ action_group = gtk_action_group_new ("lockdown-printing"); gtk_action_group_set_translation_domain (action_group, domain); gtk_ui_manager_insert_action_group (ui_manager, action_group, 0); g_object_unref (action_group); popup_action = e_popup_action_new ("print"); gtk_action_group_add_action (action_group, GTK_ACTION (popup_action)); g_object_unref (popup_action); g_object_bind_property ( web_view, "print-proxy", popup_action, "related-action", G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE); action_group = gtk_action_group_new ("lockdown-save-to-disk"); gtk_action_group_set_translation_domain (action_group, domain); gtk_ui_manager_insert_action_group (ui_manager, action_group, 0); g_object_unref (action_group); popup_action = e_popup_action_new ("save-as"); gtk_action_group_add_action (action_group, GTK_ACTION (popup_action)); g_object_unref (popup_action); g_object_bind_property ( web_view, "save-as-proxy", popup_action, "related-action", G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE); /* Because we are loading from a hard-coded string, there is * no chance of I/O errors. Failure here implies a malformed * UI definition. Full stop. */ gtk_ui_manager_add_ui_from_string (ui_manager, ui, -1, &error); if (error != NULL) g_error ("%s", error->message); id = "org.gnome.evolution.webview"; e_plugin_ui_register_manager (ui_manager, id, web_view); e_plugin_ui_enable_manager (ui_manager, id); e_extensible_load_extensions (E_EXTENSIBLE (web_view)); } GtkWidget * e_web_view_new (void) { return g_object_new (E_TYPE_WEB_VIEW, NULL); } void e_web_view_clear (EWebView *web_view) { g_return_if_fail (E_IS_WEB_VIEW (web_view)); webkit_web_view_load_uri (WEBKIT_WEB_VIEW (web_view), "about:blank"); } void e_web_view_load_string (EWebView *web_view, const gchar *string) { EWebViewClass *class; g_return_if_fail (E_IS_WEB_VIEW (web_view)); class = E_WEB_VIEW_GET_CLASS (web_view); g_return_if_fail (class->load_string != NULL); class->load_string (web_view, string); } void e_web_view_load_uri (EWebView *web_view, const gchar *uri) { EWebViewClass *class; g_return_if_fail (E_IS_WEB_VIEW (web_view)); class = E_WEB_VIEW_GET_CLASS (web_view); g_return_if_fail (class->load_uri != NULL); class->load_uri (web_view, uri); } void e_web_view_reload (EWebView *web_view) { g_return_if_fail (E_IS_WEB_VIEW (web_view)); webkit_web_view_reload (WEBKIT_WEB_VIEW (web_view)); } const gchar * e_web_view_get_uri (EWebView *web_view) { g_return_val_if_fail (E_IS_WEB_VIEW (web_view), NULL); return webkit_web_view_get_uri (WEBKIT_WEB_VIEW (web_view)); } void e_web_view_frame_load_string (EWebView *web_view, const gchar *frame_name, const gchar *string) { EWebViewClass *class; g_return_if_fail (E_IS_WEB_VIEW (web_view)); g_return_if_fail (frame_name != NULL); class = E_WEB_VIEW_GET_CLASS (web_view); g_return_if_fail (class->frame_load_string != NULL); class->frame_load_string (web_view, frame_name, string); } void e_web_view_frame_load_uri (EWebView *web_view, const gchar *frame_name, const gchar *uri) { EWebViewClass *class; g_return_if_fail (E_IS_WEB_VIEW (web_view)); g_return_if_fail (frame_name != NULL); class = E_WEB_VIEW_GET_CLASS (web_view); g_return_if_fail (class->frame_load_uri != NULL); class->frame_load_uri (web_view, frame_name, uri); } const gchar * e_web_view_frame_get_uri (EWebView *web_view, const gchar *frame_name) { WebKitWebFrame *main_frame, *frame; g_return_val_if_fail (E_IS_WEB_VIEW (web_view), NULL); g_return_val_if_fail (frame_name != NULL, NULL); main_frame = webkit_web_view_get_main_frame (WEBKIT_WEB_VIEW (web_view)); if (main_frame) { frame = webkit_web_frame_find_frame (main_frame, frame_name); if (frame) return webkit_web_frame_get_uri (frame); } return NULL; } gchar * e_web_view_get_html (EWebView *web_view) { GValue html = {0}; g_return_val_if_fail (E_IS_WEB_VIEW (web_view), NULL); if (e_web_view_exec_script (web_view, "return document.documentElement.innerHTML;", &html) == G_TYPE_STRING) return g_strdup (g_value_get_string (&html)); else return NULL; } JSGlobalContextRef e_web_view_get_global_context (EWebView *web_view) { WebKitWebFrame *main_frame; g_return_val_if_fail (E_IS_WEB_VIEW (web_view), 0); main_frame = webkit_web_view_get_main_frame (WEBKIT_WEB_VIEW (web_view)); return webkit_web_frame_get_global_context (main_frame); } GType e_web_view_exec_script (EWebView *web_view, const gchar *script, GValue *value) { WebKitWebFrame *main_frame; g_return_val_if_fail (E_IS_WEB_VIEW (web_view), G_TYPE_INVALID); g_return_val_if_fail (script != NULL, G_TYPE_INVALID); main_frame = webkit_web_view_get_main_frame (WEBKIT_WEB_VIEW (web_view)); return e_web_view_frame_exec_script (web_view, webkit_web_frame_get_name (main_frame), script, value); } GType e_web_view_frame_exec_script (EWebView *web_view, const gchar *frame_name, const gchar *script, GValue *value) { WebKitWebFrame *main_frame, *frame; JSGlobalContextRef context; JSValueRef js_value, error = NULL; JSType js_type; JSStringRef js_script; JSStringRef js_str; size_t str_len; gchar *str; g_return_val_if_fail (E_IS_WEB_VIEW (web_view), G_TYPE_INVALID); g_return_val_if_fail (script != NULL, G_TYPE_INVALID); main_frame = webkit_web_view_get_main_frame (WEBKIT_WEB_VIEW (web_view)); frame = webkit_web_frame_find_frame (main_frame, frame_name); context = webkit_web_frame_get_global_context (frame); js_script = JSStringCreateWithUTF8CString (script); js_value = JSEvaluateScript (context, js_script, NULL, NULL, 0, &error); JSStringRelease (js_script); if (error) { gchar *msg; js_str = JSValueToStringCopy (context, error, NULL); str_len = JSStringGetLength (js_str); msg = g_malloc (str_len + 1); JSStringGetUTF8CString (js_str, msg, str_len + 1); JSStringRelease (js_str); g_message ("JavaScript Execution Failed: %s", msg); g_free (msg); return G_TYPE_INVALID; } if (!value) return G_TYPE_NONE; js_type = JSValueGetType (context, js_value); switch (js_type) { case kJSTypeBoolean: g_value_init (value, G_TYPE_BOOLEAN); g_value_set_boolean (value, JSValueToBoolean (context, js_value)); break; case kJSTypeNumber: g_value_init (value, G_TYPE_DOUBLE); g_value_set_double (value, JSValueToNumber (context, js_value, NULL)); break; case kJSTypeString: js_str = JSValueToStringCopy (context, js_value, NULL); str_len = JSStringGetLength (js_str); str = g_malloc (str_len + 1); JSStringGetUTF8CString (js_str, str, str_len + 1); JSStringRelease (js_str); g_value_init (value, G_TYPE_STRING); g_value_set_string (value, str); g_free (str); break; case kJSTypeObject: g_value_init (value, G_TYPE_OBJECT); g_value_set_object (value, JSValueToObject (context, js_value, NULL)); break; case kJSTypeNull: g_value_init (value, G_TYPE_POINTER); g_value_set_pointer (value, NULL); break; case kJSTypeUndefined: break; } return G_VALUE_TYPE (value); } static JSValueRef web_view_handle_js_callback (JSContextRef ctx, JSObjectRef function, JSObjectRef this_object, size_t argument_count, const JSValueRef arguments[], JSValueRef *exception) { gpointer web_view; gpointer user_data; gchar *fnc_name; size_t fnc_name_len; EWebViewJSFunctionCallback callback; JSStringRef js_webview_prop = JSStringCreateWithUTF8CString ("webview"); JSStringRef js_userdata_prop = JSStringCreateWithUTF8CString ("user-data"); JSStringRef js_fncname_prop = JSStringCreateWithUTF8CString ("fnc-name"); JSStringRef js_fncname_str; JSValueRef js_webview = JSObjectGetProperty (ctx, function, js_webview_prop, NULL); JSValueRef js_userdata = JSObjectGetProperty (ctx, function, js_userdata_prop, NULL); JSValueRef js_fncname = JSObjectGetProperty (ctx, function, js_fncname_prop, NULL); web_view = GINT_TO_POINTER ((gint) JSValueToNumber (ctx, js_webview, NULL)); user_data = GINT_TO_POINTER ((gint) JSValueToNumber (ctx, js_userdata, NULL)); js_fncname_str = JSValueToStringCopy (ctx, js_fncname, NULL); fnc_name_len = JSStringGetLength (js_fncname_str); g_return_val_if_fail (E_IS_WEB_VIEW (web_view), 0); /* Convert fncname to gchar* and lookup the callback in hashtable */ fnc_name = g_malloc (fnc_name_len + 1); JSStringGetUTF8CString (js_fncname_str, fnc_name, fnc_name_len + 1); callback = g_hash_table_lookup (E_WEB_VIEW (web_view)->priv->js_callbacks, fnc_name); g_return_val_if_fail (callback != NULL, 0); /* Call the callback function */ callback (E_WEB_VIEW (web_view), argument_count, arguments, user_data); JSStringRelease (js_fncname_str); JSStringRelease (js_fncname_prop); JSStringRelease (js_webview_prop); JSStringRelease (js_userdata_prop); g_free (fnc_name); return 0; } void e_web_view_install_js_callback (EWebView *web_view, const gchar *fnc_name, EWebViewJSFunctionCallback callback, gpointer user_data) { WebKitWebFrame *frame; JSGlobalContextRef ctx; JSObjectRef global_obj, js_func; JSStringRef js_fnc_name, js_webview_prop, js_userdata_prop, js_fncname_prop; g_return_if_fail (E_IS_WEB_VIEW (web_view)); g_return_if_fail (fnc_name != NULL); g_return_if_fail (callback != NULL); frame = webkit_web_view_get_main_frame (WEBKIT_WEB_VIEW (web_view)); ctx = webkit_web_frame_get_global_context (frame); global_obj = JSContextGetGlobalObject (ctx); js_fnc_name = JSStringCreateWithUTF8CString (fnc_name); js_func = JSObjectMakeFunctionWithCallback (ctx, NULL, (JSObjectCallAsFunctionCallback) web_view_handle_js_callback); js_webview_prop = JSStringCreateWithUTF8CString ("webview"); js_userdata_prop = JSStringCreateWithUTF8CString ("user-data"); js_fncname_prop = JSStringCreateWithUTF8CString ("fnc-name"); /* Set some properties to the function */ JSObjectSetProperty (ctx, js_func, js_webview_prop, JSValueMakeNumber (ctx, GPOINTER_TO_INT (web_view)), 0, NULL); JSObjectSetProperty (ctx, js_func, js_userdata_prop, JSValueMakeNumber (ctx, GPOINTER_TO_INT (user_data)), 0, NULL); JSObjectSetProperty (ctx, js_func, js_fncname_prop, JSValueMakeString (ctx, js_fnc_name), 0, NULL); /* Set the function as a property of global object */ JSObjectSetProperty (ctx, global_obj, js_fnc_name, js_func, 0, NULL); JSStringRelease (js_fncname_prop); JSStringRelease (js_userdata_prop); JSStringRelease (js_webview_prop); JSStringRelease (js_fnc_name); g_hash_table_insert (web_view->priv->js_callbacks, g_strdup (fnc_name), callback); } gboolean e_web_view_get_caret_mode (EWebView *web_view) { g_return_val_if_fail (E_IS_WEB_VIEW (web_view), FALSE); return web_view->priv->caret_mode; } void e_web_view_set_caret_mode (EWebView *web_view, gboolean caret_mode) { g_return_if_fail (E_IS_WEB_VIEW (web_view)); web_view->priv->caret_mode = caret_mode; g_object_notify (G_OBJECT (web_view), "caret-mode"); } GtkTargetList * e_web_view_get_copy_target_list (EWebView *web_view) { g_return_val_if_fail (E_IS_WEB_VIEW (web_view), NULL); return webkit_web_view_get_copy_target_list ( WEBKIT_WEB_VIEW (web_view)); } gboolean e_web_view_get_disable_printing (EWebView *web_view) { g_return_val_if_fail (E_IS_WEB_VIEW (web_view), FALSE); return web_view->priv->disable_printing; } void e_web_view_set_disable_printing (EWebView *web_view, gboolean disable_printing) { g_return_if_fail (E_IS_WEB_VIEW (web_view)); web_view->priv->disable_printing = disable_printing; g_object_notify (G_OBJECT (web_view), "disable-printing"); } gboolean e_web_view_get_disable_save_to_disk (EWebView *web_view) { g_return_val_if_fail (E_IS_WEB_VIEW (web_view), FALSE); return web_view->priv->disable_save_to_disk; } void e_web_view_set_disable_save_to_disk (EWebView *web_view, gboolean disable_save_to_disk) { g_return_if_fail (E_IS_WEB_VIEW (web_view)); web_view->priv->disable_save_to_disk = disable_save_to_disk; g_object_notify (G_OBJECT (web_view), "disable-save-to-disk"); } gboolean e_web_view_get_enable_frame_flattening (EWebView *web_view) { WebKitWebSettings *settings; gboolean flattening; /* Return TRUE with fail since it's default value we set in _init(). */ g_return_val_if_fail (E_IS_WEB_VIEW (web_view), TRUE); settings = webkit_web_view_get_settings (WEBKIT_WEB_VIEW (web_view)); g_return_val_if_fail (settings != NULL, TRUE); g_object_get (G_OBJECT (settings), "enable-frame-flattening", &flattening, NULL); return flattening; } void e_web_view_set_enable_frame_flattening (EWebView *web_view, gboolean enable_frame_flattening) { WebKitWebSettings *settings; g_return_if_fail (E_IS_WEB_VIEW (web_view)); settings = webkit_web_view_get_settings (WEBKIT_WEB_VIEW (web_view)); g_return_if_fail (settings != NULL); g_object_set (G_OBJECT (settings), "enable-frame-flattening", enable_frame_flattening, NULL); } gboolean e_web_view_get_editable (EWebView *web_view) { g_return_val_if_fail (E_IS_WEB_VIEW (web_view), FALSE); return webkit_web_view_get_editable (WEBKIT_WEB_VIEW (web_view)); } void e_web_view_set_editable (EWebView *web_view, gboolean editable) { g_return_if_fail (E_IS_WEB_VIEW (web_view)); webkit_web_view_set_editable (WEBKIT_WEB_VIEW (web_view), editable); } gboolean e_web_view_get_inline_spelling (EWebView *web_view) { #if 0 /* WEBKIT - XXX No equivalent property? */ /* XXX This is just here to maintain symmetry * with e_web_view_set_inline_spelling(). */ g_return_val_if_fail (E_IS_WEB_VIEW (web_view), FALSE); return gtk_html_get_inline_spelling (GTK_HTML (web_view)); #endif return FALSE; } void e_web_view_set_inline_spelling (EWebView *web_view, gboolean inline_spelling) { #if 0 /* WEBKIT - XXX No equivalent property? */ /* XXX GtkHTML does not utilize GObject properties as well * as it could. This just wraps gtk_html_set_inline_spelling() * so we get a "notify::inline-spelling" signal. */ g_return_if_fail (E_IS_WEB_VIEW (web_view)); gtk_html_set_inline_spelling (GTK_HTML (web_view), inline_spelling); g_object_notify (G_OBJECT (web_view), "inline-spelling"); #endif } gboolean e_web_view_get_magic_links (EWebView *web_view) { #if 0 /* WEBKIT - XXX No equivalent property? */ /* XXX This is just here to maintain symmetry * with e_web_view_set_magic_links(). */ g_return_val_if_fail (E_IS_WEB_VIEW (web_view), FALSE); return gtk_html_get_magic_links (GTK_HTML (web_view)); #endif return FALSE; } void e_web_view_set_magic_links (EWebView *web_view, gboolean magic_links) { #if 0 /* WEBKIT - XXX No equivalent property? */ /* XXX GtkHTML does not utilize GObject properties as well * as it could. This just wraps gtk_html_set_magic_links() * so we can get a "notify::magic-links" signal. */ g_return_if_fail (E_IS_WEB_VIEW (web_view)); gtk_html_set_magic_links (GTK_HTML (web_view), magic_links); g_object_notify (G_OBJECT (web_view), "magic-links"); #endif } gboolean e_web_view_get_magic_smileys (EWebView *web_view) { #if 0 /* WEBKIT - No equivalent property? */ /* XXX This is just here to maintain symmetry * with e_web_view_set_magic_smileys(). */ g_return_val_if_fail (E_IS_WEB_VIEW (web_view), FALSE); return gtk_html_get_magic_smileys (GTK_HTML (web_view)); #endif return FALSE; } void e_web_view_set_magic_smileys (EWebView *web_view, gboolean magic_smileys) { #if 0 /* WEBKIT - No equivalent property? */ /* XXX GtkHTML does not utilize GObject properties as well * as it could. This just wraps gtk_html_set_magic_smileys() * so we can get a "notify::magic-smileys" signal. */ g_return_if_fail (E_IS_WEB_VIEW (web_view)); gtk_html_set_magic_smileys (GTK_HTML (web_view), magic_smileys); g_object_notify (G_OBJECT (web_view), "magic-smileys"); #endif } const gchar * e_web_view_get_selected_uri (EWebView *web_view) { g_return_val_if_fail (E_IS_WEB_VIEW (web_view), NULL); return web_view->priv->selected_uri; } void e_web_view_set_selected_uri (EWebView *web_view, const gchar *selected_uri) { g_return_if_fail (E_IS_WEB_VIEW (web_view)); g_free (web_view->priv->selected_uri); web_view->priv->selected_uri = g_strdup (selected_uri); g_object_notify (G_OBJECT (web_view), "selected-uri"); } GdkPixbufAnimation * e_web_view_get_cursor_image (EWebView *web_view) { g_return_val_if_fail (E_IS_WEB_VIEW (web_view), NULL); return web_view->priv->cursor_image; } void e_web_view_set_cursor_image (EWebView *web_view, GdkPixbufAnimation *image) { g_return_if_fail (E_IS_WEB_VIEW (web_view)); if (image != NULL) g_object_ref (image); if (web_view->priv->cursor_image != NULL) g_object_unref (web_view->priv->cursor_image); web_view->priv->cursor_image = image; g_object_notify (G_OBJECT (web_view), "cursor-image"); } const gchar * e_web_view_get_cursor_image_src (EWebView *web_view) { g_return_val_if_fail (E_IS_WEB_VIEW (web_view), NULL); return web_view->priv->cursor_image_src; } void e_web_view_set_cursor_image_src (EWebView *web_view, const gchar *src_uri) { g_return_if_fail (E_IS_WEB_VIEW (web_view)); g_free (web_view->priv->cursor_image_src); web_view->priv->cursor_image_src = g_strdup (src_uri); g_object_notify (G_OBJECT (web_view), "cursor-image-src"); } GtkAction * e_web_view_get_open_proxy (EWebView *web_view) { g_return_val_if_fail (E_IS_WEB_VIEW (web_view), FALSE); return web_view->priv->open_proxy; } void e_web_view_set_open_proxy (EWebView *web_view, GtkAction *open_proxy) { g_return_if_fail (E_IS_WEB_VIEW (web_view)); if (open_proxy != NULL) { g_return_if_fail (GTK_IS_ACTION (open_proxy)); g_object_ref (open_proxy); } if (web_view->priv->open_proxy != NULL) g_object_unref (web_view->priv->open_proxy); web_view->priv->open_proxy = open_proxy; g_object_notify (G_OBJECT (web_view), "open-proxy"); } GtkTargetList * e_web_view_get_paste_target_list (EWebView *web_view) { g_return_val_if_fail (E_IS_WEB_VIEW (web_view), NULL); return webkit_web_view_get_paste_target_list ( WEBKIT_WEB_VIEW (web_view)); } GtkAction * e_web_view_get_print_proxy (EWebView *web_view) { g_return_val_if_fail (E_IS_WEB_VIEW (web_view), FALSE); return web_view->priv->print_proxy; } void e_web_view_set_print_proxy (EWebView *web_view, GtkAction *print_proxy) { g_return_if_fail (E_IS_WEB_VIEW (web_view)); if (print_proxy != NULL) { g_return_if_fail (GTK_IS_ACTION (print_proxy)); g_object_ref (print_proxy); } if (web_view->priv->print_proxy != NULL) g_object_unref (web_view->priv->print_proxy); web_view->priv->print_proxy = print_proxy; g_object_notify (G_OBJECT (web_view), "print-proxy"); } GtkAction * e_web_view_get_save_as_proxy (EWebView *web_view) { g_return_val_if_fail (E_IS_WEB_VIEW (web_view), FALSE); return web_view->priv->save_as_proxy; } void e_web_view_set_save_as_proxy (EWebView *web_view, GtkAction *save_as_proxy) { g_return_if_fail (E_IS_WEB_VIEW (web_view)); if (save_as_proxy != NULL) { g_return_if_fail (GTK_IS_ACTION (save_as_proxy)); g_object_ref (save_as_proxy); } if (web_view->priv->save_as_proxy != NULL) g_object_unref (web_view->priv->save_as_proxy); web_view->priv->save_as_proxy = save_as_proxy; g_object_notify (G_OBJECT (web_view), "save-as-proxy"); } GSList * e_web_view_get_highlights (EWebView *web_view) { g_return_val_if_fail (E_IS_WEB_VIEW (web_view), NULL); return web_view->priv->highlights; } void e_web_view_add_highlight (EWebView *web_view, const gchar *highlight) { g_return_if_fail (E_IS_WEB_VIEW (web_view)); g_return_if_fail (highlight && *highlight); web_view->priv->highlights = g_slist_append (web_view->priv->highlights, g_strdup (highlight)); web_view_update_document_highlights (web_view); } void e_web_view_clear_highlights (EWebView *web_view) { g_return_if_fail (E_IS_WEB_VIEW (web_view)); if (!web_view->priv->highlights) return; g_slist_free_full (web_view->priv->highlights, g_free); web_view->priv->highlights = NULL; web_view_update_document_highlights (web_view); } GtkAction * e_web_view_get_action (EWebView *web_view, const gchar *action_name) { GtkUIManager *ui_manager; g_return_val_if_fail (E_IS_WEB_VIEW (web_view), NULL); g_return_val_if_fail (action_name != NULL, NULL); ui_manager = e_web_view_get_ui_manager (web_view); return e_lookup_action (ui_manager, action_name); } GtkActionGroup * e_web_view_get_action_group (EWebView *web_view, const gchar *group_name) { GtkUIManager *ui_manager; g_return_val_if_fail (E_IS_WEB_VIEW (web_view), NULL); g_return_val_if_fail (group_name != NULL, NULL); ui_manager = e_web_view_get_ui_manager (web_view); return e_lookup_action_group (ui_manager, group_name); } gchar * e_web_view_extract_uri (EWebView *web_view, GdkEventButton *event) { EWebViewClass *class; g_return_val_if_fail (E_IS_WEB_VIEW (web_view), NULL); class = E_WEB_VIEW_GET_CLASS (web_view); g_return_val_if_fail (class->extract_uri != NULL, NULL); return class->extract_uri (web_view, event); } void e_web_view_copy_clipboard (EWebView *web_view) { g_return_if_fail (E_IS_WEB_VIEW (web_view)); webkit_web_view_copy_clipboard (WEBKIT_WEB_VIEW (web_view)); } void e_web_view_cut_clipboard (EWebView *web_view) { g_return_if_fail (E_IS_WEB_VIEW (web_view)); webkit_web_view_cut_clipboard (WEBKIT_WEB_VIEW (web_view)); } gboolean e_web_view_is_selection_active (EWebView *web_view) { g_return_val_if_fail (E_IS_WEB_VIEW (web_view), FALSE); return webkit_web_view_has_selection (WEBKIT_WEB_VIEW (web_view)); } void e_web_view_paste_clipboard (EWebView *web_view) { g_return_if_fail (E_IS_WEB_VIEW (web_view)); webkit_web_view_paste_clipboard (WEBKIT_WEB_VIEW (web_view)); } gboolean e_web_view_scroll_forward (EWebView *web_view) { g_return_val_if_fail (E_IS_WEB_VIEW (web_view), FALSE); webkit_web_view_move_cursor ( WEBKIT_WEB_VIEW (web_view), GTK_MOVEMENT_PAGES, 1); return TRUE; /* XXX This means nothing. */ } gboolean e_web_view_scroll_backward (EWebView *web_view) { g_return_val_if_fail (E_IS_WEB_VIEW (web_view), FALSE); webkit_web_view_move_cursor ( WEBKIT_WEB_VIEW (web_view), GTK_MOVEMENT_PAGES, -1); return TRUE; /* XXX This means nothing. */ } void e_web_view_select_all (EWebView *web_view) { g_return_if_fail (E_IS_WEB_VIEW (web_view)); webkit_web_view_select_all (WEBKIT_WEB_VIEW (web_view)); } void e_web_view_unselect_all (EWebView *web_view) { #if 0 /* WEBKIT */ g_return_if_fail (E_IS_WEB_VIEW (web_view)); gtk_html_command (GTK_HTML (web_view), "unselect-all"); #endif } void e_web_view_zoom_100 (EWebView *web_view) { g_return_if_fail (E_IS_WEB_VIEW (web_view)); webkit_web_view_set_zoom_level (WEBKIT_WEB_VIEW (web_view), 1.0); } void e_web_view_zoom_in (EWebView *web_view) { g_return_if_fail (E_IS_WEB_VIEW (web_view)); webkit_web_view_zoom_in (WEBKIT_WEB_VIEW (web_view)); } void e_web_view_zoom_out (EWebView *web_view) { g_return_if_fail (E_IS_WEB_VIEW (web_view)); webkit_web_view_zoom_out (WEBKIT_WEB_VIEW (web_view)); } GtkUIManager * e_web_view_get_ui_manager (EWebView *web_view) { g_return_val_if_fail (E_IS_WEB_VIEW (web_view), NULL); return web_view->priv->ui_manager; } GtkWidget * e_web_view_get_popup_menu (EWebView *web_view) { GtkUIManager *ui_manager; GtkWidget *menu; g_return_val_if_fail (E_IS_WEB_VIEW (web_view), NULL); ui_manager = e_web_view_get_ui_manager (web_view); menu = gtk_ui_manager_get_widget (ui_manager, "/context"); g_return_val_if_fail (GTK_IS_MENU (menu), NULL); return menu; } void e_web_view_show_popup_menu (EWebView *web_view, GdkEventButton *event, GtkMenuPositionFunc func, gpointer user_data) { GtkWidget *menu; g_return_if_fail (E_IS_WEB_VIEW (web_view)); e_web_view_update_actions (web_view); menu = e_web_view_get_popup_menu (web_view); if (event != NULL) gtk_menu_popup ( GTK_MENU (menu), NULL, NULL, func, user_data, event->button, event->time); else gtk_menu_popup ( GTK_MENU (menu), NULL, NULL, func, user_data, 0, gtk_get_current_event_time ()); } void e_web_view_status_message (EWebView *web_view, const gchar *status_message) { g_return_if_fail (E_IS_WEB_VIEW (web_view)); g_signal_emit (web_view, signals[STATUS_MESSAGE], 0, status_message); } void e_web_view_stop_loading (EWebView *web_view) { g_return_if_fail (E_IS_WEB_VIEW (web_view)); g_signal_emit (web_view, signals[STOP_LOADING], 0); } void e_web_view_update_actions (EWebView *web_view) { g_return_if_fail (E_IS_WEB_VIEW (web_view)); g_signal_emit (web_view, signals[UPDATE_ACTIONS], 0); } gchar * e_web_view_get_selection_html (EWebView *web_view) { WebKitDOMDocument *document; WebKitDOMDOMWindow *window; WebKitDOMDOMSelection *selection; WebKitDOMRange *range; WebKitDOMDocumentFragment *fragment; WebKitDOMHTMLElement *element; g_return_val_if_fail (E_IS_WEB_VIEW (web_view), NULL); if (!webkit_web_view_has_selection (WEBKIT_WEB_VIEW (web_view))) return NULL; document = webkit_web_view_get_dom_document (WEBKIT_WEB_VIEW (web_view)); window = webkit_dom_document_get_default_view (document); selection = webkit_dom_dom_window_get_selection (window); if (!selection) return NULL; range = webkit_dom_dom_selection_get_range_at (selection, 0, NULL); if (!range) return NULL; fragment = webkit_dom_range_clone_contents (range, NULL); if (!fragment) return NULL; element = WEBKIT_DOM_HTML_ELEMENT (webkit_dom_document_create_element (document, "div", NULL)); webkit_dom_node_append_child (WEBKIT_DOM_NODE (element), WEBKIT_DOM_NODE (fragment), NULL); return webkit_dom_html_element_get_inner_html (element); } void e_web_view_set_settings (EWebView *web_view, WebKitWebSettings *settings) { g_return_if_fail (E_IS_WEB_VIEW (web_view)); if (settings == webkit_web_view_get_settings (WEBKIT_WEB_VIEW (web_view))) return; g_object_bind_property (settings, "enable-caret-browsing", web_view, "caret-mode", G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE); webkit_web_view_set_settings (WEBKIT_WEB_VIEW (web_view), settings); } WebKitWebSettings * e_web_view_get_default_settings (void) { WebKitWebSettings *settings; settings = webkit_web_settings_new (); g_object_set (G_OBJECT (settings), "enable-frame-flattening", TRUE, "enable-java-applet", FALSE, "enable-html5-database", FALSE, "enable-html5-local-storage", FALSE, "enable-offline-web-application-cache", FALSE, "enable-site-specific-quirks", TRUE, "enable-scripts", FALSE, NULL); return settings; } void e_web_view_update_fonts (EWebView *web_view) { GString *stylesheet; gchar *base64; gchar *aa = NULL; WebKitWebSettings *settings; PangoFontDescription *min_size, *ms, *vw; const gchar *styles[] = { "normal", "oblique", "italic" }; const gchar *smoothing = NULL; GtkStyleContext *context; GdkColor *link, *visited; ms = NULL; vw = NULL; if (E_WEB_VIEW_GET_CLASS (web_view)->set_fonts) E_WEB_VIEW_GET_CLASS (web_view)->set_fonts (web_view, &ms, &vw); if (ms == NULL) { gchar *font; font = g_settings_get_string ( web_view->priv->font_settings, "monospace-font-name"); ms = pango_font_description_from_string ( font ? font : "monospace 10"); g_free (font); } if (vw == NULL) { gchar *font; font = g_settings_get_string ( web_view->priv->font_settings, "font-name"); vw = pango_font_description_from_string ( font ? font : "serif 10"); g_free (font); } if (pango_font_description_get_size (ms) < pango_font_description_get_size (vw)) { min_size = ms; } else { min_size = vw; } stylesheet = g_string_new (""); g_string_append_printf (stylesheet, "body {\n" " font-family: '%s';\n" " font-size: %dpt;\n" " font-weight: %d;\n" " font-style: %s;\n", pango_font_description_get_family (vw), pango_font_description_get_size (vw) / PANGO_SCALE, pango_font_description_get_weight (vw), styles[pango_font_description_get_style (vw)]); if (web_view->priv->aliasing_settings != NULL) aa = g_settings_get_string ( web_view->priv->aliasing_settings, "antialiasing"); if (g_strcmp0 (aa, "none") == 0) smoothing = "none"; else if (g_strcmp0 (aa, "grayscale") == 0) smoothing = "antialiased"; else if (g_strcmp0 (aa, "rgba") == 0) smoothing = "subpixel-antialiased"; if (smoothing != NULL) g_string_append_printf ( stylesheet, " -webkit-font-smoothing: %s;\n", smoothing); g_free (aa); g_string_append (stylesheet, "}\n"); g_string_append_printf (stylesheet, "pre,code,.pre {\n" " font-family: '%s';\n" " font-size: %dpt;\n" " font-weight: %d;\n" " font-style: %s;\n" "}", pango_font_description_get_family (ms), pango_font_description_get_size (ms) / PANGO_SCALE, pango_font_description_get_weight (ms), styles[pango_font_description_get_style (ms)]); context = gtk_widget_get_style_context (GTK_WIDGET (web_view)); gtk_style_context_get_style (context, "link-color", &link, "visited-link-color", &visited, NULL); if (!link) { link = g_new0 (GdkColor, 1); link->blue = G_MAXINT16; } if (!visited) { visited = g_new0 (GdkColor, 1); visited->red = G_MAXINT16; } g_string_append_printf (stylesheet, "a {\n" " color: #%06x;\n" "}\n" "a:visited {\n" " color: #%06x;\n" "}\n", e_color_to_value (link), e_color_to_value (visited)); if (link) gdk_color_free (link); if (visited) gdk_color_free (visited); base64 = g_base64_encode ((guchar *) stylesheet->str, stylesheet->len); g_string_free (stylesheet, TRUE); stylesheet = g_string_new ("data:text/css;charset=utf-8;base64,"); g_string_append (stylesheet, base64); g_free (base64); settings = webkit_web_view_get_settings (WEBKIT_WEB_VIEW (web_view)); g_object_set (G_OBJECT (settings), "default-font-size", pango_font_description_get_size (vw) / PANGO_SCALE, "default-font-family", pango_font_description_get_family (vw), "monospace-font-family", pango_font_description_get_family (ms), "default-monospace-font-size", (pango_font_description_get_size (ms) / PANGO_SCALE), "minimum-font-size", (pango_font_description_get_size (min_size) / PANGO_SCALE), "user-stylesheet-uri", stylesheet->str, NULL); g_string_free (stylesheet, TRUE); pango_font_description_free (ms); pango_font_description_free (vw); } void e_web_view_install_request_handler (EWebView *web_view, GType handler_type) { SoupSession *session; SoupSessionFeature *feature; gboolean new; session = webkit_get_default_session (); feature = soup_session_get_feature (session, SOUP_TYPE_REQUESTER); new = FALSE; if (feature == NULL) { feature = SOUP_SESSION_FEATURE (soup_requester_new ()); soup_session_add_feature (session, feature); new = TRUE; } soup_session_feature_add_feature (feature, handler_type); if (new) { g_object_unref (feature); } }