"
"
| "
"
", tmp, "", NULL); g_free (tmp); } return inner_html; } } frames = webkit_dom_document_get_elements_by_tag_name ( document, "IFRAME"); length = webkit_dom_node_list_get_length (frames); for (ii = 0; ii < length; ii++) { WebKitDOMNode *node; gchar *text; node = webkit_dom_node_list_item (frames, ii); text = web_view_get_frame_selection_html ( WEBKIT_DOM_ELEMENT (node)); if (text != NULL) return text; } g_object_unref (frames); return NULL; } gchar * e_web_view_get_selection_html (EWebView *web_view) { WebKitDOMDocument *document; WebKitDOMNodeList *frames; gulong ii, length; g_return_val_if_fail (E_IS_WEB_VIEW (web_view), NULL); if (!webkit_web_view_has_selection (WEBKIT_WEB_VIEW (web_view))) return NULL; document = webkit_web_view_get_dom_document (WEBKIT_WEB_VIEW (web_view)); frames = webkit_dom_document_get_elements_by_tag_name (document, "IFRAME"); length = webkit_dom_node_list_get_length (frames); for (ii = 0; ii < length; ii++) { gchar *text; WebKitDOMNode *node; node = webkit_dom_node_list_item (frames, ii); text = web_view_get_frame_selection_html ( WEBKIT_DOM_ELEMENT (node)); if (text != NULL) return text; } g_object_unref (frames); return NULL; } void e_web_view_update_fonts (EWebView *web_view) { EWebViewClass *class; GString *stylesheet; gchar *base64; gchar *aa = NULL; WebKitWebSettings *settings; PangoFontDescription *min_size, *ms, *vw; const gchar *styles[] = { "normal", "oblique", "italic" }; const gchar *smoothing = NULL; GtkStyleContext *context; GdkColor *link = NULL; GdkColor *visited = NULL; g_return_if_fail (E_IS_WEB_VIEW (web_view)); ms = NULL; vw = NULL; class = E_WEB_VIEW_GET_CLASS (web_view); if (class->set_fonts != NULL) class->set_fonts (web_view, &ms, &vw); if (ms == NULL) { gchar *font; font = g_settings_get_string ( web_view->priv->font_settings, "monospace-font-name"); ms = pango_font_description_from_string ( (font != NULL) ? font : "monospace 10"); g_free (font); } if (vw == NULL) { gchar *font; font = g_settings_get_string ( web_view->priv->font_settings, "font-name"); vw = pango_font_description_from_string ( (font != NULL) ? font : "serif 10"); g_free (font); } if (pango_font_description_get_size (ms) < pango_font_description_get_size (vw)) { min_size = ms; } else { min_size = vw; } stylesheet = g_string_new (""); g_string_append_printf ( stylesheet, "body {\n" " font-family: '%s';\n" " font-size: %dpt;\n" " font-weight: %d;\n" " font-style: %s;\n", pango_font_description_get_family (vw), pango_font_description_get_size (vw) / PANGO_SCALE, pango_font_description_get_weight (vw), styles[pango_font_description_get_style (vw)]); if (web_view->priv->aliasing_settings != NULL) aa = g_settings_get_string ( web_view->priv->aliasing_settings, "antialiasing"); if (g_strcmp0 (aa, "none") == 0) smoothing = "none"; else if (g_strcmp0 (aa, "grayscale") == 0) smoothing = "antialiased"; else if (g_strcmp0 (aa, "rgba") == 0) smoothing = "subpixel-antialiased"; if (smoothing != NULL) g_string_append_printf ( stylesheet, " -webkit-font-smoothing: %s;\n", smoothing); g_free (aa); g_string_append (stylesheet, "}\n"); g_string_append_printf ( stylesheet, "pre,code,.pre {\n" " font-family: '%s';\n" " font-size: %dpt;\n" " font-weight: %d;\n" " font-style: %s;\n" "}", pango_font_description_get_family (ms), pango_font_description_get_size (ms) / PANGO_SCALE, pango_font_description_get_weight (ms), styles[pango_font_description_get_style (ms)]); context = gtk_widget_get_style_context (GTK_WIDGET (web_view)); gtk_style_context_get_style ( context, "link-color", &link, "visited-link-color", &visited, NULL); if (link == NULL) { link = g_slice_new0 (GdkColor); link->blue = G_MAXINT16; } if (visited == NULL) { visited = g_slice_new0 (GdkColor); visited->red = G_MAXINT16; } g_string_append_printf ( stylesheet, "a {\n" " color: #%06x;\n" "}\n" "a:visited {\n" " color: #%06x;\n" "}\n", e_color_to_value (link), e_color_to_value (visited)); gdk_color_free (link); gdk_color_free (visited); base64 = g_base64_encode ((guchar *) stylesheet->str, stylesheet->len); g_string_free (stylesheet, TRUE); stylesheet = g_string_new ("data:text/css;charset=utf-8;base64,"); g_string_append (stylesheet, base64); g_free (base64); settings = webkit_web_view_get_settings (WEBKIT_WEB_VIEW (web_view)); g_object_set ( G_OBJECT (settings), "default-font-size", pango_font_description_get_size (vw) / PANGO_SCALE, "default-font-family", pango_font_description_get_family (vw), "monospace-font-family", pango_font_description_get_family (ms), "default-monospace-font-size", pango_font_description_get_size (ms) / PANGO_SCALE, "minimum-font-size", pango_font_description_get_size (min_size) / PANGO_SCALE, "user-stylesheet-uri", stylesheet->str, NULL); g_string_free (stylesheet, TRUE); pango_font_description_free (ms); pango_font_description_free (vw); } /* 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 a #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 a #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; GFile *destination = NULL; GtkWidget *dialog; 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; dialog = gtk_file_chooser_dialog_new ( _("Save Image"), toplevel, GTK_FILE_CHOOSER_ACTION_SAVE, _("_Cancel"), GTK_RESPONSE_CANCEL, _("_Save"), GTK_RESPONSE_ACCEPT, NULL); gtk_dialog_set_default_response ( GTK_DIALOG (dialog), GTK_RESPONSE_ACCEPT); file_chooser = GTK_FILE_CHOOSER (dialog); 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); } if (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_ACCEPT) destination = gtk_file_chooser_get_file (file_chooser); gtk_widget_destroy (dialog); 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); } } /* Helper for e_web_view_request() */ static void web_view_request_send_cb (GObject *source_object, GAsyncResult *result, gpointer user_data) { GSimpleAsyncResult *simple; AsyncContext *async_context; GError *local_error = NULL; simple = G_SIMPLE_ASYNC_RESULT (user_data); async_context = g_simple_async_result_get_op_res_gpointer (simple); async_context->input_stream = soup_request_send_finish ( SOUP_REQUEST (source_object), result, &local_error); if (local_error != NULL) g_simple_async_result_take_error (simple, local_error); g_simple_async_result_complete (simple); } /** * 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 by way of a #SoupRequest to WebKit's * default #SoupSession, incorporating both e_web_view_redirect_uri() and the * custom request handlers installed via e_web_view_install_request_handler(). * * 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) { SoupSession *session; SoupRequest *request; gchar *real_uri; GSimpleAsyncResult *simple; AsyncContext *async_context; GError *local_error = NULL; g_return_if_fail (E_IS_WEB_VIEW (web_view)); g_return_if_fail (uri != NULL); session = webkit_get_default_session (); async_context = g_slice_new0 (AsyncContext); simple = g_simple_async_result_new ( G_OBJECT (web_view), callback, user_data, e_web_view_request); g_simple_async_result_set_check_cancellable (simple, cancellable); g_simple_async_result_set_op_res_gpointer ( simple, async_context, (GDestroyNotify) async_context_free); real_uri = e_web_view_redirect_uri (web_view, uri); request = soup_session_request (session, real_uri, &local_error); g_free (real_uri); /* Sanity check. */ g_return_if_fail ( ((request != NULL) && (local_error == NULL)) || ((request == NULL) && (local_error != NULL))); if (request != NULL) { soup_request_send_async ( request, cancellable, web_view_request_send_cb, g_object_ref (simple)); g_object_unref (request); } else { g_simple_async_result_take_error (simple, local_error); g_simple_async_result_complete_in_idle (simple); } g_object_unref (simple); } /** * 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) { GSimpleAsyncResult *simple; AsyncContext *async_context; g_return_val_if_fail ( g_simple_async_result_is_valid ( result, G_OBJECT (web_view), e_web_view_request), NULL); simple = G_SIMPLE_ASYNC_RESULT (result); async_context = g_simple_async_result_get_op_res_gpointer (simple); if (g_simple_async_result_propagate_error (simple, error)) return NULL; g_return_val_if_fail (async_context->input_stream != NULL, NULL); return g_object_ref (async_context->input_stream); } void e_web_view_install_request_handler (EWebView *web_view, GType handler_type) { SoupSession *session; session = webkit_get_default_session (); soup_session_add_feature_by_type (session, handler_type); } void e_web_view_create_and_add_css_style_sheet (WebKitDOMDocument *document, const gchar *style_sheet_id) { WebKitDOMElement *style_element; style_element = webkit_dom_document_get_element_by_id (document, style_sheet_id); if (!style_element) { /* Create new