From a6e4d7b84b68acf2758e8f52acec95d6f8918e7a Mon Sep 17 00:00:00 2001 From: Christian Dywan Date: Sun, 19 Oct 2008 22:18:07 +0200 Subject: [PATCH] Load and cache favicons with libsoup instead of GIO The previous code relied on GIO implementing HTTP with libsoup, so this is effectively not a new dependency. And since many people don't have GVfs and even if they do, somehow GIO doesn't work as good as one should expect, direct use of libsoup is actually an improvement. Plus the new code caches icons on disk which we didn't do before. --- midori/midori-view.c | 199 +++++++++++++++++++++++++------------------ midori/wscript_build | 2 +- wscript | 9 ++ 3 files changed, 125 insertions(+), 85 deletions(-) diff --git a/midori/midori-view.c b/midori/midori-view.c index 680c266e..345049cc 100644 --- a/midori/midori-view.c +++ b/midori/midori-view.c @@ -24,12 +24,13 @@ #include #include -#if HAVE_GIO - #include -#endif #include #include +#if HAVE_LIBSOUP + #include +#endif + /* This is unstable API, so we need to declare it */ gchar* webkit_web_view_get_selected_text (WebKitWebView* web_view); @@ -64,6 +65,10 @@ struct _MidoriView GtkWidget* tab_title; GtkWidget* tab_close; KatzeItem* item; + + #if HAVE_LIBSOUP + SoupSession* session; + #endif }; struct _MidoriViewClass @@ -434,119 +439,136 @@ midori_view_notify_icon_cb (MidoriView* view, gtk_image_new_from_pixbuf (view->icon)); } -#if HAVE_GIO -void -loadable_icon_finish_cb (GdkPixbuf* icon, - GAsyncResult* res, - MidoriView* view) +#if HAVE_LIBSOUP +static void +midori_view_got_headers_cb (SoupMessage* msg, + MidoriView* view) +{ + const gchar* mime; + + switch (msg->status_code) + { + case 200: + mime = soup_message_headers_get (msg->response_headers, "content-type"); + if (!g_str_has_prefix (mime, "image/")) + soup_session_cancel_message (view->session, msg, 200); + break; + case 301: + break; + default: + soup_session_cancel_message (view->session, msg, 200); + } +} + +static gchar* +midori_view_get_cached_icon_file (gchar* uri) +{ + gchar* cache_dir; + gchar* checksum; + gchar* icon_file; + gchar* icon_path; + + cache_dir = g_build_filename (g_get_user_cache_dir (), + PACKAGE_NAME, "icons", NULL); + g_mkdir_with_parents (cache_dir, 0755); + #if GLIB_CHECK_VERSION(2, 16, 0) + checksum = g_compute_checksum_for_string (G_CHECKSUM_MD5, uri, -1); + #else + checksum = g_strdup_printf ("%u", g_str_hash (uri)); + #endif + icon_file = g_strdup_printf ("%s.ico", checksum); + g_free (checksum); + icon_path = g_build_filename (cache_dir, icon_file, NULL); + g_free (icon_file); + return icon_path; +} + +static void +midori_view_got_body_cb (SoupMessage* msg, + MidoriView* view) { GdkPixbuf* pixbuf; - GInputStream* stream; - GError* error; + SoupURI* soup_uri; + gchar* uri; + gchar* icon_file; + FILE* fp; GdkPixbuf* pixbuf_scaled; gint icon_width, icon_height; pixbuf = NULL; - stream = g_loadable_icon_load_finish (G_LOADABLE_ICON (icon), - res, NULL, NULL); - if (stream) + if (msg->response_body->length > 0) { - error = NULL; - pixbuf = gdk_pixbuf_new_from_stream (stream, NULL, &error); - if (error) - g_warning (_("Icon couldn't be loaded: %s\n"), error->message); - g_object_unref (stream); + soup_uri = soup_message_get_uri (msg); + uri = soup_uri_to_string (soup_uri, FALSE); + icon_file = midori_view_get_cached_icon_file (uri); + g_free (uri); + if ((fp = fopen (icon_file, "w"))) + { + fwrite (msg->response_body->data, + 1, msg->response_body->length, fp); + fclose (fp); + pixbuf = gdk_pixbuf_new_from_file (icon_file, NULL); + } + g_free (icon_file); } if (!pixbuf) pixbuf = gtk_widget_render_icon (GTK_WIDGET (view), GTK_STOCK_FILE, GTK_ICON_SIZE_MENU, NULL); - gtk_icon_size_lookup (GTK_ICON_SIZE_MENU, &icon_width, &icon_height); pixbuf_scaled = gdk_pixbuf_scale_simple (pixbuf, icon_width, icon_height, GDK_INTERP_BILINEAR); g_object_unref (pixbuf); + katze_object_assign (view->icon, pixbuf_scaled); g_object_notify (G_OBJECT (view), "icon"); } - -void -file_info_finish_cb (GFile* icon_file, - GAsyncResult* res, - MidoriView* view) -{ - GFileInfo* info; - const gchar* content_type; - GIcon* icon; - GFile* parent; - GFile* file; - GdkPixbuf* pixbuf; - gint icon_width, icon_height; - GdkPixbuf* pixbuf_scaled; - - info = g_file_query_info_finish (G_FILE (icon_file), res, NULL); - if (info) - { - content_type = g_file_info_get_content_type (info); - if (g_str_has_prefix (content_type, "image/")) - { - icon = g_file_icon_new (icon_file); - g_loadable_icon_load_async (G_LOADABLE_ICON (icon), - 0, NULL, (GAsyncReadyCallback)loadable_icon_finish_cb, view); - return; - } - } - - file = g_file_get_parent (icon_file); - parent = g_file_get_parent (file); - /* We need to check if file equals the parent due to a GIO bug */ - if (parent && !g_file_equal (file, parent)) - { - icon_file = g_file_get_child (parent, "favicon.ico"); - g_file_query_info_async (icon_file, - G_FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE, - G_FILE_QUERY_INFO_NONE, 0, NULL, - (GAsyncReadyCallback)file_info_finish_cb, view); - return; - } - - pixbuf = gtk_widget_render_icon (GTK_WIDGET (view), - GTK_STOCK_FILE, GTK_ICON_SIZE_MENU, NULL); - gtk_icon_size_lookup (GTK_ICON_SIZE_MENU, &icon_width, &icon_height); - pixbuf_scaled = gdk_pixbuf_scale_simple (pixbuf, icon_width, icon_height, - GDK_INTERP_BILINEAR); - g_object_unref (pixbuf); - - view->icon = pixbuf_scaled; - g_object_notify (G_OBJECT (view), "icon"); -} #endif static void _midori_web_view_load_icon (MidoriView* view) { - #if HAVE_GIO - GFile* file; - GFile* icon_file; + #if HAVE_LIBSOUP + guint i; + gchar* uri; + gchar* icon_file; + SoupMessage* msg; #endif GdkPixbuf* pixbuf; gint icon_width, icon_height; GdkPixbuf* pixbuf_scaled; - #if HAVE_GIO - if (view->uri) + pixbuf = NULL; + #if HAVE_LIBSOUP + if (view->uri && g_str_has_prefix (view->uri, "http://")) { - file = g_file_new_for_uri (view->uri); - icon_file = g_file_get_child (file, "favicon.ico"); - g_file_query_info_async (icon_file, - G_FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE, - G_FILE_QUERY_INFO_NONE, 0, NULL, - (GAsyncReadyCallback)file_info_finish_cb, view); - return; + i = 8; + while (view->uri[i] != '\0' && view->uri[i] != '/') + i++; + if (view->uri[i] == '/') + { + uri = g_strdup (view->uri); + uri[i] = '\0'; + uri = g_strdup_printf ("%s/favicon.ico", uri); + icon_file = midori_view_get_cached_icon_file (uri); + if (g_file_test (icon_file, G_FILE_TEST_EXISTS)) + pixbuf = gdk_pixbuf_new_from_file (icon_file, NULL); + else + { + msg = soup_message_new ("GET", uri); + g_signal_connect (msg, "got-headers", + G_CALLBACK (midori_view_got_headers_cb), view); + g_signal_connect (msg, "got-body", + G_CALLBACK (midori_view_got_body_cb), view); + soup_session_queue_message (view->session, msg, NULL, NULL); + } + g_free (uri); + } } #endif - pixbuf = gtk_widget_render_icon (GTK_WIDGET (view), - GTK_STOCK_FILE, GTK_ICON_SIZE_MENU, NULL); + if (!pixbuf) + pixbuf = gtk_widget_render_icon (GTK_WIDGET (view), + GTK_STOCK_FILE, GTK_ICON_SIZE_MENU, NULL); gtk_icon_size_lookup (GTK_ICON_SIZE_MENU, &icon_width, &icon_height); pixbuf_scaled = gdk_pixbuf_scale_simple (pixbuf, icon_width, icon_height, GDK_INTERP_BILINEAR); @@ -1056,6 +1078,11 @@ midori_view_init (MidoriView* view) view->download_manager = NULL; + #if HAVE_LIBSOUP + if (!g_thread_supported ()) g_thread_init (NULL); + view->session = soup_session_async_new (); + #endif + g_object_connect (view, "signal::notify::icon", midori_view_notify_icon_cb, NULL, @@ -1094,6 +1121,10 @@ midori_view_finalize (GObject* object) g_free (view->download_manager); + #if HAVE_LIBSOUP + g_object_unref (view->session); + #endif + /* web_frame = webkit_web_view_get_main_frame (WEBKIT_WEB_VIEW (view->web_view)); g_signal_handlers_disconnect_by_func (web_frame, diff --git a/midori/wscript_build b/midori/wscript_build index 951ebc97..a5ae6932 100644 --- a/midori/wscript_build +++ b/midori/wscript_build @@ -6,5 +6,5 @@ obj = bld.create_obj ('cc', 'program') obj.target = 'midori' obj.includes = '. ..' obj.find_sources_in_dirs ('.') -obj.uselib = 'UNIQUE GIO GTK GTKSOURCEVIEW SQLITE WEBKIT LIBXML' +obj.uselib = 'UNIQUE LIBSOUP GIO GTK GTKSOURCEVIEW SQLITE WEBKIT LIBXML' obj.uselib_local = 'katze' diff --git a/wscript b/wscript index eb735587..160fb253 100644 --- a/wscript +++ b/wscript @@ -95,6 +95,13 @@ def configure (conf): single_instance = 'no' conf.check_message_custom ('single instance', 'support', single_instance) + if not Params.g_options.disable_libsoup: + conf.check_pkg ('libsoup-2.4', destvar='LIBSOUP', mandatory=False) + libsoup = ['not available','yes'][conf.env['HAVE_LIBSOUP'] == 1] + else: + libsoup = 'no' + conf.check_message_custom ('libsoup', 'support', libsoup) + if not Params.g_options.disable_gio: conf.check_pkg ('gio-2.0', destvar='GIO', vnum='2.16.0', mandatory=False) gio = ['not available','yes'][conf.env['HAVE_GIO'] == 1] @@ -152,6 +159,8 @@ def set_options (opt): opt.add_option ('--disable-unique', action='store_true', default=False, help='Disables Unique support', dest='disable_unique') + opt.add_option ('--disable-libsoup', action='store_true', default=False, + help='Disables libsoup support', dest='disable_libsoup') opt.add_option ('--disable-gio', action='store_true', default=False, help='Disables GIO support', dest='disable_gio') opt.add_option ('--disable-sqlite', action='store_true', default=False,