Make Litehtml image loading non-blocking using threads
authorAndrej Kacian <ticho@claws-mail.org>
Tue, 5 Mar 2019 07:40:32 +0000 (08:40 +0100)
committerAndrej Kacian <ticho@claws-mail.org>
Tue, 5 Mar 2019 07:45:29 +0000 (08:45 +0100)
src/plugins/litehtml_viewer/Makefile.am
src/plugins/litehtml_viewer/container_linux.cpp
src/plugins/litehtml_viewer/container_linux.h
src/plugins/litehtml_viewer/container_linux_images.cpp [new file with mode: 0644]
src/plugins/litehtml_viewer/lh_widget.cpp
src/plugins/litehtml_viewer/lh_widget.h

index 81ce766..a1dcd91 100644 (file)
@@ -40,6 +40,7 @@ litehtml_viewer_la_CFLAGS = -std=c99
 
 litehtml_viewer_la_SOURCES = \
        container_linux.cpp \
+       container_linux_images.cpp \
        plugin.c \
        lh_prefs.c \
        lh_viewer.c \
index e64d413..923ce76 100644 (file)
@@ -106,65 +106,6 @@ void container_linux::draw_list_marker( litehtml::uint_ptr hdc, const litehtml::
        }
 }
 
-void container_linux::load_image( const litehtml::tchar_t* src, const litehtml::tchar_t* baseurl, bool redraw_on_ready )
-{
-       litehtml::tstring url;
-       make_url(src, baseurl, url);
-       bool found = false;
-
-       for (auto ii = m_images.cbegin(); ii != m_images.cend(); ++ii) {
-               const image *i = &(*ii);
-
-               if (!strcmp(i->first.c_str(), url.c_str())) {
-                       found = true;
-                       break;
-               }
-       }
-
-       if(!found)
-       {
-               try
-               {
-                       GdkPixbuf *img = get_image(url.c_str(), true);
-                       if(img)
-                       {
-                               m_images.push_back(std::make_pair(url, img));
-                       }
-               } catch(...)
-               {
-                       int iii=0;
-                       iii++;
-               }
-       }
-}
-
-void container_linux::get_image_size( const litehtml::tchar_t* src, const litehtml::tchar_t* baseurl, litehtml::size& sz )
-{
-       litehtml::tstring url;
-       make_url(src, baseurl, url);
-       bool found = false;
-       const image *img = NULL;
-
-       for (auto ii = m_images.cbegin(); ii != m_images.cend(); ++ii) {
-               const image *i = &(*ii);
-               if (i->first == url) {
-                       img = i;
-                       found = true;
-                       break;
-               }
-       }
-
-       if(img != NULL)
-       {
-               sz.width        = gdk_pixbuf_get_width(img->second);
-               sz.height       = gdk_pixbuf_get_height(img->second);
-       } else
-       {
-               sz.width        = 0;
-               sz.height       = 0;
-       }
-}
-
 void container_linux::draw_background( litehtml::uint_ptr hdc, const litehtml::background_paint& bg )
 {
        cairo_t* cr = (cairo_t*) hdc;
@@ -654,64 +595,6 @@ void container_linux::fill_ellipse( cairo_t* cr, int x, int y, int width, int he
        cairo_restore(cr);
 }
 
-void container_linux::clear_images()
-{
-       lock_images_cache();
-
-       for(auto i = m_images.begin(); i != m_images.end(); ++i) {
-               image *img = &(*i);
-
-               if (img->second) {
-                       g_object_unref(img->second);
-               }
-       }
-
-       m_images.clear();
-
-       unlock_images_cache();
-}
-
-gint container_linux::clear_images(gint desired_size)
-{
-       gint size = 0;
-       gint num = 0;
-
-       lock_images_cache();
-
-       /* First, tally up size of all the stored GdkPixbufs and
-        * deallocate those which make the total size be above
-        * the desired_size limit. We will remove their list
-        * elements later. */
-       for (auto i = m_images.rbegin(); i != m_images.rend(); ++i) {
-               image *img = &(*i);
-               gint cursize;
-
-               if (img->second == NULL)
-                       continue;
-
-               cursize = gdk_pixbuf_get_byte_length(img->second);
-
-               if (size + cursize > desired_size) {
-                       g_object_unref(img->second);
-                       img->second = NULL;
-                       num++;
-               } else {
-                       size += cursize;
-               }
-       }
-
-       /* Remove elements whose GdkPixbuf pointers point to NULL. */
-       m_images.remove_if([&](image _img) -> bool {
-                       if (_img.second == NULL)
-                               return true;
-                       return false;
-                       });
-
-       unlock_images_cache();
-
-       return num;
-}
-
 std::shared_ptr<litehtml::element>     container_linux::create_element(const litehtml::tchar_t *tag_name,
                                                                                                                                          const litehtml::string_map &attributes,
                                                                                                                                          const std::shared_ptr<litehtml::document> &doc)
index 0b5f006..627bda6 100644 (file)
@@ -87,7 +87,6 @@ public:
        virtual void                                            del_clip() override;
 
        virtual void                                            make_url( const litehtml::tchar_t* url, const litehtml::tchar_t* basepath, litehtml::tstring& out );
-       virtual GdkPixbuf       *get_image(const litehtml::tchar_t* url, bool redraw_on_ready) = 0;
 
        void                                                            clear_images();
 
@@ -95,6 +94,9 @@ public:
         * starting from oldest stored. */
        gint                                                            clear_images(gint desired_size);
 
+       void                                                            add_image_to_cache(const gchar *url, GdkPixbuf *image);
+       virtual void                            redraw(gboolean force_render) = 0;
+
 protected:
        virtual void                                            draw_ellipse(cairo_t* cr, int x, int y, int width, int height, const litehtml::web_color& color, int line_width);
        virtual void                                            fill_ellipse(cairo_t* cr, int x, int y, int width, int height, const litehtml::web_color& color);
diff --git a/src/plugins/litehtml_viewer/container_linux_images.cpp b/src/plugins/litehtml_viewer/container_linux_images.cpp
new file mode 100644 (file)
index 0000000..eeefd40
--- /dev/null
@@ -0,0 +1,238 @@
+/*
+ * Claws Mail -- A GTK+ based, lightweight, and fast e-mail client
+ * Copyright(C) 2019 the Claws Mail Team
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write tothe Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#include "claws-features.h"
+#endif
+
+#include "common/utils.h"
+
+#include "container_linux.h"
+#include "http.h"
+#include "lh_prefs.h"
+
+static GdkPixbuf *lh_get_image(const litehtml::tchar_t* url)
+{
+       GError *error = NULL;
+       GdkPixbuf *pixbuf = NULL;
+       http* http_loader = NULL;
+
+       if (!lh_prefs_get()->enable_remote_content) {
+               debug_print("blocking download of image from '%s'\n", url);
+               return NULL;
+       }
+
+       debug_print("allowing download of image from '%s'\n", url);
+
+       http_loader = new http();
+       GInputStream *image = http_loader->load_url(url, &error);
+
+       if (error || !image) {
+               if (error) {
+                       g_warning("lh_get_image: Could not create pixbuf %s",
+                                       error->message);
+                       g_clear_error(&error);
+               }
+               goto theend;
+       }
+
+       pixbuf = gdk_pixbuf_new_from_stream(image, NULL, &error);
+       if (error) {
+               g_warning("lh_get_image: Could not create pixbuf %s",
+                               error->message);
+               pixbuf = NULL;
+               g_clear_error(&error);
+       }
+
+theend:
+       if (http_loader) {
+               delete http_loader;
+       }
+
+       return pixbuf;
+}
+
+struct FetchCtx {
+       container_linux *container;
+       gchar *url;
+};
+
+static void get_image_threaded(GTask *task, gpointer source, gpointer task_data, GCancellable *cancellable)
+{
+       struct FetchCtx *ctx = (struct FetchCtx *)task_data;
+       GdkPixbuf *pixbuf = lh_get_image(ctx->url);
+
+       g_task_return_pointer(task, pixbuf, NULL);
+}
+
+static void get_image_callback(GObject *source, GAsyncResult *res, gpointer user_data)
+{
+       GdkPixbuf *pixbuf;
+       struct FetchCtx *ctx = (struct FetchCtx *)user_data;
+
+       pixbuf = GDK_PIXBUF(g_task_propagate_pointer(G_TASK(res), NULL));
+
+       if (pixbuf != NULL) {
+               ctx->container->add_image_to_cache(ctx->url, pixbuf);
+               ctx->container->redraw(true);
+       }
+
+       g_free(ctx->url);
+       g_free(ctx);
+}
+
+void container_linux::load_image( const litehtml::tchar_t* src, const litehtml::tchar_t* baseurl, bool redraw_on_ready )
+{
+       litehtml::tstring url;
+       make_url(src, baseurl, url);
+       bool found = false;
+
+       lock_images_cache();
+
+       for (auto ii = m_images.cbegin(); ii != m_images.cend(); ++ii) {
+               const image *i = &(*ii);
+
+               if (!strcmp(i->first.c_str(), url.c_str())) {
+                       found = true;
+                       break;
+               }
+       }
+
+       unlock_images_cache();
+
+       if(!found) {
+               struct FetchCtx *ctx = g_new(struct FetchCtx, 1);
+               ctx->url = g_strdup(url.c_str());
+               ctx->container = this;
+
+               GTask *task = g_task_new(this, NULL, get_image_callback, ctx);
+               g_task_set_task_data(task, ctx, NULL);
+               g_task_run_in_thread(task, get_image_threaded);
+       } else {
+               debug_print("found image in cache: '%s'\n", url.c_str());
+       }
+}
+
+void container_linux::get_image_size( const litehtml::tchar_t* src, const litehtml::tchar_t* baseurl, litehtml::size& sz )
+{
+       litehtml::tstring url;
+       make_url(src, baseurl, url);
+       bool found = false;
+       const image *img = NULL;
+
+       lock_images_cache();
+
+       for (auto ii = m_images.cbegin(); ii != m_images.cend(); ++ii) {
+               const image *i = &(*ii);
+               if (i->first == url) {
+                       img = i;
+                       found = true;
+                       break;
+               }
+       }
+
+       if(img != NULL)
+       {
+               sz.width        = gdk_pixbuf_get_width(img->second);
+               sz.height       = gdk_pixbuf_get_height(img->second);
+       } else
+       {
+               sz.width        = 0;
+               sz.height       = 0;
+       }
+
+       unlock_images_cache();
+}
+
+void container_linux::add_image_to_cache(const gchar *url, GdkPixbuf *image)
+{
+       g_return_if_fail(url != NULL);
+       g_return_if_fail(image != NULL);
+
+       debug_print("adding image to cache: '%s'\n", url);
+       lock_images_cache();
+       m_images.push_back(std::make_pair(url, image));
+       unlock_images_cache();
+}
+void container_linux::lock_images_cache(void)
+{
+       g_rec_mutex_lock(&m_images_lock);
+}
+
+void container_linux::unlock_images_cache(void)
+{
+       g_rec_mutex_unlock(&m_images_lock);
+}
+
+void container_linux::clear_images()
+{
+       lock_images_cache();
+
+       for(auto i = m_images.begin(); i != m_images.end(); ++i) {
+               image *img = &(*i);
+
+               if (img->second) {
+                       g_object_unref(img->second);
+               }
+       }
+
+       m_images.clear();
+
+       unlock_images_cache();
+}
+
+gint container_linux::clear_images(gint desired_size)
+{
+       gint size = 0;
+       gint num = 0;
+
+       lock_images_cache();
+
+       /* First, tally up size of all the stored GdkPixbufs and
+        * deallocate those which make the total size be above
+        * the desired_size limit. We will remove their list
+        * elements later. */
+       for (auto i = m_images.rbegin(); i != m_images.rend(); ++i) {
+               image *img = &(*i);
+               gint cursize;
+
+               if (img->second == NULL)
+                       continue;
+
+               cursize = gdk_pixbuf_get_byte_length(img->second);
+
+               if (size + cursize > desired_size) {
+                       g_object_unref(img->second);
+                       img->second = NULL;
+                       num++;
+               } else {
+                       size += cursize;
+               }
+       }
+
+       /* Remove elements whose GdkPixbuf pointers point to NULL. */
+       m_images.remove_if([&](image _img) -> bool {
+                       if (_img.second == NULL)
+                               return true;
+                       return false;
+                       });
+
+       unlock_images_cache();
+
+       return num;
+}
index eb3ec2e..759b46e 100644 (file)
@@ -36,7 +36,6 @@
 #include "lh_prefs.h"
 #include "lh_widget.h"
 #include "lh_widget_wrapped.h"
