[evolution/wip/webkit2] Convert secure button from widget to a native HTML code



commit 0b1a78ba9668f3ba0f4ab511c82cd89b1eedaa64
Author: Milan Crha <mcrha redhat com>
Date:   Wed May 18 20:00:10 2016 +0200

    Convert secure button from widget to a native HTML code
    
    Also provide generic RegisterElementClicked method and ElementClicked
    signal for the EWebView's web extension, which are used by
    e_web_view_register_element_clicked() function and its
    counter part e_web_view_unregister_element_clicked(), even the later
    is only for completeness, rather than being meant to be used.

 e-util/e-web-view.c                           |  231 ++++++++++++++++
 e-util/e-web-view.h                           |   16 ++
 em-format/Makefile.am                         |    2 +
 em-format/e-mail-formatter-secure-button.c    |  358 ++++---------------------
 em-format/e-mail-parser-application-smime.c   |    2 +-
 em-format/e-mail-parser-inlinepgp-encrypted.c |    2 +-
 em-format/e-mail-parser-inlinepgp-signed.c    |    2 +-
 em-format/e-mail-parser-multipart-encrypted.c |    2 +-
 em-format/e-mail-parser-multipart-signed.c    |    2 +-
 em-format/e-mail-parser-secure-button.c       |    5 +-
 em-format/e-mail-part-secure-button.c         |  321 ++++++++++++++++++++++
 em-format/e-mail-part-secure-button.h         |   61 +++++
 em-format/e-mail-part.c                       |   15 +
 em-format/e-mail-part.h                       |    4 +
 mail/e-mail-display.c                         |   14 +-
 po/POTFILES.in                                |    1 +
 web-extensions/e-web-extension.c              |  111 ++++++++-
 17 files changed, 837 insertions(+), 312 deletions(-)
---
diff --git a/e-util/e-web-view.c b/e-util/e-web-view.c
index 62e6361..d734403 100644
--- a/e-util/e-web-view.c
+++ b/e-util/e-web-view.c
@@ -54,6 +54,11 @@ typedef enum {
 
 typedef struct _AsyncContext AsyncContext;
 
+typedef struct _ElementClickedData {
+       EWebViewElementClickedFunc callback;
+       gpointer user_data;
+} ElementClickedData;
+
 struct _EWebViewPrivate {
        GtkUIManager *ui_manager;
        gchar *selected_uri;
@@ -94,6 +99,9 @@ struct _EWebViewPrivate {
        gboolean has_hover_link;
 
        GSList *content_requests; /* EContentRequest * */
+
+       GHashTable *element_clicked_cbs; /* gchar *element_class ~> GPtrArray {ElementClickedData} */
+       guint web_extension_element_clicked_signal_id;
 };
 
 struct _AsyncContext {
@@ -748,6 +756,9 @@ web_view_load_changed_cb (WebKitWebView *webkit_web_view,
                }
        }
 
+       if (load_event == WEBKIT_LOAD_STARTED)
+               g_hash_table_remove_all (web_view->priv->element_clicked_cbs);
+
        if (load_event != WEBKIT_LOAD_FINISHED)
                return;
 
@@ -999,6 +1010,15 @@ web_view_dispose (GObject *object)
                priv->failed_to_find_text_handler_id = 0;
        }
 
+       if (priv->web_extension && priv->web_extension_element_clicked_signal_id) {
+               g_dbus_connection_signal_unsubscribe (
+                       g_dbus_proxy_get_connection (priv->web_extension),
+                       priv->web_extension_element_clicked_signal_id);
+               priv->web_extension_element_clicked_signal_id = 0;
+       }
+
+       g_hash_table_remove_all (priv->element_clicked_cbs);
+
        g_slist_free_full (priv->content_requests, g_object_unref);
        priv->content_requests = NULL;
 
@@ -1032,6 +1052,8 @@ web_view_finalize (GObject *object)
                priv->old_settings = NULL;
        }
 
+       g_hash_table_destroy (priv->element_clicked_cbs);
+
        /* Chain up to parent's finalize() method. */
        G_OBJECT_CLASS (e_web_view_parent_class)->finalize (object);
 }
@@ -1456,6 +1478,81 @@ web_view_stop_loading (EWebView *web_view)
 }
 
 static void
+web_view_register_element_clicked_hfunc (gpointer key,
+                                        gpointer value,
+                                        gpointer user_data)
+{
+       const gchar *elem_class = key;
+       EWebView *web_view = user_data;
+
+       g_return_if_fail (elem_class && *elem_class);
+       g_return_if_fail (E_IS_WEB_VIEW (web_view));
+
+       if (!web_view->priv->web_extension)
+               return;
+
+       g_dbus_proxy_call (
+               web_view->priv->web_extension,
+               "RegisterElementClicked",
+               g_variant_new (
+                       "(ts)",
+                       webkit_web_view_get_page_id (WEBKIT_WEB_VIEW (web_view)),
+                       elem_class),
+               G_DBUS_CALL_FLAGS_NONE,
+               -1,
+               NULL,
+               NULL,
+               NULL);
+}
+
+static void
+web_view_element_clicked_signal_cb (GDBusConnection *connection,
+                                   const gchar *sender_name,
+                                   const gchar *object_path,
+                                   const gchar *interface_name,
+                                   const gchar *signal_name,
+                                   GVariant *parameters,
+                                   gpointer user_data)
+{
+       EWebView *web_view = user_data;
+       const gchar *elem_class = NULL, *elem_value = NULL;
+       GtkAllocation elem_position;
+       guint64 page_id = 0;
+       gint position_left = 0, position_top = 0, position_width = 0, position_height = 0;
+       GPtrArray *listeners;
+
+       if (g_strcmp0 (signal_name, "ElementClicked") != 0)
+               return;
+
+       g_return_if_fail (E_IS_WEB_VIEW (web_view));
+
+       if (!parameters)
+               return;
+
+       g_variant_get (parameters, "(t&s&siiii)", &page_id, &elem_class, &elem_value, &position_left, 
&position_top, &position_width, &position_height);
+
+       if (!elem_class || !*elem_class || page_id != webkit_web_view_get_page_id (WEBKIT_WEB_VIEW 
(web_view)))
+               return;
+
+       elem_position.x = position_left;
+       elem_position.y = position_top;
+       elem_position.width = position_width;
+       elem_position.height = position_height;
+
+       listeners = g_hash_table_lookup (web_view->priv->element_clicked_cbs, elem_class);
+       if (listeners) {
+               guint ii;
+
+               for (ii = 0; ii <listeners->len; ii++) {
+                       ElementClickedData *ecd = g_ptr_array_index (listeners, ii);
+
+                       if (ecd && ecd->callback)
+                               ecd->callback (web_view, elem_class, elem_value, &elem_position, 
ecd->user_data);
+               }
+       }
+}
+
+static void
 web_extension_proxy_created_cb (GDBusProxy *proxy,
                                 GAsyncResult *result,
                                 EWebView *web_view)
@@ -1466,6 +1563,21 @@ web_extension_proxy_created_cb (GDBusProxy *proxy,
        if (!web_view->priv->web_extension) {
                g_warning ("Error creating web extension proxy: %s\n", error->message);
                g_error_free (error);
+       } else {
+               web_view->priv->web_extension_element_clicked_signal_id =
+                       g_dbus_connection_signal_subscribe (
+                               g_dbus_proxy_get_connection (web_view->priv->web_extension),
+                               g_dbus_proxy_get_name (web_view->priv->web_extension),
+                               E_WEB_EXTENSION_INTERFACE,
+                               "ElementClicked",
+                               E_WEB_EXTENSION_OBJECT_PATH,
+                               NULL,
+                               G_DBUS_SIGNAL_FLAGS_NONE,
+                               web_view_element_clicked_signal_cb,
+                               web_view,
+                               NULL);
+
+               g_hash_table_foreach (web_view->priv->element_clicked_cbs, 
web_view_register_element_clicked_hfunc, web_view);
        }
 }
 
