/pidgin/main: 247f6f06531f: Fix regression: saving WebView images

Tomasz Wasilczyk twasilczyk at pidgin.im
Sat Apr 5 19:59:23 EDT 2014


Changeset: 247f6f06531ffc0d44bcb343b92d7640de42fce9
Author:	 Tomasz Wasilczyk <twasilczyk at pidgin.im>
Date:	 2014-04-06 01:59 +0200
Branch:	 default
URL: https://hg.pidgin.im/pidgin/main/rev/247f6f06531f

Description:

Fix regression: saving WebView images

diffstat:

 pidgin/gtkwebview.c |  194 ++++++++++++++++++++++++++++++++++++++++++++++++++-
 1 files changed, 187 insertions(+), 7 deletions(-)

diffs (263 lines):

diff --git a/pidgin/gtkwebview.c b/pidgin/gtkwebview.c
--- a/pidgin/gtkwebview.c
+++ b/pidgin/gtkwebview.c
@@ -108,6 +108,9 @@ typedef struct _PidginWebViewPriv {
 	/* WebKit inspector */
 	WebKitWebView *inspector_view;
 	GtkWindow *inspector_win;
+
+	/* Resources cache */
+	GHashTable *loaded_images;
 } PidginWebViewPriv;
 
 /******************************************************************************
@@ -197,6 +200,48 @@ webview_resource_loading(WebKitWebView *
 }
 
 static void
+webview_resource_loaded(WebKitWebView *web_view, WebKitWebFrame *web_frame,
+	WebKitWebResource *web_resource, gpointer user_data)
+{
+	PidginWebViewPriv *priv = PIDGIN_WEBVIEW_GET_PRIVATE(web_view);
+	const gchar *uri;
+	GString *data;
+	PurpleStoredImage *image;
+
+	if (!purple_str_has_caseprefix(
+		webkit_web_resource_get_mime_type(web_resource), "image/"))
+	{
+		return;
+	}
+
+	uri = webkit_web_resource_get_uri(web_resource);
+	if (g_hash_table_lookup(priv->loaded_images, uri))
+		return;
+
+	data = webkit_web_resource_get_data(web_resource);
+	if (data->len == 0)
+		return;
+
+	/* TODO: we could avoid copying data, if uri is a
+	 * PURPLE_STORED_IMAGE_PROTOCOL or PURPLE_STOCK_IMAGE_PROTOCOL */
+	image = purple_imgstore_new(g_memdup(data->str, data->len),
+		data->len, NULL);
+	g_return_if_fail(image != NULL);
+
+	g_hash_table_insert(priv->loaded_images, g_strdup(uri), image);
+}
+
+static PurpleStoredImage *
+webview_resource_get_loaded(WebKitWebView *web_view, const gchar *uri)
+{
+	PidginWebViewPriv *priv = PIDGIN_WEBVIEW_GET_PRIVATE(web_view);
+
+	g_return_val_if_fail(priv != NULL, NULL);
+
+	return g_hash_table_lookup(priv->loaded_images, uri);
+}
+
+static void
 process_load_queue_element(PidginWebView *webview)
 {
 	PidginWebViewPriv *priv = PIDGIN_WEBVIEW_GET_PRIVATE(webview);
@@ -516,31 +561,107 @@ get_unicode_menu(WebKitWebView *webview)
 }
 
 static void
