/*
* 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 <http://www.gnu.org/licenses/>
*
*/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include "e-web-view.h"
#include <math.h>
#include <JavaScriptCore/JavaScript.h>
#include <string.h>
#include <glib/gi18n-lib.h>
#include <pango/pango.h>
#include <camel/camel.h>
#include <libebackend/libebackend.h>
#include <e-util/e-util.h>
#include <libevolution-utils/e-alert-dialog.h>
#include <libevolution-utils/e-alert-sink.h>
#include <e-util/e-plugin-ui.h>
#include <e-util/e-file-request.h>
#include <e-util/e-stock-request.h>
#define LIBSOUP_USE_UNSTABLE_REQUEST_API
#include <libsoup/soup.h>
#include <libsoup/soup-requester.h>
#include "e-popup-action.h"
#include "e-selectable.h"
#include <stdlib.h>
#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 guint signals[LAST_SIGNAL];
static const gchar *ui =
"<ui>"
" <popup name='context'>"
" <menuitem action='copy-clipboard'/>"
" <separator/>"
" <placeholder name='custom-actions-1'>"
" <menuitem action='open'/>"
" <menuitem action='save-as'/>"
" <menuitem action='http-open'/>"
" <menuitem action='send-message'/>"
" <menuitem action='print'/>"
" </placeholder>"
" <placeholder name='custom-actions-2'>"
" <menuitem action='uri-copy'/>"
" <menuitem action='mailto-copy'/>"
" <menuitem action='image-copy'/>"
" </placeholder>"
" <placeholder name='custom-actions-3'/>"
" <separator/>"
" <menuitem action='select-all'/>"
" <placeholder name='inspect-menu' />"
" </popup>"
"</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);
/* See https://bugzilla.gnome.org/show_bug.cgi?id=681400
* FIXME: This can be removed once we require WebKitGtk 1.10+ */
#if WEBKIT_CHECK_VERSION (1, 9, 6)
webkit_dom_element_set_class_name (
span, "__evo-highlight");
#else
webkit_dom_html_element_set_class_name (
WEBKIT_DOM_HTML_ELEMENT (span), "__evo-highlight");
#endif
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;
GtkAllocation allocation, allocation_copy;
status = webkit_web_view_get_load_status (web_view);
if (status != WEBKIT_LOAD_FINISHED)
return;
/* Workaround webkit bug https://bugs.webkit.org/show_bug.cgi?id=89553 */
gtk_widget_get_allocation (GTK_WIDGET (web_view), &allocation_copy);
allocation = allocation_copy;
allocation.width -= 10;
allocation.height -= 10;
gtk_widget_size_allocate (GTK_WIDGET (web_view), &allocation);
gtk_widget_size_allocate (GTK_WIDGET (web_view), &allocation_copy);
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 (e_web_view_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 (e_web_view_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
e_extensible_load_extensions (E_EXTENSIBLE (object));
/* Chain up to parent's constructed() method. */
G_OBJECT_CLASS (e_web_view_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;
}
g_list_free (subresources);
}
g_object_unref (test);
}
if (event != NULL && event->button != 3)
goto chainup;
uri = e_web_view_extract_uri (web_view, event);
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 (e_web_view_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);
g_object_unref (result);
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,
GdkEventButton *event)
{
GtkActionGroup *action_group;
gboolean can_copy;
gboolean scheme_is_http = FALSE;
gboolean scheme_is_mailto = FALSE;
gboolean uri_is_valid = FALSE;
gboolean has_cursor_image;
gboolean visible;
WebKitHitTestResult *hit_test;
WebKitHitTestResultContext context;
const gchar *group_name;
const gchar *uri;
uri = e_web_view_get_selected_uri (web_view);
can_copy = webkit_web_view_can_copy_clipboard (
WEBKIT_WEB_VIEW (web_view));
hit_test = webkit_web_view_get_hit_test_result (
WEBKIT_WEB_VIEW (web_view), event);
g_object_get (G_OBJECT (hit_test), "context", &context, NULL);
has_cursor_image = (context & WEBKIT_HIT_TEST_RESULT_CONTEXT_IMAGE);
/* 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 = can_copy;
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,
"<html>"
"<head>"
"<meta http-equiv=\"content-type\""
" content=\"text/html; charset=utf-8\">"
"</head>"
"<body>");
g_string_append (
buffer,
"<table bgcolor='#000000' width='100%'"
" cellpadding='1' cellspacing='0'>"
"<tr>"
"<td>"
"<table bgcolor='#dddddd' width='100%' cellpadding='6'>"
"<tr>");
g_string_append_printf (
buffer,
"<tr>"
"<td valign='top'>"
"<img src='gtk-stock://%s/?size=%d'/>"
"</td>"
"<td align='left' width='100%%'>"
"<h3>%s</h3>"
"%s"
"</td>"
"</tr>",
icon_name,
GTK_ICON_SIZE_DIALOG,
e_alert_get_primary_text (alert),
e_alert_get_secondary_text (alert));
g_string_append (
buffer,
"</table>"
"</td>"
"</tr>"
"</table>"
"</body>"
"</html>");
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
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__POINTER,
G_TYPE_NONE, 1, G_TYPE_POINTER);
/* 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);
}
GtkWidget *
e_web_view_new (void)
{
return g_object_new (E_TYPE_WEB_VIEW, NULL);
}
void
e_web_view_clear (EWebView *web_view)
{
GtkStyle *style;
gchar *html;
g_return_if_fail (E_IS_WEB_VIEW (web_view));
style = gtk_widget_get_style (GTK_WIDGET (web_view));
html = g_strdup_printf (
"<html><head></head><body bgcolor=\"#%06x\"></body></html>",
e_color_to_value (&style->base[GTK_STATE_NORMAL]));
webkit_web_view_load_html_string (WEBKIT_WEB_VIEW (web_view), html, NULL);
g_free (html);
}
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));
if ((web_view->priv->caret_mode ? 1 : 0) == (caret_mode ? 1 : 0))
return;
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));
if ((web_view->priv->disable_printing ? 1 : 0) == (disable_printing ? 1 : 0))
return;
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));
if ((web_view->priv->disable_save_to_disk ? 1 : 0) == (disable_save_to_disk ? 1 : 0))
return;
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));
if (g_strcmp0 (web_view->priv->selected_uri, selected_uri) == 0)
return;
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 (web_view->priv->cursor_image == image)
return;
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));
if (g_strcmp0 (web_view->priv->cursor_image_src, src_uri) == 0)
return;
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 (web_view->priv->open_proxy == open_proxy)
return;
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 (web_view->priv->print_proxy == print_proxy)
return;
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 (web_view->priv->save_as_proxy == save_as_proxy)
return;
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, event);
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,
GdkEventButton *event)
{
g_return_if_fail (E_IS_WEB_VIEW (web_view));
g_signal_emit (web_view, signals[UPDATE_ACTIONS], 0, event);
}
static gchar *
web_view_get_frame_selection_html (WebKitDOMElement *iframe)
{
WebKitDOMDocument *document;
WebKitDOMDOMWindow *window;
WebKitDOMDOMSelection *selection;
WebKitDOMNodeList *frames;
gulong ii, length;
document = webkit_dom_html_iframe_element_get_content_document (
WEBKIT_DOM_HTML_IFRAME_ELEMENT (iframe));
window = webkit_dom_document_get_default_view (document);
selection = webkit_dom_dom_window_get_selection (window);
if (selection && (webkit_dom_dom_selection_get_range_count (selection) > 0)) {
WebKitDOMRange *range;
WebKitDOMElement *element;
WebKitDOMDocumentFragment *fragment;
range = webkit_dom_dom_selection_get_range_at (selection, 0, NULL);
if (range) {
fragment = webkit_dom_range_clone_contents (
range, NULL);
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 (
WEBKIT_DOM_HTML_ELEMENT (element));
}
}
frames = webkit_dom_document_get_elements_by_tag_name (
document, "IFRAME");
length = webkit_dom_node_list_get_length (frames);
for (ii = 0; ii < length; ii++) {
WebKitDOMNode *node;
gchar *text;
node = webkit_dom_node_list_item (frames, ii);
text = web_view_get_frame_selection_html (
WEBKIT_DOM_ELEMENT (node));
if (text) {
return text;
}
}
return NULL;
}
gchar *
e_web_view_get_selection_html (EWebView *web_view)
{
WebKitDOMDocument *document;
WebKitDOMNodeList *frames;
gulong ii, length;
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));
frames = webkit_dom_document_get_elements_by_tag_name (document, "IFRAME");
length = webkit_dom_node_list_get_length (frames);
for (ii = 0; ii < length; ii++) {
gchar *text;
WebKitDOMNode *node;
node = webkit_dom_node_list_item (frames, ii);
text = web_view_get_frame_selection_html (
WEBKIT_DOM_ELEMENT (node));
if (text) {
return text;
}
}
return NULL;
}
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 = NULL;
GdkColor *visited = NULL;
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 == NULL) {
link = g_slice_new0 (GdkColor);
link->blue = G_MAXINT16;
}
if (visited == NULL) {
visited = g_slice_new0 (GdkColor);
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));
gdk_color_free (link);
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);
}
}