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.
This commit is contained in:
Christian Dywan 2008-10-19 22:18:07 +02:00
parent a08b760d4e
commit a6e4d7b84b
3 changed files with 125 additions and 85 deletions

View file

@ -24,12 +24,13 @@
#include <string.h> #include <string.h>
#include <stdlib.h> #include <stdlib.h>
#if HAVE_GIO
#include <gio/gio.h>
#endif
#include <glib/gi18n.h> #include <glib/gi18n.h>
#include <webkit/webkit.h> #include <webkit/webkit.h>
#if HAVE_LIBSOUP
#include <libsoup/soup.h>
#endif
/* This is unstable API, so we need to declare it */ /* This is unstable API, so we need to declare it */
gchar* gchar*
webkit_web_view_get_selected_text (WebKitWebView* web_view); webkit_web_view_get_selected_text (WebKitWebView* web_view);
@ -64,6 +65,10 @@ struct _MidoriView
GtkWidget* tab_title; GtkWidget* tab_title;
GtkWidget* tab_close; GtkWidget* tab_close;
KatzeItem* item; KatzeItem* item;
#if HAVE_LIBSOUP
SoupSession* session;
#endif
}; };
struct _MidoriViewClass struct _MidoriViewClass
@ -434,119 +439,136 @@ midori_view_notify_icon_cb (MidoriView* view,
gtk_image_new_from_pixbuf (view->icon)); gtk_image_new_from_pixbuf (view->icon));
} }
#if HAVE_GIO #if HAVE_LIBSOUP
void static void
loadable_icon_finish_cb (GdkPixbuf* icon, midori_view_got_headers_cb (SoupMessage* msg,
GAsyncResult* res, MidoriView* view)
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; GdkPixbuf* pixbuf;
GInputStream* stream; SoupURI* soup_uri;
GError* error; gchar* uri;
gchar* icon_file;
FILE* fp;
GdkPixbuf* pixbuf_scaled; GdkPixbuf* pixbuf_scaled;
gint icon_width, icon_height; gint icon_width, icon_height;
pixbuf = NULL; pixbuf = NULL;
stream = g_loadable_icon_load_finish (G_LOADABLE_ICON (icon), if (msg->response_body->length > 0)
res, NULL, NULL);
if (stream)
{ {
error = NULL; soup_uri = soup_message_get_uri (msg);
pixbuf = gdk_pixbuf_new_from_stream (stream, NULL, &error); uri = soup_uri_to_string (soup_uri, FALSE);
if (error) icon_file = midori_view_get_cached_icon_file (uri);
g_warning (_("Icon couldn't be loaded: %s\n"), error->message); g_free (uri);
g_object_unref (stream); 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) if (!pixbuf)
pixbuf = gtk_widget_render_icon (GTK_WIDGET (view), pixbuf = gtk_widget_render_icon (GTK_WIDGET (view),
GTK_STOCK_FILE, GTK_ICON_SIZE_MENU, NULL); GTK_STOCK_FILE, GTK_ICON_SIZE_MENU, NULL);
gtk_icon_size_lookup (GTK_ICON_SIZE_MENU, &icon_width, &icon_height); gtk_icon_size_lookup (GTK_ICON_SIZE_MENU, &icon_width, &icon_height);
pixbuf_scaled = gdk_pixbuf_scale_simple (pixbuf, icon_width, icon_height, pixbuf_scaled = gdk_pixbuf_scale_simple (pixbuf, icon_width, icon_height,
GDK_INTERP_BILINEAR); GDK_INTERP_BILINEAR);
g_object_unref (pixbuf); g_object_unref (pixbuf);
katze_object_assign (view->icon, pixbuf_scaled); katze_object_assign (view->icon, pixbuf_scaled);
g_object_notify (G_OBJECT (view), "icon"); 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 #endif
static void static void
_midori_web_view_load_icon (MidoriView* view) _midori_web_view_load_icon (MidoriView* view)
{ {
#if HAVE_GIO #if HAVE_LIBSOUP
GFile* file; guint i;
GFile* icon_file; gchar* uri;
gchar* icon_file;
SoupMessage* msg;
#endif #endif
GdkPixbuf* pixbuf; GdkPixbuf* pixbuf;
gint icon_width, icon_height; gint icon_width, icon_height;
GdkPixbuf* pixbuf_scaled; GdkPixbuf* pixbuf_scaled;
#if HAVE_GIO pixbuf = NULL;
if (view->uri) #if HAVE_LIBSOUP
if (view->uri && g_str_has_prefix (view->uri, "http://"))
{ {
file = g_file_new_for_uri (view->uri); i = 8;
icon_file = g_file_get_child (file, "favicon.ico"); while (view->uri[i] != '\0' && view->uri[i] != '/')
g_file_query_info_async (icon_file, i++;
G_FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE, if (view->uri[i] == '/')
G_FILE_QUERY_INFO_NONE, 0, NULL, {
(GAsyncReadyCallback)file_info_finish_cb, view); uri = g_strdup (view->uri);
return; 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 #endif
pixbuf = gtk_widget_render_icon (GTK_WIDGET (view), if (!pixbuf)
GTK_STOCK_FILE, GTK_ICON_SIZE_MENU, NULL); 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); gtk_icon_size_lookup (GTK_ICON_SIZE_MENU, &icon_width, &icon_height);
pixbuf_scaled = gdk_pixbuf_scale_simple (pixbuf, icon_width, icon_height, pixbuf_scaled = gdk_pixbuf_scale_simple (pixbuf, icon_width, icon_height,
GDK_INTERP_BILINEAR); GDK_INTERP_BILINEAR);
@ -1056,6 +1078,11 @@ midori_view_init (MidoriView* view)
view->download_manager = NULL; 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, g_object_connect (view,
"signal::notify::icon", "signal::notify::icon",
midori_view_notify_icon_cb, NULL, midori_view_notify_icon_cb, NULL,
@ -1094,6 +1121,10 @@ midori_view_finalize (GObject* object)
g_free (view->download_manager); g_free (view->download_manager);
#if HAVE_LIBSOUP
g_object_unref (view->session);
#endif
/* web_frame = webkit_web_view_get_main_frame /* web_frame = webkit_web_view_get_main_frame
(WEBKIT_WEB_VIEW (view->web_view)); (WEBKIT_WEB_VIEW (view->web_view));
g_signal_handlers_disconnect_by_func (web_frame, g_signal_handlers_disconnect_by_func (web_frame,

View file

@ -6,5 +6,5 @@ obj = bld.create_obj ('cc', 'program')
obj.target = 'midori' obj.target = 'midori'
obj.includes = '. ..' obj.includes = '. ..'
obj.find_sources_in_dirs ('.') 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' obj.uselib_local = 'katze'

View file

@ -95,6 +95,13 @@ def configure (conf):
single_instance = 'no' single_instance = 'no'
conf.check_message_custom ('single instance', 'support', single_instance) 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: if not Params.g_options.disable_gio:
conf.check_pkg ('gio-2.0', destvar='GIO', vnum='2.16.0', mandatory=False) conf.check_pkg ('gio-2.0', destvar='GIO', vnum='2.16.0', mandatory=False)
gio = ['not available','yes'][conf.env['HAVE_GIO'] == 1] 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, opt.add_option ('--disable-unique', action='store_true', default=False,
help='Disables Unique support', dest='disable_unique') 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, opt.add_option ('--disable-gio', action='store_true', default=False,
help='Disables GIO support', dest='disable_gio') help='Disables GIO support', dest='disable_gio')
opt.add_option ('--disable-sqlite', action='store_true', default=False, opt.add_option ('--disable-sqlite', action='store_true', default=False,