@@ -2307,6 +2419,8 @@ e_web_view_init (EWebView *web_view)
        id = "org.gnome.evolution.webview";
        e_plugin_ui_register_manager (ui_manager, id, web_view);
        e_plugin_ui_enable_manager (ui_manager, id);
+
+       web_view->priv->element_clicked_cbs = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, 
(GDestroyNotify) g_ptr_array_unref);
 }
 
 GtkWidget *
@@ -4150,3 +4264,120 @@ e_web_view_set_document_iframe_src (EWebView *web_view,
                NULL,
                e_web_view_set_document_iframe_src_done_cb, NULL);
 }
+
+/**
+ * EWebViewElementClickedFunc:
+ * @web_view: an #EWebView
+ * @element_class: an element class, as set on the element which had been clicked
+ * @element_value: a 'value' attribute content of the clicked element
+ * @element_position: a #GtkAllocation with the position of the clicked element
+ * @user_data: user data as provided in the e_web_view_register_element_clicked() call
+ *
+ * The callback is called whenever an element of class @element_class is clicked.
+ * The @element_value is a content of the 'value' attribute of the clicked element.
+ * The @element_location is the place of the element within the web page.
+ *
+ * See: e_web_view_register_element_clicked, e_web_view_unregister_element_clicked
+ *
+ * Since: 3.22
+ **/
+
+/**
+ * e_web_view_register_element_clicked:
+ * @web_view: an #EWebView
+ * @element_class: an element class on which to listen for clicking
+ * @callback: an #EWebViewElementClickedFunc to call, when the element is clicked
+ * @user_data: user data to pass to @callback
+ *
+ * Registers a @callback to be called when any element of the class @element_class
+ * is clicked. If the element contains a 'value' attribute, then it is passed to
+ * the @callback too. These callback are valid until a new content of the @web_view
+ * is loaded, after which all the registered callbacks are forgotten.
+ *
+ * Since: 3.22
+ **/
+void
+e_web_view_register_element_clicked (EWebView *web_view,
+                                    const gchar *element_class,
+                                    EWebViewElementClickedFunc callback,
+                                    gpointer user_data)
+{
+       ElementClickedData *ecd;
+       GPtrArray *cbs;
+       guint ii;
+
+       g_return_if_fail (E_IS_WEB_VIEW (web_view));
+       g_return_if_fail (element_class != NULL);
+       g_return_if_fail (callback != NULL);
+
+       cbs = g_hash_table_lookup (web_view->priv->element_clicked_cbs, element_class);
+       if (cbs) {
+               for (ii = 0; ii < cbs->len; ii++) {
+                       ecd = g_ptr_array_index (cbs, ii);
+
+                       if (ecd && ecd->callback == callback && ecd->user_data == user_data) {
+                               g_warning ("%s: Callback already registered", G_STRFUNC);
+                               return;
+                       }
+               }
+       }
+
+       ecd = g_new0 (ElementClickedData, 1);
+       ecd->callback = callback;
+       ecd->user_data = user_data;
+
+       if (!cbs) {
+               web_view_register_element_clicked_hfunc ((gpointer) element_class, NULL, web_view);
+
+               cbs = g_ptr_array_new_full (1, g_free);
+               g_ptr_array_add (cbs, ecd);
+
+               g_hash_table_insert (web_view->priv->element_clicked_cbs, g_strdup (element_class), cbs);
+       } else {
+               g_ptr_array_add (cbs, ecd);
+       }
+}
+
+/**
+ * e_web_view_unregister_element_clicked:
+ * @web_view: an #EWebView
+ * @element_class: an element class on which to listen for clicking
+ * @callback: an #EWebViewElementClickedFunc to call, when the element is clicked
+ * @user_data: user data to pass to @callback
+ *
+ * Unregisters the @callback for the @element_class with the given @user_data, which
+ * should be previously registered with e_web_view_register_element_clicked(). This
+ * unregister is usually not needed, because the @web_view unregisters all callbacks
+ * when a new content is loaded.
+ *
+ * Since: 3.22
+ **/
+void
+e_web_view_unregister_element_clicked (EWebView *web_view,
+                                      const gchar *element_class,
+                                      EWebViewElementClickedFunc callback,
+                                      gpointer user_data)
+{
+       ElementClickedData *ecd;
+       GPtrArray *cbs;
+       guint ii;
+
+       g_return_if_fail (E_IS_WEB_VIEW (web_view));
+       g_return_if_fail (element_class != NULL);
+       g_return_if_fail (callback != NULL);
+
+       cbs = g_hash_table_lookup (web_view->priv->element_clicked_cbs, element_class);
+       if (!cbs)
+               return;
+
+       for (ii = 0; ii < cbs->len; ii++) {
+               ecd = g_ptr_array_index (cbs, ii);
+
+               if (ecd && ecd->callback == callback && ecd->user_data == user_data) {
+                       g_ptr_array_remove (cbs, ecd);
+                       if (!cbs->len)
+                               g_hash_table_remove (web_view->priv->element_clicked_cbs, element_class);
+                       break;
+               }
+       }
+}
diff --git a/e-util/e-web-view.h b/e-util/e-web-view.h
index 57714a0..73e3bcc 100644
--- a/e-util/e-web-view.h
+++ b/e-util/e-web-view.h
@@ -66,6 +66,12 @@ typedef enum {
        GTK_STOCK_URI_SCHEME
 } EURIScheme;
 