-#include "http.h"
 
 extern "C" {
 const gchar *prefs_common_get_uri_cmd(void);
@@ -167,53 +166,6 @@ void lh_widget::get_client_rect(litehtml::position& client) const
 //                     client.width, client.height);
 }
 
-GdkPixbuf *lh_widget::get_image(const litehtml::tchar_t* url, bool redraw_on_ready)
-{
-       GError *error = NULL;
-       GdkPixbuf *pixbuf = NULL;
-       http* http_loader = NULL;
-
-       if (!lh_prefs_get()->enable_remote_content) {
-               debug_print("blocking download of image from '%s'\n", url);
-               return NULL;
-       }
-
-       debug_print("Loading... %s\n", url);
-       gchar *msg = g_strdup_printf("Loading %s ...", url);
-        lh_widget_statusbar_push(msg);
-       g_free(msg);
-       
-       http_loader = new http();
-       GInputStream *image = http_loader->load_url(url, &error);
-    
-       if (error || !image) {
-           if (error) {
-               g_warning("lh_widget::get_image: Could not create pixbuf %s", error->message);
-               g_clear_error(&error);
-           }
-           goto statusbar_pop;
-       }
-
-       pixbuf = gdk_pixbuf_new_from_stream(image, NULL, &error);
-       if (error) {
-           g_warning("lh_widget::get_image: Could not create pixbuf %s", error->message);
-           pixbuf = NULL;
-           g_clear_error(&error);
-       }
-
-/*     if (redraw_on_ready) {
-               redraw();
-       }*/
-
-statusbar_pop:
-       lh_widget_statusbar_pop();
-       if (http_loader) {
-               delete http_loader;
-       }
-       
-       return pixbuf;
-}
-
 void lh_widget::open_html(const gchar *contents)
 {
        gint num = clear_images(lh_prefs_get()->image_cache_size * 1024 * 1000);
@@ -234,7 +186,7 @@ void lh_widget::open_html(const gchar *contents)
                adj = gtk_scrolled_window_get_vadjustment(
                                GTK_SCROLLED_WINDOW(m_scrolled_window));
                gtk_adjustment_set_value(adj, 0.0);
-               redraw();
+               redraw(false);
        }
        lh_widget_statusbar_pop();
 }
