]> spindle.queued.net Git - midori/commitdiff
Load and cache favicons with libsoup instead of GIO
authorChristian Dywan <christian@twotoasts.de>
Sun, 19 Oct 2008 20:18:07 +0000 (22:18 +0200)
committerChristian Dywan <christian@twotoasts.de>
Sun, 19 Oct 2008 20:18:07 +0000 (22:18 +0200)
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
midori/wscript_build
wscript

index 680c266e7cabc62f9ab694ee8173ff74fa4ca7c7..345049cc60b7381378bab3acf264d3d0038f322c 100644 (file)
 
 #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,89 +439,87 @@ 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)
 {
-    GdkPixbuf* pixbuf;
-    GInputStream* stream;
-    GError* error;
-    GdkPixbuf* pixbuf_scaled;
-    gint icon_width, icon_height;
+    const gchar* mime;
 
-    pixbuf = NULL;
-    stream = g_loadable_icon_load_finish (G_LOADABLE_ICON (icon),
-                                          res, NULL, NULL);
-    if (stream)
+    switch (msg->status_code)
     {
-        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);
+    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);
     }
-    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");
+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;
 }
 
-void
-file_info_finish_cb (GFile*        icon_file,
-                     GAsyncResult* res,
-                     MidoriView*   view)
+static void
+midori_view_got_body_cb (SoupMessage* msg,
+                         MidoriView*  view)
 {
-    GFileInfo* info;
-    const gchar* content_type;
-    GIcon* icon;
-    GFile* parent;
-    GFile* file;
     GdkPixbuf* pixbuf;
-    gint icon_width, icon_height;
+    SoupURI* soup_uri;
+    gchar* uri;
+    gchar* icon_file;
+    FILE* fp;
     GdkPixbuf* pixbuf_scaled;
+    gint icon_width, icon_height;
 
-    info = g_file_query_info_finish (G_FILE (icon_file), res, NULL);
-    if (info)
+    pixbuf = NULL;
+    if (msg->response_body->length > 0)
     {
-        content_type = g_file_info_get_content_type (info);
-        if (g_str_has_prefix (content_type, "image/"))
+        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")))
         {
-            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;
+            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);
     }
-
-    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);
+    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);
 
-    view->icon = pixbuf_scaled;
+    katze_object_assign (view->icon, pixbuf_scaled);
     g_object_notify (G_OBJECT (view), "icon");
 }
 #endif
@@ -524,29 +527,48 @@ file_info_finish_cb (GFile*        icon_file,
 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,
index 951ebc9768d4839b6c6904bc4960f8b6f0762845..a5ae693262c2982e4f21c06f2be39f97a4d689a0 100644 (file)
@@ -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 eb735587fea382768cea2187dbad13d64582b0b1..160fb253878019b8691db8d6c387454b48fa7b1e 100644 (file)
--- 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,