+typedef void (*EWebViewElementClickedFunc) (EWebView *web_view,
+                                           const gchar *element_class,
+                                           const gchar *element_value,
+                                           const GtkAllocation *element_position,
+                                           gpointer user_data);
+
 struct _EWebView {
        WebKitWebView parent;
        EWebViewPrivate *priv;
@@ -260,6 +266,16 @@ void               e_web_view_set_document_iframe_src
                                                (EWebView *web_view,
                                                 const gchar *document_uri,
                                                 const gchar *new_iframe_src);
+void           e_web_view_register_element_clicked
+                                               (EWebView *web_view,
+                                                const gchar *element_class,
+                                                EWebViewElementClickedFunc callback,
+                                                gpointer user_data);
+void           e_web_view_unregister_element_clicked
+                                               (EWebView *web_view,
+                                                const gchar *element_class,
+                                                EWebViewElementClickedFunc callback,
+                                                gpointer user_data);
 G_END_DECLS
 
 #endif /* E_WEB_VIEW_H */
diff --git a/em-format/Makefile.am b/em-format/Makefile.am
index 7827d79..8939280 100644
--- a/em-format/Makefile.am
+++ b/em-format/Makefile.am
@@ -39,6 +39,7 @@ evolution_mail_formatter_include_HEADERS =            \
        e-mail-part-headers.h                           \
        e-mail-part-image.h                             \
        e-mail-part-list.h                              \
+       e-mail-part-secure-button.h                     \
        e-mail-part-utils.h                             \
        e-mail-stripsig-filter.h
 
@@ -121,6 +122,7 @@ libevolution_mail_formatter_la_SOURCES =            \
        e-mail-part-headers.c                           \
        e-mail-part-image.c                             \
        e-mail-part-list.c                              \
+       e-mail-part-secure-button.c                     \
        e-mail-part-utils.c                             \
        e-mail-stripsig-filter.c                        \
        $(SMIME_EXTENSIONS)
diff --git a/em-format/e-mail-formatter-secure-button.c b/em-format/e-mail-formatter-secure-button.c
index 0ed0ef4..8269fcc 100644
--- a/em-format/e-mail-formatter-secure-button.c
+++ b/em-format/e-mail-formatter-secure-button.c
@@ -23,11 +23,6 @@
 
 #include <e-util/e-util.h>
 
-#if defined (HAVE_NSS) && defined (ENABLE_SMIME)
-#include "certificate-manager.h"
-#include "e-cert-db.h"
-#endif
-
 #include "e-mail-formatter-extension.h"
 
 typedef EMailFormatterExtension EMailFormatterSecureButton;
@@ -41,7 +36,7 @@ G_DEFINE_TYPE (
        E_TYPE_MAIL_FORMATTER_EXTENSION)
 
 static const gchar *formatter_mime_types[] = {
-       "application/vnd.evolution.widget.secure-button",
+       "application/vnd.evolution.secure-button",
        NULL
 };
 
@@ -74,139 +69,25 @@ static const GdkRGBA smime_sign_colour[6] = {
        { 0.0, 0.0, 0.0, 1.0 }
 };
 
-static gboolean
-emfe_secure_button_format (EMailFormatterExtension *extension,
-                           EMailFormatter *formatter,
-                           EMailFormatterContext *context,
-                           EMailPart *part,
-                           GOutputStream *stream,
-                           GCancellable *cancellable)
-{
-       gchar *str;
-
-       if ((context->mode != E_MAIL_FORMATTER_MODE_NORMAL) &&
-           (context->mode != E_MAIL_FORMATTER_MODE_RAW) &&
-           (context->mode != E_MAIL_FORMATTER_MODE_ALL_HEADERS))
-               return FALSE;
-
-       str = g_strdup_printf (
-               "<object type=\"application/vnd.evolution.widget.secure-button\" "
-               "height=\"20\" width=\"100%%\" data=\"%s\" id=\"%s\"></object>",
-               e_mail_part_get_id (part),
-               e_mail_part_get_id (part));
-
-       g_output_stream_write_all (
-               stream, str, strlen (str), NULL, cancellable, NULL);
-
-       g_free (str);
-
-       return TRUE;
-}
+/* This is awkward, but there is no header file for it. On the other hand,
+   the functions are meant private, where they really are, being defined this way. */
+const gchar *e_mail_formatter_secure_button_get_encrypt_description (camel_cipher_validity_encrypt_t status);
+const gchar *e_mail_formatter_secure_button_get_sign_description (camel_cipher_validity_sign_t status);
 
-#if defined (HAVE_NSS) && defined (ENABLE_SMIME)
-static void
-viewcert_clicked (GtkWidget *button,
-                  GtkWidget *grid)
+const gchar *
+e_mail_formatter_secure_button_get_sign_description (camel_cipher_validity_sign_t status)
 {
-       CamelCipherCertInfo *info = g_object_get_data ((GObject *) button, "e-cert-info");
-       ECert *ec = NULL;
-
-       if (info->cert_data)
-               ec = e_cert_new (CERT_DupCertificate (info->cert_data));
-
-       if (ec != NULL) {
-               GtkWidget *dialog, *parent;
-
-               parent = gtk_widget_get_toplevel (grid);
-               if (!parent || !GTK_IS_WINDOW (parent))
-                       parent = NULL;
-
-               dialog = e_cert_manager_new_certificate_viewer ((GtkWindow *) parent, ec);
-
-               gtk_widget_show (dialog);
-               g_signal_connect (
-                       dialog, "response",
-                       G_CALLBACK (gtk_widget_destroy), NULL);
-
-               g_object_unref (ec);
-       } else {
-               g_warning (
-                       "can't find certificate for %s <%s>",
-                       info->name ? info->name : "",
-                       info->email ? info->email : "");
-       }
-}
-#endif
+       g_return_val_if_fail (status >= 0 && status < G_N_ELEMENTS (smime_sign_table), NULL);
 
-static void
-info_response (GtkWidget *widget,
-               guint button,
-               gpointer user_data)
-{
-       gtk_widget_destroy (widget);
+       return _(smime_sign_table[status].description);
 }
 
-static void
-add_cert_table (GtkWidget *grid,
-                GQueue *certlist,
-                gpointer user_data)
+const gchar *
+e_mail_formatter_secure_button_get_encrypt_description (camel_cipher_validity_encrypt_t status)
 {
-       GList *head, *link;
-       GtkTable *table;
-       gint n = 0;
-
-       table = (GtkTable *) gtk_table_new (certlist->length, 2, FALSE);
-
-       head = g_queue_peek_head_link (certlist);
-
-       for (link = head; link != NULL; link = g_list_next (link)) {
-               CamelCipherCertInfo *info = link->data;
-               gchar *la = NULL;
-               const gchar *l = NULL;
-
-               if (info->name) {
-                       if (info->email && strcmp (info->name, info->email) != 0)
-                               l = la = g_strdup_printf ("%s <%s>", info->name, info->email);
-                       else
-                               l = info->name;
-               } else {
-                       if (info->email)
-                               l = info->email;
-               }
+       g_return_val_if_fail (status >= 0 && status < G_N_ELEMENTS (smime_encrypt_table), NULL);
 
-               if (l) {
-                       GtkWidget *w;
-#if defined (HAVE_NSS) && defined (ENABLE_SMIME)
-                       ECert *ec = NULL;
-#endif
-                       w = gtk_label_new (l);
-                       gtk_misc_set_alignment ((GtkMisc *) w, 0.0, 0.5);
-                       g_free (la);
-                       gtk_table_attach (table, w, 0, 1, n, n + 1, GTK_FILL, GTK_FILL, 3, 3);
-#if defined (HAVE_NSS) && defined (ENABLE_SMIME)
-                       w = gtk_button_new_with_mnemonic (_("_View Certificate"));
-                       gtk_table_attach (table, w, 1, 2, n, n + 1, 0, 0, 3, 3);
-                       g_object_set_data ((GObject *) w, "e-cert-info", info);
-                       g_signal_connect (
-                               w, "clicked",
-                               G_CALLBACK (viewcert_clicked), grid);
-
-                       if (info->cert_data)
-                               ec = e_cert_new (CERT_DupCertificate (info->cert_data));
-
-                       if (ec == NULL)
-                               gtk_widget_set_sensitive (w, FALSE);
-                       else
-                               g_object_unref (ec);
-#else
-                       w = gtk_label_new (_("This certificate is not viewable"));
-                       gtk_table_attach (table, w, 1, 2, n, n + 1, 0, 0, 3, 3);
-#endif
-                       n++;
-               }
-       }
-
-       gtk_container_add (GTK_CONTAINER (grid), GTK_WIDGET (table));
+       return _(smime_encrypt_table[status].description);
 }
 
 static void
@@ -244,9 +125,9 @@ format_cert_infos (GQueue *cert_infos,
                        g_string_append (output_buffer, cinfo->name);
 
                        if (cinfo->email != NULL && *cinfo->email != '\0') {
-                               g_string_append (output_buffer, " <");
+                               g_string_append (output_buffer, " &lt;");
                                g_string_append (output_buffer, cinfo->email);
-                               g_string_append (output_buffer, ">");
+                               g_string_append (output_buffer, "&gt;");
                        }
 
                } else if (cinfo->email != NULL && *cinfo->email != '\0') {
@@ -261,115 +142,16 @@ format_cert_infos (GQueue *cert_infos,
 }
 
 static void
-secure_button_clicked_cb (GtkWidget *widget,
-                          CamelCipherValidity *validity)
-{
-       GtkBuilder *builder;
-       GtkWidget *grid, *w;
-       GtkWidget *dialog;
-
-       g_return_if_fail (validity != NULL);
-
-       /* Make sure our custom widget classes are registered with
-        * GType before we load the GtkBuilder definition file. */
-       g_type_ensure (E_TYPE_DATE_EDIT);
-
-       builder = gtk_builder_new ();
-       e_load_ui_builder_definition (builder, "mail-dialogs.ui");
-
-       dialog = e_builder_get_widget (builder, "message_security_dialog");
-
-       grid = e_builder_get_widget (builder, "signature_grid");
-       w = gtk_label_new (_(smime_sign_table[validity->sign.status].description));
-       gtk_misc_set_alignment ((GtkMisc *) w, 0.0, 0.5);
-       gtk_label_set_line_wrap ((GtkLabel *) w, TRUE);
-       gtk_container_add (GTK_CONTAINER (grid), w);
-       if (validity->sign.description) {
-               GtkTextBuffer *buffer;
-
-               buffer = gtk_text_buffer_new (NULL);
-               gtk_text_buffer_set_text (
-                       buffer, validity->sign.description,
-                       strlen (validity->sign.description));
-               w = g_object_new (
-                       gtk_scrolled_window_get_type (),
-                       "hscrollbar_policy", GTK_POLICY_AUTOMATIC,
-                       "vscrollbar_policy", GTK_POLICY_AUTOMATIC,
-                       "shadow_type", GTK_SHADOW_IN,
-                       "expand", TRUE,
-                       "child", g_object_new (gtk_text_view_get_type (),
-                       "buffer", buffer,
-                       "cursor_visible", FALSE,
-                       "editable", FALSE,
-                       NULL),
-                       "width_request", 500,
-                       "height_request", 80,
-                       NULL);
-               g_object_unref (buffer);
-
-               gtk_container_add (GTK_CONTAINER (grid), w);
-       }
-
-       if (!g_queue_is_empty (&validity->sign.signers))
-               add_cert_table (
-                       grid, &validity->sign.signers, NULL);
-
-       gtk_widget_show_all (grid);
-
-       grid = e_builder_get_widget (builder, "encryption_grid");
-       w = gtk_label_new (_(smime_encrypt_table[validity->encrypt.status].description));
-       gtk_misc_set_alignment ((GtkMisc *) w, 0.0, 0.5);
-       gtk_label_set_line_wrap ((GtkLabel *) w, TRUE);
-       gtk_container_add (GTK_CONTAINER (grid), w);
-       if (validity->encrypt.description) {
-               GtkTextBuffer *buffer;
-
-               buffer = gtk_text_buffer_new (NULL);
-               gtk_text_buffer_set_text (
-                       buffer, validity->encrypt.description,
-                       strlen (validity->encrypt.description));
-               w = g_object_new (
-                       gtk_scrolled_window_get_type (),
-                       "hscrollbar_policy", GTK_POLICY_AUTOMATIC,
-                       "vscrollbar_policy", GTK_POLICY_AUTOMATIC,
-                       "shadow_type", GTK_SHADOW_IN,
-                       "expand", TRUE,
-                       "child", g_object_new (gtk_text_view_get_type (),
-                       "buffer", buffer,
-                       "cursor_visible", FALSE,
-                       "editable", FALSE,
-                       NULL),
-                       "width_request", 500,
-                       "height_request", 80,
-                       NULL);
-               g_object_unref (buffer);
-
-               gtk_container_add (GTK_CONTAINER (grid), w);
-       }
-
-       if (!g_queue_is_empty (&validity->encrypt.encrypters))
-               add_cert_table (grid, &validity->encrypt.encrypters, NULL);
-
-       gtk_widget_show_all (grid);
-
-       g_object_unref (builder);
-
-       g_signal_connect (
-               dialog, "response",
-               G_CALLBACK (info_response), NULL);
-
-       gtk_widget_show (dialog);
-}
-
-static GtkWidget *
-secure_button_get_widget_for_validity (CamelCipherValidity *validity)
+secure_button_format_validity (EMailPart *part,
+                              CamelCipherValidity *validity,
+                              GString *html)
 {
-       GtkWidget *box, *button, *layout, *widget;
        const gchar *icon_name;
        gchar *description;
+       gint icon_width, icon_height;
        GString *buffer;
 
-       g_return_val_if_fail (validity != NULL, NULL);
+       g_return_if_fail (validity != NULL);
 
        buffer = g_string_new ("");
 
@@ -390,7 +172,7 @@ secure_button_get_widget_for_validity (CamelCipherValidity *validity)
                gint status;
 
                if (validity->sign.status != CAMEL_CIPHER_VALIDITY_SIGN_NONE)
-                       g_string_append (buffer, "\n");
+                       g_string_append (buffer, "<br>\n");
 
                status = validity->encrypt.status;
                desc = smime_encrypt_table[status].shortdesc;
@@ -400,87 +182,66 @@ secure_button_get_widget_for_validity (CamelCipherValidity *validity)
        description = g_string_free (buffer, FALSE);
 
        /* FIXME: need to have it based on encryption and signing too */
-       if (validity->sign.status != 0)
+       if (validity->sign.status != CAMEL_CIPHER_VALIDITY_SIGN_NONE)
                icon_name = smime_sign_table[validity->sign.status].icon;
        else
                icon_name = smime_encrypt_table[validity->encrypt.status].icon;
 
-       box = gtk_event_box_new ();
-       if (validity->sign.status != 0)
-               gtk_widget_override_background_color (
-                       box, GTK_STATE_FLAG_NORMAL,
-                       &smime_sign_colour[validity->sign.status]);
-
-       layout = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 5);
-       gtk_container_add (GTK_CONTAINER (box), layout);
-
-       button = gtk_button_new ();
-       gtk_box_pack_start (GTK_BOX (layout), button, FALSE, FALSE, 0);
-       g_signal_connect (
-               button, "clicked",
-               G_CALLBACK (secure_button_clicked_cb), validity);
-
-       widget = gtk_image_new_from_icon_name (
-                       icon_name, GTK_ICON_SIZE_LARGE_TOOLBAR);
-       gtk_button_set_image (GTK_BUTTON (button), widget);
-
-       widget = gtk_label_new (description);
-       g_object_set (G_OBJECT (widget),
-               "wrap", TRUE,
-               "width-chars", 20,
-               "max-width-chars", 80,
-               "xalign", 0.0,
-               "halign", GTK_ALIGN_FILL,
-               "hexpand", TRUE,
-               NULL);
-       /* make sure the text color doesn't change with theme */
-       gtk_widget_override_color (widget, GTK_STATE_FLAG_NORMAL, &smime_sign_colour[5]);
-       gtk_box_pack_start (GTK_BOX (layout), widget, TRUE, TRUE, 0);
+       if (!gtk_icon_size_lookup (GTK_ICON_SIZE_LARGE_TOOLBAR, &icon_width, &icon_height)) {
+               icon_width = 24;
+               icon_height = 24;
+       }
 
-       g_free (description);
+       g_string_append (html, "<table width=\"100%\" style=\"vertical-align:middle;");
+       if (validity->sign.status != CAMEL_CIPHER_VALIDITY_SIGN_NONE &&
+           smime_sign_colour[validity->sign.status].alpha > 1e-9)
+               g_string_append_printf (html, " background:#%06x;",
+                       e_rgba_to_value (&smime_sign_colour[validity->sign.status]));
+       g_string_append (html, "\"><tr>");
+
+       g_string_append_printf (html,
+               "<td style=\"width:1px;\"><button type=\"button\" class=\"secure-button\" 
id=\"secure-button\" value=\"%p:%p\" accesskey=\"\" style=\"vertical-align:middle;\">"
+               "<img src=\"gtk-stock://%s?size=%d\" width=\"%dpx\" height=\"%dpx\" 
style=\"vertical-align:middle;\"></button></td><td><span style=\"color:#%06x; 
vertical-align:middle;\">%s</span></td>",
+               part, validity, icon_name, GTK_ICON_SIZE_LARGE_TOOLBAR,
+               icon_width, icon_height, e_rgba_to_value (&smime_sign_colour[5]), description);
 
-       return box;
+       g_free (description);
+       g_string_append (html, "</tr></table>\n");
 }
 
-static GtkWidget *
-emfe_secure_button_get_widget (EMailFormatterExtension *extension,
-                               EMailPartList *context,
-                               EMailPart *part,
-                               GHashTable *params)
+static gboolean
+emfe_secure_button_format (EMailFormatterExtension *extension,
+                           EMailFormatter *formatter,
+                           EMailFormatterContext *context,
+                           EMailPart *part,
+                           GOutputStream *stream,
+                           GCancellable *cancellable)
 {
-       GtkWidget *grid;
        GList *head, *link;
+       GString *html;
 
-       g_return_val_if_fail (part != NULL, NULL);
-
-       grid = g_object_new (
-               GTK_TYPE_GRID,
-               "orientation", GTK_ORIENTATION_VERTICAL,
-               "row-spacing", 2,
-               "halign", GTK_ALIGN_FILL,
-               "hexpand", TRUE,
-               NULL);
+       if ((context->mode != E_MAIL_FORMATTER_MODE_NORMAL) &&
+           (context->mode != E_MAIL_FORMATTER_MODE_RAW) &&
+           (context->mode != E_MAIL_FORMATTER_MODE_ALL_HEADERS))
+               return FALSE;
 
+       html = g_string_new ("");
        head = g_queue_peek_head_link (&part->validities);
 
        for (link = head; link != NULL; link = g_list_next (link)) {
                EMailPartValidityPair *pair = link->data;
-               GtkWidget *widget;
 
-               if (pair == NULL)
+               if (!pair)
                        continue;
 
-               widget = secure_button_get_widget_for_validity (pair->validity);
-               if (widget != NULL) {
-                       gtk_widget_set_halign (widget, GTK_ALIGN_FILL);
-                       gtk_widget_set_hexpand (widget, TRUE);
-                       gtk_container_add (GTK_CONTAINER (grid), widget);
-               }
+               secure_button_format_validity (part, pair->validity, html);
        }
 
-       gtk_widget_show_all (grid);
+       g_output_stream_write_all (stream, html->str, html->len, NULL, cancellable, NULL);
 
-       return grid;
+       g_string_free (html, TRUE);
+
+       return TRUE;
 }
 
 static void
@@ -489,7 +250,6 @@ e_mail_formatter_secure_button_class_init (EMailFormatterExtensionClass *class)
        class->mime_types = formatter_mime_types;
        class->priority = G_PRIORITY_LOW;
        class->format = emfe_secure_button_format;
-       class->get_widget = emfe_secure_button_get_widget;
 }
 
 static void
