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 <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,117 +439,134 @@ 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)
|
||||||
|
{
|
||||||
|
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)
|
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
|
||||||
|
|
||||||
|
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);
|
||||||
|
@ -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,
|
||||||
|
|
|
@ -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'
|
||||||
|
|
9
wscript
9
wscript
|
@ -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,
|
||||||
|
|
Loading…
Reference in a new issue