@@ -261,7 +213,7 @@ void lh_widget::draw(cairo_t *cr)
        m_html->draw((litehtml::uint_ptr)cr, 0, 0, &pos);
 }
 
-void lh_widget::redraw()
+void lh_widget::redraw(gboolean force_render)
 {
        GtkAllocation rect;
        gint width;
@@ -279,7 +231,7 @@ void lh_widget::redraw()
        m_height = gdk_window_get_height(gdkwin);
 
        /* If the available width has changed, rerender the HTML content. */
-       if (m_rendered_width != width) {
+       if (m_rendered_width != width || force_render) {
                debug_print("lh_widget::redraw: width changed: %d != %d\n",
                                m_rendered_width, width);
 
@@ -459,7 +411,7 @@ static gboolean expose_event_cb(GtkWidget *widget, GdkEvent *event,
                gpointer user_data)
 {
        lh_widget *w = (lh_widget *)user_data;
-       w->redraw();
+       w->redraw(false);
        return FALSE;
 }
 
index 5ca1d38..204d612 100644 (file)
@@ -44,7 +44,6 @@ class lh_widget : public container_linux
                void import_css(litehtml::tstring& text, const litehtml::tstring& url, litehtml::tstring& baseurl);
                void get_client_rect(litehtml::position& client) const;
                inline const litehtml::tchar_t *get_default_font_name() const { return m_font_name; };
-               GdkPixbuf *get_image(const litehtml::tchar_t* url, bool redraw_on_ready);
 
                inline int get_default_font_size() const { return m_font_size; };
                litehtml::uint_ptr create_font(const litehtml::tchar_t* faceName, int size, int weight, litehtml::font_style italic, unsigned int decoration, litehtml::font_metrics* fm);
@@ -53,7 +52,7 @@ class lh_widget : public container_linux
                void draw_text(litehtml::uint_ptr hdc, const litehtml::tchar_t* text, litehtml::uint_ptr hFont, litehtml::web_color color, const litehtml::position& pos);
 
                void draw(cairo_t *cr);
-               void redraw();
+               void redraw(gboolean force_render);
                void open_html(const gchar *contents);
                void clear();
                void update_cursor();