From 6bd1c6833a2c51898ac45865767dd01ba66a95c5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dan=20Vr=C3=A1til?= Date: Wed, 28 Mar 2012 18:37:35 +0200 Subject: WebKit port - port widgets --- widgets/misc/e-web-view.c | 1402 ++++++++++++++++++++++++++++++--------------- 1 file changed, 934 insertions(+), 468 deletions(-) (limited to 'widgets/misc/e-web-view.c') diff --git a/widgets/misc/e-web-view.c b/widgets/misc/e-web-view.c index 26c922470b..1fdc5559d9 100644 --- a/widgets/misc/e-web-view.c +++ b/widgets/misc/e-web-view.c @@ -22,6 +22,10 @@ #include "e-web-view.h" +#include + +#include + #include #include @@ -33,8 +37,11 @@ #include #include +#include + #include "e-popup-action.h" #include "e-selectable.h" +#include #define E_WEB_VIEW_GET_PRIVATE(obj) \ (G_TYPE_INSTANCE_GET_PRIVATE \ @@ -49,51 +56,39 @@ struct _EWebViewPrivate { GdkPixbufAnimation *cursor_image; gchar *cursor_image_src; + GHashTable *js_callbacks; + + GSList *highlights; + GtkAction *open_proxy; GtkAction *print_proxy; GtkAction *save_as_proxy; - GtkTargetList *copy_target_list; - GtkTargetList *paste_target_list; - /* Lockdown Options */ guint disable_printing : 1; guint disable_save_to_disk : 1; -}; -struct _EWebViewRequest { - GFile *file; - EWebView *web_view; - GCancellable *cancellable; - GInputStream *input_stream; - GtkHTMLStream *output_stream; - gchar buffer[4096]; + guint caret_mode : 1; }; enum { PROP_0, - PROP_ANIMATE, PROP_CARET_MODE, PROP_COPY_TARGET_LIST, PROP_CURSOR_IMAGE, PROP_CURSOR_IMAGE_SRC, PROP_DISABLE_PRINTING, PROP_DISABLE_SAVE_TO_DISK, - PROP_EDITABLE, PROP_INLINE_SPELLING, PROP_MAGIC_LINKS, PROP_MAGIC_SMILEYS, PROP_OPEN_PROXY, - PROP_PASTE_TARGET_LIST, PROP_PRINT_PROXY, PROP_SAVE_AS_PROXY, PROP_SELECTED_URI }; enum { - COPY_CLIPBOARD, - CUT_CLIPBOARD, - PASTE_CLIPBOARD, POPUP_EVENT, STATUS_MESSAGE, STOP_LOADING, @@ -125,6 +120,7 @@ static const gchar *ui = " " " " " " +" " " " ""; @@ -135,7 +131,7 @@ static void e_web_view_selectable_init (ESelectableInterface *interface); G_DEFINE_TYPE_WITH_CODE ( EWebView, e_web_view, - GTK_TYPE_HTML, + WEBKIT_TYPE_WEB_VIEW, G_IMPLEMENT_INTERFACE ( E_TYPE_EXTENSIBLE, NULL) G_IMPLEMENT_INTERFACE ( @@ -145,141 +141,6 @@ G_DEFINE_TYPE_WITH_CODE ( E_TYPE_SELECTABLE, e_web_view_selectable_init)) -static EWebViewRequest * -web_view_request_new (EWebView *web_view, - const gchar *uri, - GtkHTMLStream *stream) -{ - EWebViewRequest *request; - GList *list; - - request = g_slice_new (EWebViewRequest); - - /* Try to detect file paths posing as URIs. */ - if (*uri == '/') - request->file = g_file_new_for_path (uri); - else - request->file = g_file_new_for_uri (uri); - - request->web_view = g_object_ref (web_view); - request->cancellable = g_cancellable_new (); - request->input_stream = NULL; - request->output_stream = stream; - - list = request->web_view->priv->requests; - list = g_list_prepend (list, request); - request->web_view->priv->requests = list; - - return request; -} - -static void -web_view_request_free (EWebViewRequest *request) -{ - GList *list; - - list = request->web_view->priv->requests; - list = g_list_remove (list, request); - request->web_view->priv->requests = list; - - g_object_unref (request->file); - g_object_unref (request->web_view); - g_object_unref (request->cancellable); - - if (request->input_stream != NULL) - g_object_unref (request->input_stream); - - g_slice_free (EWebViewRequest, request); -} - -static void -web_view_request_cancel (EWebViewRequest *request) -{ - g_cancellable_cancel (request->cancellable); -} - -static gboolean -web_view_request_check_for_error (EWebViewRequest *request, - GError *error) -{ - GtkHTML *html; - GtkHTMLStream *stream; - - if (error == NULL) - return FALSE; - - if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED)) { - /* use this error, but do not close the stream */ - g_error_free (error); - return TRUE; - } - - /* XXX Should we log errors that are not cancellations? */ - - html = GTK_HTML (request->web_view); - stream = request->output_stream; - - gtk_html_end (html, stream, GTK_HTML_STREAM_ERROR); - web_view_request_free (request); - g_error_free (error); - - return TRUE; -} - -static void -web_view_request_stream_read_cb (GInputStream *input_stream, - GAsyncResult *result, - EWebViewRequest *request) -{ - gssize bytes_read; - GError *error = NULL; - - bytes_read = g_input_stream_read_finish (input_stream, result, &error); - - if (web_view_request_check_for_error (request, error)) - return; - - if (bytes_read == 0) { - gtk_html_end ( - GTK_HTML (request->web_view), - request->output_stream, GTK_HTML_STREAM_OK); - web_view_request_free (request); - return; - } - - gtk_html_write ( - GTK_HTML (request->web_view), - request->output_stream, request->buffer, bytes_read); - - g_input_stream_read_async ( - request->input_stream, request->buffer, - sizeof (request->buffer), G_PRIORITY_DEFAULT, - request->cancellable, (GAsyncReadyCallback) - web_view_request_stream_read_cb, request); -} - -static void -web_view_request_read_cb (GFile *file, - GAsyncResult *result, - EWebViewRequest *request) -{ - GFileInputStream *input_stream; - GError *error = NULL; - - /* Input stream might be NULL, so don't use cast macro. */ - input_stream = g_file_read_finish (file, result, &error); - request->input_stream = (GInputStream *) input_stream; - - if (web_view_request_check_for_error (request, error)) - return; - - g_input_stream_read_async ( - request->input_stream, request->buffer, - sizeof (request->buffer), G_PRIORITY_DEFAULT, - request->cancellable, (GAsyncReadyCallback) - web_view_request_stream_read_cb, request); -} - static void action_copy_clipboard_cb (GtkAction *action, EWebView *web_view) @@ -470,71 +331,149 @@ static GtkActionEntry standard_entries[] = { G_CALLBACK (action_select_all_cb) } }; -static gboolean -web_view_button_press_event_cb (EWebView *web_view, - GdkEventButton *event, - GtkHTML *frame) +static void +web_view_menu_item_select_cb (EWebView *web_view, + GtkWidget *widget) { - gboolean event_handled = FALSE; - gchar *uri = NULL; + GtkAction *action; + GtkActivatable *activatable; + const gchar *tooltip; - if (event) { - GdkPixbufAnimation *anim; - gchar *image_src; + activatable = GTK_ACTIVATABLE (widget); + action = gtk_activatable_get_related_action (activatable); + tooltip = gtk_action_get_tooltip (action); - if (frame == NULL) - frame = GTK_HTML (web_view); + if (tooltip == NULL) + return; - anim = gtk_html_get_image_at (frame, event->x, event->y); - e_web_view_set_cursor_image (web_view, anim); - if (anim != NULL) - g_object_unref (anim); + e_web_view_status_message (web_view, tooltip); +} - image_src = gtk_html_get_image_src_at ( - frame, event->x, event->y); - e_web_view_set_cursor_image_src (web_view, image_src); - g_free (image_src); - } +static void +replace_text (WebKitDOMNode *node, + const gchar *text, + WebKitDOMNode *replacement) +{ + /* NodeType 3 = TEXT_NODE */ + if (webkit_dom_node_get_node_type (node) == 3) { + + gint text_length = strlen (text); + + while (node) { + + WebKitDOMNode *current_node, *replacement_node; + const gchar *node_data, *offset; + goffset split_offset; + gint data_length; + + current_node = node; + + /* Don't use the WEBKIT_DOM_CHARACTER_DATA macro for + * casting. WebKit lies about type of the object and + * GLib will throw runtime warning about node not being + * WebKitDOMCharacterData, but the function will return + * correct and valid data. + * IMO it's bug in the Gtk bindings and WebKit internally + * handles it by the nodeType so therefor it works + * event for "invalid" objects. But really, who knows..? + */ + node_data = webkit_dom_character_data_get_data ( + (WebKitDOMCharacterData *) node); + + offset = strstr (node_data, text); + if (!offset) { + node = NULL; + continue; + } + + split_offset = offset - node_data + text_length; + replacement_node = + webkit_dom_node_clone_node (replacement, TRUE); + + data_length = webkit_dom_character_data_get_length ( + (WebKitDOMCharacterData *) node); + if (split_offset < data_length) { + + WebKitDOMNode *parent_node; + + node = WEBKIT_DOM_NODE ( + webkit_dom_text_split_text ( + (WebKitDOMText *) node, + offset - node_data + text_length, + NULL)); + parent_node = webkit_dom_node_get_parent_node (node); + webkit_dom_node_insert_before ( + parent_node, replacement_node, + node, NULL); + + } else { + WebKitDOMNode *parent_node; + + parent_node = webkit_dom_node_get_parent_node (node); + webkit_dom_node_append_child ( + parent_node, + replacement_node, NULL); + } + + webkit_dom_character_data_delete_data ( + (WebKitDOMCharacterData *) (current_node), + offset - node_data, text_length, NULL); + } - if (event != NULL && event->button != 3) - return FALSE; + } else { - /* Only extract a URI if no selection is active. Selected text - * implies the user is more likely to want to copy the selection - * to the clipboard than open a link within the selection. */ - if (!e_web_view_is_selection_active (web_view)) - uri = e_web_view_extract_uri (web_view, event, frame); + WebKitDOMNode *child, *next_child; - if (uri != NULL && g_str_has_prefix (uri, "##")) { - g_free (uri); - return FALSE; - } + /* Iframe? Let's traverse inside! */ + if (WEBKIT_DOM_IS_HTML_IFRAME_ELEMENT (node)) { - g_signal_emit ( - web_view, signals[POPUP_EVENT], 0, - event, uri, &event_handled); + WebKitDOMDocument *frame_document; - g_free (uri); + frame_document = + webkit_dom_html_iframe_element_get_content_document ( + WEBKIT_DOM_HTML_IFRAME_ELEMENT (node)); + replace_text (WEBKIT_DOM_NODE (frame_document), + text, replacement); + + } else { + + child = webkit_dom_node_get_first_child (node); + while (child) { + next_child = webkit_dom_node_get_next_sibling (child); + replace_text (child, text, replacement); + child = next_child; + } + } + } - return event_handled; } static void -web_view_menu_item_select_cb (EWebView *web_view, - GtkWidget *widget) +web_view_update_document_highlights (EWebView *web_view) { - GtkAction *action; - GtkActivatable *activatable; - const gchar *tooltip; + WebKitDOMDocument *document; + GSList *iter; - activatable = GTK_ACTIVATABLE (widget); - action = gtk_activatable_get_related_action (activatable); - tooltip = gtk_action_get_tooltip (action); + document = webkit_web_view_get_dom_document (WEBKIT_WEB_VIEW (web_view)); - if (tooltip == NULL) - return; + for (iter = web_view->priv->highlights; iter; iter = iter->next) { - e_web_view_status_message (web_view, tooltip); + WebKitDOMDocumentFragment *frag; + WebKitDOMElement *span; + + span = webkit_dom_document_create_element (document, "span", NULL); + webkit_dom_html_element_set_class_name ( + WEBKIT_DOM_HTML_ELEMENT (span), "__evo-highlight"); + webkit_dom_html_element_set_inner_text ( + WEBKIT_DOM_HTML_ELEMENT (span), iter->data, NULL); + + frag = webkit_dom_document_create_document_fragment (document); + webkit_dom_node_append_child ( + WEBKIT_DOM_NODE (frag), WEBKIT_DOM_NODE (span), NULL); + + replace_text (WEBKIT_DOM_NODE (document), + iter->data, WEBKIT_DOM_NODE (frag)); + } } static void @@ -560,6 +499,86 @@ web_view_connect_proxy_cb (EWebView *web_view, G_CALLBACK (web_view_menu_item_deselect_cb), web_view); } +static GtkWidget * +web_view_create_plugin_widget_cb (EWebView *web_view, + const gchar *mime_type, + const gchar *uri, + GHashTable *param) +{ + EWebViewClass *class; + + /* XXX WebKitWebView does not provide a class method for + * this signal, so we do so we can override the default + * behavior from subclasses for special URI types. */ + + class = E_WEB_VIEW_GET_CLASS (web_view); + g_return_val_if_fail (class->create_plugin_widget != NULL, NULL); + + return class->create_plugin_widget (web_view, mime_type, uri, param); +} + +static void +web_view_hovering_over_link_cb (EWebView *web_view, + const gchar *title, + const gchar *uri) +{ + EWebViewClass *class; + + /* XXX WebKitWebView does not provide a class method for + * this signal, so we do so we can override the default + * behavior from subclasses for special URI types. */ + + class = E_WEB_VIEW_GET_CLASS (web_view); + g_return_if_fail (class->hovering_over_link != NULL); + + class->hovering_over_link (web_view, title, uri); +} + +static gboolean +web_view_navigation_policy_decision_requested_cb (EWebView *web_view, + WebKitWebFrame *frame, + WebKitNetworkRequest *request, + WebKitWebNavigationAction *navigation_action, + WebKitWebPolicyDecision *policy_decision) +{ + EWebViewClass *class; + WebKitWebNavigationReason reason; + const gchar *uri; + + reason = webkit_web_navigation_action_get_reason (navigation_action); + if (reason != WEBKIT_WEB_NAVIGATION_REASON_LINK_CLICKED) + return FALSE; + + /* XXX WebKitWebView does not provide a class method for + * this signal, so we do so we can override the default + * behavior from subclasses for special URI types. */ + + class = E_WEB_VIEW_GET_CLASS (web_view); + g_return_val_if_fail (class->link_clicked != NULL, FALSE); + + webkit_web_policy_decision_ignore (policy_decision); + + uri = webkit_network_request_get_uri (request); + + class->link_clicked (web_view, uri); + + return TRUE; +} + +static void +web_view_load_status_changed_cb (WebKitWebView *web_view, + GParamSpec *pspec, + gpointer user_data) +{ + WebKitLoadStatus status; + + status = webkit_web_view_get_load_status (web_view); + if (status != WEBKIT_LOAD_FINISHED) + return; + + web_view_update_document_highlights (E_WEB_VIEW (web_view)); +} + static void web_view_set_property (GObject *object, guint property_id, @@ -567,12 +586,6 @@ web_view_set_property (GObject *object, GParamSpec *pspec) { switch (property_id) { - case PROP_ANIMATE: - e_web_view_set_animate ( - E_WEB_VIEW (object), - g_value_get_boolean (value)); - return; - case PROP_CARET_MODE: e_web_view_set_caret_mode ( E_WEB_VIEW (object), @@ -603,12 +616,6 @@ web_view_set_property (GObject *object, g_value_get_boolean (value)); return; - case PROP_EDITABLE: - e_web_view_set_editable ( - E_WEB_VIEW (object), - g_value_get_boolean (value)); - return; - case PROP_INLINE_SPELLING: e_web_view_set_inline_spelling ( E_WEB_VIEW (object), @@ -662,24 +669,12 @@ web_view_get_property (GObject *object, GParamSpec *pspec) { switch (property_id) { - case PROP_ANIMATE: - g_value_set_boolean ( - value, e_web_view_get_animate ( - E_WEB_VIEW (object))); - return; - 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: - g_value_set_boxed ( - value, e_web_view_get_copy_target_list ( - E_WEB_VIEW (object))); - return; - case PROP_CURSOR_IMAGE: g_value_set_object ( value, e_web_view_get_cursor_image ( @@ -704,12 +699,6 @@ web_view_get_property (GObject *object, E_WEB_VIEW (object))); return; - case PROP_EDITABLE: - g_value_set_boolean ( - value, e_web_view_get_editable ( - E_WEB_VIEW (object))); - return; - case PROP_INLINE_SPELLING: g_value_set_boolean ( value, e_web_view_get_inline_spelling ( @@ -734,12 +723,6 @@ web_view_get_property (GObject *object, E_WEB_VIEW (object))); return; - case PROP_PASTE_TARGET_LIST: - g_value_set_boxed ( - value, e_web_view_get_paste_target_list ( - E_WEB_VIEW (object))); - return; - case PROP_PRINT_PROXY: g_value_set_object ( value, e_web_view_get_print_proxy ( @@ -789,16 +772,6 @@ web_view_dispose (GObject *object) priv->save_as_proxy = NULL; } - if (priv->copy_target_list != NULL) { - gtk_target_list_unref (priv->copy_target_list); - priv->copy_target_list = NULL; - } - - if (priv->paste_target_list != NULL) { - gtk_target_list_unref (priv->paste_target_list); - priv->paste_target_list = NULL; - } - if (priv->cursor_image != NULL) { g_object_unref (priv->cursor_image); priv->cursor_image = NULL; @@ -809,6 +782,16 @@ web_view_dispose (GObject *object) priv->cursor_image_src = NULL; } + if (priv->js_callbacks != NULL) { + g_hash_table_destroy (priv->js_callbacks); + priv->js_callbacks = NULL; + } + + if (priv->highlights != NULL) { + g_slist_free_full (priv->highlights, g_free); + priv->highlights = NULL; + } + /* Chain up to parent's dispose() method. */ G_OBJECT_CLASS (parent_class)->dispose (object); } @@ -861,12 +844,103 @@ web_view_button_press_event (GtkWidget *widget, { GtkWidgetClass *widget_class; EWebView *web_view; + gboolean event_handled = FALSE; + gchar *uri; web_view = E_WEB_VIEW (widget); - if (web_view_button_press_event_cb (web_view, event, NULL)) + if (event) { + WebKitHitTestResult *test; + WebKitHitTestResultContext context; + + if (web_view->priv->cursor_image) { + g_object_unref (web_view->priv->cursor_image); + web_view->priv->cursor_image = NULL; + } + + if (web_view->priv->cursor_image_src) { + g_free (web_view->priv->cursor_image_src); + web_view->priv->cursor_image_src = NULL; + } + + test = webkit_web_view_get_hit_test_result (WEBKIT_WEB_VIEW (web_view), event); + + if (!test) + goto chainup; + + g_object_get (G_OBJECT (test), "context", &context, NULL); + if (context & WEBKIT_HIT_TEST_RESULT_CONTEXT_IMAGE) { + WebKitWebDataSource *data_source; + WebKitWebFrame *frame; + GList *subresources, *res; + + g_object_get (G_OBJECT (test), "image-uri", &uri, NULL); + + if (!uri) + goto chainup; + + if (web_view->priv->cursor_image_src) + g_free (web_view->priv->cursor_image_src); + web_view->priv->cursor_image_src = uri; + + /* Iterate through all resources of the loaded webpage and + try to find resource with URI matching cursor_image_src */ + frame = webkit_web_view_get_main_frame (WEBKIT_WEB_VIEW (web_view)); + data_source = webkit_web_frame_get_data_source (frame); + subresources = webkit_web_data_source_get_subresources (data_source); + for (res = subresources; res; res = res->next) { + WebKitWebResource *src = res->data; + GdkPixbufLoader *loader; + GString *data; + + if (g_strcmp0 (webkit_web_resource_get_uri (src), + web_view->priv->cursor_image_src) != 0) + continue; + + data = webkit_web_resource_get_data (src); + if (!data) + break; + + loader = gdk_pixbuf_loader_new (); + if (!gdk_pixbuf_loader_write (loader, + (guchar *) data->str, data->len, NULL)) { + g_object_unref (loader); + break; + } + gdk_pixbuf_loader_close (loader, NULL); + + if (web_view->priv->cursor_image) + g_object_unref (web_view->priv->cursor_image); + + web_view->priv->cursor_image = + g_object_ref (gdk_pixbuf_loader_get_animation (loader)); + + g_object_unref (loader); + break; + } + } + } + + if (event != NULL && event->button != 3) + goto chainup; + + uri = e_web_view_extract_uri (web_view, event); + + if (uri != NULL && g_str_has_prefix (uri, "##")) { + g_free (uri); + goto chainup; + } + + g_signal_emit ( + web_view, signals[POPUP_EVENT], 0, + event, uri, &event_handled); + + g_free (uri); + + if (event_handled) return TRUE; +chainup: /* Chain up to parent's button_press_event() method. */ widget_class = GTK_WIDGET_CLASS (parent_class); return widget_class->button_press_event (widget, event); @@ -879,10 +953,10 @@ web_view_scroll_event (GtkWidget *widget, if (event->state & GDK_CONTROL_MASK) { switch (event->direction) { case GDK_SCROLL_UP: - gtk_html_zoom_in (GTK_HTML (widget)); + e_web_view_zoom_in (E_WEB_VIEW (widget)); return TRUE; case GDK_SCROLL_DOWN: - gtk_html_zoom_out (GTK_HTML (widget)); + e_web_view_zoom_out (E_WEB_VIEW (widget)); return TRUE; default: break; @@ -892,74 +966,71 @@ web_view_scroll_event (GtkWidget *widget, return FALSE; } -static void -web_view_url_requested (GtkHTML *html, - const gchar *uri, - GtkHTMLStream *stream) +static GtkWidget * +web_view_create_plugin_widget (EWebView *web_view, + const gchar *mime_type, + const gchar *uri, + GHashTable *param) { - EWebViewRequest *request; + GtkWidget *widget = NULL; - request = web_view_request_new (E_WEB_VIEW (html), uri, stream); + if (g_strcmp0 (mime_type, "image/x-themed-icon") == 0) { + GtkIconTheme *icon_theme; + GdkPixbuf *pixbuf; + gpointer data; + glong size = 0; + GError *error = NULL; - g_file_read_async ( - request->file, G_PRIORITY_DEFAULT, - request->cancellable, (GAsyncReadyCallback) - web_view_request_read_cb, request); -} + icon_theme = gtk_icon_theme_get_default (); -static void -web_view_gtkhtml_link_clicked (GtkHTML *html, - const gchar *uri) -{ - EWebViewClass *class; - EWebView *web_view; + if (size == 0) { + data = g_hash_table_lookup (param, "width"); + if (data != NULL) + size = MAX (size, strtol (data, NULL, 10)); + } - web_view = E_WEB_VIEW (html); + if (size == 0) { + data = g_hash_table_lookup (param, "height"); + if (data != NULL) + size = MAX (size, strtol (data, NULL, 10)); + } - class = E_WEB_VIEW_GET_CLASS (web_view); - g_return_if_fail (class->link_clicked != NULL); + if (size == 0) + size = 32; /* arbitrary default */ + + pixbuf = gtk_icon_theme_load_icon ( + icon_theme, uri, size, 0, &error); + if (pixbuf != NULL) { + widget = gtk_image_new_from_pixbuf (pixbuf); + g_object_unref (pixbuf); + } else if (error != NULL) { + g_warning ("%s", error->message); + g_error_free (error); + } + } - class->link_clicked (web_view, uri); + return widget; } -static void -web_view_on_url (GtkHTML *html, - const gchar *uri) +static gchar * +web_view_extract_uri (EWebView *web_view, + GdkEventButton *event) { - EWebViewClass *class; - EWebView *web_view; - - web_view = E_WEB_VIEW (html); - - class = E_WEB_VIEW_GET_CLASS (web_view); - g_return_if_fail (class->hovering_over_link != NULL); + WebKitHitTestResult *result; + WebKitHitTestResultContext context; + gchar *uri = NULL; - /* XXX WebKit would supply a title here. */ - class->hovering_over_link (web_view, NULL, uri); -} + result = webkit_web_view_get_hit_test_result ( + WEBKIT_WEB_VIEW (web_view), event); -static void -web_view_iframe_created (GtkHTML *html, - GtkHTML *iframe) -{ - g_signal_connect_swapped ( - iframe, "button-press-event", - G_CALLBACK (web_view_button_press_event_cb), html); -} + g_object_get (result, "context", &context, "link-uri", &uri, NULL); -static gchar * -web_view_extract_uri (EWebView *web_view, - GdkEventButton *event, - GtkHTML *html) -{ - gchar *uri; + if (context & WEBKIT_HIT_TEST_RESULT_CONTEXT_LINK) + return uri; - if (event != NULL) - uri = gtk_html_get_url_at (html, event->x, event->y); - else - uri = gtk_html_get_cursor_url (html); + g_free (uri); - return uri; + return NULL; } static void @@ -1030,30 +1101,62 @@ static void web_view_load_string (EWebView *web_view, const gchar *string) { - if (string != NULL && *string != '\0') - gtk_html_load_from_string (GTK_HTML (web_view), string, -1); - else - e_web_view_clear (web_view); + if (string == NULL) + string = ""; + + webkit_web_view_load_string ( + WEBKIT_WEB_VIEW (web_view), + string, "text/html", "UTF-8", "file://"); } static void -web_view_copy_clipboard (EWebView *web_view) +web_view_load_uri (EWebView *web_view, + const gchar *uri) { - gtk_html_command (GTK_HTML (web_view), "copy"); + if (uri == NULL) + uri = "about:blank"; + + webkit_web_view_load_uri ( + WEBKIT_WEB_VIEW (web_view), uri); } static void -web_view_cut_clipboard (EWebView *web_view) +web_view_frame_load_string (EWebView *web_view, + const gchar *frame_name, + const gchar *string) { - if (e_web_view_get_editable (web_view)) - gtk_html_command (GTK_HTML (web_view), "cut"); + WebKitWebFrame *main_frame, *frame; + + if (string == NULL) + string = ""; + + main_frame = webkit_web_view_get_main_frame (WEBKIT_WEB_VIEW (web_view)); + if (main_frame) { + frame = webkit_web_frame_find_frame (main_frame, frame_name); + + if (frame) + webkit_web_frame_load_string ( + frame, string, "text/html", "UTF-8", "file://"); + } } static void -web_view_paste_clipboard (EWebView *web_view) +web_view_frame_load_uri (EWebView *web_view, + const gchar *frame_name, + const gchar *uri) { - if (e_web_view_get_editable (web_view)) - gtk_html_command (GTK_HTML (web_view), "paste"); + WebKitWebFrame *main_frame, *frame; + + if (uri == NULL) + uri = "about:blank"; + + main_frame = webkit_web_view_get_main_frame (WEBKIT_WEB_VIEW (web_view)); + if (main_frame) { + frame = webkit_web_frame_find_frame (main_frame, frame_name); + + if (frame) + webkit_web_frame_load_uri (frame, uri); + } } static gboolean @@ -1070,11 +1173,7 @@ web_view_popup_event (EWebView *web_view, static void web_view_stop_loading (EWebView *web_view) { - g_list_foreach ( - web_view->priv->requests, (GFunc) - web_view_request_cancel, NULL); - - gtk_html_stop (GTK_HTML (web_view)); + webkit_web_view_stop_loading (WEBKIT_WEB_VIEW (web_view)); } static void @@ -1267,48 +1366,27 @@ web_view_selectable_update_actions (ESelectable *selectable, GdkAtom *clipboard_targets, gint n_clipboard_targets) { - EWebView *web_view; + WebKitWebView *web_view; GtkAction *action; - /*GtkTargetList *target_list;*/ - gboolean can_paste = FALSE; - gboolean editable; - gboolean have_selection; gboolean sensitive; const gchar *tooltip; - /*gint ii;*/ - - web_view = E_WEB_VIEW (selectable); - editable = e_web_view_get_editable (web_view); - have_selection = e_web_view_is_selection_active (web_view); - /* XXX GtkHtml implements its own clipboard instead of using - * GDK_SELECTION_CLIPBOARD, so we don't get notifications - * when the clipboard contents change. The logic below - * is what we would do if GtkHtml worked properly. - * Instead, we need to keep the Paste action sensitive so - * its accelerator overrides GtkHtml's key binding. */ -#if 0 - target_list = e_selectable_get_paste_target_list (selectable); - for (ii = 0; ii < n_clipboard_targets && !can_paste; ii++) - can_paste = gtk_target_list_find ( - target_list, clipboard_targets[ii], NULL); -#endif - can_paste = TRUE; + web_view = WEBKIT_WEB_VIEW (selectable); action = e_focus_tracker_get_cut_clipboard_action (focus_tracker); - sensitive = editable && have_selection; + sensitive = webkit_web_view_can_cut_clipboard (web_view); tooltip = _("Cut the selection"); gtk_action_set_sensitive (action, sensitive); gtk_action_set_tooltip (action, tooltip); action = e_focus_tracker_get_copy_clipboard_action (focus_tracker); - sensitive = have_selection; + sensitive = webkit_web_view_can_copy_clipboard (web_view); tooltip = _("Copy the selection"); gtk_action_set_sensitive (action, sensitive); gtk_action_set_tooltip (action, tooltip); action = e_focus_tracker_get_paste_clipboard_action (focus_tracker); - sensitive = editable && can_paste; + sensitive = webkit_web_view_can_paste_clipboard (web_view); tooltip = _("Paste the clipboard"); gtk_action_set_sensitive (action, sensitive); gtk_action_set_tooltip (action, tooltip); @@ -1349,7 +1427,9 @@ e_web_view_class_init (EWebViewClass *class) { GObjectClass *object_class; GtkWidgetClass *widget_class; +#if 0 /* WEBKIT */ GtkHTMLClass *html_class; +#endif parent_class = g_type_class_peek_parent (class); g_type_class_add_private (class, sizeof (EWebViewPrivate)); @@ -1365,33 +1445,23 @@ e_web_view_class_init (EWebViewClass *class) widget_class->button_press_event = web_view_button_press_event; widget_class->scroll_event = web_view_scroll_event; +#if 0 /* WEBKIT */ html_class = GTK_HTML_CLASS (class); html_class->url_requested = web_view_url_requested; - html_class->link_clicked = web_view_gtkhtml_link_clicked; - html_class->on_url = web_view_on_url; - html_class->iframe_created = web_view_iframe_created; +#endif + class->create_plugin_widget = web_view_create_plugin_widget; class->extract_uri = web_view_extract_uri; class->hovering_over_link = web_view_hovering_over_link; class->link_clicked = web_view_link_clicked; class->load_string = web_view_load_string; - class->copy_clipboard = web_view_copy_clipboard; - class->cut_clipboard = web_view_cut_clipboard; - class->paste_clipboard = web_view_paste_clipboard; + class->load_uri = web_view_load_uri; + class->frame_load_string = web_view_frame_load_string; + class->frame_load_uri = web_view_frame_load_uri; class->popup_event = web_view_popup_event; class->stop_loading = web_view_stop_loading; class->update_actions = web_view_update_actions; - g_object_class_install_property ( - object_class, - PROP_ANIMATE, - g_param_spec_boolean ( - "animate", - "Animate Images", - NULL, - FALSE, - G_PARAM_READWRITE)); - g_object_class_install_property ( object_class, PROP_CARET_MODE, @@ -1402,12 +1472,6 @@ e_web_view_class_init (EWebViewClass *class) FALSE, G_PARAM_READWRITE)); - /* Inherited from ESelectableInterface */ - g_object_class_override_property ( - object_class, - PROP_COPY_TARGET_LIST, - "copy-target-list"); - g_object_class_install_property ( object_class, PROP_CURSOR_IMAGE, @@ -1450,16 +1514,6 @@ e_web_view_class_init (EWebViewClass *class) G_PARAM_READWRITE | G_PARAM_CONSTRUCT)); - g_object_class_install_property ( - object_class, - PROP_EDITABLE, - g_param_spec_boolean ( - "editable", - "Editable", - NULL, - FALSE, - G_PARAM_READWRITE)); - g_object_class_install_property ( object_class, PROP_INLINE_SPELLING, @@ -1500,12 +1554,6 @@ e_web_view_class_init (EWebViewClass *class) GTK_TYPE_ACTION, G_PARAM_READWRITE)); - /* Inherited from ESelectableInterface */ - g_object_class_override_property ( - object_class, - PROP_PASTE_TARGET_LIST, - "paste-target-list"); - g_object_class_install_property ( object_class, PROP_PRINT_PROXY, @@ -1536,33 +1584,6 @@ e_web_view_class_init (EWebViewClass *class) NULL, G_PARAM_READWRITE)); - signals[COPY_CLIPBOARD] = g_signal_new ( - "copy-clipboard", - G_TYPE_FROM_CLASS (class), - G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, - G_STRUCT_OFFSET (EWebViewClass, copy_clipboard), - NULL, NULL, - g_cclosure_marshal_VOID__VOID, - G_TYPE_NONE, 0); - - signals[CUT_CLIPBOARD] = g_signal_new ( - "cut-clipboard", - G_TYPE_FROM_CLASS (class), - G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, - G_STRUCT_OFFSET (EWebViewClass, cut_clipboard), - NULL, NULL, - g_cclosure_marshal_VOID__VOID, - G_TYPE_NONE, 0); - - signals[PASTE_CLIPBOARD] = g_signal_new ( - "paste-clipboard", - G_TYPE_FROM_CLASS (class), - G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, - G_STRUCT_OFFSET (EWebViewClass, paste_clipboard), - NULL, NULL, - g_cclosure_marshal_VOID__VOID, - G_TYPE_NONE, 0); - signals[POPUP_EVENT] = g_signal_new ( "popup-event", G_TYPE_FROM_CLASS (class), @@ -1634,26 +1655,46 @@ e_web_view_init (EWebView *web_view) { GtkUIManager *ui_manager; GtkActionGroup *action_group; - GtkTargetList *target_list; EPopupAction *popup_action; + WebKitWebSettings *web_settings; const gchar *domain = GETTEXT_PACKAGE; const gchar *id; GError *error = NULL; web_view->priv = E_WEB_VIEW_GET_PRIVATE (web_view); + web_view->priv->highlights = NULL; + + g_signal_connect ( + web_view, "create-plugin-widget", + G_CALLBACK (web_view_create_plugin_widget_cb), NULL); + + g_signal_connect ( + web_view, "hovering-over-link", + G_CALLBACK (web_view_hovering_over_link_cb), NULL); + + g_signal_connect ( + web_view, "navigation-policy-decision-requested", + G_CALLBACK (web_view_navigation_policy_decision_requested_cb), + NULL); + + g_signal_connect ( + web_view, "notify::load-status", + G_CALLBACK (web_view_load_status_changed_cb), NULL); + ui_manager = gtk_ui_manager_new (); web_view->priv->ui_manager = ui_manager; + web_view->priv->js_callbacks = g_hash_table_new_full (g_str_hash, g_str_equal, + (GDestroyNotify) g_free, NULL); + g_signal_connect_swapped ( ui_manager, "connect-proxy", G_CALLBACK (web_view_connect_proxy_cb), web_view); - target_list = gtk_target_list_new (NULL, 0); - web_view->priv->copy_target_list = target_list; - - target_list = gtk_target_list_new (NULL, 0); - web_view->priv->paste_target_list = target_list; + web_settings = e_web_view_get_default_settings (GTK_WIDGET (web_view)); + e_web_view_set_settings (web_view, web_settings); + g_object_unref (web_settings); action_group = gtk_action_group_new ("uri"); gtk_action_group_set_translation_domain (action_group, domain); @@ -1763,6 +1804,7 @@ e_web_view_init (EWebView *web_view) e_plugin_ui_enable_manager (ui_manager, id); e_extensible_load_extensions (E_EXTENSIBLE (web_view)); + } GtkWidget * @@ -1776,7 +1818,7 @@ e_web_view_clear (EWebView *web_view) { g_return_if_fail (E_IS_WEB_VIEW (web_view)); - gtk_html_load_empty (GTK_HTML (web_view)); + e_web_view_load_string (web_view, NULL); } void @@ -1793,54 +1835,316 @@ e_web_view_load_string (EWebView *web_view, class->load_string (web_view, string); } -gboolean -e_web_view_get_animate (EWebView *web_view) +void +e_web_view_load_uri (EWebView *web_view, + const gchar *uri) { - /* XXX This is just here to maintain symmetry - * with e_web_view_set_animate(). */ + EWebViewClass *class; - g_return_val_if_fail (E_IS_WEB_VIEW (web_view), FALSE); + g_return_if_fail (E_IS_WEB_VIEW (web_view)); + + class = E_WEB_VIEW_GET_CLASS (web_view); + g_return_if_fail (class->load_uri != NULL); - return gtk_html_get_animate (GTK_HTML (web_view)); + class->load_uri (web_view, uri); } void -e_web_view_set_animate (EWebView *web_view, - gboolean animate) +e_web_view_reload (EWebView *web_view) { - /* XXX GtkHTML does not utilize GObject properties as well - * as it could. This just wraps gtk_html_set_animate() - * so we can get a "notify::animate" signal. */ + g_return_if_fail (E_IS_WEB_VIEW (web_view)); + + webkit_web_view_reload (WEBKIT_WEB_VIEW (web_view)); +} + +const gchar * +e_web_view_get_uri (EWebView *web_view) +{ + g_return_val_if_fail (E_IS_WEB_VIEW (web_view), NULL); + + return webkit_web_view_get_uri (WEBKIT_WEB_VIEW (web_view)); +} + +void +e_web_view_frame_load_string (EWebView *web_view, + const gchar *frame_name, + const gchar *string) +{ + EWebViewClass *class; + + g_return_if_fail (E_IS_WEB_VIEW (web_view)); + g_return_if_fail (frame_name != NULL); + + class = E_WEB_VIEW_GET_CLASS (web_view); + g_return_if_fail (class->frame_load_string != NULL); + + class->frame_load_string (web_view, frame_name, string); +} + +void +e_web_view_frame_load_uri (EWebView *web_view, + const gchar *frame_name, + const gchar *uri) +{ + EWebViewClass *class; + + g_return_if_fail (E_IS_WEB_VIEW (web_view)); + g_return_if_fail (frame_name != NULL); + + class = E_WEB_VIEW_GET_CLASS (web_view); + g_return_if_fail (class->frame_load_uri != NULL); + + class->frame_load_uri (web_view, frame_name, uri); +} + +const gchar * +e_web_view_frame_get_uri (EWebView *web_view, + const gchar *frame_name) +{ + WebKitWebFrame *main_frame, *frame; + + g_return_val_if_fail (E_IS_WEB_VIEW (web_view), NULL); + g_return_val_if_fail (frame_name != NULL, NULL); + + main_frame = webkit_web_view_get_main_frame (WEBKIT_WEB_VIEW (web_view)); + if (main_frame) { + frame = webkit_web_frame_find_frame (main_frame, frame_name); + + if (frame) + return webkit_web_frame_get_uri (frame); + } + + return NULL; +} + +gchar * +e_web_view_get_html (EWebView *web_view) +{ + GValue html = {0}; + + g_return_val_if_fail (E_IS_WEB_VIEW (web_view), NULL); + + if (e_web_view_exec_script (web_view, "return document.documentElement.innerHTML;", &html) == G_TYPE_STRING) + return g_strdup (g_value_get_string (&html)); + else + return NULL; +} + +JSGlobalContextRef +e_web_view_get_global_context (EWebView *web_view) +{ + WebKitWebFrame *main_frame; + + g_return_val_if_fail (E_IS_WEB_VIEW (web_view), 0); + + main_frame = webkit_web_view_get_main_frame (WEBKIT_WEB_VIEW (web_view)); + return webkit_web_frame_get_global_context (main_frame); +} + +GType +e_web_view_exec_script (EWebView *web_view, + const gchar *script, + GValue *value) +{ + WebKitWebFrame *main_frame; + + g_return_val_if_fail (E_IS_WEB_VIEW (web_view), G_TYPE_INVALID); + g_return_val_if_fail (script != NULL, G_TYPE_INVALID); + + main_frame = webkit_web_view_get_main_frame (WEBKIT_WEB_VIEW (web_view)); + + return e_web_view_frame_exec_script (web_view, + webkit_web_frame_get_name (main_frame), + script, value); +} + +GType +e_web_view_frame_exec_script (EWebView *web_view, + const gchar *frame_name, + const gchar *script, + GValue *value) +{ + WebKitWebFrame *main_frame, *frame; + JSGlobalContextRef context; + JSValueRef js_value, error = NULL; + JSType js_type; + JSStringRef js_script; + JSStringRef js_str; + size_t str_len; + gchar *str; + + g_return_val_if_fail (E_IS_WEB_VIEW (web_view), G_TYPE_INVALID); + g_return_val_if_fail (script != NULL, G_TYPE_INVALID); + + main_frame = webkit_web_view_get_main_frame (WEBKIT_WEB_VIEW (web_view)); + frame = webkit_web_frame_find_frame (main_frame, frame_name); + + context = webkit_web_frame_get_global_context (frame); + + js_script = JSStringCreateWithUTF8CString (script); + js_value = JSEvaluateScript (context, js_script, NULL, NULL, 0, &error); + JSStringRelease (js_script); + + if (error) { + gchar *msg; + js_str = JSValueToStringCopy (context, error, NULL); + str_len = JSStringGetLength (js_str); + + msg = g_malloc (str_len + 1); + JSStringGetUTF8CString (js_str, msg, str_len + 1); + JSStringRelease (js_str); + + g_message ("JavaScript Execution Failed: %s", msg); + g_free (msg); + + return G_TYPE_INVALID; + } + + if (!value) + return G_TYPE_NONE; + + js_type = JSValueGetType (context, js_value); + switch (js_type) { + case kJSTypeBoolean: + g_value_init (value, G_TYPE_BOOLEAN); + g_value_set_boolean (value, JSValueToBoolean (context, js_value)); + break; + case kJSTypeNumber: + g_value_init (value, G_TYPE_DOUBLE); + g_value_set_double (value, JSValueToNumber (context, js_value, NULL)); + break; + case kJSTypeString: + js_str = JSValueToStringCopy (context, js_value, NULL); + str_len = JSStringGetLength (js_str); + str = g_malloc (str_len + 1); + JSStringGetUTF8CString (js_str, str, str_len + 1); + JSStringRelease (js_str); + g_value_init (value, G_TYPE_STRING); + g_value_set_string (value, str); + g_free (str); + break; + case kJSTypeObject: + g_value_init (value, G_TYPE_OBJECT); + g_value_set_object (value, JSValueToObject (context, js_value, NULL)); + break; + case kJSTypeNull: + g_value_init (value, G_TYPE_POINTER); + g_value_set_pointer (value, NULL); + break; + case kJSTypeUndefined: + break; + } + + return G_VALUE_TYPE (value); +} + +static JSValueRef +web_view_handle_js_callback (JSContextRef ctx, + JSObjectRef function, + JSObjectRef this_object, + size_t argument_count, + const JSValueRef arguments[], + JSValueRef *exception) +{ + gpointer web_view; + gpointer user_data; + gchar *fnc_name; + size_t fnc_name_len; + + EWebViewJSFunctionCallback callback; + + JSStringRef js_webview_prop = JSStringCreateWithUTF8CString ("webview"); + JSStringRef js_userdata_prop = JSStringCreateWithUTF8CString ("user-data"); + JSStringRef js_fncname_prop = JSStringCreateWithUTF8CString ("fnc-name"); + JSStringRef js_fncname_str; + + JSValueRef js_webview = JSObjectGetProperty (ctx, function, js_webview_prop, NULL); + JSValueRef js_userdata = JSObjectGetProperty (ctx, function, js_userdata_prop, NULL); + JSValueRef js_fncname = JSObjectGetProperty (ctx, function, js_fncname_prop, NULL); + + web_view = GINT_TO_POINTER ((gint) JSValueToNumber (ctx, js_webview, NULL)); + user_data = GINT_TO_POINTER ((gint) JSValueToNumber (ctx, js_userdata, NULL)); + js_fncname_str = JSValueToStringCopy (ctx, js_fncname, NULL); + fnc_name_len = JSStringGetLength (js_fncname_str); + + g_return_val_if_fail (E_IS_WEB_VIEW (web_view), 0); + + /* Convert fncname to gchar* and lookup the callback in hashtable */ + fnc_name = g_malloc (fnc_name_len + 1); + JSStringGetUTF8CString (js_fncname_str, fnc_name, fnc_name_len + 1); + callback = g_hash_table_lookup (E_WEB_VIEW (web_view)->priv->js_callbacks, fnc_name); + + g_return_val_if_fail (callback != NULL, 0); + + /* Call the callback function */ + callback (E_WEB_VIEW (web_view), argument_count, arguments, user_data); + + JSStringRelease (js_fncname_str); + JSStringRelease (js_fncname_prop); + JSStringRelease (js_webview_prop); + JSStringRelease (js_userdata_prop); + + g_free (fnc_name); + + return 0; +} + +void +e_web_view_install_js_callback (EWebView *web_view, + const gchar *fnc_name, + EWebViewJSFunctionCallback callback, + gpointer user_data) +{ + WebKitWebFrame *frame; + JSGlobalContextRef ctx; + JSObjectRef global_obj, js_func; + JSStringRef js_fnc_name, js_webview_prop, js_userdata_prop, js_fncname_prop; g_return_if_fail (E_IS_WEB_VIEW (web_view)); + g_return_if_fail (fnc_name != NULL); + g_return_if_fail (callback != NULL); + + frame = webkit_web_view_get_main_frame (WEBKIT_WEB_VIEW (web_view)); + ctx = webkit_web_frame_get_global_context (frame); + global_obj = JSContextGetGlobalObject (ctx); + js_fnc_name = JSStringCreateWithUTF8CString (fnc_name); + js_func = JSObjectMakeFunctionWithCallback (ctx, NULL, + (JSObjectCallAsFunctionCallback) web_view_handle_js_callback); + js_webview_prop = JSStringCreateWithUTF8CString ("webview"); + js_userdata_prop = JSStringCreateWithUTF8CString ("user-data"); + js_fncname_prop = JSStringCreateWithUTF8CString ("fnc-name"); - gtk_html_set_animate (GTK_HTML (web_view), animate); + /* Set some properties to the function */ + JSObjectSetProperty (ctx, js_func, js_webview_prop, JSValueMakeNumber (ctx, GPOINTER_TO_INT (web_view)), 0, NULL); + JSObjectSetProperty (ctx, js_func, js_userdata_prop, JSValueMakeNumber (ctx, GPOINTER_TO_INT (user_data)), 0, NULL); + JSObjectSetProperty (ctx, js_func, js_fncname_prop, JSValueMakeString (ctx, js_fnc_name), 0, NULL); - g_object_notify (G_OBJECT (web_view), "animate"); + /* Set the function as a property of global object */ + JSObjectSetProperty (ctx, global_obj, js_fnc_name, js_func, 0, NULL); + + JSStringRelease (js_fncname_prop); + JSStringRelease (js_userdata_prop); + JSStringRelease (js_webview_prop); + JSStringRelease (js_fnc_name); + + g_hash_table_insert (web_view->priv->js_callbacks, g_strdup (fnc_name), callback); } gboolean e_web_view_get_caret_mode (EWebView *web_view) { - /* XXX This is just here to maintain symmetry - * with e_web_view_set_caret_mode(). */ - g_return_val_if_fail (E_IS_WEB_VIEW (web_view), FALSE); - return gtk_html_get_caret_mode (GTK_HTML (web_view)); + return web_view->priv->caret_mode; } void e_web_view_set_caret_mode (EWebView *web_view, gboolean caret_mode) { - /* XXX GtkHTML does not utilize GObject properties as well - * as it could. This just wraps gtk_html_set_caret_mode() - * so we can get a "notify::caret-mode" signal. */ - g_return_if_fail (E_IS_WEB_VIEW (web_view)); - gtk_html_set_caret_mode (GTK_HTML (web_view), caret_mode); + web_view->priv->caret_mode = caret_mode; g_object_notify (G_OBJECT (web_view), "caret-mode"); } @@ -1850,7 +2154,8 @@ e_web_view_get_copy_target_list (EWebView *web_view) { g_return_val_if_fail (E_IS_WEB_VIEW (web_view), NULL); - return web_view->priv->copy_target_list; + return webkit_web_view_get_copy_target_list ( + WEBKIT_WEB_VIEW (web_view)); } gboolean @@ -1892,46 +2197,74 @@ e_web_view_set_disable_save_to_disk (EWebView *web_view, } gboolean -e_web_view_get_editable (EWebView *web_view) +e_web_view_get_enable_frame_flattening (EWebView * web_view) { - /* XXX This is just here to maintain symmetry - * with e_web_view_set_editable(). */ + WebKitWebSettings *settings; + gboolean flattening; + + /* Return TRUE with fail since it's default value we set in _init(). */ + g_return_val_if_fail (E_IS_WEB_VIEW (web_view), TRUE); + + settings = webkit_web_view_get_settings (WEBKIT_WEB_VIEW (web_view)); + g_return_val_if_fail (settings != NULL, TRUE); + + g_object_get (G_OBJECT (settings), "enable-frame-flattening", &flattening, NULL); + + return flattening; +} + +void +e_web_view_set_enable_frame_flattening (EWebView * web_view, + gboolean enable_frame_flattening) +{ + WebKitWebSettings *settings; + + g_return_if_fail (E_IS_WEB_VIEW (web_view)); + + settings = webkit_web_view_get_settings (WEBKIT_WEB_VIEW (web_view)); + g_return_if_fail (settings != NULL); + + g_object_set (G_OBJECT (settings), "enable-frame-flattening", + enable_frame_flattening, NULL); +} +gboolean +e_web_view_get_editable (EWebView *web_view) +{ g_return_val_if_fail (E_IS_WEB_VIEW (web_view), FALSE); - return gtk_html_get_editable (GTK_HTML (web_view)); + return webkit_web_view_get_editable (WEBKIT_WEB_VIEW (web_view)); } void e_web_view_set_editable (EWebView *web_view, gboolean editable) { - /* XXX GtkHTML does not utilize GObject properties as well - * as it could. This just wraps gtk_html_set_editable() - * so we can get a "notify::editable" signal. */ - g_return_if_fail (E_IS_WEB_VIEW (web_view)); - gtk_html_set_editable (GTK_HTML (web_view), editable); - - g_object_notify (G_OBJECT (web_view), "editable"); + webkit_web_view_set_editable (WEBKIT_WEB_VIEW (web_view), editable); } gboolean e_web_view_get_inline_spelling (EWebView *web_view) { +#if 0 /* WEBKIT - XXX No equivalent property? */ /* XXX This is just here to maintain symmetry * with e_web_view_set_inline_spelling(). */ g_return_val_if_fail (E_IS_WEB_VIEW (web_view), FALSE); return gtk_html_get_inline_spelling (GTK_HTML (web_view)); +#endif + + return FALSE; } void e_web_view_set_inline_spelling (EWebView *web_view, gboolean inline_spelling) { +#if 0 /* WEBKIT - XXX No equivalent property? */ /* XXX GtkHTML does not utilize GObject properties as well * as it could. This just wraps gtk_html_set_inline_spelling() * so we get a "notify::inline-spelling" signal. */ @@ -1941,23 +2274,29 @@ e_web_view_set_inline_spelling (EWebView *web_view, gtk_html_set_inline_spelling (GTK_HTML (web_view), inline_spelling); g_object_notify (G_OBJECT (web_view), "inline-spelling"); +#endif } gboolean e_web_view_get_magic_links (EWebView *web_view) { +#if 0 /* WEBKIT - XXX No equivalent property? */ /* XXX This is just here to maintain symmetry * with e_web_view_set_magic_links(). */ g_return_val_if_fail (E_IS_WEB_VIEW (web_view), FALSE); return gtk_html_get_magic_links (GTK_HTML (web_view)); +#endif + + return FALSE; } void e_web_view_set_magic_links (EWebView *web_view, gboolean magic_links) { +#if 0 /* WEBKIT - XXX No equivalent property? */ /* XXX GtkHTML does not utilize GObject properties as well * as it could. This just wraps gtk_html_set_magic_links() * so we can get a "notify::magic-links" signal. */ @@ -1967,23 +2306,29 @@ e_web_view_set_magic_links (EWebView *web_view, gtk_html_set_magic_links (GTK_HTML (web_view), magic_links); g_object_notify (G_OBJECT (web_view), "magic-links"); +#endif } gboolean e_web_view_get_magic_smileys (EWebView *web_view) { +#if 0 /* WEBKIT - No equivalent property? */ /* XXX This is just here to maintain symmetry * with e_web_view_set_magic_smileys(). */ g_return_val_if_fail (E_IS_WEB_VIEW (web_view), FALSE); return gtk_html_get_magic_smileys (GTK_HTML (web_view)); +#endif + + return FALSE; } void e_web_view_set_magic_smileys (EWebView *web_view, gboolean magic_smileys) { +#if 0 /* WEBKIT - No equivalent property? */ /* XXX GtkHTML does not utilize GObject properties as well * as it could. This just wraps gtk_html_set_magic_smileys() * so we can get a "notify::magic-smileys" signal. */ @@ -1993,6 +2338,7 @@ e_web_view_set_magic_smileys (EWebView *web_view, gtk_html_set_magic_smileys (GTK_HTML (web_view), magic_smileys); g_object_notify (G_OBJECT (web_view), "magic-smileys"); +#endif } const gchar * @@ -2092,7 +2438,8 @@ e_web_view_get_paste_target_list (EWebView *web_view) { g_return_val_if_fail (E_IS_WEB_VIEW (web_view), NULL); - return web_view->priv->paste_target_list; + return webkit_web_view_get_paste_target_list ( + WEBKIT_WEB_VIEW (web_view)); } GtkAction * @@ -2149,6 +2496,40 @@ e_web_view_set_save_as_proxy (EWebView *web_view, g_object_notify (G_OBJECT (web_view), "save-as-proxy"); } +GSList * +e_web_view_get_highlights (EWebView *web_view) +{ + g_return_val_if_fail (E_IS_WEB_VIEW (web_view), NULL); + + return web_view->priv->highlights; +} + +void +e_web_view_add_highlight (EWebView *web_view, + const gchar *highlight) +{ + g_return_if_fail (E_IS_WEB_VIEW (web_view)); + g_return_if_fail (highlight && *highlight); + + web_view->priv->highlights = + g_slist_append (web_view->priv->highlights, g_strdup (highlight)); + + web_view_update_document_highlights (web_view); +} + +void e_web_view_clear_highlights (EWebView *web_view) +{ + g_return_if_fail (E_IS_WEB_VIEW (web_view)); + + if (!web_view->priv->highlights) + return; + + g_slist_free_full (web_view->priv->highlights, g_free); + web_view->priv->highlights = NULL; + + web_view_update_document_highlights (web_view); +} + GtkAction * e_web_view_get_action (EWebView *web_view, const gchar *action_name) @@ -2179,20 +2560,16 @@ e_web_view_get_action_group (EWebView *web_view, gchar * e_web_view_extract_uri (EWebView *web_view, - GdkEventButton *event, - GtkHTML *frame) + GdkEventButton *event) { EWebViewClass *class; g_return_val_if_fail (E_IS_WEB_VIEW (web_view), NULL); - if (frame == NULL) - frame = GTK_HTML (web_view); - class = E_WEB_VIEW_GET_CLASS (web_view); g_return_val_if_fail (class->extract_uri != NULL, NULL); - return class->extract_uri (web_view, event, frame); + return class->extract_uri (web_view, event); } void @@ -2200,7 +2577,7 @@ e_web_view_copy_clipboard (EWebView *web_view) { g_return_if_fail (E_IS_WEB_VIEW (web_view)); - g_signal_emit (web_view, signals[COPY_CLIPBOARD], 0); + webkit_web_view_copy_clipboard (WEBKIT_WEB_VIEW (web_view)); } void @@ -2208,7 +2585,7 @@ e_web_view_cut_clipboard (EWebView *web_view) { g_return_if_fail (E_IS_WEB_VIEW (web_view)); - g_signal_emit (web_view, signals[CUT_CLIPBOARD], 0); + webkit_web_view_cut_clipboard (WEBKIT_WEB_VIEW (web_view)); } gboolean @@ -2216,7 +2593,7 @@ e_web_view_is_selection_active (EWebView *web_view) { g_return_val_if_fail (E_IS_WEB_VIEW (web_view), FALSE); - return gtk_html_command (GTK_HTML (web_view), "is-selection-active"); + return webkit_web_view_has_selection (WEBKIT_WEB_VIEW (web_view)); } void @@ -2224,7 +2601,7 @@ e_web_view_paste_clipboard (EWebView *web_view) { g_return_if_fail (E_IS_WEB_VIEW (web_view)); - g_signal_emit (web_view, signals[PASTE_CLIPBOARD], 0); + webkit_web_view_paste_clipboard (WEBKIT_WEB_VIEW (web_view)); } gboolean @@ -2232,7 +2609,10 @@ e_web_view_scroll_forward (EWebView *web_view) { g_return_val_if_fail (E_IS_WEB_VIEW (web_view), FALSE); - return gtk_html_command (GTK_HTML (web_view), "scroll-forward"); + webkit_web_view_move_cursor ( + WEBKIT_WEB_VIEW (web_view), GTK_MOVEMENT_PAGES, 1); + + return TRUE; /* XXX This means nothing. */ } gboolean @@ -2240,7 +2620,10 @@ e_web_view_scroll_backward (EWebView *web_view) { g_return_val_if_fail (E_IS_WEB_VIEW (web_view), FALSE); - return gtk_html_command (GTK_HTML (web_view), "scroll-backward"); + webkit_web_view_move_cursor ( + WEBKIT_WEB_VIEW (web_view), GTK_MOVEMENT_PAGES, -1); + + return TRUE; /* XXX This means nothing. */ } void @@ -2248,15 +2631,17 @@ e_web_view_select_all (EWebView *web_view) { g_return_if_fail (E_IS_WEB_VIEW (web_view)); - gtk_html_command (GTK_HTML (web_view), "select-all"); + webkit_web_view_select_all (WEBKIT_WEB_VIEW (web_view)); } void e_web_view_unselect_all (EWebView *web_view) { +#if 0 /* WEBKIT */ g_return_if_fail (E_IS_WEB_VIEW (web_view)); gtk_html_command (GTK_HTML (web_view), "unselect-all"); +#endif } void @@ -2264,7 +2649,7 @@ e_web_view_zoom_100 (EWebView *web_view) { g_return_if_fail (E_IS_WEB_VIEW (web_view)); - gtk_html_command (GTK_HTML (web_view), "zoom-reset"); + webkit_web_view_set_zoom_level (WEBKIT_WEB_VIEW (web_view), 1.0); } void @@ -2272,7 +2657,7 @@ e_web_view_zoom_in (EWebView *web_view) { g_return_if_fail (E_IS_WEB_VIEW (web_view)); - gtk_html_command (GTK_HTML (web_view), "zoom-in"); + webkit_web_view_zoom_in (WEBKIT_WEB_VIEW (web_view)); } void @@ -2280,7 +2665,7 @@ e_web_view_zoom_out (EWebView *web_view) { g_return_if_fail (E_IS_WEB_VIEW (web_view)); - gtk_html_command (GTK_HTML (web_view), "zoom-out"); + webkit_web_view_zoom_out (WEBKIT_WEB_VIEW (web_view)); } GtkUIManager * @@ -2354,3 +2739,84 @@ e_web_view_update_actions (EWebView *web_view) g_signal_emit (web_view, signals[UPDATE_ACTIONS], 0); } + +gchar * +e_web_view_get_selection_html (EWebView *web_view) +{ + WebKitDOMDocument *document; + WebKitDOMDOMWindow *window; + WebKitDOMDOMSelection *selection; + WebKitDOMRange *range; + WebKitDOMDocumentFragment *fragment; + WebKitDOMHTMLElement *element; + + g_return_val_if_fail (E_IS_WEB_VIEW (web_view), NULL); + + if (!webkit_web_view_has_selection (WEBKIT_WEB_VIEW (web_view))) + return NULL; + + document = webkit_web_view_get_dom_document (WEBKIT_WEB_VIEW (web_view)); + window = webkit_dom_document_get_default_view (document); + selection = webkit_dom_dom_window_get_selection (window); + if (!selection) + return NULL; + + range = webkit_dom_dom_selection_get_range_at (selection, 0, NULL); + if (!range) + return NULL; + + fragment = webkit_dom_range_clone_contents (range, NULL); + if (!fragment) + return NULL; + + element = WEBKIT_DOM_HTML_ELEMENT (webkit_dom_document_create_element (document, "div", NULL)); + webkit_dom_node_append_child (WEBKIT_DOM_NODE (element), + WEBKIT_DOM_NODE (fragment), NULL); + + return webkit_dom_html_element_get_inner_html (element); +} + +void +e_web_view_set_settings (EWebView *web_view, + WebKitWebSettings *settings) +{ + g_return_if_fail (E_IS_WEB_VIEW (web_view)); + + if (settings == webkit_web_view_get_settings (WEBKIT_WEB_VIEW (web_view))) + return; + + g_object_bind_property (settings, "enable-caret-browsing", web_view, "caret-mode", + G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE); + + webkit_web_view_set_settings (WEBKIT_WEB_VIEW (web_view), settings); +} + +WebKitWebSettings * +e_web_view_get_default_settings (GtkWidget *parent_widget) +{ + GtkStyleContext *context; + const PangoFontDescription *font; + WebKitWebSettings *settings; + + g_return_val_if_fail (GTK_IS_WIDGET (parent_widget), NULL); + + settings = webkit_web_settings_new (); + + /* Use same font-size as rest of Evolution */ + context = gtk_widget_get_style_context (parent_widget); + font = gtk_style_context_get_font (context, GTK_STATE_FLAG_NORMAL); + + g_object_set (G_OBJECT (settings), + "default-font-size", (pango_font_description_get_size (font) / PANGO_SCALE), + "default-monospace-font-size", (pango_font_description_get_size (font) / PANGO_SCALE), + "enable-frame-flattening", TRUE, + "enable-java-applet", FALSE, + "enable-html5-database", FALSE, + "enable-html5-local-storage", FALSE, + "enable-offline-web-application-cache", FALSE, + "enable-site-specific-quirks", TRUE, + "enable-scripts", FALSE, + NULL); + + return settings; +} -- cgit v1.2.3