diff --git a/em-format/e-mail-parser-application-smime.c b/em-format/e-mail-parser-application-smime.c
index 95f07b4..f3673bf 100644
--- a/em-format/e-mail-parser-application-smime.c
+++ b/em-format/e-mail-parser-application-smime.c
@@ -120,7 +120,7 @@ empe_app_smime_parse (EMailParserExtension *extension,
 
                        e_mail_parser_parse_part_as (
                                parser, part, part_id,
-                               "application/vnd.evolution.widget.secure-button",
+                               "application/vnd.evolution.secure-button",
                                cancellable, &work_queue);
 
                        mail_part = g_queue_peek_head (&work_queue);
diff --git a/em-format/e-mail-parser-inlinepgp-encrypted.c b/em-format/e-mail-parser-inlinepgp-encrypted.c
index 56d5fc5..bdabcb8 100644
--- a/em-format/e-mail-parser-inlinepgp-encrypted.c
+++ b/em-format/e-mail-parser-inlinepgp-encrypted.c
@@ -148,7 +148,7 @@ empe_inlinepgp_encrypted_parse (EMailParserExtension *extension,
 
                e_mail_parser_parse_part_as (
                        parser, part, part_id,
-                       "application/vnd.evolution.widget.secure-button",
+                       "application/vnd.evolution.secure-button",
                        cancellable, &work_queue);
 
                mail_part = g_queue_peek_head (&work_queue);
diff --git a/em-format/e-mail-parser-inlinepgp-signed.c b/em-format/e-mail-parser-inlinepgp-signed.c
index 9f08583..84f38bd 100644
--- a/em-format/e-mail-parser-inlinepgp-signed.c
+++ b/em-format/e-mail-parser-inlinepgp-signed.c
@@ -160,7 +160,7 @@ empe_inlinepgp_signed_parse (EMailParserExtension *extension,
 
                e_mail_parser_parse_part_as (
                        parser, part, part_id,
-                       "application/vnd.evolution.widget.secure-button",
+                       "application/vnd.evolution.secure-button",
                        cancellable, &work_queue);
 
                mail_part = g_queue_peek_head (&work_queue);
diff --git a/em-format/e-mail-parser-multipart-encrypted.c b/em-format/e-mail-parser-multipart-encrypted.c
index dff97b3..6c8a65e 100644
--- a/em-format/e-mail-parser-multipart-encrypted.c
+++ b/em-format/e-mail-parser-multipart-encrypted.c
@@ -143,7 +143,7 @@ empe_mp_encrypted_parse (EMailParserExtension *extension,
 
                e_mail_parser_parse_part_as (
                        parser, part, part_id,
-                       "application/vnd.evolution.widget.secure-button",
+                       "application/vnd.evolution.secure-button",
                        cancellable, &work_queue);
 
                mail_part = g_queue_peek_head (&work_queue);
diff --git a/em-format/e-mail-parser-multipart-signed.c b/em-format/e-mail-parser-multipart-signed.c
index c467ec7..f90d645 100644
--- a/em-format/e-mail-parser-multipart-signed.c
+++ b/em-format/e-mail-parser-multipart-signed.c
@@ -188,7 +188,7 @@ empe_mp_signed_parse (EMailParserExtension *extension,
 
                e_mail_parser_parse_part_as (
                        parser, part, part_id,
-                       "application/vnd.evolution.widget.secure-button",
+                       "application/vnd.evolution.secure-button",
                        cancellable, &work_queue);
 
                mail_part = g_queue_peek_head (&work_queue);
diff --git a/em-format/e-mail-parser-secure-button.c b/em-format/e-mail-parser-secure-button.c
index 61832a0..e458598 100644
--- a/em-format/e-mail-parser-secure-button.c
+++ b/em-format/e-mail-parser-secure-button.c
@@ -23,6 +23,7 @@
 
 #include <e-util/e-util.h>
 
+#include "e-mail-part-secure-button.h"
 #include "e-mail-parser-extension.h"
 
 typedef EMailParserExtension EMailParserSecureButton;
@@ -36,7 +37,7 @@ G_DEFINE_TYPE (
        E_TYPE_MAIL_PARSER_EXTENSION)
 
 static const gchar *parser_mime_types[] = {
-       "application/vnd.evolution.widget.secure-button",
+       "application/vnd.evolution.secure-button",
        NULL
 };
 
@@ -53,7 +54,7 @@ empe_secure_button_parse (EMailParserExtension *extension,
 
        len = part_id->len;
        g_string_append (part_id, ".secure_button");
-       mail_part = e_mail_part_new (part, part_id->str);
+       mail_part = e_mail_part_secure_button_new (part, part_id->str);
        e_mail_part_set_mime_type (mail_part, parser_mime_types[0]);
        g_string_truncate (part_id, len);
 
diff --git a/em-format/e-mail-part-secure-button.c b/em-format/e-mail-part-secure-button.c
new file mode 100644
index 0000000..c10e718
--- /dev/null
+++ b/em-format/e-mail-part-secure-button.c
@@ -0,0 +1,321 @@
+/*
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation.
+ *
+ * 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 Lesser General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <glib/gi18n-lib.h>
+
+#include <e-util/e-util.h>
+
+#if defined (HAVE_NSS) && defined (ENABLE_SMIME)
+#include "certificate-manager.h"
+#include "e-cert-db.h"
+#endif
+
+#include "e-mail-part-secure-button.h"
+
+G_DEFINE_TYPE (EMailPartSecureButton, e_mail_part_secure_button, E_TYPE_MAIL_PART)
+
+const gchar *e_mail_formatter_secure_button_get_encrypt_description (camel_cipher_validity_encrypt_t status);
+const gchar *e_mail_formatter_secure_button_get_sign_description (camel_cipher_validity_sign_t status);
+
+
+#if defined (HAVE_NSS) && defined (ENABLE_SMIME)
+static void
+viewcert_clicked (GtkWidget *button,
+                  GtkWidget *grid)
+{
+       CamelCipherCertInfo *info = g_object_get_data ((GObject *) button, "e-cert-info");
+       ECert *ec = NULL;
+
+       if (info->cert_data)
+               ec = e_cert_new (CERT_DupCertificate (info->cert_data));
+
+       if (ec != NULL) {
+               GtkWidget *dialog, *parent;
+
+               parent = gtk_widget_get_toplevel (grid);
+               if (!parent || !GTK_IS_WINDOW (parent))
+                       parent = NULL;
+
+               dialog = e_cert_manager_new_certificate_viewer ((GtkWindow *) parent, ec);
+
+               gtk_widget_show (dialog);
+               g_signal_connect (
+                       dialog, "response",
+                       G_CALLBACK (gtk_widget_destroy), NULL);
+
+               g_object_unref (ec);
+       } else {
+               g_warning (
+                       "can't find certificate for %s <%s>",
+                       info->name ? info->name : "",
+                       info->email ? info->email : "");
+       }
+}
+#endif
+
+static void
+info_response (GtkWidget *widget,
+               guint button,
+               gpointer user_data)
+{
+       gtk_widget_destroy (widget);
+}
+
+static void
+add_cert_table (GtkWidget *grid,
+                GQueue *certlist,
+                gpointer user_data)
+{
+       GList *head, *link;
+       GtkTable *table;
+       gint n = 0;
+
+       table = (GtkTable *) gtk_table_new (certlist->length, 2, FALSE);
+
+       head = g_queue_peek_head_link (certlist);
+
+       for (link = head; link != NULL; link = g_list_next (link)) {
+               CamelCipherCertInfo *info = link->data;
+               gchar *la = NULL;
+               const gchar *l = NULL;
+
+               if (info->name) {
+                       if (info->email && strcmp (info->name, info->email) != 0)
+                               l = la = g_strdup_printf ("%s <%s>", info->name, info->email);
+                       else
+                               l = info->name;
+               } else {
+                       if (info->email)
+                               l = info->email;
+               }
+
+               if (l) {
+                       GtkWidget *w;
+#if defined (HAVE_NSS) && defined (ENABLE_SMIME)
+                       ECert *ec = NULL;
+#endif
+                       w = gtk_label_new (l);
+                       gtk_misc_set_alignment ((GtkMisc *) w, 0.0, 0.5);
+                       g_free (la);
+                       gtk_table_attach (table, w, 0, 1, n, n + 1, GTK_FILL, GTK_FILL, 3, 3);
+#if defined (HAVE_NSS) && defined (ENABLE_SMIME)
+                       w = gtk_button_new_with_mnemonic (_("_View Certificate"));
+                       gtk_table_attach (table, w, 1, 2, n, n + 1, 0, 0, 3, 3);
+                       g_object_set_data ((GObject *) w, "e-cert-info", info);
+                       g_signal_connect (
+                               w, "clicked",
+                               G_CALLBACK (viewcert_clicked), grid);
+
+                       if (info->cert_data)
+                               ec = e_cert_new (CERT_DupCertificate (info->cert_data));
+
+                       if (ec == NULL)
+                               gtk_widget_set_sensitive (w, FALSE);
+                       else
+                               g_object_unref (ec);
+#else
+                       w = gtk_label_new (_("This certificate is not viewable"));
+                       gtk_table_attach (table, w, 1, 2, n, n + 1, 0, 0, 3, 3);
+#endif
+                       n++;
+               }
+       }
+
+       gtk_container_add (GTK_CONTAINER (grid), GTK_WIDGET (table));
+}
+
+static void
+secure_button_show_validity_dialog (EWebView *web_view,
+                                   CamelCipherValidity *validity)
+{
+       GtkBuilder *builder;
+       GtkWidget *grid, *w;
+       GtkWidget *dialog;
+
+       g_return_if_fail (validity != NULL);
+
+       /* Make sure our custom widget classes are registered with
+        * GType before we load the GtkBuilder definition file. */
+       g_type_ensure (E_TYPE_DATE_EDIT);
+
+       builder = gtk_builder_new ();
+       e_load_ui_builder_definition (builder, "mail-dialogs.ui");
+
+       dialog = e_builder_get_widget (builder, "message_security_dialog");
+
+       w = gtk_widget_get_toplevel (GTK_WIDGET (web_view));
+       if (GTK_IS_WINDOW (w))
+               gtk_window_set_transient_for (GTK_WINDOW (dialog), GTK_WINDOW (w));
+
+       grid = e_builder_get_widget (builder, "signature_grid");
+       w = gtk_label_new (e_mail_formatter_secure_button_get_sign_description (validity->sign.status));
+       gtk_misc_set_alignment ((GtkMisc *) w, 0.0, 0.5);
+       gtk_label_set_line_wrap ((GtkLabel *) w, TRUE);
+       gtk_container_add (GTK_CONTAINER (grid), w);
+       if (validity->sign.description) {
+               GtkTextBuffer *buffer;
+
+               buffer = gtk_text_buffer_new (NULL);
+               gtk_text_buffer_set_text (
+                       buffer, validity->sign.description,
+                       strlen (validity->sign.description));
+               w = g_object_new (
+                       gtk_scrolled_window_get_type (),
+                       "hscrollbar_policy", GTK_POLICY_AUTOMATIC,
+                       "vscrollbar_policy", GTK_POLICY_AUTOMATIC,
+                       "shadow_type", GTK_SHADOW_IN,
+                       "expand", TRUE,
+                       "child", g_object_new (gtk_text_view_get_type (),
+                       "buffer", buffer,
+                       "cursor_visible", FALSE,
+                       "editable", FALSE,
+                       NULL),
+                       "width_request", 500,
+                       "height_request", 80,
+                       NULL);
+               g_object_unref (buffer);
+
+               gtk_container_add (GTK_CONTAINER (grid), w);
+       }
+
+       if (!g_queue_is_empty (&validity->sign.signers))
+               add_cert_table (grid, &validity->sign.signers, NULL);
+
+       gtk_widget_show_all (grid);
+
+       grid = e_builder_get_widget (builder, "encryption_grid");
+       w = gtk_label_new (e_mail_formatter_secure_button_get_encrypt_description (validity->encrypt.status));
+       gtk_misc_set_alignment ((GtkMisc *) w, 0.0, 0.5);
+       gtk_label_set_line_wrap ((GtkLabel *) w, TRUE);
+       gtk_container_add (GTK_CONTAINER (grid), w);
+       if (validity->encrypt.description) {
+               GtkTextBuffer *buffer;
+
+               buffer = gtk_text_buffer_new (NULL);
+               gtk_text_buffer_set_text (
+                       buffer, validity->encrypt.description,
+                       strlen (validity->encrypt.description));
+               w = g_object_new (
+                       gtk_scrolled_window_get_type (),
+                       "hscrollbar_policy", GTK_POLICY_AUTOMATIC,
+                       "vscrollbar_policy", GTK_POLICY_AUTOMATIC,
+                       "shadow_type", GTK_SHADOW_IN,
+                       "expand", TRUE,
+                       "child", g_object_new (gtk_text_view_get_type (),
+                       "buffer", buffer,
+                       "cursor_visible", FALSE,
+                       "editable", FALSE,
+                       NULL),
+                       "width_request", 500,
+                       "height_request", 80,
+                       NULL);
+               g_object_unref (buffer);
+
+               gtk_container_add (GTK_CONTAINER (grid), w);
+       }
+
+       if (!g_queue_is_empty (&validity->encrypt.encrypters))
+               add_cert_table (grid, &validity->encrypt.encrypters, NULL);
+
+       gtk_widget_show_all (grid);
+
+       g_object_unref (builder);
+
+       g_signal_connect (
+               dialog, "response",
+               G_CALLBACK (info_response), NULL);
+
+       gtk_widget_show (dialog);
+}
+
+static void
+secure_button_clicked_cb (EWebView *web_view,
+                         const gchar *element_class,
+                         const gchar *element_value,
+                         const GtkAllocation *element_position,
+                         gpointer user_data)
+{
+       EMailPart *mail_part = user_data;
+       GList *head, *link;
+       gboolean can_use;
+       gchar *tmp;
+
+       g_return_if_fail (E_IS_MAIL_PART_SECURE_BUTTON (mail_part));
+
+       tmp = g_strdup_printf ("%p:", mail_part);
+       can_use = element_value && g_str_has_prefix (element_value, tmp);
+       if (can_use)
+               element_value += strlen (tmp);
+       g_free (tmp);
+
+       if (!can_use)
+               return;
+
+       head = g_queue_peek_head_link (&mail_part->validities);
+       for (link = head; link != NULL; link = g_list_next (link)) {
+               EMailPartValidityPair *pair = link->data;
+
+               if (!pair)
+                       continue;
+
+               tmp = g_strdup_printf ("%p", pair->validity);
+               can_use = g_strcmp0 (element_value, tmp) == 0;
+               g_free (tmp);
+
+               if (can_use) {
+                       secure_button_show_validity_dialog (web_view, pair->validity);
+                       break;
+               }
+       }
+}
+
+static void
+mail_part_secure_button_web_view_loaded (EMailPart *mail_part,
+                                        EWebView *web_view)
+{
+       g_return_if_fail (E_IS_MAIL_PART_SECURE_BUTTON (mail_part));
+       g_return_if_fail (E_IS_WEB_VIEW (web_view));
+
+       e_web_view_register_element_clicked (web_view, "secure-button", secure_button_clicked_cb, mail_part);
+}
+
+static void
+e_mail_part_secure_button_class_init (EMailPartSecureButtonClass *class)
+{
+       EMailPartClass *mail_part_class;
+
+       mail_part_class = E_MAIL_PART_CLASS (class);
+       mail_part_class->web_view_loaded = mail_part_secure_button_web_view_loaded;
+}
+
+static void
+e_mail_part_secure_button_init (EMailPartSecureButton *part)
+{
+}
+
+EMailPart *
+e_mail_part_secure_button_new (CamelMimePart *mime_part,
+                       const gchar *id)
+{
+       g_return_val_if_fail (id != NULL, NULL);
+
+       return g_object_new (
+               E_TYPE_MAIL_PART_SECURE_BUTTON,
+               "id", id, "mime-part", mime_part, NULL);
+}
diff --git a/em-format/e-mail-part-secure-button.h b/em-format/e-mail-part-secure-button.h
new file mode 100644
index 0000000..b40a0d3
--- /dev/null
+++ b/em-format/e-mail-part-secure-button.h
@@ -0,0 +1,61 @@
+/*
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation.
+ *
+ * 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 Lesser General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifndef E_MAIL_PART_SECURE_BUTTON_H
+#define E_MAIL_PART_SECURE_BUTTON_H
+
+#include <em-format/e-mail-part.h>
+
+/* Standard GObject macros */
+#define E_TYPE_MAIL_PART_SECURE_BUTTON \
+       (e_mail_part_secure_button_get_type ())
+#define E_MAIL_PART_SECURE_BUTTON(obj) \
+       (G_TYPE_CHECK_INSTANCE_CAST \
+       ((obj), E_TYPE_MAIL_PART_SECURE_BUTTON, EMailPartSecureButton))
+#define E_MAIL_PART_SECURE_BUTTON_CLASS(cls) \
+       (G_TYPE_CHECK_CLASS_CAST \
+       ((cls), E_TYPE_MAIL_PART_SECURE_BUTTON, EMailPartSecureButtonClass))
+#define E_IS_MAIL_PART_SECURE_BUTTON(obj) \
+       (G_TYPE_CHECK_INSTANCE_TYPE \
+       ((obj), E_TYPE_MAIL_PART_SECURE_BUTTON))
+#define E_IS_MAIL_PART_SECURE_BUTTON_CLASS(cls) \
+       (G_TYPE_CHECK_CLASS_TYPE \
+       ((cls), E_TYPE_MAIL_PART_SECURE_BUTTON))
+#define E_MAIL_PART_SECURE_BUTTON_GET_CLASS(obj) \
+       (G_TYPE_INSTANCE_GET_CLASS \
+       ((obj), E_TYPE_MAIL_PART_SECURE_BUTTON, EMailPartSecureButtonClass))
+
+G_BEGIN_DECLS
+
+typedef struct _EMailPartSecureButton EMailPartSecureButton;
+typedef struct _EMailPartSecureButtonClass EMailPartSecureButtonClass;
+typedef struct _EMailPartSecureButtonPrivate EMailPartSecureButtonPrivate;
+
+struct _EMailPartSecureButton {
+       EMailPart parent;
+       EMailPartSecureButtonPrivate *priv;
+};
+
+struct _EMailPartSecureButtonClass {
+       EMailPartClass parent_class;
+};
+
+GType          e_mail_part_secure_button_get_type      (void) G_GNUC_CONST;
+EMailPart *    e_mail_part_secure_button_new           (CamelMimePart *mime_part,
+                                                        const gchar *id);
+
+G_END_DECLS
+
+#endif /* E_MAIL_PART_SECURE_BUTTON_H */
diff --git a/em-format/e-mail-part.c b/em-format/e-mail-part.c
index 8795cde..f225126 100644
--- a/em-format/e-mail-part.c
+++ b/em-format/e-mail-part.c
@@ -597,6 +597,21 @@ e_mail_part_bind_dom_element (EMailPart *part,
                class->bind_dom_element (part, web_extension, page_id, element_id);
 }
 
+void
+e_mail_part_web_view_loaded (EMailPart *part,
+                            EWebView *web_view)
+{
+       EMailPartClass *klass;
+
+       g_return_if_fail (E_IS_MAIL_PART (part));
+       g_return_if_fail (E_IS_WEB_VIEW (web_view));
+
+       klass = E_MAIL_PART_GET_CLASS (part);
+
+       if (klass->web_view_loaded)
+               klass->web_view_loaded (part, web_view);
+}
+
 static EMailPartValidityPair *
 mail_part_find_validity_pair (EMailPart *part,
                               EMailPartValidityFlags validity_type)
diff --git a/em-format/e-mail-part.h b/em-format/e-mail-part.h
index 7ff9163..72e17ad 100644
--- a/em-format/e-mail-part.h
+++ b/em-format/e-mail-part.h
@@ -89,6 +89,8 @@ struct _EMailPartClass {
                                                 GDBusProxy *web_extension,
                                                 guint64 page_id,
                                                 const gchar *element_id);
+       void            (*web_view_loaded)      (EMailPart *part,
+                                                EWebView *web_view);
 };
 
 GType          e_mail_part_get_type            (void) G_GNUC_CONST;
@@ -125,6 +127,8 @@ void                e_mail_part_bind_dom_element    (EMailPart *part,
                                                 GDBusProxy *web_extension,
                                                 guint64 page_id,
                                                 const gchar *element_id);
+void           e_mail_part_web_view_loaded     (EMailPart *part,
+                                                EWebView *web_view);
 void           e_mail_part_update_validity     (EMailPart *part,
                                                 CamelCipherValidity *validity,
                                                 EMailPartValidityFlags validity_type);
diff --git a/mail/e-mail-display.c b/mail/e-mail-display.c
index f00d98a..a7fe93b 100644
--- a/mail/e-mail-display.c
+++ b/mail/e-mail-display.c
@@ -1133,16 +1133,17 @@ mail_element_exists_cb (GDBusProxy *web_extension,
 }
 
 static void
-mail_parts_bind_dom (WebKitWebView *web_view,
+mail_parts_bind_dom (WebKitWebView *wk_web_view,
                      WebKitLoadEvent load_event,
                      gpointer user_data)
 {
        EMailDisplay *display;
+       EWebView *web_view;
        GQueue queue = G_QUEUE_INIT;
        GList *head, *link;
        GDBusProxy *web_extension;
 
-       display = E_MAIL_DISPLAY (web_view);
+       display = E_MAIL_DISPLAY (wk_web_view);
 
        if (load_event == WEBKIT_LOAD_STARTED) {
                e_mail_display_cleanup_skipped_uris (display);
@@ -1157,12 +1158,13 @@ mail_parts_bind_dom (WebKitWebView *web_view,
 
        initialize_web_view_colors (display);
 
-       web_extension = e_web_view_get_web_extension_proxy (E_WEB_VIEW (display));
+       web_view = E_WEB_VIEW (display);
+
+       web_extension = e_web_view_get_web_extension_proxy (web_view);
        if (!web_extension)
                return;
 
-       e_mail_part_list_queue_parts (
-               display->priv->part_list, NULL, &queue);
+       e_mail_part_list_queue_parts (display->priv->part_list, NULL, &queue);
        head = g_queue_peek_head_link (&queue);
 
        for (link = head; link != NULL; link = g_list_next (link)) {
@@ -1171,6 +1173,8 @@ mail_parts_bind_dom (WebKitWebView *web_view,
 
                part_id = e_mail_part_get_id (part);
 
+               e_mail_part_web_view_loaded (part, web_view);
+
                g_dbus_proxy_call (
                        web_extension,
                        "ElementExists",
diff --git a/po/POTFILES.in b/po/POTFILES.in
index 815837e..4d1da85 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -148,6 +148,7 @@ em-format/e-mail-parser-message-external.c
 em-format/e-mail-parser-multipart-encrypted.c
 em-format/e-mail-parser-multipart-signed.c
 em-format/e-mail-part-headers.c
+em-format/e-mail-part-secure-button.c
 em-format/e-mail-part-utils.c
 e-util/ea-calendar-item.c
 e-util/e-action-combo-box.c
diff --git a/web-extensions/e-web-extension.c b/web-extensions/e-web-extension.c
index 0e5ebba..54aaa52 100644
--- a/web-extensions/e-web-extension.c
+++ b/web-extensions/e-web-extension.c
@@ -36,6 +36,8 @@
 #define WEBKIT_DOM_USE_UNSTABLE_API
 #include <webkitdom/WebKitDOMDOMWindowUnstable.h>
 
+#define WEB_EXTENSION_PAGE_ID_KEY "web-extension-page-id"
+
 #define E_WEB_EXTENSION_GET_PRIVATE(obj) \
        (G_TYPE_INSTANCE_GET_PRIVATE \
        ((obj), E_TYPE_WEB_EXTENSION, EWebExtensionPrivate))
@@ -54,6 +56,19 @@ struct _EWebExtensionPrivate {
 static const char introspection_xml[] =
 "<node>"
 "  <interface name='" E_WEB_EXTENSION_INTERFACE "'>"
+"    <method name='RegisterElementClicked'>"
+"      <arg type='t' name='page_id' direction='in'/>"
+"      <arg type='s' name='element_class' direction='in'/>"
+"    </method>"
+"    <signal name='ElementClicked'>"
+"      <arg type='t' name='page_id' direction='out'/>"
+"      <arg type='s' name='element_class' direction='out'/>"
+"      <arg type='s' name='element_value' direction='out'/>"
+"      <arg type='i' name='position_left' direction='out'/>"
+"      <arg type='i' name='position_top' direction='out'/>"
+"      <arg type='i' name='position_width' direction='out'/>"
+"      <arg type='i' name='position_height' direction='out'/>"
+"    </signal>"
 "    <signal name='HeadersCollapsed'>"
 "      <arg type='b' name='expanded' direction='out'/>"
 "    </signal>"
@@ -159,6 +174,58 @@ get_webkit_web_page_or_return_dbus_error (GDBusMethodInvocation *invocation,
 }
 
 static void
+element_clicked_cb (WebKitDOMElement *element,
+                   WebKitDOMEvent *event,
+                   gpointer user_data)
+{
+       EWebExtension *extension = user_data;
+       WebKitDOMElement *offset_parent;
+       gchar *attr_class, *attr_value;
+       const guint64 *ppage_id;
+       gdouble with_parents_left, with_parents_top;
+       GError *error = NULL;
+
+       g_return_if_fail (E_IS_WEB_EXTENSION (extension));
+       g_return_if_fail (G_IS_OBJECT (element));
+
+       ppage_id = g_object_get_data (G_OBJECT (element), WEB_EXTENSION_PAGE_ID_KEY);
+       g_return_if_fail (ppage_id != NULL);
+
+       with_parents_left = webkit_dom_element_get_offset_left (element);
+       with_parents_top = webkit_dom_element_get_offset_top (element);
+
+       offset_parent = element;
+       while (offset_parent = webkit_dom_element_get_offset_parent (offset_parent), offset_parent) {
+               with_parents_left += webkit_dom_element_get_offset_left (offset_parent);
+               with_parents_top += webkit_dom_element_get_offset_top (offset_parent);
+       }
+
+       attr_class = webkit_dom_element_get_class_name (element);
+       attr_value = webkit_dom_element_get_attribute (element, "value");
+
+       g_dbus_connection_emit_signal (
+               extension->priv->dbus_connection,
+               NULL,
+               E_WEB_EXTENSION_OBJECT_PATH,
+               E_WEB_EXTENSION_INTERFACE,
+               "ElementClicked",
+               g_variant_new ("(tssiiii)", *ppage_id, attr_class ? attr_class : "", attr_value ? attr_value 
: "",
+                       (gint) with_parents_left,
+                       (gint) with_parents_top,
+                       (gint) webkit_dom_element_get_offset_width (element),
+                       (gint) webkit_dom_element_get_offset_height (element)),
+               &error);
+
+       if (error) {
+               g_warning ("Error emitting signal ElementClicked: %s\n", error->message);
+               g_error_free (error);
+       }
+
+       g_free (attr_class);
+       g_free (attr_value);
+}
+
+static void
 handle_method_call (GDBusConnection *connection,
                     const char *sender,
                     const char *object_path,
@@ -179,7 +246,49 @@ handle_method_call (GDBusConnection *connection,
 
        if (camel_debug ("wex"))
                printf ("EWebExtension - %s - %s\n", G_STRFUNC, method_name);
-       if (g_strcmp0 (method_name, "ReplaceLocalImageLinks") == 0) {
+       if (g_strcmp0 (method_name, "RegisterElementClicked") == 0) {
+               const gchar *element_class = NULL;
+
+               g_variant_get (parameters, "(t&s)", &page_id, &element_class);
+
+               web_page = get_webkit_web_page_or_return_dbus_error (invocation, web_extension, page_id);
+               if (!web_page)
+                       return;
+
+               if (!element_class || !*element_class) {
+                       g_warn_if_fail (element_class && *element_class);
+               } else {
+                       WebKitDOMHTMLCollection *collection;
+
+                       document = webkit_web_page_get_dom_document (web_page);
+                       collection = webkit_dom_document_get_elements_by_class_name_as_html_collection 
(document, element_class);
+                       if (collection) {
+                               gulong ii, len;
+
+                               len = webkit_dom_html_collection_get_length (collection);
+                               for (ii = 0; ii < len; ii++) {
+                                       WebKitDOMNode *node;
+
+                                       node = webkit_dom_html_collection_item (collection, ii);
+                                       if (WEBKIT_DOM_IS_EVENT_TARGET (node)) {
+                                               guint64 *ppage_id;
+
+                                               ppage_id = g_new0 (guint64, 1);
+                                               *ppage_id = page_id;
+
+                                               g_object_set_data_full (G_OBJECT (node), 
WEB_EXTENSION_PAGE_ID_KEY, ppage_id, g_free);
+
+                                               webkit_dom_event_target_add_event_listener (
+                                                       WEBKIT_DOM_EVENT_TARGET (node), "click",
+                                                       G_CALLBACK (element_clicked_cb), FALSE, extension);
+                                       }
+                               }
+                       }
+                       g_clear_object (&collection);
+               }
+
+               g_dbus_method_invocation_return_value (invocation, NULL);
+       } else if (g_strcmp0 (method_name, "ReplaceLocalImageLinks") == 0) {
                g_variant_get (parameters, "(t)", &page_id);
                web_page = get_webkit_web_page_or_return_dbus_error (
                        invocation, web_extension, page_id);


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]