+webview_image_saved(GtkWidget *dialog, gint response, gpointer _unused)
+{
+	PurpleStoredImage *image;
+	gchar *filename;
+	GError *error = NULL;
+
+	if (response != GTK_RESPONSE_ACCEPT) {
+		gtk_widget_destroy(dialog);
+		return;
+	}
+
+	image = g_object_get_data(G_OBJECT(dialog), "pidgin-gtkwebview-image");
+	g_return_if_fail(image != NULL);
+
+	filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog));
+	g_return_if_fail(filename != NULL);
+	g_return_if_fail(filename[0] != '\0');
+
+	g_file_set_contents(filename, purple_imgstore_get_data(image),
+		purple_imgstore_get_size(image), &error);
+	if (error) {
+		purple_debug_error("gtkwebview", "Failed saving image: %s",
+			error->message);
+		/* TODO: we should display a notification here */
+	}
+
+	g_free(filename);
+	gtk_widget_destroy(dialog);
+}
+
+static void
+webview_image_save(GtkWidget *item, WebKitDOMHTMLImageElement *image_node)
+{
+	const gchar *src = webkit_dom_html_image_element_get_src(image_node);
+	WebKitWebView *webview;
+	PurpleStoredImage *image;
+	GtkFileChooserDialog *dialog;
+	gchar *filename;
+	GtkWidget *parent;
+
+	webview = g_object_get_data(G_OBJECT(image_node), "pidgin-gtkwebview");
+	g_return_if_fail(webview != NULL);
+
+	image = webview_resource_get_loaded(webview, src);
+	g_return_if_fail(image != NULL);
+
+	parent = gtk_widget_get_ancestor(item, GTK_TYPE_WINDOW);
+	dialog = GTK_FILE_CHOOSER_DIALOG(gtk_file_chooser_dialog_new(
+		_("Save Image"),
+		parent ? GTK_WINDOW(parent) : NULL,
+		GTK_FILE_CHOOSER_ACTION_SAVE,
+		GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
+		GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
+		NULL));
+	gtk_file_chooser_set_do_overwrite_confirmation(GTK_FILE_CHOOSER(dialog), TRUE);
+	gtk_dialog_set_default_response(GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT);
+
+	/* TODO: use image's file name, if there is one */
+	filename = g_strdup_printf(_("image.%s"),
+		purple_imgstore_get_extension(image));
+	gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(dialog), filename);
+	g_free(filename);
+
+	g_signal_connect(G_OBJECT(dialog), "response",
+		G_CALLBACK(webview_image_saved), NULL);
+
+	purple_imgstore_ref(image);
+	g_object_set_data_full(G_OBJECT(dialog), "pidgin-gtkwebview-image",
+		image, (GDestroyNotify)purple_imgstore_unref);
+
+	gtk_widget_show(GTK_WIDGET(dialog));
+}
+
+static void
 do_popup_menu(WebKitWebView *webview, int button, int time, int context,
               WebKitDOMNode *node, const char *uri)
 {
 	GtkWidget *menu;
 	GtkWidget *cut, *copy, *paste, *delete, *select;
+	gboolean show_clipboard = TRUE;
+	WebKitDOMHTMLImageElement *image_node = NULL;
 
 	menu = gtk_menu_new();
 	g_signal_connect(menu, "selection-done",
 	                 G_CALLBACK(gtk_widget_destroy), NULL);
 
-	if ((context & WEBKIT_HIT_TEST_RESULT_CONTEXT_LINK)
-	 && !(context & WEBKIT_HIT_TEST_RESULT_CONTEXT_SELECTION)) {
+	if (context & WEBKIT_HIT_TEST_RESULT_CONTEXT_LINK) {
 		PidginWebViewProtocol *proto = NULL;
 		GList *children;
+		WebKitDOMNode *link_node = node;
 
-		while (node && !WEBKIT_DOM_IS_HTML_ANCHOR_ELEMENT(node)) {
-			node = webkit_dom_node_get_parent_node(node);
+		while (link_node && !WEBKIT_DOM_IS_HTML_ANCHOR_ELEMENT(link_node)) {
+			link_node = webkit_dom_node_get_parent_node(node);
 		}
 
-		if (uri && node)
+		if (uri && link_node)
 			proto = webview_find_protocol(uri, FALSE);
 
 		if (proto && proto->context_menu) {
 			proto->context_menu(PIDGIN_WEBVIEW(webview),
-			                    WEBKIT_DOM_HTML_ANCHOR_ELEMENT(node), menu);
+			                    WEBKIT_DOM_HTML_ANCHOR_ELEMENT(link_node), menu);
 		}
 
 		children = gtk_container_get_children(GTK_CONTAINER(menu));
@@ -554,7 +675,58 @@ do_popup_menu(WebKitWebView *webview, in
 		}
 		gtk_widget_show_all(menu);
 
-	} else {
+		show_clipboard = FALSE;
+	}
+
+	if (context & WEBKIT_HIT_TEST_RESULT_CONTEXT_IMAGE) {
+		WebKitDOMNode *_image_node = node;
+
+		while (_image_node && !WEBKIT_DOM_IS_HTML_IMAGE_ELEMENT(_image_node)) {
+			_image_node = webkit_dom_node_get_parent_node(_image_node);
+		}
+		if (_image_node)
+			image_node = WEBKIT_DOM_HTML_IMAGE_ELEMENT(_image_node);
+		/* don't do it on our theme smileys */
+	}
+	if (image_node && webkit_dom_html_image_element_get_complete(image_node)) {
+		GtkWidget *menu_item;
+		int width, height;
+
+		width = webkit_dom_html_image_element_get_width(image_node);
+		height = webkit_dom_html_image_element_get_height(image_node);
+
+		/* XXX */
+		g_object_set_data(G_OBJECT(image_node), "pidgin-gtkwebview", webview);
+
+		menu_item = gtk_image_menu_item_new_with_mnemonic(
+			_("_Save Image..."));
+		gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(menu_item),
+			gtk_image_new_from_stock(GTK_STOCK_SAVE, GTK_ICON_SIZE_MENU));
+		g_signal_connect_object(G_OBJECT(menu_item), "activate",
+			G_CALLBACK(webview_image_save), image_node, 0);
+		gtk_widget_show(menu_item);
+		gtk_menu_shell_append(GTK_MENU_SHELL(menu), menu_item);
+
+#if 0
+		/* TODO */
+		if (width <= 96 && height <= 96) {
+			menu_item = gtk_image_menu_item_new_with_mnemonic(
+				_("_Add Custom Smiley..."));
+			gtk_image_menu_item_set_image(
+				GTK_IMAGE_MENU_ITEM(menu_item),
+				gtk_image_new_from_stock(GTK_STOCK_ADD,
+					GTK_ICON_SIZE_MENU));
+			g_signal_connect(G_OBJECT(item), "activate",
+				G_CALLBACK(), save);
+			gtk_widget_show(menu_item);
+			gtk_menu_shell_append(GTK_MENU_SHELL(menu), menu_item);
+		}
+#endif
+
+		show_clipboard = FALSE;
+	}
+
+	if (show_clipboard) {
 		/* Using connect_swapped means we don't need any wrapper functions */
 		cut = pidgin_new_item_from_stock(menu, _("Cu_t"), GTK_STOCK_CUT,
 		                                 NULL, NULL, 0, 0, NULL);
@@ -928,6 +1100,8 @@ pidgin_webview_finalize(GObject *webview
 	}
 	g_queue_free(priv->load_queue);
 
+	g_hash_table_destroy(priv->loaded_images);
+
 	G_OBJECT_CLASS(parent_class)->finalize(G_OBJECT(webview));
 }
 
@@ -1090,11 +1264,17 @@ pidgin_webview_init(PidginWebView *webvi
 	g_signal_connect(G_OBJECT(webview), "resource-request-starting",
 	                 G_CALLBACK(webview_resource_loading), NULL);
 
+	g_signal_connect(G_OBJECT(webview), "resource-load-finished",
+		G_CALLBACK(webview_resource_loaded), NULL);
+
 	inspector = webkit_web_view_get_inspector(WEBKIT_WEB_VIEW(webview));
 	g_signal_connect(G_OBJECT(inspector), "inspect-web-view",
 		G_CALLBACK(webview_inspector_create), NULL);
 	g_signal_connect(G_OBJECT(inspector), "show-window",
 		G_CALLBACK(webview_inspector_show), webview);
+
+	priv->loaded_images = g_hash_table_new_full(g_str_hash, g_str_equal,
+		g_free, (GDestroyNotify)purple_imgstore_unref);
 }
 
 GType



More information about the Commits mailing list