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:
parent
a08b760d4e
commit
a6e4d7b84b
3 changed files with 125 additions and 85 deletions
|
@ -24,12 +24,13 @@
|
|||
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#if HAVE_GIO
|
||||
#include <gio/gio.h>
|
||||
#endif
|
||||
#include <glib/gi18n.h>
|
||||
#include <webkit/webkit.h>
|
||||
|
||||
#if HAVE_LIBSOUP
|
||||
#include <libsoup/soup.h>
|
||||
#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,117 +439,134 @@ 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,
|
||||
#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
|
||||
|
||||
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);
|
||||
|
@ -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,
|
||||
|
|
|
@ -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'
|
||||
|
|
9
wscript
9
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,
|
||||
|
|
Loading…
Reference in a new issue