fix CID 1596595: Resource leaks, and CID 1596594: (CHECKED_RETURN)
[claws.git] / src / plugins / litehtml_viewer / lh_widget.cpp
index 903c496763272b8fbc110cd2122241994c3607d2..19181804fc71bdb010b48f50e75090ec06f147e7 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Claws Mail -- A GTK+ based, lightweight, and fast e-mail client
+ * 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
@@ -41,11 +41,7 @@ extern "C" {
 const gchar *prefs_common_get_uri_cmd(void);
 }
 
-char master_css[] = {
-#include "css.inc"
-};
-
-static gboolean expose_event_cb(GtkWidget *widget, GdkEvent *event,
+static gboolean draw_cb(GtkWidget *widget, cairo_t *cr,
                gpointer user_data);
 static gboolean button_press_event(GtkWidget *widget, GdkEventButton *event,
                gpointer user_data);
@@ -60,6 +56,9 @@ lh_widget::lh_widget()
 {
        GtkWidget *item;
 
+       m_force_render = false;
+       m_blank = false;
+
        /* scrolled window */
        m_scrolled_window = gtk_scrolled_window_new(NULL, NULL);
        gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(m_scrolled_window),
@@ -75,8 +74,8 @@ lh_widget::lh_widget()
        /* drawing area */
        m_drawing_area = gtk_drawing_area_new();
        gtk_container_add(GTK_CONTAINER(m_viewport), m_drawing_area);
-       g_signal_connect(m_drawing_area, "expose-event",
-                       G_CALLBACK(expose_event_cb), this);
+       g_signal_connect(m_drawing_area, "draw",
+                       G_CALLBACK(draw_cb), this);
        g_signal_connect(m_drawing_area, "motion_notify_event",
                        G_CALLBACK(motion_notify_event), this);
        g_signal_connect(m_drawing_area, "button_press_event",
@@ -99,7 +98,6 @@ lh_widget::lh_widget()
 
        m_html = NULL;
        m_rendered_width = 0;
-       m_context.load_master_stylesheet(master_css);
 
        m_font_name = NULL;
        m_font_size = 0;
@@ -108,6 +106,8 @@ lh_widget::lh_widget()
 
        m_showing_url = FALSE;
 
+       m_cairo_context = NULL;
+
        gtk_widget_set_events(m_drawing_area,
                                GDK_BUTTON_RELEASE_MASK
                              | GDK_BUTTON_PRESS_MASK
@@ -129,21 +129,25 @@ GtkWidget *lh_widget::get_widget() const
        return m_scrolled_window;
 }
 
-void lh_widget::set_caption(const litehtml::tchar_t* caption)
+void lh_widget::set_caption(const char *caption)
 {
        debug_print("lh_widget set_caption\n");
        return;
 }
 
-void lh_widget::set_base_url(const litehtml::tchar_t* base_url)
+void lh_widget::set_base_url(const char *base_url)
 {
        debug_print("lh_widget set_base_url '%s'\n",
                        (base_url ? base_url : "(null)"));
-       m_base_url = base_url;
+       if (base_url)
+               m_base_url = base_url;
+       else
+               m_base_url.clear();
+
        return;
 }
 
-void lh_widget::on_anchor_click(const litehtml::tchar_t* url, const litehtml::element::ptr& el)
+void lh_widget::on_anchor_click(const char *url, const litehtml::element::ptr& el)
 {
        debug_print("lh_widget on_anchor_click. url -> %s\n", url);
 
@@ -151,10 +155,9 @@ void lh_widget::on_anchor_click(const litehtml::tchar_t* url, const litehtml::el
        return;
 }
 
-void lh_widget::import_css(litehtml::tstring& text, const litehtml::tstring& url, litehtml::tstring& baseurl)
+void lh_widget::import_css(litehtml::string& text, const litehtml::string& url, litehtml::string& baseurl)
 {
-       debug_print("lh_widget import_css\n");
-       baseurl = master_css;
+       debug_print("lh_widget import_css. url=\"%s\" baseurl=\"%s\"\n", url.c_str(), baseurl.c_str());
 }
 
 void lh_widget::get_client_rect(litehtml::position& client) const
@@ -181,7 +184,7 @@ void lh_widget::open_html(const gchar *contents)
        update_font();
 
        lh_widget_statusbar_push("Loading HTML part ...");
-       m_html = litehtml::document::createFromString(contents, this, &m_context);
+       m_html = litehtml::document::createFromString(contents, this);
        m_rendered_width = 0;
        if (m_html != NULL) {
                debug_print("lh_widget::open_html created document\n");
@@ -191,11 +194,17 @@ 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(false);
+               m_blank = false;
        }
        lh_widget_statusbar_pop();
 }
 
+void lh_widget::rerender()
+{
+       m_force_render = true;
+       gtk_widget_queue_draw(m_drawing_area);
+}
+
 void lh_widget::draw(cairo_t *cr)
 {
        double x1, x2, y1, y2;
@@ -218,25 +227,26 @@ void lh_widget::draw(cairo_t *cr)
        m_html->draw((litehtml::uint_ptr)cr, 0, 0, &pos);
 }
 
-void lh_widget::redraw(gboolean force_render)
+void lh_widget::redraw()
 {
        GtkAllocation rect;
        gint width;
        GdkWindow *gdkwin;
        cairo_t *cr;
-
-       paint_white();
+       cairo_region_t *creg;
+       GdkDrawingContext *gdkctx;
+       gboolean destroy = FALSE;
 
        if (m_html == NULL)
                return;
 
        /* Get width of the viewport. */
-       gdkwin = gtk_viewport_get_view_window(GTK_VIEWPORT(m_viewport));
-       width = gdk_window_get_width(gdkwin);
-       m_height = gdk_window_get_height(gdkwin);
+       gtk_widget_get_allocation(GTK_WIDGET(m_viewport), &rect);
+       width = rect.width;
+       m_height = rect.height;
 
        /* If the available width has changed, rerender the HTML content. */
-       if (m_rendered_width != width || force_render) {
+       if (m_rendered_width != width || std::atomic_exchange(&m_force_render, false)) {
                debug_print("lh_widget::redraw: width changed: %d != %d\n",
                                m_rendered_width, width);
 
@@ -255,49 +265,48 @@ void lh_widget::redraw(gboolean force_render)
                                m_html->width(), m_html->height());
        }
 
-       /* Paint the rendered HTML. */
-       gdkwin = gtk_widget_get_window(m_drawing_area);
-       if (gdkwin == NULL) {
-               g_warning("lh_widget::redraw: No GdkWindow to draw on!");
-               return;
+       /* Use provided cairo context, if any. Otherwise create our own. */
+       if (m_cairo_context != NULL) {
+               cr = m_cairo_context;
+       } else {
+               gdkwin = gtk_widget_get_window(m_drawing_area);
+               if (gdkwin == NULL) {
+                       g_warning("lh_widget::redraw: No GdkWindow to draw on!");
+                       return;
+               }
+               creg = cairo_region_create_rectangle(&rect);
+               gdkctx = gdk_window_begin_draw_frame(gdkwin, creg);
+               cr = gdk_drawing_context_get_cairo_context(gdkctx);
+               destroy = TRUE;
        }
-       cr = gdk_cairo_create(GDK_DRAWABLE(gdkwin));
-       draw(cr);
-
-       cairo_destroy(cr);
-}
 
-void lh_widget::paint_white()
-{
-       GdkWindow *gdkwin = gtk_widget_get_window(m_drawing_area);
-       if (gdkwin == NULL) {
-               g_warning("lh_widget::clear: No GdkWindow to draw on!");
-               return;
+       if(!std::atomic_exchange(&m_blank, false)) {
+               draw(cr);
+       } else {
+               cairo_rectangle(cr, rect.x, rect.y, rect.width, rect.height);
+               cairo_set_source_rgb(cr, 255, 255, 255);
+               cairo_fill(cr);
        }
-       cairo_t *cr = gdk_cairo_create(GDK_DRAWABLE(gdkwin));
 
-       /* Paint white background. */
-       gint width, height;
-       gdk_drawable_get_size(gdkwin, &width, &height);
-       cairo_rectangle(cr, 0, 0, width, height);
-       cairo_set_source_rgb(cr, 255, 255, 255);
-       cairo_fill(cr);
-
-       cairo_destroy(cr);
+       /* Only destroy the used cairo context if we created it earlier. */
+       if (destroy) {
+               gdk_window_end_draw_frame(gdkwin, gdkctx);
+               cairo_region_destroy(creg);
+       }
 }
+
 void lh_widget::clear()
 {
        m_html = nullptr;
-       paint_white();
+       m_blank = true;
        m_rendered_width = 0;
        m_base_url.clear();
        m_clicked_url.clear();
 }
 
-void lh_widget::set_cursor(const litehtml::tchar_t* cursor)
+void lh_widget::set_cursor(const char *cursor)
 {
-       litehtml::element::ptr over_el = m_html->over_element();
-       gint x, y;
+       litehtml::element::const_ptr over_el = m_html->get_over_element();
 
        if (m_showing_url &&
                        (over_el == NULL || over_el != m_over_element)) {
@@ -311,10 +320,10 @@ void lh_widget::set_cursor(const litehtml::tchar_t* cursor)
        }
 }
 
-void lh_widget::update_cursor(const litehtml::tchar_t* cursor)
+void lh_widget::update_cursor(const char *cursor)
 {
        GdkCursorType cursType = GDK_ARROW;
-       const litehtml::tchar_t *href = get_href_at(m_over_element);
+       const char *href = get_href_at(m_over_element);
 
        /* If there is a href, and litehtml is okay with showing a pointer
         * cursor ("pointer" or "auto"), set it, otherwise keep the
@@ -327,7 +336,9 @@ void lh_widget::update_cursor(const litehtml::tchar_t* cursor)
        if (cursType == GDK_ARROW) {
                gdk_window_set_cursor(gtk_widget_get_window(m_drawing_area), NULL);
        } else {
-               gdk_window_set_cursor(gtk_widget_get_window(m_drawing_area), gdk_cursor_new(cursType));
+               gdk_window_set_cursor(gtk_widget_get_window(m_drawing_area),
+                               gdk_cursor_new_for_display(gtk_widget_get_display(m_drawing_area),
+                                       cursType));
        }
 
        /* If there is a href, show it in statusbar */
@@ -337,9 +348,9 @@ void lh_widget::update_cursor(const litehtml::tchar_t* cursor)
        }
 }
 
-const litehtml::tchar_t *lh_widget::get_href_at(litehtml::element::ptr element) const
+const char *lh_widget::get_href_at(litehtml::element::const_ptr element)
 {
-       litehtml::element::ptr el;
+       litehtml::element::const_ptr el;
 
        if (element == NULL)
                return NULL;
@@ -361,21 +372,7 @@ const litehtml::tchar_t *lh_widget::get_href_at(litehtml::element::ptr element)
 
        /* At this point, over_el is pointing at an anchor tag, so let's
         * grab its href attribute. */
-       return el->get_attr(_t("href"));
-}
-
-const litehtml::tchar_t *lh_widget::get_href_at(const gint x, const gint y) const
-{
-       litehtml::element::ptr over_el, el;
-
-       if (m_html == NULL)
-               return NULL;
-
-       over_el = m_html->root()->get_element_by_point(x, y, x, y);
-       if (over_el == NULL)
-               return NULL;
-
-       return get_href_at(over_el);
+       return el->get_attr("href");
 }
 
 void lh_widget::print()
@@ -384,7 +381,7 @@ void lh_widget::print()
     gtk_widget_realize(GTK_WIDGET(m_drawing_area));
 }
 
-void lh_widget::popup_context_menu(const litehtml::tchar_t *url,
+void lh_widget::popup_context_menu(const char *url,
                GdkEventButton *event)
 {
        cm_return_if_fail(url != NULL);
@@ -394,8 +391,7 @@ void lh_widget::popup_context_menu(const litehtml::tchar_t *url,
 
        m_clicked_url = url;
        gtk_widget_show_all(m_context_menu);
-       gtk_menu_popup(GTK_MENU(m_context_menu), NULL, NULL, NULL, NULL,
-                       event->button, event->time);
+       gtk_menu_popup_at_pointer(GTK_MENU(m_context_menu), (GdkEvent *)event);
 }
 
 void lh_widget::update_font()
@@ -416,12 +412,12 @@ void lh_widget::update_font()
        debug_print("Font set to '%s', size %d\n", m_font_name, m_font_size);
 }
 
-const litehtml::tstring lh_widget::fullurl(const litehtml::tchar_t *url) const
+const litehtml::string lh_widget::fullurl(const char *url) const
 {
        if (*url == '#' && !m_base_url.empty())
                return m_base_url + url;
 
-       return _t(url);
+       return url;
 }
 
 void lh_widget::set_partinfo(MimeInfo *partinfo)
@@ -429,7 +425,7 @@ void lh_widget::set_partinfo(MimeInfo *partinfo)
        m_partinfo = partinfo;
 }
 
-GdkPixbuf *lh_widget::get_local_image(const litehtml::tstring url) const
+GdkPixbuf *lh_widget::get_local_image(const litehtml::string url) const
 {
        GdkPixbuf *pixbuf;
        const gchar *name;
@@ -455,7 +451,7 @@ GdkPixbuf *lh_widget::get_local_image(const litehtml::tstring url) const
 
                        pixbuf = procmime_get_part_as_pixbuf(p, &error);
                        if (error != NULL) {
-                               g_warning("Couldn't load image: %s\n", error->message);
+                               g_warning("couldn't load image: %s", error->message);
                                g_error_free(error);
                                return NULL;
                        }
@@ -468,12 +464,20 @@ GdkPixbuf *lh_widget::get_local_image(const litehtml::tstring url) const
        return NULL;
 }
 
+void lh_widget::set_cairo_context(cairo_t *cr)
+{
+       m_cairo_context = cr;
+}
+
+
 ////////////////////////////////////////////////
-static gboolean expose_event_cb(GtkWidget *widget, GdkEvent *event,
+static gboolean draw_cb(GtkWidget *widget, cairo_t *cr,
                gpointer user_data)
 {
        lh_widget *w = (lh_widget *)user_data;
-       w->redraw(false);
+       w->set_cairo_context(cr);
+       w->redraw();
+       w->set_cairo_context(NULL);
        return FALSE;
 }
 
@@ -484,22 +488,22 @@ static gboolean button_press_event(GtkWidget *widget, GdkEventButton *event,
        lh_widget *w = (lh_widget *)user_data;
 
        if (w->m_html == NULL)
-               return false;
+               return FALSE;
 
        //debug_print("lh_widget on_button_press_event\n");
 
        if (event->type == GDK_2BUTTON_PRESS ||
                        event->type == GDK_3BUTTON_PRESS)
-               return true;
+               return TRUE;
 
        /* Right-click */
        if (event->button == 3) {
-               const litehtml::tchar_t *url = w->get_href_at((gint)event->x, (gint)event->y);
+               const char *url = w->get_href_at(w->m_html->get_over_element());
 
                if (url != NULL)
                        w->popup_context_menu(url, event);
 
-               return true;
+               return TRUE;
        }
 
        if(w->m_html->on_lbutton_down((int) event->x, (int) event->y,
@@ -510,7 +514,7 @@ static gboolean button_press_event(GtkWidget *widget, GdkEventButton *event,
                }
        }
        
-       return true;
+       return TRUE;
 }
 
 static gboolean motion_notify_event(GtkWidget *widget, GdkEventButton *event,
@@ -533,7 +537,7 @@ static gboolean motion_notify_event(GtkWidget *widget, GdkEventButton *event,
         }
        }
        
-       return true;
+       return TRUE;
 }
 
 static gboolean button_release_event(GtkWidget *widget, GdkEventButton *event,
@@ -541,20 +545,19 @@ static gboolean button_release_event(GtkWidget *widget, GdkEventButton *event,
 {
     litehtml::position::vector redraw_boxes;
     lh_widget *w = (lh_widget *)user_data;
-    GError* error = NULL;
 
        if (w->m_html == NULL)
-               return false;
+               return FALSE;
 
        //debug_print("lh_widget on_button_release_event\n");
 
        if (event->type == GDK_2BUTTON_PRESS ||
                        event->type == GDK_3BUTTON_PRESS)
-               return true;
+               return TRUE;
 
        /* Right-click */
        if (event->button == 3)
-               return true;
+               return TRUE;
 
        w->m_clicked_url.clear();
 
@@ -573,7 +576,7 @@ static gboolean button_release_event(GtkWidget *widget, GdkEventButton *event,
             open_uri(w->m_clicked_url.c_str(), prefs_common_get_uri_cmd());
     }
 
-       return true;
+       return TRUE;
 }
 
 static void open_link_cb(GtkMenuItem *item, gpointer user_data)