4551 lines
120 KiB
C
4551 lines
120 KiB
C
/*
|
|
* 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.
|
|
*
|
|
* 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 General Public License
|
|
* for more details.
|
|
*
|
|
* You should have received a copy of the GNU Lesser General Public License
|
|
* along with this program; if not, see <http://www.gnu.org/licenses/>.
|
|
*
|
|
*/
|
|
|
|
#include "evolution-config.h"
|
|
|
|
#include <glib/gi18n-lib.h>
|
|
|
|
#include <math.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
#include <pango/pango.h>
|
|
|
|
#include <camel/camel.h>
|
|
#include <libebackend/libebackend.h>
|
|
|
|
#include <libsoup/soup.h>
|
|
|
|
#include "e-alert-dialog.h"
|
|
#include "e-alert-sink.h"
|
|
#include "e-file-request.h"
|
|
#include "e-misc-utils.h"
|
|
#include "e-plugin-ui.h"
|
|
#include "e-popup-action.h"
|
|
#include "e-selectable.h"
|
|
#include "e-stock-request.h"
|
|
#include "e-web-view-jsc-utils.h"
|
|
|
|
#include "e-web-view.h"
|
|
|
|
#define E_WEB_VIEW_GET_PRIVATE(obj) \
|
|
(G_TYPE_INSTANCE_GET_PRIVATE \
|
|
((obj), E_TYPE_WEB_VIEW, EWebViewPrivate))
|
|
|
|
typedef struct _AsyncContext AsyncContext;
|
|
|
|
typedef struct _ElementClickedData {
|
|
EWebViewElementClickedFunc callback;
|
|
gpointer user_data;
|
|
} ElementClickedData;
|
|
|
|
struct _EWebViewPrivate {
|
|
GtkUIManager *ui_manager;
|
|
gchar *selected_uri;
|
|
gchar *cursor_image_src;
|
|
|
|
GQueue highlights;
|
|
gboolean highlights_enabled;
|
|
|
|
GtkAction *open_proxy;
|
|
GtkAction *print_proxy;
|
|
GtkAction *save_as_proxy;
|
|
|
|
/* Lockdown Options */
|
|
gboolean disable_printing;
|
|
gboolean disable_save_to_disk;
|
|
|
|
gboolean caret_mode;
|
|
|
|
GSettings *font_settings;
|
|
gulong font_name_changed_handler_id;
|
|
gulong monospace_font_name_changed_handler_id;
|
|
|
|
GHashTable *scheme_handlers; /* gchar *scheme ~> EContentRequest */
|
|
GHashTable *old_settings;
|
|
|
|
WebKitFindController *find_controller;
|
|
gulong found_text_handler_id;
|
|
gulong failed_to_find_text_handler_id;
|
|
|
|
gboolean has_hover_link;
|
|
|
|
GHashTable *element_clicked_cbs; /* gchar *element_class ~> GPtrArray {ElementClickedData} */
|
|
|
|
gboolean has_selection;
|
|
gboolean need_input;
|
|
|
|
GCancellable *cancellable;
|
|
|
|
gchar *last_popup_iframe_src;
|
|
gchar *last_popup_iframe_id;
|
|
gchar *last_popup_element_id;
|
|
gchar *last_popup_link_uri;
|
|
|
|
gint minimum_font_size;
|
|
};
|
|
|
|
struct _AsyncContext {
|
|
GTask *task;
|
|
EActivity *activity;
|
|
GFile *destination;
|
|
GInputStream *input_stream;
|
|
EContentRequest *content_request;
|
|
gchar *uri;
|
|
};
|
|
|
|
enum {
|
|
PROP_0,
|
|
PROP_CARET_MODE,
|
|
PROP_COPY_TARGET_LIST,
|
|
PROP_CURSOR_IMAGE_SRC,
|
|
PROP_DISABLE_PRINTING,
|
|
PROP_DISABLE_SAVE_TO_DISK,
|
|
PROP_HAS_SELECTION,
|
|
PROP_NEED_INPUT,
|
|
PROP_MINIMUM_FONT_SIZE,
|
|
PROP_OPEN_PROXY,
|
|
PROP_PASTE_TARGET_LIST,
|
|
PROP_PRINT_PROXY,
|
|
PROP_SAVE_AS_PROXY,
|
|
PROP_SELECTED_URI
|
|
};
|
|
|
|
enum {
|
|
NEW_ACTIVITY,
|
|
POPUP_EVENT,
|
|
STATUS_MESSAGE,
|
|
STOP_LOADING,
|
|
UPDATE_ACTIONS,
|
|
PROCESS_MAILTO,
|
|
URI_REQUESTED,
|
|
CONTENT_LOADED,
|
|
BEFORE_POPUP_EVENT,
|
|
RESOURCE_LOADED,
|
|
LAST_SIGNAL
|
|
};
|
|
|
|
static guint signals[LAST_SIGNAL];
|
|
|
|
static const gchar *ui =
|
|
"<ui>"
|
|
" <popup name='context'>"
|
|
" <menuitem action='copy-clipboard'/>"
|
|
" <menuitem action='search-web'/>"
|
|
" <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='mailto-copy-raw'/>"
|
|
" <menuitem action='image-copy'/>"
|
|
" <menuitem action='image-save'/>"
|
|
" </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 *iface);
|
|
static void e_web_view_selectable_init (ESelectableInterface *iface);
|
|
|
|
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
|
|
async_context_free (gpointer ptr)
|
|
{
|
|
AsyncContext *async_context = ptr;
|
|
|
|
if (!async_context)
|
|
return;
|
|
|
|
g_clear_object (&async_context->task);
|
|
g_clear_object (&async_context->activity);
|
|
g_clear_object (&async_context->destination);
|
|
g_clear_object (&async_context->input_stream);
|
|
g_clear_object (&async_context->content_request);
|
|
g_free (async_context->uri);
|
|
|
|
g_slice_free (AsyncContext, async_context);
|
|
}
|
|
|
|
static void
|
|
e_web_view_update_spell_checking (EWebView *web_view,
|
|
GSettings *settings)
|
|
{
|
|
WebKitWebContext *web_context;
|
|
|
|
web_context = webkit_web_view_get_context (WEBKIT_WEB_VIEW (web_view));
|
|
|
|
if (g_settings_get_boolean (settings, "composer-inline-spelling")) {
|
|
gchar **languages;
|
|
|
|
languages = g_settings_get_strv (settings, "composer-spell-languages");
|
|
|
|
webkit_web_context_set_spell_checking_languages (web_context, (const gchar * const *) languages);
|
|
webkit_web_context_set_spell_checking_enabled (web_context, languages != NULL);
|
|
|
|
g_strfreev (languages);
|
|
} else {
|
|
webkit_web_context_set_spell_checking_languages (web_context, NULL);
|
|
webkit_web_context_set_spell_checking_enabled (web_context, FALSE);
|
|
}
|
|
}
|
|
|
|
static void
|
|
e_web_view_spell_settings_changed_cb (GSettings *settings,
|
|
const gchar *key,
|
|
gpointer user_data)
|
|
{
|
|
EWebView *web_view = user_data;
|
|
|
|
e_web_view_update_spell_checking (web_view, settings);
|
|
}
|
|
|
|
static void
|
|
action_copy_clipboard_cb (GtkAction *action,
|
|
EWebView *web_view)
|
|
{
|
|
e_web_view_copy_clipboard (web_view);
|
|
}
|
|
|
|
static void
|
|
e_web_view_search_web_get_selection_cb (GObject *source,
|
|
GAsyncResult *result,
|
|
gpointer user_data)
|
|
{
|
|
GSList *texts;
|
|
GError *local_error = NULL;
|
|
|
|
g_return_if_fail (E_IS_WEB_VIEW (source));
|
|
|
|
e_web_view_jsc_get_selection_finish (WEBKIT_WEB_VIEW (source), result, &texts, &local_error);
|
|
|
|
if (local_error &&
|
|
!g_error_matches (local_error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) {
|
|
e_alert_submit (E_ALERT_SINK (source), "widgets:get-selected-text-failed", local_error->message, NULL);
|
|
} else if (texts) {
|
|
GSettings *settings;
|
|
gchar *text = texts->data;
|
|
gchar *uri_prefix;
|
|
gchar *escaped;
|
|
gchar *uri;
|
|
|
|
g_strstrip (text);
|
|
|
|
settings = e_util_ref_settings ("org.gnome.evolution.shell");
|
|
uri_prefix = g_settings_get_string (settings, "search-web-uri-prefix");
|
|
g_object_unref (settings);
|
|
|
|
escaped = camel_url_encode (text, "& ?#:;,/\\");
|
|
|
|
uri = g_strconcat (uri_prefix, escaped, NULL);
|
|
if (uri && g_ascii_strncasecmp (uri, "https://", 8) == 0) {
|
|
GtkWidget *toplevel;
|
|
|
|
toplevel = gtk_widget_get_toplevel (GTK_WIDGET (source));
|
|
|
|
e_show_uri (GTK_IS_WINDOW (toplevel) ? GTK_WINDOW (toplevel) : NULL, uri);
|
|
} else {
|
|
g_printerr ("Incorrect URI provided, expects https:// prefix, but has got: '%s'\n", uri ? uri : "null");
|
|
}
|
|
|
|
g_free (uri_prefix);
|
|
g_free (escaped);
|
|
g_free (uri);
|
|
}
|
|
|
|
g_clear_error (&local_error);
|
|
g_slist_free_full (texts, g_free);
|
|
}
|
|
|
|
static void
|
|
action_search_web_cb (GtkAction *action,
|
|
EWebView *web_view)
|
|
{
|
|
e_web_view_jsc_get_selection (WEBKIT_WEB_VIEW (web_view), E_TEXT_FORMAT_PLAIN, web_view->priv->cancellable,
|
|
e_web_view_search_web_get_selection_cb, NULL);
|
|
}
|
|
|
|
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
|
|
webview_mailto_copy (EWebView *web_view,
|
|
gboolean only_email_address)
|
|
{
|
|
CamelURL *curl;
|
|
CamelInternetAddress *inet_addr;
|
|
GtkClipboard *clipboard;
|
|
const gchar *uri, *name = NULL, *email = NULL;
|
|
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);
|
|
if (only_email_address &&
|
|
camel_internet_address_get (inet_addr, 0, &name, &email) &&
|
|
email && *email) {
|
|
text = g_strdup (email);
|
|
} else {
|
|
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_mailto_copy_cb (GtkAction *action,
|
|
EWebView *web_view)
|
|
{
|
|
webview_mailto_copy (web_view, FALSE);
|
|
}
|
|
|
|
static void
|
|
action_mailto_copy_raw_cb (GtkAction *action,
|
|
EWebView *web_view)
|
|
{
|
|
webview_mailto_copy (web_view, TRUE);
|
|
}
|
|
|
|
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;
|
|
|
|
uri = e_web_view_get_selected_uri (web_view);
|
|
g_return_if_fail (uri != NULL);
|
|
|
|
clipboard = gtk_clipboard_get (GDK_SELECTION_PRIMARY);
|
|
gtk_clipboard_set_text (clipboard, uri, -1);
|
|
gtk_clipboard_store (clipboard);
|
|
|
|
clipboard = gtk_clipboard_get (GDK_SELECTION_CLIPBOARD);
|
|
gtk_clipboard_set_text (clipboard, uri, -1);
|
|
gtk_clipboard_store (clipboard);
|
|
}
|
|
|
|
static void
|
|
action_image_copy_cb (GtkAction *action,
|
|
EWebView *web_view)
|
|
{
|
|
e_web_view_cursor_image_copy (web_view);
|
|
}
|
|
|
|
static void
|
|
action_image_save_cb (GtkAction *action,
|
|
EWebView *web_view)
|
|
{
|
|
e_web_view_cursor_image_save (web_view);
|
|
}
|
|
|
|
static GtkActionEntry uri_entries[] = {
|
|
|
|
{ "uri-copy",
|
|
"edit-copy",
|
|
N_("_Copy Link Location"),
|
|
"<Control>c",
|
|
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",
|
|
"edit-copy",
|
|
N_("_Copy Email Address"),
|
|
"<Control>c",
|
|
N_("Copy the email address to the clipboard"),
|
|
G_CALLBACK (action_mailto_copy_cb) },
|
|
|
|
{ "mailto-copy-raw",
|
|
"edit-copy",
|
|
N_("Copy _Raw Email Address"),
|
|
NULL,
|
|
N_("Copy the raw email address to the clipboard"),
|
|
G_CALLBACK (action_mailto_copy_raw_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",
|
|
"edit-copy",
|
|
N_("_Copy Image"),
|
|
"<Control>c",
|
|
N_("Copy the image to the clipboard"),
|
|
G_CALLBACK (action_image_copy_cb) },
|
|
|
|
{ "image-save",
|
|
"document-save",
|
|
N_("Save _Image…"),
|
|
"<Control>s",
|
|
N_("Save the image to a file"),
|
|
G_CALLBACK (action_image_save_cb) }
|
|
};
|
|
|
|
static GtkActionEntry selection_entries[] = {
|
|
|
|
{ "copy-clipboard",
|
|
"edit-copy",
|
|
N_("_Copy"),
|
|
"<Control>c",
|
|
N_("Copy the selection"),
|
|
G_CALLBACK (action_copy_clipboard_cb) },
|
|
|
|
{ "search-web",
|
|
NULL,
|
|
N_("Search _Web…"),
|
|
NULL,
|
|
N_("Search the Web with the selected text"),
|
|
G_CALLBACK (action_search_web_cb) }
|
|
};
|
|
|
|
static GtkActionEntry standard_entries[] = {
|
|
|
|
{ "select-all",
|
|
"edit-select-all",
|
|
N_("Select _All"),
|
|
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
|
|
webkit_find_controller_found_text_cb (WebKitFindController *find_controller,
|
|
guint match_count,
|
|
EWebView *web_view)
|
|
{
|
|
if (web_view->priv->highlights_enabled && !g_queue_is_empty (&web_view->priv->highlights))
|
|
e_web_view_unselect_all (web_view);
|
|
}
|
|
|
|
static void
|
|
webkit_find_controller_failed_to_found_text_cb (WebKitFindController *find_controller,
|
|
EWebView *web_view)
|
|
{
|
|
}
|
|
|
|
static void
|
|
web_view_set_find_controller (EWebView *web_view)
|
|
{
|
|
WebKitFindController *find_controller;
|
|
|
|
find_controller =
|
|
webkit_web_view_get_find_controller (WEBKIT_WEB_VIEW (web_view));
|
|
|
|
web_view->priv->found_text_handler_id = g_signal_connect (
|
|
find_controller, "found-text",
|
|
G_CALLBACK (webkit_find_controller_found_text_cb), web_view);
|
|
|
|
web_view->priv->failed_to_find_text_handler_id = g_signal_connect (
|
|
find_controller, "failed-to-find-text",
|
|
G_CALLBACK (webkit_find_controller_failed_to_found_text_cb), web_view);
|
|
|
|
web_view->priv->find_controller = find_controller;
|
|
}
|
|
|
|
static void
|
|
web_view_update_document_highlights (EWebView *web_view)
|
|
{
|
|
GList *head, *link;
|
|
|
|
head = g_queue_peek_head_link (&web_view->priv->highlights);
|
|
|
|
for (link = head; link != NULL; link = g_list_next (link)) {
|
|
webkit_find_controller_search (
|
|
web_view->priv->find_controller,
|
|
link->data,
|
|
WEBKIT_FIND_OPTIONS_CASE_INSENSITIVE,
|
|
G_MAXUINT);
|
|
}
|
|
}
|
|
|
|
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 void
|
|
web_view_got_elem_from_point_for_popup_event_cb (GObject *source_object,
|
|
GAsyncResult *result,
|
|
gpointer user_data)
|
|
{
|
|
EWebView *web_view;
|
|
GdkEvent *event = user_data;
|
|
GError *error = NULL;
|
|
|
|
g_return_if_fail (E_IS_WEB_VIEW (source_object));
|
|
|
|
web_view = E_WEB_VIEW (source_object);
|
|
|
|
g_clear_pointer (&web_view->priv->last_popup_iframe_src, g_free);
|
|
g_clear_pointer (&web_view->priv->last_popup_iframe_id, g_free);
|
|
g_clear_pointer (&web_view->priv->last_popup_element_id, g_free);
|
|
|
|
if (!e_web_view_jsc_get_element_from_point_finish (WEBKIT_WEB_VIEW (web_view), result,
|
|
&web_view->priv->last_popup_iframe_src,
|
|
&web_view->priv->last_popup_iframe_id,
|
|
&web_view->priv->last_popup_element_id,
|
|
&error)) {
|
|
g_warning ("%s: Failed to get element from point: %s", G_STRFUNC, error ? error->message : "Unknown error");
|
|
}
|
|
|
|
if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) {
|
|
gboolean handled = FALSE;
|
|
|
|
g_signal_emit (web_view, signals[BEFORE_POPUP_EVENT], 0,
|
|
web_view->priv->last_popup_link_uri, NULL);
|
|
|
|
g_signal_emit (web_view, signals[POPUP_EVENT], 0,
|
|
web_view->priv->last_popup_link_uri, event, &handled);
|
|
}
|
|
|
|
if (event)
|
|
gdk_event_free (event);
|
|
g_clear_error (&error);
|
|
}
|
|
|
|
static gboolean
|
|
web_view_context_menu_cb (WebKitWebView *webkit_web_view,
|
|
WebKitContextMenu *context_menu,
|
|
GdkEvent *event,
|
|
WebKitHitTestResult *hit_test_result,
|
|
gpointer user_data)
|
|
{
|
|
WebKitHitTestResultContext context;
|
|
EWebView *web_view;
|
|
gchar *link_uri = NULL;
|
|
gdouble xx, yy;
|
|
|
|
web_view = E_WEB_VIEW (webkit_web_view);
|
|
|
|
g_clear_pointer (&web_view->priv->cursor_image_src, g_free);
|
|
g_clear_pointer (&web_view->priv->last_popup_iframe_src, g_free);
|
|
g_clear_pointer (&web_view->priv->last_popup_iframe_id, g_free);
|
|
g_clear_pointer (&web_view->priv->last_popup_element_id, g_free);
|
|
g_clear_pointer (&web_view->priv->last_popup_link_uri, g_free);
|
|
|
|
if (!hit_test_result)
|
|
return FALSE;
|
|
|
|
context = webkit_hit_test_result_get_context (hit_test_result);
|
|
|
|
/* Show the default menu for an editable */
|
|
if ((context & WEBKIT_HIT_TEST_RESULT_CONTEXT_EDITABLE) != 0)
|
|
return FALSE;
|
|
|
|
if (context & WEBKIT_HIT_TEST_RESULT_CONTEXT_IMAGE) {
|
|
gchar *image_uri = NULL;
|
|
|
|
g_object_get (hit_test_result, "image-uri", &image_uri, NULL);
|
|
|
|
web_view->priv->cursor_image_src = image_uri;
|
|
}
|
|
|
|
if (context & WEBKIT_HIT_TEST_RESULT_CONTEXT_LINK)
|
|
g_object_get (hit_test_result, "link-uri", &link_uri, NULL);
|
|
|
|
web_view->priv->last_popup_link_uri = link_uri;
|
|
|
|
if (!gdk_event_get_coords (event, &xx, &yy)) {
|
|
xx = 1;
|
|
yy = 1;
|
|
}
|
|
|
|
e_web_view_jsc_get_element_from_point (WEBKIT_WEB_VIEW (web_view), xx, yy, web_view->priv->cancellable,
|
|
web_view_got_elem_from_point_for_popup_event_cb, event ? gdk_event_copy (event) : NULL);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static void
|
|
web_view_mouse_target_changed_cb (EWebView *web_view,
|
|
WebKitHitTestResult *hit_test_result,
|
|
guint modifiers,
|
|
gpointer user_data)
|
|
{
|
|
EWebViewClass *class;
|
|
const gchar *title, *uri;
|
|
|
|
title = webkit_hit_test_result_get_link_title (hit_test_result);
|
|
uri = webkit_hit_test_result_get_link_uri (hit_test_result);
|
|
|
|
web_view->priv->has_hover_link = uri && *uri;
|
|
|
|
/* 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 != NULL);
|
|
g_return_if_fail (class->hovering_over_link != NULL);
|
|
|
|
class->hovering_over_link (web_view, title, uri);
|
|
}
|
|
|
|
static gboolean
|
|
web_view_decide_policy_cb (EWebView *web_view,
|
|
WebKitPolicyDecision *decision,
|
|
WebKitPolicyDecisionType type)
|
|
{
|
|
EWebViewClass *class;
|
|
WebKitNavigationPolicyDecision *navigation_decision;
|
|
WebKitNavigationAction *navigation_action;
|
|
WebKitNavigationType navigation_type;
|
|
WebKitURIRequest *request;
|
|
const gchar *uri, *view_uri;
|
|
|
|
if (type != WEBKIT_POLICY_DECISION_TYPE_NAVIGATION_ACTION &&
|
|
type != WEBKIT_POLICY_DECISION_TYPE_NEW_WINDOW_ACTION)
|
|
return FALSE;
|
|
|
|
navigation_decision = WEBKIT_NAVIGATION_POLICY_DECISION (decision);
|
|
navigation_action = webkit_navigation_policy_decision_get_navigation_action (navigation_decision);
|
|
navigation_type = webkit_navigation_action_get_navigation_type (navigation_action);
|
|
|
|
if (navigation_type != WEBKIT_NAVIGATION_TYPE_LINK_CLICKED)
|
|
return FALSE;
|
|
|
|
request = webkit_navigation_action_get_request (navigation_action);
|
|
uri = webkit_uri_request_get_uri (request);
|
|
view_uri = webkit_web_view_get_uri (WEBKIT_WEB_VIEW (web_view));
|
|
|
|
/* Allow navigation through fragments in page */
|
|
if (uri && *uri && view_uri && *view_uri) {
|
|
GUri *uri_link, *uri_view;
|
|
|
|
uri_link = g_uri_parse (uri, SOUP_HTTP_URI_FLAGS | G_URI_FLAGS_PARSE_RELAXED, NULL);
|
|
uri_view = g_uri_parse (view_uri, SOUP_HTTP_URI_FLAGS | G_URI_FLAGS_PARSE_RELAXED, NULL);
|
|
if (uri_link && uri_view) {
|
|
const gchar *tmp1, *tmp2;
|
|
|
|
tmp1 = g_uri_get_scheme (uri_link);
|
|
tmp2 = g_uri_get_scheme (uri_view);
|
|
|
|
/* The scheme on both URIs should be the same */
|
|
if (tmp1 && tmp2 && g_ascii_strcasecmp (tmp1, tmp2) != 0)
|
|
goto free_uris;
|
|
|
|
tmp1 = g_uri_get_host (uri_link);
|
|
tmp2 = g_uri_get_host (uri_view);
|
|
|
|
/* The host on both URIs should be the same */
|
|
if (tmp1 && tmp2 && g_ascii_strcasecmp (tmp1, tmp2) != 0)
|
|
goto free_uris;
|
|
|
|
/* URI from link should have fragment set - could be empty */
|
|
if (g_uri_get_fragment (uri_link)) {
|
|
g_uri_unref (uri_link);
|
|
g_uri_unref (uri_view);
|
|
webkit_policy_decision_use (decision);
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
free_uris:
|
|
if (uri_link)
|
|
g_uri_unref (uri_link);
|
|
if (uri_view)
|
|
g_uri_unref (uri_view);
|
|
}
|
|
|
|
/* 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 != NULL, FALSE);
|
|
g_return_val_if_fail (class->link_clicked != NULL, FALSE);
|
|
|
|
webkit_policy_decision_ignore (decision);
|
|
|
|
class->link_clicked (web_view, uri);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static void
|
|
e_web_view_update_styles (EWebView *web_view,
|
|
const gchar *iframe_id)
|
|
{
|
|
GdkRGBA color;
|
|
gchar *color_value;
|
|
gchar *style;
|
|
GtkStyleContext *style_context;
|
|
WebKitWebView *webkit_web_view;
|
|
gdouble bg_brightness, fg_brightness;
|
|
|
|
style_context = gtk_widget_get_style_context (GTK_WIDGET (web_view));
|
|
|
|
if (gtk_style_context_lookup_color (style_context, "theme_base_color", &color))
|
|
color_value = g_strdup_printf ("#%06x", e_rgba_to_value (&color));
|
|
else {
|
|
color_value = g_strdup (E_UTILS_DEFAULT_THEME_BASE_COLOR);
|
|
if (!gdk_rgba_parse (&color, color_value)) {
|
|
color.red = 1.0;
|
|
color.green = 1.0;
|
|
color.blue = 1.0;
|
|
color.alpha = 1.0;
|
|
}
|
|
}
|
|
|
|
style = g_strconcat ("background-color: ", color_value, ";", NULL);
|
|
|
|
webkit_web_view = WEBKIT_WEB_VIEW (web_view);
|
|
|
|
webkit_web_view_set_background_color (webkit_web_view, &color);
|
|
|
|
e_web_view_jsc_add_rule_into_style_sheet (webkit_web_view,
|
|
iframe_id,
|
|
"-e-web-view-style-sheet",
|
|
".-e-web-view-background-color",
|
|
style,
|
|
web_view->priv->cancellable);
|
|
|
|
bg_brightness = e_utils_get_color_brightness (&color);
|
|
|
|
g_free (color_value);
|
|
g_free (style);
|
|
|
|
if (gtk_style_context_lookup_color (style_context, "theme_fg_color", &color))
|
|
color_value = g_strdup_printf ("#%06x", e_rgba_to_value (&color));
|
|
else
|
|
color_value = g_strdup (E_UTILS_DEFAULT_THEME_FG_COLOR);
|
|
|
|
style = g_strconcat ("color: ", color_value, ";", NULL);
|
|
|
|
e_web_view_jsc_add_rule_into_style_sheet (webkit_web_view,
|
|
iframe_id,
|
|
"-e-web-view-style-sheet",
|
|
".-e-web-view-text-color",
|
|
style,
|
|
web_view->priv->cancellable);
|
|
|
|
fg_brightness = e_utils_get_color_brightness (&color);
|
|
|
|
g_free (color_value);
|
|
g_free (style);
|
|
|
|
/* assume darker background than text color means dark theme */
|
|
if (bg_brightness < fg_brightness) {
|
|
e_web_view_jsc_add_rule_into_style_sheet (webkit_web_view,
|
|
iframe_id,
|
|
"-e-web-view-style-sheet",
|
|
"button",
|
|
"color-scheme: dark;",
|
|
web_view->priv->cancellable);
|
|
e_web_view_jsc_add_rule_into_style_sheet (webkit_web_view,
|
|
iframe_id,
|
|
"-e-web-view-style-sheet",
|
|
".-evo-color-scheme-light",
|
|
"display: none;",
|
|
web_view->priv->cancellable);
|
|
e_web_view_jsc_add_rule_into_style_sheet (webkit_web_view,
|
|
iframe_id,
|
|
"-e-web-view-style-sheet",
|
|
".-evo-color-scheme-dark",
|
|
"display: initial;",
|
|
web_view->priv->cancellable);
|
|
} else {
|
|
e_web_view_jsc_add_rule_into_style_sheet (webkit_web_view,
|
|
iframe_id,
|
|
"-e-web-view-style-sheet",
|
|
"button",
|
|
"color-scheme: inherit;",
|
|
web_view->priv->cancellable);
|
|
e_web_view_jsc_add_rule_into_style_sheet (webkit_web_view,
|
|
iframe_id,
|
|
"-e-web-view-style-sheet",
|
|
".-evo-color-scheme-light",
|
|
"display: initial;",
|
|
web_view->priv->cancellable);
|
|
e_web_view_jsc_add_rule_into_style_sheet (webkit_web_view,
|
|
iframe_id,
|
|
"-e-web-view-style-sheet",
|
|
".-evo-color-scheme-dark",
|
|
"display: none;",
|
|
web_view->priv->cancellable);
|
|
}
|
|
|
|
e_web_view_jsc_add_rule_into_style_sheet (webkit_web_view,
|
|
iframe_id,
|
|
"-e-web-view-style-sheet",
|
|
"body, div, p, td",
|
|
"unicode-bidi: plaintext;",
|
|
web_view->priv->cancellable);
|
|
}
|
|
|
|
static void
|
|
style_updated_cb (EWebView *web_view)
|
|
{
|
|
e_web_view_update_styles (web_view, "*");
|
|
}
|
|
|
|
static void
|
|
e_web_view_set_has_selection (EWebView *web_view,
|
|
gboolean has_selection)
|
|
{
|
|
g_return_if_fail (E_IS_WEB_VIEW (web_view));
|
|
|
|
if ((!web_view->priv->has_selection) == (!has_selection))
|
|
return;
|
|
|
|
web_view->priv->has_selection = has_selection;
|
|
|
|
g_object_notify (G_OBJECT (web_view), "has-selection");
|
|
}
|
|
|
|
static void
|
|
web_view_load_changed_cb (WebKitWebView *webkit_web_view,
|
|
WebKitLoadEvent load_event,
|
|
gpointer user_data)
|
|
{
|
|
EWebView *web_view;
|
|
|
|
web_view = E_WEB_VIEW (webkit_web_view);
|
|
|
|
if (load_event == WEBKIT_LOAD_STARTED) {
|
|
g_hash_table_remove_all (web_view->priv->element_clicked_cbs);
|
|
e_web_view_set_has_selection (web_view, FALSE);
|
|
}
|
|
|
|
if (load_event != WEBKIT_LOAD_FINISHED)
|
|
return;
|
|
|
|
/* Make sure the initialize function is called for the top document when it is loaded. */
|
|
e_web_view_jsc_run_script (webkit_web_view, web_view->priv->cancellable,
|
|
"Evo.EnsureMainDocumentInitialized();");
|
|
|
|
e_web_view_update_styles (web_view, "");
|
|
|
|
web_view_update_document_highlights (web_view);
|
|
}
|
|
|
|
static GObjectConstructParam*
|
|
find_property (guint n_properties,
|
|
GObjectConstructParam* properties,
|
|
GParamSpec* param_spec)
|
|
{
|
|
while (n_properties--) {
|
|
if (properties->pspec == param_spec)
|
|
return properties;
|
|
properties++;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
static void
|
|
web_view_uri_request_done_cb (GObject *source_object,
|
|
GAsyncResult *result,
|
|
gpointer user_data)
|
|
{
|
|
WebKitURISchemeRequest *request = user_data;
|
|
WebKitWebView *web_view;
|
|
GInputStream *stream = NULL;
|
|
gint64 stream_length = -1;
|
|
gchar *mime_type = NULL;
|
|
GError *error = NULL;
|
|
|
|
g_return_if_fail (E_IS_CONTENT_REQUEST (source_object));
|
|
g_return_if_fail (WEBKIT_IS_URI_SCHEME_REQUEST (request));
|
|
|
|
if (!e_content_request_process_finish (E_CONTENT_REQUEST (source_object),
|
|
result, &stream, &stream_length, &mime_type, &error)) {
|
|
if (!error)
|
|
error = g_error_new (G_IO_ERROR, G_IO_ERROR_FAILED, "Failed to get '%s'", webkit_uri_scheme_request_get_uri (request));
|
|
webkit_uri_scheme_request_finish_error (request, error);
|
|
g_clear_error (&error);
|
|
} else {
|
|
webkit_uri_scheme_request_finish (request, stream, stream_length, mime_type);
|
|
|
|
g_clear_object (&stream);
|
|
g_free (mime_type);
|
|
}
|
|
|
|
web_view = webkit_uri_scheme_request_get_web_view (request);
|
|
g_signal_emit (web_view, signals[RESOURCE_LOADED], 0, NULL);
|
|
|
|
g_object_unref (request);
|
|
}
|
|
|
|
static void
|
|
e_web_view_process_uri_request (EWebView *web_view,
|
|
WebKitURISchemeRequest *request)
|
|
{
|
|
EContentRequest *content_request;
|
|
const gchar *scheme;
|
|
const gchar *uri;
|
|
gchar *redirect_to_uri = NULL;
|
|
|
|
g_return_if_fail (E_IS_WEB_VIEW (web_view));
|
|
g_return_if_fail (WEBKIT_IS_URI_SCHEME_REQUEST (request));
|
|
|
|
scheme = webkit_uri_scheme_request_get_scheme (request);
|
|
g_return_if_fail (scheme != NULL);
|
|
|
|
content_request = g_hash_table_lookup (web_view->priv->scheme_handlers, scheme);
|
|
|
|
if (!content_request) {
|
|
g_warning ("%s: Cannot find handler for scheme '%s'", G_STRFUNC, scheme);
|
|
return;
|
|
}
|
|
|
|
uri = webkit_uri_scheme_request_get_uri (request);
|
|
|
|
g_return_if_fail (e_content_request_can_process_uri (content_request, uri));
|
|
|
|
/* Expects an empty string to abandon the request,
|
|
or NULL to keep the passed-in uri,
|
|
or a new uri to load instead. */
|
|
g_signal_emit (web_view, signals[URI_REQUESTED], 0, uri, &redirect_to_uri);
|
|
|
|
if (redirect_to_uri && *redirect_to_uri) {
|
|
uri = redirect_to_uri;
|
|
} else if (redirect_to_uri) {
|
|
GError *error;
|
|
|
|
g_free (redirect_to_uri);
|
|
|
|
error = g_error_new_literal (G_IO_ERROR, G_IO_ERROR_CANCELLED, "Cancelled");
|
|
|
|
webkit_uri_scheme_request_finish_error (request, error);
|
|
g_clear_error (&error);
|
|
|
|
return;
|
|
}
|
|
|
|
e_content_request_process (content_request, uri, G_OBJECT (web_view), web_view->priv->cancellable,
|
|
web_view_uri_request_done_cb, g_object_ref (request));
|
|
|
|
g_free (redirect_to_uri);
|
|
}
|
|
|
|
static void
|
|
web_view_process_uri_request_cb (WebKitURISchemeRequest *request,
|
|
gpointer user_data)
|
|
{
|
|
WebKitWebView *web_view;
|
|
|
|
web_view = webkit_uri_scheme_request_get_web_view (request);
|
|
|
|
if (E_IS_WEB_VIEW (web_view)) {
|
|
e_web_view_process_uri_request (E_WEB_VIEW (web_view), request);
|
|
} else {
|
|
GError *error;
|
|
|
|
error = g_error_new_literal (G_IO_ERROR, G_IO_ERROR_FAILED, "Unexpected WebView type");
|
|
webkit_uri_scheme_request_finish_error (request, error);
|
|
g_clear_error (&error);
|
|
|
|
g_warning ("%s: Unexpected WebView type '%s' received", G_STRFUNC, web_view ? G_OBJECT_TYPE_NAME (web_view) : "null");
|
|
|
|
return;
|
|
}
|
|
}
|
|
|
|
static GSList *known_schemes = NULL;
|
|
|
|
static void
|
|
web_view_web_context_gone (gpointer user_data,
|
|
GObject *obj)
|
|
{
|
|
gpointer *pweb_context = user_data;
|
|
|
|
g_return_if_fail (pweb_context != NULL);
|
|
|
|
*pweb_context = NULL;
|
|
|
|
g_slist_free_full (known_schemes, g_free);
|
|
known_schemes = NULL;
|
|
}
|
|
|
|
static void
|
|
web_view_ensure_scheme_known (WebKitWebContext *web_context,
|
|
const gchar *scheme)
|
|
{
|
|
GSList *link;
|
|
|
|
g_return_if_fail (WEBKIT_IS_WEB_CONTEXT (web_context));
|
|
g_return_if_fail (scheme != NULL);
|
|
|
|
for (link = known_schemes; link; link = g_slist_next (link)) {
|
|
if (g_strcmp0 (scheme, link->data) == 0)
|
|
break;
|
|
}
|
|
|
|
if (!link) {
|
|
known_schemes = g_slist_prepend (known_schemes, g_strdup (scheme));
|
|
|
|
webkit_web_context_register_uri_scheme (web_context, scheme, web_view_process_uri_request_cb, NULL, NULL);
|
|
}
|
|
}
|
|
|
|
static GObject*
|
|
web_view_constructor (GType type,
|
|
guint n_construct_properties,
|
|
GObjectConstructParam *construct_properties)
|
|
{
|
|
GObjectClass* object_class;
|
|
GParamSpec* param_spec;
|
|
GObjectConstructParam *param = NULL;
|
|
|
|
object_class = G_OBJECT_CLASS (g_type_class_ref(type));
|
|
g_return_val_if_fail (object_class != NULL, NULL);
|
|
|
|
if (construct_properties && n_construct_properties != 0) {
|
|
param_spec = g_object_class_find_property (object_class, "settings");
|
|
if ((param = find_property (n_construct_properties, construct_properties, param_spec)))
|
|
g_value_take_object (param->value, e_web_view_get_default_webkit_settings ());
|
|
param_spec = g_object_class_find_property(object_class, "user-content-manager");
|
|
if ((param = find_property (n_construct_properties, construct_properties, param_spec)))
|
|
g_value_take_object (param->value, webkit_user_content_manager_new ());
|
|
param_spec = g_object_class_find_property (object_class, "web-context");
|
|
if ((param = find_property (n_construct_properties, construct_properties, param_spec))) {
|
|
/* Share one web_context between all previews. */
|
|
static gpointer web_context = NULL;
|
|
|
|
if (!web_context) {
|
|
GSList *link;
|
|
gchar *plugins_path;
|
|
#ifdef ENABLE_MAINTAINER_MODE
|
|
const gchar *source_webkitdatadir;
|
|
#endif
|
|
|
|
web_context = webkit_web_context_new ();
|
|
|
|
webkit_web_context_set_cache_model (web_context, WEBKIT_CACHE_MODEL_DOCUMENT_VIEWER);
|
|
webkit_web_context_set_web_extensions_directory (web_context, EVOLUTION_WEB_EXTENSIONS_DIR);
|
|
webkit_web_context_set_sandbox_enabled (web_context, TRUE);
|
|
webkit_web_context_add_path_to_sandbox (web_context, EVOLUTION_WEBKITDATADIR, TRUE);
|
|
|
|
plugins_path = g_build_filename (e_get_user_data_dir (), "preview-plugins", NULL);
|
|
if (g_file_test (plugins_path, G_FILE_TEST_IS_DIR))
|
|
webkit_web_context_add_path_to_sandbox (web_context, plugins_path, TRUE);
|
|
g_free (plugins_path);
|
|
|
|
#ifdef ENABLE_MAINTAINER_MODE
|
|
source_webkitdatadir = g_getenv ("EVOLUTION_SOURCE_WEBKITDATADIR");
|
|
if (source_webkitdatadir && *source_webkitdatadir)
|
|
webkit_web_context_add_path_to_sandbox (web_context, source_webkitdatadir, TRUE);
|
|
#endif
|
|
|
|
g_object_weak_ref (G_OBJECT (web_context), web_view_web_context_gone, &web_context);
|
|
|
|
for (link = known_schemes; link; link = g_slist_next (link)) {
|
|
const gchar *scheme = link->data;
|
|
|
|
webkit_web_context_register_uri_scheme (web_context, scheme, web_view_process_uri_request_cb, NULL, NULL);
|
|
}
|
|
} else {
|
|
g_object_ref (web_context);
|
|
}
|
|
|
|
g_value_take_object (param->value, web_context);
|
|
}
|
|
}
|
|
|
|
g_type_class_unref (object_class);
|
|
|
|
return G_OBJECT_CLASS (e_web_view_parent_class)->constructor (type, n_construct_properties, construct_properties);
|
|
}
|
|
|
|
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_COPY_TARGET_LIST:
|
|
/* This is a fake property. */
|
|
g_warning ("%s: EWebView::copy-target-list not used", G_STRFUNC);
|
|
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_MINIMUM_FONT_SIZE:
|
|
e_web_view_set_minimum_font_size (
|
|
E_WEB_VIEW (object),
|
|
g_value_get_int (value));
|
|
return;
|
|
|
|
case PROP_OPEN_PROXY:
|
|
e_web_view_set_open_proxy (
|
|
E_WEB_VIEW (object),
|
|
g_value_get_object (value));
|
|
return;
|
|
|
|
case PROP_PASTE_TARGET_LIST:
|
|
/* This is a fake property. */
|
|
g_warning ("%s: EWebView::paste-target-list not used", G_STRFUNC);
|
|
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_COPY_TARGET_LIST:
|
|
/* This is a fake property. */
|
|
g_value_set_boxed (value, NULL);
|
|
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_HAS_SELECTION:
|
|
g_value_set_boolean (value, e_web_view_has_selection (E_WEB_VIEW (object)));
|
|
return;
|
|
|
|
case PROP_MINIMUM_FONT_SIZE:
|
|
g_value_set_int (
|
|
value, e_web_view_get_minimum_font_size (
|
|
E_WEB_VIEW (object)));
|
|
return;
|
|
|
|
case PROP_NEED_INPUT:
|
|
g_value_set_boolean (
|
|
value, e_web_view_get_need_input (
|
|
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_PASTE_TARGET_LIST:
|
|
/* This is a fake property. */
|
|
g_value_set_boxed (value, NULL);
|
|
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;
|
|
|
|
/* This can be called during dispose, thus disconnect early */
|
|
g_signal_handlers_disconnect_by_func (object, G_CALLBACK (style_updated_cb), NULL);
|
|
|
|
priv = E_WEB_VIEW_GET_PRIVATE (object);
|
|
|
|
if (priv->cancellable) {
|
|
g_cancellable_cancel (priv->cancellable);
|
|
g_clear_object (&priv->cancellable);
|
|
}
|
|
|
|
if (priv->font_name_changed_handler_id > 0) {
|
|
g_signal_handler_disconnect (
|
|
priv->font_settings,
|
|
priv->font_name_changed_handler_id);
|
|
priv->font_name_changed_handler_id = 0;
|
|
}
|
|
|
|
if (priv->monospace_font_name_changed_handler_id > 0) {
|
|
g_signal_handler_disconnect (
|
|
priv->font_settings,
|
|
priv->monospace_font_name_changed_handler_id);
|
|
priv->monospace_font_name_changed_handler_id = 0;
|
|
}
|
|
|
|
if (priv->found_text_handler_id > 0) {
|
|
g_signal_handler_disconnect (
|
|
priv->find_controller,
|
|
priv->found_text_handler_id);
|
|
priv->found_text_handler_id = 0;
|
|
}
|
|
|
|
if (priv->failed_to_find_text_handler_id > 0) {
|
|
g_signal_handler_disconnect (
|
|
priv->find_controller,
|
|
priv->failed_to_find_text_handler_id);
|
|
priv->failed_to_find_text_handler_id = 0;
|
|
}
|
|
|
|
g_hash_table_remove_all (priv->scheme_handlers);
|
|
g_hash_table_remove_all (priv->element_clicked_cbs);
|
|
|
|
g_clear_object (&priv->ui_manager);
|
|
g_clear_object (&priv->open_proxy);
|
|
g_clear_object (&priv->print_proxy);
|
|
g_clear_object (&priv->save_as_proxy);
|
|
g_clear_object (&priv->font_settings);
|
|
|
|
/* 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);
|
|
|
|
g_clear_pointer (&priv->last_popup_iframe_src, g_free);
|
|
g_clear_pointer (&priv->last_popup_iframe_id, g_free);
|
|
g_clear_pointer (&priv->last_popup_element_id, g_free);
|
|
g_clear_pointer (&priv->last_popup_link_uri, g_free);
|
|
g_free (priv->selected_uri);
|
|
g_free (priv->cursor_image_src);
|
|
|
|
while (!g_queue_is_empty (&priv->highlights))
|
|
g_free (g_queue_pop_head (&priv->highlights));
|
|
|
|
g_clear_pointer (&priv->old_settings, g_hash_table_destroy);
|
|
|
|
g_hash_table_destroy (priv->scheme_handlers);
|
|
g_hash_table_destroy (priv->element_clicked_cbs);
|
|
|
|
/* Chain up to parent's finalize() method. */
|
|
G_OBJECT_CLASS (e_web_view_parent_class)->finalize (object);
|
|
}
|
|
|
|
/* 'scheme' is like "file", not "file:" */
|
|
void
|
|
e_web_view_register_content_request_for_scheme (EWebView *web_view,
|
|
const gchar *scheme,
|
|
EContentRequest *content_request)
|
|
{
|
|
g_return_if_fail (E_IS_WEB_VIEW (web_view));
|
|
g_return_if_fail (E_IS_CONTENT_REQUEST (content_request));
|
|
g_return_if_fail (scheme != NULL);
|
|
|
|
g_hash_table_insert (web_view->priv->scheme_handlers, g_strdup (scheme), g_object_ref (content_request));
|
|
|
|
web_view_ensure_scheme_known (webkit_web_view_get_context (WEBKIT_WEB_VIEW (web_view)), scheme);
|
|
}
|
|
|
|
static void
|
|
web_view_initialize (WebKitWebView *web_view)
|
|
{
|
|
EContentRequest *content_request;
|
|
GSettings *font_settings;
|
|
|
|
content_request = e_file_request_new ();
|
|
e_web_view_register_content_request_for_scheme (E_WEB_VIEW (web_view), "evo-file", content_request);
|
|
g_object_unref (content_request);
|
|
|
|
content_request = e_stock_request_new ();
|
|
e_binding_bind_property (web_view, "scale-factor",
|
|
content_request, "scale-factor",
|
|
G_BINDING_DEFAULT | G_BINDING_SYNC_CREATE);
|
|
e_web_view_register_content_request_for_scheme (E_WEB_VIEW (web_view), "gtk-stock", content_request);
|
|
g_object_unref (content_request);
|
|
|
|
font_settings = e_util_ref_settings ("org.gnome.desktop.interface");
|
|
|
|
e_web_view_update_fonts_settings (font_settings, NULL, NULL, GTK_WIDGET (web_view));
|
|
|
|
g_object_unref (font_settings);
|
|
}
|
|
|
|
static void
|
|
e_web_view_initialize_web_extensions_cb (WebKitWebContext *web_context,
|
|
gpointer user_data)
|
|
{
|
|
EWebView *web_view = user_data;
|
|
|
|
g_return_if_fail (E_IS_WEB_VIEW (web_view));
|
|
|
|
webkit_web_context_set_web_extensions_directory (web_context, EVOLUTION_WEB_EXTENSIONS_DIR);
|
|
}
|
|
|
|
static void
|
|
e_web_view_element_clicked_cb (WebKitUserContentManager *manager,
|
|
WebKitJavascriptResult *js_result,
|
|
gpointer user_data)
|
|
{
|
|
EWebView *web_view = user_data;
|
|
GtkAllocation elem_position;
|
|
GPtrArray *listeners;
|
|
JSCValue *jsc_object;
|
|
gchar *iframe_id, *elem_id, *elem_class, *elem_value;
|
|
gdouble zoom_level;
|
|
|
|
g_return_if_fail (web_view != NULL);
|
|
g_return_if_fail (js_result != NULL);
|
|
|
|
jsc_object = webkit_javascript_result_get_js_value (js_result);
|
|
g_return_if_fail (jsc_value_is_object (jsc_object));
|
|
|
|
iframe_id = e_web_view_jsc_get_object_property_string (jsc_object, "iframe-id", NULL);
|
|
elem_id = e_web_view_jsc_get_object_property_string (jsc_object, "elem-id", NULL);
|
|
elem_class = e_web_view_jsc_get_object_property_string (jsc_object, "elem-class", NULL);
|
|
elem_value = e_web_view_jsc_get_object_property_string (jsc_object, "elem-value", NULL);
|
|
elem_position.x = e_web_view_jsc_get_object_property_int32 (jsc_object, "left", 0);
|
|
elem_position.y = e_web_view_jsc_get_object_property_int32 (jsc_object, "top", 0);
|
|
elem_position.width = e_web_view_jsc_get_object_property_int32 (jsc_object, "width", 0);
|
|
elem_position.height = e_web_view_jsc_get_object_property_int32 (jsc_object, "height", 0);
|
|
|
|
zoom_level = webkit_web_view_get_zoom_level (WEBKIT_WEB_VIEW (web_view));
|
|
elem_position.x *= zoom_level;
|
|
elem_position.y *= zoom_level;
|
|
elem_position.width *= zoom_level;
|
|
elem_position.height *= zoom_level;
|
|
|
|
listeners = g_hash_table_lookup (web_view->priv->element_clicked_cbs, elem_class);
|
|
|
|
if (listeners) {
|
|
guint ii;
|
|
|
|
for (ii = 0; ii < listeners->len; ii++) {
|
|
ElementClickedData *ecd = g_ptr_array_index (listeners, ii);
|
|
|
|
if (ecd && ecd->callback)
|
|
ecd->callback (web_view, iframe_id, elem_id, elem_class, elem_value, &elem_position, ecd->user_data);
|
|
}
|
|
}
|
|
|
|
g_free (iframe_id);
|
|
g_free (elem_id);
|
|
g_free (elem_class);
|
|
g_free (elem_value);
|
|
}
|
|
|
|
static void
|
|
web_view_call_register_element_clicked (EWebView *web_view,
|
|
const gchar *iframe_id,
|
|
const gchar *only_elem_class)
|
|
{
|
|
gchar *elem_classes = NULL;
|
|
|
|
if (!only_elem_class) {
|
|
GHashTableIter iter;
|
|
gpointer key;
|
|
GString *classes;
|
|
|
|
classes = g_string_sized_new (128);
|
|
|
|
g_hash_table_iter_init (&iter, web_view->priv->element_clicked_cbs);
|
|
while (g_hash_table_iter_next (&iter, &key, NULL)) {
|
|
if (classes->len)
|
|
g_string_append_c (classes, '\n');
|
|
|
|
g_string_append (classes, key);
|
|
}
|
|
|
|
elem_classes = g_string_free (classes, FALSE);
|
|
}
|
|
|
|
e_web_view_jsc_register_element_clicked (WEBKIT_WEB_VIEW (web_view), iframe_id,
|
|
only_elem_class ? only_elem_class : elem_classes,
|
|
web_view->priv->cancellable);
|
|
|
|
g_free (elem_classes);
|
|
}
|
|
|
|
static void
|
|
e_web_view_content_loaded_cb (WebKitUserContentManager *manager,
|
|
WebKitJavascriptResult *js_result,
|
|
gpointer user_data)
|
|
{
|
|
EWebView *web_view = user_data;
|
|
JSCValue *jsc_value;
|
|
gchar *iframe_id;
|
|
|
|
g_return_if_fail (web_view != NULL);
|
|
g_return_if_fail (js_result != NULL);
|
|
|
|
jsc_value = webkit_javascript_result_get_js_value (js_result);
|
|
g_return_if_fail (jsc_value_is_string (jsc_value));
|
|
|
|
iframe_id = jsc_value_to_string (jsc_value);
|
|
|
|
if (!iframe_id || !*iframe_id)
|
|
e_web_view_update_fonts (web_view);
|
|
else
|
|
e_web_view_update_styles (web_view, iframe_id);
|
|
|
|
web_view_call_register_element_clicked (web_view, iframe_id, NULL);
|
|
|
|
g_signal_emit (web_view, signals[CONTENT_LOADED], 0, iframe_id);
|
|
|
|
g_free (iframe_id);
|
|
}
|
|
|
|
static void
|
|
e_web_view_has_selection_cb (WebKitUserContentManager *manager,
|
|
WebKitJavascriptResult *js_result,
|
|
gpointer user_data)
|
|
{
|
|
EWebView *web_view = user_data;
|
|
JSCValue *jsc_value;
|
|
|
|
g_return_if_fail (web_view != NULL);
|
|
g_return_if_fail (js_result != NULL);
|
|
|
|
jsc_value = webkit_javascript_result_get_js_value (js_result);
|
|
g_return_if_fail (jsc_value_is_boolean (jsc_value));
|
|
|
|
e_web_view_set_has_selection (web_view, jsc_value_to_boolean (jsc_value));
|
|
}
|
|
|
|
static void
|
|
e_web_view_set_need_input (EWebView *web_view,
|
|
gboolean need_input)
|
|
{
|
|
g_return_if_fail (E_IS_WEB_VIEW (web_view));
|
|
|
|
if ((!web_view->priv->need_input) == (!need_input))
|
|
return;
|
|
|
|
web_view->priv->need_input = need_input;
|
|
|
|
g_object_notify (G_OBJECT (web_view), "need-input");
|
|
}
|
|
|
|
static void
|
|
e_web_view_need_input_changed_cb (WebKitUserContentManager *manager,
|
|
WebKitJavascriptResult *js_result,
|
|
gpointer user_data)
|
|
{
|
|
EWebView *web_view = user_data;
|
|
JSCValue *jsc_value;
|
|
|
|
g_return_if_fail (web_view != NULL);
|
|
g_return_if_fail (js_result != NULL);
|
|
|
|
jsc_value = webkit_javascript_result_get_js_value (js_result);
|
|
g_return_if_fail (jsc_value_is_boolean (jsc_value));
|
|
|
|
e_web_view_set_need_input (web_view, jsc_value_to_boolean (jsc_value));
|
|
}
|
|
|
|
static void
|
|
web_view_constructed (GObject *object)
|
|
{
|
|
WebKitSettings *web_settings;
|
|
WebKitUserContentManager *manager;
|
|
EWebView *web_view = E_WEB_VIEW (object);
|
|
GSettings *settings;
|
|
|
|
#ifndef G_OS_WIN32
|
|
settings = e_util_ref_settings ("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
|
|
|
|
settings = e_util_ref_settings ("org.gnome.evolution.shell");
|
|
|
|
g_settings_bind (
|
|
settings, "webkit-minimum-font-size",
|
|
object, "minimum-font-size",
|
|
G_SETTINGS_BIND_GET);
|
|
|
|
g_clear_object (&settings);
|
|
|
|
g_signal_connect_object (webkit_web_view_get_context (WEBKIT_WEB_VIEW (web_view)), "initialize-web-extensions",
|
|
G_CALLBACK (e_web_view_initialize_web_extensions_cb), web_view, 0);
|
|
|
|
/* Chain up to parent's constructed() method. */
|
|
G_OBJECT_CLASS (e_web_view_parent_class)->constructed (object);
|
|
|
|
e_extensible_load_extensions (E_EXTENSIBLE (object));
|
|
|
|
web_settings = webkit_web_view_get_settings (WEBKIT_WEB_VIEW (object));
|
|
webkit_settings_set_enable_write_console_messages_to_stdout (web_settings, e_util_get_webkit_developer_mode_enabled ());
|
|
|
|
g_object_set (
|
|
G_OBJECT (web_settings),
|
|
"default-charset", "UTF-8",
|
|
NULL);
|
|
|
|
e_binding_bind_property (
|
|
web_settings, "enable-caret-browsing",
|
|
E_WEB_VIEW (object), "caret-mode",
|
|
G_BINDING_BIDIRECTIONAL |
|
|
G_BINDING_SYNC_CREATE);
|
|
|
|
web_view_initialize (WEBKIT_WEB_VIEW (object));
|
|
|
|
web_view_set_find_controller (web_view);
|
|
|
|
manager = webkit_web_view_get_user_content_manager (WEBKIT_WEB_VIEW (object));
|
|
|
|
g_signal_connect_object (manager, "script-message-received::elementClicked",
|
|
G_CALLBACK (e_web_view_element_clicked_cb), web_view, 0);
|
|
|
|
g_signal_connect_object (manager, "script-message-received::contentLoaded",
|
|
G_CALLBACK (e_web_view_content_loaded_cb), web_view, 0);
|
|
|
|
g_signal_connect_object (manager, "script-message-received::hasSelection",
|
|
G_CALLBACK (e_web_view_has_selection_cb), web_view, 0);
|
|
|
|
g_signal_connect_object (manager, "script-message-received::needInputChanged",
|
|
G_CALLBACK (e_web_view_need_input_changed_cb), web_view, 0);
|
|
|
|
webkit_user_content_manager_register_script_message_handler (manager, "contentLoaded");
|
|
webkit_user_content_manager_register_script_message_handler (manager, "elementClicked");
|
|
webkit_user_content_manager_register_script_message_handler (manager, "hasSelection");
|
|
webkit_user_content_manager_register_script_message_handler (manager, "needInputChanged");
|
|
|
|
settings = e_util_ref_settings ("org.gnome.evolution.mail");
|
|
g_signal_connect_object (settings, "changed::composer-inline-spelling",
|
|
G_CALLBACK (e_web_view_spell_settings_changed_cb), web_view, 0);
|
|
g_signal_connect_object (settings, "changed::composer-spell-languages",
|
|
G_CALLBACK (e_web_view_spell_settings_changed_cb), web_view, 0);
|
|
e_web_view_update_spell_checking (web_view, settings);
|
|
g_clear_object (&settings);
|
|
}
|
|
|
|
static void
|
|
e_web_view_replace_load_cancellable (EWebView *web_view,
|
|
gboolean create_new)
|
|
{
|
|
g_return_if_fail (E_IS_WEB_VIEW (web_view));
|
|
|
|
if (web_view->priv->cancellable) {
|
|
g_cancellable_cancel (web_view->priv->cancellable);
|
|
g_clear_object (&web_view->priv->cancellable);
|
|
}
|
|
|
|
if (create_new)
|
|
web_view->priv->cancellable = g_cancellable_new ();
|
|
}
|
|
|
|
static gboolean
|
|
web_view_scroll_event (GtkWidget *widget,
|
|
GdkEventScroll *event)
|
|
{
|
|
if (event->state & GDK_CONTROL_MASK) {
|
|
GdkScrollDirection direction = event->direction;
|
|
|
|
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 if (event->delta_y >= 1e-9 || event->delta_y <= -1e-9) {
|
|
return TRUE;
|
|
} else {
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
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 GTK_WIDGET_CLASS (e_web_view_parent_class)->
|
|
scroll_event (widget, event);
|
|
}
|
|
|
|
static gboolean
|
|
web_view_drag_motion (GtkWidget *widget,
|
|
GdkDragContext *context,
|
|
gint x,
|
|
gint y,
|
|
guint time_)
|
|
{
|
|
/* Made this way to not pretend that this is a drop zone,
|
|
when only FALSE had been returned, even it is supposed
|
|
to be enough. Remove web_view_drag_leave() once fixed. */
|
|
gdk_drag_status (context, 0, time_);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static gboolean
|
|
web_view_drag_drop (GtkWidget *widget,
|
|
GdkDragContext *context,
|
|
gint x,
|
|
gint y,
|
|
guint time_)
|
|
{
|
|
/* Defined to avoid crash in WebKit */
|
|
return FALSE;
|
|
}
|
|
|
|
static void
|
|
web_view_drag_leave (GtkWidget *widget,
|
|
GdkDragContext *context,
|
|
guint time_)
|
|
{
|
|
/* Defined to avoid crash in WebKit, when the web_view_drag_motion()
|
|
uses the other way around. */
|
|
}
|
|
|
|
static void
|
|
web_view_hovering_over_link (EWebView *web_view,
|
|
const gchar *title,
|
|
const gchar *uri)
|
|
{
|
|
gchar *message = e_util_get_uri_tooltip (uri);
|
|
|
|
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 || !*string) {
|
|
webkit_web_view_load_html (WEBKIT_WEB_VIEW (web_view), "", "evo-file:///");
|
|
} else {
|
|
GBytes *bytes;
|
|
|
|
bytes = g_bytes_new (string, strlen (string));
|
|
webkit_web_view_load_bytes (WEBKIT_WEB_VIEW (web_view), bytes, NULL, NULL, "evo-file:///");
|
|
g_bytes_unref (bytes);
|
|
}
|
|
}
|
|
|
|
static void
|
|
web_view_load_uri (EWebView *web_view,
|
|
const gchar *uri)
|
|
{
|
|
if (!uri)
|
|
uri = "about:blank";
|
|
|
|
webkit_web_view_load_uri (WEBKIT_WEB_VIEW (web_view), uri);
|
|
}
|
|
|
|
static gchar *
|
|
web_view_suggest_filename (EWebView *web_view,
|
|
const gchar *uri)
|
|
{
|
|
const gchar *cp;
|
|
|
|
/* Try to derive a filename from the last path segment. */
|
|
|
|
cp = strrchr (uri, '/');
|
|
if (cp != NULL) {
|
|
if (strchr (cp, '?') == NULL)
|
|
cp++;
|
|
else
|
|
cp = NULL;
|
|
}
|
|
|
|
return g_strdup (cp);
|
|
}
|
|
|
|
static void
|
|
web_view_before_popup_event (EWebView *web_view,
|
|
const gchar *uri)
|
|
{
|
|
e_web_view_set_selected_uri (web_view, uri);
|
|
e_web_view_update_actions (web_view);
|
|
}
|
|
|
|
static gboolean
|
|
web_view_popup_event (EWebView *web_view,
|
|
const gchar *uri,
|
|
GdkEvent *event)
|
|
{
|
|
e_web_view_set_selected_uri (web_view, uri);
|
|
e_web_view_show_popup_menu (web_view, event);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static void
|
|
web_view_stop_loading (EWebView *web_view)
|
|
{
|
|
e_web_view_replace_load_cancellable (web_view, FALSE);
|
|
webkit_web_view_stop_loading (WEBKIT_WEB_VIEW (web_view));
|
|
}
|
|
|
|
static void
|
|
web_view_update_actions (EWebView *web_view)
|
|
{
|
|
GtkActionGroup *action_group;
|
|
gboolean can_copy;
|
|
gboolean scheme_is_http = FALSE;
|
|
gboolean scheme_is_mailto = FALSE;
|
|
gboolean uri_is_valid = FALSE;
|
|
gboolean visible;
|
|
const gchar *cursor_image_src;
|
|
const gchar *group_name;
|
|
const gchar *uri;
|
|
|
|
g_return_if_fail (E_IS_WEB_VIEW (web_view));
|
|
|
|
uri = e_web_view_get_selected_uri (web_view);
|
|
can_copy = e_web_view_has_selection (web_view);
|
|
cursor_image_src = e_web_view_get_cursor_image_src (web_view);
|
|
|
|
/* 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);
|
|
|
|
if (visible) {
|
|
CamelURL *curl;
|
|
|
|
curl = camel_url_new (uri, NULL);
|
|
if (curl) {
|
|
CamelInternetAddress *inet_addr;
|
|
const gchar *name = NULL, *email = NULL;
|
|
GtkAction *action;
|
|
|
|
inet_addr = camel_internet_address_new ();
|
|
camel_address_decode (CAMEL_ADDRESS (inet_addr), curl->path);
|
|
|
|
action = gtk_action_group_get_action (action_group, "mailto-copy-raw");
|
|
gtk_action_set_visible (action,
|
|
camel_internet_address_get (inet_addr, 0, &name, &email) &&
|
|
name && *name && email && *email);
|
|
|
|
g_object_unref (inet_addr);
|
|
camel_url_free (curl);
|
|
}
|
|
}
|
|
|
|
group_name = "image";
|
|
visible = (cursor_image_src != NULL);
|
|
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;
|
|
const gchar *primary_text;
|
|
const gchar *secondary_text;
|
|
gint icon_width, icon_height;
|
|
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 = "dialog-information";
|
|
break;
|
|
|
|
case GTK_MESSAGE_WARNING:
|
|
icon_name = "dialog-warning";
|
|
break;
|
|
|
|
case GTK_MESSAGE_ERROR:
|
|
icon_name = "dialog-error";
|
|
break;
|
|
|
|
default:
|
|
dialog = e_alert_dialog_new (parent, alert);
|
|
gtk_dialog_run (GTK_DIALOG (dialog));
|
|
gtk_widget_destroy (dialog);
|
|
return;
|
|
}
|
|
|
|
/* Primary text is required. */
|
|
primary_text = e_alert_get_primary_text (alert);
|
|
g_return_if_fail (primary_text != NULL);
|
|
|
|
/* Secondary text is optional. */
|
|
secondary_text = e_alert_get_secondary_text (alert);
|
|
if (secondary_text == NULL)
|
|
secondary_text = "";
|
|
|
|
if (!gtk_icon_size_lookup (GTK_ICON_SIZE_DIALOG, &icon_width, &icon_height)) {
|
|
icon_width = 48;
|
|
icon_height = 48;
|
|
}
|
|
|
|
buffer = g_string_sized_new (512);
|
|
|
|
g_string_append (
|
|
buffer,
|
|
"<html>"
|
|
"<head>"
|
|
"<meta http-equiv=\"content-type\" content=\"text/html; charset=utf-8\">"
|
|
"<meta name=\"color-scheme\" content=\"light dark\">"
|
|
"</head>"
|
|
"<body>");
|
|
|
|
g_string_append (
|
|
buffer,
|
|
"<table bgcolor='#000000' width='100%'"
|
|
" cellpadding='1' cellspacing='0'>"
|
|
"<tr>"
|
|
"<td>"
|
|
"<table bgcolor='#dddddd' width='100%' cellpadding='6' style=\"color:#000000;\">"
|
|
"<tr>");
|
|
|
|
g_string_append_printf (
|
|
buffer,
|
|
"<tr>"
|
|
"<td valign='top'>"
|
|
"<img src='gtk-stock://%s/?size=%d' width=\"%dpx\" height=\"%dpx\"/>"
|
|
"</td>"
|
|
"<td align='left' width='100%%'>"
|
|
"<h3>%s</h3>"
|
|
"%s"
|
|
"</td>"
|
|
"</tr>",
|
|
icon_name,
|
|
GTK_ICON_SIZE_DIALOG,
|
|
icon_width,
|
|
icon_height,
|
|
primary_text,
|
|
secondary_text);
|
|
|
|
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_can_execute_editing_command_cb (WebKitWebView *webkit_web_view,
|
|
GAsyncResult *result,
|
|
GtkAction *action)
|
|
{
|
|
gboolean can_do_command;
|
|
|
|
can_do_command = webkit_web_view_can_execute_editing_command_finish (
|
|
webkit_web_view, result, NULL);
|
|
|
|
gtk_action_set_sensitive (action, can_do_command);
|
|
g_object_unref (action);
|
|
}
|
|
|
|
static void
|
|
web_view_selectable_update_actions (ESelectable *selectable,
|
|
EFocusTracker *focus_tracker,
|
|
GdkAtom *clipboard_targets,
|
|
gint n_clipboard_targets)
|
|
{
|
|
EWebView *web_view;
|
|
GtkAction *action;
|
|
gboolean can_copy = FALSE;
|
|
|
|
web_view = E_WEB_VIEW (selectable);
|
|
|
|
can_copy = e_web_view_has_selection (web_view);
|
|
|
|
action = e_focus_tracker_get_copy_clipboard_action (focus_tracker);
|
|
gtk_action_set_sensitive (action, can_copy);
|
|
gtk_action_set_tooltip (action, _("Copy the selection"));
|
|
|
|
action = e_focus_tracker_get_cut_clipboard_action (focus_tracker);
|
|
webkit_web_view_can_execute_editing_command (
|
|
WEBKIT_WEB_VIEW (web_view),
|
|
WEBKIT_EDITING_COMMAND_CUT,
|
|
NULL, /* cancellable */
|
|
(GAsyncReadyCallback) web_view_can_execute_editing_command_cb,
|
|
g_object_ref (action));
|
|
gtk_action_set_tooltip (action, _("Cut the selection"));
|
|
|
|
action = e_focus_tracker_get_paste_clipboard_action (focus_tracker);
|
|
webkit_web_view_can_execute_editing_command (
|
|
WEBKIT_WEB_VIEW (web_view),
|
|
WEBKIT_EDITING_COMMAND_PASTE,
|
|
NULL, /* cancellable */
|
|
(GAsyncReadyCallback) web_view_can_execute_editing_command_cb,
|
|
g_object_ref (action));
|
|
gtk_action_set_tooltip (action, _("Paste the clipboard"));
|
|
|
|
action = e_focus_tracker_get_select_all_action (focus_tracker);
|
|
gtk_action_set_sensitive (action, TRUE);
|
|
gtk_action_set_tooltip (action, _("Select all text and images"));
|
|
}
|
|
|
|
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_test_change_and_update_fonts_cb (EWebView *web_view,
|
|
const gchar *key,
|
|
GSettings *settings)
|
|
{
|
|
GVariant *new_value, *old_value;
|
|
|
|
new_value = g_settings_get_value (settings, key);
|
|
old_value = g_hash_table_lookup (web_view->priv->old_settings, key);
|
|
|
|
if (!new_value || !old_value || !g_variant_equal (new_value, old_value)) {
|
|
if (new_value)
|
|
g_hash_table_insert (web_view->priv->old_settings, g_strdup (key), new_value);
|
|
else
|
|
g_hash_table_remove (web_view->priv->old_settings, key);
|
|
|
|
e_web_view_update_fonts (web_view);
|
|
} else if (new_value) {
|
|
g_variant_unref (new_value);
|
|
}
|
|
}
|
|
|
|
static void
|
|
web_view_toplevel_event_after_cb (GtkWidget *widget,
|
|
GdkEvent *event,
|
|
EWebView *web_view)
|
|
{
|
|
if (event && event->type == GDK_MOTION_NOTIFY && web_view->priv->has_hover_link &&
|
|
gdk_event_get_window (event) != gtk_widget_get_window (GTK_WIDGET (web_view))) {
|
|
/* This won't reset WebKitGTK's cached link the cursor stays on, but do this,
|
|
instead of sending a fake signal to the WebKitGTK. */
|
|
e_web_view_status_message (web_view, NULL);
|
|
|
|
web_view->priv->has_hover_link = FALSE;
|
|
}
|
|
}
|
|
|
|
static void
|
|
web_view_map (GtkWidget *widget)
|
|
{
|
|
GtkWidget *toplevel;
|
|
|
|
toplevel = gtk_widget_get_toplevel (widget);
|
|
|
|
g_signal_connect (toplevel, "event-after", G_CALLBACK (web_view_toplevel_event_after_cb), widget);
|
|
|
|
GTK_WIDGET_CLASS (e_web_view_parent_class)->map (widget);
|
|
}
|
|
|
|
static void
|
|
web_view_unmap (GtkWidget *widget)
|
|
{
|
|
GtkWidget *toplevel;
|
|
|
|
toplevel = gtk_widget_get_toplevel (widget);
|
|
|
|
g_signal_handlers_disconnect_by_func (toplevel, G_CALLBACK (web_view_toplevel_event_after_cb), widget);
|
|
|
|
GTK_WIDGET_CLASS (e_web_view_parent_class)->unmap (widget);
|
|
|
|
e_web_view_status_message (E_WEB_VIEW (widget), NULL);
|
|
}
|
|
|
|
static void
|
|
e_web_view_class_init (EWebViewClass *class)
|
|
{
|
|
GObjectClass *object_class;
|
|
GtkWidgetClass *widget_class;
|
|
|
|
g_type_class_add_private (class, sizeof (EWebViewPrivate));
|
|
|
|
object_class = G_OBJECT_CLASS (class);
|
|
object_class->constructor = web_view_constructor;
|
|
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->scroll_event = web_view_scroll_event;
|
|
widget_class->drag_motion = web_view_drag_motion;
|
|
widget_class->drag_drop = web_view_drag_drop;
|
|
widget_class->drag_leave = web_view_drag_leave;
|
|
widget_class->map = web_view_map;
|
|
widget_class->unmap = web_view_unmap;
|
|
|
|
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->suggest_filename = web_view_suggest_filename;
|
|
class->before_popup_event = web_view_before_popup_event;
|
|
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));
|
|
|
|
/* Inherited from ESelectableInterface; just a fake property here */
|
|
g_object_class_override_property (
|
|
object_class,
|
|
PROP_COPY_TARGET_LIST,
|
|
"copy-target-list");
|
|
|
|
/* Inherited from ESelectableInterface; just a fake property here */
|
|
g_object_class_override_property (
|
|
object_class,
|
|
PROP_PASTE_TARGET_LIST,
|
|
"paste-target-list");
|
|
|
|
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_HAS_SELECTION,
|
|
g_param_spec_boolean (
|
|
"has-selection",
|
|
"Has Selection",
|
|
NULL,
|
|
FALSE,
|
|
G_PARAM_READABLE));
|
|
|
|
g_object_class_install_property (
|
|
object_class,
|
|
PROP_MINIMUM_FONT_SIZE,
|
|
g_param_spec_int (
|
|
"minimum-font-size",
|
|
"Minimum Font Size",
|
|
NULL,
|
|
G_MININT, G_MAXINT, 0,
|
|
G_PARAM_READWRITE));
|
|
|
|
g_object_class_install_property (
|
|
object_class,
|
|
PROP_NEED_INPUT,
|
|
g_param_spec_boolean (
|
|
"need-input",
|
|
"Need Input",
|
|
NULL,
|
|
FALSE,
|
|
G_PARAM_READABLE));
|
|
|
|
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[NEW_ACTIVITY] = g_signal_new (
|
|
"new-activity",
|
|
G_TYPE_FROM_CLASS (class),
|
|
G_SIGNAL_RUN_LAST,
|
|
G_STRUCT_OFFSET (EWebViewClass, new_activity),
|
|
NULL, NULL,
|
|
g_cclosure_marshal_VOID__OBJECT,
|
|
G_TYPE_NONE, 1,
|
|
E_TYPE_ACTIVITY);
|
|
|
|
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,
|
|
NULL,
|
|
G_TYPE_BOOLEAN, 2, G_TYPE_STRING, GDK_TYPE_EVENT | G_SIGNAL_TYPE_STATIC_SCOPE);
|
|
|
|
signals[BEFORE_POPUP_EVENT] = g_signal_new (
|
|
"before-popup-event",
|
|
G_TYPE_FROM_CLASS (class),
|
|
G_SIGNAL_RUN_LAST,
|
|
G_STRUCT_OFFSET (EWebViewClass, before_popup_event),
|
|
NULL, NULL,
|
|
g_cclosure_marshal_VOID__STRING,
|
|
G_TYPE_NONE, 1, 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);
|
|
|
|
/* Expects an empty string to abandon the request,
|
|
or NULL to keep the passed-in uri,
|
|
or a new uri to load instead. */
|
|
signals[URI_REQUESTED] = g_signal_new (
|
|
"uri-requested",
|
|
G_TYPE_FROM_CLASS (class),
|
|
G_SIGNAL_RUN_LAST,
|
|
G_STRUCT_OFFSET (EWebViewClass, uri_requested),
|
|
NULL, NULL, NULL,
|
|
G_TYPE_NONE, 2, G_TYPE_STRING, G_TYPE_POINTER);
|
|
|
|
signals[CONTENT_LOADED] = g_signal_new (
|
|
"content-loaded",
|
|
G_TYPE_FROM_CLASS (class),
|
|
G_SIGNAL_RUN_LAST,
|
|
G_STRUCT_OFFSET (EWebViewClass, content_loaded),
|
|
NULL, NULL, NULL,
|
|
G_TYPE_NONE, 1, G_TYPE_STRING);
|
|
|
|
signals[RESOURCE_LOADED] = g_signal_new (
|
|
"resource-loaded",
|
|
G_TYPE_FROM_CLASS (class),
|
|
G_SIGNAL_RUN_LAST,
|
|
0,
|
|
NULL, NULL, NULL,
|
|
G_TYPE_NONE, 0, G_TYPE_NONE);
|
|
}
|
|
|
|
static void
|
|
e_web_view_alert_sink_init (EAlertSinkInterface *iface)
|
|
{
|
|
iface->submit_alert = web_view_submit_alert;
|
|
}
|
|
|
|
static void
|
|
e_web_view_selectable_init (ESelectableInterface *iface)
|
|
{
|
|
iface->update_actions = web_view_selectable_update_actions;
|
|
iface->cut_clipboard = web_view_selectable_cut_clipboard;
|
|
iface->copy_clipboard = web_view_selectable_copy_clipboard;
|
|
iface->paste_clipboard = web_view_selectable_paste_clipboard;
|
|
iface->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;
|
|
GSettings *settings;
|
|
const gchar *domain = GETTEXT_PACKAGE;
|
|
const gchar *id;
|
|
gulong handler_id;
|
|
GError *error = NULL;
|
|
|
|
web_view->priv = E_WEB_VIEW_GET_PRIVATE (web_view);
|
|
|
|
web_view->priv->highlights_enabled = TRUE;
|
|
web_view->priv->old_settings = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, (GDestroyNotify) g_variant_unref);
|
|
web_view->priv->scheme_handlers = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_object_unref);
|
|
|
|
g_signal_connect (
|
|
web_view, "context-menu",
|
|
G_CALLBACK (web_view_context_menu_cb), NULL);
|
|
|
|
g_signal_connect (
|
|
web_view, "mouse-target-changed",
|
|
G_CALLBACK (web_view_mouse_target_changed_cb), NULL);
|
|
|
|
g_signal_connect (
|
|
web_view, "decide-policy",
|
|
G_CALLBACK (web_view_decide_policy_cb),
|
|
NULL);
|
|
|
|
g_signal_connect (
|
|
web_view, "load-changed",
|
|
G_CALLBACK (web_view_load_changed_cb), NULL);
|
|
|
|
g_signal_connect (
|
|
web_view, "style-updated",
|
|
G_CALLBACK (style_updated_cb), NULL);
|
|
|
|
g_signal_connect (
|
|
web_view, "state-flags-changed",
|
|
G_CALLBACK (style_updated_cb), NULL);
|
|
|
|
ui_manager = gtk_ui_manager_new ();
|
|
web_view->priv->ui_manager = ui_manager;
|
|
|
|
g_signal_connect_swapped (
|
|
ui_manager, "connect-proxy",
|
|
G_CALLBACK (web_view_connect_proxy_cb), web_view);
|
|
|
|
settings = e_util_ref_settings ("org.gnome.desktop.interface");
|
|
web_view->priv->font_settings = g_object_ref (settings);
|
|
handler_id = g_signal_connect_swapped (
|
|
settings, "changed::font-name",
|
|
G_CALLBACK (e_web_view_test_change_and_update_fonts_cb), web_view);
|
|
web_view->priv->font_name_changed_handler_id = handler_id;
|
|
handler_id = g_signal_connect_swapped (
|
|
settings, "changed::monospace-font-name",
|
|
G_CALLBACK (e_web_view_test_change_and_update_fonts_cb), web_view);
|
|
web_view->priv->monospace_font_name_changed_handler_id = handler_id;
|
|
g_object_unref (settings);
|
|
|
|
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);
|
|
|
|
e_binding_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);
|
|
|
|
e_binding_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);
|
|
|
|
e_binding_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);
|
|
|
|
web_view->priv->element_clicked_cbs = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, (GDestroyNotify) g_ptr_array_unref);
|
|
|
|
web_view->priv->cancellable = NULL;
|
|
}
|
|
|
|
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));
|
|
|
|
e_web_view_replace_load_cancellable (web_view, FALSE);
|
|
|
|
e_web_view_load_string (web_view,
|
|
"<html>"
|
|
"<head>"
|
|
"<meta name=\"color-scheme\" content=\"light dark\">"
|
|
"</head>"
|
|
"<body class=\"-e-web-view-background-color -e-web-view-text-color\"></body>"
|
|
"</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 != NULL);
|
|
g_return_if_fail (class->load_string != NULL);
|
|
|
|
e_web_view_replace_load_cancellable (web_view, TRUE);
|
|
|
|
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 != NULL);
|
|
g_return_if_fail (class->load_uri != NULL);
|
|
|
|
e_web_view_replace_load_cancellable (web_view, TRUE);
|
|
|
|
class->load_uri (web_view, uri);
|
|
}
|
|
|
|
/**
|
|
* e_web_view_suggest_filename:
|
|
* @web_view: an #EWebView
|
|
* @uri: a URI string
|
|
*
|
|
* Attempts to derive a suggested filename from the @uri for use in a
|
|
* "Save As" dialog.
|
|
*
|
|
* By default the suggested filename is the last path segment of the @uri
|
|
* (unless @uri looks like a query), but subclasses can use other mechanisms
|
|
* for custom URI schemes. For example, "cid:" URIs in an email message may
|
|
* refer to a MIME part with a suggested filename in its Content-Disposition
|
|
* header.
|
|
*
|
|
* The returned string should be freed with g_free() when finished with it,
|
|
* but callers should also be prepared for the function to return %NULL if
|
|
* a filename cannot be determined.
|
|
*
|
|
* Returns: a suggested filename, or %NULL
|
|
**/
|
|
gchar *
|
|
e_web_view_suggest_filename (EWebView *web_view,
|
|
const gchar *uri)
|
|
{
|
|
EWebViewClass *class;
|
|
gchar *filename;
|
|
|
|
g_return_val_if_fail (E_IS_WEB_VIEW (web_view), NULL);
|
|
g_return_val_if_fail (uri != NULL, NULL);
|
|
|
|
class = E_WEB_VIEW_GET_CLASS (web_view);
|
|
g_return_val_if_fail (class != NULL, NULL);
|
|
g_return_val_if_fail (class->suggest_filename != NULL, NULL);
|
|
|
|
filename = class->suggest_filename (web_view, uri);
|
|
|
|
if (filename != NULL)
|
|
e_util_make_safe_filename (filename);
|
|
|
|
return filename;
|
|
}
|
|
|
|
void
|
|
e_web_view_reload (EWebView *web_view)
|
|
{
|
|
g_return_if_fail (E_IS_WEB_VIEW (web_view));
|
|
|
|
e_web_view_replace_load_cancellable (web_view, TRUE);
|
|
|
|
webkit_web_view_reload (WEBKIT_WEB_VIEW (web_view));
|
|
}
|
|
|
|
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 == caret_mode)
|
|
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 NULL;
|
|
/* FIXME WK2 */
|
|
/*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 == disable_printing)
|
|
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 == disable_save_to_disk)
|
|
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_editable (EWebView *web_view)
|
|
{
|
|
g_return_val_if_fail (E_IS_WEB_VIEW (web_view), FALSE);
|
|
|
|
return webkit_web_view_is_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_need_input (EWebView *web_view)
|
|
{
|
|
g_return_val_if_fail (E_IS_WEB_VIEW (web_view), FALSE);
|
|
|
|
return web_view->priv->need_input;
|
|
}
|
|
|
|
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");
|
|
}
|
|
|
|
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), NULL);
|
|
|
|
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);
|
|
|
|
/* FIXME WK2
|
|
return webkit_web_view_get_paste_target_list (
|
|
WEBKIT_WEB_VIEW (web_view)); */
|
|
return NULL;
|
|
}
|
|
|
|
GtkAction *
|
|
e_web_view_get_print_proxy (EWebView *web_view)
|
|
{
|
|
g_return_val_if_fail (E_IS_WEB_VIEW (web_view), NULL);
|
|
|
|
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), NULL);
|
|
|
|
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");
|
|
}
|
|
|
|
void
|
|
e_web_view_get_last_popup_place (EWebView *web_view,
|
|
gchar **out_iframe_src,
|
|
gchar **out_iframe_id,
|
|
gchar **out_element_id,
|
|
gchar **out_link_uri)
|
|
{
|
|
g_return_if_fail (E_IS_WEB_VIEW (web_view));
|
|
|
|
if (out_iframe_src)
|
|
*out_iframe_src = g_strdup (web_view->priv->last_popup_iframe_src);
|
|
|
|
if (out_iframe_id)
|
|
*out_iframe_id = g_strdup (web_view->priv->last_popup_iframe_id);
|
|
|
|
if (out_element_id)
|
|
*out_element_id = g_strdup (web_view->priv->last_popup_element_id);
|
|
|
|
if (out_link_uri)
|
|
*out_link_uri = g_strdup (web_view->priv->last_popup_link_uri);
|
|
}
|
|
|
|
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);
|
|
|
|
g_queue_push_tail (
|
|
&web_view->priv->highlights,
|
|
g_strdup (highlight));
|
|
|
|
webkit_find_controller_search (
|
|
web_view->priv->find_controller,
|
|
highlight,
|
|
WEBKIT_FIND_OPTIONS_CASE_INSENSITIVE,
|
|
G_MAXUINT);
|
|
}
|
|
|
|
void
|
|
e_web_view_clear_highlights (EWebView *web_view)
|
|
{
|
|
g_return_if_fail (E_IS_WEB_VIEW (web_view));
|
|
|
|
webkit_find_controller_search_finish (web_view->priv->find_controller);
|
|
|
|
while (!g_queue_is_empty (&web_view->priv->highlights))
|
|
g_free (g_queue_pop_head (&web_view->priv->highlights));
|
|
}
|
|
|
|
void
|
|
e_web_view_update_highlights (EWebView *web_view)
|
|
{
|
|
g_return_if_fail (E_IS_WEB_VIEW (web_view));
|
|
|
|
web_view->priv->highlights_enabled = TRUE;
|
|
web_view_update_document_highlights (web_view);
|
|
}
|
|
|
|
void
|
|
e_web_view_disable_highlights (EWebView *web_view)
|
|
{
|
|
g_return_if_fail (E_IS_WEB_VIEW (web_view));
|
|
|
|
web_view->priv->highlights_enabled = FALSE;
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
void
|
|
e_web_view_copy_clipboard (EWebView *web_view)
|
|
{
|
|
g_return_if_fail (E_IS_WEB_VIEW (web_view));
|
|
|
|
webkit_web_view_execute_editing_command (
|
|
WEBKIT_WEB_VIEW (web_view), WEBKIT_EDITING_COMMAND_COPY);
|
|
}
|
|
|
|
void
|
|
e_web_view_cut_clipboard (EWebView *web_view)
|
|
{
|
|
g_return_if_fail (E_IS_WEB_VIEW (web_view));
|
|
|
|
webkit_web_view_execute_editing_command (
|
|
WEBKIT_WEB_VIEW (web_view), WEBKIT_EDITING_COMMAND_CUT);
|
|
}
|
|
|
|
gboolean
|
|
e_web_view_has_selection (EWebView *web_view)
|
|
{
|
|
g_return_val_if_fail (E_IS_WEB_VIEW (web_view), FALSE);
|
|
|
|
return web_view->priv->has_selection;
|
|
}
|
|
|
|
void
|
|
e_web_view_paste_clipboard (EWebView *web_view)
|
|
{
|
|
g_return_if_fail (E_IS_WEB_VIEW (web_view));
|
|
|
|
webkit_web_view_execute_editing_command (
|
|
WEBKIT_WEB_VIEW (web_view), WEBKIT_EDITING_COMMAND_PASTE);
|
|
}
|
|
|
|
gboolean
|
|
e_web_view_scroll_forward (EWebView *web_view)
|
|
{
|
|
g_return_val_if_fail (E_IS_WEB_VIEW (web_view), FALSE);
|
|
/* FIXME WK2
|
|
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);
|
|
/* FIXME WK2
|
|
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_execute_editing_command (
|
|
WEBKIT_WEB_VIEW (web_view), WEBKIT_EDITING_COMMAND_SELECT_ALL);
|
|
}
|
|
|
|
void
|
|
e_web_view_unselect_all (EWebView *web_view)
|
|
{
|
|
g_return_if_fail (E_IS_WEB_VIEW (web_view));
|
|
|
|
webkit_web_view_execute_editing_command (WEBKIT_WEB_VIEW (web_view), "Unselect");
|
|
}
|
|
|
|
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)
|
|
{
|
|
gdouble zoom_level;
|
|
|
|
g_return_if_fail (E_IS_WEB_VIEW (web_view));
|
|
|
|
/* There is no webkit_web_view_zoom_in function in WK2, so emulate it */
|
|
zoom_level = webkit_web_view_get_zoom_level (WEBKIT_WEB_VIEW (web_view));
|
|
/* zoom-step in WK1 was 0.1 */
|
|
zoom_level += 0.1;
|
|
if (zoom_level < 4.9999)
|
|
webkit_web_view_set_zoom_level (WEBKIT_WEB_VIEW (web_view), zoom_level);
|
|
}
|
|
|
|
void
|
|
e_web_view_zoom_out (EWebView *web_view)
|
|
{
|
|
gdouble zoom_level;
|
|
|
|
g_return_if_fail (E_IS_WEB_VIEW (web_view));
|
|
|
|
/* There is no webkit_web_view_zoom_out function in WK2, so emulate it */
|
|
zoom_level = webkit_web_view_get_zoom_level (WEBKIT_WEB_VIEW (web_view));
|
|
/* zoom-step in WK1 was 0.1 */
|
|
zoom_level -= 0.1;
|
|
if (zoom_level > 0.7999)
|
|
webkit_web_view_set_zoom_level (WEBKIT_WEB_VIEW (web_view), zoom_level);
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
static void
|
|
e_web_view_popup_menu_deactivate_cb (GtkMenu *popup_menu,
|
|
GtkWidget *web_view)
|
|
{
|
|
g_return_if_fail (GTK_IS_MENU (popup_menu));
|
|
|
|
g_signal_handlers_disconnect_by_func (popup_menu, e_web_view_popup_menu_deactivate_cb, web_view);
|
|
gtk_menu_detach (popup_menu);
|
|
}
|
|
|
|
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);
|
|
|
|
g_warn_if_fail (!gtk_menu_get_attach_widget (GTK_MENU (menu)));
|
|
|
|
gtk_menu_attach_to_widget (GTK_MENU (menu),
|
|
GTK_WIDGET (web_view),
|
|
NULL);
|
|
|
|
g_signal_connect (
|
|
menu, "deactivate",
|
|
G_CALLBACK (e_web_view_popup_menu_deactivate_cb), web_view);
|
|
|
|
return menu;
|
|
}
|
|
|
|
void
|
|
e_web_view_show_popup_menu (EWebView *web_view,
|
|
GdkEvent *event)
|
|
{
|
|
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);
|
|
|
|
gtk_menu_popup_at_pointer (GTK_MENU (menu), event);
|
|
}
|
|
|
|
/**
|
|
* e_web_view_new_activity:
|
|
* @web_view: an #EWebView
|
|
*
|
|
* Returns a new #EActivity for an #EWebView-related asynchronous operation,
|
|
* and emits the #EWebView::new-activity signal. By default the #EActivity
|
|
* comes loaded with a #GCancellable and sets the @web_view itself as the
|
|
* #EActivity:alert-sink (which means alerts are displayed directly in the
|
|
* content area). The signal emission allows the #EActivity to be further
|
|
* customized and/or tracked by the application.
|
|
*
|
|
* Returns: an #EActivity
|
|
**/
|
|
EActivity *
|
|
e_web_view_new_activity (EWebView *web_view)
|
|
{
|
|
EActivity *activity;
|
|
EAlertSink *alert_sink;
|
|
GCancellable *cancellable;
|
|
|
|
g_return_val_if_fail (E_IS_WEB_VIEW (web_view), NULL);
|
|
|
|
activity = e_activity_new ();
|
|
|
|
alert_sink = E_ALERT_SINK (web_view);
|
|
e_activity_set_alert_sink (activity, alert_sink);
|
|
|
|
cancellable = g_cancellable_new ();
|
|
e_activity_set_cancellable (activity, cancellable);
|
|
g_object_unref (cancellable);
|
|
|
|
g_signal_emit (web_view, signals[NEW_ACTIVITY], 0, activity);
|
|
|
|
return activity;
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
const gchar *
|
|
e_web_view_get_citation_color_for_level (gint level)
|
|
{
|
|
/* Block quote border colors are borrowed from Thunderbird. */
|
|
static const gchar *citation_color_levels[5] = {
|
|
"rgb(233,185,110)", /* level 5 - Chocolate 1 */
|
|
"rgb(114,159,207)", /* level 1 - Sky Blue 1 */
|
|
"rgb(173,127,168)", /* level 2 - Plum 1 */
|
|
"rgb(138,226,52)", /* level 3 - Chameleon 1 */
|
|
"rgb(252,175,62)", /* level 4 - Orange 1 */
|
|
};
|
|
|
|
g_return_val_if_fail (level > 0, citation_color_levels[1]);
|
|
|
|
return citation_color_levels[level % 5];
|
|
}
|
|
|
|
void
|
|
e_web_view_update_fonts_settings (GSettings *font_settings,
|
|
PangoFontDescription *ms_font,
|
|
PangoFontDescription *vw_font,
|
|
GtkWidget *view_widget)
|
|
{
|
|
gboolean clean_ms = FALSE, clean_vw = FALSE;
|
|
const gchar *styles[] = { "normal", "oblique", "italic" };
|
|
gchar fsbuff[G_ASCII_DTOSTR_BUF_SIZE];
|
|
GdkColor *link = NULL;
|
|
GdkColor *visited = NULL;
|
|
GString *stylesheet;
|
|
GtkStyleContext *context;
|
|
PangoFontDescription *ms, *vw;
|
|
WebKitSettings *wk_settings;
|
|
WebKitUserContentManager *manager;
|
|
WebKitUserStyleSheet *style_sheet;
|
|
|
|
if (!ms_font) {
|
|
gchar *font;
|
|
|
|
font = g_settings_get_string (
|
|
font_settings,
|
|
"monospace-font-name");
|
|
|
|
ms = pango_font_description_from_string (
|
|
(font && *font) ? font : "monospace 10");
|
|
|
|
clean_ms = TRUE;
|
|
|
|
g_free (font);
|
|
} else
|
|
ms = ms_font;
|
|
|
|
if (!pango_font_description_get_family (ms) ||
|
|
!pango_font_description_get_size (ms)) {
|
|
if (clean_ms)
|
|
pango_font_description_free (ms);
|
|
|
|
clean_ms = TRUE;
|
|
ms = pango_font_description_from_string ("monospace 10");
|
|
}
|
|
|
|
if (!vw_font) {
|
|
gchar *font;
|
|
|
|
font = g_settings_get_string (
|
|
font_settings,
|
|
"font-name");
|
|
|
|
vw = pango_font_description_from_string (
|
|
(font && *font) ? font : "serif 10");
|
|
|
|
clean_vw = TRUE;
|
|
|
|
g_free (font);
|
|
} else
|
|
vw = vw_font;
|
|
|
|
if (!pango_font_description_get_family (vw) ||
|
|
!pango_font_description_get_size (vw)) {
|
|
if (clean_vw)
|
|
pango_font_description_free (vw);
|
|
|
|
clean_vw = TRUE;
|
|
vw = pango_font_description_from_string ("serif 10");
|
|
}
|
|
|
|
stylesheet = g_string_new ("");
|
|
g_ascii_dtostr (fsbuff, G_ASCII_DTOSTR_BUF_SIZE,
|
|
((gdouble) pango_font_description_get_size (vw)) / PANGO_SCALE);
|
|
|
|
g_string_append_printf (
|
|
stylesheet,
|
|
"body {\n"
|
|
" font-family: '%s';\n"
|
|
" font-size: %spt;\n"
|
|
" font-weight: %d;\n"
|
|
" font-style: %s;\n",
|
|
pango_font_description_get_family (vw),
|
|
fsbuff,
|
|
pango_font_description_get_weight (vw),
|
|
styles[pango_font_description_get_style (vw)]);
|
|
|
|
g_string_append (stylesheet, "}\n");
|
|
|
|
g_ascii_dtostr (fsbuff, G_ASCII_DTOSTR_BUF_SIZE,
|
|
((gdouble) pango_font_description_get_size (ms)) / PANGO_SCALE);
|
|
|
|
g_string_append_printf (
|
|
stylesheet,
|
|
"pre,code,.pre {\n"
|
|
" font-family: '%s';\n"
|
|
" font-size: %spt;\n"
|
|
" font-weight: %d;\n"
|
|
" font-style: %s;\n"
|
|
" margin: 0px;\n"
|
|
"}\n",
|
|
pango_font_description_get_family (ms),
|
|
fsbuff,
|
|
pango_font_description_get_weight (ms),
|
|
styles[pango_font_description_get_style (ms)]);
|
|
|
|
if (view_widget) {
|
|
context = gtk_widget_get_style_context (view_widget);
|
|
gtk_style_context_get_style (
|
|
context,
|
|
"link-color", &link,
|
|
"visited-link-color", &visited,
|
|
NULL);
|
|
|
|
if (link == NULL) {
|
|
GdkRGBA rgba;
|
|
GtkStateFlags state;
|
|
|
|
link = g_slice_new0 (GdkColor);
|
|
link->blue = G_MAXINT16;
|
|
|
|
rgba.alpha = 1;
|
|
rgba.red = 0;
|
|
rgba.green = 0;
|
|
rgba.blue = 1;
|
|
|
|
state = gtk_style_context_get_state (context);
|
|
state = state & (~(GTK_STATE_FLAG_VISITED | GTK_STATE_FLAG_LINK));
|
|
state = state | GTK_STATE_FLAG_LINK;
|
|
|
|
gtk_style_context_save (context);
|
|
gtk_style_context_set_state (context, state);
|
|
gtk_style_context_get_color (context, state, &rgba);
|
|
gtk_style_context_restore (context);
|
|
|
|
e_rgba_to_color (&rgba, link);
|
|
}
|
|
|
|
if (visited == NULL) {
|
|
GdkRGBA rgba;
|
|
GtkStateFlags state;
|
|
|
|
visited = g_slice_new0 (GdkColor);
|
|
visited->red = G_MAXINT16;
|
|
|
|
rgba.alpha = 1;
|
|
rgba.red = 1;
|
|
rgba.green = 0;
|
|
rgba.blue = 0;
|
|
|
|
state = gtk_style_context_get_state (context);
|
|
state = state & (~(GTK_STATE_FLAG_VISITED | GTK_STATE_FLAG_LINK));
|
|
state = state | GTK_STATE_FLAG_VISITED;
|
|
|
|
gtk_style_context_save (context);
|
|
gtk_style_context_set_state (context, state);
|
|
gtk_style_context_get_color (context, state, &rgba);
|
|
gtk_style_context_restore (context);
|
|
|
|
e_rgba_to_color (&rgba, visited);
|
|
}
|
|
|
|
g_string_append_printf (
|
|
stylesheet,
|
|
"span.navigable, div.navigable, p.navigable {\n"
|
|
" color: #%06x;\n"
|
|
"}\n"
|
|
"a {\n"
|
|
" color: #%06x;\n"
|
|
"}\n"
|
|
"a:visited {\n"
|
|
" color: #%06x;\n"
|
|
"}\n",
|
|
e_color_to_value (link),
|
|
e_color_to_value (link),
|
|
e_color_to_value (visited));
|
|
|
|
gdk_color_free (link);
|
|
gdk_color_free (visited);
|
|
|
|
g_string_append (
|
|
stylesheet,
|
|
"blockquote[type=cite]:not(.-x-evo-plaintext-quoted) "
|
|
"{\n"
|
|
" padding: 0ch 1ch 0ch 1ch;\n"
|
|
" margin: 0ch;\n"
|
|
" border-width: 0px 2px 0px 2px;\n"
|
|
" border-style: none solid none solid;\n"
|
|
" border-radius: 2px;\n"
|
|
"}\n");
|
|
|
|
g_string_append_printf (
|
|
stylesheet,
|
|
"blockquote[type=cite]:not(.-x-evo-plaintext-quoted) "
|
|
"{\n"
|
|
" border-color: %s;\n"
|
|
" margin: 0 0 6px 0;\n"
|
|
"}\n",
|
|
e_web_view_get_citation_color_for_level (1));
|
|
|
|
g_string_append_printf (
|
|
stylesheet,
|
|
"blockquote[type=cite]:not(.-x-evo-plaintext-quoted) "
|
|
"blockquote[type=cite]:not(.-x-evo-plaintext-quoted) "
|
|
"{\n"
|
|
" border-color: %s;\n"
|
|
" margin: 0ch;\n"
|
|
"}\n",
|
|
e_web_view_get_citation_color_for_level (2));
|
|
|
|
g_string_append_printf (
|
|
stylesheet,
|
|
"blockquote[type=cite]:not(.-x-evo-plaintext-quoted) "
|
|
"blockquote[type=cite]:not(.-x-evo-plaintext-quoted) "
|
|
"blockquote[type=cite]:not(.-x-evo-plaintext-quoted) "
|
|
"{\n"
|
|
" border-color: %s;\n"
|
|
" margin: 0ch;\n"
|
|
"}\n",
|
|
e_web_view_get_citation_color_for_level (3));
|
|
|
|
g_string_append_printf (
|
|
stylesheet,
|
|
"blockquote[type=cite]:not(.-x-evo-plaintext-quoted) "
|
|
"blockquote[type=cite]:not(.-x-evo-plaintext-quoted) "
|
|
"blockquote[type=cite]:not(.-x-evo-plaintext-quoted) "
|
|
"blockquote[type=cite]:not(.-x-evo-plaintext-quoted) "
|
|
"{\n"
|
|
" border-color: %s;\n"
|
|
" margin: 0ch;\n"
|
|
"}\n",
|
|
e_web_view_get_citation_color_for_level (4));
|
|
|
|
g_string_append_printf (
|
|
stylesheet,
|
|
"blockquote[type=cite]:not(.-x-evo-plaintext-quoted) "
|
|
"blockquote[type=cite]:not(.-x-evo-plaintext-quoted) "
|
|
"blockquote[type=cite]:not(.-x-evo-plaintext-quoted) "
|
|
"blockquote[type=cite]:not(.-x-evo-plaintext-quoted) "
|
|
"blockquote[type=cite]:not(.-x-evo-plaintext-quoted) "
|
|
"{\n"
|
|
" border-color: %s;\n"
|
|
" margin: 0ch;\n"
|
|
"}\n",
|
|
e_web_view_get_citation_color_for_level (5));
|
|
|
|
g_string_append_printf (
|
|
stylesheet,
|
|
"blockquote[type=cite]:not(.-x-evo-plaintext-quoted) "
|
|
"blockquote[type=cite]:not(.-x-evo-plaintext-quoted) "
|
|
"blockquote[type=cite]:not(.-x-evo-plaintext-quoted) "
|
|
"blockquote[type=cite]:not(.-x-evo-plaintext-quoted) "
|
|
"blockquote[type=cite]:not(.-x-evo-plaintext-quoted) "
|
|
"blockquote[type=cite]:not(.-x-evo-plaintext-quoted) "
|
|
"{\n"
|
|
" border-color: %s;\n"
|
|
" padding: 0ch 0ch 0ch 1ch;\n"
|
|
" margin: 0ch;\n"
|
|
" border-width: 0px 0px 0px 2px;\n"
|
|
" border-style: none none none solid;\n"
|
|
"}\n",
|
|
e_web_view_get_citation_color_for_level (1));
|
|
}
|
|
|
|
wk_settings = webkit_web_view_get_settings (WEBKIT_WEB_VIEW (view_widget));
|
|
|
|
g_object_set (
|
|
wk_settings,
|
|
"default-font-size",
|
|
e_util_normalize_font_size (
|
|
view_widget, 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",
|
|
e_util_normalize_font_size (
|
|
view_widget, pango_font_description_get_size (ms) / PANGO_SCALE),
|
|
NULL);
|
|
|
|
manager = webkit_web_view_get_user_content_manager (WEBKIT_WEB_VIEW (view_widget));
|
|
webkit_user_content_manager_remove_all_style_sheets (manager);
|
|
|
|
style_sheet = webkit_user_style_sheet_new (
|
|
stylesheet->str,
|
|
WEBKIT_USER_CONTENT_INJECT_ALL_FRAMES,
|
|
WEBKIT_USER_STYLE_LEVEL_USER,
|
|
NULL,
|
|
NULL);
|
|
|
|
webkit_user_content_manager_add_style_sheet (manager, style_sheet);
|
|
|
|
webkit_user_style_sheet_unref (style_sheet);
|
|
|
|
g_string_free (stylesheet, TRUE);
|
|
|
|
if (clean_ms)
|
|
pango_font_description_free (ms);
|
|
if (clean_vw)
|
|
pango_font_description_free (vw);
|
|
|
|
e_web_view_update_styles (E_WEB_VIEW (view_widget), "*");
|
|
}
|
|
|
|
WebKitSettings *
|
|
e_web_view_get_default_webkit_settings (void)
|
|
{
|
|
WebKitSettings *settings;
|
|
|
|
settings = webkit_settings_new_with_settings (
|
|
"auto-load-images", TRUE,
|
|
"default-charset", "utf-8",
|
|
"enable-html5-database", FALSE,
|
|
"enable-dns-prefetching", FALSE,
|
|
"enable-html5-local-storage", FALSE,
|
|
"enable-java", FALSE,
|
|
"enable-javascript", TRUE, /* Needed for JavaScriptCore API to work */
|
|
"enable-javascript-markup", FALSE, /* Discards user-provided javascript in HTML */
|
|
"enable-offline-web-application-cache", FALSE,
|
|
"enable-page-cache", FALSE,
|
|
"enable-smooth-scrolling", FALSE,
|
|
"media-playback-allows-inline", FALSE,
|
|
"hardware-acceleration-policy", WEBKIT_HARDWARE_ACCELERATION_POLICY_NEVER,
|
|
NULL);
|
|
|
|
e_web_view_utils_apply_minimum_font_size (settings);
|
|
|
|
return settings;
|
|
}
|
|
|
|
void
|
|
e_web_view_utils_apply_minimum_font_size (WebKitSettings *wk_settings)
|
|
{
|
|
GSettings *settings;
|
|
gint value;
|
|
|
|
g_return_if_fail (WEBKIT_IS_SETTINGS (wk_settings));
|
|
|
|
settings = e_util_ref_settings ("org.gnome.evolution.shell");
|
|
value = g_settings_get_int (settings, "webkit-minimum-font-size");
|
|
g_clear_object (&settings);
|
|
|
|
if (value < 0)
|
|
value = 0;
|
|
|
|
if (webkit_settings_get_minimum_font_size (wk_settings) != (guint32) value)
|
|
webkit_settings_set_minimum_font_size (wk_settings, value);
|
|
}
|
|
|
|
gint
|
|
e_web_view_get_minimum_font_size (EWebView *web_view)
|
|
{
|
|
g_return_val_if_fail (E_IS_WEB_VIEW (web_view), -1);
|
|
|
|
return web_view->priv->minimum_font_size;
|
|
}
|
|
|
|
void
|
|
e_web_view_set_minimum_font_size (EWebView *web_view,
|
|
gint pixels)
|
|
{
|
|
g_return_if_fail (E_IS_WEB_VIEW (web_view));
|
|
|
|
if (web_view->priv->minimum_font_size != pixels) {
|
|
WebKitSettings *wk_settings;
|
|
|
|
web_view->priv->minimum_font_size = pixels;
|
|
|
|
wk_settings = webkit_web_view_get_settings (WEBKIT_WEB_VIEW (web_view));
|
|
e_web_view_utils_apply_minimum_font_size (wk_settings);
|
|
|
|
g_object_notify (G_OBJECT (web_view), "minimum-font-size");
|
|
}
|
|
}
|
|
|
|
GCancellable *
|
|
e_web_view_get_cancellable (EWebView *web_view)
|
|
{
|
|
g_return_val_if_fail (E_IS_WEB_VIEW (web_view), NULL);
|
|
|
|
return web_view->priv->cancellable;
|
|
}
|
|
|
|
void
|
|
e_web_view_update_fonts (EWebView *web_view)
|
|
{
|
|
EWebViewClass *class;
|
|
PangoFontDescription *ms = NULL, *vw = NULL;
|
|
|
|
g_return_if_fail (E_IS_WEB_VIEW (web_view));
|
|
|
|
class = E_WEB_VIEW_GET_CLASS (web_view);
|
|
g_return_if_fail (class != NULL);
|
|
|
|
if (class->set_fonts != NULL)
|
|
class->set_fonts (web_view, &ms, &vw);
|
|
|
|
e_web_view_update_fonts_settings (
|
|
web_view->priv->font_settings,
|
|
ms, vw, GTK_WIDGET (web_view));
|
|
|
|
pango_font_description_free (ms);
|
|
pango_font_description_free (vw);
|
|
}
|
|
|
|
/* Helper for e_web_view_cursor_image_copy() */
|
|
static void
|
|
web_view_cursor_image_copy_pixbuf_cb (GObject *source_object,
|
|
GAsyncResult *result,
|
|
gpointer user_data)
|
|
{
|
|
EActivity *activity;
|
|
EAlertSink *alert_sink;
|
|
GdkPixbuf *pixbuf;
|
|
GError *local_error = NULL;
|
|
|
|
activity = E_ACTIVITY (user_data);
|
|
alert_sink = e_activity_get_alert_sink (activity);
|
|
|
|
pixbuf = gdk_pixbuf_new_from_stream_finish (result, &local_error);
|
|
|
|
/* Sanity check. */
|
|
g_return_if_fail (
|
|
((pixbuf != NULL) && (local_error == NULL)) ||
|
|
((pixbuf == NULL) && (local_error != NULL)));
|
|
|
|
if (e_activity_handle_cancellation (activity, local_error)) {
|
|
g_error_free (local_error);
|
|
|
|
} else if (local_error != NULL) {
|
|
e_alert_submit (
|
|
alert_sink,
|
|
"widgets:no-image-copy",
|
|
local_error->message, NULL);
|
|
g_error_free (local_error);
|
|
|
|
} else {
|
|
GtkClipboard *clipboard;
|
|
|
|
clipboard = gtk_clipboard_get (GDK_SELECTION_CLIPBOARD);
|
|
gtk_clipboard_set_image (clipboard, pixbuf);
|
|
gtk_clipboard_store (clipboard);
|
|
|
|
e_activity_set_state (activity, E_ACTIVITY_COMPLETED);
|
|
}
|
|
|
|
g_clear_object (&activity);
|
|
g_clear_object (&pixbuf);
|
|
}
|
|
|
|
/* Helper for e_web_view_cursor_image_copy() */
|
|
static void
|
|
web_view_cursor_image_copy_request_cb (GObject *source_object,
|
|
GAsyncResult *result,
|
|
gpointer user_data)
|
|
{
|
|
EActivity *activity;
|
|
EAlertSink *alert_sink;
|
|
GCancellable *cancellable;
|
|
GInputStream *input_stream;
|
|
GError *local_error = NULL;
|
|
|
|
activity = E_ACTIVITY (user_data);
|
|
alert_sink = e_activity_get_alert_sink (activity);
|
|
cancellable = e_activity_get_cancellable (activity);
|
|
|
|
input_stream = e_web_view_request_finish (
|
|
E_WEB_VIEW (source_object), result, &local_error);
|
|
|
|
/* Sanity check. */
|
|
g_return_if_fail (
|
|
((input_stream != NULL) && (local_error == NULL)) ||
|
|
((input_stream == NULL) && (local_error != NULL)));
|
|
|
|
if (e_activity_handle_cancellation (activity, local_error)) {
|
|
g_error_free (local_error);
|
|
|
|
} else if (local_error != NULL) {
|
|
e_alert_submit (
|
|
alert_sink,
|
|
"widgets:no-image-copy",
|
|
local_error->message, NULL);
|
|
g_error_free (local_error);
|
|
|
|
} else {
|
|
gdk_pixbuf_new_from_stream_async (
|
|
input_stream, cancellable,
|
|
web_view_cursor_image_copy_pixbuf_cb,
|
|
g_object_ref (activity));
|
|
}
|
|
|
|
g_clear_object (&activity);
|
|
g_clear_object (&input_stream);
|
|
}
|
|
|
|
/**
|
|
* e_web_view_cursor_image_copy:
|
|
* @web_view: an #EWebView
|
|
*
|
|
* Asynchronously copies the image under the cursor to the clipboard.
|
|
*
|
|
* This function triggers an #EWebView::new-activity signal emission so
|
|
* the asynchronous operation can be tracked and/or cancelled.
|
|
**/
|
|
void
|
|
e_web_view_cursor_image_copy (EWebView *web_view)
|
|
{
|
|
g_return_if_fail (E_IS_WEB_VIEW (web_view));
|
|
|
|
if (web_view->priv->cursor_image_src != NULL) {
|
|
EActivity *activity;
|
|
GCancellable *cancellable;
|
|
const gchar *text;
|
|
|
|
activity = e_web_view_new_activity (web_view);
|
|
cancellable = e_activity_get_cancellable (activity);
|
|
|
|
text = _("Copying image to clipboard");
|
|
e_activity_set_text (activity, text);
|
|
|
|
e_web_view_request (
|
|
web_view,
|
|
web_view->priv->cursor_image_src,
|
|
cancellable,
|
|
web_view_cursor_image_copy_request_cb,
|
|
g_object_ref (activity));
|
|
|
|
g_object_unref (activity);
|
|
}
|
|
}
|
|
|
|
/* Helper for e_web_view_cursor_image_save() */
|
|
static void
|
|
web_view_cursor_image_save_splice_cb (GObject *source_object,
|
|
GAsyncResult *result,
|
|
gpointer user_data)
|
|
{
|
|
EActivity *activity;
|
|
EAlertSink *alert_sink;
|
|
AsyncContext *async_context;
|
|
GError *local_error = NULL;
|
|
|
|
async_context = (AsyncContext *) user_data;
|
|
|
|
activity = async_context->activity;
|
|
alert_sink = e_activity_get_alert_sink (activity);
|
|
|
|
g_output_stream_splice_finish (
|
|
G_OUTPUT_STREAM (source_object), result, &local_error);
|
|
|
|
if (e_activity_handle_cancellation (activity, local_error)) {
|
|
g_error_free (local_error);
|
|
|
|
} else if (local_error != NULL) {
|
|
e_alert_submit (
|
|
alert_sink,
|
|
"widgets:no-image-save",
|
|
local_error->message, NULL);
|
|
g_error_free (local_error);
|
|
|
|
} else {
|
|
e_activity_set_state (activity, E_ACTIVITY_COMPLETED);
|
|
}
|
|
|
|
async_context_free (async_context);
|
|
}
|
|
|
|
/* Helper for e_web_view_cursor_image_save() */
|
|
static void
|
|
web_view_cursor_image_save_replace_cb (GObject *source_object,
|
|
GAsyncResult *result,
|
|
gpointer user_data)
|
|
{
|
|
EActivity *activity;
|
|
EAlertSink *alert_sink;
|
|
GCancellable *cancellable;
|
|
GFileOutputStream *output_stream;
|
|
AsyncContext *async_context;
|
|
GError *local_error = NULL;
|
|
|
|
async_context = (AsyncContext *) user_data;
|
|
|
|
activity = async_context->activity;
|
|
alert_sink = e_activity_get_alert_sink (activity);
|
|
cancellable = e_activity_get_cancellable (activity);
|
|
|
|
output_stream = g_file_replace_finish (
|
|
G_FILE (source_object), result, &local_error);
|
|
|
|
/* Sanity check. */
|
|
g_return_if_fail (
|
|
((output_stream != NULL) && (local_error == NULL)) ||
|
|
((output_stream == NULL) && (local_error != NULL)));
|
|
|
|
if (e_activity_handle_cancellation (activity, local_error)) {
|
|
g_error_free (local_error);
|
|
async_context_free (async_context);
|
|
|
|
} else if (local_error != NULL) {
|
|
e_alert_submit (
|
|
alert_sink,
|
|
"widgets:no-image-save",
|
|
local_error->message, NULL);
|
|
g_error_free (local_error);
|
|
async_context_free (async_context);
|
|
|
|
} else {
|
|
g_output_stream_splice_async (
|
|
G_OUTPUT_STREAM (output_stream),
|
|
async_context->input_stream,
|
|
G_OUTPUT_STREAM_SPLICE_CLOSE_SOURCE |
|
|
G_OUTPUT_STREAM_SPLICE_CLOSE_TARGET,
|
|
G_PRIORITY_DEFAULT,
|
|
cancellable,
|
|
web_view_cursor_image_save_splice_cb,
|
|
async_context);
|
|
}
|
|
|
|
g_clear_object (&output_stream);
|
|
}
|
|
|
|
/* Helper for e_web_view_cursor_image_save() */
|
|
static void
|
|
web_view_cursor_image_save_request_cb (GObject *source_object,
|
|
GAsyncResult *result,
|
|
gpointer user_data)
|
|
{
|
|
EActivity *activity;
|
|
EAlertSink *alert_sink;
|
|
GCancellable *cancellable;
|
|
GInputStream *input_stream;
|
|
AsyncContext *async_context;
|
|
GError *local_error = NULL;
|
|
|
|
async_context = (AsyncContext *) user_data;
|
|
|
|
activity = async_context->activity;
|
|
alert_sink = e_activity_get_alert_sink (activity);
|
|
cancellable = e_activity_get_cancellable (activity);
|
|
|
|
input_stream = e_web_view_request_finish (
|
|
E_WEB_VIEW (source_object), result, &local_error);
|
|
|
|
/* Sanity check. */
|
|
g_return_if_fail (
|
|
((input_stream != NULL) && (local_error == NULL)) ||
|
|
((input_stream == NULL) && (local_error != NULL)));
|
|
|
|
if (e_activity_handle_cancellation (activity, local_error)) {
|
|
g_error_free (local_error);
|
|
async_context_free (async_context);
|
|
|
|
} else if (local_error != NULL) {
|
|
e_alert_submit (
|
|
alert_sink,
|
|
"widgets:no-image-save",
|
|
local_error->message, NULL);
|
|
g_error_free (local_error);
|
|
async_context_free (async_context);
|
|
|
|
} else {
|
|
async_context->input_stream = g_object_ref (input_stream);
|
|
|
|
/* Open an output stream to the destination file. */
|
|
g_file_replace_async (
|
|
async_context->destination,
|
|
NULL, FALSE,
|
|
G_FILE_CREATE_REPLACE_DESTINATION,
|
|
G_PRIORITY_DEFAULT,
|
|
cancellable,
|
|
web_view_cursor_image_save_replace_cb,
|
|
async_context);
|
|
}
|
|
|
|
g_clear_object (&input_stream);
|
|
}
|
|
|
|
/**
|
|
* e_web_view_cursor_image_save:
|
|
* @web_view: an #EWebView
|
|
*
|
|
* Prompts the user to choose a destination file and then asynchronously
|
|
* saves the image under the cursor to the destination file.
|
|
*
|
|
* This function triggers an #EWebView::new-activity signal emission so
|
|
* the asynchronous operation can be tracked and/or cancelled.
|
|
**/
|
|
void
|
|
e_web_view_cursor_image_save (EWebView *web_view)
|
|
{
|
|
GtkFileChooser *file_chooser;
|
|
GtkFileChooserNative *native;
|
|
GFile *destination = NULL;
|
|
gchar *suggestion;
|
|
gpointer toplevel;
|
|
|
|
g_return_if_fail (E_IS_WEB_VIEW (web_view));
|
|
|
|
if (web_view->priv->cursor_image_src == NULL)
|
|
return;
|
|
|
|
toplevel = gtk_widget_get_toplevel (GTK_WIDGET (web_view));
|
|
toplevel = gtk_widget_is_toplevel (toplevel) ? toplevel : NULL;
|
|
|
|
native = gtk_file_chooser_native_new (
|
|
_("Save Image"), toplevel,
|
|
GTK_FILE_CHOOSER_ACTION_SAVE,
|
|
_("_Save"), _("_Cancel"));
|
|
|
|
file_chooser = GTK_FILE_CHOOSER (native);
|
|
gtk_file_chooser_set_local_only (file_chooser, FALSE);
|
|
gtk_file_chooser_set_do_overwrite_confirmation (file_chooser, TRUE);
|
|
|
|
suggestion = e_web_view_suggest_filename (
|
|
web_view, web_view->priv->cursor_image_src);
|
|
|
|
if (suggestion != NULL) {
|
|
gtk_file_chooser_set_current_name (file_chooser, suggestion);
|
|
g_free (suggestion);
|
|
}
|
|
|
|
e_util_load_file_chooser_folder (file_chooser);
|
|
|
|
if (gtk_native_dialog_run (GTK_NATIVE_DIALOG (native)) == GTK_RESPONSE_ACCEPT) {
|
|
e_util_save_file_chooser_folder (file_chooser);
|
|
|
|
destination = gtk_file_chooser_get_file (file_chooser);
|
|
}
|
|
|
|
g_object_unref (native);
|
|
|
|
if (destination != NULL) {
|
|
EActivity *activity;
|
|
GCancellable *cancellable;
|
|
AsyncContext *async_context;
|
|
gchar *text;
|
|
gchar *uri;
|
|
|
|
activity = e_web_view_new_activity (web_view);
|
|
cancellable = e_activity_get_cancellable (activity);
|
|
|
|
uri = g_file_get_uri (destination);
|
|
text = g_strdup_printf (_("Saving image to “%s”"), uri);
|
|
e_activity_set_text (activity, text);
|
|
g_free (text);
|
|
g_free (uri);
|
|
|
|
async_context = g_slice_new0 (AsyncContext);
|
|
async_context->activity = g_object_ref (activity);
|
|
async_context->destination = g_object_ref (destination);
|
|
|
|
e_web_view_request (
|
|
web_view,
|
|
web_view->priv->cursor_image_src,
|
|
cancellable,
|
|
web_view_cursor_image_save_request_cb,
|
|
async_context);
|
|
|
|
g_object_unref (activity);
|
|
g_object_unref (destination);
|
|
}
|
|
}
|
|
|
|
static void
|
|
web_view_content_request_processed_cb (GObject *source_object,
|
|
GAsyncResult *result,
|
|
gpointer user_data)
|
|
{
|
|
AsyncContext *async_context = user_data;
|
|
GTask *task = g_steal_pointer (&async_context->task);
|
|
gint64 stream_length = -1;
|
|
gchar *mime_type = NULL;
|
|
GError *local_error = NULL;
|
|
|
|
if (!e_content_request_process_finish (E_CONTENT_REQUEST (source_object), result,
|
|
&async_context->input_stream, &stream_length, &mime_type, &local_error)) {
|
|
g_task_return_error (task, local_error);
|
|
} else {
|
|
g_task_return_boolean (task, TRUE);
|
|
}
|
|
|
|
g_free (mime_type);
|
|
g_clear_object (&task);
|
|
}
|
|
|
|
/**
|
|
* e_web_view_request:
|
|
* @web_view: an #EWebView
|
|
* @uri: the URI to load
|
|
* @cancellable: optional #GCancellable object, or %NULL
|
|
* @callback: a #GAsyncReadyCallback to call when the request is satisfied
|
|
* @user_data: data to pass to the callback function
|
|
*
|
|
* Asynchronously requests data at @uri as displaed in the @web_view.
|
|
*
|
|
* When the operation is finished, @callback will be called. You can then
|
|
* call e_web_view_request_finish() to get the result of the operation.
|
|
**/
|
|
void
|
|
e_web_view_request (EWebView *web_view,
|
|
const gchar *uri,
|
|
GCancellable *cancellable,
|
|
GAsyncReadyCallback callback,
|
|
gpointer user_data)
|
|
{
|
|
EContentRequest *content_request = NULL;
|
|
AsyncContext *async_context;
|
|
GHashTableIter iter;
|
|
GTask *task;
|
|
gboolean is_processed = FALSE;
|
|
gpointer value;
|
|
|
|
g_return_if_fail (E_IS_WEB_VIEW (web_view));
|
|
g_return_if_fail (uri != NULL);
|
|
|
|
g_hash_table_iter_init (&iter, web_view->priv->scheme_handlers);
|
|
|
|
while (g_hash_table_iter_next (&iter, NULL, &value)) {
|
|
EContentRequest *adept = value;
|
|
|
|
if (!E_IS_CONTENT_REQUEST (adept) ||
|
|
!e_content_request_can_process_uri (adept, uri))
|
|
continue;
|
|
|
|
content_request = adept;
|
|
break;
|
|
}
|
|
|
|
async_context = g_slice_new0 (AsyncContext);
|
|
async_context->uri = g_strdup (uri);
|
|
|
|
task = g_task_new (web_view, cancellable, callback, user_data);
|
|
g_task_set_task_data (task, async_context, async_context_free);
|
|
g_task_set_check_cancellable (task, TRUE);
|
|
|
|
if (content_request) {
|
|
async_context->content_request = g_object_ref (content_request);
|
|
async_context->task = g_object_ref (task);
|
|
e_content_request_process (async_context->content_request,
|
|
async_context->uri,
|
|
G_OBJECT (web_view),
|
|
cancellable,
|
|
web_view_content_request_processed_cb,
|
|
async_context);
|
|
is_processed = TRUE;
|
|
|
|
/* Handle base64-encoded "data:" URIs manually */
|
|
} else if (g_ascii_strncasecmp (uri, "data:", 5) == 0) {
|
|
/* data:[<mime type>][;charset=<charset>][;base64],<encoded data> */
|
|
const gchar *ptr, *from;
|
|
gboolean is_base64 = FALSE;
|
|
|
|
ptr = uri + 5;
|
|
from = ptr;
|
|
while (*ptr && *ptr != ',') {
|
|
ptr++;
|
|
|
|
if (*ptr == ',' || *ptr == ';') {
|
|
if (g_ascii_strncasecmp (from, "base64", ptr - from) == 0)
|
|
is_base64 = TRUE;
|
|
|
|
from = ptr + 1;
|
|
}
|
|
}
|
|
|
|
if (is_base64 && *ptr == ',') {
|
|
guchar *data;
|
|
gsize len = 0;
|
|
|
|
data = g_base64_decode (ptr + 1, &len);
|
|
|
|
if (data && len > 0) {
|
|
async_context->input_stream = g_memory_input_stream_new_from_data (data, len, g_free);
|
|
g_task_return_boolean (task, TRUE);
|
|
is_processed = TRUE;
|
|
} else {
|
|
g_free (data);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!is_processed) {
|
|
GString *shorten_uri = NULL;
|
|
gint len;
|
|
|
|
len = g_utf8_strlen (uri, -1);
|
|
|
|
/* The "data:" URIs can be quite long */
|
|
if (len > 512) {
|
|
const gchar *ptr = g_utf8_offset_to_pointer (uri, 512);
|
|
|
|
shorten_uri = g_string_sized_new (ptr - uri + 16);
|
|
g_string_append_len (shorten_uri, uri, ptr - uri);
|
|
g_string_append (shorten_uri, _("…"));
|
|
}
|
|
|
|
g_task_return_new_error (task, G_IO_ERROR, G_IO_ERROR_FAILED,
|
|
_("Cannot get URI “%s”, do not know how to download it."), shorten_uri ? shorten_uri->str : uri);
|
|
|
|
if (shorten_uri)
|
|
g_string_free (shorten_uri, TRUE);
|
|
}
|
|
|
|
g_object_unref (task);
|
|
}
|
|
|
|
/**
|
|
* e_web_view_request_finish:
|
|
* @web_view: an #EWebView
|
|
* @result: a #GAsyncResult
|
|
* @error: return location for a #GError, or %NULL
|
|
*
|
|
* Finishes the operation started with e_web_view_request().
|
|
*
|
|
* Unreference the returned #GInputStream with g_object_unref() when finished
|
|
* with it. If an error occurred, the function will set @error and return
|
|
* %NULL.
|
|
*
|
|
* Returns: a #GInputStream, or %NULL
|
|
**/
|
|
GInputStream *
|
|
e_web_view_request_finish (EWebView *web_view,
|
|
GAsyncResult *result,
|
|
GError **error)
|
|
{
|
|
AsyncContext *async_context;
|
|
|
|
g_return_val_if_fail (g_task_is_valid (result, web_view), NULL);
|
|
|
|
if (!g_task_propagate_boolean (G_TASK (result), error))
|
|
return NULL;
|
|
|
|
async_context = g_task_get_task_data (G_TASK (result));
|
|
|
|
g_return_val_if_fail (async_context->input_stream != NULL, NULL);
|
|
|
|
return g_object_ref (async_context->input_stream);
|
|
}
|
|
|
|
/**
|
|
* e_web_view_set_iframe_src:
|
|
* @web_view: an #EWebView
|
|
* @document_uri: a document URI for whose IFrame change the source
|
|
* @src_uri: the source to change the IFrame to
|
|
*
|
|
* Change IFrame source for the given @document_uri IFrame
|
|
* to the @new_iframe_src.
|
|
*
|
|
* Since: 3.22
|
|
**/
|
|
void
|
|
e_web_view_set_iframe_src (EWebView *web_view,
|
|
const gchar *iframe_id,
|
|
const gchar *src_uri)
|
|
{
|
|
g_return_if_fail (E_IS_WEB_VIEW (web_view));
|
|
|
|
e_web_view_jsc_run_script (WEBKIT_WEB_VIEW (web_view), web_view->priv->cancellable,
|
|
"Evo.SetIFrameSrc(%s, %s);",
|
|
iframe_id, src_uri);
|
|
}
|
|
|
|
/**
|
|
* EWebViewElementClickedFunc:
|
|
* @web_view: an #EWebView
|
|
* @iframe_id: an iframe ID in which the click happened; empty string for the main frame
|
|
* @element_id: an element ID
|
|
* @element_class: an element class, as set on the element which had been clicked
|
|
* @element_value: a 'value' attribute content of the clicked element
|
|
* @element_position: a #GtkAllocation with the position of the clicked element
|
|
* @user_data: user data as provided in the e_web_view_register_element_clicked() call
|
|
*
|
|
* The callback is called whenever an element of class @element_class is clicked.
|
|
* The @element_value is a content of the 'value' attribute of the clicked element.
|
|
* The @element_position is the place of the element within the web page, already
|
|
* accounting scrollbar positions.
|
|
*
|
|
* See: e_web_view_register_element_clicked, e_web_view_unregister_element_clicked
|
|
*
|
|
* Since: 3.22
|
|
**/
|
|
|
|
/**
|
|
* e_web_view_register_element_clicked:
|
|
* @web_view: an #EWebView
|
|
* @element_class: an element class on which to listen for clicking
|
|
* @callback: an #EWebViewElementClickedFunc to call, when the element is clicked
|
|
* @user_data: user data to pass to @callback
|
|
*
|
|
* Registers a @callback to be called when any element of the class @element_class
|
|
* is clicked. If the element contains a 'value' attribute, then it is passed to
|
|
* the @callback too. These callback are valid until a new content of the @web_view
|
|
* is loaded, after which all the registered callbacks are forgotten.
|
|
*
|
|
* Since: 3.22
|
|
**/
|
|
void
|
|
e_web_view_register_element_clicked (EWebView *web_view,
|
|
const gchar *element_class,
|
|
EWebViewElementClickedFunc callback,
|
|
gpointer user_data)
|
|
{
|
|
ElementClickedData *ecd;
|
|
GPtrArray *cbs;
|
|
guint ii;
|
|
|
|
g_return_if_fail (E_IS_WEB_VIEW (web_view));
|
|
g_return_if_fail (element_class != NULL);
|
|
g_return_if_fail (callback != NULL);
|
|
|
|
cbs = g_hash_table_lookup (web_view->priv->element_clicked_cbs, element_class);
|
|
if (cbs) {
|
|
for (ii = 0; ii < cbs->len; ii++) {
|
|
ecd = g_ptr_array_index (cbs, ii);
|
|
|
|
if (ecd && ecd->callback == callback && ecd->user_data == user_data) {
|
|
/* Callback is already registered, but re-register it, in case the page
|
|
was changed dynamically and new elements with the given call are added. */
|
|
web_view_call_register_element_clicked (web_view, "*", element_class);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
ecd = g_new0 (ElementClickedData, 1);
|
|
ecd->callback = callback;
|
|
ecd->user_data = user_data;
|
|
|
|
if (!cbs) {
|
|
cbs = g_ptr_array_new_full (1, g_free);
|
|
g_ptr_array_add (cbs, ecd);
|
|
|
|
g_hash_table_insert (web_view->priv->element_clicked_cbs, g_strdup (element_class), cbs);
|
|
} else {
|
|
g_ptr_array_add (cbs, ecd);
|
|
}
|
|
|
|
/* Dynamically changing page can call this multiple times; re-register all classes */
|
|
web_view_call_register_element_clicked (web_view, "*", NULL);
|
|
}
|
|
|
|
/**
|
|
* e_web_view_unregister_element_clicked:
|
|
* @web_view: an #EWebView
|
|
* @element_class: an element class on which to listen for clicking
|
|
* @callback: an #EWebViewElementClickedFunc to call, when the element is clicked
|
|
* @user_data: user data to pass to @callback
|
|
*
|
|
* Unregisters the @callback for the @element_class with the given @user_data, which
|
|
* should be previously registered with e_web_view_register_element_clicked(). This
|
|
* unregister is usually not needed, because the @web_view unregisters all callbacks
|
|
* when a new content is loaded.
|
|
*
|
|
* Since: 3.22
|
|
**/
|
|
void
|
|
e_web_view_unregister_element_clicked (EWebView *web_view,
|
|
const gchar *element_class,
|
|
EWebViewElementClickedFunc callback,
|
|
gpointer user_data)
|
|
{
|
|
ElementClickedData *ecd;
|
|
GPtrArray *cbs;
|
|
guint ii;
|
|
|
|
g_return_if_fail (E_IS_WEB_VIEW (web_view));
|
|
g_return_if_fail (element_class != NULL);
|
|
g_return_if_fail (callback != NULL);
|
|
|
|
cbs = g_hash_table_lookup (web_view->priv->element_clicked_cbs, element_class);
|
|
if (!cbs)
|
|
return;
|
|
|
|
for (ii = 0; ii < cbs->len; ii++) {
|
|
ecd = g_ptr_array_index (cbs, ii);
|
|
|
|
if (ecd && ecd->callback == callback && ecd->user_data == user_data) {
|
|
g_ptr_array_remove (cbs, ecd);
|
|
if (!cbs->len)
|
|
g_hash_table_remove (web_view->priv->element_clicked_cbs, element_class);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
e_web_view_set_element_hidden (EWebView *web_view,
|
|
const gchar *element_id,
|
|
gboolean hidden)
|
|
{
|
|
g_return_if_fail (E_IS_WEB_VIEW (web_view));
|
|
g_return_if_fail (element_id && *element_id);
|
|
|
|
e_web_view_jsc_set_element_hidden (WEBKIT_WEB_VIEW (web_view),
|
|
"*", element_id, hidden,
|
|
web_view->priv->cancellable);
|
|
}
|
|
|
|
void
|
|
e_web_view_set_element_style_property (EWebView *web_view,
|
|
const gchar *element_id,
|
|
const gchar *property_name,
|
|
const gchar *value)
|
|
{
|
|
g_return_if_fail (E_IS_WEB_VIEW (web_view));
|
|
g_return_if_fail (element_id && *element_id);
|
|
g_return_if_fail (property_name && *property_name);
|
|
|
|
e_web_view_jsc_set_element_style_property (WEBKIT_WEB_VIEW (web_view),
|
|
"*", element_id, property_name, value,
|
|
web_view->priv->cancellable);
|
|
}
|
|
|
|
void
|
|
e_web_view_set_element_attribute (EWebView *web_view,
|
|
const gchar *element_id,
|
|
const gchar *namespace_uri,
|
|
const gchar *qualified_name,
|
|
const gchar *value)
|
|
{
|
|
g_return_if_fail (E_IS_WEB_VIEW (web_view));
|
|
g_return_if_fail (element_id && *element_id);
|
|
g_return_if_fail (qualified_name && *qualified_name);
|
|
|
|
e_web_view_jsc_set_element_attribute (WEBKIT_WEB_VIEW (web_view),
|
|
"*", element_id, namespace_uri, qualified_name, value,
|
|
web_view->priv->cancellable);
|
|
}
|