[evolution] Bug 751588 - Port to WebKit2



commit 332789ff89d85f8302ae53d6abe2cd993cd28eea
Author: Tomas Popela <tpopela redhat com>
Date:   Thu Aug 11 10:13:00 2016 +0200

    Bug 751588 - Port to WebKit2

 Makefile.am                                        |    1 +
 addressbook/gui/contact-editor/e-contact-editor.c  |    4 +-
 addressbook/gui/widgets/eab-contact-display.c      |   39 +-
 addressbook/gui/widgets/eab-contact-formatter.c    |   61 -
 addressbook/gui/widgets/eab-contact-formatter.h    |    1 -
 calendar/gui/e-comp-editor-page-attachments.c      |   12 +-
 calendar/gui/itip-utils.c                          |  234 +-
 composer/e-composer-actions.c                      |   98 +-
 composer/e-composer-private.c                      |   60 +-
 composer/e-composer-private.h                      |    6 +-
 composer/e-msg-composer.c                          |  978 +-
 composer/e-msg-composer.h                          |   13 +-
 configure.ac                                       |   22 +-
 data/org.gnome.evolution.mail.gschema.xml.in       |   15 +-
 doc/reference/evolution-util/Makefile.am           |    1 -
 e-util/Makefile.am                                 |   24 +-
 e-util/e-attachment-bar.c                          |   77 +-
 e-util/e-attachment-button.c                       |  929 --
 e-util/e-attachment-button.h                       |   95 -
 e-util/e-attachment-store.c                        |  227 +-
 e-util/e-attachment-store.h                        |   15 +
 e-util/e-attachment-view.c                         |  242 +-
 e-util/e-attachment.c                              |  303 +-
 e-util/e-attachment.h                              |   29 +-
 e-util/e-content-editor.c                          | 3583 ++++
 e-util/e-content-editor.h                          | 1021 ++
 e-util/e-content-request.c                         |  188 +
 e-util/e-content-request.h                         |   93 +
 e-util/e-emoticon.c                                |    6 +
 e-util/e-emoticon.h                                |    1 +
 e-util/e-file-request.c                            |  222 +-
 e-util/e-file-request.h                            |   11 +-
 e-util/e-focus-tracker.c                           |   12 +-
 e-util/e-html-editor-actions.c                     |  828 +-
 e-util/e-html-editor-actions.h                     |    8 +-
 e-util/e-html-editor-cell-dialog.c                 |  609 +-
 e-util/e-html-editor-cell-dialog.h                 |    2 -
 e-util/e-html-editor-dialog.h                      |   10 +
 e-util/e-html-editor-find-dialog.c                 |  128 +-
 e-util/e-html-editor-hrule-dialog.c                |  275 +-
 e-util/e-html-editor-hrule-dialog.h                |    2 -
 e-util/e-html-editor-image-dialog.c                |  358 +-
 e-util/e-html-editor-image-dialog.h                |    3 +-
 e-util/e-html-editor-link-dialog.c                 |  282 +-
 e-util/e-html-editor-link-dialog.h                 |    2 -
 e-util/e-html-editor-manager.ui                    |    7 +-
 e-util/e-html-editor-page-dialog.c                 |  235 +-
 e-util/e-html-editor-private.h                     |   16 +-
 e-util/e-html-editor-replace-dialog.c              |  242 +-
 e-util/e-html-editor-selection.c                   | 8120 ---------
 e-util/e-html-editor-selection.h                   |  258 -
 e-util/e-html-editor-spell-check-dialog.c          |  313 +-
 e-util/e-html-editor-table-dialog.c                |  630 +-
 e-util/e-html-editor-text-dialog.c                 |   85 +-
 e-util/e-html-editor-utils.c                       |  653 -
 e-util/e-html-editor-utils.h                       |  115 -
 e-util/e-html-editor-view.c                        |15621 ------------------
 e-util/e-html-editor-view.h                        |  342 -
 e-util/e-html-editor.c                             |  528 +-
 e-util/e-html-editor.h                             |   20 +-
 e-util/e-mail-signature-editor.c                   |  162 +-
 e-util/e-mail-signature-editor.h                   |    9 +-
 e-util/e-mail-signature-manager.c                  |   79 +-
 e-util/e-mail-signature-preview.c                  |   86 +-
 e-util/e-marshal.list                              |    1 +
 e-util/e-misc-utils.c                              |   54 +-
 e-util/e-misc-utils.h                              |    6 +-
 e-util/e-search-bar.c                              |  193 +-
 e-util/e-simple-async-result.c                     |  221 +
 e-util/e-simple-async-result.h                     |   90 +
 e-util/e-spell-checker.c                           |  399 +-
 e-util/e-spell-checker.h                           |    8 +
 e-util/e-stock-request.c                           |  310 +-
 e-util/e-stock-request.h                           |   11 +-
 e-util/e-util-enums.h                              |  479 +-
 e-util/e-util.h                                    |   19 +-
 e-util/e-web-view-preview.c                        |   12 +-
 e-util/e-web-view.c                                | 2225 ++-
 e-util/e-web-view.h                                |  102 +-
 e-util/test-html-editor-units-utils.c              | 1045 ++
 e-util/test-html-editor-units-utils.h              |   89 +
 e-util/test-html-editor-units.c                    | 2823 ++++
 e-util/test-html-editor.c                          |  206 +-
 em-format/Makefile.am                              |    7 +-
 em-format/e-mail-formatter-attachment-bar.c        |  104 -
 em-format/e-mail-formatter-attachment.c            |  254 +-
 em-format/e-mail-formatter-audio.c                 |    2 +-
 em-format/e-mail-formatter-enums.h                 |    1 -
 em-format/e-mail-formatter-extension.c             |   61 -
 em-format/e-mail-formatter-extension.h             |   11 -
 em-format/e-mail-formatter-headers.c               |    4 +-
 em-format/e-mail-formatter-message-rfc822.c        |    4 -
 em-format/e-mail-formatter-quote-attachment.c      |  127 -
 em-format/e-mail-formatter-quote-message-rfc822.c  |    4 -
 em-format/e-mail-formatter-quote.c                 |    2 -
 em-format/e-mail-formatter-secure-button.c         |  395 +-
 em-format/e-mail-formatter.c                       |   35 +-
 em-format/e-mail-formatter.h                       |    6 +-
 em-format/e-mail-parser-application-smime.c        |    2 +-
 em-format/e-mail-parser-attachment-bar.c           |   78 -
 em-format/e-mail-parser-inlinepgp-encrypted.c      |    2 +-
 em-format/e-mail-parser-inlinepgp-signed.c         |    2 +-
 em-format/e-mail-parser-message.c                  |    6 -
 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-parser-text-plain.c               |    2 +-
 em-format/e-mail-parser.c                          |    6 +-
 em-format/e-mail-part-attachment-bar.c             |   98 -
 em-format/e-mail-part-attachment-bar.h             |   70 -
 em-format/e-mail-part-attachment.c                 |    2 +-
 em-format/e-mail-part-attachment.h                 |    2 +-
 em-format/e-mail-part-headers.c                    |   36 +-
 em-format/e-mail-part-secure-button.c              |  321 +
 em-format/e-mail-part-secure-button.h              |   61 +
 em-format/e-mail-part-utils.c                      |    5 +
 em-format/e-mail-part.c                            |   25 +-
 em-format/e-mail-part.h                            |   13 +-
 mail/Makefile.am                                   |    2 +
 mail/e-cid-request.c                               |  146 +
 mail/e-cid-request.h                               |   62 +
 mail/e-http-request.c                              |  335 +-
 mail/e-http-request.h                              |   12 +-
 mail/e-mail-browser.c                              |   57 +-
 mail/e-mail-config-identity-page.c                 |   26 +-
 mail/e-mail-display-popup-extension.c              |    6 +-
 mail/e-mail-display-popup-extension.h              |    5 +-
 mail/e-mail-display.c                              | 2115 ++--
 mail/e-mail-display.h                              |   14 +-
 mail/e-mail-notes.c                                |  207 +-
 mail/e-mail-paned-view.c                           |   14 +-
 mail/e-mail-printer.c                              |  136 +-
 mail/e-mail-reader-utils.c                         |  389 +-
 mail/e-mail-reader.c                               |  148 +-
 mail/e-mail-request.c                              |  573 +-
 mail/e-mail-request.h                              |   11 +-
 mail/em-composer-utils.c                           |  397 +-
 mail/em-composer-utils.h                           |   15 +-
 modules/Makefile.am                                |    3 +-
 modules/addressbook/eab-composer-util.c            |  279 +-
 modules/composer-autosave/e-autosave-utils.c       |   60 +-
 modules/composer-autosave/e-composer-autosave.c    |   12 +-
 modules/itip-formatter/Makefile.am                 |    5 +-
 modules/itip-formatter/e-mail-formatter-itip.c     |   23 +-
 modules/itip-formatter/e-mail-parser-itip.c        |   21 +-
 modules/itip-formatter/e-mail-part-itip.c          |  111 +-
 modules/itip-formatter/e-mail-part-itip.h          |   74 +-
 .../itip-formatter/itip-view-elements-defines.h    |   65 +
 modules/itip-formatter/itip-view.c                 | 2583 ++--
 modules/itip-formatter/itip-view.h                 |   26 +-
 modules/itip-formatter/web-extension/Makefile.am   |   26 +
 .../module-itip-formatter-dom-utils.c              |  642 +
 .../module-itip-formatter-dom-utils.h              |  111 +
 .../module-itip-formatter-web-extension.c          |  646 +
 .../module-itip-formatter-web-extension.h          |   26 +
 modules/mail/e-mail-attachment-handler.c           |  102 +-
 modules/mail/e-mail-shell-backend.c                |   34 +-
 modules/mail/e-mail-shell-content.c                |   44 +-
 modules/mail/e-mail-shell-view-actions.c           |   36 +
 modules/mail/e-mail-shell-view-actions.h           |    2 +
 modules/mail/e-mail-shell-view-private.c           |   60 +-
 modules/mail/e-mail-shell-view-private.h           |    2 +
 modules/mail/em-composer-prefs.c                   |    3 +-
 .../e-mail-display-popup-prefer-plain.c            |   86 +-
 modules/settings/Makefile.am                       |    4 +-
 modules/settings/e-settings-content-editor.c       |  224 +
 modules/settings/e-settings-content-editor.h       |   64 +
 modules/settings/e-settings-html-editor-view.c     |  223 -
 modules/settings/e-settings-html-editor-view.h     |   64 -
 modules/settings/e-settings-spell-checker.c        |    3 +-
 modules/settings/evolution-module-settings.c       |    6 +-
 .../e-mail-display-popup-text-highlight.c          |   98 +-
 modules/vcard-inline/e-mail-formatter-vcard.c      |    9 +-
 modules/vcard-inline/e-mail-part-vcard.c           |  222 +-
 modules/vcard-inline/e-mail-part-vcard.h           |    4 -
 modules/web-inspector/evolution-web-inspector.c    |  167 -
 modules/webkit-editor/Makefile.am                  |   31 +
 modules/webkit-editor/e-webkit-editor-extension.c  |  101 +
 modules/webkit-editor/e-webkit-editor-extension.h  |   62 +
 modules/webkit-editor/e-webkit-editor.c            | 6291 +++++++
 modules/webkit-editor/e-webkit-editor.h            |   68 +
 .../webkit-editor/evolution-module-webkit-editor.c |   36 +
 modules/webkit-editor/web-extension/Makefile.am    |   38 +
 .../web-extension/e-composer-dom-functions.c       |  832 +
 .../web-extension/e-composer-dom-functions.h       |   48 +
 .../web-extension/e-dialogs-dom-functions.c        | 1455 ++
 .../web-extension/e-dialogs-dom-functions.h        |  134 +
 .../web-extension/e-editor-dom-functions.c         |17386 ++++++++++++++++++++
 .../web-extension/e-editor-dom-functions.h         |  378 +
 .../webkit-editor/web-extension/e-editor-page.c    |  940 ++
 .../webkit-editor/web-extension/e-editor-page.h    |  197 +
 .../web-extension/e-editor-undo-redo-manager.c     | 2831 ++++
 .../web-extension/e-editor-undo-redo-manager.h     |  175 +
 .../web-extension/e-editor-web-extension-main.c    |   57 +
 .../web-extension/e-editor-web-extension-names.h   |   26 +
 .../web-extension/e-editor-web-extension.c         | 2501 +++
 .../web-extension/e-editor-web-extension.h         |   82 +
 .../Makefile.am                                    |   14 +-
 .../webkit-inspector/evolution-webkit-inspector.c  |  140 +
 plugins/external-editor/external-editor.c          |  153 +-
 plugins/mail-to-task/mail-to-task.c                |   14 +-
 .../mailing-list-actions/mailing-list-actions.c    |   51 +-
 plugins/templates/templates.c                      |   93 +-
 po/POTFILES.in                                     |    4 +-
 shell/e-shell.c                                    |    4 +-
 shell/main.c                                       |    4 +-
 ui/evolution-mail.ui                               |    1 +
 web-extensions/Makefile.am                         |   44 +
 web-extensions/e-dom-utils.c                       | 2022 +++
 web-extensions/e-dom-utils.h                       |  169 +
 web-extensions/e-web-extension-main.c              |   61 +
 web-extensions/e-web-extension-names.h             |   26 +
 web-extensions/e-web-extension.c                   |  999 ++
 web-extensions/e-web-extension.h                   |   72 +
 214 files changed, 59038 insertions(+), 38043 deletions(-)
---
diff --git a/Makefile.am b/Makefile.am
index 8d65a34..addfd44 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -71,6 +71,7 @@ SUBDIRS =                     \
        mail                    \
        calendar                \
        art                     \
+       web-extensions          \
        plugins                 \
        modules                 \
        $(MAINT_SUBDIR)         \
diff --git a/addressbook/gui/contact-editor/e-contact-editor.c 
b/addressbook/gui/contact-editor/e-contact-editor.c
index 488fc32..4704528 100644
--- a/addressbook/gui/contact-editor/e-contact-editor.c
+++ b/addressbook/gui/contact-editor/e-contact-editor.c
@@ -995,7 +995,7 @@ fill_in_email (EContactEditor *editor)
                email_location = eab_get_email_type_index (attr);
                slot = get_ui_slot (attr);
                if (slot < 1)
-                       slot = EMAIL_SLOTS + 1; //add at the end
+                       slot = EMAIL_SLOTS + 1; /* add at the end */
 
                gtk_list_store_append (data_store, &iter);
                gtk_list_store_set (data_store, &iter,
@@ -2103,7 +2103,7 @@ fill_in_im (EContactEditor *editor)
 
                slot = get_ui_slot (attr);
                if (slot < 0)
-                       slot = IM_SLOTS + 1; //attach at the end
+                       slot = IM_SLOTS + 1; /* attach at the end */
 
                gtk_list_store_append (data_store, &iter);
                gtk_list_store_set (data_store, &iter,
diff --git a/addressbook/gui/widgets/eab-contact-display.c b/addressbook/gui/widgets/eab-contact-display.c
index cd7fafd..8378563 100644
--- a/addressbook/gui/widgets/eab-contact-display.c
+++ b/addressbook/gui/widgets/eab-contact-display.c
@@ -28,7 +28,7 @@
 #include <string.h>
 #include <glib/gi18n.h>
 
-#include <webkit/webkit.h>
+#include <webkit2/webkit2.h>
 
 #include "e-contact-map.h"
 #include "eab-contact-formatter.h"
@@ -386,19 +386,31 @@ contact_display_object_requested (WebKitWebView *web_view,
 #endif
 
 static void
-contact_display_load_status_changed (WebKitWebView *web_view,
-                                     GParamSpec *pspec,
-                                     gpointer user_data)
+contact_display_load_changed (WebKitWebView *web_view,
+                              WebKitLoadEvent load_event,
+                              gpointer user_data)
 {
-       WebKitLoadStatus load_status;
-       WebKitDOMDocument *document;
+       GDBusProxy *web_extension;
+       GVariant* result;
 
-       load_status = webkit_web_view_get_load_status (web_view);
-       if (load_status != WEBKIT_LOAD_FINISHED)
+       if (load_event != WEBKIT_LOAD_FINISHED)
                return;
 
-       document = webkit_web_view_get_dom_document (web_view);
-       eab_contact_formatter_bind_dom (document);
+       web_extension = e_web_view_get_web_extension_proxy (E_WEB_VIEW (web_view));
+       if (web_extension) {
+               result = g_dbus_proxy_call_sync (
+                               web_extension,
+                               "EABContactFormatterBindDOM",
+                               g_variant_new (
+                                       "(t)",
+                                       webkit_web_view_get_page_id (web_view)),
+                               G_DBUS_CALL_FLAGS_NONE,
+                               -1,
+                               NULL, /* cancellable */
+                               NULL);
+               if (result)
+                       g_variant_unref (result);
+       }
 }
 
 static void
@@ -514,15 +526,12 @@ eab_contact_display_init (EABContactDisplay *display)
                G_CALLBACK (contact_display_object_requested), display);
 #endif
        e_signal_connect_notify (
-               web_view, "notify::load-status",
-               G_CALLBACK (contact_display_load_status_changed), NULL);
+               web_view, "notify::load-changed",
+               G_CALLBACK (contact_display_load_changed), NULL);
        g_signal_connect (
                web_view, "style-updated",
                G_CALLBACK (load_contact), NULL);
 
-       e_web_view_install_request_handler (E_WEB_VIEW (display), E_TYPE_FILE_REQUEST);
-       e_web_view_install_request_handler (E_WEB_VIEW (display), E_TYPE_STOCK_REQUEST);
-
        action_group = gtk_action_group_new ("internal-mailto");
        gtk_action_group_set_translation_domain (action_group, domain);
        gtk_ui_manager_insert_action_group (ui_manager, action_group, 0);
diff --git a/addressbook/gui/widgets/eab-contact-formatter.c b/addressbook/gui/widgets/eab-contact-formatter.c
index 4869811..d2e217b 100644
--- a/addressbook/gui/widgets/eab-contact-formatter.c
+++ b/addressbook/gui/widgets/eab-contact-formatter.c
@@ -1383,64 +1383,3 @@ eab_contact_formatter_format_contact (EABContactFormatter *formatter,
        else
                render_compact (formatter, contact, output_buffer);
 }
-
-static void
-collapse_contacts_list (WebKitDOMEventTarget *event_target,
-                        WebKitDOMEvent *event,
-                        gpointer user_data)
-{
-       WebKitDOMDocument *document;
-       WebKitDOMElement *list;
-       gchar *id, *list_id;
-       gchar *imagesdir, *src;
-       gboolean hidden;
-
-       document = user_data;
-       id = webkit_dom_element_get_id (WEBKIT_DOM_ELEMENT (event_target));
-
-       list_id = g_strconcat ("list-", id, NULL);
-       list = webkit_dom_document_get_element_by_id (document, list_id);
-       g_free (id);
-       g_free (list_id);
-
-       if (list == NULL)
-               return;
-
-       imagesdir = g_filename_to_uri (EVOLUTION_IMAGESDIR, NULL, NULL);
-       hidden = webkit_dom_html_element_get_hidden (WEBKIT_DOM_HTML_ELEMENT (list));
-
-       if (hidden)
-               src = g_strdup_printf ("evo-file://%s/minus.png", imagesdir);
-       else
-               src = g_strdup_printf ("evo-file://%s/plus.png", imagesdir);
-
-       webkit_dom_html_element_set_hidden (
-               WEBKIT_DOM_HTML_ELEMENT (list), !hidden);
-       webkit_dom_html_image_element_set_src (
-               WEBKIT_DOM_HTML_IMAGE_ELEMENT (event_target), src);
-
-       g_free (src);
-       g_free (imagesdir);
-}
-
-void
-eab_contact_formatter_bind_dom (WebKitDOMDocument *document)
-{
-       WebKitDOMNodeList *nodes;
-       gulong ii, length;
-
-       nodes = webkit_dom_document_get_elements_by_class_name (
-               document, "_evo_collapse_button");
-
-       length = webkit_dom_node_list_get_length (nodes);
-       for (ii = 0; ii < length; ii++) {
-               WebKitDOMNode *node;
-
-               node = webkit_dom_node_list_item (nodes, ii);
-               webkit_dom_event_target_add_event_listener (
-                       WEBKIT_DOM_EVENT_TARGET (node), "click",
-                       G_CALLBACK (collapse_contacts_list), FALSE, document);
-       }
-
-       g_object_unref (nodes);
-}
diff --git a/addressbook/gui/widgets/eab-contact-formatter.h b/addressbook/gui/widgets/eab-contact-formatter.h
index b4acc62..b691f31 100644
--- a/addressbook/gui/widgets/eab-contact-formatter.h
+++ b/addressbook/gui/widgets/eab-contact-formatter.h
@@ -75,7 +75,6 @@ void          eab_contact_formatter_format_contact
                                                (EABContactFormatter *formatter,
                                                 EContact *contact,
                                                 GString *output_buffer);
-void           eab_contact_formatter_bind_dom  (WebKitDOMDocument *document);
 
 G_END_DECLS
 
diff --git a/calendar/gui/e-comp-editor-page-attachments.c b/calendar/gui/e-comp-editor-page-attachments.c
index 776f3a2..5910c7a 100644
--- a/calendar/gui/e-comp-editor-page-attachments.c
+++ b/calendar/gui/e-comp-editor-page-attachments.c
@@ -177,17 +177,7 @@ ecep_attachments_attachment_loaded_cb (EAttachment *attachment,
        }
 
        if (!e_attachment_load_finish (attachment, result, &error)) {
-               GtkTreeRowReference *reference;
-
-               reference = e_attachment_get_reference (attachment);
-               if (gtk_tree_row_reference_valid (reference)) {
-                       GtkTreeModel *model;
-
-                       model = gtk_tree_row_reference_get_model (reference);
-
-                       e_attachment_store_remove_attachment (
-                               E_ATTACHMENT_STORE (model), attachment);
-               }
+               g_signal_emit_by_name (attachment, "load-failed", NULL);
 
                /* Ignore cancellations. */
                if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) {
diff --git a/calendar/gui/itip-utils.c b/calendar/gui/itip-utils.c
index 5a688b0..c873e99 100644
--- a/calendar/gui/itip-utils.c
+++ b/calendar/gui/itip-utils.c
@@ -1639,17 +1639,18 @@ find_enabled_identity (ESourceRegistry *registry,
        return mail_identity;
 }
 
-static void
-setup_from (ECalComponentItipMethod method,
-            ECalComponent *comp,
-            ECalClient *cal_client,
-            EComposerHeaderTable *table)
+static gchar *
+get_identity_uid_for_from (EShell *shell,
+                          ECalComponentItipMethod method,
+                          ECalComponent *comp,
+                          ECalClient *cal_client)
 {
        EClientCache *client_cache;
        ESourceRegistry *registry;
        ESource *source = NULL;
+       gchar *identity_uid = NULL;
 
-       client_cache = e_composer_header_table_ref_client_cache (table);
+       client_cache = e_shell_get_client_cache (shell);
        registry = e_client_cache_ref_registry (client_cache);
 
        /* always use organizer's email when user is an organizer */
@@ -1673,16 +1674,14 @@ setup_from (ECalComponentItipMethod method,
        }
 
        if (source != NULL) {
-               const gchar *uid;
-
-               uid = e_source_get_uid (source);
-               e_composer_header_table_set_identity_uid (table, uid);
+               identity_uid = g_strdup (e_source_get_uid (source));
 
                g_object_unref (source);
        }
 
-       g_object_unref (client_cache);
        g_object_unref (registry);
+
+       return identity_uid;
 }
 
 typedef struct {
@@ -1771,83 +1770,64 @@ itip_send_component_begin (ItipSendComponentData *isc,
        }
 }
 
+typedef struct _CreateComposerData {
+       gchar *identity_uid;
+       EDestination **destinations;
+       gchar *subject;
+       gchar *ical_string;
+       gchar *content_type;
+       gchar *event_body_text;
+       GSList *attachments_list;
+       ECalComponent *comp;
+       gboolean show_only;
+} CreateComposerData;
+
 static void
-itip_send_component_complete (ItipSendComponentData *isc)
+itip_send_component_composer_created_cb (GObject *source_object,
+                                        GAsyncResult *result,
+                                        gpointer user_data)
 {
-       GSettings *settings;
-       EMsgComposer *composer;
+       CreateComposerData *ccd = user_data;
        EComposerHeaderTable *table;
-       EDestination **destinations;
-       ECalComponent *comp = NULL;
-       icalcomponent *top_level = NULL;
-       icaltimezone *default_zone;
-       gchar *ical_string = NULL;
-       gchar *content_type = NULL;
-       gchar *subject = NULL;
+       EMsgComposer *composer;
+       GSettings *settings;
        gboolean use_24hour_format;
+       GError *error = NULL;
 
-       g_return_if_fail (isc != NULL);
+       g_return_if_fail (ccd != NULL);
 
-       if (isc->completed)
+       composer = e_msg_composer_new_finish (result, &error);
+       if (error) {
+               g_warning ("%s: Failed to create msg composer: %s", G_STRFUNC, error->message);
+               g_clear_error (&error);
                return;
-
-       isc->success = FALSE;
+       }
 
        settings = e_util_ref_settings ("org.gnome.evolution.calendar");
        use_24hour_format = g_settings_get_boolean (settings, "use-24hour-format");
        g_object_unref (settings);
 
-       default_zone = calendar_config_get_icaltimezone ();
-
-       /* Tidy up the comp */
-       comp = comp_compliant (
-               isc->registry, isc->method, isc->send_comp, isc->cal_client,
-               isc->zones, default_zone, isc->strip_alarms);
-
-       if (comp == NULL)
-               goto cleanup;
-
-       /* Recipients */
-       destinations = comp_to_list (
-               isc->registry, isc->method, comp, isc->users, FALSE,
-               isc->only_new_attendees ? g_object_get_data (
-               G_OBJECT (isc->send_comp), "new-attendees") : NULL);
-       if (isc->method != E_CAL_COMPONENT_METHOD_PUBLISH) {
-               if (destinations == NULL) {
-                       /* We sent them all via the server */
-                       isc->success = TRUE;
-                       goto cleanup;
-               }
-       }
-
-       /* Subject information */
-       subject = comp_subject (isc->registry, isc->method, comp);
-
-       composer = e_msg_composer_new (e_shell_get_default ());
        table = e_msg_composer_get_header_table (composer);
 
-       setup_from (isc->method, isc->send_comp, isc->cal_client, table);
-       e_composer_header_table_set_subject (table, subject);
-       e_composer_header_table_set_destinations_to (table, destinations);
-
-       e_destination_freev (destinations);
+       if (ccd->identity_uid)
+               e_composer_header_table_set_identity_uid (table, ccd->identity_uid);
 
-       /* Content type */
-       content_type = comp_content_type (comp, isc->method);
+       e_composer_header_table_set_subject (table, ccd->subject);
+       e_composer_header_table_set_destinations_to (table, ccd->destinations);
 
-       top_level = comp_toplevel_with_zones (isc->method, comp, isc->cal_client, isc->zones);
-       ical_string = icalcomponent_as_ical_string_r (top_level);
-
-       if (e_cal_component_get_vtype (comp) == E_CAL_COMPONENT_EVENT) {
-               e_msg_composer_set_body (composer, ical_string, content_type);
+       if (e_cal_component_get_vtype (ccd->comp) == E_CAL_COMPONENT_EVENT) {
+               if (ccd->event_body_text)
+                       e_msg_composer_set_body_text (composer, ccd->event_body_text, TRUE);
+               else
+                       e_msg_composer_set_body (composer, ccd->ical_string, ccd->content_type);
        } else {
                CamelMimePart *attachment;
                const gchar *filename;
                gchar *description;
                gchar *body;
 
-               filename = comp_filename (comp);
-               description = comp_description (comp, use_24hour_format);
+               filename = comp_filename (ccd->comp);
+               description = comp_description (ccd->comp, use_24hour_format);
 
                body = camel_text_to_html (description, CAMEL_MIME_FILTER_TOHTML_PRE, 0);
                e_msg_composer_set_body_text (composer, body, TRUE);
@@ -1855,8 +1835,8 @@ itip_send_component_complete (ItipSendComponentData *isc)
 
                attachment = camel_mime_part_new ();
                camel_mime_part_set_content (
-                       attachment, ical_string,
-                       strlen (ical_string), content_type);
+                       attachment, ccd->ical_string,
+                       strlen (ccd->ical_string), ccd->content_type);
                if (filename != NULL && *filename != '\0')
                        camel_mime_part_set_filename (attachment, filename);
                if (description != NULL && *description != '\0')
@@ -1868,23 +1848,89 @@ itip_send_component_complete (ItipSendComponentData *isc)
                g_free (description);
        }
 
-       append_cal_attachments (composer, comp, isc->attachments_list);
-       isc->attachments_list = NULL;
+       append_cal_attachments (composer, ccd->comp, ccd->attachments_list);
+       ccd->attachments_list = NULL;
 
-       if (isc->method == E_CAL_COMPONENT_METHOD_PUBLISH && !isc->users)
+       if (ccd->show_only)
                gtk_widget_show (GTK_WIDGET (composer));
        else
                e_msg_composer_send (composer);
 
+       e_destination_freev (ccd->destinations);
+       g_clear_object (&ccd->comp);
+       g_free (ccd->identity_uid);
+       g_free (ccd->subject);
+       g_free (ccd->ical_string);
+       g_free (ccd->content_type);
+       g_free (ccd->event_body_text);
+       g_free (ccd);
+}
+
+static void
+itip_send_component_complete (ItipSendComponentData *isc)
+{
+       CreateComposerData *ccd;
+       EDestination **destinations;
+       ECalComponent *comp = NULL;
+       EShell *shell;
+       icalcomponent *top_level = NULL;
+       icaltimezone *default_zone;
+
+       g_return_if_fail (isc != NULL);
+
+       if (isc->completed)
+               return;
+
+       isc->success = FALSE;
+
+       default_zone = calendar_config_get_icaltimezone ();
+
+       /* Tidy up the comp */
+       comp = comp_compliant (
+               isc->registry, isc->method, isc->send_comp, isc->cal_client,
+               isc->zones, default_zone, isc->strip_alarms);
+
+       if (comp == NULL)
+               goto cleanup;
+
+       /* Recipients */
+       destinations = comp_to_list (
+               isc->registry, isc->method, comp, isc->users, FALSE,
+               isc->only_new_attendees ? g_object_get_data (
+               G_OBJECT (isc->send_comp), "new-attendees") : NULL);
+       if (isc->method != E_CAL_COMPONENT_METHOD_PUBLISH) {
+               if (destinations == NULL) {
+                       /* We sent them all via the server */
+                       isc->success = TRUE;
+                       goto cleanup;
+               }
+       }
+
+       shell = e_shell_get_default ();
+       top_level = comp_toplevel_with_zones (isc->method, comp, isc->cal_client, isc->zones);
+
+       ccd = g_new0 (CreateComposerData, 1);
+       ccd->identity_uid = get_identity_uid_for_from (shell, isc->method, isc->send_comp, isc->cal_client);
+       ccd->destinations = destinations;
+       ccd->subject = comp_subject (isc->registry, isc->method, comp);
+       ccd->ical_string = icalcomponent_as_ical_string_r (top_level);
+       ccd->content_type = comp_content_type (comp, isc->method);
+       ccd->event_body_text = NULL;
+       ccd->attachments_list = isc->attachments_list;
+       ccd->comp = comp;
+       ccd->show_only = isc->method == E_CAL_COMPONENT_METHOD_PUBLISH && !isc->users;
+
+       isc->attachments_list = NULL;
+       comp = NULL;
+
+       e_msg_composer_new (shell, itip_send_component_composer_created_cb, ccd);
+
        isc->success = TRUE;
 
  cleanup:
        g_clear_object (&comp);
        if (top_level != NULL)
                icalcomponent_free (top_level);
-       g_free (content_type);
-       g_free (subject);
-       g_free (ical_string);
 }
 
 static void
@@ -2119,15 +2165,11 @@ reply_to_calendar_comp (ESourceRegistry *registry,
                         GSList *attachments_list)
 {
        EShell *shell;
-       EMsgComposer *composer;
-       EComposerHeaderTable *table;
-       EDestination **destinations;
        ECalComponent *comp = NULL;
        icalcomponent *top_level = NULL;
        icaltimezone *default_zone;
-       gchar *subject = NULL;
-       gchar *ical_string = NULL;
        gboolean retval = FALSE;
+       CreateComposerData *ccd;
 
        g_return_val_if_fail (E_IS_SOURCE_REGISTRY (registry), FALSE);
 
@@ -2143,24 +2185,15 @@ reply_to_calendar_comp (ESourceRegistry *registry,
        if (comp == NULL)
                goto cleanup;
 
-       /* Recipients */
-       destinations = comp_to_list (
-               registry, method, comp, NULL, reply_all, NULL);
-
-       /* Subject information */
-       subject = comp_subject (registry, method, comp);
-
-       composer = e_msg_composer_new (shell);
-       table = e_msg_composer_get_header_table (composer);
-
-       setup_from (method, send_comp, cal_client, table);
-       e_composer_header_table_set_subject (table, subject);
-       e_composer_header_table_set_destinations_to (table, destinations);
-
-       e_destination_freev (destinations);
-
        top_level = comp_toplevel_with_zones (method, comp, cal_client, zones);
-       ical_string = icalcomponent_as_ical_string_r (top_level);
+
+       ccd = g_new0 (CreateComposerData, 1);
+       ccd->identity_uid = get_identity_uid_for_from (shell, method, send_comp, cal_client);
+       ccd->destinations = comp_to_list (registry, method, comp, NULL, reply_all, NULL);
+       ccd->subject = comp_subject (registry, method, comp);
+       ccd->ical_string = icalcomponent_as_ical_string_r (top_level);
+       ccd->comp = comp;
+       ccd->show_only = TRUE;
 
        if (e_cal_component_get_vtype (comp) == E_CAL_COMPONENT_EVENT) {
 
@@ -2268,11 +2301,12 @@ reply_to_calendar_comp (ESourceRegistry *registry,
                g_string_append (body, html_description);
                g_free (html_description);
 
-               e_msg_composer_set_body_text (composer, body->str, TRUE);
-               g_string_free (body, TRUE);
+               ccd->event_body_text = g_string_free (body, FALSE);
        }
 
-       gtk_widget_show (GTK_WIDGET (composer));
+       comp = NULL;
+
+       e_msg_composer_new (shell, itip_send_component_composer_created_cb, ccd);
 
        retval = TRUE;
 
@@ -2283,8 +2317,6 @@ reply_to_calendar_comp (ESourceRegistry *registry,
        if (top_level != NULL)
                icalcomponent_free (top_level);
 
-       g_free (subject);
-       g_free (ical_string);
        return retval;
 }
 
diff --git a/composer/e-composer-actions.c b/composer/e-composer-actions.c
index 5995ef0..77e1b88 100644
--- a/composer/e-composer-actions.c
+++ b/composer/e-composer-actions.c
@@ -65,40 +65,57 @@ action_close_cb (GtkAction *action,
 }
 
 static void
+action_new_message_composer_created_cb (GObject *source_object,
+                                       GAsyncResult *result,
+                                       gpointer user_data)
+{
+       EMsgComposer *composer;
+       GError *error = NULL;
+
+       composer = e_msg_composer_new_finish (result, &error);
+       if (error) {
+               g_warning ("%s: Failed to create msg composer: %s", G_STRFUNC, error->message);
+               g_clear_error (&error);
+       } else {
+               gtk_widget_show (GTK_WIDGET (composer));
+       }
+}
+
+static void
 action_new_message_cb (GtkAction *action,
                        EMsgComposer *composer)
 {
-       EMsgComposer *new_composer;
        EShell *shell;
 
        shell = e_msg_composer_get_shell (composer);
 
-       new_composer = e_msg_composer_new (shell);
-       gtk_widget_show (GTK_WIDGET (new_composer));
+       e_msg_composer_new (shell, action_new_message_composer_created_cb, NULL);
 }
 
 static void
-action_pgp_encrypt_cb (GtkToggleAction *action,
-                       EMsgComposer *composer)
+composer_set_content_editor_changed (EMsgComposer *composer)
 {
        EHTMLEditor *editor;
-       EHTMLEditorView *view;
+       EContentEditor *cnt_editor;
 
        editor = e_msg_composer_get_editor (composer);
-       view = e_html_editor_get_view (editor);
-       e_html_editor_view_set_changed (view, TRUE);
+       cnt_editor = e_html_editor_get_content_editor (editor);
+       e_content_editor_set_changed (cnt_editor, TRUE);
+
+}
+
+static void
+action_pgp_encrypt_cb (GtkToggleAction *action,
+                       EMsgComposer *composer)
+{
+       composer_set_content_editor_changed (composer);
 }
 
 static void
 action_pgp_sign_cb (GtkToggleAction *action,
                     EMsgComposer *composer)
 {
-       EHTMLEditor *editor;
-       EHTMLEditorView *view;
-
-       editor = e_msg_composer_get_editor (composer);
-       view = e_html_editor_get_view (editor);
-       e_html_editor_view_set_changed (view, TRUE);
+       composer_set_content_editor_changed (composer);
 }
 
 static void
@@ -150,7 +167,6 @@ action_save_cb (GtkAction *action,
                 EMsgComposer *composer)
 {
        EHTMLEditor *editor;
-       EHTMLEditorView *view;
        const gchar *filename;
        gint fd;
        GError *error = NULL;
@@ -195,8 +211,7 @@ action_save_cb (GtkAction *action,
                return;
        }
 
-       view = e_html_editor_get_view (editor);
-       e_html_editor_view_set_changed (view, TRUE);
+       composer_set_content_editor_changed (composer);
 }
 
 static void
@@ -256,37 +271,14 @@ static void
 action_smime_encrypt_cb (GtkToggleAction *action,
                          EMsgComposer *composer)
 {
-       EHTMLEditor *editor;
-       EHTMLEditorView *view;
-
-       editor = e_msg_composer_get_editor (composer);
-       view = e_html_editor_get_view (editor);
-       e_html_editor_view_set_changed (view, TRUE);
+       composer_set_content_editor_changed (composer);
 }
 
 static void
 action_smime_sign_cb (GtkToggleAction *action,
                       EMsgComposer *composer)
 {
-       EHTMLEditor *editor;
-       EHTMLEditorView *view;
-
-       editor = e_msg_composer_get_editor (composer);
-       view = e_html_editor_get_view (editor);
-       e_html_editor_view_set_changed (view, TRUE);
-}
-
-static void
-action_unicode_smileys_cb (GtkToggleAction *action,
-                           EMsgComposer *composer)
-{
-       EHTMLEditor *editor;
-       EHTMLEditorView *view;
-
-       editor = e_msg_composer_get_editor (composer);
-       view = e_html_editor_get_view (editor);
-       e_html_editor_view_set_unicode_smileys (view,
-               gtk_toggle_action_get_active (action));
+       composer_set_content_editor_changed (composer);
 }
 
 static void
@@ -520,10 +512,10 @@ static GtkToggleActionEntry toggle_entries[] = {
 
        { "unicode-smileys",
          NULL,
-         N_("Unicode emoticons"),
+         N_("Unicode smilyes"),
          NULL,
-         N_("Use Unicode characters for emoticons."),
-         G_CALLBACK (action_unicode_smileys_cb),
+         N_("Use Unicode characters for smileys."),
+         NULL,  /* Handled by property bindings */
          FALSE },
 
        { "view-bcc",
@@ -566,14 +558,14 @@ e_composer_actions_init (EMsgComposer *composer)
        GtkAccelGroup *accel_group;
        GtkUIManager *ui_manager;
        EHTMLEditor *editor;
-       EHTMLEditorView *view;
+       EContentEditor *cnt_editor;
        gboolean visible;
        GIcon *gcr_gnupg_icon;
 
        g_return_if_fail (E_IS_MSG_COMPOSER (composer));
 
        editor = e_msg_composer_get_editor (composer);
-       view = e_html_editor_get_view (editor);
+       cnt_editor = e_html_editor_get_content_editor (editor);
        ui_manager = e_html_editor_get_ui_manager (editor);
 
        /* Composer Actions */
@@ -678,32 +670,32 @@ e_composer_actions_init (EMsgComposer *composer)
        }
 
        e_binding_bind_property (
-               view, "html-mode",
+               cnt_editor, "html-mode",
                ACTION (PICTURE_GALLERY), "sensitive",
                G_BINDING_SYNC_CREATE);
 
        e_binding_bind_property (
-               view, "editable",
+               cnt_editor, "editable",
                e_html_editor_get_action (editor, "edit-menu"), "sensitive",
                G_BINDING_SYNC_CREATE);
 
        e_binding_bind_property (
-               view, "editable",
+               cnt_editor, "editable",
                e_html_editor_get_action (editor, "format-menu"), "sensitive",
                G_BINDING_SYNC_CREATE);
 
        e_binding_bind_property (
-               view, "editable",
+               cnt_editor, "editable",
                e_html_editor_get_action (editor, "insert-menu"), "sensitive",
                G_BINDING_SYNC_CREATE);
 
        e_binding_bind_property (
-               view, "editable",
+               cnt_editor, "editable",
                e_html_editor_get_action (editor, "options-menu"), "sensitive",
                G_BINDING_SYNC_CREATE);
 
        e_binding_bind_property (
-               view, "editable",
+               cnt_editor, "editable",
                e_html_editor_get_action (editor, "picture-gallery"), "sensitive",
                G_BINDING_SYNC_CREATE);
 
diff --git a/composer/e-composer-private.c b/composer/e-composer-private.c
index 65b8dd5..2b20b28 100644
--- a/composer/e-composer-private.c
+++ b/composer/e-composer-private.c
@@ -64,14 +64,14 @@ static void
 composer_update_gallery_visibility (EMsgComposer *composer)
 {
        EHTMLEditor *editor;
-       EHTMLEditorView *view;
+       EContentEditor *cnt_editor;
        GtkToggleAction *toggle_action;
        gboolean gallery_active;
        gboolean is_html;
 
        editor = e_msg_composer_get_editor (composer);
-       view = e_html_editor_get_view (editor);
-       is_html = e_html_editor_view_get_html_mode (view);
+       cnt_editor = e_html_editor_get_content_editor (editor);
+       is_html = e_content_editor_get_html_mode (cnt_editor);
 
        toggle_action = GTK_TOGGLE_ACTION (ACTION (PICTURE_GALLERY));
        gallery_active = gtk_toggle_action_get_active (toggle_action);
@@ -94,7 +94,7 @@ e_composer_private_constructed (EMsgComposer *composer)
        EShell *shell;
        EClientCache *client_cache;
        EHTMLEditor *editor;
-       EHTMLEditorView *view;
+       EContentEditor *cnt_editor;
        GtkUIManager *ui_manager;
        GtkAction *action;
        GtkWidget *container;
@@ -109,7 +109,7 @@ e_composer_private_constructed (EMsgComposer *composer)
 
        editor = e_msg_composer_get_editor (composer);
        ui_manager = e_html_editor_get_ui_manager (editor);
-       view = e_html_editor_get_view (editor);
+       cnt_editor = e_html_editor_get_content_editor (editor);
 
        settings = e_util_ref_settings ("org.gnome.evolution.mail");
 
@@ -136,10 +136,9 @@ e_composer_private_constructed (EMsgComposer *composer)
        priv->saved_editable = FALSE;
        priv->drop_occured = FALSE;
        priv->dnd_is_uri = FALSE;
+       priv->dnd_history_saved = FALSE;
        priv->check_if_signature_is_changed = FALSE;
        priv->ignore_next_signature_change = FALSE;
-       priv->dnd_history_saved = FALSE;
-       priv->ignore_next_paste_clipboard_signals_emission = FALSE;
 
        priv->focused_entry = NULL;
 
@@ -214,7 +213,7 @@ e_composer_private_constructed (EMsgComposer *composer)
                E_COMPOSER_HEADER_TABLE (widget),
                E_COMPOSER_HEADER_SUBJECT);
        e_binding_bind_property (
-               view, "spell-checker",
+               cnt_editor, "spell-checker",
                header->input_widget, "spell-checker",
                G_BINDING_SYNC_CREATE);
 
@@ -233,7 +232,7 @@ e_composer_private_constructed (EMsgComposer *composer)
        gtk_widget_show (widget);
 
        e_binding_bind_property (
-               view, "editable",
+               cnt_editor, "editable",
                widget, "sensitive",
                G_BINDING_SYNC_CREATE);
 
@@ -255,11 +254,12 @@ e_composer_private_constructed (EMsgComposer *composer)
        priv->gallery_scrolled_window = g_object_ref (widget);
        gtk_widget_show (widget);
 
-       /* Reparent the scrolled window containing the web view
-        * widget into the content area of the top attachment pane. */
-
-       widget = GTK_WIDGET (view);
-       widget = gtk_widget_get_parent (widget);
+       widget = GTK_WIDGET (cnt_editor);
+       if (GTK_IS_SCROLLABLE (cnt_editor)) {
+               /* Scrollables are packed in a scrolled window */
+               widget = gtk_widget_get_parent (widget);
+               g_warn_if_fail (GTK_IS_SCROLLED_WINDOW (widget));
+       }
        gtk_widget_reparent (widget, container);
 
        /* Construct the picture gallery. */
@@ -275,7 +275,7 @@ e_composer_private_constructed (EMsgComposer *composer)
        g_free (gallery_path);
 
        e_signal_connect_notify_swapped (
-               view, "notify::html-mode",
+               cnt_editor, "notify::html-mode",
                G_CALLBACK (composer_update_gallery_visibility), composer);
 
        g_signal_connect_swapped (
@@ -290,7 +290,6 @@ e_composer_private_constructed (EMsgComposer *composer)
        for (ii = 0; ii < E_COMPOSER_NUM_HEADERS; ii++) {
                EComposerHeaderTable *table;
                EComposerHeader *header;
-               GtkAction *action;
 
                table = E_COMPOSER_HEADER_TABLE (priv->header_table);
                header = e_composer_header_table_get_header (table, ii);
@@ -520,6 +519,7 @@ e_composer_paste_image (EMsgComposer *composer,
                e_attachment_load_handle_error, composer);
        g_object_unref (attachment);
 
+
        g_free (uri);
 
        return TRUE;
@@ -670,7 +670,7 @@ composer_load_signature_cb (EMailSignatureComboBox *combo_box,
        gboolean is_html;
        GError *error = NULL;
        EHTMLEditor *editor;
-       EHTMLEditorView *view;
+       EContentEditor *cnt_editor;
 
        e_mail_signature_combo_box_load_selected_finish (
                combo_box, result, &contents, &length, &is_html, &error);
@@ -690,10 +690,10 @@ composer_load_signature_cb (EMailSignatureComboBox *combo_box,
        }
 
        editor = e_msg_composer_get_editor (composer);
-       view = e_html_editor_get_view (editor);
+       cnt_editor = e_html_editor_get_content_editor (editor);
 
-       new_signature_id = e_html_editor_view_insert_signature (
-               view,
+       new_signature_id = e_content_editor_insert_signature (
+               cnt_editor,
                contents,
                is_html,
                gtk_combo_box_get_active_id (GTK_COMBO_BOX (combo_box)),
@@ -710,13 +710,11 @@ composer_load_signature_cb (EMailSignatureComboBox *combo_box,
 }
 
 static void
-html_editor_view_is_ready_cb (EHTMLEditorView *view,
-                              EMsgComposer *composer)
+content_editor_load_finished_cb (EContentEditor *cnt_editor,
+                                 EMsgComposer *composer)
 {
        g_signal_handlers_disconnect_by_func (
-               view,
-               G_CALLBACK (html_editor_view_is_ready_cb),
-               composer);
+               cnt_editor, G_CALLBACK (content_editor_load_finished_cb), composer);
 
        e_composer_update_signature (composer);
 }
@@ -727,7 +725,7 @@ e_composer_update_signature (EMsgComposer *composer)
        EComposerHeaderTable *table;
        EMailSignatureComboBox *combo_box;
        EHTMLEditor *editor;
-       EHTMLEditorView *view;
+       EContentEditor *cnt_editor;
 
        g_return_if_fail (E_IS_MSG_COMPOSER (composer));
 
@@ -739,13 +737,13 @@ e_composer_update_signature (EMsgComposer *composer)
        table = e_msg_composer_get_header_table (composer);
        combo_box = e_composer_header_table_get_signature_combo_box (table);
        editor = e_msg_composer_get_editor (composer);
-       view = e_html_editor_get_view (editor);
+       cnt_editor = e_html_editor_get_content_editor (editor);
 
-       if (!e_html_editor_view_is_ready (view)) {
+       if (!e_content_editor_is_ready (cnt_editor)) {
                g_signal_connect (
-                       view, "is-ready",
-                       G_CALLBACK (html_editor_view_is_ready_cb), composer);
-
+                       cnt_editor, "load-finished",
+                       G_CALLBACK (content_editor_load_finished_cb),
+                       composer);
                return;
        }
 
diff --git a/composer/e-composer-private.h b/composer/e-composer-private.h
index 9a90499..b2c50df 100644
--- a/composer/e-composer-private.h
+++ b/composer/e-composer-private.h
@@ -95,17 +95,17 @@ struct _EMsgComposerPrivate {
        gboolean busy;
        gboolean disable_signature;
        gboolean is_from_draft;
+       gboolean is_from_new_message;
        /* The web view is uneditable while the editor is busy.
         * This is used to restore the previous editable state. */
        gboolean saved_editable;
        gboolean set_signature_from_message;
        gboolean drop_occured;
        gboolean dnd_is_uri;
-       gboolean check_if_signature_is_changed;
-       gboolean ignore_next_signature_change;
        gboolean is_sending_message;
        gboolean dnd_history_saved;
-       gboolean ignore_next_paste_clipboard_signals_emission;
+       gboolean check_if_signature_is_changed;
+       gboolean ignore_next_signature_change;
        gboolean last_signal_was_paste_primary;
 
        gint focused_entry_selection_start;
diff --git a/composer/e-msg-composer.c b/composer/e-msg-composer.c
index c472fcc..713d02a 100644
--- a/composer/e-msg-composer.c
+++ b/composer/e-msg-composer.c
@@ -133,7 +133,7 @@ static void add_attachments_from_multipart  (EMsgComposer *composer,
                                                 gboolean just_inlines,
                                                 gint depth);
 
-/* used by e_msg_composer_new_with_message () */
+/* used by e_msg_composer_setup_with_message () */
 static void    handle_multipart                (EMsgComposer *composer,
                                                 CamelMultipart *multipart,
                                                 gboolean keep_signature,
@@ -1043,11 +1043,11 @@ composer_add_evolution_composer_mode_header (CamelMedium *medium,
 {
        gboolean html_mode;
        EHTMLEditor *editor;
-       EHTMLEditorView *view;
+       EContentEditor *cnt_editor;
 
        editor = e_msg_composer_get_editor (composer);
-       view = e_html_editor_get_view (editor);
-       html_mode = e_html_editor_view_get_html_mode (view);
+       cnt_editor = e_html_editor_get_content_editor (editor);
+       html_mode = e_content_editor_get_html_mode (cnt_editor);
 
        camel_medium_add_header (
                medium,
@@ -1262,12 +1262,18 @@ composer_build_message (EMsgComposer *composer,
        } else {
                gchar *text;
                EHTMLEditor *editor;
-               EHTMLEditorView *view;
+               EContentEditor *cnt_editor;
 
                editor = e_msg_composer_get_editor (composer);
-               view = e_html_editor_get_view (editor);
+               cnt_editor = e_html_editor_get_content_editor (editor);
                data = g_byte_array_new ();
-               text = e_html_editor_view_get_text_plain (view);
+
+               text = e_content_editor_get_content (
+                       cnt_editor,
+                       E_CONTENT_EDITOR_GET_TEXT_PLAIN |
+                       E_CONTENT_EDITOR_GET_PROCESSED,
+                       NULL, NULL);
+
                g_byte_array_append (data, (guint8 *) text, strlen (text));
                g_free (text);
 
@@ -1330,15 +1336,14 @@ composer_build_message (EMsgComposer *composer,
        if ((flags & COMPOSER_FLAG_HTML_CONTENT) != 0 ||
            (flags & COMPOSER_FLAG_SAVE_DRAFT) != 0) {
                gchar *text;
-               guint count;
                gsize length;
                gboolean pre_encode;
                EHTMLEditor *editor;
-               EHTMLEditorView *view;
-               GList *inline_images = NULL;
+               EContentEditor *cnt_editor;
+               GSList *inline_images_parts = NULL, *link;
 
                editor = e_msg_composer_get_editor (composer);
-               view = e_html_editor_get_view (editor);
+               cnt_editor = e_html_editor_get_content_editor (editor);
 
                data = g_byte_array_new ();
                if ((flags & COMPOSER_FLAG_SAVE_DRAFT) != 0) {
@@ -1350,10 +1355,19 @@ composer_build_message (EMsgComposer *composer,
                        composer_add_evolution_composer_mode_header (
                                CAMEL_MEDIUM (context->message), composer);
 
-                       text = e_html_editor_view_get_text_html_for_drafts_with_images (
-                               view, from_domain, &inline_images);
-               } else
-                       text = e_html_editor_view_get_text_html (view, from_domain, &inline_images);
+                       text = e_content_editor_get_content (
+                               cnt_editor,
+                               E_CONTENT_EDITOR_GET_TEXT_HTML |
+                               E_CONTENT_EDITOR_GET_INLINE_IMAGES,
+                               from_domain, &inline_images_parts);
+               } else {
+                       text = e_content_editor_get_content (
+                               cnt_editor,
+                               E_CONTENT_EDITOR_GET_TEXT_HTML |
+                               E_CONTENT_EDITOR_GET_PROCESSED |
+                               E_CONTENT_EDITOR_GET_INLINE_IMAGES,
+                               from_domain, &inline_images_parts);
+               }
 
                length = strlen (text);
                g_byte_array_append (data, (guint8 *) text, (guint) length);
@@ -1409,9 +1423,7 @@ composer_build_message (EMsgComposer *composer,
 
                /* If there are inlined images, construct a multipart/related
                 * containing the multipart/alternative and the images. */
-               count = g_list_length (inline_images);
-               if (count > 0) {
-                       guint ii;
+               if (inline_images_parts) {
                        CamelMultipart *html_with_images;
 
                        html_with_images = camel_multipart_new ();
@@ -1430,10 +1442,10 @@ composer_build_message (EMsgComposer *composer,
 
                        g_object_unref (body);
 
-                       for (ii = 0; ii < count; ii++) {
-                               CamelMimePart *part = g_list_nth_data (inline_images, ii);
-                               camel_multipart_add_part (
-                                       html_with_images, part);
+                       for (link = inline_images_parts; link; link = g_slist_next (link)) {
+                               CamelMimePart *part = link->data;
+
+                               camel_multipart_add_part (html_with_images, part);
                        }
 
                        context->top_level_part =
@@ -1442,7 +1454,7 @@ composer_build_message (EMsgComposer *composer,
                        context->top_level_part =
                                CAMEL_DATA_WRAPPER (body);
                }
-               g_list_free_full (inline_images, g_object_unref);
+               g_slist_free_full (inline_images_parts, g_object_unref);
        }
 
        /* If there are attachments, wrap what we've built so far
@@ -1553,18 +1565,26 @@ set_editor_text (EMsgComposer *composer,
                  gboolean set_signature)
 {
        EHTMLEditor *editor;
-       EHTMLEditorView *view;
+       EContentEditor *cnt_editor;
 
        g_return_if_fail (E_IS_MSG_COMPOSER (composer));
        g_return_if_fail (text != NULL);
 
        editor = e_msg_composer_get_editor (composer);
-       view = e_html_editor_get_view (editor);
+       cnt_editor = e_html_editor_get_content_editor (editor);
 
        if (is_html)
-               e_html_editor_view_set_text_html (view, text);
+               e_content_editor_insert_content (
+                       cnt_editor,
+                       text,
+                       E_CONTENT_EDITOR_INSERT_TEXT_HTML |
+                       E_CONTENT_EDITOR_INSERT_REPLACE_ALL);
        else
-               e_html_editor_view_set_text_plain (view, text);
+               e_content_editor_insert_content (
+                       cnt_editor,
+                       text,
+                       E_CONTENT_EDITOR_INSERT_TEXT_PLAIN |
+                       E_CONTENT_EDITOR_INSERT_REPLACE_ALL);
 
        if (set_signature)
                e_composer_update_signature (composer);
@@ -1581,10 +1601,10 @@ attachment_store_changed_cb (EMsgComposer *composer)
         * changes on close. */
        editor = e_msg_composer_get_editor (composer);
        if (editor) {
-               EHTMLEditorView *view;
+               EContentEditor *cnt_editor;
 
-               view = e_html_editor_get_view (editor);
-               e_html_editor_view_set_changed (view, TRUE);
+               cnt_editor = e_html_editor_get_content_editor (editor);
+               e_content_editor_set_changed (cnt_editor, TRUE);
        }
 }
 
@@ -1694,8 +1714,8 @@ msg_composer_paste_clipboard_targets_cb (GtkClipboard *clipboard,
                                          gint n_targets,
                                          EMsgComposer *composer)
 {
-       EHTMLEditor *editor = e_msg_composer_get_editor (composer);
-       EHTMLEditorView *editor_view = e_html_editor_get_view (editor);
+       EHTMLEditor *editor;
+       EContentEditor *cnt_editor;
 
        if (targets == NULL || n_targets < 0)
                return;
@@ -1708,62 +1728,55 @@ msg_composer_paste_clipboard_targets_cb (GtkClipboard *clipboard,
                return;
        }
 
-       if (!e_html_editor_view_get_html_mode (editor_view) &&
+       editor = e_msg_composer_get_editor (composer);
+       cnt_editor = e_html_editor_get_content_editor (editor);
+
+       if (!e_content_editor_get_html_mode (cnt_editor) &&
            gtk_targets_include_image (targets, n_targets, TRUE)) {
                e_composer_paste_image (composer, clipboard);
                return;
        }
 
-       composer->priv->ignore_next_paste_clipboard_signals_emission = TRUE;
-
-       g_signal_emit_by_name (
-               editor_view,
-               composer->priv->last_signal_was_paste_primary ?
-                       "paste-primary-clipboard" : "paste-clipboard");
+       if (composer->priv->last_signal_was_paste_primary) {
+               e_content_editor_paste_primary (cnt_editor);
+       } else
+               e_content_editor_paste (cnt_editor);
 }
 
-static void
-msg_composer_paste_primary_clipboard_cb (EHTMLEditorView *view,
+static gboolean
+msg_composer_paste_primary_clipboard_cb (EContentEditor *cnt_editor,
                                          EMsgComposer *composer)
 {
-       if (composer->priv->ignore_next_paste_clipboard_signals_emission)
-               composer->priv->ignore_next_paste_clipboard_signals_emission = FALSE;
-       else {
-               GtkClipboard *clipboard;
+       GtkClipboard *clipboard;
 
-               clipboard = gtk_clipboard_get (GDK_SELECTION_PRIMARY);
+       clipboard = gtk_clipboard_get (GDK_SELECTION_PRIMARY);
 
-               composer->priv->last_signal_was_paste_primary = TRUE;
+       composer->priv->last_signal_was_paste_primary = TRUE;
 
-               gtk_clipboard_request_targets (
-                       clipboard, (GtkClipboardTargetsReceivedFunc)
-                       msg_composer_paste_clipboard_targets_cb, composer);
+       gtk_clipboard_request_targets (
+               clipboard, (GtkClipboardTargetsReceivedFunc)
+               msg_composer_paste_clipboard_targets_cb, composer);
 
-               g_signal_stop_emission_by_name (view, "paste-primary-clipboard");
-       }
+       return TRUE;
 }
 
-static void
-msg_composer_paste_clipboard_cb (EHTMLEditorView *view,
+static gboolean
+msg_composer_paste_clipboard_cb (EContentEditor *cnt_editor,
                                  EMsgComposer *composer)
 {
-       if (composer->priv->ignore_next_paste_clipboard_signals_emission)
-               composer->priv->ignore_next_paste_clipboard_signals_emission = FALSE;
-       else {
-               GtkClipboard *clipboard;
+       GtkClipboard *clipboard;
 
-               clipboard = gtk_clipboard_get (GDK_SELECTION_CLIPBOARD);
+       clipboard = gtk_clipboard_get (GDK_SELECTION_CLIPBOARD);
 
-               composer->priv->last_signal_was_paste_primary = FALSE;
+       composer->priv->last_signal_was_paste_primary = FALSE;
 
-               gtk_clipboard_request_targets (
-                       clipboard, (GtkClipboardTargetsReceivedFunc)
-                       msg_composer_paste_clipboard_targets_cb, composer);
+       gtk_clipboard_request_targets (
+               clipboard, (GtkClipboardTargetsReceivedFunc)
+               msg_composer_paste_clipboard_targets_cb, composer);
 
-               g_signal_stop_emission_by_name (view, "paste-clipboard");
-       }
+       return TRUE;
 }
-
+#if 0 /* FIXME WK2 */
 static gboolean
 msg_composer_drag_motion_cb (GtkWidget *widget,
                              GdkDragContext *context,
@@ -1792,50 +1805,6 @@ msg_composer_drag_motion_cb (GtkWidget *widget,
        return FALSE;
 }
 
-static void
-insert_nbsp_history_event (EHTMLEditorView *editor_view,
-                           gboolean delete,
-                           guint x,
-                           guint y)
-{
-       EHTMLEditorViewHistoryEvent *event;
-       WebKitDOMDocument *document;
-       WebKitDOMDocumentFragment *fragment;
-
-       document = webkit_web_view_get_dom_document (WEBKIT_WEB_VIEW (editor_view));
-
-       event = g_new0 (EHTMLEditorViewHistoryEvent, 1);
-       event->type = HISTORY_AND;
-       e_html_editor_view_insert_new_history_event (editor_view, event);
-
-       fragment = webkit_dom_document_create_document_fragment (document);
-       webkit_dom_node_append_child (
-               WEBKIT_DOM_NODE (fragment),
-               WEBKIT_DOM_NODE (
-                       webkit_dom_document_create_text_node (document, UNICODE_NBSP)),
-               NULL);
-
-       event = g_new0 (EHTMLEditorViewHistoryEvent, 1);
-       event->type = HISTORY_DELETE;
-
-       if (delete)
-               g_object_set_data (G_OBJECT (fragment), "history-delete-key", GINT_TO_POINTER (1));
-
-       event->data.fragment = fragment;
-
-       event->before.start.x = x;
-       event->before.start.y = y;
-       event->before.end.x = x;
-       event->before.end.y = y;
-
-       event->after.start.x = x;
-       event->after.start.y = y;
-       event->after.end.x = x;
-       event->after.end.y = y;
-
-       e_html_editor_view_insert_new_history_event (editor_view, event);
-}
-
 static gboolean
 msg_composer_drag_drop_cb (GtkWidget *widget,
                            GdkDragContext *context,
@@ -1855,195 +1824,21 @@ msg_composer_drag_drop_cb (GtkWidget *widget,
                EHTMLEditorView *editor_view = e_html_editor_get_view (editor);
 
                if ((gpointer) editor_view == (gpointer) source_widget) {
-                       EHTMLEditorSelection *selection;
-                       EHTMLEditorViewHistoryEvent *event;
-                       gboolean start_to_start, end_to_end;
-                       gchar *range_text;
-                       guint x, y;
-                       WebKitDOMDocument *document;
-                       WebKitDOMDocumentFragment *fragment;
-                       WebKitDOMDOMSelection *dom_selection;
-                       WebKitDOMDOMWindow *dom_window;
-                       WebKitDOMRange *beginning_of_line = NULL;
-                       WebKitDOMRange *range = NULL, *range_clone = NULL;
-
-                       selection = e_html_editor_view_get_selection (editor_view);
-                       document = webkit_web_view_get_dom_document (WEBKIT_WEB_VIEW (editor_view));
-
-                       if (!(dom_window = webkit_dom_document_get_default_view (document)))
-                               return FALSE;
-
-                       if (!(dom_selection = webkit_dom_dom_window_get_selection (dom_window))) {
-                               g_object_unref (dom_window);
-                               return FALSE;
-                       }
-
-                       if (webkit_dom_dom_selection_get_range_count (dom_selection) < 1) {
-                               g_object_unref (dom_selection);
-                               g_object_unref (dom_window);
-                               return FALSE;
-                       }
-
-                       /* Obtain the dragged content. */
-                       range = webkit_dom_dom_selection_get_range_at (dom_selection, 0, NULL);
-                       range_clone = webkit_dom_range_clone_range (range, NULL);
-
-                       /* Create the history event for the content that will
-                        * be removed by DnD. */
-                       event = g_new0 (EHTMLEditorViewHistoryEvent, 1);
-                       event->type = HISTORY_DELETE;
-
-                       e_html_editor_selection_get_selection_coordinates (
-                               selection,
-                               &event->before.start.x,
-                               &event->before.start.y,
-                               &event->before.end.x,
-                               &event->before.end.y);
-
-                       x = event->before.start.x;
-                       y = event->before.start.y;
-
-                       event->after.start.x = x;
-                       event->after.start.y = y;
-                       event->after.end.x = x;
-                       event->after.end.y = y;
-
-                       /* Save the content that will be removed. */
-                       fragment = webkit_dom_range_clone_contents (range_clone, NULL);
-
-                       /* Extend the cloned range to point one character after
-                        * the selection ends to later check if there is a whitespace
-                        * after it. */
-                       webkit_dom_range_set_end (
-                               range_clone,
-                               webkit_dom_range_get_end_container (range_clone, NULL),
-                               webkit_dom_range_get_end_offset (range_clone, NULL) + 1,
-                               NULL);
-                       range_text = webkit_dom_range_get_text (range_clone);
-
-                       /* Check if the current selection starts on the beginning
-                        * of line. */
-                       webkit_dom_dom_selection_modify (
-                               dom_selection, "extend", "left", "lineboundary");
-                       beginning_of_line = webkit_dom_dom_selection_get_range_at (dom_selection, 0, NULL);
-                       start_to_start = webkit_dom_range_compare_boundary_points (
-                               beginning_of_line, 0 /* START_TO_START */, range, NULL) == 0;
-
-                       /* Restore the selection to state before the check. */
-                       webkit_dom_dom_selection_remove_all_ranges (dom_selection);
-                       webkit_dom_dom_selection_add_range (dom_selection, range);
-                       g_object_unref (beginning_of_line);
-
-                       /* Check if the current selection end on the end of the line. */
-                       webkit_dom_dom_selection_modify (
-                               dom_selection, "extend", "right", "lineboundary");
-                       beginning_of_line = webkit_dom_dom_selection_get_range_at (dom_selection, 0, NULL);
-                       end_to_end = webkit_dom_range_compare_boundary_points (
-                               beginning_of_line, 2 /* END_TO_END */, range, NULL) == 0;
-
-                       /* Dragging the whole line. */
-                       if (start_to_start && end_to_end) {
-                               WebKitDOMNode *container, *actual_block, *tmp_block;
-
-                               /* Select the whole line (to the beginning of the next
-                                * one so we can reuse the undo code while undoing this.
-                                * Because of this we need to special mark the event
-                                * with history-drag-and-drop to correct the selection
-                                * after undoing it (otherwise the beginning of the next
-                                * line will be selected as well. */
-                               webkit_dom_dom_selection_modify (
-                                       dom_selection, "extend", "right", "character");
-                               g_object_unref (beginning_of_line);
-                               beginning_of_line = webkit_dom_dom_selection_get_range_at (dom_selection, 0, 
NULL);
-
-                               container = webkit_dom_range_get_end_container (range, NULL);
-                               actual_block = e_html_editor_get_parent_block_node_from_child (container);
-
-                               tmp_block = webkit_dom_range_get_end_container (beginning_of_line, NULL);
-                               if ((tmp_block = e_html_editor_get_parent_block_node_from_child (tmp_block))) 
{
-                                       e_html_editor_selection_get_selection_coordinates (
-                                               selection,
-                                               &event->before.start.x,
-                                               &event->before.start.y,
-                                               &event->before.end.x,
-                                               &event->before.end.y);
-
-                                       /* Create the right content for the history event. */
-                                       fragment = webkit_dom_document_create_document_fragment (document);
-                                       /* The removed line. */
-                                       webkit_dom_node_append_child (
-                                               WEBKIT_DOM_NODE (fragment),
-                                               webkit_dom_node_clone_node (actual_block, TRUE),
-                                               NULL);
-                                       /* The following block, but empty. */
-                                       webkit_dom_node_append_child (
-                                               WEBKIT_DOM_NODE (fragment),
-                                               webkit_dom_node_clone_node (tmp_block, FALSE),
-                                               NULL);
-                                       g_object_set_data (
-                                               G_OBJECT (fragment),
-                                               "history-drag-and-drop",
-                                               GINT_TO_POINTER (1));
-                                       /* It should act as a Delete key press. */
-                                       g_object_set_data (
-                                               G_OBJECT (fragment),
-                                               "history-delete-key",
-                                               GINT_TO_POINTER (1));
-                               }
-                       }
-
-                       event->data.fragment = fragment;
-                       e_html_editor_view_insert_new_history_event (editor_view, event);
-
-                       /* Selection is ending on the end of the line, check if
-                        * there is a space before the selection start. If so, it
-                        * will be removed and we need create the history event
-                        * for it. */
-                       if (end_to_end) {
-                               gchar *range_text_start;
-                               glong start_offset;
-
-                               start_offset = webkit_dom_range_get_start_offset (range_clone, NULL);
-                               webkit_dom_range_set_start (
-                                       range_clone,
-                                       webkit_dom_range_get_start_container (range_clone, NULL),
-                                       start_offset > 0 ? start_offset - 1 : 0,
+                       GDBusProxy *web_extension;
+
+                       web_extension = e_html_editor_view_get_web_extension_proxy (editor_view);
+                       if (web_extension) {
+                               g_dbus_proxy_call_sync (
+                                       web_extension,
+                                       "DOMSaveDragAndDropHistory",
+                                       g_variant_new (
+                                               "(t)",
+                                               webkit_web_view_get_page_id (WEBKIT_WEB_VIEW (editor_view))),
+                                       G_DBUS_CALL_FLAGS_NONE,
+                                       -1,
+                                       NULL,
                                        NULL);
-
-                               range_text_start = webkit_dom_range_get_text (range_clone);
-                               if (g_str_has_prefix (range_text_start, " ") ||
-                                   g_str_has_prefix (range_text_start, UNICODE_NBSP))
-                                       insert_nbsp_history_event (editor_view, FALSE, x, y);
-
-                               g_free (range_text_start);
                        }
-
-                       /* WebKit removes the space (if presented) after selection and
-                        * we need to create a new history event for it. */
-                       if (g_str_has_suffix (range_text, " ") ||
-                           g_str_has_suffix (range_text, UNICODE_NBSP))
-                               insert_nbsp_history_event (editor_view, TRUE, x, y);
-
-                       g_free (range_text);
-
-                       /* Restore the selection to original state. */
-                       webkit_dom_dom_selection_remove_all_ranges (dom_selection);
-                       webkit_dom_dom_selection_add_range (dom_selection, range);
-                       g_object_unref (beginning_of_line);
-
-                       /* All the things above were about removing the content,
-                        * create an AND event to continue later with inserting
-                        * the dropped content. */
-                       event = g_new0 (EHTMLEditorViewHistoryEvent, 1);
-                       event->type = HISTORY_AND;
-                       e_html_editor_view_insert_new_history_event (editor_view, event);
-
-                       g_object_unref (dom_selection);
-                       g_object_unref (dom_window);
-
-                       g_object_unref (range);
-                       g_object_unref (range_clone);
-
                        return FALSE;
                }
        }
@@ -2086,6 +1881,7 @@ msg_composer_drag_data_received_after_cb (GtkWidget *widget,
 {
        EHTMLEditor *editor;
        EHTMLEditorView *view;
+       GDBusProxy *web_extension;
 
        if (!composer->priv->drop_occured)
                goto out;
@@ -2096,8 +1892,20 @@ msg_composer_drag_data_received_after_cb (GtkWidget *widget,
 
        editor = e_msg_composer_get_editor (composer);
        view = e_html_editor_get_view (editor);
-       e_html_editor_view_save_history_for_drop (view);
-       e_html_editor_view_check_magic_links (view, FALSE);
+       web_extension = e_html_editor_view_get_web_extension_proxy (view);
+       if (!web_extension)
+               goto out;
+
+       g_dbus_proxy_call_sync (
+               web_extension,
+               "DOMCleanAfterDragAndDrop",
+               g_variant_new (
+                       "(t)",
+                       webkit_web_view_get_page_id (WEBKIT_WEB_VIEW (view))),
+               G_DBUS_CALL_FLAGS_NONE,
+               -1,
+               NULL,
+               NULL);
 
  out:
        composer->priv->drop_occured = FALSE;
@@ -2140,28 +1948,22 @@ msg_composer_drag_data_received_cb (GtkWidget *widget,
                                     EMsgComposer *composer)
 {
        EHTMLEditor *editor;
-       EHTMLEditorView *html_editor_view;
-       EHTMLEditorSelection *editor_selection;
+       EContentEditor *cnt_editor;
        gboolean html_mode, same_widget = FALSE;
        GtkWidget *source_widget;
 
        editor = e_msg_composer_get_editor (composer);
-       html_editor_view = e_html_editor_get_view (editor);
-       html_mode = e_html_editor_view_get_html_mode (html_editor_view);
-       editor_selection = e_html_editor_view_get_selection (html_editor_view);
+       cnt_editor = e_html_editor_get_content_editor (editor);
+       html_mode = e_content_editor_get_html_mode (cnt_editor);
 
        composer->priv->dnd_history_saved = TRUE;
 
        /* When we are doing DnD just inside the web view, the DnD is supposed
         * to move things around. */
        source_widget = gtk_drag_get_source_widget (context);
-       if (E_IS_HTML_EDITOR_VIEW (source_widget)) {
-               EHTMLEditor *editor = e_msg_composer_get_editor (composer);
-               EHTMLEditorView *editor_view = e_html_editor_get_view (editor);
-
-               if ((gpointer) editor_view == (gpointer) source_widget)
-                       same_widget = TRUE;
-       }
+       if (E_IS_CONTENT_EDITOR (source_widget) &&
+           ((gpointer) cnt_editor == (gpointer) source_widget))
+               same_widget = TRUE;
 
        /* Leave DnD inside the view on WebKit. */
        if (composer->priv->drop_occured && same_widget) {
@@ -2215,21 +2017,20 @@ msg_composer_drag_data_received_cb (GtkWidget *widget,
                        return;
                }
 
-               e_html_editor_selection_set_on_point (editor_selection, x, y);
+               e_content_editor_move_caret_on_coordinates (cnt_editor, x, y, FALSE);
 
                list_len = length;
                do {
                        text = next_uri ((guchar **) &data, &len, &list_len);
-                       e_html_editor_selection_insert_html (editor_selection, text);
+                       e_content_editor_insert_content (
+                               cnt_editor,
+                               text,
+                               E_CONTENT_EDITOR_INSERT_TEXT_HTML);
                        g_free (text);
                } while (list_len);
 
-               e_html_editor_view_check_magic_links (html_editor_view, FALSE);
-               e_html_editor_view_force_spell_check_in_viewport (html_editor_view);
-
-               e_html_editor_selection_scroll_to_caret (editor_selection);
-
                gtk_drag_finish (context, TRUE, FALSE, time);
+
                return;
        }
 
@@ -2253,38 +2054,38 @@ msg_composer_drag_data_received_cb (GtkWidget *widget,
                        return;
                }
 
-               e_html_editor_selection_set_on_point (editor_selection, x, y);
+               e_content_editor_move_caret_on_coordinates (cnt_editor, x, y, FALSE);
 
                list_len = length;
                do {
                        uri = next_uri ((guchar **) &data, &len, &list_len);
-                       e_html_editor_selection_insert_image (editor_selection, uri);
+                       e_content_editor_insert_image (cnt_editor, uri);
                        g_free (uri);
                } while (list_len);
 
                gtk_drag_finish (context, TRUE, FALSE, time);
        } else {
-               EAttachmentView *view = e_msg_composer_get_attachment_view (composer);
-
+               EAttachmentView *attachment_view =
+                       e_msg_composer_get_attachment_view (composer);
                /* Forward the data to the attachment view.  Note that calling
                 * e_attachment_view_drag_data_received() will not work because
                 * that function only handles the case where all the other drag
                 * handlers have failed. */
                e_attachment_paned_drag_data_received (
-                       E_ATTACHMENT_PANED (view),
+                       E_ATTACHMENT_PANED (attachment_view),
                        context, x, y, selection, info, time);
        }
 }
-
+#endif
 static void
 msg_composer_notify_header_cb (EMsgComposer *composer)
 {
+       EContentEditor *cnt_editor;
        EHTMLEditor *editor;
-       EHTMLEditorView *view;
 
        editor = e_msg_composer_get_editor (composer);
-       view = e_html_editor_get_view (editor);
-       e_html_editor_view_set_changed (view, TRUE);
+       cnt_editor = e_html_editor_get_content_editor (editor);
+       e_content_editor_set_changed (cnt_editor, TRUE);
 }
 
 static gboolean
@@ -2408,6 +2209,16 @@ msg_composer_quit_requested_cb (EShell *shell,
 }
 
 static void
+msg_composer_set_editor (EMsgComposer *composer,
+                        EHTMLEditor *editor)
+{
+       g_return_if_fail (E_IS_HTML_EDITOR (editor));
+       g_return_if_fail (composer->priv->editor == NULL);
+
+       composer->priv->editor = g_object_ref_sink (editor);
+}
+
+static void
 msg_composer_set_shell (EMsgComposer *composer,
                         EShell *shell)
 {
@@ -2427,6 +2238,12 @@ msg_composer_set_property (GObject *object,
                            GParamSpec *pspec)
 {
        switch (property_id) {
+               case PROP_EDITOR:
+                       msg_composer_set_editor (
+                               E_MSG_COMPOSER (object),
+                               g_value_get_object (value));
+                       return;
+
                case PROP_SHELL:
                        msg_composer_set_shell (
                                E_MSG_COMPOSER (object),
@@ -2450,6 +2267,12 @@ msg_composer_get_property (GObject *object,
                                E_MSG_COMPOSER (object)));
                        return;
 
+               case PROP_EDITOR:
+                       g_value_set_object (
+                               value, e_msg_composer_get_editor (
+                               E_MSG_COMPOSER (object)));
+                       return;
+
                case PROP_FOCUS_TRACKER:
                        g_value_set_object (
                                value, e_msg_composer_get_focus_tracker (
@@ -2514,9 +2337,8 @@ composer_notify_activity_cb (EActivityBar *activity_bar,
                              EMsgComposer *composer)
 {
        EHTMLEditor *editor;
-       EHTMLEditorView *view;
-       WebKitWebView *web_view;
-       gboolean editable;
+       EContentEditor *cnt_editor;
+       gboolean editable = TRUE;
        gboolean busy;
 
        busy = (e_activity_bar_get_activity (activity_bar) != NULL);
@@ -2530,16 +2352,15 @@ composer_notify_activity_cb (EActivityBar *activity_bar,
                e_msg_composer_save_focused_widget (composer);
 
        editor = e_msg_composer_get_editor (composer);
-       view = e_html_editor_get_view (editor);
-       web_view = WEBKIT_WEB_VIEW (view);
+       cnt_editor = e_html_editor_get_content_editor (editor);
 
        if (busy) {
-               editable = webkit_web_view_get_editable (web_view);
-               webkit_web_view_set_editable (web_view, FALSE);
+               editable = e_content_editor_is_editable (cnt_editor);
+               e_content_editor_set_editable (cnt_editor, FALSE);
                composer->priv->saved_editable = editable;
        } else {
                editable = composer->priv->saved_editable;
-               webkit_web_view_set_editable (web_view, editable);
+               e_content_editor_set_editable (cnt_editor, editable);
        }
 
        g_object_notify (G_OBJECT (composer), "busy");
@@ -2554,11 +2375,11 @@ msg_composer_constructed (GObject *object)
        EShell *shell;
        EMsgComposer *composer;
        EActivityBar *activity_bar;
-       EAttachmentView *view;
+       EAttachmentView *attachment_view;
        EAttachmentStore *store;
        EComposerHeaderTable *table;
        EHTMLEditor *editor;
-       EHTMLEditorView *html_editor_view;
+       EContentEditor *cnt_editor;
        GtkUIManager *ui_manager;
        GtkToggleAction *action;
        GtkTargetList *target_list;
@@ -2568,16 +2389,21 @@ msg_composer_constructed (GObject *object)
        const gchar *id;
        gboolean active;
 
+       /* Chain up to parent's constructed() method. */
+       G_OBJECT_CLASS (e_msg_composer_parent_class)->constructed (object);
+
        composer = E_MSG_COMPOSER (object);
 
+       g_return_if_fail (E_IS_HTML_EDITOR (composer->priv->editor));
+
        shell = e_msg_composer_get_shell (composer);
 
        e_composer_private_constructed (composer);
 
        editor = e_msg_composer_get_editor (composer);
-       html_editor_view = e_html_editor_get_view (editor);
+       cnt_editor = e_html_editor_get_content_editor (editor);
        ui_manager = e_html_editor_get_ui_manager (editor);
-       view = e_msg_composer_get_attachment_view (composer);
+       attachment_view = e_msg_composer_get_attachment_view (composer);
        table = E_COMPOSER_HEADER_TABLE (composer->priv->header_table);
 
        gtk_window_set_title (GTK_WINDOW (composer), _("Compose Message"));
@@ -2625,40 +2451,45 @@ msg_composer_constructed (GObject *object)
        gtk_toggle_action_set_active (action, active);
 
        action = GTK_TOGGLE_ACTION (ACTION (UNICODE_SMILEYS));
-       active = g_settings_get_boolean (settings, "composer-unicode-smileys");
-       gtk_toggle_action_set_active (action, active);
+       g_settings_bind (settings, "composer-unicode-smileys",
+                        action, "active",
+                        G_SETTINGS_BIND_DEFAULT);
+
        g_object_unref (settings);
 
        /* Clipboard Support */
 
        g_signal_connect (
-               html_editor_view, "paste-clipboard",
+               cnt_editor, "paste-clipboard",
                G_CALLBACK (msg_composer_paste_clipboard_cb), composer);
 
        g_signal_connect (
-               html_editor_view, "paste-primary-clipboard",
+               cnt_editor, "paste-primary-clipboard",
                G_CALLBACK (msg_composer_paste_primary_clipboard_cb), composer);
 
-       e_html_editor_view_reconnect_paste_clipboard_signals (html_editor_view);
        /* Drag-and-Drop Support */
+#if 0 /* FIXME WK2 */
+       EHTMLEditorView *view;
+
+       view = e_html_editor_get_view (editor);
 
        g_signal_connect (
-               html_editor_view, "drag-motion",
+               view, "drag-motion",
                G_CALLBACK (msg_composer_drag_motion_cb), composer);
 
-       g_signal_connect (
-               html_editor_view, "drag-drop",
+        g_signal_connect (
+               view, "drag-drop",
                G_CALLBACK (msg_composer_drag_drop_cb), composer);
 
        g_signal_connect (
-               html_editor_view, "drag-data-received",
+               view, "drag-data-received",
                G_CALLBACK (msg_composer_drag_data_received_cb), composer);
 
        /* Used for fixing various stuff after WebKit processed the DnD data. */
        g_signal_connect_after (
-               html_editor_view, "drag-data-received",
+               view, "drag-data-received",
                G_CALLBACK (msg_composer_drag_data_received_after_cb), composer);
-
+#endif
        g_signal_connect (
                composer->priv->gallery_icon_view, "drag-data-get",
                G_CALLBACK (msg_composer_gallery_drag_data_get), NULL);
@@ -2694,7 +2525,7 @@ msg_composer_constructed (GObject *object)
 
        /* Attachments */
 
-       store = e_attachment_view_get_store (view);
+       store = e_attachment_view_get_store (attachment_view);
 
        g_signal_connect_swapped (
                store, "row-deleted",
@@ -2705,12 +2536,12 @@ msg_composer_constructed (GObject *object)
                G_CALLBACK (attachment_store_changed_cb), composer);
 
        /* Initialization may have tripped the "changed" state. */
-       e_html_editor_view_set_changed (html_editor_view, FALSE);
+       e_content_editor_set_changed (cnt_editor, FALSE);
 
-       target_list = e_attachment_view_get_target_list (view);
+       target_list = e_attachment_view_get_target_list (attachment_view);
        targets = gtk_target_table_new_from_list (target_list, &n_targets);
 
-       target_list = gtk_drag_dest_get_target_list (GTK_WIDGET (html_editor_view));
+       target_list = gtk_drag_dest_get_target_list (GTK_WIDGET (cnt_editor));
 
        gtk_target_list_add_table (target_list, drag_dest_targets, G_N_ELEMENTS (drag_dest_targets));
        gtk_target_list_add_table (target_list, targets, n_targets);
@@ -2724,8 +2555,6 @@ msg_composer_constructed (GObject *object)
        e_extensible_load_extensions (E_EXTENSIBLE (composer));
 
        e_msg_composer_set_body_text (composer, "", TRUE);
-       /* Chain up to parent's constructed() method. */
-       G_OBJECT_CLASS (e_msg_composer_parent_class)->constructed (object);
 }
 
 static void
@@ -2782,7 +2611,7 @@ msg_composer_map (GtkWidget *widget)
        EComposerHeaderTable *table;
        GtkWidget *input_widget;
        EHTMLEditor *editor;
-       EHTMLEditorView *view;
+       EContentEditor *cnt_editor;
        const gchar *text;
 
        /* Chain up to parent's map() method. */
@@ -2813,8 +2642,8 @@ msg_composer_map (GtkWidget *widget)
        }
 
        /* Jump to the editor as a last resort. */
-       view = e_html_editor_get_view (editor);
-       gtk_widget_grab_focus (GTK_WIDGET (view));
+       cnt_editor = e_html_editor_get_content_editor (editor);
+       gtk_widget_grab_focus (GTK_WIDGET (cnt_editor));
 }
 
 static gboolean
@@ -2824,11 +2653,11 @@ msg_composer_key_press_event (GtkWidget *widget,
        EMsgComposer *composer;
        GtkWidget *input_widget;
        EHTMLEditor *editor;
-       EHTMLEditorView *view;
+       EContentEditor *cnt_editor;
 
        composer = E_MSG_COMPOSER (widget);
        editor = e_msg_composer_get_editor (composer);
-       view = e_html_editor_get_view (editor);
+       cnt_editor = e_html_editor_get_content_editor (editor);
 
        input_widget =
                e_composer_header_table_get_header (
@@ -2848,15 +2677,15 @@ msg_composer_key_press_event (GtkWidget *widget,
        }
 
        if (event->keyval == GDK_KEY_Tab && gtk_widget_is_focus (input_widget)) {
-               gtk_widget_grab_focus (GTK_WIDGET (view));
+               gtk_widget_grab_focus (GTK_WIDGET (cnt_editor));
                return TRUE;
        }
 
-       if (gtk_widget_is_focus (GTK_WIDGET (view))) {
+       if (gtk_widget_is_focus (GTK_WIDGET (cnt_editor))) {
                if (event->keyval == GDK_KEY_ISO_Left_Tab) {
                        gboolean view_processed = FALSE;
 
-                       g_signal_emit_by_name (view, "key-press-event", event, &view_processed);
+                       g_signal_emit_by_name (cnt_editor, "key-press-event", event, &view_processed);
 
                        if (!view_processed)
                                gtk_widget_grab_focus (input_widget);
@@ -2942,6 +2771,17 @@ e_msg_composer_class_init (EMsgComposerClass *class)
 
        g_object_class_install_property (
                object_class,
+               PROP_EDITOR,
+               g_param_spec_object (
+                       "editor",
+                       NULL,
+                       NULL,
+                       E_TYPE_HTML_EDITOR,
+                       G_PARAM_READWRITE |
+                       G_PARAM_CONSTRUCT_ONLY));
+
+       g_object_class_install_property (
+               object_class,
                PROP_FOCUS_TRACKER,
                g_param_spec_object (
                        "focus-tracker",
@@ -3019,31 +2859,87 @@ e_msg_composer_class_init (EMsgComposerClass *class)
 static void
 e_msg_composer_init (EMsgComposer *composer)
 {
-       EHTMLEditorView *view;
-
        composer->priv = E_MSG_COMPOSER_GET_PRIVATE (composer);
+}
 
-       composer->priv->editor = g_object_ref_sink (e_html_editor_new ());
-       view = e_html_editor_get_view (composer->priv->editor);
-       e_html_editor_view_set_is_editting_message (view, TRUE);
+static void
+e_msg_composer_editor_created_cb (GObject *source_object,
+                                 GAsyncResult *result,
+                                 gpointer user_data)
+{
+       GtkWidget *editor;
+       ESimpleAsyncResult *eresult = user_data;
+       GError *error = NULL;
+
+       g_return_if_fail (E_IS_SIMPLE_ASYNC_RESULT (eresult));
+
+       editor = e_html_editor_new_finish (result, &error);
+       if (error) {
+               g_warning ("%s: Failed to create HTML editor: %s", G_STRFUNC, error->message);
+               g_clear_error (&error);
+       } else {
+               e_simple_async_result_set_op_pointer (eresult, editor);
+               e_simple_async_result_complete (eresult);
+       }
+
+       g_object_unref (eresult);
 }
 
 /**
  * e_msg_composer_new:
  * @shell: an #EShell
+ * @callback: called when the composer is ready
+ * @user_data: user data passed to @callback
+ *
+ * Asynchronously creates an #EMsgComposer. The operation is finished
+ * with e_msg_composer_new_finish() called from within the @callback.
+ *
+ * Since: 3.22
+ **/
+void
+e_msg_composer_new (EShell *shell,
+                   GAsyncReadyCallback callback,
+                   gpointer user_data)
+{
+       ESimpleAsyncResult *eresult;
+
+       g_return_if_fail (E_IS_SHELL (shell));
+       g_return_if_fail (callback != NULL);
+
+       eresult = e_simple_async_result_new (NULL, callback, user_data, e_msg_composer_new);
+       e_simple_async_result_set_user_data (eresult, g_object_ref (shell), g_object_unref);
+
+       e_html_editor_new (e_msg_composer_editor_created_cb, eresult);
+}
+
+/**
+ * e_msg_composer_new_finish:
+ * @result: a #GAsyncResult provided by the callback from e_msg_composer_new()
+ * @error: optional #GError for errors
  *
- * Create a new message composer widget.
+ * Finishes call of e_msg_composer_new().
  *
- * Returns: A pointer to the newly created widget
+ * Since: 3.22
  **/
 EMsgComposer *
-e_msg_composer_new (EShell *shell)
+e_msg_composer_new_finish (GAsyncResult *result,
+                          GError **error)
 {
-       g_return_val_if_fail (E_IS_SHELL (shell), NULL);
+       ESimpleAsyncResult *eresult;
+       EHTMLEditor *html_editor;
+
+       g_return_val_if_fail (E_IS_SIMPLE_ASYNC_RESULT (result), NULL);
+       g_return_val_if_fail (g_async_result_is_tagged (result, e_msg_composer_new), NULL);
+
+       eresult = E_SIMPLE_ASYNC_RESULT (result);
+
+       html_editor = e_simple_async_result_get_op_pointer (eresult);
+       g_return_val_if_fail (E_IS_HTML_EDITOR (html_editor), NULL);
 
-       return g_object_new (
-               E_TYPE_MSG_COMPOSER,
-               "shell", shell, NULL);
+       return g_object_new (E_TYPE_MSG_COMPOSER,
+               "shell", e_simple_async_result_get_user_data (eresult),
+               "editor", html_editor,
+               NULL);
 }
 
 /**
@@ -3110,7 +3006,7 @@ add_attachments_handle_mime_part (EMsgComposer *composer,
        CamelContentType *content_type;
        CamelDataWrapper *wrapper;
        EHTMLEditor *editor;
-       EHTMLEditorView *view;
+       EContentEditor *cnt_editor;
 
        if (!mime_part)
                return;
@@ -3118,7 +3014,7 @@ add_attachments_handle_mime_part (EMsgComposer *composer,
        content_type = camel_mime_part_get_content_type (mime_part);
        wrapper = camel_medium_get_content (CAMEL_MEDIUM (mime_part));
        editor = e_msg_composer_get_editor (composer);
-       view = e_html_editor_get_view (editor);
+       cnt_editor = e_html_editor_get_content_editor (editor);
 
        if (CAMEL_IS_MULTIPART (wrapper)) {
                /* another layer of multipartness... */
@@ -3128,10 +3024,10 @@ add_attachments_handle_mime_part (EMsgComposer *composer,
        } else if (just_inlines) {
                if (camel_mime_part_get_content_id (mime_part) ||
                    camel_mime_part_get_content_location (mime_part))
-                       e_html_editor_view_add_inline_image_from_mime_part (
-                               view, mime_part);
+                       e_content_editor_insert_image_from_mime_part (
+                               cnt_editor, mime_part);
        } else if (related && camel_content_type_is (content_type, "image", "*")) {
-               e_html_editor_view_add_inline_image_from_mime_part (view, mime_part);
+               e_content_editor_insert_image_from_mime_part (cnt_editor, mime_part);
        } else if (camel_content_type_is (content_type, "text", "*") &&
                camel_mime_part_get_filename (mime_part) == NULL) {
                /* Do nothing if this is a text/anything without a
@@ -3525,56 +3421,26 @@ handle_multipart (EMsgComposer *composer,
                        }
 
                } else if (depth == 0 && i == 0) {
-                       EHTMLEditor *editor;
-                       gboolean is_message_from_draft, is_html = FALSE;
                        gchar *html = NULL;
                        gssize length = 0;
 
-                       editor = e_msg_composer_get_editor (composer);
-                       is_message_from_draft = e_html_editor_view_is_message_from_draft (
-                               e_html_editor_get_view (editor));
-                       is_html = camel_content_type_is (content_type, "text", "html");
-
                        /* Since the first part is not multipart/alternative,
                         * this must be the body. */
+                       html = emcu_part_to_html (
+                               composer, mime_part, &length, keep_signature, cancellable);
 
-                       /* If we are opening message from Drafts */
-                       if (is_message_from_draft) {
-                               /* Extract the body */
-                               CamelDataWrapper *dw;
-
-                               dw = camel_medium_get_content ((CamelMedium *) mime_part);
-                               if (dw) {
-                                       CamelStream *mem = camel_stream_mem_new ();
-                                       GByteArray *bytes;
-
-                                       camel_data_wrapper_decode_to_stream_sync (dw, mem, cancellable, NULL);
-                                       camel_stream_close (mem, cancellable, NULL);
-
-                                       bytes = camel_stream_mem_get_byte_array (CAMEL_STREAM_MEM (mem));
-                                       if (bytes && bytes->len)
-                                               html = g_strndup ((const gchar *) bytes->data, bytes->len);
-
-                                       g_object_unref (mem);
-                               }
-                       } else {
-                               is_html = TRUE;
-                               html = emcu_part_to_html (
-                                       composer, mime_part, &length, keep_signature, cancellable);
-                       }
-
-                       if (html)
-                               e_msg_composer_set_pending_body (composer, html, length, is_html);
+                       e_msg_composer_set_pending_body (composer, html, length, TRUE);
 
                } else if (camel_mime_part_get_content_id (mime_part) ||
                           camel_mime_part_get_content_location (mime_part)) {
                        /* special in-line attachment */
                        EHTMLEditor *editor;
+                       EContentEditor *cnt_editor;
 
                        editor = e_msg_composer_get_editor (composer);
-                       e_html_editor_view_add_inline_image_from_mime_part (
-                               e_html_editor_get_view (editor), mime_part);
+                       cnt_editor = e_html_editor_get_content_editor (editor);
 
+                       e_content_editor_insert_image_from_mime_part (cnt_editor, mime_part);
                } else {
                        /* normal attachment */
                        e_msg_composer_attach (composer, mime_part);
@@ -3586,9 +3452,7 @@ static void
 set_signature_gui (EMsgComposer *composer)
 {
        EHTMLEditor *editor;
-       EHTMLEditorView *view;
-       WebKitDOMDocument *document;
-       WebKitDOMElement *element;
+       EContentEditor *cnt_editor;
        EComposerHeaderTable *table;
        EMailSignatureComboBox *combo_box;
        gchar *uid = NULL;
@@ -3597,16 +3461,11 @@ set_signature_gui (EMsgComposer *composer)
        combo_box = e_composer_header_table_get_signature_combo_box (table);
 
        editor = e_msg_composer_get_editor (composer);
-       view = e_html_editor_get_view (editor);
-       document = webkit_web_view_get_dom_document (WEBKIT_WEB_VIEW (view));
-
-       if ((element = webkit_dom_document_query_selector (document, ".-x-evo-signature[id]", NULL)))
-               uid = webkit_dom_element_get_id (element);
+       cnt_editor = e_html_editor_get_content_editor (editor);
 
-       /* The combo box active ID is the signature's ESource UID. */
-       if (uid != NULL) {
+       if ((uid = e_content_editor_get_current_signature_uid (cnt_editor))) {
+               /* The combo box active ID is the signature's ESource UID. */
                gtk_combo_box_set_active_id (GTK_COMBO_BOX (combo_box), uid);
-               g_free (uid);
        }
 }
 
@@ -3657,25 +3516,25 @@ composer_add_auto_recipients (ESource *source,
 }
 
 /**
- * e_msg_composer_new_with_message:
- * @shell: an #EShell
+ * e_msg_composer_setup_with_message:
+ * @composer: an #EMsgComposer
  * @message: The message to use as the source
  * @keep_signature: Keep message signature, if any
  * @override_identity_uid: (allow none): Optional identity UID to use, or %NULL
  * @cancellable: optional #GCancellable object, or %NULL
  *
- * Create a new message composer widget.
+ * Sets up the message @composer with a specific @message.
  *
  * Note: Designed to work only for messages constructed using Evolution.
  *
- * Returns: A pointer to the newly created widget
+ * Since: 3.22
  **/
-EMsgComposer *
-e_msg_composer_new_with_message (EShell *shell,
-                                 CamelMimeMessage *message,
-                                 gboolean keep_signature,
-                                const gchar *override_identity_uid,
-                                 GCancellable *cancellable)
+void
+e_msg_composer_setup_with_message (EMsgComposer *composer,
+                                  CamelMimeMessage *message,
+                                  gboolean keep_signature,
+                                  const gchar *override_identity_uid,
+                                  GCancellable *cancellable)
 {
        CamelInternetAddress *from, *to, *cc, *bcc;
        GList *To = NULL, *Cc = NULL, *Bcc = NULL, *postto = NULL;
@@ -3685,19 +3544,18 @@ e_msg_composer_new_with_message (EShell *shell,
        CamelContentType *content_type;
        struct _camel_header_raw *headers;
        CamelDataWrapper *content;
-       EMsgComposer *composer;
        EMsgComposerPrivate *priv;
        EComposerHeaderTable *table;
        ESource *source = NULL;
        EHTMLEditor *editor;
-       EHTMLEditorView *view;
+       EContentEditor *cnt_editor;
        GtkToggleAction *action;
        struct _camel_header_raw *xev;
        gchar *identity_uid;
        gint len, i;
        gboolean is_message_from_draft = FALSE;
 
-       g_return_val_if_fail (E_IS_SHELL (shell), NULL);
+       g_return_if_fail (E_IS_MSG_COMPOSER (composer));
 
        headers = CAMEL_MIME_PART (message)->headers;
        while (headers != NULL) {
@@ -3711,11 +3569,10 @@ e_msg_composer_new_with_message (EShell *shell,
                headers = headers->next;
        }
 
-       composer = e_msg_composer_new (shell);
        priv = E_MSG_COMPOSER_GET_PRIVATE (composer);
-       editor = e_msg_composer_get_editor (composer);
        table = e_msg_composer_get_header_table (composer);
-       view = e_html_editor_get_view (editor);
+       editor = e_msg_composer_get_editor (composer);
+       cnt_editor = e_html_editor_get_content_editor (editor);
 
        if (postto) {
                e_composer_header_table_set_post_to_list (table, postto);
@@ -3737,7 +3594,7 @@ e_msg_composer_new_with_message (EShell *shell,
                }
                if (!identity_uid) {
                        source = em_utils_guess_mail_identity_with_recipients (
-                               e_shell_get_registry (shell), message, NULL, NULL);
+                               e_shell_get_registry (e_msg_composer_get_shell (composer)), message, NULL, 
NULL);
                        if (source)
                                identity_uid = e_source_dup_uid (source);
                }
@@ -3878,10 +3735,8 @@ e_msg_composer_new_with_message (EShell *shell,
        composer_mode = camel_medium_get_header (
                CAMEL_MEDIUM (message), "X-Evolution-Composer-Mode");
 
-       if (composer_mode && *composer_mode) {
+       if (composer_mode && *composer_mode)
                is_message_from_draft = TRUE;
-               e_html_editor_view_set_is_message_from_draft (view, TRUE);
-       }
 
        if (format != NULL) {
                gchar **flags;
@@ -3891,22 +3746,12 @@ e_msg_composer_new_with_message (EShell *shell,
 
                flags = g_strsplit (format, ", ", 0);
                for (i = 0; flags[i]; i++) {
-                       if (g_ascii_strcasecmp (flags[i], "text/html") == 0) {
-                               if (composer_mode && g_ascii_strcasecmp (composer_mode, "text/html") == 0) {
-                                       e_html_editor_view_set_html_mode (
-                                               view, TRUE);
-                               } else {
-                                       e_html_editor_view_set_html_mode (
-                                               view, FALSE);
-                               }
-                       } else if (g_ascii_strcasecmp (flags[i], "text/plain") == 0) {
-                               if (composer_mode && g_ascii_strcasecmp (composer_mode, "text/html") == 0) {
-                                       e_html_editor_view_set_html_mode (
-                                               view, TRUE);
-                               } else {
-                                       e_html_editor_view_set_html_mode (
-                                               view, FALSE);
-                               }
+                       if (g_ascii_strcasecmp (flags[i], "text/html") == 0 ||
+                           g_ascii_strcasecmp (flags[i], "text/plain") == 0) {
+                               gboolean html_mode;
+
+                               html_mode = composer_mode && !g_ascii_strcasecmp (composer_mode, "text/html");
+                               e_content_editor_set_html_mode (cnt_editor, html_mode);
                        } else if (g_ascii_strcasecmp (flags[i], "pgp-sign") == 0) {
                                action = GTK_TOGGLE_ACTION (ACTION (PGP_SIGN));
                                gtk_toggle_action_set_active (action, TRUE);
@@ -4040,7 +3885,6 @@ e_msg_composer_new_with_message (EShell *shell,
                e_msg_composer_set_pending_body (composer, html, length, is_html);
        }
 
-       e_html_editor_view_set_is_message_from_edit_as_new (view, TRUE);
        priv->set_signature_from_message = TRUE;
 
        /* We wait until now to set the body text because we need to
@@ -4049,38 +3893,36 @@ e_msg_composer_new_with_message (EShell *shell,
        e_msg_composer_flush_pending_body (composer);
 
        set_signature_gui (composer);
-
-       return composer;
 }
 
 /**
- * e_msg_composer_new_redirect:
- * @shell: an #EShell
+ * e_msg_composer_setup_redirect:
+ * @composer: an #EMsgComposer
  * @message: The message to use as the source
+ * @identity_uid: (nullable): an identity UID to use, if any
+ * @cancellable: an optional #GCancellable
  *
- * Create a new message composer widget.
+ * Sets up the message @composer as a redirect of the @message.
  *
- * Returns: A pointer to the newly created widget
+ * Since: 3.22
  **/
-EMsgComposer *
-e_msg_composer_new_redirect (EShell *shell,
-                             CamelMimeMessage *message,
-                             const gchar *identity_uid,
-                             GCancellable *cancellable)
+void
+e_msg_composer_setup_redirect (EMsgComposer *composer,
+                              CamelMimeMessage *message,
+                              const gchar *identity_uid,
+                              GCancellable *cancellable)
 {
-       EMsgComposer *composer;
        EComposerHeaderTable *table;
        EHTMLEditor *editor;
-       EHTMLEditorView *view;
+       EContentEditor *cnt_editor;
        const gchar *subject;
 
-       g_return_val_if_fail (E_IS_SHELL (shell), NULL);
-       g_return_val_if_fail (CAMEL_IS_MIME_MESSAGE (message), NULL);
+       g_return_if_fail (E_IS_MSG_COMPOSER (composer));
+       g_return_if_fail (CAMEL_IS_MIME_MESSAGE (message));
 
-       composer = e_msg_composer_new_with_message (
-               shell, message, TRUE, identity_uid, cancellable);
-       table = e_msg_composer_get_header_table (composer);
+       e_msg_composer_setup_with_message (composer, message, TRUE, identity_uid, cancellable);
 
+       table = e_msg_composer_get_header_table (composer);
        subject = camel_mime_message_get_subject (message);
 
        composer->priv->redirect = message;
@@ -4089,10 +3931,8 @@ e_msg_composer_new_redirect (EShell *shell,
        e_composer_header_table_set_subject (table, subject);
 
        editor = e_msg_composer_get_editor (composer);
-       view = e_html_editor_get_view (editor);
-       webkit_web_view_set_editable (WEBKIT_WEB_VIEW (view), FALSE);
-
-       return composer;
+       cnt_editor = e_html_editor_get_content_editor (editor);
+       e_content_editor_set_editable (cnt_editor, FALSE);
 }
 
 /**
@@ -4149,7 +3989,7 @@ msg_composer_send_cb (EMsgComposer *composer,
        CamelMimeMessage *message;
        EAlertSink *alert_sink;
        EHTMLEditor *editor;
-       EHTMLEditorView *view;
+       EContentEditor *cnt_editor;
        GError *error = NULL;
 
        alert_sink = e_activity_get_alert_sink (context->activity);
@@ -4182,8 +4022,8 @@ msg_composer_send_cb (EMsgComposer *composer,
 
        /* The callback can set editor 'changed' if anything failed. */
        editor = e_msg_composer_get_editor (composer);
-       view = e_html_editor_get_view (editor);
-       e_html_editor_view_set_changed (view, TRUE);
+       cnt_editor = e_html_editor_get_content_editor (editor);
+       e_content_editor_set_changed (cnt_editor, TRUE);
 
        composer->priv->is_sending_message = TRUE;
 
@@ -4241,15 +4081,15 @@ msg_composer_save_to_drafts_done_cb (gpointer user_data,
 {
        EMsgComposer *composer = user_data;
        EHTMLEditor *editor;
-       EHTMLEditorView *view;
+       EContentEditor *cnt_editor;
 
        g_return_if_fail (E_IS_MSG_COMPOSER (composer));
 
        editor = e_msg_composer_get_editor (composer);
-       view = e_html_editor_get_view (editor);
+       cnt_editor = e_html_editor_get_content_editor (editor);
 
        if (e_msg_composer_is_exiting (composer) &&
-           !e_html_editor_view_get_changed (view)) {
+           !e_content_editor_get_changed (cnt_editor)) {
                gtk_widget_destroy (GTK_WIDGET (composer));
        } else if (e_msg_composer_is_exiting (composer)) {
                gtk_widget_set_sensitive (GTK_WIDGET (composer), TRUE);
@@ -4266,7 +4106,7 @@ msg_composer_save_to_drafts_cb (EMsgComposer *composer,
        CamelMimeMessage *message;
        EAlertSink *alert_sink;
        EHTMLEditor *editor;
-       EHTMLEditorView *view;
+       EContentEditor *cnt_editor;
        GError *error = NULL;
 
        alert_sink = e_activity_get_alert_sink (context->activity);
@@ -4308,8 +4148,8 @@ msg_composer_save_to_drafts_cb (EMsgComposer *composer,
 
        /* The callback can set editor 'changed' if anything failed. */
        editor = e_msg_composer_get_editor (composer);
-       view = e_html_editor_get_view (editor);
-       e_html_editor_view_set_changed (view, FALSE);
+       cnt_editor = e_html_editor_get_content_editor (editor);
+       e_content_editor_set_changed (cnt_editor, TRUE);
 
        g_signal_emit (
                composer, signals[SAVE_TO_DRAFTS],
@@ -4361,7 +4201,7 @@ msg_composer_save_to_outbox_cb (EMsgComposer *composer,
        CamelMimeMessage *message;
        EAlertSink *alert_sink;
        EHTMLEditor *editor;
-       EHTMLEditorView *view;
+       EContentEditor *cnt_editor;
        GError *error = NULL;
 
        alert_sink = e_activity_get_alert_sink (context->activity);
@@ -4397,8 +4237,8 @@ msg_composer_save_to_outbox_cb (EMsgComposer *composer,
        async_context_free (context);
 
        editor = e_msg_composer_get_editor (composer);
-       view = e_html_editor_get_view (editor);
-       e_html_editor_view_set_changed (view, FALSE);
+       cnt_editor = e_html_editor_get_content_editor (editor);
+       e_content_editor_set_changed (cnt_editor, TRUE);
 }
 
 /**
@@ -4821,27 +4661,22 @@ handle_mailto (EMsgComposer *composer,
 }
 
 /**
- * e_msg_composer_new_from_url:
- * @shell: an #EShell
+ * e_msg_composer_setup_from_url:
+ * @composer: an #EMsgComposer
  * @url: a mailto URL
  *
- * Create a new message composer widget, and fill in fields as
- * defined by the provided URL.
+ * Sets up the message @composer content as defined by the provided URL.
+ *
+ * Since: 3.22
  **/
-EMsgComposer *
-e_msg_composer_new_from_url (EShell *shell,
-                             const gchar *url)
+void
+e_msg_composer_setup_from_url (EMsgComposer *composer,
+                              const gchar *url)
 {
-       EMsgComposer *composer;
-
-       g_return_val_if_fail (E_IS_SHELL (shell), NULL);
-       g_return_val_if_fail (g_ascii_strncasecmp (url, "mailto:";, 7) == 0, NULL);
-
-       composer = e_msg_composer_new (shell);
+       g_return_if_fail (E_IS_MSG_COMPOSER (composer));
+       g_return_if_fail (g_ascii_strncasecmp (url, "mailto:";, 7) == 0);
 
        handle_mailto (composer, url);
-
-       return composer;
 }
 
 /**
@@ -4881,7 +4716,7 @@ e_msg_composer_set_body (EMsgComposer *composer,
        EMsgComposerPrivate *priv = composer->priv;
        EComposerHeaderTable *table;
        EHTMLEditor *editor;
-       EHTMLEditorView *view;
+       EContentEditor *cnt_editor;
        ESource *source;
        const gchar *identity_uid;
        const gchar *content;
@@ -4889,7 +4724,7 @@ e_msg_composer_set_body (EMsgComposer *composer,
        g_return_if_fail (E_IS_MSG_COMPOSER (composer));
 
        editor = e_msg_composer_get_editor (composer);
-       view = e_html_editor_get_view (editor);
+       cnt_editor = e_html_editor_get_content_editor (editor);
        table = e_msg_composer_get_header_table (composer);
 
        /* Disable signature */
@@ -4901,8 +4736,8 @@ e_msg_composer_set_body (EMsgComposer *composer,
        content = _("The composer contains a non-text message body, which cannot be edited.");
        set_editor_text (composer, content, TRUE, FALSE);
 
-       e_html_editor_view_set_html_mode (view, FALSE);
-       webkit_web_view_set_editable (WEBKIT_WEB_VIEW (view), FALSE);
+       e_content_editor_set_html_mode (cnt_editor, FALSE);
+       e_content_editor_set_editable (cnt_editor, FALSE);
 
        g_free (priv->mime_body);
        priv->mime_body = g_strdup (body);
@@ -5202,12 +5037,12 @@ e_msg_composer_get_message (EMsgComposer *composer,
        GtkAction *action;
        ComposerFlags flags = 0;
        EHTMLEditor *editor;
-       EHTMLEditorView *view;
+       EContentEditor *cnt_editor;
 
        g_return_if_fail (E_IS_MSG_COMPOSER (composer));
 
        editor = e_msg_composer_get_editor (composer);
-       view = e_html_editor_get_view (editor);
+       cnt_editor = e_html_editor_get_content_editor (editor);
 
        simple = g_simple_async_result_new (
                G_OBJECT (composer), callback,
@@ -5215,7 +5050,7 @@ e_msg_composer_get_message (EMsgComposer *composer,
 
        g_simple_async_result_set_check_cancellable (simple, cancellable);
 
-       if (e_html_editor_view_get_html_mode (view))
+       if (e_content_editor_get_html_mode (cnt_editor))
                flags |= COMPOSER_FLAG_HTML_CONTENT;
 
        action = ACTION (PRIORITIZE_MESSAGE);
@@ -5333,7 +5168,7 @@ e_msg_composer_get_message_draft (EMsgComposer *composer,
                                   gpointer user_data)
 {
        EHTMLEditor *editor;
-       EHTMLEditorView *view;
+       EContentEditor *cnt_editor;
        GSimpleAsyncResult *simple;
        ComposerFlags flags = COMPOSER_FLAG_SAVE_DRAFT;
        GtkAction *action;
@@ -5347,9 +5182,9 @@ e_msg_composer_get_message_draft (EMsgComposer *composer,
        g_simple_async_result_set_check_cancellable (simple, cancellable);
 
        editor = e_msg_composer_get_editor (composer);
-       view = e_html_editor_get_view (editor);
+       cnt_editor = e_html_editor_get_content_editor (editor);
        /* We need to remember composer mode */
-       if (e_html_editor_view_get_html_mode (view))
+       if (e_content_editor_get_html_mode (cnt_editor))
                flags |= COMPOSER_FLAG_HTML_MODE;
        /* We want to save HTML content everytime when we save as draft */
        flags |= COMPOSER_FLAG_SAVE_DRAFT;
@@ -5482,44 +5317,22 @@ GByteArray *
 e_msg_composer_get_raw_message_text_without_signature (EMsgComposer *composer)
 {
        EHTMLEditor *editor;
-       EHTMLEditorView *view;
-       GByteArray *array;
-       gint ii, length;
-       WebKitDOMDocument *document;
-       WebKitDOMNodeList *list;
+       EContentEditor *cnt_editor;
+       gchar *content;
 
        g_return_val_if_fail (E_IS_MSG_COMPOSER (composer), NULL);
 
        editor = e_msg_composer_get_editor (composer);
-       view = e_html_editor_get_view (editor);
-       document = webkit_web_view_get_dom_document (WEBKIT_WEB_VIEW (view));
-       array = g_byte_array_new ();
+       cnt_editor = e_html_editor_get_content_editor (editor);
 
-       list = webkit_dom_document_query_selector_all (
-               document, "body > *:not(.-x-evo-signature-wrapper)", NULL);
-       length = webkit_dom_node_list_get_length (list);
-       for (ii = 0; ii < length; ii++) {
-               WebKitDOMNode *node = webkit_dom_node_list_item (list, ii);
-
-               if (!WEBKIT_DOM_IS_HTML_QUOTE_ELEMENT (node)) {
-                       gchar *text;
-
-                       text = webkit_dom_html_element_get_inner_text (WEBKIT_DOM_HTML_ELEMENT (node));
-                       g_byte_array_append (array, (guint8 *) text, strlen (text));
-                       g_free (text);
+       content = e_content_editor_get_content (
+               cnt_editor,
+               E_CONTENT_EDITOR_GET_BODY |
+               E_CONTENT_EDITOR_GET_TEXT_PLAIN |
+               E_CONTENT_EDITOR_GET_EXCLUDE_SIGNATURE,
+               NULL, NULL);
 
-                       if (WEBKIT_DOM_IS_HTML_DIV_ELEMENT (node))
-                               g_byte_array_append (array, (const guint8 *) "\n", 1);
-                       else
-                               g_byte_array_append (array, (const guint8 *) " ", 1);
-               }
-
-               g_object_unref (node);
-       }
-
-       g_object_unref (list);
-
-       return array;
+       return g_byte_array_new_take ((guint8 *) content, strlen (content));
 }
 
 /**
@@ -5531,25 +5344,21 @@ GByteArray *
 e_msg_composer_get_raw_message_text (EMsgComposer *composer)
 {
        EHTMLEditor *editor;
-       EHTMLEditorView *view;
-       GByteArray *array;
-       gchar *text;
-       WebKitDOMDocument *document;
-       WebKitDOMHTMLElement *body;
+       EContentEditor *cnt_editor;
+       gchar *content;
 
        g_return_val_if_fail (E_IS_MSG_COMPOSER (composer), NULL);
 
        editor = e_msg_composer_get_editor (composer);
-       view = e_html_editor_get_view (editor);
-       document = webkit_web_view_get_dom_document (WEBKIT_WEB_VIEW (view));
-       body = webkit_dom_document_get_body (document);
+       cnt_editor = e_html_editor_get_content_editor (editor);
 
-       array = g_byte_array_new ();
-       text = webkit_dom_html_element_get_inner_text (body);
-       g_byte_array_append (array, (guint8 *) text, strlen (text));
-       g_free (text);
+       content = e_content_editor_get_content (
+               cnt_editor,
+               E_CONTENT_EDITOR_GET_BODY |
+               E_CONTENT_EDITOR_GET_TEXT_PLAIN,
+               NULL, NULL);
 
-       return array;
+       return g_byte_array_new_take ((guint8 *) content, strlen (content));
 }
 
 gboolean
@@ -5580,7 +5389,7 @@ e_msg_composer_can_close (EMsgComposer *composer,
 {
        gboolean res = FALSE;
        EHTMLEditor *editor;
-       EHTMLEditorView *view;
+       EContentEditor *cnt_editor;
        EComposerHeaderTable *table;
        GdkWindow *window;
        GtkWidget *widget;
@@ -5589,14 +5398,14 @@ e_msg_composer_can_close (EMsgComposer *composer,
 
        widget = GTK_WIDGET (composer);
        editor = e_msg_composer_get_editor (composer);
-       view = e_html_editor_get_view (editor);
+       cnt_editor = e_html_editor_get_content_editor (editor);
 
        /* this means that there is an async operation running,
         * in which case the composer cannot be closed */
        if (!gtk_action_group_get_sensitive (composer->priv->async_actions))
                return FALSE;
 
-       if (!e_html_editor_view_get_changed (view))
+       if (!e_content_editor_get_changed (cnt_editor))
                return TRUE;
 
        window = gtk_widget_get_window (widget);
@@ -5657,6 +5466,7 @@ e_save_spell_languages (const GList *spell_dicts)
 
        /* Build a list of spell check language codes. */
        lang_array = g_ptr_array_new ();
+
        while (spell_dicts != NULL) {
                ESpellDictionary *dict = spell_dicts->data;
                const gchar *language_code;
@@ -5689,14 +5499,8 @@ e_msg_composer_save_focused_widget (EMsgComposer *composer)
        widget = gtk_window_get_focus (GTK_WINDOW (composer));
        composer->priv->focused_entry = widget;
 
-       if (E_IS_HTML_EDITOR_VIEW (widget)) {
-               EHTMLEditorSelection *selection;
-
-               selection = e_html_editor_view_get_selection (
-                       E_HTML_EDITOR_VIEW (widget));
-
-               e_html_editor_selection_save (selection);
-       }
+       if (E_IS_CONTENT_EDITOR (widget))
+               e_content_editor_selection_save (E_CONTENT_EDITOR (widget));
 
        if (GTK_IS_EDITABLE (widget)) {
                gtk_editable_get_selection_bounds (
@@ -5725,15 +5529,11 @@ e_msg_composer_restore_focus_on_composer (EMsgComposer *composer)
                        composer->priv->focused_entry_selection_end);
        }
 
-       if (E_IS_HTML_EDITOR_VIEW (widget)) {
-               EHTMLEditorSelection *selection;
-
-               e_html_editor_view_force_spell_check_in_viewport (E_HTML_EDITOR_VIEW (widget));
-
-               selection = e_html_editor_view_get_selection (
-                       E_HTML_EDITOR_VIEW (widget));
-
-               e_html_editor_selection_restore (selection);
+       if (E_IS_CONTENT_EDITOR (widget)) {
+               EContentEditor *cnt_editor = E_CONTENT_EDITOR (widget);
+               /* FIXME WK2
+               e_html_editor_view_force_spell_check (view);*/
+               e_content_editor_selection_restore (cnt_editor);
        }
 
        composer->priv->focused_entry = NULL;
diff --git a/composer/e-msg-composer.h b/composer/e-msg-composer.h
index daaca2b..c0986a5 100644
--- a/composer/e-msg-composer.h
+++ b/composer/e-msg-composer.h
@@ -81,15 +81,20 @@ struct _EMsgComposerClass {
 };
 
 GType          e_msg_composer_get_type         (void);
-EMsgComposer * e_msg_composer_new              (EShell *shell);
-EMsgComposer * e_msg_composer_new_with_message (EShell *shell,
+void           e_msg_composer_new              (EShell *shell,
+                                                GAsyncReadyCallback callback,
+                                                gpointer user_data);
+EMsgComposer * e_msg_composer_new_finish       (GAsyncResult *result,
+                                                GError **error);
+void           e_msg_composer_setup_with_message
+                                               (EMsgComposer *composer,
                                                 CamelMimeMessage *message,
                                                 gboolean keep_signature,
                                                 const gchar *override_identity_uid,
                                                 GCancellable *cancellable);
-EMsgComposer * e_msg_composer_new_from_url     (EShell *shell,
+void           e_msg_composer_setup_from_url   (EMsgComposer *composer,
                                                 const gchar *url);
-EMsgComposer * e_msg_composer_new_redirect     (EShell *shell,
+void           e_msg_composer_setup_redirect   (EMsgComposer *composer,
                                                 CamelMimeMessage *message,
                                                 const gchar *identity_uid,
                                                 GCancellable *cancellable);
diff --git a/configure.ac b/configure.ac
index ab0ab28..5439429 100644
--- a/configure.ac
+++ b/configure.ac
@@ -50,7 +50,7 @@ m4_define([gcr_minimum_version], [3.4])
 m4_define([enchant_minimum_version], [1.1.7])
 m4_define([gnome_desktop_minimum_version], [2.91.3])
 m4_define([gsettings_desktop_schemas_minimum_version], [2.91.92])
-m4_define([webkitgtk_minimum_version], [2.2.0])
+m4_define([webkit2gtk_minimum_version], [2.13.0])
 m4_define([libxml_minimum_version], [2.7.3])
 m4_define([shared_mime_info_minimum_version], [0.22])
 m4_define([libpst_minimum_version], [0.6.54])
@@ -287,7 +287,7 @@ PKG_CHECK_MODULES([GNOME_PLATFORM],
         libxml-2.0 >= libxml_minimum_version
         shared-mime-info >= shared_mime_info_minimum_version
         gsettings-desktop-schemas >= gsettings_desktop_schemas_minimum_version
-        webkitgtk-3.0 >= webkitgtk_minimum_version
+        webkit2gtk-4.0 >= webkitgtk_minimum_version
         $GIO_UNIX_REQUIREMENT])
 
 GNOME_DESKTOP_DEPENDENCY=""
@@ -1269,6 +1269,18 @@ AC_SUBST(viewsdir)
 dnl For evolution-alarm-notify.desktop
 AS_AC_EXPAND(PRIVLIBEXECDIR, "$privlibexecdir")
 
+dnl **********************************
+dnl WebKit2 Web Extensions
+dnl **********************************
+webextensionsdir="$privlibdir/web-extensions"
+webextensionswebkiteditordir="$privlibdir/web-extensions/webkit-editor"
+AC_SUBST(webextensionsdir)
+AC_SUBST(webextensionswebkiteditordir)
+
+PKG_CHECK_MODULES(WEB_EXTENSION, [webkit2gtk-4.0 >= webkit2gtk_minimum_version])
+AC_SUBST(WEB_EXTENSIONS_CFLAGS)
+AC_SUBST(WEB_EXTENSIONS_LIBS)
+
 dnl ************************
 dnl Plugins
 dnl ************************
@@ -1592,6 +1604,7 @@ modules/composer-autosave/Makefile
 modules/contact-photos/Makefile
 modules/gravatar/Makefile
 modules/itip-formatter/Makefile
+modules/itip-formatter/web-extension/Makefile
 modules/mail-config/Makefile
 modules/mail/Makefile
 modules/mailto-handler/Makefile
@@ -1607,7 +1620,9 @@ modules/startup-wizard/Makefile
 modules/text-highlight/Makefile
 modules/tnef-attachment/Makefile
 modules/vcard-inline/Makefile
-modules/web-inspector/Makefile
+modules/webkit-editor/Makefile
+modules/webkit-editor/web-extension/Makefile
+modules/webkit-inspector/Makefile
 plugins/Makefile
 plugins/attachment-reminder/Makefile
 plugins/bbdb/Makefile
@@ -1622,6 +1637,7 @@ plugins/pst-import/Makefile
 plugins/publish-calendar/Makefile
 plugins/save-calendar/Makefile
 plugins/templates/Makefile
+web-extensions/Makefile
 smime/Makefile
 smime/lib/Makefile
 smime/gui/Makefile
diff --git a/data/org.gnome.evolution.mail.gschema.xml.in b/data/org.gnome.evolution.mail.gschema.xml.in
index 4c91054..3eca005 100644
--- a/data/org.gnome.evolution.mail.gschema.xml.in
+++ b/data/org.gnome.evolution.mail.gschema.xml.in
@@ -40,6 +40,11 @@
       <_summary>Default charset in which to compose messages</_summary>
       <_description>Default charset in which to compose messages. Uses UTF-8, if not set.</_description>
     </key>
+    <key name="composer-editor" type="s">
+      <default>''</default>
+      <_summary>Name of the editor to prefer in the message composer</_summary>
+      <_description>If the name doesn't correspond to any known editor, then the built-in WebKit editor is 
used.</_description>
+    </key>
     <key name="composer-gallery-path" type="s">
       <default>''</default>
       <_summary>Path where picture gallery should search for its content</_summary>
@@ -175,11 +180,6 @@
       <_summary>List of localized 'Re'</_summary>
       <_description>Comma-separated list of localized 'Re' abbreviations to skip in a subject text when 
replying to a message, as an addition to the standard "Re" prefix. An example is 'SV,AV'.</_description>
     </key>
-    <key name="composer-developer-mode" type="b">
-      <default>false</default>
-      <_summary>Enable developer mode</_summary>
-      <_description>Enables some hidden actions and tools aimed for development and debugging.</_description>
-    </key>
     <key name="composer-word-wrap-length" type="i">
       <default>71</default>
       <_summary>Number of characters for wrapping</_summary>
@@ -299,6 +299,11 @@
       <_summary>Timeout for marking messages as seen</_summary>
       <_description>Timeout in milliseconds for marking messages as seen.</_description>
     </key>
+    <key name="show-attachment-bar" type="b">
+      <default>true</default>
+      <_summary>Show Attachment Bar</_summary>
+      <_description>Show Attachment Bar below the message preview pane when the message has 
attachments.</_description>
+    </key>
     <key name="show-email" type="b">
       <default>false</default>
       <_summary>Sender email-address column in the message list</_summary>
diff --git a/doc/reference/evolution-util/Makefile.am b/doc/reference/evolution-util/Makefile.am
index 2d1b169..db6074e 100644
--- a/doc/reference/evolution-util/Makefile.am
+++ b/doc/reference/evolution-util/Makefile.am
@@ -18,7 +18,6 @@ CFILE_GLOB = $(top_srcdir)/e-util/*.c
 IGNORE_HFILES = \
        e-html-editor-actions.h \
        e-html-editor-private.h \
-       e-html-editor-utils.h \
        e-marshal.h \
        e-table-col-dnd.h \
        e-table-defines.h \
diff --git a/e-util/Makefile.am b/e-util/Makefile.am
index 46a6bea..4c9db7b 100644
--- a/e-util/Makefile.am
+++ b/e-util/Makefile.am
@@ -62,6 +62,7 @@ noinst_PROGRAMS = \
        test-contact-store \
        test-dateedit \
        test-html-editor \
+       test-html-editor-units \
        test-mail-signatures \
        test-name-selector \
        test-preferences-window \
@@ -97,6 +98,7 @@ libevolution_util_la_CPPFLAGS = \
        -DEVOLUTION_TOOLSDIR=\""$(privlibexecdir)"\" \
        -DEVOLUTION_UIDIR=\""$(uidir)"\" \
        -DEVOLUTION_RULEDIR=\"$(ruledir)\" \
+       -DEVOLUTION_WEB_EXTENSIONS_DIR=\""$(webextensionsdir)"\" \
        -DG_LOG_DOMAIN=\"evolution-util\" \
        $(EVOLUTION_DATA_SERVER_CFLAGS) \
        $(GNOME_PLATFORM_CFLAGS) \
@@ -119,7 +121,6 @@ evolution_util_include_HEADERS =  \
        e-alert-sink.h \
        e-alert.h \
        e-attachment-bar.h \
-       e-attachment-button.h \
        e-attachment-dialog.h \
        e-attachment-handler-image.h \
        e-attachment-handler.h \
@@ -173,6 +174,8 @@ evolution_util_include_HEADERS =  \
        e-config.h \
        e-conflict-search-selector.h \
        e-contact-store.h \
+       e-content-editor.h \
+       e-content-request.h \
        e-data-capture.h \
        e-dateedit.h \
        e-datetime-format.h \
@@ -208,12 +211,9 @@ evolution_util_include_HEADERS =  \
        e-html-editor-page-dialog.h \
        e-html-editor-paragraph-dialog.h \
        e-html-editor-replace-dialog.h \
-       e-html-editor-selection.h \
        e-html-editor-spell-check-dialog.h \
        e-html-editor-table-dialog.h \
        e-html-editor-text-dialog.h \
-       e-html-editor-utils.h \
-       e-html-editor-view.h \
        e-html-editor.h \
        e-html-utils.h \
        e-icon-factory.h \
@@ -272,6 +272,7 @@ evolution_util_include_HEADERS =  \
        e-selection-model.h \
        e-selection.h \
        e-send-options.h \
+       e-simple-async-result.h \
        e-sorter-array.h \
        e-sorter.h \
        e-source-combo-box.h \
@@ -394,7 +395,6 @@ libevolution_util_la_SOURCES = \
        e-alert-sink.c \
        e-alert.c \
        e-attachment-bar.c \
-       e-attachment-button.c \
        e-attachment-dialog.c \
        e-attachment-handler-image.c \
        e-attachment-handler.c \
@@ -448,6 +448,8 @@ libevolution_util_la_SOURCES = \
        e-config.c \
        e-conflict-search-selector.c \
        e-contact-store.c \
+       e-content-editor.c \
+       e-content-request.c \
        e-data-capture.c \
        e-dateedit.c \
        e-datetime-format.c \
@@ -484,12 +486,9 @@ libevolution_util_la_SOURCES = \
        e-html-editor-paragraph-dialog.c \
        e-html-editor-private.h \
        e-html-editor-replace-dialog.c \
-       e-html-editor-selection.c \
        e-html-editor-spell-check-dialog.c \
        e-html-editor-table-dialog.c \
        e-html-editor-text-dialog.c \
-       e-html-editor-utils.c \
-       e-html-editor-view.c \
        e-html-editor.c \
        e-html-utils.c \
        e-icon-factory.c \
@@ -548,6 +547,7 @@ libevolution_util_la_SOURCES = \
        e-selection-model.c \
        e-selection.c \
        e-send-options.c \
+       e-simple-async-result.c \
        e-sorter-array.c \
        e-sorter.c \
        e-source-combo-box.c \
@@ -694,6 +694,14 @@ test_html_editor_CPPFLAGS = $(TEST_CPPFLAGS)
 test_html_editor_SOURCES = test-html-editor.c
 test_html_editor_LDADD = $(TEST_LDADD)
 
+test_html_editor_units_CPPFLAGS = $(TEST_CPPFLAGS) -DTEST_TOP_SRCDIR=\""$(top_srcdir)"\"
+test_html_editor_units_SOURCES = \
+       test-html-editor-units-utils.h \
+       test-html-editor-units-utils.c \
+       test-html-editor-units.c \
+       $(NULL)
+test_html_editor_units_LDADD = $(TEST_LDADD)
+
 test_mail_signatures_CPPFLAGS = $(TEST_CPPFLAGS)
 test_mail_signatures_SOURCES = test-mail-signatures.c
 test_mail_signatures_LDADD = $(TEST_LDADD)
diff --git a/e-util/e-attachment-bar.c b/e-util/e-attachment-bar.c
index 3916d00..e5da99f 100644
--- a/e-util/e-attachment-bar.c
+++ b/e-util/e-attachment-bar.c
@@ -50,6 +50,8 @@ struct _EAttachmentBarPrivate {
        GtkWidget *status_label;
        GtkWidget *save_all_button;
        GtkWidget *save_one_button;
+       GtkWidget *icon_scrolled_window; /* not referenced */
+       GtkWidget *tree_scrolled_window; /* not referenced */
 
        gint active_view;
        guint expanded : 1;
@@ -119,6 +121,43 @@ attachment_bar_update_status (EAttachmentBar *bar)
 }
 
 static void
+attachment_bar_notify_vadjustment_upper_cb (GObject *object,
+                                           GParamSpec *param,
+                                           gpointer user_data)
+{
+       EAttachmentBar *bar = user_data;
+       GtkAdjustment *adjustment;
+       gint max_upper, max_content_height = -2;
+       gint request_height = -1;
+
+       g_return_if_fail (E_IS_ATTACHMENT_BAR (bar));
+
+       adjustment = gtk_scrolled_window_get_vadjustment (GTK_SCROLLED_WINDOW 
(bar->priv->icon_scrolled_window));
+       max_upper = gtk_adjustment_get_upper (adjustment);
+
+       adjustment = gtk_scrolled_window_get_vadjustment (GTK_SCROLLED_WINDOW 
(bar->priv->tree_scrolled_window));
+       max_upper = MAX (max_upper, gtk_adjustment_get_upper (adjustment));
+
+       gtk_widget_style_get (GTK_WIDGET (bar), "max-content-height", &max_content_height, NULL);
+
+       if ((max_content_height >= 0 && max_content_height < 50) || max_content_height <= -2)
+               max_content_height = 50;
+
+       if (max_content_height == -1) {
+               request_height = max_upper;
+       } else if (max_content_height < max_upper) {
+               request_height = max_content_height;
+       } else {
+               request_height = max_upper;
+       }
+
+       gtk_scrolled_window_set_min_content_height (GTK_SCROLLED_WINDOW (bar->priv->icon_scrolled_window),
+               request_height);
+       gtk_scrolled_window_set_min_content_height (GTK_SCROLLED_WINDOW (bar->priv->tree_scrolled_window),
+               request_height);
+}
+
+static void
 attachment_bar_set_store (EAttachmentBar *bar,
                           EAttachmentStore *store)
 {
@@ -534,6 +573,10 @@ e_attachment_bar_class_init (EAttachmentBarClass *class)
        widget_class->button_release_event = attachment_bar_button_release_event;
        widget_class->motion_notify_event = attachment_bar_motion_notify_event;
 
+       #if GTK_CHECK_VERSION (3, 20, 0)
+       gtk_widget_class_set_css_name (widget_class, G_OBJECT_CLASS_NAME (class));
+       #endif
+
        g_object_class_install_property (
                object_class,
                PROP_ACTIVE_VIEW,
@@ -574,6 +617,15 @@ e_attachment_bar_class_init (EAttachmentBarClass *class)
 
        g_object_class_override_property (
                object_class, PROP_EDITABLE, "editable");
+
+       gtk_widget_class_install_style_property (
+               widget_class,
+               g_param_spec_int (
+                       "max-content-height",
+                       "Max Content Height",
+                       NULL,
+                       -1, G_MAXINT, 150,
+                       G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
 }
 
 static void
@@ -599,6 +651,7 @@ e_attachment_bar_init (EAttachmentBar *bar)
        GtkWidget *container;
        GtkWidget *widget;
        GtkAction *action;
+       GtkAdjustment *adjustment;
 
        bar->priv = E_ATTACHMENT_BAR_GET_PRIVATE (bar);
 
@@ -627,10 +680,16 @@ e_attachment_bar_init (EAttachmentBar *bar)
 
        container = widget;
 
+       widget = gtk_scrolled_window_new (NULL, NULL);
+       gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (widget), GTK_POLICY_AUTOMATIC, 
GTK_POLICY_AUTOMATIC);
+       gtk_container_add (GTK_CONTAINER (container), widget);
+       bar->priv->icon_scrolled_window = widget;
+       gtk_widget_show (widget);
+
        widget = e_attachment_icon_view_new ();
        gtk_widget_set_can_focus (widget, TRUE);
        gtk_icon_view_set_model (GTK_ICON_VIEW (widget), bar->priv->model);
-       gtk_container_add (GTK_CONTAINER (container), widget);
+       gtk_container_add (GTK_CONTAINER (bar->priv->icon_scrolled_window), widget);
        bar->priv->icon_view = g_object_ref (widget);
        gtk_widget_show (widget);
 
@@ -644,10 +703,16 @@ e_attachment_bar_init (EAttachmentBar *bar)
 
        container = widget;
 
+       widget = gtk_scrolled_window_new (NULL, NULL);
+       gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (widget), GTK_POLICY_AUTOMATIC, 
GTK_POLICY_AUTOMATIC);
+       gtk_container_add (GTK_CONTAINER (container), widget);
+       bar->priv->tree_scrolled_window = widget;
+       gtk_widget_show (widget);
+
        widget = e_attachment_tree_view_new ();
        gtk_widget_set_can_focus (widget, TRUE);
        gtk_tree_view_set_model (GTK_TREE_VIEW (widget), bar->priv->model);
-       gtk_container_add (GTK_CONTAINER (container), widget);
+       gtk_container_add (GTK_CONTAINER (bar->priv->tree_scrolled_window), widget);
        bar->priv->tree_view = g_object_ref (widget);
        gtk_widget_show (widget);
 
@@ -727,6 +792,14 @@ e_attachment_bar_init (EAttachmentBar *bar)
        gtk_widget_show (widget);
 
        g_object_unref (size_group);
+
+       adjustment = gtk_scrolled_window_get_vadjustment (GTK_SCROLLED_WINDOW 
(bar->priv->icon_scrolled_window));
+       e_signal_connect_notify (adjustment, "notify::upper",
+               G_CALLBACK (attachment_bar_notify_vadjustment_upper_cb), bar);
+
+       adjustment = gtk_scrolled_window_get_vadjustment (GTK_SCROLLED_WINDOW 
(bar->priv->tree_scrolled_window));
+       e_signal_connect_notify (adjustment, "notify::upper",
+               G_CALLBACK (attachment_bar_notify_vadjustment_upper_cb), bar);
 }
 
 GtkWidget *
diff --git a/e-util/e-attachment-store.c b/e-util/e-attachment-store.c
index f0648db..85fa19a 100644
--- a/e-util/e-attachment-store.c
+++ b/e-util/e-attachment-store.c
@@ -53,12 +53,157 @@ enum {
        PROP_TOTAL_SIZE
 };
 
+enum {
+       ATTACHMENT_ADDED,
+       ATTACHMENT_REMOVED,
+       LAST_SIGNAL
+};
+
+static gulong signals[LAST_SIGNAL];
+
 G_DEFINE_TYPE (
        EAttachmentStore,
        e_attachment_store,
        GTK_TYPE_LIST_STORE)
 
 static void
+attachment_store_update_file_info_cb (EAttachment *attachment,
+                                     const gchar *caption,
+                                     const gchar *content_type,
+                                     const gchar *description,
+                                     gint64 size,
+                                     gpointer user_data)
+{
+       EAttachmentStore *store = user_data;
+       GtkTreeIter iter;
+
+       g_return_if_fail (E_IS_ATTACHMENT (attachment));
+       g_return_if_fail (E_IS_ATTACHMENT_STORE (store));
+
+       if (e_attachment_store_find_attachment_iter (store, attachment, &iter)) {
+               gtk_list_store_set (
+                       GTK_LIST_STORE (store), &iter,
+                       E_ATTACHMENT_STORE_COLUMN_CAPTION, caption,
+                       E_ATTACHMENT_STORE_COLUMN_CONTENT_TYPE, content_type,
+                       E_ATTACHMENT_STORE_COLUMN_DESCRIPTION, description,
+                       E_ATTACHMENT_STORE_COLUMN_SIZE, size,
+                       -1);
+       }
+}
+
+static void
+attachment_store_update_icon_cb (EAttachment *attachment,
+                                GIcon *icon,
+                                gpointer user_data)
+{
+       EAttachmentStore *store = user_data;
+       GtkTreeIter iter;
+
+       g_return_if_fail (E_IS_ATTACHMENT (attachment));
+       g_return_if_fail (E_IS_ATTACHMENT_STORE (store));
+
+       if (e_attachment_store_find_attachment_iter (store, attachment, &iter)) {
+               gtk_list_store_set (
+                       GTK_LIST_STORE (store), &iter,
+                       E_ATTACHMENT_STORE_COLUMN_ICON, icon,
+                       -1);
+       }
+}
+
+static void
+attachment_store_update_progress_cb (EAttachment *attachment,
+                                    gboolean loading,
+                                    gboolean saving,
+                                    gint percent,
+                                    gpointer user_data)
+{
+       EAttachmentStore *store = user_data;
+       GtkTreeIter iter;
+
+       g_return_if_fail (E_IS_ATTACHMENT (attachment));
+       g_return_if_fail (E_IS_ATTACHMENT_STORE (store));
+
+       if (e_attachment_store_find_attachment_iter (store, attachment, &iter)) {
+               gtk_list_store_set (
+                       GTK_LIST_STORE (store), &iter,
+                       E_ATTACHMENT_STORE_COLUMN_LOADING, loading,
+                       E_ATTACHMENT_STORE_COLUMN_SAVING, saving,
+                       E_ATTACHMENT_STORE_COLUMN_PERCENT, percent,
+                       -1);
+       }
+}
+
+static void
+attachment_store_load_failed_cb (EAttachment *attachment,
+                                gpointer user_data)
+{
+       EAttachmentStore *store = user_data;
+
+       g_return_if_fail (E_IS_ATTACHMENT (attachment));
+       g_return_if_fail (E_IS_ATTACHMENT_STORE (store));
+
+       e_attachment_store_remove_attachment (store, attachment);
+}
+
+static void
+attachment_store_attachment_notify_cb (GObject *attachment,
+                                      GParamSpec *param,
+                                      gpointer user_data)
+{
+       EAttachmentStore *store = user_data;
+
+       g_return_if_fail (E_IS_ATTACHMENT (attachment));
+       g_return_if_fail (param != NULL);
+       g_return_if_fail (E_IS_ATTACHMENT_STORE (store));
+
+       if (g_str_equal (param->name, "loading")) {
+               g_object_notify (G_OBJECT (store), "num-loading");
+       } else if (g_str_equal (param->name, "file-info")) {
+               g_object_notify (G_OBJECT (store), "total-size");
+       }
+}
+
+static void
+attachment_store_attachment_added (EAttachmentStore *store,
+                                  EAttachment *attachment)
+{
+       g_return_if_fail (E_IS_ATTACHMENT_STORE (store));
+       g_return_if_fail (E_IS_ATTACHMENT (attachment));
+
+       g_signal_connect (attachment, "update-file-info",
+               G_CALLBACK (attachment_store_update_file_info_cb), store);
+       g_signal_connect (attachment, "update-icon",
+               G_CALLBACK (attachment_store_update_icon_cb), store);
+       g_signal_connect (attachment, "update-progress",
+               G_CALLBACK (attachment_store_update_progress_cb), store);
+       g_signal_connect (attachment, "load-failed",
+               G_CALLBACK (attachment_store_load_failed_cb), store);
+       g_signal_connect (attachment, "notify",
+               G_CALLBACK (attachment_store_attachment_notify_cb), store);
+
+       e_attachment_update_store_columns (attachment);
+}
+
+static void
+attachment_store_attachment_removed (EAttachmentStore *store,
+                                    EAttachment *attachment)
+{
+       g_return_if_fail (E_IS_ATTACHMENT_STORE (store));
+       g_return_if_fail (E_IS_ATTACHMENT (attachment));
+
+       g_signal_handlers_disconnect_by_func (attachment,
+               G_CALLBACK (attachment_store_update_file_info_cb), store);
+       g_signal_handlers_disconnect_by_func (attachment,
+               G_CALLBACK (attachment_store_update_icon_cb), store);
+       g_signal_handlers_disconnect_by_func (attachment,
+               G_CALLBACK (attachment_store_update_progress_cb), store);
+       g_signal_handlers_disconnect_by_func (attachment,
+               G_CALLBACK (attachment_store_load_failed_cb), store);
+       g_signal_handlers_disconnect_by_func (attachment,
+               G_CALLBACK (attachment_store_attachment_notify_cb), store);
+}
+
+static void
 attachment_store_get_property (GObject *object,
                                guint property_id,
                                GValue *value,
@@ -124,6 +269,9 @@ e_attachment_store_class_init (EAttachmentStoreClass *class)
        object_class->dispose = attachment_store_dispose;
        object_class->finalize = attachment_store_finalize;
 
+       class->attachment_added = attachment_store_attachment_added;
+       class->attachment_removed = attachment_store_attachment_removed;
+
        g_object_class_install_property (
                object_class,
                PROP_NUM_ATTACHMENTS,
@@ -159,6 +307,22 @@ e_attachment_store_class_init (EAttachmentStoreClass *class)
                        G_MAXUINT64,
                        0,
                        G_PARAM_READABLE));
+
+       signals[ATTACHMENT_ADDED] = g_signal_new (
+               "attachment-added",
+               G_TYPE_FROM_CLASS (class),
+               G_SIGNAL_RUN_LAST,
+               G_STRUCT_OFFSET (EAttachmentStoreClass, attachment_added),
+               NULL, NULL, NULL,
+               G_TYPE_NONE, 1, E_TYPE_ATTACHMENT);
+
+       signals[ATTACHMENT_REMOVED] = g_signal_new (
+               "attachment-removed",
+               G_TYPE_FROM_CLASS (class),
+               G_SIGNAL_RUN_LAST,
+               G_STRUCT_OFFSET (EAttachmentStoreClass, attachment_removed),
+               NULL, NULL, NULL,
+               G_TYPE_NONE, 1, E_TYPE_ATTACHMENT);
 }
 
 static void
@@ -225,13 +389,12 @@ e_attachment_store_add_attachment (EAttachmentStore *store,
                store->priv->attachment_index,
                g_object_ref (attachment), reference);
 
-       /* This lets the attachment tell us when to update. */
-       e_attachment_set_reference (attachment, reference);
-
        g_object_freeze_notify (G_OBJECT (store));
        g_object_notify (G_OBJECT (store), "num-attachments");
        g_object_notify (G_OBJECT (store), "total-size");
        g_object_thaw_notify (G_OBJECT (store));
+
+       g_signal_emit (store, signals[ATTACHMENT_ADDED], 0, attachment);
 }
 
 gboolean
@@ -243,6 +406,7 @@ e_attachment_store_remove_attachment (EAttachmentStore *store,
        GtkTreeModel *model;
        GtkTreePath *path;
        GtkTreeIter iter;
+       gboolean removed;
 
        g_return_val_if_fail (E_IS_ATTACHMENT_STORE (store), FALSE);
        g_return_val_if_fail (E_IS_ATTACHMENT (attachment), FALSE);
@@ -254,12 +418,12 @@ e_attachment_store_remove_attachment (EAttachmentStore *store,
                return FALSE;
 
        if (!gtk_tree_row_reference_valid (reference)) {
-               g_hash_table_remove (hash_table, attachment);
+               if (g_hash_table_remove (hash_table, attachment))
+                       g_signal_emit (store, signals[ATTACHMENT_REMOVED], 0, attachment);
                return FALSE;
        }
 
        e_attachment_cancel (attachment);
-       e_attachment_set_reference (attachment, NULL);
 
        model = gtk_tree_row_reference_get_model (reference);
        path = gtk_tree_row_reference_get_path (reference);
@@ -267,13 +431,16 @@ e_attachment_store_remove_attachment (EAttachmentStore *store,
        gtk_tree_path_free (path);
 
        gtk_list_store_remove (GTK_LIST_STORE (store), &iter);
-       g_hash_table_remove (hash_table, attachment);
+       removed = g_hash_table_remove (hash_table, attachment);
 
        g_object_freeze_notify (G_OBJECT (store));
        g_object_notify (G_OBJECT (store), "num-attachments");
        g_object_notify (G_OBJECT (store), "total-size");
        g_object_thaw_notify (G_OBJECT (store));
 
+       if (removed)
+               g_signal_emit (store, signals[ATTACHMENT_REMOVED], 0, attachment);
+
        return TRUE;
 }
 
@@ -304,9 +471,10 @@ e_attachment_store_remove_all (EAttachmentStore *store)
                EAttachment *attachment = iter->data;
 
                e_attachment_cancel (attachment);
-               e_attachment_set_reference (attachment, NULL);
 
                g_warn_if_fail (g_hash_table_remove (store->priv->attachment_index, attachment));
+
+               g_signal_emit (store, signals[ATTACHMENT_REMOVED], 0, attachment);
        }
 
        g_list_foreach (list, (GFunc) g_object_unref, NULL);
@@ -780,6 +948,51 @@ e_attachment_store_run_save_dialog (EAttachmentStore *store,
        return destination;
 }
 
+gboolean
+e_attachment_store_transform_num_attachments_to_visible_boolean (GBinding *binding,
+                                                                const GValue *from_value,
+                                                                GValue *to_value,
+                                                                gpointer user_data)
+{
+       g_return_val_if_fail (from_value != NULL, FALSE);
+       g_return_val_if_fail (to_value != NULL, FALSE);
+       g_return_val_if_fail (G_VALUE_HOLDS_UINT (from_value), FALSE);
+       g_return_val_if_fail (G_VALUE_HOLDS_BOOLEAN (to_value), FALSE);
+
+       g_value_set_boolean (to_value, g_value_get_uint (from_value) != 0);
+
+       return TRUE;
+}
+
+gboolean
+e_attachment_store_find_attachment_iter (EAttachmentStore *store,
+                                        EAttachment *attachment,
+                                        GtkTreeIter *out_iter)
+{
+       GtkTreeRowReference *reference;
+       GtkTreeModel *model;
+       GtkTreePath *path;
+       gboolean found;
+
+       g_return_val_if_fail (E_IS_ATTACHMENT_STORE (store), FALSE);
+       g_return_val_if_fail (E_IS_ATTACHMENT (attachment), FALSE);
+       g_return_val_if_fail (out_iter != NULL, FALSE);
+
+       reference = g_hash_table_lookup (store->priv->attachment_index, attachment);
+
+       if (!reference || !gtk_tree_row_reference_valid (reference))
+               return FALSE;
+
+       model = gtk_tree_row_reference_get_model (reference);
+       g_return_val_if_fail (model == GTK_TREE_MODEL (store), FALSE);
+
+       path = gtk_tree_row_reference_get_path (reference);
+       found = gtk_tree_model_get_iter (model, out_iter, path);
+       gtk_tree_path_free (path);
+
+       return found;
+}
+
 /******************** e_attachment_store_get_uris_async() ********************/
 
 typedef struct _UriContext UriContext;
diff --git a/e-util/e-attachment-store.h b/e-util/e-attachment-store.h
index 584110c..1f15f5d 100644
--- a/e-util/e-attachment-store.h
+++ b/e-util/e-attachment-store.h
@@ -60,6 +60,12 @@ struct _EAttachmentStore {
 
 struct _EAttachmentStoreClass {
        GtkListStoreClass parent_class;
+
+       /* Signals */
+       void    (* attachment_added)    (EAttachmentStore *store,
+                                        EAttachment *attachment);
+       void    (* attachment_removed)  (EAttachmentStore *store,
+                                        EAttachment *attachment);
 };
 
 enum {
@@ -104,6 +110,15 @@ GFile *            e_attachment_store_run_save_dialog
                                                 GList *attachment_list,
                                                 GtkWindow *parent);
 
+gboolean       e_attachment_store_transform_num_attachments_to_visible_boolean
+                                               (GBinding *binding,
+                                                const GValue *from_value,
+                                                GValue *to_value,
+                                                gpointer user_data);
+gboolean       e_attachment_store_find_attachment_iter
+                                               (EAttachmentStore *store,
+                                                EAttachment *attachment,
+                                                GtkTreeIter *out_iter);
 /* Asynchronous Operations */
 void           e_attachment_store_get_uris_async
                                                (EAttachmentStore *store,
diff --git a/e-util/e-attachment-view.c b/e-util/e-attachment-view.c
index 7020ab8..00b17df 100644
--- a/e-util/e-attachment-view.c
+++ b/e-util/e-attachment-view.c
@@ -50,15 +50,7 @@ static const gchar *ui =
 "    <menuitem action='remove'/>"
 "    <menuitem action='properties'/>"
 "    <separator/>"
-"    <placeholder name='inline-actions'>"
-"      <menuitem action='zoom-to-100'/>"
-"      <menuitem action='zoom-to-window'/>"
-"      <menuitem action='show'/>"
-"      <menuitem action='show-all'/>"
-"      <separator/>"
-"      <menuitem action='hide'/>"
-"      <menuitem action='hide-all'/>"
-"    </placeholder>"
+"    <placeholder name='inline-actions'/>"
 "    <separator/>"
 "    <placeholder name='custom-actions'/>"
 "    <separator/>"
@@ -108,44 +100,6 @@ action_cancel_cb (GtkAction *action,
 }
 
 static void
-action_hide_cb (GtkAction *action,
-                EAttachmentView *view)
-{
-       EAttachment *attachment;
-       GList *list;
-
-       list = e_attachment_view_get_selected_attachments (view);
-       g_return_if_fail (g_list_length (list) == 1);
-       attachment = list->data;
-
-       e_attachment_set_shown (attachment, FALSE);
-
-       g_list_foreach (list, (GFunc) g_object_unref, NULL);
-       g_list_free (list);
-}
-
-static void
-action_hide_all_cb (GtkAction *action,
-                    EAttachmentView *view)
-{
-       EAttachmentStore *store;
-       GList *list, *iter;
-
-       store = e_attachment_view_get_store (view);
-       list = e_attachment_store_get_attachments (store);
-
-       for (iter = list; iter != NULL; iter = iter->next) {
-               EAttachment *attachment;
-
-               attachment = E_ATTACHMENT (iter->data);
-               e_attachment_set_shown (attachment, FALSE);
-       }
-
-       g_list_foreach (list, (GFunc) g_object_unref, NULL);
-       g_list_free (list);
-}
-
-static void
 action_open_with_cb (GtkAction *action,
                      EAttachmentView *view)
 {
@@ -342,78 +296,6 @@ exit:
        g_list_free (list);
 }
 
-static void
-action_show_cb (GtkAction *action,
-                EAttachmentView *view)
-{
-       EAttachment *attachment;
-       GList *list;
-
-       list = e_attachment_view_get_selected_attachments (view);
-       g_return_if_fail (g_list_length (list) == 1);
-       attachment = list->data;
-
-       e_attachment_set_shown (attachment, TRUE);
-
-       g_list_foreach (list, (GFunc) g_object_unref, NULL);
-       g_list_free (list);
-}
-
-static void
-action_show_all_cb (GtkAction *action,
-                    EAttachmentView *view)
-{
-       EAttachmentStore *store;
-       GList *list, *iter;
-
-       store = e_attachment_view_get_store (view);
-       list = e_attachment_store_get_attachments (store);
-
-       for (iter = list; iter != NULL; iter = iter->next) {
-               EAttachment *attachment;
-
-               attachment = E_ATTACHMENT (iter->data);
-               e_attachment_set_shown (attachment, TRUE);
-       }
-
-       g_list_foreach (list, (GFunc) g_object_unref, NULL);
-       g_list_free (list);
-}
-
-static void
-action_zoom_to_100_cb (GtkAction *action,
-                      EAttachmentView *view)
-{
-       EAttachment *attachment;
-       GList *list;
-
-       list = e_attachment_view_get_selected_attachments (view);
-       g_return_if_fail (g_list_length (list) == 1);
-       attachment = list->data;
-
-       e_attachment_set_zoom_to_window (attachment, FALSE);
-
-       g_list_foreach (list, (GFunc) g_object_unref, NULL);
-       g_list_free (list);
-}
-
-static void
-action_zoom_to_window_cb (GtkAction *action,
-                         EAttachmentView *view)
-{
-       EAttachment *attachment;
-       GList *list;
-
-       list = e_attachment_view_get_selected_attachments (view);
-       g_return_if_fail (g_list_length (list) == 1);
-       attachment = list->data;
-
-       e_attachment_set_zoom_to_window (attachment, TRUE);
-
-       g_list_foreach (list, (GFunc) g_object_unref, NULL);
-       g_list_free (list);
-}
-
 static GtkActionEntry standard_entries[] = {
 
        { "cancel",
@@ -478,51 +360,6 @@ static GtkActionEntry editable_entries[] = {
          G_CALLBACK (action_remove_cb) }
 };
 
-static GtkActionEntry inline_entries[] = {
-
-       { "hide",
-         NULL,
-         N_("_Hide"),
-         NULL,
-         NULL,  /* XXX Add a tooltip! */
-         G_CALLBACK (action_hide_cb) },
-
-       { "hide-all",
-         NULL,
-         N_("Hid_e All"),
-         NULL,
-         NULL,  /* XXX Add a tooltip! */
-         G_CALLBACK (action_hide_all_cb) },
-
-       { "show",
-         NULL,
-         N_("_View Inline"),
-         NULL,
-         NULL,  /* XXX Add a tooltip! */
-         G_CALLBACK (action_show_cb) },
-
-       { "show-all",
-         NULL,
-         N_("Vie_w All Inline"),
-         NULL,
-         NULL,  /* XXX Add a tooltip! */
-         G_CALLBACK (action_show_all_cb) },
-
-       { "zoom-to-100",
-         NULL,
-         N_("_Zoom to 100%"),
-         NULL,
-         N_("Zoom the image to its natural size"),
-         G_CALLBACK (action_zoom_to_100_cb) },
-
-       { "zoom-to-window",
-         NULL,
-         N_("_Zoom to window"),
-         NULL,
-         N_("Zoom large images to not be wider than the window width"),
-         G_CALLBACK (action_zoom_to_window_cb) }
-};
-
 static void
 call_attachment_load_handle_error (GObject *source_object,
                                   GAsyncResult *result,
@@ -745,76 +582,32 @@ attachment_view_update_actions (EAttachmentView *view)
 {
        EAttachmentViewPrivate *priv;
        EAttachment *attachment;
-       EAttachmentStore *store;
        GtkActionGroup *action_group;
        GtkAction *action;
        GList *list, *iter;
-       guint n_shown = 0;
-       guint n_hidden = 0;
        guint n_selected;
        gboolean busy = FALSE;
-       gboolean can_show = FALSE;
-       gboolean shown = FALSE;
-       gboolean is_image = FALSE;
-       gboolean zoom_to_window = FALSE;
-       gboolean visible;
 
        g_return_if_fail (E_IS_ATTACHMENT_VIEW (view));
 
        priv = e_attachment_view_get_private (view);
 
-       store = e_attachment_view_get_store (view);
-       list = e_attachment_store_get_attachments (store);
-
-       for (iter = list; iter != NULL; iter = iter->next) {
-               attachment = iter->data;
-
-               if (!e_attachment_get_can_show (attachment))
-                       continue;
-
-               if (e_attachment_get_shown (attachment))
-                       n_shown++;
-               else
-                       n_hidden++;
-       }
-
-       g_list_foreach (list, (GFunc) g_object_unref, NULL);
-       g_list_free (list);
-
        list = e_attachment_view_get_selected_attachments (view);
        n_selected = g_list_length (list);
 
        if (n_selected == 1) {
-               gchar *mime_type;
-
                attachment = g_object_ref (list->data);
-               mime_type = e_attachment_dup_mime_type (attachment);
+
                busy |= e_attachment_get_loading (attachment);
                busy |= e_attachment_get_saving (attachment);
-               can_show = e_attachment_get_can_show (attachment);
-               shown = e_attachment_get_shown (attachment);
-               zoom_to_window = e_attachment_get_zoom_to_window (attachment);
-               is_image = can_show && mime_type && g_ascii_strncasecmp (mime_type, "image/", 6) == 0;
-
-               g_free (mime_type);
        } else
                attachment = NULL;
 
-       g_list_foreach (list, (GFunc) g_object_unref, NULL);
-       g_list_free (list);
+       g_list_free_full (list, g_object_unref);
 
        action = e_attachment_view_get_action (view, "cancel");
        gtk_action_set_visible (action, busy);
 
-       action = e_attachment_view_get_action (view, "hide");
-       gtk_action_set_visible (action, can_show && shown);
-
-       /* Show this action if there are multiple viewable
-        * attachments, and at least one of them is shown. */
-       visible = (n_shown + n_hidden > 1) && (n_shown > 0);
-       action = e_attachment_view_get_action (view, "hide-all");
-       gtk_action_set_visible (action, visible);
-
        action = e_attachment_view_get_action (view, "open-with");
        gtk_action_set_visible (action, !busy && n_selected == 1);
 
@@ -827,29 +620,16 @@ attachment_view_update_actions (EAttachmentView *view)
        action = e_attachment_view_get_action (view, "save-as");
        gtk_action_set_visible (action, !busy && n_selected > 0);
 
-       action = e_attachment_view_get_action (view, "show");
-       gtk_action_set_visible (action, can_show && !shown);
-
-       action = e_attachment_view_get_action (view, "zoom-to-100");
-       gtk_action_set_visible (action, can_show && shown && is_image && zoom_to_window);
-
-       action = e_attachment_view_get_action (view, "zoom-to-window");
-       gtk_action_set_visible (action, can_show && shown && is_image && !zoom_to_window);
-
-       /* Show this action if there are multiple viewable
-        * attachments, and at least one of them is hidden. */
-       visible = (n_shown + n_hidden > 1) && (n_hidden > 0);
-       action = e_attachment_view_get_action (view, "show-all");
-       gtk_action_set_visible (action, visible);
-
        /* Clear out the "openwith" action group. */
        gtk_ui_manager_remove_ui (priv->ui_manager, priv->merge_id);
        action_group = e_attachment_view_get_action_group (view, "openwith");
        e_action_group_remove_all_actions (action_group);
        gtk_ui_manager_ensure_update (priv->ui_manager);
 
-       if (attachment == NULL || busy)
+       if (!attachment || busy) {
+               g_clear_object (&attachment);
                return;
+       }
 
        list = e_attachment_list_apps (attachment);
 
@@ -913,9 +693,8 @@ attachment_view_update_actions (EAttachmentView *view)
                g_free (action_tooltip);
        }
 
+       g_list_free_full (list, g_object_unref);
        g_object_unref (attachment);
-       g_list_foreach (list, (GFunc) g_object_unref, NULL);
-       g_list_free (list);
 }
 
 static void
@@ -1005,13 +784,6 @@ e_attachment_view_init (EAttachmentView *view)
                action_group, editable_entries,
                G_N_ELEMENTS (editable_entries), view);
 
-       action_group = e_attachment_view_add_action_group (view, "inline");
-
-       gtk_action_group_add_actions (
-               action_group, inline_entries,
-               G_N_ELEMENTS (inline_entries), view);
-       gtk_action_group_set_visible (action_group, FALSE);
-
        e_attachment_view_add_action_group (view, "openwith");
 
        /* Because we are loading from a hard-coded string, there is
diff --git a/e-util/e-attachment.c b/e-util/e-attachment.c
index f9f8e9b..d451f18 100644
--- a/e-util/e-attachment.c
+++ b/e-util/e-attachment.c
@@ -35,7 +35,6 @@
 
 #include <libedataserver/libedataserver.h>
 
-#include "e-attachment-store.h"
 #include "e-icon-factory.h"
 #include "e-mktemp.h"
 #include "e-misc-utils.h"
@@ -77,8 +76,7 @@ struct _EAttachmentPrivate {
        guint can_show : 1;
        guint loading : 1;
        guint saving : 1;
-       guint shown : 1;
-       guint zoom_to_window : 1;
+       guint initially_shown : 1;
 
        guint save_self      : 1;
        guint save_extracted : 1;
@@ -86,12 +84,6 @@ struct _EAttachmentPrivate {
        CamelCipherValidityEncrypt encrypted;
        CamelCipherValiditySign signed_;
 
-       /* This is a reference to our row in an EAttachmentStore,
-        * serving as a means of broadcasting "row-changed" signals.
-        * If we are removed from the store, we lazily free the
-        * reference when it is found to be to be invalid. */
-       GtkTreeRowReference *reference;
-
        /* These are IDs for idle callbacks,
         * protected by the idle_lock mutex. */
        GMutex idle_lock;
@@ -111,15 +103,23 @@ enum {
        PROP_LOADING,
        PROP_MIME_PART,
        PROP_PERCENT,
-       PROP_REFERENCE,
        PROP_SAVE_SELF,
        PROP_SAVE_EXTRACTED,
        PROP_SAVING,
-       PROP_SHOWN,
-       PROP_SIGNED,
-       PROP_ZOOM_TO_WINDOW
+       PROP_INITIALLY_SHOWN,
+       PROP_SIGNED
+};
+
+enum {
+       LOAD_FAILED,
+       UPDATE_FILE_INFO,
+       UPDATE_ICON,
+       UPDATE_PROGRESS,
+       LAST_SIGNAL
 };
 
+static guint signals[LAST_SIGNAL];
+
 G_DEFINE_TYPE (
        EAttachment,
        e_attachment,
@@ -250,10 +250,6 @@ static gboolean
 attachment_update_file_info_columns_idle_cb (gpointer weak_ref)
 {
        EAttachment *attachment;
-       GtkTreeRowReference *reference;
-       GtkTreeModel *model;
-       GtkTreePath *path;
-       GtkTreeIter iter;
        GFileInfo *file_info;
        const gchar *content_type;
        const gchar *display_name;
@@ -271,19 +267,10 @@ attachment_update_file_info_columns_idle_cb (gpointer weak_ref)
        attachment->priv->update_file_info_columns_idle_id = 0;
        g_mutex_unlock (&attachment->priv->idle_lock);
 
-       reference = e_attachment_get_reference (attachment);
-       if (!gtk_tree_row_reference_valid (reference))
-               goto exit;
-
        file_info = e_attachment_ref_file_info (attachment);
        if (file_info == NULL)
                goto exit;
 
-       model = gtk_tree_row_reference_get_model (reference);
-       path = gtk_tree_row_reference_get_path (reference);
-       gtk_tree_model_get_iter (model, &iter, path);
-       gtk_tree_path_free (path);
-
        content_type = g_file_info_get_content_type (file_info);
        display_name = g_file_info_get_display_name (file_info);
        size = g_file_info_get_size (file_info);
@@ -298,18 +285,11 @@ attachment_update_file_info_columns_idle_cb (gpointer weak_ref)
        }
 
        if (size > 0)
-               caption = g_strdup_printf (
-                       "%s\n(%s)", description, display_size);
+               caption = g_strdup_printf ("%s\n(%s)", description, display_size);
        else
                caption = g_strdup (description);
 
-       gtk_list_store_set (
-               GTK_LIST_STORE (model), &iter,
-               E_ATTACHMENT_STORE_COLUMN_CAPTION, caption,
-               E_ATTACHMENT_STORE_COLUMN_CONTENT_TYPE, content_desc,
-               E_ATTACHMENT_STORE_COLUMN_DESCRIPTION, description,
-               E_ATTACHMENT_STORE_COLUMN_SIZE, size,
-               -1);
+       g_signal_emit (attachment, signals[UPDATE_FILE_INFO], 0, caption, content_desc, description, (gint64) 
size);
 
        g_free (content_desc);
        g_free (display_size);
@@ -347,10 +327,6 @@ static gboolean
 attachment_update_icon_column_idle_cb (gpointer weak_ref)
 {
        EAttachment *attachment;
-       GtkTreeRowReference *reference;
-       GtkTreeModel *model;
-       GtkTreePath *path;
-       GtkTreeIter iter;
        GFileInfo *file_info;
        GCancellable *cancellable;
        GIcon *icon = NULL;
@@ -365,15 +341,6 @@ attachment_update_icon_column_idle_cb (gpointer weak_ref)
        attachment->priv->update_icon_column_idle_id = 0;
        g_mutex_unlock (&attachment->priv->idle_lock);
 
-       reference = e_attachment_get_reference (attachment);
-       if (!gtk_tree_row_reference_valid (reference))
-               goto exit;
-
-       model = gtk_tree_row_reference_get_model (reference);
-       path = gtk_tree_row_reference_get_path (reference);
-       gtk_tree_model_get_iter (model, &iter, path);
-       gtk_tree_path_free (path);
-
        cancellable = attachment->priv->cancellable;
        file_info = e_attachment_ref_file_info (attachment);
 
@@ -475,10 +442,7 @@ attachment_update_icon_column_idle_cb (gpointer weak_ref)
                icon = emblemed_icon;
        }
 
-       gtk_list_store_set (
-               GTK_LIST_STORE (model), &iter,
-               E_ATTACHMENT_STORE_COLUMN_ICON, icon,
-               -1);
+       g_signal_emit (attachment, signals[UPDATE_ICON], 0, icon);
 
        /* Cache the icon to reuse for things like drag-n-drop. */
        if (attachment->priv->icon != NULL)
@@ -517,10 +481,6 @@ static gboolean
 attachment_update_progress_columns_idle_cb (gpointer weak_ref)
 {
        EAttachment *attachment;
-       GtkTreeRowReference *reference;
-       GtkTreeModel *model;
-       GtkTreePath *path;
-       GtkTreeIter iter;
        gboolean loading;
        gboolean saving;
        gint percent;
@@ -533,26 +493,12 @@ attachment_update_progress_columns_idle_cb (gpointer weak_ref)
        attachment->priv->update_progress_columns_idle_id = 0;
        g_mutex_unlock (&attachment->priv->idle_lock);
 
-       reference = e_attachment_get_reference (attachment);
-       if (!gtk_tree_row_reference_valid (reference))
-               goto exit;
-
-       model = gtk_tree_row_reference_get_model (reference);
-       path = gtk_tree_row_reference_get_path (reference);
-       gtk_tree_model_get_iter (model, &iter, path);
-       gtk_tree_path_free (path);
-
        /* Don't show progress bars until we have progress to report. */
        percent = e_attachment_get_percent (attachment);
        loading = e_attachment_get_loading (attachment) && (percent > 0);
        saving = e_attachment_get_saving (attachment) && (percent > 0);
 
-       gtk_list_store_set (
-               GTK_LIST_STORE (model), &iter,
-               E_ATTACHMENT_STORE_COLUMN_LOADING, loading,
-               E_ATTACHMENT_STORE_COLUMN_PERCENT, percent,
-               E_ATTACHMENT_STORE_COLUMN_SAVING, saving,
-               -1);
+       g_signal_emit (attachment, signals[UPDATE_PROGRESS], 0, loading, saving, percent);
 
 exit:
        g_clear_object (&attachment);
@@ -583,10 +529,6 @@ static void
 attachment_set_loading (EAttachment *attachment,
                         gboolean loading)
 {
-       GtkTreeRowReference *reference;
-
-       reference = e_attachment_get_reference (attachment);
-
        attachment->priv->percent = 0;
        attachment->priv->loading = loading;
        attachment->priv->last_percent_notify = 0;
@@ -595,12 +537,6 @@ attachment_set_loading (EAttachment *attachment,
        g_object_notify (G_OBJECT (attachment), "percent");
        g_object_notify (G_OBJECT (attachment), "loading");
        g_object_thaw_notify (G_OBJECT (attachment));
-
-       if (gtk_tree_row_reference_valid (reference)) {
-               GtkTreeModel *model;
-               model = gtk_tree_row_reference_get_model (reference);
-               g_object_notify (G_OBJECT (model), "num-loading");
-       }
 }
 
 static void
@@ -696,8 +632,8 @@ attachment_set_property (GObject *object,
                                g_value_get_object (value));
                        return;
 
-               case PROP_SHOWN:
-                       e_attachment_set_shown (
+               case PROP_INITIALLY_SHOWN:
+                       e_attachment_set_initially_shown (
                                E_ATTACHMENT (object),
                                g_value_get_boolean (value));
                        return;
@@ -708,12 +644,6 @@ attachment_set_property (GObject *object,
                                g_value_get_object (value));
                        return;
 
-               case PROP_REFERENCE:
-                       e_attachment_set_reference (
-                               E_ATTACHMENT (object),
-                               g_value_get_boxed (value));
-                       return;
-
                case PROP_SIGNED:
                        e_attachment_set_signed (
                                E_ATTACHMENT (object),
@@ -731,12 +661,6 @@ attachment_set_property (GObject *object,
                                E_ATTACHMENT (object),
                                g_value_get_boolean (value));
                        return;
-
-               case PROP_ZOOM_TO_WINDOW:
-                       e_attachment_set_zoom_to_window (
-                               E_ATTACHMENT (object),
-                               g_value_get_boolean (value));
-                       return;
        }
 
        G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
@@ -791,10 +715,10 @@ attachment_get_property (GObject *object,
                                E_ATTACHMENT (object)));
                        return;
 
-               case PROP_SHOWN:
+               case PROP_INITIALLY_SHOWN:
                        g_value_set_boolean (
                                value,
-                               e_attachment_get_shown (
+                               e_attachment_get_initially_shown (
                                E_ATTACHMENT (object)));
                        return;
 
@@ -819,13 +743,6 @@ attachment_get_property (GObject *object,
                                E_ATTACHMENT (object)));
                        return;
 
-               case PROP_REFERENCE:
-                       g_value_set_boxed (
-                               value,
-                               e_attachment_get_reference (
-                               E_ATTACHMENT (object)));
-                       return;
-
                case PROP_SAVE_SELF:
                        g_value_set_boolean (
                                value,
@@ -853,13 +770,6 @@ attachment_get_property (GObject *object,
                                e_attachment_get_signed (
                                E_ATTACHMENT (object)));
                        return;
-
-               case PROP_ZOOM_TO_WINDOW:
-                       g_value_set_boolean (
-                               value,
-                               e_attachment_get_zoom_to_window (
-                               E_ATTACHMENT (object)));
-                       return;
        }
 
        G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
@@ -883,10 +793,6 @@ attachment_dispose (GObject *object)
                priv->emblem_timeout_id = 0;
        }
 
-       /* This accepts NULL arguments. */
-       gtk_tree_row_reference_free (priv->reference);
-       priv->reference = NULL;
-
        /* Chain up to parent's dispose() method. */
        G_OBJECT_CLASS (e_attachment_parent_class)->dispose (object);
 }
@@ -1030,16 +936,6 @@ e_attachment_class_init (EAttachmentClass *class)
 
        g_object_class_install_property (
                object_class,
-               PROP_REFERENCE,
-               g_param_spec_boxed (
-                       "reference",
-                       "Reference",
-                       NULL,
-                       GTK_TYPE_TREE_ROW_REFERENCE,
-                       G_PARAM_READWRITE));
-
-       g_object_class_install_property (
-               object_class,
                PROP_SAVE_SELF,
                g_param_spec_boolean (
                        "save-self",
@@ -1070,10 +966,10 @@ e_attachment_class_init (EAttachmentClass *class)
 
        g_object_class_install_property (
                object_class,
-               PROP_SHOWN,
+               PROP_INITIALLY_SHOWN,
                g_param_spec_boolean (
-                       "shown",
-                       "Shown",
+                       "initially-shown",
+                       "Initially Shown",
                        NULL,
                        FALSE,
                        G_PARAM_READWRITE |
@@ -1093,16 +989,46 @@ e_attachment_class_init (EAttachmentClass *class)
                        G_PARAM_READWRITE |
                        G_PARAM_CONSTRUCT));
 
-       g_object_class_install_property (
-               object_class,
-               PROP_ZOOM_TO_WINDOW,
-               g_param_spec_boolean (
-                       "zoom-to-window",
-                       "Zoom to window",
-                       NULL,
-                       TRUE,
-                       G_PARAM_READWRITE |
-                       G_PARAM_CONSTRUCT));
+       signals[UPDATE_FILE_INFO] = g_signal_new (
+               "update-file-info",
+               G_TYPE_FROM_CLASS (class),
+               G_SIGNAL_RUN_LAST,
+               G_STRUCT_OFFSET (EAttachmentClass, update_file_info),
+               NULL, NULL, NULL,
+               G_TYPE_NONE, 4,
+               G_TYPE_STRING,
+               G_TYPE_STRING,
+               G_TYPE_STRING,
+               G_TYPE_INT64);
+
+       signals[UPDATE_ICON] = g_signal_new (
+               "update-icon",
+               G_TYPE_FROM_CLASS (class),
+               G_SIGNAL_RUN_LAST,
+               G_STRUCT_OFFSET (EAttachmentClass, update_icon),
+               NULL, NULL, NULL,
+               G_TYPE_NONE, 1,
+               G_TYPE_ICON);
+
+       signals[UPDATE_PROGRESS] = g_signal_new (
+               "update-progress",
+               G_TYPE_FROM_CLASS (class),
+               G_SIGNAL_RUN_LAST,
+               G_STRUCT_OFFSET (EAttachmentClass, update_progress),
+               NULL, NULL, NULL,
+               G_TYPE_NONE, 3,
+               G_TYPE_BOOLEAN,
+               G_TYPE_BOOLEAN,
+               G_TYPE_INT);
+
+       signals[LOAD_FAILED] = g_signal_new (
+               "load-failed",
+               G_TYPE_FROM_CLASS (class),
+               G_SIGNAL_RUN_LAST,
+               G_STRUCT_OFFSET (EAttachmentClass, load_failed),
+               NULL, NULL, NULL,
+               G_TYPE_NONE, 0,
+               G_TYPE_NONE);
 }
 
 static void
@@ -1140,18 +1066,6 @@ e_attachment_init (EAttachment *attachment)
                attachment, "notify::percent",
                G_CALLBACK (attachment_update_progress_columns), NULL);
 
-       g_signal_connect (
-               attachment, "notify::reference",
-               G_CALLBACK (attachment_update_file_info_columns), NULL);
-
-       g_signal_connect (
-               attachment, "notify::reference",
-               G_CALLBACK (attachment_update_icon_column), NULL);
-
-       g_signal_connect (
-               attachment, "notify::reference",
-               G_CALLBACK (attachment_update_progress_columns), NULL);
-
        e_signal_connect_notify (
                attachment, "notify::saving",
                G_CALLBACK (attachment_update_icon_column), NULL);
@@ -1476,7 +1390,6 @@ void
 e_attachment_set_file_info (EAttachment *attachment,
                             GFileInfo *file_info)
 {
-       GtkTreeRowReference *reference;
        GIcon *icon;
 
        g_return_if_fail (E_IS_ATTACHMENT (attachment));
@@ -1501,14 +1414,6 @@ e_attachment_set_file_info (EAttachment *attachment,
        g_mutex_unlock (&attachment->priv->property_lock);
 
        g_object_notify (G_OBJECT (attachment), "file-info");
-
-       /* Tell the EAttachmentStore its total size changed. */
-       reference = e_attachment_get_reference (attachment);
-       if (gtk_tree_row_reference_valid (reference)) {
-               GtkTreeModel *model;
-               model = gtk_tree_row_reference_get_model (reference);
-               g_object_notify (G_OBJECT (model), "total-size");
-       }
 }
 
 /**
@@ -1616,29 +1521,6 @@ e_attachment_get_percent (EAttachment *attachment)
        return attachment->priv->percent;
 }
 
-GtkTreeRowReference *
-e_attachment_get_reference (EAttachment *attachment)
-{
-       g_return_val_if_fail (E_IS_ATTACHMENT (attachment), NULL);
-
-       return attachment->priv->reference;
-}
-
-void
-e_attachment_set_reference (EAttachment *attachment,
-                            GtkTreeRowReference *reference)
-{
-       g_return_if_fail (E_IS_ATTACHMENT (attachment));
-
-       if (reference != NULL)
-               reference = gtk_tree_row_reference_copy (reference);
-
-       gtk_tree_row_reference_free (attachment->priv->reference);
-       attachment->priv->reference = reference;
-
-       g_object_notify (G_OBJECT (attachment), "reference");
-}
-
 gboolean
 e_attachment_get_saving (EAttachment *attachment)
 {
@@ -1648,44 +1530,22 @@ e_attachment_get_saving (EAttachment *attachment)
 }
 
 gboolean
-e_attachment_get_shown (EAttachment *attachment)
-{
-       g_return_val_if_fail (E_IS_ATTACHMENT (attachment), FALSE);
-
-       return attachment->priv->shown;
-}
-
-void
-e_attachment_set_shown (EAttachment *attachment,
-                        gboolean shown)
-{
-       g_return_if_fail (E_IS_ATTACHMENT (attachment));
-
-       attachment->priv->shown = shown;
-
-       g_object_notify (G_OBJECT (attachment), "shown");
-}
-
-gboolean
-e_attachment_get_zoom_to_window (EAttachment *attachment)
+e_attachment_get_initially_shown (EAttachment *attachment)
 {
        g_return_val_if_fail (E_IS_ATTACHMENT (attachment), FALSE);
 
-       return attachment->priv->zoom_to_window;
+       return attachment->priv->initially_shown;
 }
 
 void
-e_attachment_set_zoom_to_window (EAttachment *attachment,
-                                gboolean zoom_to_window)
+e_attachment_set_initially_shown (EAttachment *attachment,
+                                 gboolean initially_shown)
 {
        g_return_if_fail (E_IS_ATTACHMENT (attachment));
 
-       if ((attachment->priv->zoom_to_window ? 1 : 0) == (zoom_to_window ? 1 : 0))
-               return;
-
-       attachment->priv->zoom_to_window = zoom_to_window;
+       attachment->priv->initially_shown = initially_shown;
 
-       g_object_notify (G_OBJECT (attachment), "zoom-to-window");
+       g_object_notify (G_OBJECT (attachment), "initially-shown");
 }
 
 gboolean
@@ -1868,6 +1728,16 @@ exit:
        return app_info_list;
 }
 
+void
+e_attachment_update_store_columns (EAttachment *attachment)
+{
+       g_return_if_fail (E_IS_ATTACHMENT (attachment));
+
+       attachment_update_file_info_columns (attachment);
+       attachment_update_icon_column (attachment);
+       attachment_update_progress_columns (attachment);
+}
+
 /************************* e_attachment_load_async() *************************/
 
 typedef struct _LoadContext LoadContext;
@@ -2500,7 +2370,6 @@ e_attachment_load_handle_error (EAttachment *attachment,
 {
        GtkWidget *dialog;
        GFileInfo *file_info;
-       GtkTreeRowReference *reference;
        const gchar *display_name;
        const gchar *primary_text;
        GError *error = NULL;
@@ -2512,17 +2381,7 @@ e_attachment_load_handle_error (EAttachment *attachment,
        if (e_attachment_load_finish (attachment, result, &error))
                return;
 
-       /* XXX Calling EAttachmentStore functions from here violates
-        *     the abstraction, but for now it's not hurting anything. */
-       reference = e_attachment_get_reference (attachment);
-       if (gtk_tree_row_reference_valid (reference)) {
-               GtkTreeModel *model;
-
-               model = gtk_tree_row_reference_get_model (reference);
-
-               e_attachment_store_remove_attachment (
-                       E_ATTACHMENT_STORE (model), attachment);
-       }
+       g_signal_emit (attachment, signals[LOAD_FAILED], 0, NULL);
 
        /* Ignore cancellations. */
        if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) {
diff --git a/e-util/e-attachment.h b/e-util/e-attachment.h
index d371008..5fa662e 100644
--- a/e-util/e-attachment.h
+++ b/e-util/e-attachment.h
@@ -60,6 +60,20 @@ struct _EAttachment {
 
 struct _EAttachmentClass {
        GObjectClass parent_class;
+
+       /* Signals */
+       void    (*update_file_info)             (EAttachment *attachment,
+                                                const gchar *caption,
+                                                const gchar *content_type,
+                                                const gchar *description,
+                                                gint64 size);
+       void    (*update_icon)                  (EAttachment *attachment,
+                                                GIcon *icon);
+       void    (*update_progress)              (EAttachment *attachment,
+                                                gboolean loading,
+                                                gboolean saving,
+                                                gint percent);
+       void    (*load_failed)                  (EAttachment *attachment);
 };
 
 GType          e_attachment_get_type           (void) G_GNUC_CONST;
@@ -92,17 +106,10 @@ CamelMimePart *    e_attachment_ref_mime_part      (EAttachment *attachment);
 void           e_attachment_set_mime_part      (EAttachment *attachment,
                                                 CamelMimePart *mime_part);
 gint           e_attachment_get_percent        (EAttachment *attachment);
-GtkTreeRowReference *
-               e_attachment_get_reference      (EAttachment *attachment);
-void           e_attachment_set_reference      (EAttachment *attachment,
-                                                GtkTreeRowReference *reference);
 gboolean       e_attachment_get_saving         (EAttachment *attachment);
-gboolean       e_attachment_get_shown          (EAttachment *attachment);
-void           e_attachment_set_shown          (EAttachment *attachment,
-                                                gboolean shown);
-gboolean       e_attachment_get_zoom_to_window (EAttachment *attachment);
-void           e_attachment_set_zoom_to_window (EAttachment *attachment,
-                                                gboolean zoom_to_window);
+gboolean       e_attachment_get_initially_shown(EAttachment *attachment);
+void           e_attachment_set_initially_shown(EAttachment *attachment,
+                                                gboolean initially_shown);
 gboolean       e_attachment_get_save_self      (EAttachment *attachment);
 void           e_attachment_set_save_self      (EAttachment *attachment,
                                                 gboolean save_self);
@@ -121,6 +128,8 @@ gchar *             e_attachment_dup_description    (EAttachment *attachment);
 gchar *                e_attachment_dup_thumbnail_path (EAttachment *attachment);
 gboolean       e_attachment_is_rfc822          (EAttachment *attachment);
 GList *                e_attachment_list_apps          (EAttachment *attachment);
+void           e_attachment_update_store_columns
+                                               (EAttachment *attachment);
 
 /* Asynchronous Operations */
 void           e_attachment_load_async         (EAttachment *attachment,
diff --git a/e-util/e-content-editor.c b/e-util/e-content-editor.c
new file mode 100644
index 0000000..2224af0
--- /dev/null
+++ b/e-util/e-content-editor.c
@@ -0,0 +1,3583 @@
+/*
+ * Copyright (C) 2016 Red Hat, Inc. (www.redhat.com)
+ *
+ * This library 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 library 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 Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <glib.h>
+#include <glib-object.h>
+
+#include <libedataserver/libedataserver.h>
+
+#include "e-html-editor.h"
+#include "e-util-enumtypes.h"
+#include "e-content-editor.h"
+
+G_DEFINE_INTERFACE (EContentEditor, e_content_editor, GTK_TYPE_WIDGET);
+
+enum {
+       LOAD_FINISHED,
+       PASTE_CLIPBOARD,
+       PASTE_PRIMARY_CLIPBOARD,
+       CONTEXT_MENU_REQUESTED,
+       FIND_DONE,
+       REPLACE_ALL_DONE,
+       LAST_SIGNAL
+};
+
+static guint signals[LAST_SIGNAL] = { 0 };
+
+static void
+e_content_editor_default_init (EContentEditorInterface *iface)
+{
+       /**
+        * EContentEditor:can-copy
+        *
+        * Determines whether it's possible to copy to clipboard. The action
+        * is usually disabled when there is no selection to copy.
+        */
+       g_object_interface_install_property (
+               iface,
+               g_param_spec_boolean (
+                       "can-copy",
+                       "Can Copy",
+                       NULL,
+                       FALSE,
+                       G_PARAM_READABLE |
+                       G_PARAM_STATIC_STRINGS));
+
+       /**
+        * EContentEditor:can-cut
+        *
+        * Determines whether it's possible to cut to clipboard. The action
+        * is usually disabled when there is no selection to cut.
+        */
+       g_object_interface_install_property (
+               iface,
+               g_param_spec_boolean (
+                       "can-cut",
+                       "Can Cut",
+                       NULL,
+                       FALSE,
+                       G_PARAM_READABLE |
+                       G_PARAM_STATIC_STRINGS));
+
+       /**
+        * EContentEditor:can-paste
+        *
+        * Determines whether it's possible to paste from clipboard. The action
+        * is usually disabled when there is no valid content in clipboard to
+        * paste.
+        */
+       g_object_interface_install_property (
+               iface,
+               g_param_spec_boolean (
+                       "can-paste",
+                       "Can Paste",
+                       NULL,
+                       FALSE,
+                       G_PARAM_READABLE |
+                       G_PARAM_STATIC_STRINGS));
+
+       /**
+        * EContentEditor:can-redo
+        *
+        * Determines whether it's possible to redo previous action. The action
+        * is usually disabled when there is no action to redo.
+        */
+       g_object_interface_install_property (
+               iface,
+               g_param_spec_boolean (
+                       "can-redo",
+                       "Can Redo",
+                       NULL,
+                       FALSE,
+                       G_PARAM_READABLE |
+                       G_PARAM_STATIC_STRINGS));
+
+       /**
+        * EContentEditor:can-undo
+        *
+        * Determines whether it's possible to undo last action. The action
+        * is usually disabled when there is no previous action to undo.
+        */
+       g_object_interface_install_property (
+               iface,
+               g_param_spec_boolean (
+                       "can-undo",
+                       "Can Undo",
+                       NULL,
+                       FALSE,
+                       G_PARAM_READABLE |
+                       G_PARAM_STATIC_STRINGS));
+
+       /**
+        * EContentEditor:editable
+        *
+        * Determines whether the editor is editable or read-only.
+        **/
+       g_object_interface_install_property (
+               iface,
+               g_param_spec_boolean (
+                       "editable",
+                       "Editable",
+                       "Wheter editor is editable",
+                       TRUE,
+                       G_PARAM_READWRITE |
+                       G_PARAM_STATIC_STRINGS));
+
+       /**
+        * EContentEditor:changed
+        *
+        * Determines whether document has been modified
+        */
+       g_object_interface_install_property (
+               iface,
+               g_param_spec_boolean (
+                       "changed",
+                       "Changed property",
+                       "Whether editor changed",
+                       FALSE,
+                       G_PARAM_READWRITE |
+                       G_PARAM_STATIC_STRINGS));
+
+       /**
+        * EContentEditor:html-mode
+        *
+        * Determines whether HTML or plain text mode is enabled.
+        **/
+       g_object_interface_install_property (
+               iface,
+               g_param_spec_boolean (
+                       "html-mode",
+                       "HTML Mode",
+                       "Edit HTML or plain text",
+                       TRUE,
+                       G_PARAM_READWRITE |
+                       G_PARAM_STATIC_STRINGS));
+
+       /**
+        * EContentEditor:alignment
+        *
+        * Holds alignment of current paragraph.
+        */
+       g_object_interface_install_property (
+               iface,
+               g_param_spec_enum (
+                       "alignment",
+                       NULL,
+                       NULL,
+                       E_TYPE_CONTENT_EDITOR_ALIGNMENT,
+                       E_CONTENT_EDITOR_ALIGNMENT_LEFT,
+                       G_PARAM_READWRITE));
+
+       /**
+        * EContentEditor:background-color
+        *
+        * Holds background color of current selection or at current cursor
+        * position.
+        */
+       g_object_interface_install_property (
+               iface,
+               g_param_spec_boxed (
+                       "background-color",
+                       NULL,
+                       NULL,
+                       GDK_TYPE_RGBA,
+                       G_PARAM_READWRITE));
+
+       /**
+        * EContentEditor:block-format
+        *
+        * Holds block format of current paragraph. See
+        * #EContentEditorBlockFormat for valid values.
+        */
+       g_object_interface_install_property (
+               iface,
+               g_param_spec_enum (
+                       "block-format",
+                       NULL,
+                       NULL,
+                       E_TYPE_CONTENT_EDITOR_BLOCK_FORMAT,
+                       E_CONTENT_EDITOR_BLOCK_FORMAT_NONE,
+                       G_PARAM_READWRITE));
+
+       /**
+        * EContentEditor:bold
+        *
+        * Holds whether current selection or text at current cursor position
+        * is bold.
+        */
+       g_object_interface_install_property (
+               iface,
+               g_param_spec_boolean (
+                       "bold",
+                       NULL,
+                       NULL,
+                       FALSE,
+                       G_PARAM_READWRITE |
+                       G_PARAM_STATIC_STRINGS));
+
+       /**
+        * EContentEditor:font-color
+        *
+        * Holds font color of current selection or at current cursor position.
+        */
+       g_object_interface_install_property (
+               iface,
+               g_param_spec_boxed (
+                       "font-color",
+                       NULL,
+                       NULL,
+                       GDK_TYPE_RGBA,
+                       G_PARAM_READWRITE |
+                       G_PARAM_STATIC_STRINGS));
+
+       /**
+        * EContentEditor:font-name
+        *
+        * Holds name of font in current selection or at current cursor
+        * position.
+        */
+       g_object_interface_install_property (
+               iface,
+               g_param_spec_string (
+                       "font-name",
+                       NULL,
+                       NULL,
+                       NULL,
+                       G_PARAM_READWRITE |
+                       G_PARAM_STATIC_STRINGS));
+
+       /**
+        * EContentEditor:font-size
+        *
+        * Holds point size of current selection or at current cursor position.
+        */
+       g_object_interface_install_property (
+               iface,
+               g_param_spec_int (
+                       "font-size",
+                       NULL,
+                       NULL,
+                       1,
+                       7,
+                       3,
+                       G_PARAM_READWRITE |
+                       G_PARAM_STATIC_STRINGS));
+
+       /**
+        * EContentEditor:indented
+        *
+        * Holds whether current paragraph is indented. This does not include
+        * citations.
+        */
+       g_object_interface_install_property (
+               iface,
+               g_param_spec_boolean (
+                       "indented",
+                       NULL,
+                       NULL,
+                       FALSE,
+                       G_PARAM_READABLE |
+                       G_PARAM_STATIC_STRINGS));
+
+       /**
+        * EContentEditor:italic
+        *
+        * Holds whether current selection or letter at current cursor position
+        * is italic.
+        */
+       g_object_interface_install_property (
+               iface,
+               g_param_spec_boolean (
+                       "italic",
+                       NULL,
+                       NULL,
+                       FALSE,
+                       G_PARAM_READWRITE |
+                       G_PARAM_STATIC_STRINGS));
+
+       /**
+        * EContentEditor:monospaced
+        *
+        * Holds whether current selection or letter at current cursor position
+        * is monospaced.
+        */
+       g_object_interface_install_property (
+               iface,
+               g_param_spec_boolean (
+                       "monospaced",
+                       NULL,
+                       NULL,
+                       FALSE,
+                       G_PARAM_READWRITE |
+                       G_PARAM_STATIC_STRINGS));
+
+       /**
+        * EContentEditor:strikethrough
+        *
+        * Holds whether current selection or letter at current cursor position
+        * is strikethrough.
+        */
+       g_object_interface_install_property (
+               iface,
+               g_param_spec_boolean (
+                       "strikethrough",
+                       NULL,
+                       NULL,
+                       FALSE,
+                       G_PARAM_READWRITE |
+                       G_PARAM_STATIC_STRINGS));
+
+       /**
+        * EContentEditor:superscript
+        *
+        * Holds whether current selection or letter at current cursor position
+        * is in superscript.
+        */
+       g_object_interface_install_property (
+               iface,
+               g_param_spec_boolean (
+                       "superscript",
+                       NULL,
+                       NULL,
+                       FALSE,
+                       G_PARAM_READWRITE |
+                       G_PARAM_STATIC_STRINGS));
+
+       /**
+        * EContentEditor:subscript
+        *
+        * Holds whether current selection or letter at current cursor position
+        * is in subscript.
+        */
+       g_object_interface_install_property (
+               iface,
+               g_param_spec_boolean (
+                       "subscript",
+                       NULL,
+                       NULL,
+                       FALSE,
+                       G_PARAM_READWRITE |
+                       G_PARAM_STATIC_STRINGS));
+
+       /**
+        * EContentEditor:underline
+        *
+        * Holds whether current selection or letter at current cursor position
+        * is underlined.
+        */
+       g_object_interface_install_property (
+               iface,
+               g_param_spec_boolean (
+                       "underline",
+                       NULL,
+                       NULL,
+                       FALSE,
+                       G_PARAM_READWRITE |
+                       G_PARAM_STATIC_STRINGS));
+
+       /**
+        * EContentEditor:spell-check-enabled
+        *
+        * Holds whether the spell checking is enabled.
+        */
+       g_object_interface_install_property (
+               iface,
+               g_param_spec_boolean (
+                       "spell-check-enabled",
+                       NULL,
+                       NULL,
+                       FALSE,
+                       G_PARAM_READWRITE |
+                       G_PARAM_STATIC_STRINGS));
+
+       /**
+        * EContentEditor:spell-checker:
+        *
+        * The #ESpellChecker used for spell checking.
+        **/
+       g_object_interface_install_property (
+               iface,
+               g_param_spec_object (
+                       "spell-checker",
+                       "Spell Checker",
+                       "The spell checker",
+                       E_TYPE_SPELL_CHECKER,
+                       G_PARAM_READABLE |
+                       G_PARAM_STATIC_STRINGS));
+
+       /**
+        * EContentEditor:paste-clipboard
+        *
+        * Emitted when user presses middle button on EContentEditor.
+        */
+       signals[PASTE_CLIPBOARD] = g_signal_new (
+               "paste-clipboard",
+               E_TYPE_CONTENT_EDITOR,
+               G_SIGNAL_RUN_LAST,
+               G_STRUCT_OFFSET (EContentEditorInterface, paste_clipboard),
+               g_signal_accumulator_true_handled, NULL,
+               NULL,
+               G_TYPE_BOOLEAN, 0);
+
+       /**
+        * EContentEditor:paste-primary-clipboard
+        *
+        * Emitted when user presses middle button on EWebKitContentEditor.
+        */
+       signals[PASTE_PRIMARY_CLIPBOARD] = g_signal_new (
+               "paste-primary-clipboard",
+               E_TYPE_CONTENT_EDITOR,
+               G_SIGNAL_RUN_LAST,
+               G_STRUCT_OFFSET (EContentEditorInterface, paste_primary_clipboard),
+               g_signal_accumulator_true_handled, NULL,
+               NULL,
+               G_TYPE_BOOLEAN, 0);
+
+       /**
+        * EContentEditor:load-finished
+        *
+        * Emitted when the content editor has finished loading.
+        */
+       signals[LOAD_FINISHED] = g_signal_new (
+               "load-finished",
+               E_TYPE_CONTENT_EDITOR,
+               G_SIGNAL_RUN_LAST,
+               G_STRUCT_OFFSET (EContentEditorInterface, load_finished),
+               NULL, NULL,
+               NULL,
+               G_TYPE_NONE, 0);
+
+       /**
+        * EContentEditor:context-menu-requested
+        *
+        * Emitted whenever a context menu is requested.
+        */
+       signals[CONTEXT_MENU_REQUESTED] = g_signal_new (
+               "context-menu-requested",
+               E_TYPE_CONTENT_EDITOR,
+               G_SIGNAL_RUN_LAST,
+               G_STRUCT_OFFSET (EContentEditorInterface, context_menu_requested),
+               g_signal_accumulator_true_handled, NULL,
+               NULL,
+               G_TYPE_BOOLEAN, 2,
+               G_TYPE_INT,
+               GDK_TYPE_EVENT | G_SIGNAL_TYPE_STATIC_SCOPE);
+
+       /**
+        * EContentEditor::find-done
+        *
+        * Emitted when the call to e_content_editor_find() is done.
+        **/
+       signals[FIND_DONE] = g_signal_new (
+               "find-done",
+               E_TYPE_CONTENT_EDITOR,
+               G_SIGNAL_RUN_LAST,
+               G_STRUCT_OFFSET (EContentEditorInterface, find_done),
+               NULL, NULL,
+               NULL,
+               G_TYPE_NONE, 1,
+               G_TYPE_UINT);
+
+       /**
+        * EContentEditor::replace-all-done
+        *
+        * Emitted when the call to e_content_editor_replace_all() is done.
+        **/
+       signals[REPLACE_ALL_DONE] = g_signal_new (
+               "replace-all-done",
+               E_TYPE_CONTENT_EDITOR,
+               G_SIGNAL_RUN_LAST,
+               G_STRUCT_OFFSET (EContentEditorInterface, replace_all_done),
+               NULL, NULL,
+               NULL,
+               G_TYPE_NONE, 1,
+               G_TYPE_UINT);
+}
+
+ESpellChecker *
+e_content_editor_ref_spell_checker (EContentEditor *editor)
+{
+       ESpellChecker *spell_checker = NULL;
+
+       g_return_val_if_fail (E_IS_CONTENT_EDITOR (editor), NULL);
+
+       g_object_get (G_OBJECT (editor), "spell-checker", &spell_checker, NULL);
+
+       return spell_checker;
+}
+
+gboolean
+e_content_editor_can_cut (EContentEditor *editor)
+{
+       gboolean value = FALSE;
+
+       g_return_val_if_fail (E_IS_CONTENT_EDITOR (editor), FALSE);
+
+       g_object_get (G_OBJECT (editor), "can-cut", &value, NULL);
+
+       return value;
+}
+
+gboolean
+e_content_editor_can_copy (EContentEditor *editor)
+{
+       gboolean value = FALSE;
+
+       g_return_val_if_fail (E_IS_CONTENT_EDITOR (editor), FALSE);
+
+       g_object_get (G_OBJECT (editor), "can-copy", &value, NULL);
+
+       return value;
+}
+
+gboolean
+e_content_editor_can_paste (EContentEditor *editor)
+{
+       gboolean value = FALSE;
+
+       g_return_val_if_fail (E_IS_CONTENT_EDITOR (editor), FALSE);
+
+       g_object_get (G_OBJECT (editor), "can-paste", &value, NULL);
+
+       return value;
+}
+
+gboolean
+e_content_editor_can_undo (EContentEditor *editor)
+{
+       gboolean value = FALSE;
+
+       g_return_val_if_fail (E_IS_CONTENT_EDITOR (editor), FALSE);
+
+       g_object_get (G_OBJECT (editor), "can-undo", &value, NULL);
+
+       return value;
+}
+
+gboolean
+e_content_editor_can_redo (EContentEditor *editor)
+{
+       gboolean value = FALSE;
+
+       g_return_val_if_fail (E_IS_CONTENT_EDITOR (editor), FALSE);
+
+       g_object_get (G_OBJECT (editor), "can-redo", &value, NULL);
+
+       return value;
+}
+
+/**
+ * e_content_editor_is_indented:
+ * @editor: an #EContentEditor
+ *
+ * Returns whether the current paragraph is indented. This does not include
+ * citations.
+ *
+ * Returns: %TRUE when current paragraph is indented, %FALSE otherwise.
+ *
+ * Since: 3.22
+ **/
+gboolean
+e_content_editor_is_indented (EContentEditor *editor)
+{
+       gboolean value = FALSE;
+
+       g_return_val_if_fail (E_IS_CONTENT_EDITOR (editor), FALSE);
+
+       g_object_get (G_OBJECT (editor), "indented", &value, NULL);
+
+       return value;
+}
+
+gboolean
+e_content_editor_get_spell_check_enabled (EContentEditor *editor)
+{
+       gboolean value = FALSE;
+
+       g_return_val_if_fail (E_IS_CONTENT_EDITOR (editor), FALSE);
+
+       g_object_get (G_OBJECT (editor), "spell-check-enabled", &value, NULL);
+
+       return value;
+}
+
+void
+e_content_editor_set_spell_check_enabled (EContentEditor *editor,
+                                         gboolean enable)
+{
+       g_return_if_fail (E_IS_CONTENT_EDITOR (editor));
+
+       g_object_set (G_OBJECT (editor), "spell-check-enabled", enable, NULL);
+}
+
+gboolean
+e_content_editor_is_editable (EContentEditor *editor)
+{
+       gboolean value = FALSE;
+
+       g_return_val_if_fail (E_IS_CONTENT_EDITOR (editor), FALSE);
+
+       g_object_get (G_OBJECT (editor), "editable", &value, NULL);
+
+       return value;
+}
+
+void
+e_content_editor_set_editable (EContentEditor *editor,
+                              gboolean editable)
+{
+       g_return_if_fail (E_IS_CONTENT_EDITOR (editor));
+
+       g_object_set (G_OBJECT (editor), "editable", editable, NULL);
+}
+
+gboolean
+e_content_editor_get_changed (EContentEditor *editor)
+{
+       gboolean value = FALSE;
+
+       g_return_val_if_fail (E_IS_CONTENT_EDITOR (editor), FALSE);
+
+       g_object_get (G_OBJECT (editor), "changed", &value, NULL);
+
+       return value;
+}
+
+void
+e_content_editor_set_changed (EContentEditor *editor,
+                             gboolean changed)
+{
+       g_return_if_fail (E_IS_CONTENT_EDITOR (editor));
+
+       g_object_set (G_OBJECT (editor), "changed", changed, NULL);
+}
+
+gboolean
+e_content_editor_get_html_mode (EContentEditor *editor)
+{
+       gboolean value = FALSE;
+
+       g_return_val_if_fail (E_IS_CONTENT_EDITOR (editor), FALSE);
+
+       g_object_get (G_OBJECT (editor), "html-mode", &value, NULL);
+
+       return value;
+}
+
+void
+e_content_editor_set_html_mode (EContentEditor *editor,
+                               gboolean html_mode)
+{
+       g_return_if_fail (E_IS_CONTENT_EDITOR (editor));
+
+       g_object_set (G_OBJECT (editor), "html-mode", html_mode, NULL);
+}
+
+/**
+ * e_content_editor_set_alignment:
+ * @editor: an #EContentEditor
+ * @value: an #EContentEditorAlignment value to apply
+ *
+ * Sets alignment of current paragraph to @value.
+ *
+ * Since: 3.22
+ **/
+void
+e_content_editor_set_alignment (EContentEditor *editor,
+                               EContentEditorAlignment value)
+{
+       g_return_if_fail (E_IS_CONTENT_EDITOR (editor));
+
+       g_object_set (G_OBJECT (editor), "alignment", value, NULL);
+}
+
+/**
+ * e_content_editor_get_alignment:
+ * @editor: #an EContentEditor
+ *
+ * Returns alignment of the current paragraph.
+ *
+ * Returns: #EContentEditorAlignment
+ *
+ * Since: 3.22
+ **/
+EContentEditorAlignment
+e_content_editor_get_alignment (EContentEditor *editor)
+{
+       EContentEditorAlignment value = E_CONTENT_EDITOR_ALIGNMENT_LEFT;
+
+       g_return_val_if_fail (E_IS_CONTENT_EDITOR (editor), E_CONTENT_EDITOR_ALIGNMENT_LEFT);
+
+       g_object_get (G_OBJECT (editor), "alignment", &value, NULL);
+
+       return value;
+}
+
+/**
+ * e_content_editor_set_background_color:
+ * @editor: an #EContentEditor
+ * @value: a #GdkRGBA
+ *
+ * Sets the background color of the current selection or letter at the current cursor position to
+ * a color defined by @value.
+ *
+ * Since: 3.22
+ **/
+void
+e_content_editor_set_background_color (EContentEditor *editor,
+                                      const GdkRGBA *value)
+{
+       g_return_if_fail (E_IS_CONTENT_EDITOR (editor));
+       g_return_if_fail (value != NULL);
+
+       g_object_set (G_OBJECT (editor), "background-color", value, NULL);
+}
+
+/**
+ * e_content_editor_dup_background_color:
+ * @editor: an #EContentEditor
+ *
+ * Returns the background color used in the current selection or at letter
+ * at the current cursor position.
+ *
+ * Returns: (transfer-full): A newly allocated #GdkRGBA structure with
+ *   the current background color. Free the returned value with gdk_rgba_free()
+ *   when done with it.
+ *
+ * Since: 3.22
+ **/
+GdkRGBA *
+e_content_editor_dup_background_color (EContentEditor *editor)
+{
+       GdkRGBA *value = NULL;
+
+       g_return_val_if_fail (E_IS_CONTENT_EDITOR (editor), NULL);
+
+       g_object_get (G_OBJECT (editor), "background-color", &value, NULL);
+
+       return value;
+}
+
+/**
+ * e_content_editor_set_font_color:
+ * @editor: an #EContentEditor
+ * @value: a #GdkRGBA
+ *
+ * Sets the font color of the current selection or letter at the current cursor position to
+ * a color defined by @value.
+ *
+ * Since: 3.22
+ **/
+void
+e_content_editor_set_font_color (EContentEditor *editor,
+                                const GdkRGBA *value)
+{
+       g_return_if_fail (E_IS_CONTENT_EDITOR (editor));
+       g_return_if_fail (value != NULL);
+
+       g_object_set (G_OBJECT (editor), "font-color", value, NULL);
+}
+
+/**
+ * e_content_editor_dup_font_color:
+ * @editor: an #EContentEditor
+ *
+ * Returns the font color used in the current selection or at letter
+ * at the current cursor position.
+ *
+ * Returns: (transfer-full): A newly allocated #GdkRGBA structure with
+ *   the current font color. Free the returned value with gdk_rgba_free()
+ *   when done with it.
+ *
+ * Since: 3.22
+ **/
+GdkRGBA *
+e_content_editor_dup_font_color (EContentEditor *editor)
+{
+       GdkRGBA *value = NULL;
+
+       g_return_val_if_fail (E_IS_CONTENT_EDITOR (editor), NULL);
+
+       g_object_get (G_OBJECT (editor), "font-color", &value, NULL);
+
+       return value;
+}
+
+/**
+ * e_content_editor_set_font_name:
+ * @editor: an #EContentEditor
+ * @value: a font name to apply
+ *
+ * Sets font name of current selection or of letter at current cursor position
+ * to @value.
+ *
+ * Since: 3.22
+ **/
+void
+e_content_editor_set_font_name (EContentEditor *editor,
+                               const gchar *value)
+{
+       g_return_if_fail (E_IS_CONTENT_EDITOR (editor));
+       g_return_if_fail (value != NULL);
+
+       g_object_set (G_OBJECT (editor), "font-name", value, NULL);
+}
+
+/**
+ * e_content_editor_dup_font_name:
+ * @editor: an #EContentEditor
+ *
+ * Returns a name of the font used in the current selection or at letter
+ * at the current cursor position.
+ *
+ * Returns: (transfer-full): A newly allocated string with the font name.
+ *    Free it with g_free() when done with it.
+ *
+ * Since: 3.22
+ **/
+gchar *
+e_content_editor_dup_font_name (EContentEditor *editor)
+{
+       gchar *value = NULL;
+
+       g_return_val_if_fail (E_IS_CONTENT_EDITOR (editor), NULL);
+
+       g_object_get (G_OBJECT (editor), "font-name", &value, NULL);
+
+       return value;
+}
+
+/**
+ * e_content_editor_set_font_size:
+ * @editor: an #EContentEditor
+ * @value: font size to apply
+ *
+ * Sets font size of current selection or of letter at current cursor position
+ * to @value.
+ *
+ * Since: 3.22
+ **/
+void
+e_content_editor_set_font_size (EContentEditor *editor,
+                               gint value)
+{
+       g_return_if_fail (E_IS_CONTENT_EDITOR (editor));
+
+       g_object_set (G_OBJECT (editor), "font-size", value, NULL);
+}
+
+/**
+ * e_content_editor_get_font_size:
+ * @editor: an #EContentEditor
+ *
+ * Returns fotn size of the current selection or letter at the current
+ * cursor position.
+ *
+ * Returns: Current font size.
+ *
+ * Since: 3.22
+ **/
+gint
+e_content_editor_get_font_size (EContentEditor *editor)
+{
+       gint value = -1;
+
+       g_return_val_if_fail (E_IS_CONTENT_EDITOR (editor), -1);
+
+       g_object_get (G_OBJECT (editor), "font-size", &value, NULL);
+
+       return value;
+}
+
+/**
+ * e_content_editor_set_block_format:
+ * @editor: an #EContentEditor
+ * @value: an #EContentEditorBlockFormat value
+ *
+ * Changes block format of the current paragraph to @value.
+ *
+ * Since: 3.22
+ **/
+void
+e_content_editor_set_block_format (EContentEditor *editor,
+                                  EContentEditorBlockFormat value)
+{
+       g_return_if_fail (E_IS_CONTENT_EDITOR (editor));
+
+       g_object_set (G_OBJECT (editor), "block-format", value, NULL);
+}
+
+/**
+ * e_content_editor_get_block_format:
+ * @editor: an #EContentEditor
+ *
+ * Returns block format of the current paragraph.
+ *
+ * Returns: #EContentEditorBlockFormat
+ *
+ * Since: 3.22
+ **/
+EContentEditorBlockFormat
+e_content_editor_get_block_format (EContentEditor *editor)
+{
+       EContentEditorBlockFormat value = E_CONTENT_EDITOR_BLOCK_FORMAT_NONE;
+
+       g_return_val_if_fail (E_IS_CONTENT_EDITOR (editor), E_CONTENT_EDITOR_BLOCK_FORMAT_NONE);
+
+       g_object_get (G_OBJECT (editor), "block-format", &value, NULL);
+
+       return value;
+}
+
+/**
+ * e_content_editor_set_bold:
+ * @editor: an #EContentEditor
+ * @bold: %TRUE to enable bold, %FALSE to disable
+ *
+ * Changes bold formatting of current selection or letter at current
+ * cursor position.
+ *
+ * Since: 3.22
+ **/
+void
+e_content_editor_set_bold (EContentEditor *editor,
+                          gboolean bold)
+{
+       g_return_if_fail (E_IS_CONTENT_EDITOR (editor));
+
+       g_object_set (G_OBJECT (editor), "bold", bold, NULL);
+}
+
+/**
+ * e_content_editor_is_bold:
+ * @editor: an #EContentEditor
+ *
+ * Returns whether current selection or letter at current cursor position is bold.
+ *
+ * Returns: %TRUE when selection is bold, %FALSE otherwise.
+ *
+ * Since: 3.22
+ **/
+gboolean
+e_content_editor_is_bold (EContentEditor *editor)
+{
+       gboolean value = FALSE;
+
+       g_return_val_if_fail (E_IS_CONTENT_EDITOR (editor), FALSE);
+
+       g_object_get (G_OBJECT (editor), "bold", &value, NULL);
+
+       return value;
+}
+
+/**
+ * e_content_editor_set_italic:
+ * @editor: an #EContentEditor
+ * @italic: %TRUE to enable italic, %FALSE to disable
+ *
+ * Changes italic formatting of current selection or letter at current
+ * cursor position.
+ *
+ * Since: 3.22
+ **/
+void
+e_content_editor_set_italic (EContentEditor *editor,
+                            gboolean italic)
+{
+       g_return_if_fail (E_IS_CONTENT_EDITOR (editor));
+
+       g_object_set (G_OBJECT (editor), "italic", italic, NULL);
+}
+
+/**
+ * e_content_editor_is_italic:
+ * @editor: an #EContentEditor
+ *
+ * Returns whether current selection or letter at current cursor position
+ * is italic.
+ *
+ * Returns: %TRUE when selection is italic, %FALSE otherwise.
+ *
+ * Since: 3.22
+ **/
+gboolean
+e_content_editor_is_italic (EContentEditor *editor)
+{
+       gboolean value = FALSE;
+
+       g_return_val_if_fail (E_IS_CONTENT_EDITOR (editor), FALSE);
+
+       g_object_get (G_OBJECT (editor), "italic", &value, NULL);
+
+       return value;
+}
+
+/**
+ * e_content_editor_set_monospaced:
+ * @editor: an #EContentEditor
+ * @monospaced: %TRUE to enable monospaced, %FALSE to disable
+ *
+ * Changes monospaced formatting of current selection or letter
+ * at current cursor position.
+ *
+ * Since: 3.22
+ **/
+void
+e_content_editor_set_monospaced (EContentEditor *editor,
+                                gboolean monospaced)
+{
+       g_return_if_fail (E_IS_CONTENT_EDITOR (editor));
+
+       g_object_set (G_OBJECT (editor), "monospaced", monospaced, NULL);
+}
+
+/**
+ * e_content_editor_is_monospaced:
+ * @editor: an #EContentEditor
+ *
+ * Returns whether current selection or letter at current cursor position
+ * is monospaced.
+ *
+ * Returns: %TRUE when selection is monospaced, %FALSE otherwise.
+ *
+ * Since: 3.22
+ **/
+gboolean
+e_content_editor_is_monospaced (EContentEditor *editor)
+{
+       gboolean value = FALSE;
+
+       g_return_val_if_fail (E_IS_CONTENT_EDITOR (editor), FALSE);
+
+       g_object_get (G_OBJECT (editor), "monospaced", &value, NULL);
+
+       return value;
+}
+
+/**
+ * e_content_editor_set_strikethrough:
+ * @editor: an #EContentEditor
+ * @strikethrough: %TRUE to enable strikethrough, %FALSE to disable
+ *
+ * Changes strike through formatting of current selection or letter at current
+ * cursor position.
+ *
+ * Since: 3.22
+ **/
+void
+e_content_editor_set_strikethrough (EContentEditor *editor,
+                                   gboolean strikethrough)
+{
+       g_return_if_fail (E_IS_CONTENT_EDITOR (editor));
+
+       g_object_set (G_OBJECT (editor), "strikethrough", strikethrough, NULL);
+}
+
+/**
+ * e_content_editor_is_strikethrough:
+ * @editor: an #EContentEditor
+ *
+ * Returns whether current selection or letter at current cursor position
+ * is striked through.
+ *
+ * Returns: %TRUE when selection is striked through, %FALSE otherwise.
+ *
+ * Since: 3.22
+ **/
+gboolean
+e_content_editor_is_strikethrough (EContentEditor *editor)
+{
+       gboolean value = FALSE;
+
+       g_return_val_if_fail (E_IS_CONTENT_EDITOR (editor), FALSE);
+
+       g_object_get (G_OBJECT (editor), "strikethrough", &value, NULL);
+
+       return value;
+}
+
+/**
+ * e_content_editor_set_subscript:
+ * @editor: an #EContentEditor
+ * @subscript: %TRUE to enable subscript, %FALSE to disable
+ *
+ * Changes subscript of current selection or letter at current
+ * cursor position.
+ *
+ * Since: 3.22
+ **/
+void
+e_content_editor_set_subscript (EContentEditor *editor,
+                               gboolean subscript)
+{
+       g_return_if_fail (E_IS_CONTENT_EDITOR (editor));
+
+       g_object_set (G_OBJECT (editor), "subscript", subscript, NULL);
+}
+
+/**
+ * e_content_editor_is_subscript:
+ * @editor: an #EContentEditor
+ *
+ * Returns whether current selection or letter at current cursor position
+ * is in subscript.
+ *
+ * Returns: %TRUE when selection is in subscript, %FALSE otherwise.
+ *
+ * Since: 3.22
+ **/
+gboolean
+e_content_editor_is_subscript (EContentEditor *editor)
+{
+       gboolean value = FALSE;
+
+       g_return_val_if_fail (E_IS_CONTENT_EDITOR (editor), FALSE);
+
+       g_object_get (G_OBJECT (editor), "subscript", &value, NULL);
+
+       return value;
+}
+
+/**
+ * e_content_editor_set_superscript:
+ * @editor: an #EContentEditor
+ * @superscript: %TRUE to enable superscript, %FALSE to disable
+ *
+ * Changes superscript of the current selection or letter at current
+ * cursor position.
+ *
+ * Since: 3.22
+ **/
+void
+e_content_editor_set_superscript (EContentEditor *editor,
+                                 gboolean superscript)
+{
+       g_return_if_fail (E_IS_CONTENT_EDITOR (editor));
+
+       g_object_set (G_OBJECT (editor), "superscript", superscript, NULL);
+}
+
+/**
+ * e_content_editor_is_superscript:
+ * @editor: an #EContentEditor
+ *
+ * Returns whether current selection or letter at current cursor position
+ * is in superscript.
+ *
+ * Returns: %TRUE when selection is in superscript, %FALSE otherwise.
+ *
+ * Since: 3.22
+ **/
+gboolean
+e_content_editor_is_superscript (EContentEditor *editor)
+{
+       gboolean value = FALSE;
+
+       g_return_val_if_fail (E_IS_CONTENT_EDITOR (editor), FALSE);
+
+       g_object_get (G_OBJECT (editor), "superscript", &value, NULL);
+
+       return value;
+}
+
+/**
+ * e_content_editor_set_underline:
+ * @editor: an #EContentEditor
+ * @underline: %TRUE to enable underline, %FALSE to disable
+ *
+ * Changes underline formatting of current selection or letter
+ * at current cursor position.
+ *
+ * Since: 3.22
+ **/
+void
+e_content_editor_set_underline (EContentEditor *editor,
+                               gboolean underline)
+{
+       g_return_if_fail (E_IS_CONTENT_EDITOR (editor));
+
+       g_object_set (G_OBJECT (editor), "underline", underline, NULL);
+}
+
+/**
+ * e_content_editor_is_underline:
+ * @editor: an #EContentEditor
+ *
+ * Returns whether current selection or letter at current cursor position
+ * is underlined.
+ *
+ * Returns: %TRUE when selection is underlined, %FALSE otherwise.
+ *
+ * Since: 3.22
+ **/
+gboolean
+e_content_editor_is_underline (EContentEditor *editor)
+{
+       gboolean value = FALSE;
+
+       g_return_val_if_fail (E_IS_CONTENT_EDITOR (editor), FALSE);
+
+       g_object_get (G_OBJECT (editor), "underline", &value, NULL);
+
+       return value;
+}
+
+/**
+ * e_content_editor_setup_editor:
+ * @content_editor: an #EContentEditor
+ * @callback: an #EContentEditorInitializedCallback function
+ * @user_data: data to pass to @callback
+ *
+ * Initilizes the @content_editor. Once the initialization is done,
+ * the @callback is called with the passed @user_data.
+ *
+ * Since: 3.22
+ **/
+void
+e_content_editor_initialize (EContentEditor *content_editor,
+                            EContentEditorInitializedCallback callback,
+                            gpointer user_data)
+{
+       EContentEditorInterface *iface;
+
+       g_return_if_fail (E_IS_CONTENT_EDITOR (content_editor));
+       g_return_if_fail (callback != NULL);
+
+       iface = E_CONTENT_EDITOR_GET_IFACE (content_editor);
+       g_return_if_fail (iface != NULL);
+       g_return_if_fail (iface->initialize != NULL);
+
+       iface->initialize (content_editor, callback, user_data);
+}
+
+/**
+ * e_content_editor_setup_editor:
+ * @content_editor: an #EContentEditor
+ * @html_editor: an #EHTMLEditor
+ *
+ * Called the first time the @content_editor is picked to be used within
+ * the @html_editor. This is typically used to modify the UI
+ * of the @html_editor. This method implementation is optional.
+ *
+ * Since: 3.22
+ **/
+void
+e_content_editor_setup_editor (EContentEditor *content_editor,
+                              EHTMLEditor *html_editor)
+{
+       EContentEditorInterface *iface;
+
+       g_return_if_fail (E_IS_CONTENT_EDITOR (content_editor));
+       g_return_if_fail (E_IS_HTML_EDITOR (html_editor));
+
+       iface = E_CONTENT_EDITOR_GET_IFACE (content_editor);
+       g_return_if_fail (iface != NULL);
+
+       if (iface->setup_editor)
+               iface->setup_editor (content_editor, html_editor);
+}
+
+void
+e_content_editor_update_styles (EContentEditor *editor)
+{
+       EContentEditorInterface *iface;
+
+       g_return_if_fail (E_IS_CONTENT_EDITOR (editor));
+
+       iface = E_CONTENT_EDITOR_GET_IFACE (editor);
+       g_return_if_fail (iface != NULL);
+       g_return_if_fail (iface->update_styles != NULL);
+
+       iface->update_styles (editor);
+}
+
+void
+e_content_editor_insert_content (EContentEditor *editor,
+                                 const gchar *content,
+                                 EContentEditorInsertContentFlags flags)
+{
+       EContentEditorInterface *iface;
+
+       g_return_if_fail (E_IS_CONTENT_EDITOR (editor));
+       g_return_if_fail (content != NULL);
+
+       iface = E_CONTENT_EDITOR_GET_IFACE (editor);
+       g_return_if_fail (iface != NULL);
+       g_return_if_fail (iface->insert_content != NULL);
+
+       iface->insert_content (editor, content, flags);
+}
+
+gchar *
+e_content_editor_get_content (EContentEditor *editor,
+                              EContentEditorGetContentFlags flags,
+                             const gchar *inline_images_from_domain,
+                             GSList **inline_images_parts /* newly created CamelMimePart * */)
+{
+       EContentEditorInterface *iface;
+
+       g_return_val_if_fail (E_IS_CONTENT_EDITOR (editor), NULL);
+       if ((flags & E_CONTENT_EDITOR_GET_INLINE_IMAGES)) {
+               g_return_val_if_fail (inline_images_from_domain != NULL, NULL);
+               g_return_val_if_fail (inline_images_parts != NULL, NULL);
+       }
+
+       iface = E_CONTENT_EDITOR_GET_IFACE (editor);
+       g_return_val_if_fail (iface != NULL, NULL);
+       g_return_val_if_fail (iface->get_content != NULL, NULL);
+
+       return iface->get_content (editor, flags, inline_images_from_domain, inline_images_parts);
+}
+
+void
+e_content_editor_insert_image_from_mime_part (EContentEditor *editor,
+                                              CamelMimePart *part)
+{
+       EContentEditorInterface *iface;
+
+       g_return_if_fail (E_IS_CONTENT_EDITOR (editor));
+       g_return_if_fail (part != NULL);
+
+       iface = E_CONTENT_EDITOR_GET_IFACE (editor);
+       g_return_if_fail (iface != NULL);
+       g_return_if_fail (iface->insert_image_from_mime_part != NULL);
+
+       iface->insert_image_from_mime_part (editor, part);
+}
+
+/**
+ * e_content_editor_insert_image:
+ * @editor: an #EContentEditor
+ * @uri: an URI of the source image
+ *
+ * Inserts image at current cursor position using @uri as source. When a
+ * text range is selected, it will be replaced by the image.
+ *
+ * Since: 3.22
+ **/
+void
+e_content_editor_insert_image (EContentEditor *editor,
+                               const gchar *uri)
+{
+       EContentEditorInterface *iface;
+
+       g_return_if_fail (E_IS_CONTENT_EDITOR (editor));
+       g_return_if_fail (uri != NULL);
+
+       iface = E_CONTENT_EDITOR_GET_IFACE (editor);
+       g_return_if_fail (iface != NULL);
+       g_return_if_fail (iface->insert_image != NULL);
+
+       iface->insert_image (editor, uri);
+}
+
+void
+e_content_editor_insert_emoticon (EContentEditor *editor,
+                                  EEmoticon *emoticon)
+{
+       EContentEditorInterface *iface;
+
+       g_return_if_fail (E_IS_CONTENT_EDITOR (editor));
+       g_return_if_fail (emoticon != NULL);
+
+       iface = E_CONTENT_EDITOR_GET_IFACE (editor);
+       g_return_if_fail (iface != NULL);
+       g_return_if_fail (iface->insert_emoticon != NULL);
+
+       iface->insert_emoticon (editor, emoticon);
+}
+
+void
+e_content_editor_move_caret_on_coordinates (EContentEditor *editor,
+                                            gint x,
+                                            gint y,
+                                            gboolean cancel_if_not_collapsed)
+{
+       EContentEditorInterface *iface;
+
+       g_return_if_fail (E_IS_CONTENT_EDITOR (editor));
+       g_return_if_fail (x > 0);
+       g_return_if_fail (y > 0);
+
+       iface = E_CONTENT_EDITOR_GET_IFACE (editor);
+       g_return_if_fail (iface != NULL);
+       g_return_if_fail (iface->move_caret_on_coordinates != NULL);
+
+       iface->move_caret_on_coordinates (editor, x, y, cancel_if_not_collapsed);
+}
+
+void
+e_content_editor_cut (EContentEditor *editor)
+{
+       EContentEditorInterface *iface;
+
+       g_return_if_fail (E_IS_CONTENT_EDITOR (editor));
+
+       iface = E_CONTENT_EDITOR_GET_IFACE (editor);
+       g_return_if_fail (iface != NULL);
+       g_return_if_fail (iface->cut != NULL);
+
+       iface->cut (editor);
+}
+
+void
+e_content_editor_copy (EContentEditor *editor)
+{
+       EContentEditorInterface *iface;
+
+       g_return_if_fail (E_IS_CONTENT_EDITOR (editor));
+
+       iface = E_CONTENT_EDITOR_GET_IFACE (editor);
+       g_return_if_fail (iface != NULL);
+       g_return_if_fail (iface->copy != NULL);
+
+       iface->copy (editor);
+}
+
+void
+e_content_editor_paste (EContentEditor *editor)
+{
+       EContentEditorInterface *iface;
+
+       g_return_if_fail (E_IS_CONTENT_EDITOR (editor));
+
+       iface = E_CONTENT_EDITOR_GET_IFACE (editor);
+       g_return_if_fail (iface != NULL);
+       g_return_if_fail (iface->paste != NULL);
+
+       iface->paste (editor);
+}
+
+void
+e_content_editor_paste_primary (EContentEditor *editor)
+{
+       EContentEditorInterface *iface;
+
+       g_return_if_fail (E_IS_CONTENT_EDITOR (editor));
+
+       iface = E_CONTENT_EDITOR_GET_IFACE (editor);
+       g_return_if_fail (iface != NULL);
+       g_return_if_fail (iface->paste_primary != NULL);
+
+       iface->paste_primary (editor);
+}
+
+void
+e_content_editor_undo (EContentEditor *editor)
+{
+       EContentEditorInterface *iface;
+
+       g_return_if_fail (E_IS_CONTENT_EDITOR (editor));
+
+       iface = E_CONTENT_EDITOR_GET_IFACE (editor);
+       g_return_if_fail (iface != NULL);
+       g_return_if_fail (iface->undo != NULL);
+
+       iface->undo (editor);
+}
+
+void
+e_content_editor_redo (EContentEditor *editor)
+{
+       EContentEditorInterface *iface;
+
+       g_return_if_fail (E_IS_CONTENT_EDITOR (editor));
+
+       iface = E_CONTENT_EDITOR_GET_IFACE (editor);
+       g_return_if_fail (iface != NULL);
+       g_return_if_fail (iface->redo != NULL);
+
+       iface->redo (editor);
+}
+
+void
+e_content_editor_clear_undo_redo_history (EContentEditor *editor)
+{
+       EContentEditorInterface *iface;
+
+       g_return_if_fail (E_IS_CONTENT_EDITOR (editor));
+
+       iface = E_CONTENT_EDITOR_GET_IFACE (editor);
+       g_return_if_fail (iface != NULL);
+       g_return_if_fail (iface->clear_undo_redo_history != NULL);
+
+       iface->clear_undo_redo_history (editor);
+}
+
+void
+e_content_editor_set_spell_checking_languages (EContentEditor *editor,
+                                               const gchar **languages)
+{
+       EContentEditorInterface *iface;
+
+       g_return_if_fail (E_IS_CONTENT_EDITOR (editor));
+
+       iface = E_CONTENT_EDITOR_GET_IFACE (editor);
+       g_return_if_fail (iface != NULL);
+       g_return_if_fail (iface->set_spell_checking_languages != NULL);
+
+       iface->set_spell_checking_languages (editor, languages);
+}
+
+void
+e_content_editor_select_all (EContentEditor *editor)
+{
+       EContentEditorInterface *iface;
+
+       g_return_if_fail (E_IS_CONTENT_EDITOR (editor));
+
+       iface = E_CONTENT_EDITOR_GET_IFACE (editor);
+       g_return_if_fail (iface != NULL);
+       g_return_if_fail (iface->select_all != NULL);
+
+       iface->select_all (editor);
+}
+
+/**
+ * e_content_editor_get_selected_text:
+ * @editor: an #EContentEditor
+ *
+ * Returns currently selected string.
+ *
+ * Returns: (transfer-full): A newly allocated string with the content of current selection.
+ *
+ * Since: 3.22
+ **/
+gchar *
+e_content_editor_get_selected_text (EContentEditor *editor)
+{
+       EContentEditorInterface *iface;
+
+       g_return_val_if_fail (E_IS_CONTENT_EDITOR (editor), NULL);
+
+       iface = E_CONTENT_EDITOR_GET_IFACE (editor);
+       g_return_val_if_fail (iface != NULL, NULL);
+       g_return_val_if_fail (iface->get_selected_text != NULL, NULL);
+
+       return iface->get_selected_text (editor);
+}
+
+/**
+ * e_content_editor_get_caret_word:
+ * @editor: an #EContentEditor
+ *
+ * Returns word under cursor.
+ *
+ * Returns: (transfer-full): A newly allocated string with current caret word or %NULL
+ * when there is no text under cursor or when selection is active.
+ *
+ * Since: 3.22
+ **/
+gchar *
+e_content_editor_get_caret_word (EContentEditor *editor)
+{
+       EContentEditorInterface *iface;
+
+       g_return_val_if_fail (E_IS_CONTENT_EDITOR (editor), NULL);
+
+       iface = E_CONTENT_EDITOR_GET_IFACE (editor);
+       g_return_val_if_fail (iface != NULL, NULL);
+       g_return_val_if_fail (iface->get_caret_word != NULL, NULL);
+
+       return iface->get_caret_word (editor);
+}
+
+/**
+ * e_content_editor_replace_caret_word:
+ * @editor: an #EContentEditor
+ * @replacement: a string to replace current caret word with
+ *
+ * Replaces current word under cursor with @replacement.
+ *
+ * Since: 3.22
+ **/
+void
+e_content_editor_replace_caret_word (EContentEditor *editor,
+                                     const gchar *replacement)
+{
+       EContentEditorInterface *iface;
+
+       g_return_if_fail (E_IS_CONTENT_EDITOR (editor));
+       g_return_if_fail (replacement != NULL);
+
+       iface = E_CONTENT_EDITOR_GET_IFACE (editor);
+       g_return_if_fail (iface != NULL);
+       g_return_if_fail (iface->replace_caret_word != NULL);
+
+       iface->replace_caret_word (editor, replacement);
+}
+
+void
+e_content_editor_selection_indent (EContentEditor *editor)
+{
+       EContentEditorInterface *iface;
+
+       g_return_if_fail (E_IS_CONTENT_EDITOR (editor));
+
+       iface = E_CONTENT_EDITOR_GET_IFACE (editor);
+       g_return_if_fail (iface != NULL);
+       g_return_if_fail (iface->selection_indent != NULL);
+
+       iface->selection_indent (editor);
+}
+
+void
+e_content_editor_selection_unindent (EContentEditor *editor)
+{
+       EContentEditorInterface *iface;
+
+       g_return_if_fail (E_IS_CONTENT_EDITOR (editor));
+
+       iface = E_CONTENT_EDITOR_GET_IFACE (editor);
+       g_return_if_fail (iface != NULL);
+       g_return_if_fail (iface->selection_unindent != NULL);
+
+       iface->selection_unindent (editor);
+}
+
+/**
+ * e_content_editor_selection_create_link:
+ * @editor: an #EContentEditor
+ * @uri: destination of the new link
+ *
+ * Converts current selection into a link pointing to @url.
+ *
+ * Since: 3.22
+ **/
+void
+e_content_editor_selection_create_link (EContentEditor *editor,
+                                        const gchar *uri)
+{
+       EContentEditorInterface *iface;
+
+       g_return_if_fail (E_IS_CONTENT_EDITOR (editor));
+       g_return_if_fail (uri != NULL);
+
+       iface = E_CONTENT_EDITOR_GET_IFACE (editor);
+       g_return_if_fail (iface != NULL);
+       g_return_if_fail (iface->selection_create_link != NULL);
+
+       iface->selection_create_link (editor, uri);
+}
+
+/**
+ * e_content_editor_selection_unlink:
+ * @editor: an #EContentEditor
+ *
+ * Removes any links (&lt;A&gt; elements) from current selection or at current
+ * cursor position.
+ *
+ * Since: 3.22
+ **/
+void
+e_content_editor_selection_unlink (EContentEditor *editor)
+{
+       EContentEditorInterface *iface;
+
+       g_return_if_fail (E_IS_CONTENT_EDITOR (editor));
+
+       iface = E_CONTENT_EDITOR_GET_IFACE (editor);
+       g_return_if_fail (iface != NULL);
+       g_return_if_fail (iface->selection_unlink != NULL);
+
+       iface->selection_unlink (editor);
+}
+
+/**
+ * e_content_editor_find:
+ * @editor: an #EContentEditor
+ * @flags: a bit-OR of #EContentEditorFindFlags flags
+ * @text: a text to find
+ *
+ * Searches the content of the @editor for the occurrence of the @text.
+ * The @flags modify the behaviour of the search. The found text,
+ * if any, is supposed to be selected.
+ *
+ * Once the search is done, the "find-done" signal should be
+ * emitted, by using e_content_editor_emit_find_done().
+ *
+ * Since: 3.22
+ **/
+void
+e_content_editor_find (EContentEditor *editor,
+                      guint32 flags,
+                      const gchar *text)
+{
+       EContentEditorInterface *iface;
+
+       g_return_if_fail (E_IS_CONTENT_EDITOR (editor));
+       g_return_if_fail (text != NULL);
+
+       iface = E_CONTENT_EDITOR_GET_IFACE (editor);
+       g_return_if_fail (iface != NULL);
+       g_return_if_fail (iface->find != NULL);
+
+       iface->find (editor, flags, text);
+}
+
+/**
+ * e_content_editor_replace:
+ * @editor: an #EContentEditor
+ * @replacement: a string to replace current selection with
+ *
+ * Replaces currently selected text with @replacement.
+ *
+ * Since: 3.22
+ **/
+void
+e_content_editor_replace (EContentEditor *editor,
+                         const gchar *replacement)
+{
+       EContentEditorInterface *iface;
+
+       g_return_if_fail (E_IS_CONTENT_EDITOR (editor));
+       g_return_if_fail (replacement != NULL);
+
+       iface = E_CONTENT_EDITOR_GET_IFACE (editor);
+       g_return_if_fail (iface != NULL);
+       g_return_if_fail (iface->replace != NULL);
+
+       iface->replace (editor, replacement);
+}
+
+/**
+ * e_content_editor_replace_all:
+ * @editor: an #EContentEditor
+ * @flags: a bit-OR of #EContentEditorFindFlags flags
+ * @find_text: a text to find
+ * @replace_with: a text to replace the found text with
+ *
+ * Searches the content of the @editor for all the occurrences of
+ * the @find_text and replaces them with the @replace_with.
+ * The @flags modify the behaviour of the search.
+ *
+ * Once the replace is done, the "replace-all-done" signal should be
+ * emitted, by using e_content_editor_emit_replace_all_done().
+ *
+ * Since: 3.22
+ **/
+void
+e_content_editor_replace_all (EContentEditor *editor,
+                             guint32 flags,
+                             const gchar *find_text,
+                             const gchar *replace_with)
+{
+       EContentEditorInterface *iface;
+
+       g_return_if_fail (E_IS_CONTENT_EDITOR (editor));
+       g_return_if_fail (find_text != NULL);
+       g_return_if_fail (replace_with != NULL);
+
+       iface = E_CONTENT_EDITOR_GET_IFACE (editor);
+       g_return_if_fail (iface != NULL);
+       g_return_if_fail (iface->replace_all != NULL);
+
+       iface->replace_all (editor, flags, find_text, replace_with);
+}
+
+/**
+ * e_content_editor_selection_save:
+ * @editor: an #EContentEditor
+ *
+ * Saves current cursor position or current selection range. The selection can
+ * be later restored by calling e_content_editor_selection_restore().
+ *
+ * Note that calling e_content_editor_selection_save() overwrites previously saved
+ * position.
+ *
+ * Note that this method inserts special markings into the HTML code that are
+ * used to later restore the selection. It can happen that by deleting some
+ * segments of the document some of the markings are deleted too. In that case
+ * restoring the selection by e_content_editor_selection_restore() can fail. Also by
+ * moving text segments (Cut & Paste) can result in moving the markings
+ * elsewhere, thus e_content_editor_selection_restore() will restore the selection
+ * incorrectly.
+ *
+ * It is recommended to use this method only when you are not planning to make
+ * bigger changes to content or structure of the document (formatting changes
+ * are usually OK).
+ *
+ * Since: 3.22
+ **/
+void
+e_content_editor_selection_save (EContentEditor *editor)
+{
+       EContentEditorInterface *iface;
+
+       g_return_if_fail (E_IS_CONTENT_EDITOR (editor));
+
+       iface = E_CONTENT_EDITOR_GET_IFACE (editor);
+       g_return_if_fail (iface != NULL);
+       g_return_if_fail (iface->selection_save != NULL);
+
+       iface->selection_save (editor);
+}
+
+/**
+ * e_content_editor_selection_restore:
+ * @editor: an #EContentEditor
+ *
+ * Restores cursor position or selection range that was saved by
+ * e_content_editor_selection_save().
+ *
+ * Note that calling this function without calling e_content_editor_selection_save()
+ * before is a programming error and the behavior is undefined.
+ *
+ * Since: 3.22
+ **/
+void
+e_content_editor_selection_restore (EContentEditor *editor)
+{
+       EContentEditorInterface *iface;
+
+       g_return_if_fail (E_IS_CONTENT_EDITOR (editor));
+
+       iface = E_CONTENT_EDITOR_GET_IFACE (editor);
+       g_return_if_fail (iface != NULL);
+       g_return_if_fail (iface->selection_restore != NULL);
+
+       iface->selection_restore (editor);
+}
+
+void
+e_content_editor_selection_wrap (EContentEditor *editor)
+{
+       EContentEditorInterface *iface;
+
+       g_return_if_fail (E_IS_CONTENT_EDITOR (editor));
+
+       iface = E_CONTENT_EDITOR_GET_IFACE (editor);
+       g_return_if_fail (iface != NULL);
+       g_return_if_fail (iface->selection_wrap != NULL);
+
+       iface->selection_wrap (editor);
+}
+
+guint
+e_content_editor_get_caret_position (EContentEditor *editor)
+{
+       EContentEditorInterface *iface;
+
+       g_return_val_if_fail (E_IS_CONTENT_EDITOR (editor), 0);
+
+       iface = E_CONTENT_EDITOR_GET_IFACE (editor);
+       g_return_val_if_fail (iface != NULL, 0);
+       g_return_val_if_fail (iface->get_caret_position != NULL, 0);
+
+       return iface->get_caret_position (editor);
+}
+
+guint
+e_content_editor_get_caret_offset (EContentEditor *editor)
+{
+       EContentEditorInterface *iface;
+
+       g_return_val_if_fail (E_IS_CONTENT_EDITOR (editor), 0);
+
+       iface = E_CONTENT_EDITOR_GET_IFACE (editor);
+       g_return_val_if_fail (iface != NULL, 0);
+       g_return_val_if_fail (iface->get_caret_offset != NULL, 0);
+
+       return iface->get_caret_offset (editor);
+}
+
+gchar *
+e_content_editor_get_current_signature_uid (EContentEditor *editor)
+{
+       EContentEditorInterface *iface;
+
+       g_return_val_if_fail (E_IS_CONTENT_EDITOR (editor), NULL);
+
+       iface = E_CONTENT_EDITOR_GET_IFACE (editor);
+       g_return_val_if_fail (iface != NULL, NULL);
+       g_return_val_if_fail (iface->get_current_signature_uid != NULL, NULL);
+
+       return iface->get_current_signature_uid (editor);
+}
+
+gboolean
+e_content_editor_is_ready (EContentEditor *editor)
+{
+       EContentEditorInterface *iface;
+
+       g_return_val_if_fail (E_IS_CONTENT_EDITOR (editor), FALSE);
+
+       iface = E_CONTENT_EDITOR_GET_IFACE (editor);
+       g_return_val_if_fail (iface != NULL, FALSE);
+       g_return_val_if_fail (iface->is_ready != NULL, FALSE);
+
+       return iface->is_ready (editor);
+}
+
+gchar *
+e_content_editor_insert_signature (EContentEditor *editor,
+                                   const gchar *content,
+                                   gboolean is_html,
+                                   const gchar *signature_id,
+                                   gboolean *set_signature_from_message,
+                                   gboolean *check_if_signature_is_changed,
+                                   gboolean *ignore_next_signature_change)
+{
+       EContentEditorInterface *iface;
+
+       g_return_val_if_fail (E_IS_CONTENT_EDITOR (editor), FALSE);
+
+       iface = E_CONTENT_EDITOR_GET_IFACE (editor);
+       g_return_val_if_fail (iface != NULL, FALSE);
+       g_return_val_if_fail (iface->insert_signature != NULL, FALSE);
+
+       return iface->insert_signature (
+               editor,
+               content,
+               is_html,
+               signature_id,
+               set_signature_from_message,
+               check_if_signature_is_changed,
+               ignore_next_signature_change);
+}
+
+void
+e_content_editor_delete_cell_contents (EContentEditor *editor)
+{
+       EContentEditorInterface *iface;
+
+       g_return_if_fail (E_IS_CONTENT_EDITOR (editor));
+
+       iface = E_CONTENT_EDITOR_GET_IFACE (editor);
+       g_return_if_fail (iface != NULL);
+       g_return_if_fail (iface->delete_cell_contents != NULL);
+
+       iface->delete_cell_contents (editor);
+}
+
+void
+e_content_editor_delete_column (EContentEditor *editor)
+{
+       EContentEditorInterface *iface;
+
+       g_return_if_fail (E_IS_CONTENT_EDITOR (editor));
+
+       iface = E_CONTENT_EDITOR_GET_IFACE (editor);
+       g_return_if_fail (iface != NULL);
+       g_return_if_fail (iface->delete_column != NULL);
+
+       iface->delete_column (editor);
+}
+
+void
+e_content_editor_delete_row (EContentEditor *editor)
+{
+       EContentEditorInterface *iface;
+
+       g_return_if_fail (E_IS_CONTENT_EDITOR (editor));
+
+       iface = E_CONTENT_EDITOR_GET_IFACE (editor);
+       g_return_if_fail (iface != NULL);
+       g_return_if_fail (iface->delete_row != NULL);
+
+       iface->delete_row (editor);
+}
+
+void
+e_content_editor_delete_table (EContentEditor *editor)
+{
+       EContentEditorInterface *iface;
+
+       g_return_if_fail (E_IS_CONTENT_EDITOR (editor));
+
+       iface = E_CONTENT_EDITOR_GET_IFACE (editor);
+       g_return_if_fail (iface != NULL);
+       g_return_if_fail (iface->delete_table != NULL);
+
+       iface->delete_table (editor);
+}
+
+void
+e_content_editor_insert_column_after (EContentEditor *editor)
+{
+       EContentEditorInterface *iface;
+
+       g_return_if_fail (E_IS_CONTENT_EDITOR (editor));
+
+       iface = E_CONTENT_EDITOR_GET_IFACE (editor);
+       g_return_if_fail (iface != NULL);
+       g_return_if_fail (iface->insert_column_after != NULL);
+
+       iface->insert_column_after (editor);
+}
+
+void
+e_content_editor_insert_column_before (EContentEditor *editor)
+{
+       EContentEditorInterface *iface;
+
+       g_return_if_fail (E_IS_CONTENT_EDITOR (editor));
+
+       iface = E_CONTENT_EDITOR_GET_IFACE (editor);
+       g_return_if_fail (iface != NULL);
+       g_return_if_fail (iface->insert_column_before != NULL);
+
+       iface->insert_column_before (editor);
+}
+
+void
+e_content_editor_insert_row_above (EContentEditor *editor)
+{
+       EContentEditorInterface *iface;
+
+       g_return_if_fail (E_IS_CONTENT_EDITOR (editor));
+
+       iface = E_CONTENT_EDITOR_GET_IFACE (editor);
+       g_return_if_fail (iface != NULL);
+       g_return_if_fail (iface->insert_row_above != NULL);
+
+       iface->insert_row_above (editor);
+}
+
+void
+e_content_editor_insert_row_below (EContentEditor *editor)
+{
+       EContentEditorInterface *iface;
+
+       g_return_if_fail (E_IS_CONTENT_EDITOR (editor));
+
+       iface = E_CONTENT_EDITOR_GET_IFACE (editor);
+       g_return_if_fail (iface != NULL);
+       g_return_if_fail (iface->insert_row_below != NULL);
+
+       iface->insert_row_below (editor);
+}
+
+gboolean
+e_content_editor_on_h_rule_dialog_open (EContentEditor *editor)
+{
+       EContentEditorInterface *iface;
+
+       g_return_val_if_fail (E_IS_CONTENT_EDITOR (editor), FALSE);
+
+       iface = E_CONTENT_EDITOR_GET_IFACE (editor);
+       g_return_val_if_fail (iface != NULL, FALSE);
+       g_return_val_if_fail (iface->on_h_rule_dialog_open != NULL, FALSE);
+
+       return iface->on_h_rule_dialog_open (editor);
+}
+
+void
+e_content_editor_on_h_rule_dialog_close (EContentEditor *editor)
+{
+       EContentEditorInterface *iface;
+
+       g_return_if_fail (E_IS_CONTENT_EDITOR (editor));
+
+       iface = E_CONTENT_EDITOR_GET_IFACE (editor);
+       g_return_if_fail (iface != NULL);
+       g_return_if_fail (iface->on_h_rule_dialog_close != NULL);
+
+       iface->on_h_rule_dialog_close (editor);
+}
+
+void
+e_content_editor_h_rule_set_align (EContentEditor *editor,
+                                   const gchar *value)
+{
+       EContentEditorInterface *iface;
+
+       g_return_if_fail (E_IS_CONTENT_EDITOR (editor));
+
+       iface = E_CONTENT_EDITOR_GET_IFACE (editor);
+       g_return_if_fail (iface != NULL);
+       g_return_if_fail (iface->h_rule_set_align != NULL);
+
+       iface->h_rule_set_align (editor, value);
+}
+
+gchar *
+e_content_editor_h_rule_get_align (EContentEditor *editor)
+{
+       EContentEditorInterface *iface;
+
+       g_return_val_if_fail (E_IS_CONTENT_EDITOR (editor), NULL);
+
+       iface = E_CONTENT_EDITOR_GET_IFACE (editor);
+       g_return_val_if_fail (iface != NULL, NULL);
+       g_return_val_if_fail (iface->h_rule_get_align != NULL, NULL);
+
+       return iface->h_rule_get_align (editor);
+}
+
+void
+e_content_editor_h_rule_set_size (EContentEditor *editor,
+                                  gint value)
+{
+       EContentEditorInterface *iface;
+
+       g_return_if_fail (E_IS_CONTENT_EDITOR (editor));
+
+       iface = E_CONTENT_EDITOR_GET_IFACE (editor);
+       g_return_if_fail (iface != NULL);
+       g_return_if_fail (iface->h_rule_set_size != NULL);
+
+       iface->h_rule_set_size (editor, value);
+}
+
+gint
+e_content_editor_h_rule_get_size (EContentEditor *editor)
+{
+       EContentEditorInterface *iface;
+
+       g_return_val_if_fail (E_IS_CONTENT_EDITOR (editor), 0);
+
+       iface = E_CONTENT_EDITOR_GET_IFACE (editor);
+       g_return_val_if_fail (iface != NULL, 0);
+       g_return_val_if_fail (iface->h_rule_get_size != NULL, 0);
+
+       return iface->h_rule_get_size (editor);
+}
+
+void
+e_content_editor_h_rule_set_width (EContentEditor *editor,
+                                   gint value,
+                                   EContentEditorUnit unit)
+{
+       EContentEditorInterface *iface;
+
+       g_return_if_fail (E_IS_CONTENT_EDITOR (editor));
+
+       iface = E_CONTENT_EDITOR_GET_IFACE (editor);
+       g_return_if_fail (iface != NULL);
+       g_return_if_fail (iface->h_rule_set_width != NULL);
+
+       iface->h_rule_set_width (editor, value, unit);
+}
+
+gint
+e_content_editor_h_rule_get_width (EContentEditor *editor,
+                                   EContentEditorUnit *unit)
+{
+       EContentEditorInterface *iface;
+
+       g_return_val_if_fail (E_IS_CONTENT_EDITOR (editor), 0);
+       g_return_val_if_fail (unit != NULL, 0);
+
+       iface = E_CONTENT_EDITOR_GET_IFACE (editor);
+       g_return_val_if_fail (iface != NULL, 0);
+       g_return_val_if_fail (iface->h_rule_get_width != NULL, 0);
+
+       return iface->h_rule_get_width (editor, unit);
+}
+
+void
+e_content_editor_h_rule_set_no_shade (EContentEditor *editor,
+                                      gboolean value)
+{
+       EContentEditorInterface *iface;
+
+       g_return_if_fail (E_IS_CONTENT_EDITOR (editor));
+
+       iface = E_CONTENT_EDITOR_GET_IFACE (editor);
+       g_return_if_fail (iface != NULL);
+       g_return_if_fail (iface->h_rule_set_no_shade != NULL);
+
+       iface->h_rule_set_no_shade (editor, value);
+}
+
+gboolean
+e_content_editor_h_rule_get_no_shade (EContentEditor *editor)
+{
+       EContentEditorInterface *iface;
+
+       g_return_val_if_fail (E_IS_CONTENT_EDITOR (editor), FALSE);
+
+       iface = E_CONTENT_EDITOR_GET_IFACE (editor);
+       g_return_val_if_fail (iface != NULL, FALSE);
+       g_return_val_if_fail (iface->h_rule_get_no_shade != NULL, FALSE);
+
+       return iface->h_rule_get_no_shade (editor);
+}
+
+void
+e_content_editor_on_image_dialog_open (EContentEditor *editor)
+{
+       EContentEditorInterface *iface;
+
+       g_return_if_fail (E_IS_CONTENT_EDITOR (editor));
+
+       iface = E_CONTENT_EDITOR_GET_IFACE (editor);
+       g_return_if_fail (iface != NULL);
+       g_return_if_fail (iface->on_image_dialog_open != NULL);
+
+       iface->on_image_dialog_open (editor);
+}
+
+void
+e_content_editor_on_image_dialog_close (EContentEditor *editor)
+{
+       EContentEditorInterface *iface;
+
+       g_return_if_fail (E_IS_CONTENT_EDITOR (editor));
+
+       iface = E_CONTENT_EDITOR_GET_IFACE (editor);
+       g_return_if_fail (iface != NULL);
+       g_return_if_fail (iface->on_image_dialog_close != NULL);
+
+       iface->on_image_dialog_close (editor);
+}
+
+void
+e_content_editor_image_set_width_follow (EContentEditor *editor,
+                                         gboolean value)
+{
+       EContentEditorInterface *iface;
+
+       g_return_if_fail (E_IS_CONTENT_EDITOR (editor));
+
+       iface = E_CONTENT_EDITOR_GET_IFACE (editor);
+       g_return_if_fail (iface != NULL);
+       g_return_if_fail (iface->image_set_width_follow != NULL);
+
+       iface->image_set_width_follow (editor, value);
+}
+
+void
+e_content_editor_image_set_src (EContentEditor *editor,
+                                const gchar *value)
+{
+       EContentEditorInterface *iface;
+
+       g_return_if_fail (E_IS_CONTENT_EDITOR (editor));
+
+       iface = E_CONTENT_EDITOR_GET_IFACE (editor);
+       g_return_if_fail (iface != NULL);
+       g_return_if_fail (iface->image_set_src != NULL);
+
+       iface->image_set_src (editor, value);
+}
+
+gchar *
+e_content_editor_image_get_src (EContentEditor *editor)
+{
+       EContentEditorInterface *iface;
+
+       g_return_val_if_fail (E_IS_CONTENT_EDITOR (editor), FALSE);
+
+       iface = E_CONTENT_EDITOR_GET_IFACE (editor);
+       g_return_val_if_fail (iface != NULL, FALSE);
+       g_return_val_if_fail (iface->image_get_src != NULL, FALSE);
+
+       return iface->image_get_src (editor);
+}
+
+void
+e_content_editor_image_set_alt (EContentEditor *editor,
+                                const gchar *value)
+{
+       EContentEditorInterface *iface;
+
+       g_return_if_fail (E_IS_CONTENT_EDITOR (editor));
+
+       iface = E_CONTENT_EDITOR_GET_IFACE (editor);
+       g_return_if_fail (iface != NULL);
+       g_return_if_fail (iface->image_set_alt != NULL);
+
+       iface->image_set_alt (editor, value);
+}
+
+gchar *
+e_content_editor_image_get_alt (EContentEditor *editor)
+{
+       EContentEditorInterface *iface;
+
+       g_return_val_if_fail (E_IS_CONTENT_EDITOR (editor), FALSE);
+
+       iface = E_CONTENT_EDITOR_GET_IFACE (editor);
+       g_return_val_if_fail (iface != NULL, FALSE);
+       g_return_val_if_fail (iface->image_get_alt != NULL, FALSE);
+
+       return iface->image_get_alt (editor);
+}
+
+void
+e_content_editor_image_set_url (EContentEditor *editor,
+                                const gchar *value)
+{
+       EContentEditorInterface *iface;
+
+       g_return_if_fail (E_IS_CONTENT_EDITOR (editor));
+
+       iface = E_CONTENT_EDITOR_GET_IFACE (editor);
+       g_return_if_fail (iface != NULL);
+       g_return_if_fail (iface->image_set_url != NULL);
+
+       iface->image_set_url (editor, value);
+}
+
+gchar *
+e_content_editor_image_get_url (EContentEditor *editor)
+{
+       EContentEditorInterface *iface;
+
+       g_return_val_if_fail (E_IS_CONTENT_EDITOR (editor), FALSE);
+
+       iface = E_CONTENT_EDITOR_GET_IFACE (editor);
+       g_return_val_if_fail (iface != NULL, FALSE);
+       g_return_val_if_fail (iface->image_get_url != NULL, FALSE);
+
+       return iface->image_get_url (editor);
+}
+
+void
+e_content_editor_image_set_vspace (EContentEditor *editor,
+                                   gint value)
+{
+       EContentEditorInterface *iface;
+
+       g_return_if_fail (E_IS_CONTENT_EDITOR (editor));
+
+       iface = E_CONTENT_EDITOR_GET_IFACE (editor);
+       g_return_if_fail (iface != NULL);
+       g_return_if_fail (iface->image_set_vspace != NULL);
+
+       iface->image_set_vspace (editor, value);
+}
+
+gint
+e_content_editor_image_get_vspace (EContentEditor *editor)
+{
+       EContentEditorInterface *iface;
+
+       g_return_val_if_fail (E_IS_CONTENT_EDITOR (editor), 0);
+
+       iface = E_CONTENT_EDITOR_GET_IFACE (editor);
+       g_return_val_if_fail (iface != NULL, 0);
+       g_return_val_if_fail (iface->image_get_vspace != NULL, 0);
+
+       return iface->image_get_vspace (editor);
+}
+
+
+void
+e_content_editor_image_set_hspace (EContentEditor *editor,
+                                   gint value)
+{
+       EContentEditorInterface *iface;
+
+       g_return_if_fail (E_IS_CONTENT_EDITOR (editor));
+
+       iface = E_CONTENT_EDITOR_GET_IFACE (editor);
+       g_return_if_fail (iface != NULL);
+       g_return_if_fail (iface->image_set_hspace != NULL);
+
+       iface->image_set_hspace (editor, value);
+}
+
+gint
+e_content_editor_image_get_hspace (EContentEditor *editor)
+{
+       EContentEditorInterface *iface;
+
+       g_return_val_if_fail (E_IS_CONTENT_EDITOR (editor), 0);
+
+       iface = E_CONTENT_EDITOR_GET_IFACE (editor);
+       g_return_val_if_fail (iface != NULL, 0);
+       g_return_val_if_fail (iface->image_get_hspace != NULL, 0);
+
+       return iface->image_get_hspace (editor);
+}
+
+void
+e_content_editor_image_set_border (EContentEditor *editor,
+                                   gint value)
+{
+       EContentEditorInterface *iface;
+
+       g_return_if_fail (E_IS_CONTENT_EDITOR (editor));
+
+       iface = E_CONTENT_EDITOR_GET_IFACE (editor);
+       g_return_if_fail (iface != NULL);
+       g_return_if_fail (iface->image_set_border != NULL);
+
+       iface->image_set_border (editor, value);
+}
+
+gint
+e_content_editor_image_get_border (EContentEditor *editor)
+{
+       EContentEditorInterface *iface;
+
+       g_return_val_if_fail (E_IS_CONTENT_EDITOR (editor), FALSE);
+
+       iface = E_CONTENT_EDITOR_GET_IFACE (editor);
+       g_return_val_if_fail (iface != NULL, FALSE);
+       g_return_val_if_fail (iface->image_get_border != NULL, FALSE);
+
+       return iface->image_get_border (editor);
+}
+
+void
+e_content_editor_image_set_align (EContentEditor *editor,
+                                  const gchar *value)
+{
+       EContentEditorInterface *iface;
+
+       g_return_if_fail (E_IS_CONTENT_EDITOR (editor));
+
+       iface = E_CONTENT_EDITOR_GET_IFACE (editor);
+       g_return_if_fail (iface != NULL);
+       g_return_if_fail (iface->image_set_align != NULL);
+
+       iface->image_set_align (editor, value);
+}
+
+gchar *
+e_content_editor_image_get_align (EContentEditor *editor)
+{
+       EContentEditorInterface *iface;
+
+       g_return_val_if_fail (E_IS_CONTENT_EDITOR (editor), FALSE);
+
+       iface = E_CONTENT_EDITOR_GET_IFACE (editor);
+       g_return_val_if_fail (iface != NULL, FALSE);
+       g_return_val_if_fail (iface->image_get_align != NULL, FALSE);
+
+       return iface->image_get_align (editor);
+}
+
+gint32
+e_content_editor_image_get_natural_width (EContentEditor *editor)
+{
+       EContentEditorInterface *iface;
+
+       g_return_val_if_fail (E_IS_CONTENT_EDITOR (editor), 0);
+
+       iface = E_CONTENT_EDITOR_GET_IFACE (editor);
+       g_return_val_if_fail (iface != NULL, 0);
+       g_return_val_if_fail (iface->image_get_natural_width != NULL, 0);
+
+       return iface->image_get_natural_width (editor);
+}
+
+gint32
+e_content_editor_image_get_natural_height (EContentEditor *editor)
+{
+       EContentEditorInterface *iface;
+
+       g_return_val_if_fail (E_IS_CONTENT_EDITOR (editor), 0);
+
+       iface = E_CONTENT_EDITOR_GET_IFACE (editor);
+       g_return_val_if_fail (iface != NULL, 0);
+       g_return_val_if_fail (iface->image_get_natural_height != NULL, 0);
+
+       return iface->image_get_natural_height (editor);
+}
+
+void
+e_content_editor_image_set_width (EContentEditor *editor,
+                                  gint value)
+{
+       EContentEditorInterface *iface;
+
+       g_return_if_fail (E_IS_CONTENT_EDITOR (editor));
+
+       iface = E_CONTENT_EDITOR_GET_IFACE (editor);
+       g_return_if_fail (iface != NULL);
+       g_return_if_fail (iface->image_set_width != NULL);
+
+       iface->image_set_width (editor, value);
+}
+
+gint32
+e_content_editor_image_get_width (EContentEditor *editor)
+{
+       EContentEditorInterface *iface;
+
+       g_return_val_if_fail (E_IS_CONTENT_EDITOR (editor), 0);
+       g_return_val_if_fail (E_IS_CONTENT_EDITOR (editor), 0);
+
+       iface = E_CONTENT_EDITOR_GET_IFACE (editor);
+       g_return_val_if_fail (iface != NULL, 0);
+       g_return_val_if_fail (iface->image_get_width != NULL, 0);
+
+       return iface->image_get_width (editor);
+}
+
+void
+e_content_editor_image_set_height (EContentEditor *editor,
+                                   gint value)
+{
+       EContentEditorInterface *iface;
+
+       g_return_if_fail (E_IS_CONTENT_EDITOR (editor));
+
+       iface = E_CONTENT_EDITOR_GET_IFACE (editor);
+       g_return_if_fail (iface != NULL);
+       g_return_if_fail (iface->image_set_height != NULL);
+
+       iface->image_set_height (editor, value);
+}
+
+gint32
+e_content_editor_image_get_height (EContentEditor *editor)
+{
+       EContentEditorInterface *iface;
+
+       g_return_val_if_fail (E_IS_CONTENT_EDITOR (editor), 0);
+
+       iface = E_CONTENT_EDITOR_GET_IFACE (editor);
+       g_return_val_if_fail (iface != NULL, 0);
+       g_return_val_if_fail (iface->image_get_height != NULL, 0);
+
+       return iface->image_get_height (editor);
+}
+
+void
+e_content_editor_image_set_height_follow (EContentEditor *editor,
+                                          gboolean value)
+{
+       EContentEditorInterface *iface;
+
+       g_return_if_fail (E_IS_CONTENT_EDITOR (editor));
+
+       iface = E_CONTENT_EDITOR_GET_IFACE (editor);
+       g_return_if_fail (iface != NULL);
+       g_return_if_fail (iface->image_set_height_follow != NULL);
+
+       iface->image_set_height_follow (editor, value);
+}
+
+void
+e_content_editor_on_link_dialog_open (EContentEditor *editor)
+{
+       EContentEditorInterface *iface;
+
+       g_return_if_fail (E_IS_CONTENT_EDITOR (editor));
+
+       iface = E_CONTENT_EDITOR_GET_IFACE (editor);
+       g_return_if_fail (iface != NULL);
+       g_return_if_fail (iface->on_link_dialog_open != NULL);
+
+       iface->on_link_dialog_open (editor);
+}
+
+void
+e_content_editor_on_link_dialog_close (EContentEditor *editor)
+{
+       EContentEditorInterface *iface;
+
+       g_return_if_fail (E_IS_CONTENT_EDITOR (editor));
+
+       iface = E_CONTENT_EDITOR_GET_IFACE (editor);
+       g_return_if_fail (iface != NULL);
+       g_return_if_fail (iface->on_link_dialog_close != NULL);
+
+       iface->on_link_dialog_close (editor);
+}
+
+void
+e_content_editor_link_get_values (EContentEditor *editor,
+                                  gchar **href,
+                                  gchar **text)
+{
+       EContentEditorInterface *iface;
+
+       g_return_if_fail (E_IS_CONTENT_EDITOR (editor));
+
+       iface = E_CONTENT_EDITOR_GET_IFACE (editor);
+       g_return_if_fail (iface != NULL);
+       g_return_if_fail (iface->link_get_values != NULL);
+
+       return iface->link_get_values (editor, href, text);
+}
+
+void
+e_content_editor_link_set_values (EContentEditor *editor,
+                                  const gchar *href,
+                                  const gchar *text)
+{
+       EContentEditorInterface *iface;
+
+       g_return_if_fail (E_IS_CONTENT_EDITOR (editor));
+
+       iface = E_CONTENT_EDITOR_GET_IFACE (editor);
+       g_return_if_fail (iface != NULL);
+       g_return_if_fail (iface->link_set_values != NULL);
+
+       iface->link_set_values (editor, href, text);
+}
+
+void
+e_content_editor_on_page_dialog_open (EContentEditor *editor)
+{
+       EContentEditorInterface *iface;
+
+       g_return_if_fail (E_IS_CONTENT_EDITOR (editor));
+
+       iface = E_CONTENT_EDITOR_GET_IFACE (editor);
+       g_return_if_fail (iface != NULL);
+       g_return_if_fail (iface->on_page_dialog_open != NULL);
+
+       iface->on_page_dialog_open (editor);
+}
+
+void
+e_content_editor_on_page_dialog_close (EContentEditor *editor)
+{
+       EContentEditorInterface *iface;
+
+       g_return_if_fail (E_IS_CONTENT_EDITOR (editor));
+
+       iface = E_CONTENT_EDITOR_GET_IFACE (editor);
+       g_return_if_fail (iface != NULL);
+       g_return_if_fail (iface->on_page_dialog_close != NULL);
+
+       iface->on_page_dialog_close (editor);
+}
+
+void
+e_content_editor_page_set_text_color (EContentEditor *editor,
+                                      const GdkRGBA *value)
+{
+       EContentEditorInterface *iface;
+
+       g_return_if_fail (E_IS_CONTENT_EDITOR (editor));
+       g_return_if_fail (value != NULL);
+
+       iface = E_CONTENT_EDITOR_GET_IFACE (editor);
+       g_return_if_fail (iface != NULL);
+       g_return_if_fail (iface->page_set_text_color != NULL);
+
+       iface->page_set_text_color (editor, value);
+}
+
+void
+e_content_editor_page_get_text_color (EContentEditor *editor,
+                                      GdkRGBA *value)
+{
+       EContentEditorInterface *iface;
+
+       g_return_if_fail (E_IS_CONTENT_EDITOR (editor));
+       g_return_if_fail (value != NULL);
+
+       iface = E_CONTENT_EDITOR_GET_IFACE (editor);
+       g_return_if_fail (iface != NULL);
+       g_return_if_fail (iface->page_get_text_color != NULL);
+
+       return iface->page_get_text_color (editor, value);
+}
+
+void
+e_content_editor_page_set_background_color (EContentEditor *editor,
+                                            const GdkRGBA *value)
+{
+       EContentEditorInterface *iface;
+
+       g_return_if_fail (E_IS_CONTENT_EDITOR (editor));
+       g_return_if_fail (value != NULL);
+
+       iface = E_CONTENT_EDITOR_GET_IFACE (editor);
+       g_return_if_fail (iface != NULL);
+       g_return_if_fail (iface->page_set_background_color != NULL);
+
+       iface->page_set_background_color (editor, value);
+}
+
+void
+e_content_editor_page_get_background_color (EContentEditor *editor,
+                                            GdkRGBA *value)
+{
+       EContentEditorInterface *iface;
+
+       g_return_if_fail (E_IS_CONTENT_EDITOR (editor));
+       g_return_if_fail (value != NULL);
+
+       iface = E_CONTENT_EDITOR_GET_IFACE (editor);
+       g_return_if_fail (iface != NULL);
+       g_return_if_fail (iface->page_get_background_color != NULL);
+
+       return iface->page_get_background_color (editor, value);
+}
+
+void
+e_content_editor_page_set_link_color (EContentEditor *editor,
+                                      const GdkRGBA *value)
+{
+       EContentEditorInterface *iface;
+
+       g_return_if_fail (E_IS_CONTENT_EDITOR (editor));
+       g_return_if_fail (value != NULL);
+
+       iface = E_CONTENT_EDITOR_GET_IFACE (editor);
+       g_return_if_fail (iface != NULL);
+       g_return_if_fail (iface->page_set_link_color != NULL);
+
+       iface->page_set_link_color (editor, value);
+}
+
+void
+e_content_editor_page_get_link_color (EContentEditor *editor,
+                                      GdkRGBA *value)
+{
+       EContentEditorInterface *iface;
+
+       g_return_if_fail (E_IS_CONTENT_EDITOR (editor));
+       g_return_if_fail (value != NULL);
+
+       iface = E_CONTENT_EDITOR_GET_IFACE (editor);
+       g_return_if_fail (iface != NULL);
+       g_return_if_fail (iface->page_get_link_color != NULL);
+
+       return iface->page_get_link_color (editor, value);
+}
+
+void
+e_content_editor_page_set_visited_link_color (EContentEditor *editor,
+                                              const GdkRGBA *value)
+{
+       EContentEditorInterface *iface;
+
+       g_return_if_fail (E_IS_CONTENT_EDITOR (editor));
+       g_return_if_fail (value != NULL);
+
+       iface = E_CONTENT_EDITOR_GET_IFACE (editor);
+       g_return_if_fail (iface != NULL);
+       g_return_if_fail (iface->page_set_visited_link_color != NULL);
+
+       iface->page_set_visited_link_color (editor, value);
+}
+
+void
+e_content_editor_page_get_visited_link_color (EContentEditor *editor,
+                                              GdkRGBA *value)
+{
+       EContentEditorInterface *iface;
+
+       g_return_if_fail (E_IS_CONTENT_EDITOR (editor));
+       g_return_if_fail (value != NULL);
+
+       iface = E_CONTENT_EDITOR_GET_IFACE (editor);
+       g_return_if_fail (iface != NULL);
+       g_return_if_fail (iface->page_get_visited_link_color != NULL);
+
+       return iface->page_get_visited_link_color (editor, value);
+}
+
+/* uri could be NULL -> removes the current image */
+void
+e_content_editor_page_set_background_image_uri (EContentEditor *editor,
+                                                const gchar *uri)
+{
+       EContentEditorInterface *iface;
+
+       g_return_if_fail (E_IS_CONTENT_EDITOR (editor));
+
+       iface = E_CONTENT_EDITOR_GET_IFACE (editor);
+       g_return_if_fail (iface != NULL);
+       g_return_if_fail (iface->page_set_background_image_uri != NULL);
+
+       iface->page_set_background_image_uri (editor, uri);
+}
+
+gchar *
+e_content_editor_page_get_background_image_uri (EContentEditor *editor)
+{
+       EContentEditorInterface *iface;
+
+       g_return_val_if_fail (E_IS_CONTENT_EDITOR (editor), NULL);
+
+       iface = E_CONTENT_EDITOR_GET_IFACE (editor);
+       g_return_val_if_fail (iface != NULL, NULL);
+       g_return_val_if_fail (iface->page_get_background_image_uri != NULL, NULL);
+
+       return iface->page_get_background_image_uri (editor);
+}
+
+void
+e_content_editor_on_cell_dialog_open (EContentEditor *editor)
+{
+       EContentEditorInterface *iface;
+
+       g_return_if_fail (E_IS_CONTENT_EDITOR (editor));
+
+       iface = E_CONTENT_EDITOR_GET_IFACE (editor);
+       g_return_if_fail (iface != NULL);
+       g_return_if_fail (iface->on_cell_dialog_open != NULL);
+
+       iface->on_cell_dialog_open (editor);
+}
+
+void
+e_content_editor_on_cell_dialog_close (EContentEditor *editor)
+{
+       EContentEditorInterface *iface;
+
+       g_return_if_fail (E_IS_CONTENT_EDITOR (editor));
+
+       iface = E_CONTENT_EDITOR_GET_IFACE (editor);
+       g_return_if_fail (iface != NULL);
+       g_return_if_fail (iface->on_cell_dialog_close != NULL);
+
+       iface->on_cell_dialog_close (editor);
+}
+
+void
+e_content_editor_cell_set_v_align (EContentEditor *editor,
+                                   const gchar *value,
+                                   EContentEditorScope scope)
+{
+       EContentEditorInterface *iface;
+
+       g_return_if_fail (E_IS_CONTENT_EDITOR (editor));
+       g_return_if_fail (value != NULL);
+
+       iface = E_CONTENT_EDITOR_GET_IFACE (editor);
+       g_return_if_fail (iface != NULL);
+       g_return_if_fail (iface->cell_set_v_align != NULL);
+
+       iface->cell_set_v_align (editor, value, scope);
+}
+
+gchar *
+e_content_editor_cell_get_v_align (EContentEditor *editor)
+{
+       EContentEditorInterface *iface;
+
+       g_return_val_if_fail (E_IS_CONTENT_EDITOR (editor), NULL);
+
+       iface = E_CONTENT_EDITOR_GET_IFACE (editor);
+       g_return_val_if_fail (iface != NULL, NULL);
+       g_return_val_if_fail (iface->cell_get_v_align != NULL, NULL);
+
+       return iface->cell_get_v_align (editor);
+}
+
+void
+e_content_editor_cell_set_align        (EContentEditor *editor,
+                                 const gchar *value,
+                                 EContentEditorScope scope)
+{
+       EContentEditorInterface *iface;
+
+       g_return_if_fail (E_IS_CONTENT_EDITOR (editor));
+       g_return_if_fail (value != NULL);
+
+       iface = E_CONTENT_EDITOR_GET_IFACE (editor);
+       g_return_if_fail (iface != NULL);
+       g_return_if_fail (iface->cell_set_align != NULL);
+
+       iface->cell_set_align (editor, value, scope);
+}
+
+gchar *
+e_content_editor_cell_get_align (EContentEditor *editor)
+{
+       EContentEditorInterface *iface;
+
+       g_return_val_if_fail (E_IS_CONTENT_EDITOR (editor), NULL);
+
+       iface = E_CONTENT_EDITOR_GET_IFACE (editor);
+       g_return_val_if_fail (iface != NULL, NULL);
+       g_return_val_if_fail (iface->cell_get_align != NULL, NULL);
+
+       return iface->cell_get_align (editor);
+}
+
+void
+e_content_editor_cell_set_wrap (EContentEditor *editor,
+                                gboolean value,
+                                EContentEditorScope scope)
+{
+       EContentEditorInterface *iface;
+
+       g_return_if_fail (E_IS_CONTENT_EDITOR (editor));
+
+       iface = E_CONTENT_EDITOR_GET_IFACE (editor);
+       g_return_if_fail (iface != NULL);
+       g_return_if_fail (iface->cell_set_wrap != NULL);
+
+       iface->cell_set_wrap (editor, value, scope);
+}
+
+gboolean
+e_content_editor_cell_get_wrap (EContentEditor *editor)
+{
+       EContentEditorInterface *iface;
+
+       g_return_val_if_fail (E_IS_CONTENT_EDITOR (editor), FALSE);
+
+       iface = E_CONTENT_EDITOR_GET_IFACE (editor);
+       g_return_val_if_fail (iface != NULL, FALSE);
+       g_return_val_if_fail (iface->cell_get_wrap != NULL, FALSE);
+
+       return iface->cell_get_wrap (editor);
+}
+
+void
+e_content_editor_cell_set_header_style (EContentEditor *editor,
+                                        gboolean value,
+                                        EContentEditorScope scope)
+{
+       EContentEditorInterface *iface;
+
+       g_return_if_fail (E_IS_CONTENT_EDITOR (editor));
+
+       iface = E_CONTENT_EDITOR_GET_IFACE (editor);
+       g_return_if_fail (iface != NULL);
+       g_return_if_fail (iface->cell_set_header_style != NULL);
+
+       iface->cell_set_header_style (editor, value, scope);
+}
+
+gboolean
+e_content_editor_cell_is_header (EContentEditor *editor)
+{
+       EContentEditorInterface *iface;
+
+       g_return_val_if_fail (E_IS_CONTENT_EDITOR (editor), FALSE);
+
+       iface = E_CONTENT_EDITOR_GET_IFACE (editor);
+       g_return_val_if_fail (iface != NULL, FALSE);
+       g_return_val_if_fail (iface->cell_is_header != NULL, FALSE);
+
+       return iface->cell_is_header (editor);
+}
+
+void
+e_content_editor_cell_set_width (EContentEditor *editor,
+                                 gint value,
+                                 EContentEditorUnit unit,
+                                 EContentEditorScope scope)
+{
+       EContentEditorInterface *iface;
+
+       g_return_if_fail (E_IS_CONTENT_EDITOR (editor));
+
+       iface = E_CONTENT_EDITOR_GET_IFACE (editor);
+       g_return_if_fail (iface != NULL);
+       g_return_if_fail (iface->cell_set_width != NULL);
+
+       iface->cell_set_width (editor, value, unit, scope);
+}
+
+gint
+e_content_editor_cell_get_width (EContentEditor *editor,
+                                 EContentEditorUnit *unit)
+{
+       EContentEditorInterface *iface;
+
+       g_return_val_if_fail (E_IS_CONTENT_EDITOR (editor), 0);
+       g_return_val_if_fail (unit != NULL, 0);
+
+       iface = E_CONTENT_EDITOR_GET_IFACE (editor);
+       g_return_val_if_fail (iface != NULL, 0);
+       g_return_val_if_fail (iface->cell_get_width != NULL, 0);
+
+       return iface->cell_get_width (editor, unit);
+}
+
+void
+e_content_editor_cell_set_row_span (EContentEditor *editor,
+                                    gint value,
+                                    EContentEditorScope scope)
+{
+       EContentEditorInterface *iface;
+
+       g_return_if_fail (E_IS_CONTENT_EDITOR (editor));
+
+       iface = E_CONTENT_EDITOR_GET_IFACE (editor);
+       g_return_if_fail (iface != NULL);
+       g_return_if_fail (iface->cell_set_row_span != NULL);
+
+       iface->cell_set_row_span (editor, value, scope);
+}
+
+gint
+e_content_editor_cell_get_row_span (EContentEditor *editor)
+{
+       EContentEditorInterface *iface;
+
+       g_return_val_if_fail (E_IS_CONTENT_EDITOR (editor), 0);
+
+       iface = E_CONTENT_EDITOR_GET_IFACE (editor);
+       g_return_val_if_fail (iface != NULL, 0);
+       g_return_val_if_fail (iface->cell_get_row_span != NULL, 0);
+
+       return iface->cell_get_row_span (editor);
+}
+
+void
+e_content_editor_cell_set_col_span (EContentEditor *editor,
+                                    gint value,
+                                    EContentEditorScope scope)
+{
+       EContentEditorInterface *iface;
+
+       g_return_if_fail (E_IS_CONTENT_EDITOR (editor));
+
+       iface = E_CONTENT_EDITOR_GET_IFACE (editor);
+       g_return_if_fail (iface != NULL);
+       g_return_if_fail (iface->cell_set_col_span != NULL);
+
+       iface->cell_set_col_span (editor, value, scope);
+}
+
+gint
+e_content_editor_cell_get_col_span (EContentEditor *editor)
+{
+       EContentEditorInterface *iface;
+
+       g_return_val_if_fail (E_IS_CONTENT_EDITOR (editor), 0);
+
+       iface = E_CONTENT_EDITOR_GET_IFACE (editor);
+       g_return_val_if_fail (iface != NULL, 0);
+       g_return_val_if_fail (iface->cell_get_col_span != NULL, 0);
+
+       return iface->cell_get_col_span (editor);
+}
+
+/* uri could be NULL -> removes the current image */
+void
+e_content_editor_cell_set_background_image_uri (EContentEditor *editor,
+                                                const gchar *uri)
+{
+       EContentEditorInterface *iface;
+
+       g_return_if_fail (E_IS_CONTENT_EDITOR (editor));
+
+       iface = E_CONTENT_EDITOR_GET_IFACE (editor);
+       g_return_if_fail (iface != NULL);
+       g_return_if_fail (iface->cell_set_background_image_uri != NULL);
+
+       iface->cell_set_background_image_uri (editor, uri);
+}
+
+gchar *
+e_content_editor_cell_get_background_image_uri (EContentEditor *editor)
+{
+       EContentEditorInterface *iface;
+
+       g_return_val_if_fail (E_IS_CONTENT_EDITOR (editor), NULL);
+
+       iface = E_CONTENT_EDITOR_GET_IFACE (editor);
+       g_return_val_if_fail (iface != NULL, NULL);
+       g_return_val_if_fail (iface->cell_get_background_image_uri != NULL, NULL);
+
+       return iface->cell_get_background_image_uri (editor);
+}
+
+void
+e_content_editor_cell_set_background_color (EContentEditor *editor,
+                                            const GdkRGBA *value,
+                                            EContentEditorScope scope)
+{
+       EContentEditorInterface *iface;
+
+       g_return_if_fail (E_IS_CONTENT_EDITOR (editor));
+       g_return_if_fail (value != NULL);
+
+       iface = E_CONTENT_EDITOR_GET_IFACE (editor);
+       g_return_if_fail (iface != NULL);
+       g_return_if_fail (iface->cell_set_background_color != NULL);
+
+       iface->cell_set_background_color (editor, value, scope);
+}
+
+void
+e_content_editor_cell_get_background_color (EContentEditor *editor,
+                                            GdkRGBA *value)
+{
+       EContentEditorInterface *iface;
+
+       g_return_if_fail (E_IS_CONTENT_EDITOR (editor));
+
+       iface = E_CONTENT_EDITOR_GET_IFACE (editor);
+       g_return_if_fail (iface != NULL);
+       g_return_if_fail (iface->cell_get_background_color != NULL);
+
+       iface->cell_get_background_color (editor, value);
+}
+
+void
+e_content_editor_table_set_row_count (EContentEditor *editor,
+                                      guint value)
+{
+       EContentEditorInterface *iface;
+
+       g_return_if_fail (E_IS_CONTENT_EDITOR (editor));
+
+       iface = E_CONTENT_EDITOR_GET_IFACE (editor);
+       g_return_if_fail (iface != NULL);
+       g_return_if_fail (iface->table_set_row_count != NULL);
+
+       iface->table_set_row_count (editor, value);
+}
+
+guint
+e_content_editor_table_get_row_count (EContentEditor *editor)
+{
+       EContentEditorInterface *iface;
+
+       g_return_val_if_fail (E_IS_CONTENT_EDITOR (editor), 0);
+
+       iface = E_CONTENT_EDITOR_GET_IFACE (editor);
+       g_return_val_if_fail (iface != NULL, 0);
+       g_return_val_if_fail (iface->table_get_row_count != NULL, 0);
+
+       return iface->table_get_row_count (editor);
+}
+
+void
+e_content_editor_table_set_column_count (EContentEditor *editor,
+                                         guint value)
+{
+       EContentEditorInterface *iface;
+
+       g_return_if_fail (E_IS_CONTENT_EDITOR (editor));
+
+       iface = E_CONTENT_EDITOR_GET_IFACE (editor);
+       g_return_if_fail (iface != NULL);
+       g_return_if_fail (iface->table_set_column_count != NULL);
+
+       iface->table_set_column_count (editor, value);
+}
+
+guint
+e_content_editor_table_get_column_count (EContentEditor *editor)
+{
+       EContentEditorInterface *iface;
+
+       g_return_val_if_fail (E_IS_CONTENT_EDITOR (editor), 0);
+
+       iface = E_CONTENT_EDITOR_GET_IFACE (editor);
+       g_return_val_if_fail (iface != NULL, 0);
+       g_return_val_if_fail (iface->table_get_column_count != NULL, 0);
+
+       return iface->table_get_column_count (editor);
+}
+
+void
+e_content_editor_table_set_width (EContentEditor *editor,
+                                  gint value,
+                                  EContentEditorUnit unit)
+{
+       EContentEditorInterface *iface;
+
+       g_return_if_fail (E_IS_CONTENT_EDITOR (editor));
+
+       iface = E_CONTENT_EDITOR_GET_IFACE (editor);
+       g_return_if_fail (iface != NULL);
+       g_return_if_fail (iface->table_set_width != NULL);
+
+       iface->table_set_width (editor, value, unit);
+}
+
+guint
+e_content_editor_table_get_width (EContentEditor *editor,
+                                  EContentEditorUnit *unit)
+{
+       EContentEditorInterface *iface;
+
+       g_return_val_if_fail (E_IS_CONTENT_EDITOR (editor), 0);
+
+       iface = E_CONTENT_EDITOR_GET_IFACE (editor);
+       g_return_val_if_fail (iface != NULL, 0);
+       g_return_val_if_fail (iface->table_get_width != NULL, 0);
+
+       return iface->table_get_width (editor, unit);
+}
+
+void
+e_content_editor_table_set_align (EContentEditor *editor,
+                                  const gchar *value)
+{
+       EContentEditorInterface *iface;
+
+       g_return_if_fail (E_IS_CONTENT_EDITOR (editor));
+
+       iface = E_CONTENT_EDITOR_GET_IFACE (editor);
+       g_return_if_fail (iface != NULL);
+       g_return_if_fail (iface->table_set_align != NULL);
+
+       iface->table_set_align (editor, value);
+}
+
+gchar *
+e_content_editor_table_get_align (EContentEditor *editor)
+{
+       EContentEditorInterface *iface;
+
+       g_return_val_if_fail (E_IS_CONTENT_EDITOR (editor), NULL);
+
+       iface = E_CONTENT_EDITOR_GET_IFACE (editor);
+       g_return_val_if_fail (iface != NULL, NULL);
+       g_return_val_if_fail (iface->table_get_align != NULL, NULL);
+
+       return iface->table_get_align (editor);
+}
+
+void
+e_content_editor_table_set_padding (EContentEditor *editor,
+                                    gint value)
+{
+       EContentEditorInterface *iface;
+
+       g_return_if_fail (E_IS_CONTENT_EDITOR (editor));
+
+       iface = E_CONTENT_EDITOR_GET_IFACE (editor);
+       g_return_if_fail (iface != NULL);
+       g_return_if_fail (iface->table_set_padding != NULL);
+
+       iface->table_set_padding (editor, value);
+}
+
+gint
+e_content_editor_table_get_padding (EContentEditor *editor)
+{
+       EContentEditorInterface *iface;
+
+       g_return_val_if_fail (E_IS_CONTENT_EDITOR (editor), 0);
+
+       iface = E_CONTENT_EDITOR_GET_IFACE (editor);
+       g_return_val_if_fail (iface != NULL, 0);
+       g_return_val_if_fail (iface->table_get_padding != NULL, 0);
+
+       return iface->table_get_padding (editor);
+}
+
+void
+e_content_editor_table_set_spacing (EContentEditor *editor,
+                                    gint value)
+{
+       EContentEditorInterface *iface;
+
+       g_return_if_fail (E_IS_CONTENT_EDITOR (editor));
+
+       iface = E_CONTENT_EDITOR_GET_IFACE (editor);
+       g_return_if_fail (iface != NULL);
+       g_return_if_fail (iface->table_set_spacing != NULL);
+
+       iface->table_set_spacing (editor, value);
+}
+
+gint
+e_content_editor_table_get_spacing (EContentEditor *editor)
+{
+       EContentEditorInterface *iface;
+
+       g_return_val_if_fail (E_IS_CONTENT_EDITOR (editor), 0);
+
+       iface = E_CONTENT_EDITOR_GET_IFACE (editor);
+       g_return_val_if_fail (iface != NULL, 0);
+       g_return_val_if_fail (iface->table_get_spacing != NULL, 0);
+
+       return iface->table_get_spacing (editor);
+}
+
+void
+e_content_editor_table_set_border (EContentEditor *editor,
+                                   gint value)
+{
+       EContentEditorInterface *iface;
+
+       g_return_if_fail (E_IS_CONTENT_EDITOR (editor));
+
+       iface = E_CONTENT_EDITOR_GET_IFACE (editor);
+       g_return_if_fail (iface != NULL);
+       g_return_if_fail (iface->table_set_border != NULL);
+
+       iface->table_set_border (editor, value);
+}
+
+gint
+e_content_editor_table_get_border (EContentEditor *editor)
+{
+       EContentEditorInterface *iface;
+
+       g_return_val_if_fail (E_IS_CONTENT_EDITOR (editor), 0);
+
+       iface = E_CONTENT_EDITOR_GET_IFACE (editor);
+       g_return_val_if_fail (iface != NULL, 0);
+       g_return_val_if_fail (iface->table_get_border != NULL, 0);
+
+       return iface->table_get_border (editor);
+}
+
+gchar *
+e_content_editor_table_get_background_image_uri (EContentEditor *editor)
+{
+       EContentEditorInterface *iface;
+
+       g_return_val_if_fail (E_IS_CONTENT_EDITOR (editor), NULL);
+
+       iface = E_CONTENT_EDITOR_GET_IFACE (editor);
+       g_return_val_if_fail (iface != NULL, NULL);
+       g_return_val_if_fail (iface->table_get_background_image_uri != NULL, NULL);
+
+       return iface->table_get_background_image_uri (editor);
+}
+
+void
+e_content_editor_table_set_background_image_uri (EContentEditor *editor,
+                                                 const gchar *uri)
+{
+       EContentEditorInterface *iface;
+
+       g_return_if_fail (E_IS_CONTENT_EDITOR (editor));
+
+       iface = E_CONTENT_EDITOR_GET_IFACE (editor);
+       g_return_if_fail (iface != NULL);
+       g_return_if_fail (iface->table_set_background_image_uri != NULL);
+
+       iface->table_set_background_image_uri (editor, uri);
+}
+
+void
+e_content_editor_table_get_background_color (EContentEditor *editor,
+                                             GdkRGBA *value)
+{
+       EContentEditorInterface *iface;
+
+       g_return_if_fail (E_IS_CONTENT_EDITOR (editor));
+
+       iface = E_CONTENT_EDITOR_GET_IFACE (editor);
+       g_return_if_fail (iface != NULL);
+       g_return_if_fail (iface->table_get_background_color != NULL);
+
+       iface->table_get_background_color (editor, value);
+}
+
+void
+e_content_editor_table_set_background_color (EContentEditor *editor,
+                                             const GdkRGBA *value)
+{
+       EContentEditorInterface *iface;
+
+       g_return_if_fail (E_IS_CONTENT_EDITOR (editor));
+
+       iface = E_CONTENT_EDITOR_GET_IFACE (editor);
+       g_return_if_fail (iface != NULL);
+       g_return_if_fail (iface->table_set_background_color != NULL);
+
+       iface->table_set_background_color (editor, value);
+}
+
+gboolean
+e_content_editor_on_table_dialog_open (EContentEditor *editor)
+{
+       EContentEditorInterface *iface;
+
+       g_return_val_if_fail (E_IS_CONTENT_EDITOR (editor), FALSE);
+
+       iface = E_CONTENT_EDITOR_GET_IFACE (editor);
+       g_return_val_if_fail (iface != NULL, FALSE);
+       g_return_val_if_fail (iface->on_table_dialog_open != NULL, FALSE);
+
+       return iface->on_table_dialog_open (editor);
+}
+
+void
+e_content_editor_on_table_dialog_close (EContentEditor *editor)
+{
+       EContentEditorInterface *iface;
+
+       g_return_if_fail (E_IS_CONTENT_EDITOR (editor));
+
+       iface = E_CONTENT_EDITOR_GET_IFACE (editor);
+       g_return_if_fail (iface != NULL);
+       g_return_if_fail (iface->on_table_dialog_close != NULL);
+
+       iface->on_table_dialog_close (editor);
+}
+
+void
+e_content_editor_on_spell_check_dialog_open (EContentEditor *editor)
+{
+       EContentEditorInterface *iface;
+
+       g_return_if_fail (E_IS_CONTENT_EDITOR (editor));
+
+       iface = E_CONTENT_EDITOR_GET_IFACE (editor);
+       g_return_if_fail (iface != NULL);
+       g_return_if_fail (iface->on_spell_check_dialog_close != NULL);
+
+       iface->on_spell_check_dialog_close (editor);
+}
+
+void
+e_content_editor_on_spell_check_dialog_close (EContentEditor *editor)
+{
+       EContentEditorInterface *iface;
+
+       g_return_if_fail (E_IS_CONTENT_EDITOR (editor));
+
+       iface = E_CONTENT_EDITOR_GET_IFACE (editor);
+       g_return_if_fail (iface != NULL);
+       g_return_if_fail (iface->on_spell_check_dialog_close != NULL);
+
+       iface->on_spell_check_dialog_close (editor);
+}
+
+gchar *
+e_content_editor_spell_check_next_word (EContentEditor *editor,
+                                        const gchar *word)
+{
+       EContentEditorInterface *iface;
+
+       g_return_val_if_fail (E_IS_CONTENT_EDITOR (editor), NULL);
+
+       iface = E_CONTENT_EDITOR_GET_IFACE (editor);
+       g_return_val_if_fail (iface != NULL, NULL);
+       g_return_val_if_fail (iface->spell_check_next_word != NULL, NULL);
+
+       return iface->spell_check_next_word (editor, word);
+}
+
+gchar *
+e_content_editor_spell_check_prev_word (EContentEditor *editor,
+                                        const gchar *word)
+{
+       EContentEditorInterface *iface;
+
+       g_return_val_if_fail (E_IS_CONTENT_EDITOR (editor), NULL);
+
+       iface = E_CONTENT_EDITOR_GET_IFACE (editor);
+       g_return_val_if_fail (iface != NULL, NULL);
+       g_return_val_if_fail (iface->spell_check_prev_word != NULL, NULL);
+
+       return iface->spell_check_prev_word (editor, word);
+}
+
+void
+e_content_editor_on_replace_dialog_open (EContentEditor *editor)
+{
+       EContentEditorInterface *iface;
+
+       g_return_if_fail (E_IS_CONTENT_EDITOR (editor));
+
+       iface = E_CONTENT_EDITOR_GET_IFACE (editor);
+       g_return_if_fail (iface != NULL);
+       g_return_if_fail (iface->on_replace_dialog_open != NULL);
+
+       iface->on_replace_dialog_open (editor);
+}
+
+void
+e_content_editor_on_replace_dialog_close (EContentEditor *editor)
+{
+       EContentEditorInterface *iface;
+
+       g_return_if_fail (E_IS_CONTENT_EDITOR (editor));
+
+       iface = E_CONTENT_EDITOR_GET_IFACE (editor);
+       g_return_if_fail (iface != NULL);
+       g_return_if_fail (iface->on_replace_dialog_close != NULL);
+
+       iface->on_replace_dialog_close (editor);
+}
+
+void
+e_content_editor_on_find_dialog_open (EContentEditor *editor)
+{
+       EContentEditorInterface *iface;
+
+       g_return_if_fail (E_IS_CONTENT_EDITOR (editor));
+
+       iface = E_CONTENT_EDITOR_GET_IFACE (editor);
+       g_return_if_fail (iface != NULL);
+       g_return_if_fail (iface->on_find_dialog_open != NULL);
+
+       iface->on_find_dialog_open (editor);
+}
+
+void
+e_content_editor_on_find_dialog_close (EContentEditor *editor)
+{
+       EContentEditorInterface *iface;
+
+       g_return_if_fail (E_IS_CONTENT_EDITOR (editor));
+
+       iface = E_CONTENT_EDITOR_GET_IFACE (editor);
+       g_return_if_fail (iface != NULL);
+       g_return_if_fail (iface->on_find_dialog_close != NULL);
+
+       iface->on_find_dialog_close (editor);
+}
+
+void
+e_content_editor_emit_load_finished (EContentEditor *editor)
+{
+       g_return_if_fail (E_IS_CONTENT_EDITOR (editor));
+
+       g_signal_emit (editor, signals[LOAD_FINISHED], 0);
+}
+
+gboolean
+e_content_editor_emit_paste_clipboard (EContentEditor *editor)
+{
+       gboolean handled = FALSE;
+
+       g_return_val_if_fail (E_IS_CONTENT_EDITOR (editor), FALSE);
+
+       g_signal_emit (editor, signals[PASTE_CLIPBOARD], 0, &handled);
+
+       return handled;
+}
+
+gboolean
+e_content_editor_emit_paste_primary_clipboard (EContentEditor *editor)
+{
+       gboolean handled = FALSE;
+
+       g_return_val_if_fail (E_IS_CONTENT_EDITOR (editor), FALSE);
+
+       g_signal_emit (editor, signals[PASTE_PRIMARY_CLIPBOARD], 0, &handled);
+
+       return handled;
+}
+
+gboolean
+e_content_editor_emit_context_menu_requested (EContentEditor *editor,
+                                             EContentEditorNodeFlags flags,
+                                             GdkEvent *event)
+{
+       gboolean handled = FALSE;
+
+       g_return_val_if_fail (E_IS_CONTENT_EDITOR (editor), FALSE);
+
+       g_signal_emit (editor, signals[CONTEXT_MENU_REQUESTED], 0, flags, event, &handled);
+
+       return handled;
+}
+
+void
+e_content_editor_emit_find_done (EContentEditor *editor,
+                                guint match_count)
+{
+       g_return_if_fail (E_IS_CONTENT_EDITOR (editor));
+
+       g_signal_emit (editor, signals[FIND_DONE], 0, match_count);
+}
+
+void
+e_content_editor_emit_replace_all_done (EContentEditor *editor,
+                                       guint replaced_count)
+{
+       g_return_if_fail (E_IS_CONTENT_EDITOR (editor));
+
+       g_signal_emit (editor, signals[REPLACE_ALL_DONE], 0, replaced_count);
+}
diff --git a/e-util/e-content-editor.h b/e-util/e-content-editor.h
new file mode 100644
index 0000000..8d3d64e
--- /dev/null
+++ b/e-util/e-content-editor.h
@@ -0,0 +1,1021 @@
+/*
+ * Copyright (C) 2016 Red Hat, Inc. (www.redhat.com)
+ *
+ * This library 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 library 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 Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#if !defined (__E_UTIL_H_INSIDE__) && !defined (LIBEUTIL_COMPILATION)
+#error "Only <e-util/e-util.h> should be included directly."
+#endif
+
+#ifndef E_CONTENT_EDITOR_H
+#define E_CONTENT_EDITOR_H
+
+#include <glib-object.h>
+#include <gtk/gtk.h>
+
+#include <camel/camel.h>
+
+#include <e-util/e-emoticon.h>
+#include <e-util/e-spell-checker.h>
+#include <e-util/e-util-enums.h>
+
+#define DEFAULT_CONTENT_EDITOR_NAME "WebKit"
+
+G_BEGIN_DECLS
+
+struct _EHTMLEditor;
+
+#define E_TYPE_CONTENT_EDITOR e_content_editor_get_type ()
+G_DECLARE_INTERFACE (EContentEditor, e_content_editor, E, CONTENT_EDITOR, GtkWidget)
+
+typedef void (*EContentEditorInitializedCallback)      (EContentEditor *content_editor,
+                                                        gpointer user_data);
+
+struct _EContentEditorInterface {
+       GTypeInterface parent_interface;
+
+       void            (*initialize)                   (EContentEditor *content_editor,
+                                                        EContentEditorInitializedCallback callback,
+                                                        gpointer user_data);
+       void            (*setup_editor)                 (EContentEditor *content_editor,
+                                                        struct _EHTMLEditor *html_editor);
+       void            (*update_styles)                (EContentEditor *editor);
+       void            (*insert_content)               (EContentEditor *editor,
+                                                        const gchar *content,
+                                                        EContentEditorInsertContentFlags flags);
+
+       gchar *         (*get_content)                  (EContentEditor *editor,
+                                                        EContentEditorGetContentFlags flags,
+                                                        const gchar *inline_images_from_domain,
+                                                        GSList **inline_images_parts /* newly created 
CamelMimePart * */);
+
+       void            (*insert_image)                 (EContentEditor *editor,
+                                                        const gchar *uri);
+
+       void            (*insert_image_from_mime_part)
+                                                       (EContentEditor *editor,
+                                                        CamelMimePart *part);
+
+       void            (*insert_emoticon)              (EContentEditor *editor,
+                                                        EEmoticon *emoticon);
+
+       void            (*move_caret_on_coordinates)    (EContentEditor *editor,
+                                                        gint x,
+                                                        gint y,
+                                                        gboolean cancel_if_not_collapsed);
+
+       void            (*cut)                          (EContentEditor *editor);
+
+       void            (*copy)                         (EContentEditor *editor);
+
+       void            (*paste)                        (EContentEditor *editor);
+
+       void            (*paste_primary)                (EContentEditor *editor);
+
+       void            (*undo)                         (EContentEditor *editor);
+
+       void            (*redo)                         (EContentEditor *editor);
+
+       void            (*clear_undo_redo_history)      (EContentEditor *editor);
+
+       void            (*set_spell_checking_languages) (EContentEditor *editor,
+                                                        const gchar **languages);
+
+       gchar *         (*get_selected_text)            (EContentEditor *editor);
+
+       gchar *         (*get_caret_word)               (EContentEditor *editor);
+
+       void            (*replace_caret_word)           (EContentEditor *editor,
+                                                        const gchar *replacement);
+
+       void            (*select_all)                   (EContentEditor *editor);
+
+       void            (*selection_indent)             (EContentEditor *editor);
+
+       void            (*selection_unindent)           (EContentEditor *editor);
+
+       void            (*selection_create_link)        (EContentEditor *editor,
+                                                        const gchar *uri);
+
+       void            (*selection_unlink)             (EContentEditor *editor);
+
+       void            (*find)                         (EContentEditor *editor,
+                                                        guint32 flags,
+                                                        const gchar *text);
+
+       void            (*replace)                      (EContentEditor *editor,
+                                                        const gchar *replacement);
+
+       void            (*replace_all)                  (EContentEditor *editor,
+                                                        guint32 flags,
+                                                        const gchar *find_text,
+                                                        const gchar *replace_with);
+
+       void            (*selection_save)               (EContentEditor *editor);
+
+       void            (*selection_restore)            (EContentEditor *editor);
+
+       void            (*selection_wrap)               (EContentEditor *editor);
+
+       guint           (*get_caret_position)           (EContentEditor *editor);
+
+       guint           (*get_caret_offset)             (EContentEditor *editor);
+
+       gchar *         (*get_current_signature_uid)    (EContentEditor *editor);
+
+       gboolean        (*is_ready)                     (EContentEditor *editor);
+
+       gchar *         (*insert_signature)             (EContentEditor *editor,
+                                                        const gchar *content,
+                                                        gboolean is_html,
+                                                        const gchar *signature_id,
+                                                        gboolean *set_signature_from_message,
+                                                        gboolean *check_if_signature_is_changed,
+                                                        gboolean *ignore_next_signature_change);
+
+       void            (*delete_cell_contents)         (EContentEditor *editor);
+
+       void            (*delete_column)                (EContentEditor *editor);
+
+       void            (*delete_row)                   (EContentEditor *editor);
+
+       void            (*delete_table)                 (EContentEditor *editor);
+
+       void            (*insert_column_after)          (EContentEditor *editor);
+
+       void            (*insert_column_before)         (EContentEditor *editor);
+
+       void            (*insert_row_above)             (EContentEditor *editor);
+
+       void            (*insert_row_below)             (EContentEditor *editor);
+
+       gboolean        (*on_h_rule_dialog_open)        (EContentEditor *editor);
+
+       void            (*on_h_rule_dialog_close)       (EContentEditor *editor);
+
+       void            (*h_rule_set_align)             (EContentEditor *editor,
+                                                        const gchar *value);
+
+       gchar *         (*h_rule_get_align)             (EContentEditor *editor);
+
+       void            (*h_rule_set_size)              (EContentEditor *editor,
+                                                        gint value);
+
+       gint            (*h_rule_get_size)              (EContentEditor *editor);
+
+       void            (*h_rule_set_width)             (EContentEditor *editor,
+                                                        gint value,
+                                                        EContentEditorUnit unit);
+
+       gint            (*h_rule_get_width)             (EContentEditor *editor,
+                                                        EContentEditorUnit *unit);
+
+       void            (*h_rule_set_no_shade)          (EContentEditor *editor,
+                                                        gboolean value);
+
+       gboolean        (*h_rule_get_no_shade)          (EContentEditor *editor);
+
+       void            (*on_image_dialog_open)         (EContentEditor *editor);
+
+       void            (*on_image_dialog_close)        (EContentEditor *editor);
+
+       void            (*image_set_src)                (EContentEditor *editor,
+                                                        const gchar *value);
+
+       gchar *         (*image_get_src)                (EContentEditor *editor);
+
+       void            (*image_set_alt)                (EContentEditor *editor,
+                                                        const gchar *value);
+
+       gchar *         (*image_get_alt)                (EContentEditor *editor);
+
+       gint32          (*image_get_natural_width)      (EContentEditor *editor);
+
+       gint32          (*image_get_width)              (EContentEditor *editor);
+
+       void            (*image_set_width)              (EContentEditor *editor,
+                                                        gint value);
+
+       void            (*image_set_width_follow)       (EContentEditor *editor,
+                                                        gboolean value);
+
+       gint32          (*image_get_natural_height)     (EContentEditor *editor);
+
+       gint32          (*image_get_height)             (EContentEditor *editor);
+
+       void            (*image_set_height)             (EContentEditor *editor,
+                                                        gint value);
+
+       void            (*image_set_height_follow)      (EContentEditor *editor,
+                                                        gboolean value);
+
+       void            (*image_set_url)                (EContentEditor *editor,
+                                                        const gchar *value);
+
+       gchar *         (*image_get_url)                (EContentEditor *editor);
+
+       void            (*image_set_vspace)             (EContentEditor *editor,
+                                                        gint value);
+
+       gint            (*image_get_vspace)             (EContentEditor *editor);
+
+       void            (*image_set_hspace)             (EContentEditor *editor,
+                                                        gint value);
+
+       gint            (*image_get_hspace)             (EContentEditor *editor);
+
+       void            (*image_set_border)             (EContentEditor *editor,
+                                                        gint border);
+
+       gint            (*image_get_border)             (EContentEditor *editor);
+
+       void            (*image_set_align)              (EContentEditor *editor,
+                                                        const gchar *value);
+
+       gchar *         (*image_get_align)              (EContentEditor *editor);
+
+       void            (*on_link_dialog_open)          (EContentEditor *editor);
+
+       void            (*on_link_dialog_close)         (EContentEditor *editor);
+
+       void            (*link_get_values)              (EContentEditor *editor,
+                                                        gchar **href,
+                                                        gchar **text);
+
+       void            (*link_set_values)              (EContentEditor *editor,
+                                                        const gchar *href,
+                                                        const gchar *text);
+
+       void            (*on_page_dialog_open)          (EContentEditor *editor);
+
+       void            (*on_page_dialog_close)         (EContentEditor *editor);
+
+       void            (*page_set_text_color)          (EContentEditor *editor,
+                                                        const GdkRGBA *value);
+
+       void            (*page_get_text_color)          (EContentEditor *editor,
+                                                        GdkRGBA *value);
+
+       void            (*page_set_background_color)    (EContentEditor *editor,
+                                                        const GdkRGBA *value);
+
+       void            (*page_get_background_color)    (EContentEditor *editor,
+                                                        GdkRGBA *value);
+
+       void            (*page_set_link_color)          (EContentEditor *editor,
+                                                        const GdkRGBA *value);
+
+       void            (*page_get_link_color)          (EContentEditor *editor,
+                                                        GdkRGBA *value);
+
+       void            (*page_set_visited_link_color)  (EContentEditor *editor,
+                                                        const GdkRGBA *value);
+
+       void            (*page_get_visited_link_color)  (EContentEditor *editor,
+                                                        GdkRGBA *value);
+
+       void            (*page_set_background_image_uri)
+                                                       (EContentEditor *editor,
+                                                        const gchar *uri);
+
+       gchar *         (*page_get_background_image_uri)
+                                                       (EContentEditor *editor);
+
+       void            (*on_cell_dialog_open)          (EContentEditor *editor);
+
+       void            (*on_cell_dialog_close)         (EContentEditor *editor);
+
+       void            (*cell_set_v_align)             (EContentEditor *editor,
+                                                        const gchar *value,
+                                                        EContentEditorScope scope);
+
+       gchar *         (*cell_get_v_align)             (EContentEditor *editor);
+
+       void            (*cell_set_align)               (EContentEditor *editor,
+                                                        const gchar *value,
+                                                        EContentEditorScope scope);
+
+       gchar *         (*cell_get_align)               (EContentEditor *editor);
+
+       void            (*cell_set_wrap)                (EContentEditor *editor,
+                                                        gboolean value,
+                                                        EContentEditorScope scope);
+
+       gboolean        (*cell_get_wrap)                (EContentEditor *editor);
+
+       void            (*cell_set_header_style)        (EContentEditor *editor,
+                                                        gboolean value,
+                                                        EContentEditorScope scope);
+
+       gboolean        (*cell_is_header)               (EContentEditor *editor);
+
+       void            (*cell_set_width)               (EContentEditor *editor,
+                                                        gint value,
+                                                        EContentEditorUnit unit,
+                                                        EContentEditorScope scope);
+
+       gint            (*cell_get_width)               (EContentEditor *editor,
+                                                        EContentEditorUnit *unit);
+
+       void            (*cell_set_row_span)            (EContentEditor *editor,
+                                                        gint value,
+                                                        EContentEditorScope scope);
+
+       gint            (*cell_get_row_span)            (EContentEditor *editor);
+
+       void            (*cell_set_col_span)            (EContentEditor *editor,
+                                                        gint value,
+                                                        EContentEditorScope scope);
+
+       gint            (*cell_get_col_span)            (EContentEditor *editor);
+
+       gchar *         (*cell_get_background_image_uri)
+                                                       (EContentEditor *editor);
+
+       void            (*cell_set_background_image_uri)
+                                                       (EContentEditor *editor,
+                                                        const gchar *uri);
+
+       void            (*cell_get_background_color)    (EContentEditor *editor,
+                                                        GdkRGBA *value);
+
+       void            (*cell_set_background_color)    (EContentEditor *editor,
+                                                        const GdkRGBA *value,
+                                                        EContentEditorScope scope);
+
+       void            (*table_set_row_count)          (EContentEditor *editor,
+                                                        guint value);
+
+       guint           (*table_get_row_count)          (EContentEditor *editor);
+
+       void            (*table_set_column_count)       (EContentEditor *editor,
+                                                        guint value);
+
+       guint           (*table_get_column_count)       (EContentEditor *editor);
+
+       void            (*table_set_width)              (EContentEditor *editor,
+                                                        gint value,
+                                                        EContentEditorUnit unit);
+
+       guint           (*table_get_width)              (EContentEditor *editor,
+                                                        EContentEditorUnit *unit);
+
+       void            (*table_set_align)              (EContentEditor *editor,
+                                                        const gchar *value);
+
+       gchar *         (*table_get_align)              (EContentEditor *editor);
+
+       void            (*table_set_padding)            (EContentEditor *editor,
+                                                        gint value);
+
+       gint            (*table_get_padding)            (EContentEditor *editor);
+
+       void            (*table_set_spacing)            (EContentEditor *editor,
+                                                        gint value);
+
+       gint            (*table_get_spacing)            (EContentEditor *editor);
+
+       void            (*table_set_border)             (EContentEditor *editor,
+                                                        gint value);
+
+       gint            (*table_get_border)             (EContentEditor *editor);
+
+       gchar *         (*table_get_background_image_uri)
+                                                       (EContentEditor *editor);
+
+       void            (*table_set_background_image_uri)
+                                                       (EContentEditor *editor,
+                                                        const gchar *uri);
+
+       void            (*table_get_background_color)   (EContentEditor *editor,
+                                                        GdkRGBA *value);
+
+       void            (*table_set_background_color)   (EContentEditor *editor,
+                                                        const GdkRGBA *value);
+
+       gboolean        (*on_table_dialog_open)         (EContentEditor *editor);
+
+       void            (*on_table_dialog_close)        (EContentEditor *editor);
+
+       void            (*on_spell_check_dialog_open)   (EContentEditor *editor);
+
+       void            (*on_spell_check_dialog_close)  (EContentEditor *editor);
+
+       gchar *         (*spell_check_next_word)        (EContentEditor *editor,
+                                                        const gchar *word);
+
+       gchar *         (*spell_check_prev_word)        (EContentEditor *editor,
+                                                        const gchar *word);
+
+       void            (*on_replace_dialog_open)       (EContentEditor *editor);
+
+       void            (*on_replace_dialog_close)      (EContentEditor *editor);
+
+       void            (*on_find_dialog_open)          (EContentEditor *editor);
+
+       void            (*on_find_dialog_close)         (EContentEditor *editor);
+
+       /* Signals */
+       void            (*load_finished)                (EContentEditor *editor);
+       gboolean        (*paste_clipboard)              (EContentEditor *editor);
+       gboolean        (*paste_primary_clipboard)      (EContentEditor *editor);
+       gboolean        (*context_menu_requested)       (EContentEditor *editor,
+                                                        EContentEditorNodeFlags flags,
+                                                        GdkEvent *event);
+       void            (*find_done)                    (EContentEditor *editor,
+                                                        guint match_count);
+       void            (*replace_all_done)             (EContentEditor *editor,
+                                                        guint replaced_count);
+};
+
+/* Properties */
+
+ESpellChecker *        e_content_editor_ref_spell_checker
+                                               (EContentEditor *editor);
+gboolean       e_content_editor_can_cut        (EContentEditor *editor);
+gboolean       e_content_editor_can_copy       (EContentEditor *editor);
+gboolean       e_content_editor_can_paste      (EContentEditor *editor);
+gboolean       e_content_editor_can_undo       (EContentEditor *editor);
+gboolean       e_content_editor_can_redo       (EContentEditor *editor);
+gboolean       e_content_editor_is_indented    (EContentEditor *editor);
+gboolean       e_content_editor_get_spell_check_enabled
+                                               (EContentEditor *editor);
+void           e_content_editor_set_spell_check_enabled
+                                               (EContentEditor *editor,
+                                                gboolean enable);
+gboolean       e_content_editor_is_editable    (EContentEditor *editor);
+void           e_content_editor_set_editable   (EContentEditor *editor,
+                                                gboolean editable);
+gboolean       e_content_editor_get_changed    (EContentEditor *editor);
+void           e_content_editor_set_changed    (EContentEditor *editor,
+                                                gboolean changed);
+gboolean       e_content_editor_get_html_mode  (EContentEditor *editor);
+void           e_content_editor_set_html_mode  (EContentEditor *editor,
+                                                gboolean html_mode);
+void           e_content_editor_set_alignment  (EContentEditor *editor,
+                                                EContentEditorAlignment value);
+EContentEditorAlignment
+               e_content_editor_get_alignment  (EContentEditor *editor);
+void           e_content_editor_set_background_color
+                                               (EContentEditor *editor,
+                                                const GdkRGBA *value);
+GdkRGBA *      e_content_editor_dup_background_color
+                                               (EContentEditor *editor);
+void           e_content_editor_set_font_color (EContentEditor *editor,
+                                                const GdkRGBA *value);
+GdkRGBA *      e_content_editor_dup_font_color (EContentEditor *editor);
+void           e_content_editor_set_font_name  (EContentEditor *editor,
+                                                const gchar *value);
+gchar *                e_content_editor_dup_font_name  (EContentEditor *editor);
+void           e_content_editor_set_font_size  (EContentEditor *editor,
+                                                gint value);
+gint           e_content_editor_get_font_size  (EContentEditor *editor);
+void           e_content_editor_set_block_format
+                                               (EContentEditor *editor,
+                                                EContentEditorBlockFormat value);
+EContentEditorBlockFormat
+               e_content_editor_get_block_format
+                                               (EContentEditor *editor);
+void           e_content_editor_set_bold       (EContentEditor *editor,
+                                                gboolean bold);
+gboolean       e_content_editor_is_bold        (EContentEditor *editor);
+void           e_content_editor_set_italic     (EContentEditor *editor,
+                                                gboolean italic);
+gboolean       e_content_editor_is_italic      (EContentEditor *editor);
+void           e_content_editor_set_monospaced (EContentEditor *editor,
+                                                gboolean monospaced);
+gboolean       e_content_editor_is_monospaced (EContentEditor *editor);
+void           e_content_editor_set_strikethrough
+                                               (EContentEditor *editor,
+                                                gboolean strikethrough);
+gboolean       e_content_editor_is_strikethrough
+                                               (EContentEditor *editor);
+void           e_content_editor_set_subscript  (EContentEditor *editor,
+                                                gboolean subscript);
+gboolean       e_content_editor_is_subscript   (EContentEditor *editor);
+void           e_content_editor_set_superscript
+                                               (EContentEditor *editor,
+                                                gboolean superscript);
+gboolean       e_content_editor_is_superscript
+                                               (EContentEditor *editor);
+void           e_content_editor_set_underline  (EContentEditor *editor,
+                                                gboolean underline);
+gboolean       e_content_editor_is_underline   (EContentEditor *editor);
+
+/* Methods */
+void           e_content_editor_initialize     (EContentEditor *content_editor,
+                                                EContentEditorInitializedCallback callback,
+                                                gpointer user_data);
+void           e_content_editor_setup_editor   (EContentEditor *content_editor,
+                                                struct _EHTMLEditor *html_editor);
+void           e_content_editor_update_styles  (EContentEditor *editor);
+void           e_content_editor_insert_content (EContentEditor *editor,
+                                                const gchar *content,
+                                                EContentEditorInsertContentFlags flags);
+
+gchar *                e_content_editor_get_content    (EContentEditor *editor,
+                                                EContentEditorGetContentFlags flags,
+                                                const gchar *inline_images_from_domain,
+                                                GSList **inline_images_parts /* newly created CamelMimePart 
* */);
+
+void            e_content_editor_insert_image_from_mime_part
+                                               (EContentEditor *editor,
+                                                CamelMimePart *part);
+
+void           e_content_editor_insert_image   (EContentEditor *editor,
+                                                const gchar *uri);
+
+void           e_content_editor_insert_emoticon
+                                               (EContentEditor *editor,
+                                                EEmoticon *emoticon);
+
+void           e_content_editor_move_caret_on_coordinates
+                                               (EContentEditor *editor,
+                                                gint x,
+                                                gint y,
+                                                gboolean cancel_if_not_collapsed);
+
+void           e_content_editor_cut            (EContentEditor *editor);
+
+void           e_content_editor_copy           (EContentEditor *editor);
+
+void           e_content_editor_paste          (EContentEditor *editor);
+
+void           e_content_editor_paste_primary  (EContentEditor *editor);
+
+void           e_content_editor_undo           (EContentEditor *editor);
+
+void           e_content_editor_redo           (EContentEditor *editor);
+
+void           e_content_editor_clear_undo_redo_history
+                                               (EContentEditor *editor);
+
+void           e_content_editor_set_spell_checking_languages
+                                               (EContentEditor *editor,
+                                                const gchar **languages);
+
+void           e_content_editor_select_all     (EContentEditor *editor);
+
+gchar *                e_content_editor_get_selected_text
+                                               (EContentEditor *editor);
+
+gchar *                e_content_editor_get_caret_word (EContentEditor *editor);
+
+void           e_content_editor_replace_caret_word
+                                               (EContentEditor *editor,
+                                                const gchar *replacement);
+
+void           e_content_editor_selection_indent
+                                               (EContentEditor *editor);
+
+void           e_content_editor_selection_unindent
+                                               (EContentEditor *editor);
+
+void           e_content_editor_selection_create_link
+                                               (EContentEditor *editor,
+                                                const gchar *uri);
+
+void           e_content_editor_selection_unlink
+                                               (EContentEditor *editor);
+
+void           e_content_editor_find           (EContentEditor *editor,
+                                                guint32 flags,
+                                                const gchar *text);
+
+void           e_content_editor_replace        (EContentEditor *editor,
+                                                const gchar *replacement);
+
+void           e_content_editor_replace_all    (EContentEditor *editor,
+                                                guint32 flags,
+                                                const gchar *find_text,
+                                                const gchar *replace_with);
+
+void           e_content_editor_selection_save (EContentEditor *editor);
+
+void           e_content_editor_selection_restore
+                                               (EContentEditor *editor);
+
+void           e_content_editor_selection_wrap (EContentEditor *editor);
+
+guint          e_content_editor_get_caret_position
+                                               (EContentEditor *editor);
+
+guint          e_content_editor_get_caret_offset
+                                               (EContentEditor *editor);
+
+gchar *                e_content_editor_get_current_signature_uid
+                                               (EContentEditor *editor);
+
+gboolean       e_content_editor_is_ready       (EContentEditor *editor);
+
+gchar *                e_content_editor_insert_signature
+                                               (EContentEditor *editor,
+                                                const gchar *content,
+                                                gboolean is_html,
+                                                const gchar *signature_id,
+                                                gboolean *set_signature_from_message,
+                                                gboolean *check_if_signature_is_changed,
+                                                gboolean *ignore_next_signature_change);
+
+void           e_content_editor_delete_cell_contents
+                                               (EContentEditor *editor);
+void
+               e_content_editor_delete_column  (EContentEditor *editor);
+
+void           e_content_editor_delete_row     (EContentEditor *editor);
+
+void           e_content_editor_delete_table   (EContentEditor *editor);
+
+void           e_content_editor_insert_column_after
+                                               (EContentEditor *editor);
+
+void           e_content_editor_insert_column_before
+                                               (EContentEditor *editor);
+
+void           e_content_editor_insert_row_above
+                                               (EContentEditor *editor);
+
+void           e_content_editor_insert_row_below
+                                               (EContentEditor *editor);
+
+gboolean       e_content_editor_on_h_rule_dialog_open
+                                               (EContentEditor *editor);
+
+void           e_content_editor_on_h_rule_dialog_close
+                                               (EContentEditor *editor);
+
+void           e_content_editor_h_rule_set_align
+                                               (EContentEditor *editor,
+                                                const gchar *value);
+
+gchar *                e_content_editor_h_rule_get_align
+                                               (EContentEditor *editor);
+
+void           e_content_editor_h_rule_set_size
+                                               (EContentEditor *editor,
+                                                gint value);
+
+gint           e_content_editor_h_rule_get_size
+                                               (EContentEditor *editor);
+
+void           e_content_editor_h_rule_set_width
+                                               (EContentEditor *editor,
+                                                gint value,
+                                                EContentEditorUnit unit);
+
+gint           e_content_editor_h_rule_get_width
+                                               (EContentEditor *editor,
+                                                EContentEditorUnit *unit);
+
+void           e_content_editor_h_rule_set_no_shade
+                                               (EContentEditor *editor,
+                                                gboolean value);
+
+gboolean       e_content_editor_h_rule_get_no_shade
+                                               (EContentEditor *editor);
+
+void           e_content_editor_on_image_dialog_open
+                                               (EContentEditor *editor);
+
+void           e_content_editor_on_image_dialog_close
+                                               (EContentEditor *editor);
+
+void           e_content_editor_image_set_src  (EContentEditor *editor,
+                                                const gchar *value);
+
+gchar *                e_content_editor_image_get_src  (EContentEditor *editor);
+
+void           e_content_editor_image_set_alt  (EContentEditor *editor,
+                                                const gchar *value);
+
+gchar *                e_content_editor_image_get_alt  (EContentEditor *editor);
+
+void           e_content_editor_image_set_url  (EContentEditor *editor,
+                                                const gchar *value);
+
+gchar *                e_content_editor_image_get_url  (EContentEditor *editor);
+
+void           e_content_editor_image_set_vspace
+                                               (EContentEditor *editor,
+                                                gint value);
+
+gint           e_content_editor_image_get_vspace
+                                               (EContentEditor *editor);
+
+void           e_content_editor_image_set_hspace
+                                               (EContentEditor *editor,
+                                                gint value);
+
+gint           e_content_editor_image_get_hspace
+                                               (EContentEditor *editor);
+
+void           e_content_editor_image_set_border
+                                               (EContentEditor *editor,
+                                                gint value);
+
+gint           e_content_editor_image_get_border
+                                               (EContentEditor *editor);
+
+void           e_content_editor_image_set_align
+                                               (EContentEditor *editor,
+                                                const gchar *value);
+
+gchar *                e_content_editor_image_get_align
+                                               (EContentEditor *editor);
+
+void           e_content_editor_image_set_width
+                                               (EContentEditor *editor,
+                                                gint value);
+
+gint32         e_content_editor_image_get_width
+                                               (EContentEditor *editor);
+
+gint32         e_content_editor_image_get_natural_width
+                                               (EContentEditor *editor);
+
+void           e_content_editor_image_set_width_follow
+                                               (EContentEditor *editor,
+                                                gboolean value);
+void           e_content_editor_image_set_height
+                                               (EContentEditor *editor,
+                                                gint value);
+
+gint32         e_content_editor_image_get_height
+                                               (EContentEditor *editor);
+
+gint32         e_content_editor_image_get_natural_height
+                                               (EContentEditor *editor);
+
+void           e_content_editor_image_set_height_follow
+                                               (EContentEditor *editor,
+                                                gboolean value);
+
+void           e_content_editor_on_link_dialog_open
+                                               (EContentEditor *editor);
+
+void           e_content_editor_on_link_dialog_close
+                                               (EContentEditor *editor);
+
+void           e_content_editor_link_get_values
+                                               (EContentEditor *editor,
+                                                gchar **href,
+                                                gchar **text);
+
+void           e_content_editor_link_set_values
+                                               (EContentEditor *editor,
+                                                const gchar *href,
+                                                const gchar *text);
+
+void           e_content_editor_page_set_text_color
+                                               (EContentEditor *editor,
+                                                const GdkRGBA *value);
+
+void           e_content_editor_on_page_dialog_open
+                                               (EContentEditor *editor);
+
+void           e_content_editor_on_page_dialog_close
+                                               (EContentEditor *editor);
+
+void           e_content_editor_page_get_text_color
+                                               (EContentEditor *editor,
+                                                GdkRGBA *value);
+
+void           e_content_editor_page_set_background_color
+                                               (EContentEditor *editor,
+                                                const GdkRGBA *value);
+
+void           e_content_editor_page_get_background_color
+                                               (EContentEditor *editor,
+                                                GdkRGBA *value);
+
+void           e_content_editor_page_set_link_color
+                                               (EContentEditor *editor,
+                                                const GdkRGBA *value);
+
+void           e_content_editor_page_get_link_color
+                                               (EContentEditor *editor,
+                                                GdkRGBA *value);
+
+void           e_content_editor_page_set_visited_link_color
+                                               (EContentEditor *editor,
+                                                const GdkRGBA *value);
+
+void           e_content_editor_page_get_visited_link_color
+                                               (EContentEditor *editor,
+                                                GdkRGBA *value);
+
+void           e_content_editor_page_set_background_image_uri
+                                               (EContentEditor *editor,
+                                                const gchar *uri);
+
+gchar *                e_content_editor_page_get_background_image_uri
+                                               (EContentEditor *editor);
+
+void           e_content_editor_on_cell_dialog_open
+                                               (EContentEditor *editor);
+
+void           e_content_editor_on_cell_dialog_close
+                                               (EContentEditor *editor);
+
+void           e_content_editor_cell_set_v_align
+                                               (EContentEditor *editor,
+                                                const gchar *value,
+                                                EContentEditorScope scope);
+
+gchar *                e_content_editor_cell_get_v_align
+                                               (EContentEditor *editor);
+
+void           e_content_editor_cell_set_align (EContentEditor *editor,
+                                                const gchar *value,
+                                                EContentEditorScope scope);
+
+gchar *                e_content_editor_cell_get_align (EContentEditor *editor);
+
+void           e_content_editor_cell_set_wrap  (EContentEditor *editor,
+                                                gboolean value,
+                                                EContentEditorScope scope);
+
+gboolean       e_content_editor_cell_get_wrap  (EContentEditor *editor);
+
+void           e_content_editor_cell_set_header_style
+                                               (EContentEditor *editor,
+                                                gboolean value,
+                                                EContentEditorScope scope);
+
+gboolean       e_content_editor_cell_is_header (EContentEditor *editor);
+
+void           e_content_editor_cell_set_width (EContentEditor *editor,
+                                                gint value,
+                                                EContentEditorUnit unit,
+                                                EContentEditorScope scope);
+
+gint           e_content_editor_cell_get_width (EContentEditor *editor,
+                                                EContentEditorUnit *unit);
+
+void           e_content_editor_cell_set_row_span
+                                               (EContentEditor *editor,
+                                                gint value,
+                                                EContentEditorScope scope);
+
+gint           e_content_editor_cell_get_row_span
+                                               (EContentEditor *editor);
+
+void           e_content_editor_cell_set_col_span
+                                               (EContentEditor *editor,
+                                                gint value,
+                                                EContentEditorScope scope);
+
+gint           e_content_editor_cell_get_col_span
+                                               (EContentEditor *editor);
+
+void           e_content_editor_cell_set_background_image_uri
+                                               (EContentEditor *editor,
+                                                const gchar *uri);
+
+gchar *                e_content_editor_cell_get_background_image_uri
+                                               (EContentEditor *editor);
+
+void           e_content_editor_cell_set_background_color
+                                               (EContentEditor *editor,
+                                                const GdkRGBA *value,
+                                                EContentEditorScope scope);
+
+void           e_content_editor_cell_get_background_color
+                                               (EContentEditor *editor,
+                                                GdkRGBA *value);
+
+void           e_content_editor_table_set_row_count
+                                               (EContentEditor *editor,
+                                                guint value);
+
+guint          e_content_editor_table_get_row_count
+                                               (EContentEditor *editor);
+
+void           e_content_editor_table_set_column_count
+                                               (EContentEditor *editor,
+                                                guint value);
+
+guint          e_content_editor_table_get_column_count
+                                               (EContentEditor *editor);
+
+void           e_content_editor_table_set_width
+                                               (EContentEditor *editor,
+                                                gint value,
+                                                EContentEditorUnit unit);
+
+guint          e_content_editor_table_get_width
+                                               (EContentEditor *editor,
+                                                EContentEditorUnit *unit);
+
+void           e_content_editor_table_set_align
+                                               (EContentEditor *editor,
+                                                const gchar *value);
+
+gchar *                e_content_editor_table_get_align
+                                               (EContentEditor *editor);
+
+void           e_content_editor_table_set_padding
+                                               (EContentEditor *editor,
+                                                gint value);
+
+gint           e_content_editor_table_get_padding
+                                               (EContentEditor *editor);
+
+void           e_content_editor_table_set_spacing
+                                               (EContentEditor *editor,
+                                                gint value);
+
+gint           e_content_editor_table_get_spacing
+                                               (EContentEditor *editor);
+
+void           e_content_editor_table_set_border
+                                               (EContentEditor *editor,
+                                                gint value);
+
+gint           e_content_editor_table_get_border
+                                               (EContentEditor *editor);
+
+gchar *                e_content_editor_table_get_background_image_uri
+                                               (EContentEditor *editor);
+
+void           e_content_editor_table_set_background_image_uri
+                                               (EContentEditor *editor,
+                                                const gchar *uri);
+
+void           e_content_editor_table_get_background_color
+                                               (EContentEditor *editor,
+                                                GdkRGBA *value);
+
+void           e_content_editor_table_set_background_color
+                                               (EContentEditor *editor,
+                                                const GdkRGBA *value);
+
+gboolean       e_content_editor_on_table_dialog_open
+                                               (EContentEditor *editor);
+
+void           e_content_editor_on_table_dialog_close
+                                               (EContentEditor *editor);
+
+void           e_content_editor_on_spell_check_dialog_open
+                                               (EContentEditor *editor);
+
+void           e_content_editor_on_spell_check_dialog_close
+                                               (EContentEditor *editor);
+
+gchar *                e_content_editor_spell_check_next_word
+                                               (EContentEditor *editor,
+                                                const gchar *word);
+
+gchar *                e_content_editor_spell_check_prev_word
+                                               (EContentEditor *editor,
+                                                const gchar *word);
+
+void           e_content_editor_spell_check_replace_all
+                                               (EContentEditor *editor,
+                                                const gchar *word,
+                                                const gchar *replacement);
+
+void           e_content_editor_on_replace_dialog_open
+                                               (EContentEditor *editor);
+
+void           e_content_editor_on_replace_dialog_close
+                                               (EContentEditor *editor);
+
+void           e_content_editor_on_find_dialog_open
+                                               (EContentEditor *editor);
+
+void           e_content_editor_on_find_dialog_close
+                                               (EContentEditor *editor);
+
+/* Signal helpers */
+
+void           e_content_editor_emit_load_finished
+                                               (EContentEditor *editor);
+gboolean       e_content_editor_emit_paste_clipboard
+                                               (EContentEditor *editor);
+gboolean       e_content_editor_emit_paste_primary_clipboard
+                                               (EContentEditor *editor);
+gboolean       e_content_editor_emit_context_menu_requested
+                                               (EContentEditor *editor,
+                                                EContentEditorNodeFlags flags,
+                                                GdkEvent *event);
+void           e_content_editor_emit_find_done (EContentEditor *editor,
+                                                guint match_count);
+void           e_content_editor_emit_replace_all_done
+                                               (EContentEditor *editor,
+                                                guint replaced_count);
+
+G_END_DECLS
+
+#endif /* E_CONTENT_EDITOR_H */
diff --git a/e-util/e-content-request.c b/e-util/e-content-request.c
new file mode 100644
index 0000000..9448d44
--- /dev/null
+++ b/e-util/e-content-request.c
@@ -0,0 +1,188 @@
+/*
+ * Copyright (C) 2016 Red Hat, Inc. (www.redhat.com)
+ *
+ * This library 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 library 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 Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <errno.h>
+
+#include <glib.h>
+#include <glib-object.h>
+#include <gio/gio.h>
+
+#include "e-content-request.h"
+
+G_DEFINE_INTERFACE (EContentRequest, e_content_request, G_TYPE_OBJECT)
+
+static void
+e_content_request_default_init (EContentRequestInterface *iface)
+{
+}
+
+gboolean
+e_content_request_can_process_uri (EContentRequest *request,
+                                  const gchar *uri)
+{
+       EContentRequestInterface *iface;
+
+       g_return_val_if_fail (E_IS_CONTENT_REQUEST (request), FALSE);
+       g_return_val_if_fail (uri != NULL, FALSE);
+
+       iface = E_CONTENT_REQUEST_GET_INTERFACE (request);
+       g_return_val_if_fail (iface != NULL, FALSE);
+       g_return_val_if_fail (iface->can_process_uri != NULL, FALSE);
+
+       return iface->can_process_uri (request, uri);
+}
+
+gboolean
+e_content_request_process_sync (EContentRequest *request,
+                               const gchar *uri,
+                               GObject *requester,
+                               GInputStream **out_stream,
+                               gint64 *out_stream_length,
+                               gchar **out_mime_type,
+                               GCancellable *cancellable,
+                               GError **error)
+{
+       EContentRequestInterface *iface;
+       GError *local_error = NULL;
+
+       g_return_val_if_fail (E_IS_CONTENT_REQUEST (request), FALSE);
+       g_return_val_if_fail (uri != NULL, FALSE);
+       g_return_val_if_fail (G_IS_OBJECT (requester), FALSE);
+       g_return_val_if_fail (out_stream != NULL, FALSE);
+       g_return_val_if_fail (out_stream_length != NULL, FALSE);
+       g_return_val_if_fail (out_mime_type != NULL, FALSE);
+
+       iface = E_CONTENT_REQUEST_GET_INTERFACE (request);
+       g_return_val_if_fail (iface != NULL, FALSE);
+       g_return_val_if_fail (iface->process_sync != NULL, FALSE);
+
+       if (!iface->process_sync (request, uri, requester, out_stream, out_stream_length, out_mime_type, 
cancellable, &local_error)) {
+               if (!local_error)
+                       local_error = g_error_new_literal (G_IO_ERROR, G_IO_ERROR_FAILED, g_strerror 
(ENOENT));
+
+               g_propagate_error (error, local_error);
+
+               return FALSE;
+       }
+
+       return TRUE;
+}
+
+typedef struct _ThreadData
+{
+       gchar *uri;
+       GObject *requester;
+       GInputStream *out_stream;
+       gint64 out_stream_length;
+       gchar *out_mime_type;
+} ThreadData;
+
+static void
+thread_data_free (gpointer ptr)
+{
+       ThreadData *td = ptr;
+
+       if (td) {
+               g_clear_object (&td->out_stream);
+               g_clear_object (&td->requester);
+               g_free (td->uri);
+               g_free (td->out_mime_type);
+               g_free (td);
+       }
+}
+
+static void
+content_request_process_thread (GTask *task,
+                               gpointer source_object,
+                               gpointer task_data,
+                               GCancellable *cancellable)
+{
+       ThreadData *td = task_data;
+       GError *local_error = NULL;
+
+       g_return_if_fail (E_IS_CONTENT_REQUEST (source_object));
+       g_return_if_fail (td != NULL);
+
+       if (!e_content_request_process_sync (E_CONTENT_REQUEST (source_object),
+               td->uri, td->requester, &td->out_stream, &td->out_stream_length, &td->out_mime_type,
+               cancellable, &local_error)) {
+               g_task_return_error (task, local_error);
+       } else {
+               g_task_return_boolean (task, TRUE);
+       }
+}
+
+void
+e_content_request_process (EContentRequest *request,
+                          const gchar *uri,
+                          GObject *requester,
+                          GCancellable *cancellable,
+                          GAsyncReadyCallback callback,
+                          gpointer user_data)
+{
+       GTask *task;
+       ThreadData *td;
+
+       g_return_if_fail (E_IS_CONTENT_REQUEST (request));
+       g_return_if_fail (uri != NULL);
+       g_return_if_fail (G_IS_OBJECT (requester));
+
+       td = g_new0 (ThreadData, 1);
+       td->uri = g_strdup (uri);
+       td->requester = g_object_ref (requester);
+
+       task = g_task_new (request, cancellable, callback, user_data);
+       g_task_set_task_data (task, td, thread_data_free);
+       g_task_run_in_thread (task, content_request_process_thread);
+       g_object_unref (task);
+}
+
+gboolean
+e_content_request_process_finish (EContentRequest *request,
+                                 GAsyncResult *result,
+                                 GInputStream **out_stream,
+                                 gint64 *out_stream_length,
+                                 gchar **out_mime_type,
+                                 GError **error)
+{
+       ThreadData *td;
+
+       g_return_val_if_fail (g_task_is_valid (result, request), FALSE);
+       g_return_val_if_fail (E_IS_CONTENT_REQUEST (request), FALSE);
+       g_return_val_if_fail (G_IS_TASK (result), FALSE);
+       g_return_val_if_fail (out_stream != NULL, FALSE);
+       g_return_val_if_fail (out_stream_length != NULL, FALSE);
+       g_return_val_if_fail (out_mime_type != NULL, FALSE);
+
+       td = g_task_get_task_data (G_TASK (result));
+       g_return_val_if_fail (td != NULL, FALSE);
+
+       if (!g_task_propagate_boolean (G_TASK (result), error))
+               return FALSE;
+
+       *out_stream = td->out_stream;
+       *out_stream_length = td->out_stream_length;
+       *out_mime_type = td->out_mime_type;
+
+       td->out_stream = NULL;
+       td->out_mime_type = NULL;
+
+       return TRUE;
+}
diff --git a/e-util/e-content-request.h b/e-util/e-content-request.h
new file mode 100644
index 0000000..7df94c4
--- /dev/null
+++ b/e-util/e-content-request.h
@@ -0,0 +1,93 @@
+/*
+ * Copyright (C) 2016 Red Hat, Inc. (www.redhat.com)
+ *
+ * This library 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 library 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 Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#if !defined (__E_UTIL_H_INSIDE__) && !defined (LIBEUTIL_COMPILATION)
+#error "Only <e-util/e-util.h> should be included directly."
+#endif
+
+#ifndef E_CONTENT_REQUEST_H
+#define E_CONTENT_REQUEST_H
+
+#include <glib.h>
+#include <glib-object.h>
+#include <gio/gio.h>
+
+/* Standard GObject macros */
+#define E_TYPE_CONTENT_REQUEST \
+       (e_content_request_get_type ())
+#define E_CONTENT_REQUEST(obj) \
+       (G_TYPE_CHECK_INSTANCE_CAST \
+       ((obj), E_TYPE_CONTENT_REQUEST, EContentRequest))
+#define E_CONTENT_REQUEST_INTERFACE(cls) \
+       (G_TYPE_CHECK_CLASS_CAST \
+       ((cls), E_TYPE_CONTENT_REQUEST, EContentRequestInterface))
+#define E_IS_CONTENT_REQUEST(obj) \
+       (G_TYPE_CHECK_INSTANCE_TYPE \
+       ((obj), E_TYPE_CONTENT_REQUEST))
+#define E_IS_CONTENT_REQUEST_INTERFACE(cls) \
+       (G_TYPE_CHECK_CLASS_TYPE \
+       ((cls), E_TYPE_CONTENT_REQUEST))
+#define E_CONTENT_REQUEST_GET_INTERFACE(obj) \
+       (G_TYPE_INSTANCE_GET_INTERFACE \
+       ((obj), E_TYPE_CONTENT_REQUEST, EContentRequestInterface))
+
+G_BEGIN_DECLS
+
+typedef struct _EContentRequest EContentRequest;
+typedef struct _EContentRequestInterface EContentRequestInterface;
+
+struct _EContentRequestInterface {
+       GTypeInterface parent_interface;
+
+       gboolean        (* can_process_uri)     (EContentRequest *request,
+                                                const gchar *uri);
+       gboolean        (* process_sync)        (EContentRequest *request,
+                                                const gchar *uri,
+                                                GObject *requester,
+                                                GInputStream **out_stream,
+                                                gint64 *out_stream_length,
+                                                gchar **out_mime_type,
+                                                GCancellable *cancellable,
+                                                GError **error);
+};
+
+GType          e_content_request_get_type              (void);
+gboolean       e_content_request_can_process_uri       (EContentRequest *request,
+                                                        const gchar *uri);
+gboolean       e_content_request_process_sync          (EContentRequest *request,
+                                                        const gchar *uri,
+                                                        GObject *requester,
+                                                        GInputStream **out_stream,
+                                                        gint64 *out_stream_length,
+                                                        gchar **out_mime_type,
+                                                        GCancellable *cancellable,
+                                                        GError **error);
+void           e_content_request_process               (EContentRequest *request,
+                                                        const gchar *uri,
+                                                        GObject *requester,
+                                                        GCancellable *cancellable,
+                                                        GAsyncReadyCallback callback,
+                                                        gpointer user_data);
+gboolean       e_content_request_process_finish        (EContentRequest *request,
+                                                        GAsyncResult *result,
+                                                        GInputStream **out_stream,
+                                                        gint64 *out_stream_length,
+                                                        gchar **out_mime_type,
+                                                        GError **error);
+
+G_END_DECLS
+
+#endif /* E_CONTENT_REQUEST_H */
diff --git a/e-util/e-emoticon.c b/e-util/e-emoticon.c
index cc2fd41..53a9f49 100644
--- a/e-util/e-emoticon.c
+++ b/e-util/e-emoticon.c
@@ -121,3 +121,9 @@ e_emoticon_get_uri (EEmoticon *emoticon)
 
        return uri;
 }
+
+const gchar *
+e_emoticon_get_name (EEmoticon *emoticon)
+{
+       return emoticon->icon_name;
+}
diff --git a/e-util/e-emoticon.h b/e-util/e-emoticon.h
index e77806f..3b9e8c0 100644
--- a/e-util/e-emoticon.h
+++ b/e-util/e-emoticon.h
@@ -48,6 +48,7 @@ gboolean      e_emoticon_equal                (EEmoticon *emoticon_a,
 EEmoticon *    e_emoticon_copy                 (EEmoticon *emoticon);
 void           e_emoticon_free                 (EEmoticon *emoticon);
 gchar *                e_emoticon_get_uri              (EEmoticon *emoticon);
+const gchar *  e_emoticon_get_name             (EEmoticon *emoticon);
 
 G_END_DECLS
 
diff --git a/e-util/e-file-request.c b/e-util/e-file-request.c
index f6b4ac2..8cdb18a 100644
--- a/e-util/e-file-request.c
+++ b/e-util/e-file-request.c
@@ -15,178 +15,124 @@
  *
  */
 
-#define LIBSOUP_USE_UNSTABLE_REQUEST_API
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
 
-#include "e-file-request.h"
+#include <stdio.h>
+#include <string.h>
 
 #include <libsoup/soup.h>
 
-#include <stdio.h>
-#include <string.h>
+#include "e-file-request.h"
 
 #define d(x)
 
-#define E_FILE_REQUEST_GET_PRIVATE(obj) \
-       (G_TYPE_INSTANCE_GET_PRIVATE \
-       ((obj), E_TYPE_FILE_REQUEST, EFileRequestPrivate))
-
 struct _EFileRequestPrivate {
-       gchar *content_type;
-       gint content_length;
+       gint dummy;
 };
 
-G_DEFINE_TYPE (EFileRequest, e_file_request, SOUP_TYPE_REQUEST)
-
-static void
-handle_file_request (GSimpleAsyncResult *res,
-                     GObject *object,
-                     GCancellable *cancellable)
-{
-       EFileRequest *request = E_FILE_REQUEST (object);
-       SoupURI *uri;
-       GInputStream *stream;
-       gchar *contents;
-       gsize length;
-
-       if (g_cancellable_is_cancelled (cancellable))
-               return;
-
-       uri = soup_request_get_uri (SOUP_REQUEST (request));
-
-       if (g_file_get_contents (uri->path, &contents, &length, NULL)) {
+static void e_file_request_content_request_init (EContentRequestInterface *iface);
 
-               request->priv->content_type =
-                       g_content_type_guess (uri->path, NULL, 0, NULL);
-               request->priv->content_length = length;
-
-               stream = g_memory_input_stream_new_from_data (
-                               contents, length, (GDestroyNotify) g_free);
-               g_simple_async_result_set_op_res_gpointer (res, stream, g_object_unref);
-       }
-}
-
-static void
-file_request_finalize (GObject *object)
-{
-       EFileRequest *request = E_FILE_REQUEST (object);
-
-       if (request->priv->content_type) {
-               g_free (request->priv->content_type);
-               request->priv->content_type = NULL;
-       }
-
-       G_OBJECT_CLASS (e_file_request_parent_class)->finalize (object);
-}
+G_DEFINE_TYPE_WITH_CODE (EFileRequest, e_file_request, G_TYPE_OBJECT,
+       G_IMPLEMENT_INTERFACE (E_TYPE_CONTENT_REQUEST, e_file_request_content_request_init))
 
 static gboolean
-file_request_check_uri (SoupRequest *request,
-                       SoupURI *uri,
-                       GError **error)
-{
-       return (strcmp (uri->scheme, "evo-file") == 0);
-}
-
-static void
-file_request_send_async (SoupRequest *request,
-                         GCancellable *cancellable,
-                         GAsyncReadyCallback callback,
-                         gpointer user_data)
+e_file_request_can_process_uri (EContentRequest *request,
+                               const gchar *uri)
 {
-       GSimpleAsyncResult *simple;
-
-       d (
-               SoupURI *soup_uri = soup_request_get_uri (request);
-               gchar *uri = soup_uri_to_string (soup_uri, FALSE);
-               printf ("received request for %s\n", uri);
-               g_free (uri);
-       );
-
-       /* WebKit won't allow us to load data through local file:// protocol
-        * when using "remote" mail:// protocol, so we have evo-file://
-        * which WebKit thinks it's remote, but in fact it behaves like
-        * oridnary file:// */
-
-       simple = g_simple_async_result_new (
-               G_OBJECT (request), callback, user_data,
-               file_request_send_async);
-
-       g_simple_async_result_set_check_cancellable (simple, cancellable);
-
-       g_simple_async_result_run_in_thread (
-               simple, handle_file_request,
-               G_PRIORITY_DEFAULT, cancellable);
+       g_return_val_if_fail (E_IS_FILE_REQUEST (request), FALSE);
+       g_return_val_if_fail (uri != NULL, FALSE);
 
-       g_object_unref (simple);
+       return g_ascii_strncasecmp (uri, "evo-file:", 9) == 0;
 }
 
-static GInputStream *
-file_request_send_finish (SoupRequest *request,
-                          GAsyncResult *result,
-                          GError **error)
+static gboolean
+e_file_request_process_sync (EContentRequest *request,
+                            const gchar *uri,
+                            GObject *requester,
+                            GInputStream **out_stream,
+                            gint64 *out_stream_length,
+                            gchar **out_mime_type,
+                            GCancellable *cancellable,
+                            GError **error)
 {
-       GSimpleAsyncResult *simple;
-       GInputStream *stream;
-
-       simple = G_SIMPLE_ASYNC_RESULT (result);
-       stream = g_simple_async_result_get_op_res_gpointer (simple);
-
-       /* Reset the stream before passing it back to webkit */
-       if (stream && G_IS_SEEKABLE (stream))
-               g_seekable_seek (G_SEEKABLE (stream), 0, G_SEEK_SET, NULL, NULL);
-
-       if (!stream) /* We must always return something */
-               stream = g_memory_input_stream_new ();
-       else
-               g_object_ref (stream);
-
-       return stream;
-}
+       GFile *file;
+       GFileInputStream *file_input_stream;
+       GFileInfo *info;
+       goffset total_size;
+       SoupURI *suri;
+
+       g_return_val_if_fail (E_IS_FILE_REQUEST (request), FALSE);
+       g_return_val_if_fail (uri != NULL, FALSE);
+
+       if (g_cancellable_set_error_if_cancelled (cancellable, error))
+               return FALSE;
+
+       suri = soup_uri_new (uri);
+       g_return_val_if_fail (suri != NULL, FALSE);
+
+       file = g_file_new_for_path (suri->path);
+       file_input_stream = g_file_read (file, cancellable, error);
+
+       if (file_input_stream) {
+               total_size = -1;
+               info = g_file_input_stream_query_info (file_input_stream, G_FILE_ATTRIBUTE_STANDARD_SIZE, 
cancellable, NULL);
+               if (info) {
+                       if (g_file_info_has_attribute (info, G_FILE_ATTRIBUTE_STANDARD_SIZE))
+                               total_size = g_file_info_get_size (info);
+                       g_object_unref (info);
+               }
+
+               if (total_size == -1) {
+                       info = g_file_query_info (file, G_FILE_ATTRIBUTE_STANDARD_SIZE, 
G_FILE_QUERY_INFO_NONE, cancellable, NULL);
+                       if (info) {
+                               if (g_file_info_has_attribute (info, G_FILE_ATTRIBUTE_STANDARD_SIZE))
+                                       total_size = g_file_info_get_size (info);
+                               g_object_unref (info);
+                       }
+               }
+       } else {
+               total_size = -1;
+       }
 
-static goffset
-file_request_get_content_length (SoupRequest *request)
-{
-       EFileRequest *efr = E_FILE_REQUEST (request);
+       if (file_input_stream) {
+               *out_stream = G_INPUT_STREAM (file_input_stream);
+               *out_stream_length = (gint64) total_size;
+               *out_mime_type = g_content_type_guess (suri->path, NULL, 0, NULL);
+       } else {
+               *out_stream = NULL;
+               *out_stream_length = (gint64) total_size;
+               *out_mime_type = NULL;
+       }
 
-       d (printf ("Content-Length: %d bytes\n", efr->priv->content_length));
+       g_object_unref (file);
+       soup_uri_free (suri);
 
-       return efr->priv->content_length;
+       return file_input_stream != NULL;
 }
 
-static const gchar *
-file_request_get_content_type (SoupRequest *request)
+static void
+e_file_request_content_request_init (EContentRequestInterface *iface)
 {
-       EFileRequest *efr = E_FILE_REQUEST (request);
-
-       d (printf ("Content-Type: %s\n", efr->priv->content_type));
-
-       return efr->priv->content_type;
+       iface->can_process_uri = e_file_request_can_process_uri;
+       iface->process_sync = e_file_request_process_sync;
 }
 
-static const gchar *data_schemes[] = { "evo-file", NULL };
-
 static void
 e_file_request_class_init (EFileRequestClass *class)
 {
-       GObjectClass *object_class;
-       SoupRequestClass *request_class;
-
        g_type_class_add_private (class, sizeof (EFileRequestPrivate));
-
-       object_class = G_OBJECT_CLASS (class);
-       object_class->finalize = file_request_finalize;
-
-       request_class = SOUP_REQUEST_CLASS (class);
-       request_class->schemes = data_schemes;
-       request_class->send_async = file_request_send_async;
-       request_class->send_finish = file_request_send_finish;
-       request_class->get_content_type = file_request_get_content_type;
-       request_class->get_content_length = file_request_get_content_length;
-       request_class->check_uri = file_request_check_uri;
 }
 
 static void
 e_file_request_init (EFileRequest *request)
 {
-       request->priv = E_FILE_REQUEST_GET_PRIVATE (request);
+       request->priv = G_TYPE_INSTANCE_GET_PRIVATE (request, E_TYPE_FILE_REQUEST, EFileRequestPrivate);
 }
 
+EContentRequest *
+e_file_request_new (void)
+{
+       return g_object_new (E_TYPE_FILE_REQUEST, NULL);
+}
diff --git a/e-util/e-file-request.h b/e-util/e-file-request.h
index ab46c9a..706309d 100644
--- a/e-util/e-file-request.h
+++ b/e-util/e-file-request.h
@@ -22,10 +22,7 @@
 #ifndef E_FILE_REQUEST_H
 #define E_FILE_REQUEST_H
 
-#define LIBSOUP_USE_UNSTABLE_REQUEST_API
-
-#include <libsoup/soup.h>
-#include <libsoup/soup-request.h>
+#include <e-util/e-content-request.h>
 
 /* Standard GObject macros */
 #define E_TYPE_FILE_REQUEST \
@@ -53,15 +50,17 @@ typedef struct _EFileRequestClass EFileRequestClass;
 typedef struct _EFileRequestPrivate EFileRequestPrivate;
 
 struct _EFileRequest {
-       SoupRequest parent;
+       GObject parent;
        EFileRequestPrivate *priv;
 };
 
 struct _EFileRequestClass {
-       SoupRequestClass parent;
+       GObjectClass parent;
 };
 
 GType          e_file_request_get_type         (void) G_GNUC_CONST;
+EContentRequest *
+               e_file_request_new              (void);
 
 G_END_DECLS
 
diff --git a/e-util/e-focus-tracker.c b/e-util/e-focus-tracker.c
index 7a9477a..794e56d 100644
--- a/e-util/e-focus-tracker.c
+++ b/e-util/e-focus-tracker.c
@@ -28,7 +28,7 @@
 
 #include "e-selectable.h"
 #include "e-widget-undo.h"
-#include "e-html-editor-view.h"
+#include "e-content-editor.h"
 
 #define E_FOCUS_TRACKER_GET_PRIVATE(obj) \
        (G_TYPE_INSTANCE_GET_PRIVATE \
@@ -257,7 +257,7 @@ focus_tracker_text_view_update_actions (EFocusTracker *focus_tracker,
 
 static void
 focus_tracker_editor_update_actions (EFocusTracker *focus_tracker,
-                                     EHTMLEditorView *view,
+                                     EContentEditor *cnt_editor,
                                      GdkAtom *targets,
                                      gint n_targets)
 {
@@ -266,7 +266,7 @@ focus_tracker_editor_update_actions (EFocusTracker *focus_tracker,
        gboolean can_cut;
        gboolean can_paste;
 
-       g_object_get (view,
+       g_object_get (cnt_editor,
                      "can-copy", &can_copy,
                      "can-cut", &can_cut,
                      "can-paste", &can_paste,
@@ -368,9 +368,9 @@ focus_tracker_targets_received_cb (GtkClipboard *clipboard,
                        focus_tracker, GTK_TEXT_VIEW (focus),
                        targets, n_targets);
 
-       else if (E_IS_HTML_EDITOR_VIEW (focus))
+       else if (E_IS_CONTENT_EDITOR (focus))
                focus_tracker_editor_update_actions (
-                       focus_tracker, E_HTML_EDITOR_VIEW (focus),
+                       focus_tracker, E_CONTENT_EDITOR (focus),
                        targets, n_targets);
 
        g_object_unref (focus_tracker);
@@ -391,7 +391,7 @@ focus_tracker_set_focus_cb (GtkWindow *window,
                if (GTK_IS_TEXT_VIEW (focus))
                        break;
 
-               if (E_IS_HTML_EDITOR_VIEW (focus))
+               if (E_IS_CONTENT_EDITOR (focus))
                        break;
 
                focus = gtk_widget_get_parent (focus);
diff --git a/e-util/e-html-editor-actions.c b/e-util/e-html-editor-actions.c
index 638b05c..aaf9dd7 100644
--- a/e-util/e-html-editor-actions.c
+++ b/e-util/e-html-editor-actions.c
@@ -29,20 +29,20 @@
 #include "e-html-editor.h"
 #include "e-html-editor-private.h"
 #include "e-html-editor-actions.h"
-#include "e-html-editor-utils.h"
 #include "e-emoticon-action.h"
 #include "e-emoticon-chooser.h"
 #include "e-image-chooser-dialog.h"
 #include "e-spell-checker.h"
 #include "e-misc-utils.h"
-#include "e-web-view.h"
+#include "e-selection.h"
+#include "e-content-editor.h"
 
 static void
 insert_html_file_ready_cb (GFile *file,
                            GAsyncResult *result,
                            EHTMLEditor *editor)
 {
-       EHTMLEditorSelection *selection;
+       EContentEditor *cnt_editor;
        gchar *contents = NULL;
        gsize length;
        GError *error = NULL;
@@ -66,9 +66,10 @@ insert_html_file_ready_cb (GFile *file,
                return;
        }
 
-       selection = e_html_editor_view_get_selection (
-               e_html_editor_get_view (editor));
-       e_html_editor_selection_insert_html (selection, contents);
+       cnt_editor = e_html_editor_get_content_editor (editor);
+       e_content_editor_insert_content (
+               cnt_editor, contents, E_CONTENT_EDITOR_INSERT_TEXT_HTML);
+
        g_free (contents);
 
        g_object_unref (editor);
@@ -79,7 +80,7 @@ insert_text_file_ready_cb (GFile *file,
                            GAsyncResult *result,
                            EHTMLEditor *editor)
 {
-       EHTMLEditorSelection *selection;
+       EContentEditor *cnt_editor;
        gchar *contents;
        gsize length;
        GError *error = NULL;
@@ -103,9 +104,10 @@ insert_text_file_ready_cb (GFile *file,
                return;
        }
 
-       selection = e_html_editor_view_get_selection (
-               e_html_editor_get_view (editor));
-       e_html_editor_selection_insert_text (selection, contents);
+       cnt_editor = e_html_editor_get_content_editor (editor);
+       e_content_editor_insert_content (
+               cnt_editor, contents, E_CONTENT_EDITOR_INSERT_TEXT_PLAIN);
+
        g_free (contents);
 
        g_object_unref (editor);
@@ -116,464 +118,172 @@ insert_text_file_ready_cb (GFile *file,
  *****************************************************************************/
 
 static void
-prepare_history_for_table (EHTMLEditor *editor,
-                           WebKitDOMElement *table,
-                           EHTMLEditorViewHistoryEvent *ev)
-{
-       EHTMLEditorSelection *selection;
-       EHTMLEditorView *view;
-
-       view = e_html_editor_get_view (editor);
-       selection = e_html_editor_view_get_selection (view);
-
-       ev->type = HISTORY_TABLE_DIALOG;
-
-       e_html_editor_selection_get_selection_coordinates (
-               selection, &ev->before.start.x, &ev->before.start.y, &ev->before.end.x, &ev->before.end.y);
-       ev->data.dom.from = webkit_dom_node_clone_node (
-               WEBKIT_DOM_NODE (table), TRUE);
-}
-
-static void
-save_history_for_table (EHTMLEditor *editor,
-                        WebKitDOMElement *table,
-                        EHTMLEditorViewHistoryEvent *ev)
-{
-       EHTMLEditorSelection *selection;
-       EHTMLEditorView *view;
-
-       view = e_html_editor_get_view (editor);
-       selection = e_html_editor_view_get_selection (view);
-
-       if (table)
-               ev->data.dom.to = webkit_dom_node_clone_node (
-                       WEBKIT_DOM_NODE (table), TRUE);
-       else
-               ev->data.dom.to = NULL;
-       e_html_editor_selection_get_selection_coordinates (
-               selection, &ev->after.start.x, &ev->after.start.y, &ev->after.end.x, &ev->after.end.y);
-       e_html_editor_view_insert_new_history_event (view, ev);
-}
-
-static void
 action_context_delete_cell_contents_cb (GtkAction *action,
-                                        EHTMLEditor *editor)
+                               EHTMLEditor *editor)
 {
-       EHTMLEditorViewHistoryEvent *ev = NULL;
-       WebKitDOMNode *node;
-       WebKitDOMElement *cell, *table;
-
-       g_return_if_fail (editor->priv->table_cell != NULL);
+       EContentEditor *cnt_editor;
 
-       cell = e_html_editor_dom_node_find_parent_element (editor->priv->table_cell, "TD");
-       if (!cell) {
-               cell = e_html_editor_dom_node_find_parent_element (
-                                       editor->priv->table_cell, "TH");
-       }
-       g_return_if_fail (cell != NULL);
-
-       table = e_html_editor_dom_node_find_parent_element (WEBKIT_DOM_NODE (cell), "TABLE");
-       g_return_if_fail (table != NULL);
-
-       ev = g_new0 (EHTMLEditorViewHistoryEvent, 1);
-       prepare_history_for_table (editor, table, ev);
-
-       while ((node = webkit_dom_node_get_first_child (WEBKIT_DOM_NODE (cell))))
-               remove_node (node);
-
-       save_history_for_table (editor, table, ev);
+       cnt_editor = e_html_editor_get_content_editor (editor);
+       e_content_editor_delete_cell_contents (cnt_editor);
 }
 
 static void
 action_context_delete_column_cb (GtkAction *action,
                                  EHTMLEditor *editor)
 {
-       EHTMLEditorViewHistoryEvent *ev = NULL;
-       WebKitDOMElement *cell, *table;
-       WebKitDOMHTMLCollection *rows;
-       gulong index, length, ii;
-
-       g_return_if_fail (editor->priv->table_cell != NULL);
-
-       /* Find TD in which the selection starts */
-       cell = e_html_editor_dom_node_find_parent_element (editor->priv->table_cell, "TD");
-       if (!cell) {
-               cell = e_html_editor_dom_node_find_parent_element (
-                                       editor->priv->table_cell, "TH");
-       }
-       g_return_if_fail (cell != NULL);
+       EContentEditor *cnt_editor;
 
-       table = e_html_editor_dom_node_find_parent_element (WEBKIT_DOM_NODE (cell), "TABLE");
-       g_return_if_fail (table != NULL);
-
-       ev = g_new0 (EHTMLEditorViewHistoryEvent, 1);
-       prepare_history_for_table (editor, table, ev);
-
-       rows = webkit_dom_html_table_element_get_rows (
-                       WEBKIT_DOM_HTML_TABLE_ELEMENT (table));
-       length = webkit_dom_html_collection_get_length (rows);
-
-       index = webkit_dom_html_table_cell_element_get_cell_index (
-                       WEBKIT_DOM_HTML_TABLE_CELL_ELEMENT (cell));
-
-       for (ii = 0; ii < length; ii++) {
-               WebKitDOMNode *row;
-
-               row = webkit_dom_html_collection_item (rows, ii);
-
-               webkit_dom_html_table_row_element_delete_cell (
-                       WEBKIT_DOM_HTML_TABLE_ROW_ELEMENT (row), index, NULL);
-               g_object_unref (row);
-       }
-       g_object_unref (rows);
-
-       save_history_for_table (editor, table, ev);
+       cnt_editor = e_html_editor_get_content_editor (editor);
+       e_content_editor_delete_column (cnt_editor);
 }
 
 static void
 action_context_delete_row_cb (GtkAction *action,
                               EHTMLEditor *editor)
 {
-       EHTMLEditorViewHistoryEvent *ev = NULL;
-       WebKitDOMElement *row, *table;
-
-       g_return_if_fail (editor->priv->table_cell != NULL);
-
-       row = e_html_editor_dom_node_find_parent_element (editor->priv->table_cell, "TR");
-       g_return_if_fail (row != NULL);
-
-       table = e_html_editor_dom_node_find_parent_element (editor->priv->table_cell, "TABLE");
-       g_return_if_fail (table != NULL);
+       EContentEditor *cnt_editor;
 
-       ev = g_new0 (EHTMLEditorViewHistoryEvent, 1);
-       prepare_history_for_table (editor, table, ev);
-
-       remove_node (WEBKIT_DOM_NODE (row));
-
-       save_history_for_table (editor, table, ev);
+       cnt_editor = e_html_editor_get_content_editor (editor);
+       e_content_editor_delete_row (cnt_editor);
 }
 
 static void
 action_context_delete_table_cb (GtkAction *action,
                                 EHTMLEditor *editor)
 {
-       WebKitDOMElement *table;
-       EHTMLEditorViewHistoryEvent *ev = NULL;
-
-       g_return_if_fail (editor->priv->table_cell != NULL);
-
-       table = e_html_editor_dom_node_find_parent_element (editor->priv->table_cell, "TABLE");
-       g_return_if_fail (table != NULL);
-
-       ev = g_new0 (EHTMLEditorViewHistoryEvent, 1);
-       prepare_history_for_table (editor, table, ev);
-
-       remove_node (WEBKIT_DOM_NODE (table));
+       EContentEditor *cnt_editor;
 
-       save_history_for_table (editor, NULL, ev);
+       cnt_editor = e_html_editor_get_content_editor (editor);
+       e_content_editor_delete_table (cnt_editor);
 }
 
 static void
 action_context_insert_column_after_cb (GtkAction *action,
                                        EHTMLEditor *editor)
 {
-       EHTMLEditorViewHistoryEvent *ev = NULL;
-       WebKitDOMElement *cell, *row, *table;
-       gulong index;
-
-       g_return_if_fail (editor->priv->table_cell != NULL);
-
-       cell = e_html_editor_dom_node_find_parent_element (editor->priv->table_cell, "TD");
-       if (!cell) {
-               cell = e_html_editor_dom_node_find_parent_element (
-                                       editor->priv->table_cell, "TH");
-       }
-       g_return_if_fail (cell != NULL);
-
-       row = e_html_editor_dom_node_find_parent_element (WEBKIT_DOM_NODE (cell), "TR");
-       g_return_if_fail (row != NULL);
-
-       table = e_html_editor_dom_node_find_parent_element (editor->priv->table_cell, "TABLE");
-       g_return_if_fail (table != NULL);
+       EContentEditor *cnt_editor;
 
-       ev = g_new0 (EHTMLEditorViewHistoryEvent, 1);
-       prepare_history_for_table (editor, table, ev);
-
-       /* Get the first row in the table */
-       row = WEBKIT_DOM_ELEMENT (
-               webkit_dom_node_get_first_child (
-                       webkit_dom_node_get_parent_node (WEBKIT_DOM_NODE (row))));
-
-       index = webkit_dom_html_table_cell_element_get_cell_index (
-                       WEBKIT_DOM_HTML_TABLE_CELL_ELEMENT (cell));
-
-       while (row) {
-               webkit_dom_html_table_row_element_insert_cell (
-                       WEBKIT_DOM_HTML_TABLE_ROW_ELEMENT (row), index + 1, NULL);
-
-               row = WEBKIT_DOM_ELEMENT (
-                       webkit_dom_node_get_next_sibling (WEBKIT_DOM_NODE (row)));
-       }
-
-       save_history_for_table (editor, table, ev);
+       cnt_editor = e_html_editor_get_content_editor (editor);
+       e_content_editor_insert_column_after (cnt_editor);
 }
 
 static void
 action_context_insert_column_before_cb (GtkAction *action,
                                         EHTMLEditor *editor)
 {
-       EHTMLEditorViewHistoryEvent *ev = NULL;
-       WebKitDOMElement *cell, *row, *table;
-       gulong index;
-
-       g_return_if_fail (editor->priv->table_cell != NULL);
-
-       cell = e_html_editor_dom_node_find_parent_element (editor->priv->table_cell, "TD");
-       if (!cell) {
-               cell = e_html_editor_dom_node_find_parent_element (
-                               editor->priv->table_cell, "TH");
-       }
-       g_return_if_fail (cell != NULL);
-
-       row = e_html_editor_dom_node_find_parent_element (WEBKIT_DOM_NODE (cell), "TR");
-       g_return_if_fail (row != NULL);
-
-       table = e_html_editor_dom_node_find_parent_element (editor->priv->table_cell, "TABLE");
-       g_return_if_fail (table != NULL);
+       EContentEditor *cnt_editor;
 
-       ev = g_new0 (EHTMLEditorViewHistoryEvent, 1);
-       prepare_history_for_table (editor, table, ev);
-
-       /* Get the first row in the table */
-       row = WEBKIT_DOM_ELEMENT (
-               webkit_dom_node_get_first_child (
-                       webkit_dom_node_get_parent_node (WEBKIT_DOM_NODE (row))));
-
-       index = webkit_dom_html_table_cell_element_get_cell_index (
-                       WEBKIT_DOM_HTML_TABLE_CELL_ELEMENT (cell));
-
-       while (row) {
-               webkit_dom_html_table_row_element_insert_cell (
-                       WEBKIT_DOM_HTML_TABLE_ROW_ELEMENT (row), index - 1, NULL);
-
-               row = WEBKIT_DOM_ELEMENT (
-                       webkit_dom_node_get_next_sibling (WEBKIT_DOM_NODE (row)));
-       }
-
-       save_history_for_table (editor, table, ev);
+       cnt_editor = e_html_editor_get_content_editor (editor);
+       e_content_editor_insert_column_before (cnt_editor);
 }
 
 static void
 action_context_insert_row_above_cb (GtkAction *action,
                                     EHTMLEditor *editor)
 {
-       EHTMLEditorViewHistoryEvent *ev = NULL;
-       WebKitDOMElement *row, *table;
-       WebKitDOMHTMLCollection *cells;
-       WebKitDOMHTMLElement *new_row;
-       gulong index, cell_count, ii;
-
-       g_return_if_fail (editor->priv->table_cell != NULL);
-
-       row = e_html_editor_dom_node_find_parent_element (editor->priv->table_cell, "TR");
-       g_return_if_fail (row != NULL);
-
-       table = e_html_editor_dom_node_find_parent_element (WEBKIT_DOM_NODE (row), "TABLE");
-       g_return_if_fail (table != NULL);
-
-       ev = g_new0 (EHTMLEditorViewHistoryEvent, 1);
-       prepare_history_for_table (editor, table, ev);
-
-       index = webkit_dom_html_table_row_element_get_row_index (
-                       WEBKIT_DOM_HTML_TABLE_ROW_ELEMENT (row));
+       EContentEditor *cnt_editor;
 
-       new_row = webkit_dom_html_table_element_insert_row (
-                       WEBKIT_DOM_HTML_TABLE_ELEMENT (table), index, NULL);
-
-       cells = webkit_dom_html_table_row_element_get_cells (
-                       WEBKIT_DOM_HTML_TABLE_ROW_ELEMENT (row));
-       cell_count = webkit_dom_html_collection_get_length (cells);
-       for (ii = 0; ii < cell_count; ii++) {
-               webkit_dom_html_table_row_element_insert_cell (
-                       WEBKIT_DOM_HTML_TABLE_ROW_ELEMENT (new_row), -1, NULL);
-       }
-       g_object_unref (cells);
-
-       save_history_for_table (editor, table, ev);
+       cnt_editor = e_html_editor_get_content_editor (editor);
+       e_content_editor_insert_row_above (cnt_editor);
 }
 
 static void
 action_context_insert_row_below_cb (GtkAction *action,
                                     EHTMLEditor *editor)
 {
-       EHTMLEditorViewHistoryEvent *ev = NULL;
-       WebKitDOMElement *row, *table;
-       WebKitDOMHTMLCollection *cells;
-       WebKitDOMHTMLElement *new_row;
-       gulong index, cell_count, ii;
-
-       g_return_if_fail (editor->priv->table_cell != NULL);
-
-       row = e_html_editor_dom_node_find_parent_element (editor->priv->table_cell, "TR");
-       g_return_if_fail (row != NULL);
-
-       table = e_html_editor_dom_node_find_parent_element (WEBKIT_DOM_NODE (row), "TABLE");
-       g_return_if_fail (table != NULL);
-
-       ev = g_new0 (EHTMLEditorViewHistoryEvent, 1);
-       prepare_history_for_table (editor, table, ev);
-
-       index = webkit_dom_html_table_row_element_get_row_index (
-                       WEBKIT_DOM_HTML_TABLE_ROW_ELEMENT (row));
-
-       new_row = webkit_dom_html_table_element_insert_row (
-                       WEBKIT_DOM_HTML_TABLE_ELEMENT (table), index + 1, NULL);
-
-       cells = webkit_dom_html_table_row_element_get_cells (
-                       WEBKIT_DOM_HTML_TABLE_ROW_ELEMENT (row));
-       cell_count = webkit_dom_html_collection_get_length (cells);
-       for (ii = 0; ii < cell_count; ii++) {
-               webkit_dom_html_table_row_element_insert_cell (
-                       WEBKIT_DOM_HTML_TABLE_ROW_ELEMENT (new_row), -1, NULL);
-       }
-       g_object_unref (cells);
+       EContentEditor *cnt_editor;
 
-       save_history_for_table (editor, table, ev);
+       cnt_editor = e_html_editor_get_content_editor (editor);
+       e_content_editor_insert_row_below (cnt_editor);
 }
 
 static void
 action_context_remove_link_cb (GtkAction *action,
                                EHTMLEditor *editor)
 {
-       EHTMLEditorView *view;
-       EHTMLEditorSelection *selection;
+       EContentEditor *cnt_editor;
 
-       view = e_html_editor_get_view (editor);
-       selection = e_html_editor_view_get_selection (view);
-
-       e_html_editor_selection_unlink (selection);
+       cnt_editor = e_html_editor_get_content_editor (editor);
+       e_content_editor_selection_unlink (cnt_editor);
 }
 
 static void
 action_context_spell_add_cb (GtkAction *action,
                              EHTMLEditor *editor)
 {
+       EContentEditor *cnt_editor;
        ESpellChecker *spell_checker;
-       EHTMLEditorSelection *selection;
        gchar *word;
 
-       spell_checker = e_html_editor_view_get_spell_checker (
-               editor->priv->html_editor_view);
-       selection = e_html_editor_view_get_selection (editor->priv->html_editor_view);
-
-       word = e_html_editor_selection_get_caret_word (selection);
-       if (word && *word) {
+       cnt_editor = e_html_editor_get_content_editor (editor);
+       spell_checker = e_content_editor_ref_spell_checker (cnt_editor);
+       word = e_content_editor_get_caret_word (cnt_editor);
+       if (word && *word)
                e_spell_checker_learn_word (spell_checker, word);
-       }
+       g_free (word);
+       g_clear_object (&spell_checker);
 }
 
 static void
 action_context_spell_ignore_cb (GtkAction *action,
                                 EHTMLEditor *editor)
 {
+       EContentEditor *cnt_editor;
        ESpellChecker *spell_checker;
-       EHTMLEditorSelection *selection;
        gchar *word;
 
-       spell_checker = e_html_editor_view_get_spell_checker (
-               editor->priv->html_editor_view);
-       selection = e_html_editor_view_get_selection (editor->priv->html_editor_view);
-
-       word = e_html_editor_selection_get_caret_word (selection);
-       if (word && *word) {
+       cnt_editor = e_html_editor_get_content_editor (editor);
+       spell_checker = e_content_editor_ref_spell_checker (cnt_editor);
+       word = e_content_editor_get_caret_word (cnt_editor);
+       if (word && *word)
                e_spell_checker_ignore_word (spell_checker, word);
-       }
+       g_free (word);
+       g_clear_object (&spell_checker);
 }
 
 static void
 action_copy_cb (GtkAction *action,
                 EHTMLEditor *editor)
 {
-       EHTMLEditorView *view = e_html_editor_get_view (editor);
+       EContentEditor *cnt_editor;
 
-       webkit_web_view_copy_clipboard (WEBKIT_WEB_VIEW (view));
+       cnt_editor = e_html_editor_get_content_editor (editor);
+       e_content_editor_copy (cnt_editor);
 }
 
 static void
 action_cut_cb (GtkAction *action,
                EHTMLEditor *editor)
 {
-       EHTMLEditorView *view = e_html_editor_get_view (editor);
-       EHTMLEditorSelection *selection;
-       EHTMLEditorViewHistoryEvent *ev;
-       WebKitDOMDocument *document;
-       WebKitDOMDocumentFragment *fragment;
-       WebKitDOMDOMWindow *dom_window;
-       WebKitDOMDOMSelection *dom_selection;
-       WebKitDOMRange *range;
-
-       document = webkit_web_view_get_dom_document (WEBKIT_WEB_VIEW (view));
-       dom_window = webkit_dom_document_get_default_view (document);
-       dom_selection = webkit_dom_dom_window_get_selection (dom_window);
-       g_object_unref (dom_window);
-
-       if (!webkit_dom_dom_selection_get_range_count (dom_selection) ||
-           webkit_dom_dom_selection_get_is_collapsed (dom_selection)) {
-               g_object_unref (dom_selection);
-               return;
-       }
-
-       range = webkit_dom_dom_selection_get_range_at (dom_selection, 0, NULL);
-
-       selection = e_html_editor_view_get_selection (view);
-
-       ev = g_new0 (EHTMLEditorViewHistoryEvent, 1);
-       ev->type = HISTORY_DELETE;
+       EContentEditor *cnt_editor;
 
-       e_html_editor_selection_get_selection_coordinates (
-               selection, &ev->before.start.x, &ev->before.start.y, &ev->before.end.x, &ev->before.end.y);
-
-       /* Save the fragment. */
-       fragment = webkit_dom_range_clone_contents (range, NULL);
-       g_object_unref (range);
-       g_object_unref (dom_selection);
-       ev->data.fragment = g_object_ref (fragment);
-
-       webkit_web_view_cut_clipboard (WEBKIT_WEB_VIEW (view));
-
-       ev->after.start.x = ev->before.start.x;
-       ev->after.start.y = ev->before.start.y;
-       ev->after.end.x = ev->before.start.x;
-       ev->after.end.y = ev->before.start.y;
-
-       e_html_editor_view_insert_new_history_event (view, ev);
-
-       e_html_editor_view_force_spell_check_for_current_paragraph (view);
+       cnt_editor = e_html_editor_get_content_editor (editor);
+       e_content_editor_cut (cnt_editor);
 }
 
 static void
 action_indent_cb (GtkAction *action,
                   EHTMLEditor *editor)
 {
-       EHTMLEditorView *view = e_html_editor_get_view (editor);
+       EContentEditor *cnt_editor;
 
-       if (gtk_widget_has_focus (GTK_WIDGET (view)))
-               e_html_editor_selection_indent (editor->priv->selection);
+       cnt_editor = e_html_editor_get_content_editor (editor);
+       if (gtk_widget_has_focus (GTK_WIDGET (cnt_editor)))
+               e_content_editor_selection_indent (cnt_editor);
 }
 
 static void
 action_insert_emoticon_cb (GtkAction *action,
                            EHTMLEditor *editor)
 {
-       EHTMLEditorView *view;
+       EContentEditor *cnt_editor;
        EEmoticon *emoticon;
 
-       emoticon = e_emoticon_chooser_get_current_emoticon (
-                                       E_EMOTICON_CHOOSER (action));
+       emoticon = e_emoticon_chooser_get_current_emoticon (E_EMOTICON_CHOOSER (action));
        g_return_if_fail (emoticon != NULL);
 
-       view = e_html_editor_get_view (editor);
-       e_html_editor_view_insert_smiley (view, emoticon);
+       cnt_editor = e_html_editor_get_content_editor (editor);
+       e_content_editor_insert_emoticon (cnt_editor, emoticon);
 }
 
 static void
@@ -620,15 +330,13 @@ action_insert_image_cb (GtkAction *action,
        dialog = e_image_chooser_dialog_new (C_("dialog-title", "Insert Image"), NULL);
 
        if (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_ACCEPT) {
-               EHTMLEditorView *view;
-               EHTMLEditorSelection *selection;
+               EContentEditor *cnt_editor;
                gchar *uri;
 
                uri = gtk_file_chooser_get_uri (GTK_FILE_CHOOSER (dialog));
 
-               view = e_html_editor_get_view (editor);
-               selection = e_html_editor_view_get_selection (view);
-               e_html_editor_selection_insert_image (selection, uri);
+               cnt_editor = e_html_editor_get_content_editor (editor);
+               e_content_editor_insert_image (cnt_editor, uri);
 
                g_free (uri);
        }
@@ -644,8 +352,7 @@ action_insert_link_cb (GtkAction *action,
                editor->priv->link_dialog =
                        e_html_editor_link_dialog_new (editor);
 
-       e_html_editor_link_dialog_show (
-               E_HTML_EDITOR_LINK_DIALOG (editor->priv->link_dialog), NULL);
+       gtk_window_present (GTK_WINDOW (editor->priv->link_dialog));
 }
 
 static void
@@ -656,8 +363,7 @@ action_insert_rule_cb (GtkAction *action,
                editor->priv->hrule_dialog =
                        e_html_editor_hrule_dialog_new (editor);
 
-       e_html_editor_hrule_dialog_show (
-               E_HTML_EDITOR_HRULE_DIALOG (editor->priv->hrule_dialog), NULL);
+       gtk_window_present (GTK_WINDOW (editor->priv->hrule_dialog));
 }
 
 static void
@@ -710,19 +416,20 @@ static void
 action_language_cb (GtkToggleAction *toggle_action,
                     EHTMLEditor *editor)
 {
-       ESpellChecker *checker;
-       EHTMLEditorView *view;
+       ESpellChecker *spell_checker;
+       EContentEditor *cnt_editor;
        const gchar *language_code;
        GtkAction *add_action;
        gchar *action_name;
        gboolean active;
 
-       view = e_html_editor_get_view (editor);
-       checker = e_html_editor_view_get_spell_checker (view);
+       cnt_editor = e_html_editor_get_content_editor (editor);
+       spell_checker = e_content_editor_ref_spell_checker (cnt_editor);
        language_code = gtk_action_get_name (GTK_ACTION (toggle_action));
 
        active = gtk_toggle_action_get_active (toggle_action);
-       e_spell_checker_set_language_active (checker, language_code, active);
+       e_spell_checker_set_language_active (spell_checker, language_code, active);
+       g_clear_object (&spell_checker);
 
        /* Update "Add Word To" context menu item visibility. */
        action_name = g_strdup_printf ("context-spell-add-%s", language_code);
@@ -739,15 +446,15 @@ static gboolean
 update_mode_combobox (gpointer data)
 {
        EHTMLEditor *editor = data;
-       EHTMLEditorView *view;
+       EContentEditor *cnt_editor;
        GtkAction *action;
        gboolean is_html;
 
        if (!E_IS_HTML_EDITOR (editor))
                return FALSE;
 
-       view = e_html_editor_get_view (editor);
-       is_html = e_html_editor_view_get_html_mode (view);
+       cnt_editor = e_html_editor_get_content_editor (editor);
+       is_html = e_content_editor_get_html_mode (cnt_editor);
 
        action = gtk_action_group_get_action (
                editor->priv->core_editor_actions, "mode-html");
@@ -762,13 +469,13 @@ action_mode_cb (GtkRadioAction *action,
                 GtkRadioAction *current,
                 EHTMLEditor *editor)
 {
+       EContentEditor *cnt_editor;
        GtkActionGroup *action_group;
-       EHTMLEditorView *view;
        GtkWidget *style_combo_box;
        gboolean is_html;
 
-       view = e_html_editor_get_view (editor);
-       is_html = e_html_editor_view_get_html_mode (view);
+       cnt_editor = e_html_editor_get_content_editor (editor);
+       is_html = e_content_editor_get_html_mode (cnt_editor);
 
        /* This must be done from idle callback, because apparently we can change
         * current value in callback of current value change */
@@ -776,7 +483,6 @@ action_mode_cb (GtkRadioAction *action,
 
        action_group = editor->priv->html_actions;
        gtk_action_group_set_visible (action_group, is_html);
-       gtk_action_group_set_sensitive (action_group, is_html);
 
        action_group = editor->priv->html_context_actions;
        gtk_action_group_set_visible (action_group, is_html);
@@ -814,36 +520,111 @@ static void
 action_paste_cb (GtkAction *action,
                  EHTMLEditor *editor)
 {
-       EHTMLEditorView *view = e_html_editor_get_view (editor);
+       EContentEditor *cnt_editor;
 
-       webkit_web_view_paste_clipboard (WEBKIT_WEB_VIEW (view));
-       e_html_editor_view_force_spell_check_in_viewport (view);
+       cnt_editor = e_html_editor_get_content_editor (editor);
+       e_content_editor_paste (cnt_editor);
+}
+
+static void
+clipboard_text_received_for_paste_as_text (GtkClipboard *clipboard,
+                                           const gchar *text,
+                                           EHTMLEditor *editor)
+{
+       EContentEditor *cnt_editor;
+
+       cnt_editor = e_html_editor_get_content_editor (editor);
+       e_content_editor_insert_content (
+               cnt_editor,
+               text,
+               E_CONTENT_EDITOR_INSERT_CONVERT |
+               E_CONTENT_EDITOR_INSERT_TEXT_PLAIN);
 }
 
 static void
 action_paste_as_text_cb (GtkAction *action,
                          EHTMLEditor *editor)
 {
-       EHTMLEditorView *view = e_html_editor_get_view (editor);
+       EContentEditor *cnt_editor;
+       GtkClipboard *clipboard;
+
+       cnt_editor = e_html_editor_get_content_editor (editor);
+       if (!gtk_widget_has_focus (GTK_WIDGET (cnt_editor)))
+               gtk_widget_grab_focus (GTK_WIDGET (cnt_editor));
+
+       clipboard = gtk_clipboard_get_for_display (
+               gdk_display_get_default (),
+               GDK_SELECTION_CLIPBOARD);
+
+       gtk_clipboard_request_text (
+               clipboard,
+               (GtkClipboardTextReceivedFunc) clipboard_text_received_for_paste_as_text,
+               editor);
+}
+
+static void
+paste_quote_text (EHTMLEditor *editor,
+                 const gchar *text,
+                 gboolean is_html)
+{
+       EContentEditor *cnt_editor;
+
+       g_return_if_fail (E_IS_HTML_EDITOR (editor));
+       g_return_if_fail (text != NULL);
+
+       cnt_editor = e_html_editor_get_content_editor (editor);
+       e_content_editor_insert_content (
+               cnt_editor,
+               text,
+               E_CONTENT_EDITOR_INSERT_QUOTE_CONTENT |
+               (is_html ? E_CONTENT_EDITOR_INSERT_TEXT_HTML : E_CONTENT_EDITOR_INSERT_TEXT_PLAIN));
+}
+
+static void
+clipboard_html_received_for_paste_quote (GtkClipboard *clipboard,
+                                         const gchar *text,
+                                         gpointer user_data)
+{
+       EHTMLEditor *editor = user_data;
 
-       if (!gtk_widget_has_focus (GTK_WIDGET (view)))
-               gtk_widget_grab_focus (GTK_WIDGET (view));
+       g_return_if_fail (E_IS_HTML_EDITOR (editor));
+       g_return_if_fail (text != NULL);
 
-       e_html_editor_view_paste_as_text (view);
-       e_html_editor_view_force_spell_check_in_viewport (view);
+       paste_quote_text (editor, text, TRUE);
+}
+
+static void
+clipboard_text_received_for_paste_quote (GtkClipboard *clipboard,
+                                         const gchar *text,
+                                         gpointer user_data)
+{
+       EHTMLEditor *editor = user_data;
+
+       g_return_if_fail (E_IS_HTML_EDITOR (editor));
+       g_return_if_fail (text != NULL);
+
+       paste_quote_text (editor, text, FALSE);
 }
 
 static void
 action_paste_quote_cb (GtkAction *action,
                        EHTMLEditor *editor)
 {
-       EHTMLEditorView *view = e_html_editor_get_view (editor);
+       EContentEditor *cnt_editor;
+       GtkClipboard *clipboard;
 
-       if (!gtk_widget_has_focus (GTK_WIDGET (view)))
-               gtk_widget_grab_focus (GTK_WIDGET (view));
+       cnt_editor = e_html_editor_get_content_editor (editor);
+       if (!gtk_widget_has_focus (GTK_WIDGET (cnt_editor)))
+               gtk_widget_grab_focus (GTK_WIDGET (cnt_editor));
 
-       e_html_editor_view_paste_clipboard_quoted (view);
-       e_html_editor_view_force_spell_check_in_viewport (view);
+       clipboard = gtk_clipboard_get_for_display (
+               gdk_display_get_default (),
+               GDK_SELECTION_CLIPBOARD);
+
+       if (e_clipboard_wait_is_html_available (clipboard))
+               e_clipboard_request_html (clipboard, clipboard_html_received_for_paste_quote, editor);
+       else if (gtk_clipboard_wait_is_text_available (clipboard))
+               gtk_clipboard_request_text (clipboard, clipboard_text_received_for_paste_quote, editor);
 }
 
 static void
@@ -855,9 +636,7 @@ action_properties_cell_cb (GtkAction *action,
                        e_html_editor_cell_dialog_new (editor);
        }
 
-       e_html_editor_cell_dialog_show (
-               E_HTML_EDITOR_CELL_DIALOG (editor->priv->cell_dialog),
-               editor->priv->table_cell);
+       gtk_window_present (GTK_WINDOW (editor->priv->cell_dialog));
 }
 
 static void
@@ -870,8 +649,7 @@ action_properties_image_cb (GtkAction *action,
        }
 
        e_html_editor_image_dialog_show (
-               E_HTML_EDITOR_IMAGE_DIALOG (editor->priv->image_dialog),
-               editor->priv->image);
+               E_HTML_EDITOR_IMAGE_DIALOG (editor->priv->image_dialog));
 }
 
 static void
@@ -883,9 +661,7 @@ action_properties_link_cb (GtkAction *action,
                        e_html_editor_link_dialog_new (editor);
        }
 
-       e_html_editor_link_dialog_show (
-               E_HTML_EDITOR_LINK_DIALOG (editor->priv->link_dialog),
-               editor->priv->current_node);
+       gtk_window_present (GTK_WINDOW (editor->priv->link_dialog));
 }
 
 static void
@@ -921,9 +697,7 @@ action_properties_rule_cb (GtkAction *action,
                        e_html_editor_hrule_dialog_new (editor);
        }
 
-       e_html_editor_hrule_dialog_show (
-               E_HTML_EDITOR_HRULE_DIALOG (editor->priv->hrule_dialog),
-               editor->priv->current_node);
+       gtk_window_present (GTK_WINDOW (editor->priv->hrule_dialog));
 }
 
 static void
@@ -954,20 +728,22 @@ static void
 action_redo_cb (GtkAction *action,
                 EHTMLEditor *editor)
 {
-       EHTMLEditorView *view = e_html_editor_get_view (editor);
+       EContentEditor *cnt_editor;
 
-       if (gtk_widget_has_focus (GTK_WIDGET (view)))
-               e_html_editor_view_redo (view);
+       cnt_editor = e_html_editor_get_content_editor (editor);
+       if (gtk_widget_has_focus (GTK_WIDGET (cnt_editor)))
+               e_content_editor_redo (cnt_editor);
 }
 
 static void
 action_select_all_cb (GtkAction *action,
                       EHTMLEditor *editor)
 {
-       EHTMLEditorView *view = e_html_editor_get_view (editor);
+       EContentEditor *cnt_editor;
 
-       if (gtk_widget_has_focus (GTK_WIDGET (view)))
-               webkit_web_view_select_all (WEBKIT_WEB_VIEW (view));
+       cnt_editor = e_html_editor_get_content_editor (editor);
+       if (gtk_widget_has_focus (GTK_WIDGET (cnt_editor)))
+               e_content_editor_select_all (cnt_editor);
 }
 
 static void
@@ -1021,43 +797,34 @@ static void
 action_undo_cb (GtkAction *action,
                 EHTMLEditor *editor)
 {
-       EHTMLEditorView *view = e_html_editor_get_view (editor);
+       EContentEditor *cnt_editor;
 
-       if (gtk_widget_has_focus (GTK_WIDGET (view)))
-               e_html_editor_view_undo (view);
+       cnt_editor = e_html_editor_get_content_editor (editor);
+       if (gtk_widget_has_focus (GTK_WIDGET (cnt_editor))) {
+               e_content_editor_undo (cnt_editor);
+       }
 }
 
 static void
 action_unindent_cb (GtkAction *action,
                     EHTMLEditor *editor)
 {
-       EHTMLEditorView *view = e_html_editor_get_view (editor);
+       EContentEditor *cnt_editor;
 
-       if (gtk_widget_has_focus (GTK_WIDGET (view)))
-               e_html_editor_selection_unindent (editor->priv->selection);
+       cnt_editor = e_html_editor_get_content_editor (editor);
+       if (gtk_widget_has_focus (GTK_WIDGET (cnt_editor)))
+               e_content_editor_selection_unindent (cnt_editor);
 }
 
 static void
 action_wrap_lines_cb (GtkAction *action,
                       EHTMLEditor *editor)
 {
-       EHTMLEditorView *view = e_html_editor_get_view (editor);
+       EContentEditor *cnt_editor;
 
-       if (gtk_widget_has_focus (GTK_WIDGET (view)))
-               e_html_editor_selection_wrap_lines (editor->priv->selection);
-}
-
-static void
-action_show_webkit_inspector_cb (GtkAction *action,
-                                 EHTMLEditor *editor)
-{
-       WebKitWebInspector *inspector;
-       EHTMLEditorView *view;
-
-       view = e_html_editor_get_view (editor);
-       inspector = webkit_web_view_get_inspector (WEBKIT_WEB_VIEW (view));
-
-       webkit_web_inspector_show (inspector);
+       cnt_editor = e_html_editor_get_content_editor (editor);
+       if (gtk_widget_has_focus (GTK_WIDGET (cnt_editor)))
+               e_content_editor_selection_wrap (cnt_editor);
 }
 
 /*****************************************************************************
@@ -1239,14 +1006,7 @@ static GtkActionEntry core_editor_entries[] = {
          N_("_Wrap Lines"),
          "<Control>k",
          NULL,
-         G_CALLBACK (action_wrap_lines_cb) },
-
-       { "webkit-inspector",
-          NULL,
-          N_("Open Inspector"),
-          NULL,
-          NULL,
-          G_CALLBACK (action_show_webkit_inspector_cb) },
+         G_CALLBACK (action_wrap_lines_cb) }
 };
 
 static GtkRadioActionEntry core_justify_entries[] = {
@@ -1256,21 +1016,21 @@ static GtkRadioActionEntry core_justify_entries[] = {
          N_("_Center"),
          "<Control>e",
          N_("Center Alignment"),
-         E_HTML_EDITOR_SELECTION_ALIGNMENT_CENTER },
+         E_CONTENT_EDITOR_ALIGNMENT_CENTER },
 
        { "justify-left",
          "format-justify-left",
          N_("_Left"),
          "<Control>l",
          N_("Left Alignment"),
-         E_HTML_EDITOR_SELECTION_ALIGNMENT_LEFT },
+         E_CONTENT_EDITOR_ALIGNMENT_LEFT },
 
        { "justify-right",
          "format-justify-right",
          N_("_Right"),
          "<Control>r",
          N_("Right Alignment"),
-         E_HTML_EDITOR_SELECTION_ALIGNMENT_RIGHT }
+         E_CONTENT_EDITOR_ALIGNMENT_RIGHT }
 };
 
 static GtkRadioActionEntry core_mode_entries[] = {
@@ -1280,14 +1040,14 @@ static GtkRadioActionEntry core_mode_entries[] = {
          N_("_HTML"),
          NULL,
          N_("HTML editing mode"),
-         TRUE },       /* e_html_editor_view_set_html_mode */
+         TRUE },       /* e_content_editor_set_html_mode */
 
        { "mode-plain",
          NULL,
          N_("Plain _Text"),
          NULL,
          N_("Plain text editing mode"),
-         FALSE }       /* e_html_editor_view_set_html_mode */
+         FALSE }       /* e_content_editor_set_html_mode */
 };
 
 static GtkRadioActionEntry core_style_entries[] = {
@@ -1297,98 +1057,91 @@ static GtkRadioActionEntry core_style_entries[] = {
          N_("_Normal"),
          "<Control>0",
          NULL,
-         E_HTML_EDITOR_SELECTION_BLOCK_FORMAT_PARAGRAPH },
+         E_CONTENT_EDITOR_BLOCK_FORMAT_PARAGRAPH },
 
        { "style-h1",
          NULL,
          N_("Header _1"),
          "<Control>1",
          NULL,
-         E_HTML_EDITOR_SELECTION_BLOCK_FORMAT_H1 },
+         E_CONTENT_EDITOR_BLOCK_FORMAT_H1 },
 
        { "style-h2",
          NULL,
          N_("Header _2"),
          "<Control>2",
          NULL,
-         E_HTML_EDITOR_SELECTION_BLOCK_FORMAT_H2 },
+         E_CONTENT_EDITOR_BLOCK_FORMAT_H2 },
 
        { "style-h3",
          NULL,
          N_("Header _3"),
          "<Control>3",
          NULL,
-         E_HTML_EDITOR_SELECTION_BLOCK_FORMAT_H3 },
+         E_CONTENT_EDITOR_BLOCK_FORMAT_H3 },
 
        { "style-h4",
          NULL,
          N_("Header _4"),
          "<Control>4",
          NULL,
-         E_HTML_EDITOR_SELECTION_BLOCK_FORMAT_H4 },
+         E_CONTENT_EDITOR_BLOCK_FORMAT_H4 },
 
        { "style-h5",
          NULL,
          N_("Header _5"),
          "<Control>5",
          NULL,
-         E_HTML_EDITOR_SELECTION_BLOCK_FORMAT_H5 },
+         E_CONTENT_EDITOR_BLOCK_FORMAT_H5 },
 
        { "style-h6",
          NULL,
          N_("Header _6"),
          "<Control>6",
          NULL,
-         E_HTML_EDITOR_SELECTION_BLOCK_FORMAT_H6 },
+         E_CONTENT_EDITOR_BLOCK_FORMAT_H6 },
 
         { "style-preformat",
           NULL,
           N_("_Preformatted"),
           "<Control>7",
           NULL,
-          E_HTML_EDITOR_SELECTION_BLOCK_FORMAT_PRE },
+          E_CONTENT_EDITOR_BLOCK_FORMAT_PRE },
 
        { "style-address",
          NULL,
          N_("A_ddress"),
          "<Control>8",
          NULL,
-         E_HTML_EDITOR_SELECTION_BLOCK_FORMAT_ADDRESS },
-
-        { "style-blockquote",
-          NULL,
-          N_("_Blockquote"),
-          "<Control>9",
-          NULL,
-          E_HTML_EDITOR_SELECTION_BLOCK_FORMAT_BLOCKQUOTE },
+         E_CONTENT_EDITOR_BLOCK_FORMAT_ADDRESS },
 
        { "style-list-bullet",
          NULL,
          N_("_Bulleted List"),
          NULL,
          NULL,
-         E_HTML_EDITOR_SELECTION_BLOCK_FORMAT_UNORDERED_LIST },
+         E_CONTENT_EDITOR_BLOCK_FORMAT_UNORDERED_LIST },
 
        { "style-list-roman",
          NULL,
          N_("_Roman Numeral List"),
          NULL,
          NULL,
-         E_HTML_EDITOR_SELECTION_BLOCK_FORMAT_ORDERED_LIST_ROMAN },
+         E_CONTENT_EDITOR_BLOCK_FORMAT_ORDERED_LIST_ROMAN },
 
        { "style-list-number",
          NULL,
          N_("Numbered _List"),
          NULL,
          NULL,
-         E_HTML_EDITOR_SELECTION_BLOCK_FORMAT_ORDERED_LIST },
+         E_CONTENT_EDITOR_BLOCK_FORMAT_ORDERED_LIST },
 
        { "style-list-alpha",
          NULL,
          N_("_Alphabetical List"),
          NULL,
          NULL,
-         E_HTML_EDITOR_SELECTION_BLOCK_FORMAT_ORDERED_LIST_ALPHA }
+         E_CONTENT_EDITOR_BLOCK_FORMAT_ORDERED_LIST_ALPHA }
 };
 
 /*****************************************************************************
@@ -1549,7 +1302,7 @@ static GtkRadioActionEntry html_size_entries[] = {
          N_("-2"),
          NULL,
          NULL,
-         E_HTML_EDITOR_SELECTION_FONT_SIZE_TINY },
+         E_CONTENT_EDITOR_FONT_SIZE_TINY },
 
        { "size-minus-one",
          NULL,
@@ -1557,7 +1310,7 @@ static GtkRadioActionEntry html_size_entries[] = {
          N_("-1"),
          NULL,
          NULL,
-         E_HTML_EDITOR_SELECTION_FONT_SIZE_SMALL },
+         E_CONTENT_EDITOR_FONT_SIZE_SMALL },
 
        { "size-plus-zero",
          NULL,
@@ -1565,7 +1318,7 @@ static GtkRadioActionEntry html_size_entries[] = {
          N_("+0"),
          NULL,
          NULL,
-         E_HTML_EDITOR_SELECTION_FONT_SIZE_NORMAL },
+         E_CONTENT_EDITOR_FONT_SIZE_NORMAL },
 
        { "size-plus-one",
          NULL,
@@ -1573,7 +1326,7 @@ static GtkRadioActionEntry html_size_entries[] = {
          N_("+1"),
          NULL,
          NULL,
-         E_HTML_EDITOR_SELECTION_FONT_SIZE_BIG },
+         E_CONTENT_EDITOR_FONT_SIZE_BIG },
 
        { "size-plus-two",
          NULL,
@@ -1581,7 +1334,7 @@ static GtkRadioActionEntry html_size_entries[] = {
          N_("+2"),
          NULL,
          NULL,
-         E_HTML_EDITOR_SELECTION_FONT_SIZE_BIGGER },
+         E_CONTENT_EDITOR_FONT_SIZE_BIGGER },
 
        { "size-plus-three",
          NULL,
@@ -1589,7 +1342,7 @@ static GtkRadioActionEntry html_size_entries[] = {
          N_("+3"),
          NULL,
          NULL,
-         E_HTML_EDITOR_SELECTION_FONT_SIZE_LARGE },
+         E_CONTENT_EDITOR_FONT_SIZE_LARGE },
 
        { "size-plus-four",
          NULL,
@@ -1597,7 +1350,7 @@ static GtkRadioActionEntry html_size_entries[] = {
          N_("+4"),
          NULL,
          NULL,
-         E_HTML_EDITOR_SELECTION_FONT_SIZE_VERY_LARGE }
+         E_CONTENT_EDITOR_FONT_SIZE_VERY_LARGE }
 };
 
 /*****************************************************************************
@@ -1649,13 +1402,6 @@ static GtkActionEntry context_entries[] = {
          NULL,
          NULL },
 
-       { "context-input-methods-menu",
-         NULL,
-         N_("Input Methods"),
-         NULL,
-         NULL,
-         NULL },
-
        { "context-insert-table-menu",
          NULL,
          /* Translators: Popup menu item caption, containing all the Insert options for a table */
@@ -1717,13 +1463,6 @@ static GtkActionEntry html_context_entries[] = {
          NULL,
          G_CALLBACK (action_context_insert_row_below_cb) },
 
-       { "context-insert-table",
-         NULL,
-         N_("Table"),
-         NULL,
-         NULL,
-         G_CALLBACK (action_insert_table_cb) },
-
        { "context-properties-cell",
          NULL,
          N_("Cell..."),
@@ -1832,27 +1571,27 @@ static GtkActionEntry spell_context_entries[] = {
 static void
 editor_actions_setup_languages_menu (EHTMLEditor *editor)
 {
-       ESpellChecker *checker;
-       EHTMLEditorView *view;
+       ESpellChecker *spell_checker;
+       EContentEditor *cnt_editor;
        GtkUIManager *manager;
        GtkActionGroup *action_group;
-       GList *list, *link;
+       GList *list = NULL, *link;
        guint merge_id;
 
        manager = editor->priv->manager;
        action_group = editor->priv->language_actions;
-       view = e_html_editor_get_view (editor);
-       checker = e_html_editor_view_get_spell_checker (view);
+       cnt_editor = e_html_editor_get_content_editor (editor);
+       spell_checker = e_content_editor_ref_spell_checker (cnt_editor);
        merge_id = gtk_ui_manager_new_merge_id (manager);
 
-       list = e_spell_checker_list_available_dicts (checker);
+       list = e_spell_checker_list_available_dicts (spell_checker);
 
        for (link = list; link != NULL; link = g_list_next (link)) {
                ESpellDictionary *dictionary = link->data;
                GtkToggleAction *action;
                const gchar *language_name;
                GString *escaped_name = NULL;
-               gboolean active;
+               gboolean active = FALSE;
 
                language_name = e_spell_dictionary_get_name (dictionary);
                if (language_name && strchr (language_name, '_') != NULL)
@@ -1870,7 +1609,7 @@ editor_actions_setup_languages_menu (EHTMLEditor *editor)
                 * We're not prepared to invoke the signal handler yet.
                 * The "Add Word To" actions have not yet been added. */
                active = e_spell_checker_get_language_active (
-                       checker, e_spell_dictionary_get_code (dictionary));
+                       spell_checker, e_spell_dictionary_get_code (dictionary));
                gtk_toggle_action_set_active (action, active);
 
                g_signal_connect (
@@ -1891,21 +1630,24 @@ editor_actions_setup_languages_menu (EHTMLEditor *editor)
        }
 
        g_list_free (list);
+       g_clear_object (&spell_checker);
 }
 
 static void
 editor_actions_setup_spell_check_menu (EHTMLEditor *editor)
 {
-       ESpellChecker *checker;
+       EContentEditor *cnt_editor;
+       ESpellChecker *spell_checker;
        GtkUIManager *manager;
        GtkActionGroup *action_group;
-       GList *available_dicts, *iter;
+       GList *available_dicts = NULL, *iter;
        guint merge_id;
 
        manager = editor->priv->manager;
        action_group = editor->priv->spell_check_actions;;
-       checker = e_html_editor_view_get_spell_checker (editor->priv->html_editor_view);
-       available_dicts = e_spell_checker_list_available_dicts (checker);
+       cnt_editor = e_html_editor_get_content_editor (editor);
+       spell_checker = e_content_editor_ref_spell_checker (cnt_editor);
+       available_dicts = e_spell_checker_list_available_dicts (spell_checker);
        merge_id = gtk_ui_manager_new_merge_id (manager);
 
        for (iter = available_dicts; iter; iter = iter->next) {
@@ -1955,7 +1697,7 @@ editor_actions_setup_spell_check_menu (EHTMLEditor *editor)
 
                /* Visibility is dependent on whether the
                 * corresponding language action is active. */
-               gtk_action_set_visible (action, e_spell_checker_get_language_active (checker, code));
+               gtk_action_set_visible (action, FALSE);
 
                gtk_action_group_add_action (action_group, action);
 
@@ -1975,6 +1717,7 @@ editor_actions_setup_spell_check_menu (EHTMLEditor *editor)
        }
 
        g_list_free (available_dicts);
+       g_clear_object (&spell_checker);
 }
 
 void
@@ -1984,14 +1727,11 @@ editor_actions_init (EHTMLEditor *editor)
        GtkActionGroup *action_group;
        GtkUIManager *manager;
        const gchar *domain;
-       EHTMLEditorView *view;
-       GSettings *settings;
 
        g_return_if_fail (E_IS_HTML_EDITOR (editor));
 
        manager = e_html_editor_get_ui_manager (editor);
        domain = GETTEXT_PACKAGE;
-       view = e_html_editor_get_view (editor);
 
        /* Core Actions */
        action_group = editor->priv->core_actions;
@@ -2009,7 +1749,7 @@ editor_actions_init (EHTMLEditor *editor)
        gtk_action_group_add_radio_actions (
                action_group, core_justify_entries,
                G_N_ELEMENTS (core_justify_entries),
-               E_HTML_EDITOR_SELECTION_ALIGNMENT_LEFT,
+               E_CONTENT_EDITOR_ALIGNMENT_LEFT,
                NULL, NULL);
        gtk_action_group_add_radio_actions (
                action_group, core_mode_entries,
@@ -2019,19 +1759,10 @@ editor_actions_init (EHTMLEditor *editor)
        gtk_action_group_add_radio_actions (
                action_group, core_style_entries,
                G_N_ELEMENTS (core_style_entries),
-               E_HTML_EDITOR_SELECTION_BLOCK_FORMAT_PARAGRAPH,
+               E_CONTENT_EDITOR_BLOCK_FORMAT_PARAGRAPH,
                NULL, NULL);
        gtk_ui_manager_insert_action_group (manager, action_group, 0);
 
-       action = gtk_action_group_get_action (action_group, "mode-html");
-       e_binding_bind_property (
-               view, "html-mode",
-               action, "current-value",
-               G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE);
-
-       /* Synchronize wiget mode with the buttons */
-       e_html_editor_view_set_html_mode (view, TRUE);
-
        /* Face Action */
        action = e_emoticon_action_new (
                "insert-emoticon", _("_Emoticon"),
@@ -2055,7 +1786,7 @@ editor_actions_init (EHTMLEditor *editor)
        gtk_action_group_add_radio_actions (
                action_group, html_size_entries,
                G_N_ELEMENTS (html_size_entries),
-               E_HTML_EDITOR_SELECTION_FONT_SIZE_NORMAL,
+               E_CONTENT_EDITOR_FONT_SIZE_NORMAL,
                NULL, NULL);
        gtk_ui_manager_insert_action_group (manager, action_group, 0);
 
@@ -2120,92 +1851,113 @@ editor_actions_init (EHTMLEditor *editor)
 
        gtk_action_set_sensitive (ACTION (UNINDENT), FALSE);
        gtk_action_set_sensitive (ACTION (FIND_AGAIN), FALSE);
+}
+
+void
+editor_actions_bind (EHTMLEditor *editor)
+{
+       GtkAction *action;
+       GtkActionGroup *action_group;
+       EContentEditor *cnt_editor;
 
+       g_return_if_fail (E_IS_HTML_EDITOR (editor));
+
+       cnt_editor = e_html_editor_get_content_editor (editor);
+
+       action_group = editor->priv->core_editor_actions;
+       action = gtk_action_group_get_action (action_group, "mode-html");
        e_binding_bind_property (
-               view, "can-redo",
+               cnt_editor, "html-mode",
+               action, "current-value",
+               G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE);
+
+       /* Synchronize widget mode with the buttons */
+       e_content_editor_set_html_mode (cnt_editor, TRUE);
+
+       e_binding_bind_property (
+               cnt_editor, "can-redo",
                ACTION (REDO), "sensitive",
                G_BINDING_SYNC_CREATE);
        e_binding_bind_property (
-               view, "can-undo",
+               cnt_editor, "can-undo",
                ACTION (UNDO), "sensitive",
                G_BINDING_SYNC_CREATE);
        e_binding_bind_property (
-               view, "can-copy",
+               cnt_editor, "can-copy",
                ACTION (COPY), "sensitive",
                G_BINDING_SYNC_CREATE);
        e_binding_bind_property (
-               view, "can-cut",
+               cnt_editor, "can-cut",
                ACTION (CUT), "sensitive",
                G_BINDING_SYNC_CREATE);
        e_binding_bind_property (
-               view, "can-paste",
+               cnt_editor, "can-paste",
                ACTION (PASTE), "sensitive",
                G_BINDING_SYNC_CREATE);
        e_binding_bind_property (
-               view, "can-paste",
+               cnt_editor, "can-paste",
                ACTION (PASTE_QUOTE), "sensitive",
                G_BINDING_SYNC_CREATE);
 
        /* This is connected to JUSTIFY_LEFT action only, but
         * it automatically applies on all actions in the group. */
        e_binding_bind_property (
-               editor->priv->selection, "alignment",
+               cnt_editor, "alignment",
                ACTION (JUSTIFY_LEFT), "current-value",
                G_BINDING_SYNC_CREATE | G_BINDING_BIDIRECTIONAL);
        e_binding_bind_property (
-               editor->priv->selection, "bold",
+               cnt_editor, "bold",
                ACTION (BOLD), "active",
                G_BINDING_SYNC_CREATE | G_BINDING_BIDIRECTIONAL);
        e_binding_bind_property (
-               editor->priv->selection, "font-size",
+               cnt_editor, "font-size",
                ACTION (FONT_SIZE_GROUP), "current-value",
                G_BINDING_SYNC_CREATE | G_BINDING_BIDIRECTIONAL);
        e_binding_bind_property (
-               editor->priv->selection, "block-format",
+               cnt_editor, "block-format",
                ACTION (STYLE_NORMAL), "current-value",
                G_BINDING_SYNC_CREATE | G_BINDING_BIDIRECTIONAL);
        e_binding_bind_property (
-               editor->priv->selection, "indented",
+               cnt_editor, "indented",
                ACTION (UNINDENT), "sensitive",
                G_BINDING_SYNC_CREATE);
        e_binding_bind_property (
-               editor->priv->selection, "italic",
+               cnt_editor, "italic",
                ACTION (ITALIC), "active",
                G_BINDING_SYNC_CREATE | G_BINDING_BIDIRECTIONAL);
        e_binding_bind_property (
-               editor->priv->selection, "monospaced",
+               cnt_editor, "monospaced",
                ACTION (MONOSPACED), "active",
                G_BINDING_SYNC_CREATE | G_BINDING_BIDIRECTIONAL);
        e_binding_bind_property (
-               editor->priv->selection, "strikethrough",
+               cnt_editor, "strikethrough",
                ACTION (STRIKETHROUGH), "active",
                G_BINDING_SYNC_CREATE | G_BINDING_BIDIRECTIONAL);
        e_binding_bind_property (
-               editor->priv->selection, "underline",
+               cnt_editor, "underline",
                ACTION (UNDERLINE), "active",
                G_BINDING_SYNC_CREATE | G_BINDING_BIDIRECTIONAL);
 
+       e_binding_bind_property (
+               cnt_editor, "html-mode",
+               editor->priv->html_actions, "sensitive",
+               G_BINDING_SYNC_CREATE);
+
        /* Disable all actions and toolbars when editor is not editable */
        e_binding_bind_property (
-               view, "editable",
+               cnt_editor, "editable",
                editor->priv->core_editor_actions, "sensitive",
                G_BINDING_SYNC_CREATE);
        e_binding_bind_property (
-               view, "editable",
+               cnt_editor, "editable",
                editor->priv->html_actions, "sensitive",
                G_BINDING_SYNC_CREATE);
        e_binding_bind_property (
-               view, "editable",
+               cnt_editor, "editable",
                editor->priv->spell_check_actions, "sensitive",
                G_BINDING_SYNC_CREATE);
        e_binding_bind_property (
-               view, "editable",
+               cnt_editor, "editable",
                editor->priv->suggestion_actions, "sensitive",
                G_BINDING_SYNC_CREATE);
-
-       settings = e_util_ref_settings ("org.gnome.evolution.mail");
-       gtk_action_set_visible (
-               ACTION (WEBKIT_INSPECTOR),
-               g_settings_get_boolean (settings, "composer-developer-mode"));
-       g_object_unref (settings);
 }
diff --git a/e-util/e-html-editor-actions.h b/e-util/e-html-editor-actions.h
index 6991503..f724db0 100644
--- a/e-util/e-html-editor-actions.h
+++ b/e-util/e-html-editor-actions.h
@@ -47,8 +47,6 @@
        E_HTML_EDITOR_ACTION ((editor), "context-insert-row-above")
 #define E_HTML_EDITOR_ACTION_CONTEXT_INSERT_ROW_BELOW(editor) \
        E_HTML_EDITOR_ACTION ((editor), "context-insert-row-below")
-#define E_HTML_EDITOR_ACTION_CONTEXT_INSERT_TABLE(editor) \
-       E_HTML_EDITOR_ACTION ((editor), "context-insert-table")
 #define E_HTML_EDITOR_ACTION_CONTEXT_PROPERTIES_CELL(editor) \
        E_HTML_EDITOR_ACTION ((editor), "context-properties-cell")
 #define E_HTML_EDITOR_ACTION_CONTEXT_PROPERTIES_IMAGE(editor) \
@@ -85,6 +83,8 @@
        E_HTML_EDITOR_ACTION ((editor), "format-menu")
 #define E_HTML_EDITOR_ACTION_FORMAT_TEXT(editor) \
        E_HTML_EDITOR_ACTION ((editor), "format-text")
+#define E_HTML_EDITOR_ACTION_INSERT_EMOTICON(editor) \
+       E_HTML_EDITOR_ACTION ((editor), "insert-emoticon")
 #define E_HTML_EDITOR_ACTION_INSERT_IMAGE(editor) \
        E_HTML_EDITOR_ACTION ((editor), "insert-image")
 #define E_HTML_EDITOR_ACTION_INSERT_LINK(editor) \
@@ -157,7 +157,7 @@
        E_HTML_EDITOR_ACTION ((editor), "undo")
 #define E_HTML_EDITOR_ACTION_UNINDENT(editor) \
        E_HTML_EDITOR_ACTION ((editor), "unindent")
-#define E_HTML_EDITOR_ACTION_WEBKIT_INSPECTOR(editor) \
-       E_HTML_EDITOR_ACTION ((editor), "webkit-inspector")
+#define E_HTML_EDITOR_ACTION_WRAP_LINES(editor) \
+       E_HTML_EDITOR_ACTION ((editor), "wrap-lines")
 
 #endif /* E_HTML_EDITOR_ACTIONS_H */
diff --git a/e-util/e-html-editor-cell-dialog.c b/e-util/e-html-editor-cell-dialog.c
index 0ee0da2..b5379ec 100644
--- a/e-util/e-html-editor-cell-dialog.c
+++ b/e-util/e-html-editor-cell-dialog.c
@@ -29,10 +29,8 @@
 
 #include "e-color-combo.h"
 #include "e-dialog-widgets.h"
-#include "e-html-editor-utils.h"
 #include "e-image-chooser-dialog.h"
 #include "e-misc-utils.h"
-#include "e-misc-utils.h"
 
 #define E_HTML_EDITOR_CELL_DIALOG_GET_PRIVATE(obj) \
        (G_TYPE_INSTANCE_GET_PRIVATE \
@@ -62,10 +60,7 @@ struct _EHTMLEditorCellDialogPrivate {
 
        GtkWidget *remove_image_button;
 
-       WebKitDOMElement *cell;
        guint scope;
-
-       EHTMLEditorViewHistoryEvent *history_event;
 };
 
 enum {
@@ -77,151 +72,12 @@ enum {
 
 static GdkRGBA transparent = { 0, 0, 0, 0 };
 
-typedef void (*DOMStrFunc) (WebKitDOMHTMLTableCellElement *cell, const gchar *val, gpointer user_data);
-typedef void (*DOMUlongFunc) (WebKitDOMHTMLTableCellElement *cell, gulong val, gpointer user_data);
-typedef void (*DOMBoolFunc) (WebKitDOMHTMLTableCellElement *cell, gboolean val, gpointer user_data);
-
 G_DEFINE_TYPE (
        EHTMLEditorCellDialog,
        e_html_editor_cell_dialog,
        E_TYPE_HTML_EDITOR_DIALOG);
 
 static void
-call_cell_dom_func (WebKitDOMHTMLTableCellElement *cell,
-                    gpointer func,
-                    GValue *value,
-                    gpointer user_data)
-{
-       if (G_VALUE_HOLDS_STRING (value)) {
-               DOMStrFunc f = func;
-               f (cell, g_value_get_string (value), user_data);
-       } else if (G_VALUE_HOLDS_ULONG (value)) {
-               DOMUlongFunc f = func;
-               f (cell, g_value_get_ulong (value), user_data);
-       } else if (G_VALUE_HOLDS_BOOLEAN (value)) {
-               DOMBoolFunc f = func;
-               f (cell, g_value_get_boolean (value), user_data);
-       }
-}
-
-static void
-for_each_cell_do (WebKitDOMElement *row,
-                  gpointer func,
-                  GValue *value,
-                  gpointer user_data)
-{
-       WebKitDOMHTMLCollection *cells;
-       gulong ii, length;
-       cells = webkit_dom_html_table_row_element_get_cells (
-                       WEBKIT_DOM_HTML_TABLE_ROW_ELEMENT (row));
-       length = webkit_dom_html_collection_get_length (cells);
-       for (ii = 0; ii < length; ii++) {
-               WebKitDOMNode *cell;
-               cell = webkit_dom_html_collection_item (cells, ii);
-               if (!cell) {
-                       continue;
-               }
-
-               call_cell_dom_func (
-                       WEBKIT_DOM_HTML_TABLE_CELL_ELEMENT (cell), func, value, user_data);
-               g_object_unref (cell);
-       }
-       g_object_unref (cells);
-}
-
-static void
-html_editor_cell_dialog_set_attribute (EHTMLEditorCellDialog *dialog,
-                                       gpointer func,
-                                       GValue *value,
-                                       gpointer user_data)
-{
-       if (dialog->priv->scope == SCOPE_CELL) {
-
-               call_cell_dom_func (
-                       WEBKIT_DOM_HTML_TABLE_CELL_ELEMENT (dialog->priv->cell),
-                       func, value, user_data);
-
-       } else if (dialog->priv->scope == SCOPE_COLUMN) {
-               gulong index, ii, length;
-               WebKitDOMElement *table;
-               WebKitDOMHTMLCollection *rows;
-
-               index = webkit_dom_html_table_cell_element_get_cell_index (
-                               WEBKIT_DOM_HTML_TABLE_CELL_ELEMENT (dialog->priv->cell));
-               table = e_html_editor_dom_node_find_parent_element (
-                               WEBKIT_DOM_NODE (dialog->priv->cell), "TABLE");
-               if (!table) {
-                       return;
-               }
-
-               rows = webkit_dom_html_table_element_get_rows (
-                               WEBKIT_DOM_HTML_TABLE_ELEMENT (table));
-               length = webkit_dom_html_collection_get_length (rows);
-               for (ii = 0; ii < length; ii++) {
-                       WebKitDOMNode *row, *cell;
-                       WebKitDOMHTMLCollection *cells;
-
-                       row = webkit_dom_html_collection_item (rows, ii);
-                       cells = webkit_dom_html_table_row_element_get_cells (
-                                       WEBKIT_DOM_HTML_TABLE_ROW_ELEMENT (row));
-                       cell = webkit_dom_html_collection_item (cells, index);
-                       if (!cell) {
-                               g_object_unref (row);
-                               g_object_unref (cells);
-                               continue;
-                       }
-
-                       call_cell_dom_func (
-                               WEBKIT_DOM_HTML_TABLE_CELL_ELEMENT (cell),
-                               func, value, user_data);
-                       g_object_unref (row);
-                       g_object_unref (cells);
-                       g_object_unref (cell);
-               }
-               g_object_unref (rows);
-
-       } else if (dialog->priv->scope == SCOPE_ROW) {
-               WebKitDOMElement *row;
-
-               row = e_html_editor_dom_node_find_parent_element (
-                               WEBKIT_DOM_NODE (dialog->priv->cell), "TR");
-               if (!row) {
-                       return;
-               }
-
-               for_each_cell_do (row, func, value, user_data);
-
-       } else if (dialog->priv->scope == SCOPE_TABLE) {
-               gulong ii, length;
-               WebKitDOMElement *table;
-               WebKitDOMHTMLCollection *rows;
-
-               table = e_html_editor_dom_node_find_parent_element (
-                               WEBKIT_DOM_NODE (dialog->priv->cell), "TABLE");
-               if (!table) {
-                       return;
-               }
-
-               rows = webkit_dom_html_table_element_get_rows (
-                               WEBKIT_DOM_HTML_TABLE_ELEMENT (table));
-               length = webkit_dom_html_collection_get_length (rows);
-               for (ii = 0; ii < length; ii++) {
-                       WebKitDOMNode *row;
-
-                       row = webkit_dom_html_collection_item (rows, ii);
-                       if (!row) {
-                               continue;
-                       }
-
-                       for_each_cell_do (
-                               WEBKIT_DOM_ELEMENT (row), func, value, user_data);
-                       g_object_unref (row);
-               }
-               g_object_unref (rows);
-       }
-}
-
-static void
 html_editor_cell_dialog_set_scope (EHTMLEditorCellDialog *dialog)
 {
        if (gtk_toggle_button_get_active (
@@ -250,249 +106,184 @@ html_editor_cell_dialog_set_scope (EHTMLEditorCellDialog *dialog)
 static  void
 html_editor_cell_dialog_set_valign (EHTMLEditorCellDialog *dialog)
 {
-       GValue val = { 0 };
-
-       g_value_init (&val, G_TYPE_STRING);
-       g_value_set_string (
-               &val,
-               gtk_combo_box_get_active_id (
-                       GTK_COMBO_BOX (dialog->priv->valign_combo)));
+       EHTMLEditor *editor;
+       EContentEditor *cnt_editor;
 
-       html_editor_cell_dialog_set_attribute (
-               dialog, webkit_dom_html_table_cell_element_set_v_align, &val, NULL);
+       editor = e_html_editor_dialog_get_editor (E_HTML_EDITOR_DIALOG (dialog));
+       cnt_editor = e_html_editor_get_content_editor (editor);
 
-       g_value_unset (&val);
+       e_content_editor_cell_set_v_align (
+               cnt_editor,
+               gtk_combo_box_get_active_id (
+                       GTK_COMBO_BOX (dialog->priv->valign_combo)),
+               dialog->priv->scope);
 }
 
 static void
 html_editor_cell_dialog_set_halign (EHTMLEditorCellDialog *dialog)
 {
-       GValue val = { 0 };
-
-       g_value_init (&val, G_TYPE_STRING);
-       g_value_set_string (
-               &val,
-               gtk_combo_box_get_active_id (
-                       GTK_COMBO_BOX (dialog->priv->halign_combo)));
+       EHTMLEditor *editor;
+       EContentEditor *cnt_editor;
 
-       html_editor_cell_dialog_set_attribute (
-               dialog, webkit_dom_html_table_cell_element_set_align, &val, NULL);
+       editor = e_html_editor_dialog_get_editor (E_HTML_EDITOR_DIALOG (dialog));
+       cnt_editor = e_html_editor_get_content_editor (editor);
 
-       g_value_unset (&val);
+       e_content_editor_cell_set_align (
+               cnt_editor,
+               gtk_combo_box_get_active_id (
+                       GTK_COMBO_BOX (dialog->priv->halign_combo)),
+               dialog->priv->scope);
 }
 
 static void
 html_editor_cell_dialog_set_wrap_text (EHTMLEditorCellDialog *dialog)
 {
-       GValue val = { 0 };
-
-       g_value_init (&val, G_TYPE_BOOLEAN);
-       g_value_set_boolean (
-               &val,
-               !gtk_toggle_button_get_active (
-                       GTK_TOGGLE_BUTTON (dialog->priv->wrap_text_check)));
-
-       html_editor_cell_dialog_set_attribute (
-               dialog, webkit_dom_html_table_cell_element_set_no_wrap, &val, NULL);
-}
-
-static void
-cell_set_header_style (WebKitDOMHTMLTableCellElement *cell,
-                       gboolean header_style,
-                       gpointer user_data)
-{
-       EHTMLEditorCellDialog *dialog = user_data;
-       WebKitDOMDocument *document;
-       WebKitDOMNodeList *nodes;
-       WebKitDOMElement *new_cell;
-       gulong length, ii;
-       gchar *tagname;
-
-       document = webkit_dom_node_get_owner_document (WEBKIT_DOM_NODE (cell));
-       tagname = webkit_dom_element_get_tag_name (WEBKIT_DOM_ELEMENT (cell));
-
-       if (header_style && (g_ascii_strncasecmp (tagname, "TD", 2) == 0)) {
-
-               new_cell = webkit_dom_document_create_element (document, "TH", NULL);
-
-       } else if (!header_style && (g_ascii_strncasecmp (tagname, "TH", 2) == 0)) {
-
-               new_cell = webkit_dom_document_create_element (document, "TD", NULL);
-
-       } else {
-               g_free (tagname);
-               return;
-       }
-
-       /* Move all child nodes from cell to new_cell */
-       nodes = webkit_dom_node_get_child_nodes (WEBKIT_DOM_NODE (cell));
-       length = webkit_dom_node_list_get_length (nodes);
-       for (ii = 0; ii < length; ii++) {
-               WebKitDOMNode *node;
-
-               node = webkit_dom_node_list_item (nodes, ii);
-               webkit_dom_node_append_child (
-                       WEBKIT_DOM_NODE (new_cell), node, NULL);
-               g_object_unref (node);
-       }
-       g_object_unref (nodes);
-
-       /* Insert new_cell before cell and remove cell */
-       webkit_dom_node_insert_before (
-               webkit_dom_node_get_parent_node (
-                       WEBKIT_DOM_NODE (cell)),
-               WEBKIT_DOM_NODE (new_cell),
-               WEBKIT_DOM_NODE (cell), NULL);
-
-       webkit_dom_node_remove_child (
-               webkit_dom_node_get_parent_node (WEBKIT_DOM_NODE (cell)),
-               WEBKIT_DOM_NODE (cell), NULL);
+       EHTMLEditor *editor;
+       EContentEditor *cnt_editor;
 
-       dialog->priv->cell = new_cell;
+       editor = e_html_editor_dialog_get_editor (E_HTML_EDITOR_DIALOG (dialog));
+       cnt_editor = e_html_editor_get_content_editor (editor);
 
-       g_free (tagname);
+       e_content_editor_cell_set_wrap (
+               cnt_editor,
+               gtk_toggle_button_get_active (
+                       GTK_TOGGLE_BUTTON (dialog->priv->wrap_text_check)),
+               dialog->priv->scope);
 }
 
 static void
 html_editor_cell_dialog_set_header_style (EHTMLEditorCellDialog *dialog)
 {
-       GValue val = { 0 };
+       EHTMLEditor *editor;
+       EContentEditor *cnt_editor;
 
-       g_value_init (&val, G_TYPE_BOOLEAN);
-       g_value_set_boolean (
-               &val,
-               gtk_toggle_button_get_active (
-                       GTK_TOGGLE_BUTTON (dialog->priv->header_style_check)));
+       editor = e_html_editor_dialog_get_editor (E_HTML_EDITOR_DIALOG (dialog));
+       cnt_editor = e_html_editor_get_content_editor (editor);
 
-       html_editor_cell_dialog_set_attribute (
-               dialog, cell_set_header_style, &val, dialog);
+       e_content_editor_cell_set_header_style (
+               cnt_editor,
+               gtk_toggle_button_get_active (
+                       GTK_TOGGLE_BUTTON (dialog->priv->header_style_check)),
+               dialog->priv->scope);
 }
 
 static void
 html_editor_cell_dialog_set_width (EHTMLEditorCellDialog *dialog)
 {
-       GValue val = { 0 };
-       gchar *width;
+       EHTMLEditor *editor;
+       EContentEditor *cnt_editor;
 
-       if (!gtk_toggle_button_get_active (
-               GTK_TOGGLE_BUTTON (dialog->priv->width_check))) {
+       editor = e_html_editor_dialog_get_editor (E_HTML_EDITOR_DIALOG (dialog));
+       cnt_editor = e_html_editor_get_content_editor (editor);
 
-               width = g_strdup ("auto");
-       } else {
+       if (gtk_toggle_button_get_active (
+               GTK_TOGGLE_BUTTON (dialog->priv->width_check))) {
 
-               width = g_strdup_printf (
-                       "%d%s",
+               e_content_editor_cell_set_width (
+                       cnt_editor,
                        gtk_spin_button_get_value_as_int (
                                GTK_SPIN_BUTTON (dialog->priv->width_edit)),
-                       ((gtk_combo_box_get_active (
+                       (gtk_combo_box_get_active (
                                GTK_COMBO_BOX (dialog->priv->width_units)) == 0) ?
-                                       "px" : "%"));
-       }
+                                       E_CONTENT_EDITOR_UNIT_PIXEL :
+                                       E_CONTENT_EDITOR_UNIT_PERCENTAGE,
+                       dialog->priv->scope);
+       } else
+               e_content_editor_cell_set_width (
+                       cnt_editor, 0, E_CONTENT_EDITOR_UNIT_AUTO, dialog->priv->scope);
+}
 
-       g_value_init (&val, G_TYPE_STRING);
-       g_value_take_string (&val, width);
-       html_editor_cell_dialog_set_attribute (
-               dialog, webkit_dom_html_table_cell_element_set_width, &val, NULL);
+static void
+html_editor_cell_dialog_width_units_changed (GtkWidget *widget,
+                                             EHTMLEditorCellDialog *dialog)
+{
+       if (gtk_combo_box_get_active (GTK_COMBO_BOX (dialog->priv->width_units)) == 0) {
+               gtk_spin_button_set_range (
+                       GTK_SPIN_BUTTON (dialog->priv->width_edit), 0, G_MAXUINT);
+       } else
+               gtk_spin_button_set_range (
+                       GTK_SPIN_BUTTON (dialog->priv->width_edit), 0, 100);
 
-       g_free (width);
+       html_editor_cell_dialog_set_width (dialog);
 }
 
 static void
 html_editor_cell_dialog_set_column_span (EHTMLEditorCellDialog *dialog)
 {
-       GValue val = { 0 };
+       EHTMLEditor *editor;
+       EContentEditor *cnt_editor;
 
-       g_value_init (&val, G_TYPE_ULONG);
-       g_value_set_ulong (
-               &val,
-               gtk_spin_button_get_value_as_int (
-                       GTK_SPIN_BUTTON (dialog->priv->col_span_edit)));
+       editor = e_html_editor_dialog_get_editor (E_HTML_EDITOR_DIALOG (dialog));
+       cnt_editor = e_html_editor_get_content_editor (editor);
 
-       html_editor_cell_dialog_set_attribute (
-               dialog, webkit_dom_html_table_cell_element_set_col_span, &val, NULL);
+       e_content_editor_cell_set_col_span (
+               cnt_editor,
+               gtk_spin_button_get_value_as_int (
+                       GTK_SPIN_BUTTON (dialog->priv->col_span_edit)),
+               dialog->priv->scope);
 }
 
 static void
 html_editor_cell_dialog_set_row_span (EHTMLEditorCellDialog *dialog)
 {
-       GValue val = { 0 };
+       EHTMLEditor *editor;
+       EContentEditor *cnt_editor;
 
-       g_value_init (&val, G_TYPE_ULONG);
-       g_value_set_ulong (
-               &val,
-               gtk_spin_button_get_value_as_int (
-                       GTK_SPIN_BUTTON (dialog->priv->row_span_edit)));
+       editor = e_html_editor_dialog_get_editor (E_HTML_EDITOR_DIALOG (dialog));
+       cnt_editor = e_html_editor_get_content_editor (editor);
 
-       html_editor_cell_dialog_set_attribute (
-               dialog, webkit_dom_html_table_cell_element_set_row_span, &val, NULL);
+       e_content_editor_cell_set_row_span (
+               cnt_editor,
+               gtk_spin_button_get_value_as_int (
+                       GTK_SPIN_BUTTON (dialog->priv->row_span_edit)),
+               dialog->priv->scope);
 }
 
 static void
 html_editor_cell_dialog_set_background_color (EHTMLEditorCellDialog *dialog)
 {
-       gchar *color = NULL;
+       EHTMLEditor *editor;
+       EContentEditor *cnt_editor;
        GdkRGBA rgba;
-       GValue val = { 0 };
+
+       editor = e_html_editor_dialog_get_editor (E_HTML_EDITOR_DIALOG (dialog));
+       cnt_editor = e_html_editor_get_content_editor (editor);
 
        e_color_combo_get_current_color (
                E_COLOR_COMBO (dialog->priv->background_color_picker), &rgba);
-       if (rgba.alpha != 0.0)
-               color = g_strdup_printf ("#%06x", e_rgba_to_value (&rgba));
-       else
-               color = g_strdup ("");
-
-       g_value_init (&val, G_TYPE_STRING);
-       g_value_take_string (&val, color);
-
-       html_editor_cell_dialog_set_attribute (
-               dialog, webkit_dom_html_table_cell_element_set_bg_color, &val, NULL);
-
-       g_free (color);
+       e_content_editor_cell_set_background_color (cnt_editor, &rgba, dialog->priv->scope);
 }
 
 static void
-cell_set_background_image (WebKitDOMHTMLTableCellElement *cell,
-                           const gchar *uri,
-                           EHTMLEditorCellDialog *dialog)
+html_editor_cell_dialog_set_background_image (EHTMLEditorCellDialog *dialog)
 {
        EHTMLEditor *editor;
-       EHTMLEditorView *view;
+       EContentEditor *cnt_editor;
+       gchar *uri;
 
        editor = e_html_editor_dialog_get_editor (E_HTML_EDITOR_DIALOG (dialog));
-       view = e_html_editor_get_view (editor);
-
-       if (uri && *uri) {
-               e_html_editor_selection_replace_image_src (
-                       e_html_editor_view_get_selection (view),
-                       WEBKIT_DOM_ELEMENT (cell),
-                       uri);
-       } else
-               remove_image_attributes_from_element (WEBKIT_DOM_ELEMENT (cell));
-
-       gtk_widget_set_sensitive (dialog->priv->remove_image_button, uri && *uri);
-}
-
-static void
-html_editor_cell_dialog_set_background_image (EHTMLEditorCellDialog *dialog)
-{
-       gchar *uri;
-       GValue val = { 0 };
+       cnt_editor = e_html_editor_get_content_editor (editor);
 
        uri = gtk_file_chooser_get_uri (
                GTK_FILE_CHOOSER (dialog->priv->background_image_chooser));
 
-       g_value_init (&val, G_TYPE_STRING);
-       g_value_take_string (&val, uri);
+       e_content_editor_cell_set_background_image_uri (cnt_editor, uri);
+
+       gtk_widget_set_sensitive (dialog->priv->remove_image_button, uri && *uri);
 
-       html_editor_cell_dialog_set_attribute (
-               dialog, cell_set_background_image, &val, dialog);
+       g_free (uri);
 }
 
 static void
 html_editor_cell_dialog_remove_image (EHTMLEditorCellDialog *dialog)
 {
-       remove_image_attributes_from_element (
-               WEBKIT_DOM_ELEMENT (dialog->priv->cell));
+       EHTMLEditor *editor;
+       EContentEditor *cnt_editor;
+
+       editor = e_html_editor_dialog_get_editor (E_HTML_EDITOR_DIALOG (dialog));
+       cnt_editor = e_html_editor_get_content_editor (editor);
+
+       e_content_editor_cell_set_background_image_uri (cnt_editor, NULL);
 
        gtk_file_chooser_unselect_all (
                GTK_FILE_CHOOSER (dialog->priv->background_image_chooser));
@@ -504,123 +295,72 @@ static void
 html_editor_cell_dialog_show (GtkWidget *widget)
 {
        EHTMLEditor *editor;
+       EContentEditor *cnt_editor;
+       EContentEditorUnit unit;
        EHTMLEditorCellDialog *dialog;
-       EHTMLEditorView *view;
-       gchar *tmp;
-       GdkRGBA color;
+       GdkRGBA rgba;
+       gchar *alignment, *uri;
+       gint width;
 
        dialog = E_HTML_EDITOR_CELL_DIALOG (widget);
        editor = e_html_editor_dialog_get_editor (E_HTML_EDITOR_DIALOG (dialog));
-       view = e_html_editor_get_view (editor);
+       cnt_editor = e_html_editor_get_content_editor (editor);
 
-       if (!e_html_editor_view_is_undo_redo_in_progress (view)) {
-               EHTMLEditorViewHistoryEvent *ev;
-               WebKitDOMElement *table;
-
-               ev = g_new0 (EHTMLEditorViewHistoryEvent, 1);
-               ev->type = HISTORY_TABLE_DIALOG;
-
-               e_html_editor_selection_get_selection_coordinates (
-                       e_html_editor_view_get_selection (view),
-                       &ev->before.start.x, &ev->before.start.y,
-                       &ev->before.end.x, &ev->before.end.y);
-
-               table = e_html_editor_dom_node_find_parent_element (
-                               WEBKIT_DOM_NODE (dialog->priv->cell), "TABLE");
-               ev->data.dom.from = webkit_dom_node_clone_node (
-                       WEBKIT_DOM_NODE (table), TRUE);
-               dialog->priv->history_event = ev;
-       }
+       e_content_editor_on_cell_dialog_open (cnt_editor);
 
        gtk_toggle_button_set_active (
                GTK_TOGGLE_BUTTON (dialog->priv->scope_cell_button), TRUE);
 
-       tmp = webkit_dom_html_table_cell_element_get_align (
-                       WEBKIT_DOM_HTML_TABLE_CELL_ELEMENT (dialog->priv->cell));
+       alignment = e_content_editor_cell_get_align (cnt_editor);
        gtk_combo_box_set_active_id (
                GTK_COMBO_BOX (dialog->priv->halign_combo),
-               (tmp && *tmp) ? tmp : "left");
-       g_free (tmp);
+               (alignment && *alignment) ? alignment : "left");
+       g_free (alignment);
 
-       tmp = webkit_dom_html_table_cell_element_get_v_align (
-                       WEBKIT_DOM_HTML_TABLE_CELL_ELEMENT (dialog->priv->cell));
+       alignment = e_content_editor_cell_get_v_align (cnt_editor);
        gtk_combo_box_set_active_id (
                GTK_COMBO_BOX (dialog->priv->valign_combo),
-               (tmp && *tmp) ? tmp : "middle");
-       g_free (tmp);
+               (alignment && *alignment) ? alignment : "middle");
+       g_free (alignment);
 
        gtk_toggle_button_set_active (
                GTK_TOGGLE_BUTTON (dialog->priv->wrap_text_check),
-               !webkit_dom_html_table_cell_element_get_no_wrap (
-                       WEBKIT_DOM_HTML_TABLE_CELL_ELEMENT (dialog->priv->cell)));
+               e_content_editor_cell_get_wrap (cnt_editor));
 
-       tmp = webkit_dom_element_get_tag_name (
-               WEBKIT_DOM_ELEMENT (dialog->priv->cell));
        gtk_toggle_button_set_active (
                GTK_TOGGLE_BUTTON (dialog->priv->header_style_check),
-               (g_ascii_strncasecmp (tmp, "TH", 2) == 0));
-       g_free (tmp);
-
-       tmp = webkit_dom_html_table_cell_element_get_width (
-               WEBKIT_DOM_HTML_TABLE_CELL_ELEMENT (dialog->priv->cell));
-       if (tmp && *tmp) {
-               gint val = atoi (tmp);
-               gtk_spin_button_set_value (
-                       GTK_SPIN_BUTTON (dialog->priv->width_edit), val);
-               gtk_toggle_button_set_active (
-                       GTK_TOGGLE_BUTTON (dialog->priv->width_check), TRUE);
-       } else {
-               gtk_spin_button_set_value (
-                       GTK_SPIN_BUTTON (dialog->priv->width_edit), 0);
-               gtk_toggle_button_set_active (
-                       GTK_TOGGLE_BUTTON (dialog->priv->width_check), FALSE);
-       }
+               e_content_editor_cell_is_header (cnt_editor));
+
+       width = e_content_editor_cell_get_width (cnt_editor, &unit);
+       gtk_spin_button_set_value (
+               GTK_SPIN_BUTTON (dialog->priv->width_edit), width);
+       gtk_toggle_button_set_active (
+               GTK_TOGGLE_BUTTON (dialog->priv->width_check),
+               unit != E_CONTENT_EDITOR_UNIT_AUTO);
        gtk_combo_box_set_active_id (
-               GTK_COMBO_BOX (dialog->priv->width_units), "units-px");
-       g_free (tmp);
+               GTK_COMBO_BOX (dialog->priv->width_units),
+               (unit == E_CONTENT_EDITOR_UNIT_PIXEL) ? "units-px" : "units-percent");
 
        gtk_spin_button_set_value (
                GTK_SPIN_BUTTON (dialog->priv->row_span_edit),
-               webkit_dom_html_table_cell_element_get_row_span (
-                       WEBKIT_DOM_HTML_TABLE_CELL_ELEMENT (dialog->priv->cell)));
+               e_content_editor_cell_get_row_span (cnt_editor));
+
        gtk_spin_button_set_value (
                GTK_SPIN_BUTTON (dialog->priv->col_span_edit),
-               webkit_dom_html_table_cell_element_get_col_span (
-                       WEBKIT_DOM_HTML_TABLE_CELL_ELEMENT (dialog->priv->cell)));
-
-       if (webkit_dom_element_has_attribute (
-               WEBKIT_DOM_ELEMENT (dialog->priv->cell), "background")) {
-               tmp = webkit_dom_element_get_attribute (
-                       WEBKIT_DOM_ELEMENT (dialog->priv->cell), "data-uri");
+               e_content_editor_cell_get_col_span (cnt_editor));
 
+       uri = e_content_editor_cell_get_background_image_uri (cnt_editor);
+       if (uri && *uri)
                gtk_file_chooser_set_uri (
-                       GTK_FILE_CHOOSER (dialog->priv->background_image_chooser),
-                       tmp);
-
-               g_free (tmp);
-       } else {
+                       GTK_FILE_CHOOSER (dialog->priv->background_image_chooser), uri);
+       else
                gtk_file_chooser_unselect_all (
                        GTK_FILE_CHOOSER (dialog->priv->background_image_chooser));
-       }
+       g_free (uri);
 
-       tmp = webkit_dom_html_table_cell_element_get_bg_color (
-               WEBKIT_DOM_HTML_TABLE_CELL_ELEMENT (dialog->priv->cell));
-       if (tmp && *tmp) {
-               if (gdk_rgba_parse (&color, tmp)) {
-                       e_color_combo_set_current_color (
-                               E_COLOR_COMBO (dialog->priv->background_color_picker),
-                               &color);
-               } else {
-                       e_color_combo_set_current_color (
-                               E_COLOR_COMBO (dialog->priv->background_color_picker),
-                               &transparent);
-               }
-       } else {
-               e_color_combo_set_current_color (
-                       E_COLOR_COMBO (dialog->priv->background_color_picker),
-                       &transparent);
-       }
-       g_free (tmp);
+       e_content_editor_cell_get_background_color (cnt_editor, &rgba);
+       e_color_combo_set_current_color (
+               E_COLOR_COMBO (dialog->priv->background_color_picker), &rgba);
 
        GTK_WIDGET_CLASS (e_html_editor_cell_dialog_parent_class)->show (widget);
 }
@@ -628,43 +368,15 @@ html_editor_cell_dialog_show (GtkWidget *widget)
 static void
 html_editor_cell_dialog_hide (GtkWidget *widget)
 {
-       EHTMLEditorCellDialogPrivate *priv;
-       EHTMLEditorViewHistoryEvent *ev;
-
-       priv = E_HTML_EDITOR_CELL_DIALOG_GET_PRIVATE (widget);
-       ev = priv->history_event;
-
-       if (ev) {
-               EHTMLEditorCellDialog *dialog;
-               EHTMLEditor *editor;
-               EHTMLEditorSelection *selection;
-               EHTMLEditorView *view;
-               WebKitDOMElement *table;
-
-               dialog = E_HTML_EDITOR_CELL_DIALOG (widget);
-               editor = e_html_editor_dialog_get_editor (E_HTML_EDITOR_DIALOG (dialog));
-               view = e_html_editor_get_view (editor);
-               selection = e_html_editor_view_get_selection (view);
-
-               table = e_html_editor_dom_node_find_parent_element (
-                               WEBKIT_DOM_NODE (dialog->priv->cell), "TABLE");
-
-               ev->data.dom.to = webkit_dom_node_clone_node (
-                       WEBKIT_DOM_NODE (table), TRUE);
-
-               if (!webkit_dom_node_is_equal_node (ev->data.dom.from, ev->data.dom.to)) {
-                       e_html_editor_selection_get_selection_coordinates (
-                               selection, &ev->after.start.x, &ev->after.start.y, &ev->after.end.x, 
&ev->after.end.y);
-                       e_html_editor_view_insert_new_history_event (view, ev);
-               } else {
-                       g_object_unref (ev->data.dom.from);
-                       g_object_unref (ev->data.dom.to);
-                       g_free (ev);
-               }
-       }
+       EHTMLEditor *editor;
+       EHTMLEditorCellDialog *dialog;
+       EContentEditor *cnt_editor;
+
+       dialog = E_HTML_EDITOR_CELL_DIALOG (widget);
+       editor = e_html_editor_dialog_get_editor (E_HTML_EDITOR_DIALOG (dialog));
+       cnt_editor = e_html_editor_get_content_editor (editor);
 
-       g_object_unref (priv->cell);
-       priv->cell = NULL;
+       e_content_editor_on_cell_dialog_close (cnt_editor);
 
        GTK_WIDGET_CLASS (e_html_editor_cell_dialog_parent_class)->hide (widget);
 }
@@ -842,7 +554,8 @@ e_html_editor_cell_dialog_init (EHTMLEditorCellDialog *dialog)
        gtk_grid_attach (grid, widget, 0, 0, 1, 1);
        dialog->priv->width_check = widget;
 
-       widget = gtk_spin_button_new_with_range (0, G_MAXUINT, 1);
+       widget = gtk_spin_button_new_with_range (1, 100, 1);
+       gtk_spin_button_set_digits (GTK_SPIN_BUTTON (widget), 0);
        gtk_grid_attach (grid, widget, 1, 0, 1, 1);
        dialog->priv->width_edit = widget;
 
@@ -855,14 +568,14 @@ e_html_editor_cell_dialog_init (EHTMLEditorCellDialog *dialog)
                G_BINDING_SYNC_CREATE);
 
        widget = gtk_combo_box_text_new ();
-       gtk_combo_box_text_append (GTK_COMBO_BOX_TEXT (widget), "unit-px", "px");
-       gtk_combo_box_text_append (GTK_COMBO_BOX_TEXT (widget), "unit-percent", "%");
+       gtk_combo_box_text_append (GTK_COMBO_BOX_TEXT (widget), "units-px", "px");
+       gtk_combo_box_text_append (GTK_COMBO_BOX_TEXT (widget), "units-percent", "%");
        gtk_grid_attach (grid, widget, 2, 0, 1, 1);
        dialog->priv->width_units = widget;
 
-       g_signal_connect_swapped (
+       g_signal_connect (
                widget, "changed",
-               G_CALLBACK (html_editor_cell_dialog_set_width), dialog);
+               G_CALLBACK (html_editor_cell_dialog_width_units_changed), dialog);
        e_binding_bind_property (
                dialog->priv->width_check, "active",
                widget, "sensitive",
@@ -975,23 +688,3 @@ e_html_editor_cell_dialog_new (EHTMLEditor *editor)
                        "title", _("Cell Properties"),
                        NULL));
 }
-
-void
-e_html_editor_cell_dialog_show (EHTMLEditorCellDialog *dialog,
-                                WebKitDOMNode *cell)
-{
-       EHTMLEditorCellDialogClass *class;
-
-       g_return_if_fail (E_IS_HTML_EDITOR_CELL_DIALOG (dialog));
-       g_return_if_fail (cell != NULL);
-
-       dialog->priv->cell = e_html_editor_dom_node_find_parent_element (cell, "TD");
-       if (dialog->priv->cell == NULL) {
-               dialog->priv->cell =
-                       e_html_editor_dom_node_find_parent_element (cell, "TH");
-       }
-
-       class = E_HTML_EDITOR_CELL_DIALOG_GET_CLASS (dialog);
-       GTK_WIDGET_CLASS (class)->show (GTK_WIDGET (dialog));
-}
-
diff --git a/e-util/e-html-editor-cell-dialog.h b/e-util/e-html-editor-cell-dialog.h
index 3a7f608..68ae1bb 100644
--- a/e-util/e-html-editor-cell-dialog.h
+++ b/e-util/e-html-editor-cell-dialog.h
@@ -64,8 +64,6 @@ struct _EHTMLEditorCellDialogClass {
 GType          e_html_editor_cell_dialog_get_type
                                                (void) G_GNUC_CONST;
 GtkWidget *    e_html_editor_cell_dialog_new   (EHTMLEditor *editor);
-void           e_html_editor_cell_dialog_show  (EHTMLEditorCellDialog *dialog,
-                                                WebKitDOMNode *cell);
 
 G_END_DECLS
 
diff --git a/e-util/e-html-editor-dialog.h b/e-util/e-html-editor-dialog.h
index 37fc7a5..f1f5700 100644
--- a/e-util/e-html-editor-dialog.h
+++ b/e-util/e-html-editor-dialog.h
@@ -62,6 +62,16 @@ struct _EHTMLEditorDialogClass {
        GtkWindowClass parent_class;
 };
 
+#if 0 /* FIXME WK2 */
+struct _EContentEditorDialogInterface {
+       GTypeInterface parent_interface;
+
+       void            (*dialog_opened)                (EContentEditorDialog *dialog);
+
+       void            (*dialog_closed)                (EContentEditorDialog *dialog);
+};
+#endif
+
 GType          e_html_editor_dialog_get_type   (void) G_GNUC_CONST;
 EHTMLEditor *  e_html_editor_dialog_get_editor (EHTMLEditorDialog *dialog);
 GtkBox *       e_html_editor_dialog_get_button_box
diff --git a/e-util/e-html-editor-find-dialog.c b/e-util/e-html-editor-find-dialog.c
index 6fe61cf..f1ad2f2 100644
--- a/e-util/e-html-editor-find-dialog.c
+++ b/e-util/e-html-editor-find-dialog.c
@@ -41,6 +41,9 @@ struct _EHTMLEditorFindDialogPrivate {
        GtkWidget *find_button;
 
        GtkWidget *result_label;
+
+       EContentEditor *cnt_editor;
+       gulong find_done_handler_id;
 };
 
 G_DEFINE_TYPE (
@@ -56,6 +59,17 @@ reset_dialog (EHTMLEditorFindDialog *dialog)
 }
 
 static void
+html_editor_find_dialog_hide (GtkWidget *widget)
+{
+       EHTMLEditorFindDialog *dialog = E_HTML_EDITOR_FIND_DIALOG (widget);
+
+       e_content_editor_on_find_dialog_close (dialog->priv->cnt_editor);
+
+       /* Chain up to parent's implementation */
+       GTK_WIDGET_CLASS (e_html_editor_find_dialog_parent_class)->hide (widget);
+}
+
+static void
 html_editor_find_dialog_show (GtkWidget *widget)
 {
        EHTMLEditorFindDialog *dialog = E_HTML_EDITOR_FIND_DIALOG (widget);
@@ -63,48 +77,45 @@ html_editor_find_dialog_show (GtkWidget *widget)
        reset_dialog (dialog);
        gtk_widget_grab_focus (dialog->priv->entry);
 
+       e_content_editor_on_find_dialog_open (dialog->priv->cnt_editor);
+
        /* Chain up to parent's implementation */
        GTK_WIDGET_CLASS (e_html_editor_find_dialog_parent_class)->show (widget);
 }
 
 static void
-html_editor_find_dialog_find_cb (EHTMLEditorFindDialog *dialog)
+content_editor_find_done_cb (EContentEditor *cnt_editor,
+                            guint match_count,
+                            EHTMLEditorFindDialog *dialog)
 {
-       gboolean found;
-       EHTMLEditor *editor;
-       EHTMLEditorView *view;
-
-       editor = e_html_editor_dialog_get_editor (E_HTML_EDITOR_DIALOG (dialog));
-       view = e_html_editor_get_view (editor);
-       found = webkit_web_view_search_text (
-                       WEBKIT_WEB_VIEW (view),
-                       gtk_entry_get_text (
-                               GTK_ENTRY (dialog->priv->entry)),
-                       gtk_toggle_button_get_active (
-                               GTK_TOGGLE_BUTTON (
-                                       dialog->priv->case_sensitive)),
-                       !gtk_toggle_button_get_active (
-                               GTK_TOGGLE_BUTTON (
-                                       dialog->priv->backwards)),
-                       gtk_toggle_button_get_active (
-                               GTK_TOGGLE_BUTTON (
-                                       dialog->priv->wrap_search)));
-
-       gtk_widget_set_sensitive (dialog->priv->find_button, found);
-
-       /* We give focus to WebKit so that the selection is highlited.
-        * Without focus selection is not visible (at least with my default
-        * color scheme). The focus in fact is not given to WebKit, because
-        * this dialog is modal, but it satisfies it in a way that it paints
-        * the selection :) */
-       gtk_widget_grab_focus (GTK_WIDGET (view));
-
-       if (!found) {
-               gtk_label_set_label (
-                       GTK_LABEL (dialog->priv->result_label),
-                       N_("No match found"));
+       if (match_count) {
+               gtk_widget_hide (dialog->priv->result_label);
+       } else {
+               gtk_label_set_label (GTK_LABEL (dialog->priv->result_label), _("No match found"));
                gtk_widget_show (dialog->priv->result_label);
        }
+
+       gtk_widget_set_sensitive (dialog->priv->find_button, match_count > 0);
+}
+
+static void
+html_editor_find_dialog_find_cb (EHTMLEditorFindDialog *dialog)
+{
+       guint32 flags = E_CONTENT_EDITOR_FIND_NEXT;
+
+       if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (dialog->priv->backwards)))
+               flags |= E_CONTENT_EDITOR_FIND_MODE_BACKWARDS;
+
+       if (!gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (dialog->priv->case_sensitive)))
+               flags |= E_CONTENT_EDITOR_FIND_CASE_INSENSITIVE;
+
+       if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (dialog->priv->wrap_search)))
+               flags |= E_CONTENT_EDITOR_FIND_WRAP_AROUND;
+
+       e_content_editor_find (
+               dialog->priv->cnt_editor,
+               flags,
+               gtk_entry_get_text (GTK_ENTRY (dialog->priv->entry)));
 }
 
 static gboolean
@@ -125,13 +136,59 @@ entry_key_release_event (GtkWidget *widget,
 }
 
 static void
+html_editor_find_dialog_dispose (GObject *object)
+{
+       EHTMLEditorFindDialogPrivate *priv;
+
+       priv = E_HTML_EDITOR_FIND_DIALOG_GET_PRIVATE (object);
+
+       if (priv->find_done_handler_id > 0) {
+               g_signal_handler_disconnect (
+                       priv->cnt_editor,
+                       priv->find_done_handler_id);
+               priv->find_done_handler_id = 0;
+       }
+
+       /* Chain up to parent's dispose() method. */
+       G_OBJECT_CLASS (e_html_editor_find_dialog_parent_class)->dispose (object);
+}
+
+static void
+html_editor_find_dialog_constructed (GObject *object)
+{
+       EHTMLEditor *editor;
+       EHTMLEditorFindDialog *dialog;
+       EContentEditor *cnt_editor;
+
+       dialog = E_HTML_EDITOR_FIND_DIALOG (object);
+       dialog->priv = E_HTML_EDITOR_FIND_DIALOG_GET_PRIVATE (dialog);
+
+       editor = e_html_editor_dialog_get_editor (E_HTML_EDITOR_DIALOG (dialog));
+       cnt_editor = e_html_editor_get_content_editor (editor);
+
+       dialog->priv->find_done_handler_id = g_signal_connect (
+               cnt_editor, "find-done",
+               G_CALLBACK (content_editor_find_done_cb), dialog);
+
+       dialog->priv->cnt_editor = cnt_editor;
+
+       G_OBJECT_CLASS (e_html_editor_find_dialog_parent_class)->constructed (object);
+}
+
+static void
 e_html_editor_find_dialog_class_init (EHTMLEditorFindDialogClass *class)
 {
+       GObjectClass *object_class;
        GtkWidgetClass *widget_class;
 
        g_type_class_add_private (class, sizeof (EHTMLEditorFindDialogPrivate));
 
+       object_class = G_OBJECT_CLASS (class);
+       object_class->constructed = html_editor_find_dialog_constructed;
+       object_class->dispose = html_editor_find_dialog_dispose;
+
        widget_class = GTK_WIDGET_CLASS (class);
+       widget_class->hide = html_editor_find_dialog_hide;
        widget_class->show = html_editor_find_dialog_show;
 }
 
@@ -217,9 +274,8 @@ e_html_editor_find_dialog_new (EHTMLEditor *editor)
 void
 e_html_editor_find_dialog_find_next (EHTMLEditorFindDialog *dialog)
 {
-       if (gtk_entry_get_text_length (GTK_ENTRY (dialog->priv->entry)) == 0) {
+       if (gtk_entry_get_text_length (GTK_ENTRY (dialog->priv->entry)) == 0)
                return;
-       }
 
        html_editor_find_dialog_find_cb (dialog);
 }
diff --git a/e-util/e-html-editor-hrule-dialog.c b/e-util/e-html-editor-hrule-dialog.c
index bb92276..5dc6860 100644
--- a/e-util/e-html-editor-hrule-dialog.c
+++ b/e-util/e-html-editor-hrule-dialog.c
@@ -23,11 +23,8 @@
 #endif
 
 #include "e-html-editor-hrule-dialog.h"
-#include "e-html-editor-utils.h"
-#include "e-html-editor-view.h"
 
 #include <glib/gi18n-lib.h>
-#include <webkit/webkitdom.h>
 #include <stdlib.h>
 
 #define E_HTML_EDITOR_HRULE_DIALOG_GET_PRIVATE(obj) \
@@ -41,10 +38,6 @@ struct _EHTMLEditorHRuleDialogPrivate {
 
        GtkWidget *alignment_combo;
        GtkWidget *shaded_check;
-
-       WebKitDOMHTMLHRElement *hr_element;
-
-       EHTMLEditorViewHistoryEvent *history_event;
 };
 
 G_DEFINE_TYPE (
@@ -55,185 +48,148 @@ G_DEFINE_TYPE (
 static void
 html_editor_hrule_dialog_set_alignment (EHTMLEditorHRuleDialog *dialog)
 {
-       const gchar *alignment;
+       EHTMLEditor *editor;
+       EContentEditor *cnt_editor;
+       const gchar *value;
 
-       g_return_if_fail (WEBKIT_DOM_IS_HTMLHR_ELEMENT (dialog->priv->hr_element));
+       editor = e_html_editor_dialog_get_editor (E_HTML_EDITOR_DIALOG (dialog));
+       cnt_editor = e_html_editor_get_content_editor (editor);
 
-       alignment = gtk_combo_box_get_active_id (
-                       GTK_COMBO_BOX (dialog->priv->alignment_combo));
+       value = gtk_combo_box_get_active_id (
+               GTK_COMBO_BOX (dialog->priv->alignment_combo));
 
-       webkit_dom_htmlhr_element_set_align (dialog->priv->hr_element, alignment);
+       e_content_editor_h_rule_set_align (cnt_editor, value);
 }
 
 static void
 html_editor_hrule_dialog_get_alignment (EHTMLEditorHRuleDialog *dialog)
 {
-       gchar *alignment;
-
-       g_return_if_fail (WEBKIT_DOM_IS_HTMLHR_ELEMENT (dialog->priv->hr_element));
+       EHTMLEditor *editor;
+       EContentEditor *cnt_editor;
+       gchar *value;
 
-       alignment = webkit_dom_htmlhr_element_get_align (dialog->priv->hr_element);
+       editor = e_html_editor_dialog_get_editor (E_HTML_EDITOR_DIALOG (dialog));
+       cnt_editor = e_html_editor_get_content_editor (editor);
 
-       gtk_combo_box_set_active_id (
-               GTK_COMBO_BOX (dialog->priv->alignment_combo), alignment);
-       g_free (alignment);
+       value = e_content_editor_h_rule_get_align (cnt_editor);
+       if (value && *value)
+               gtk_combo_box_set_active_id (
+                       GTK_COMBO_BOX (dialog->priv->alignment_combo), value);
+       g_free (value);
 }
 
 static void
 html_editor_hrule_dialog_set_size (EHTMLEditorHRuleDialog *dialog)
 {
-       gchar *size;
-
-       g_return_if_fail (WEBKIT_DOM_IS_HTMLHR_ELEMENT (dialog->priv->hr_element));
-
-       size = g_strdup_printf (
-               "%d",
-               (gint) gtk_spin_button_get_value (
-                       GTK_SPIN_BUTTON (dialog->priv->size_edit)));
+       EHTMLEditor *editor;
+       EContentEditor *cnt_editor;
+       gint value;
 
-       webkit_dom_htmlhr_element_set_size (dialog->priv->hr_element, size);
+       editor = e_html_editor_dialog_get_editor (E_HTML_EDITOR_DIALOG (dialog));
+       cnt_editor = e_html_editor_get_content_editor (editor);
 
-       g_free (size);
+       value = gtk_spin_button_get_value_as_int  GTK_SPIN_BUTTON (dialog->priv->size_edit);
+       e_content_editor_h_rule_set_size (cnt_editor, value);
 }
 
 static void
 html_editor_hrule_dialog_get_size (EHTMLEditorHRuleDialog *dialog)
 {
-       gchar *size;
-       gint size_int = 0;
+       EHTMLEditor *editor;
+       EContentEditor *cnt_editor;
+       gint value;
 
-       g_return_if_fail (WEBKIT_DOM_IS_HTMLHR_ELEMENT (dialog->priv->hr_element));
+       editor = e_html_editor_dialog_get_editor (E_HTML_EDITOR_DIALOG (dialog));
+       cnt_editor = e_html_editor_get_content_editor (editor);
 
-       size = webkit_dom_htmlhr_element_get_size (dialog->priv->hr_element);
-       if (size && *size) {
-               size_int = atoi (size);
-       }
-
-       if (size_int == 0) {
-               size_int = 2;
-       }
+       value = e_content_editor_h_rule_get_size (cnt_editor);
 
        gtk_spin_button_set_value (
-               GTK_SPIN_BUTTON (dialog->priv->size_edit), (gdouble) size_int);
-
-       g_free (size);
+               GTK_SPIN_BUTTON (dialog->priv->size_edit), (gdouble) value);
 }
 
 static void
 html_editor_hrule_dialog_set_width (EHTMLEditorHRuleDialog *dialog)
 {
-       gchar *width, *units;
+       EHTMLEditor *editor;
+       EContentEditor *cnt_editor;
 
-       g_return_if_fail (WEBKIT_DOM_IS_HTMLHR_ELEMENT (dialog->priv->hr_element));
+       editor = e_html_editor_dialog_get_editor (E_HTML_EDITOR_DIALOG (dialog));
+       cnt_editor = e_html_editor_get_content_editor (editor);
 
-       units = gtk_combo_box_text_get_active_text (
-                       GTK_COMBO_BOX_TEXT (dialog->priv->unit_combo));
-       width = g_strdup_printf (
-               "%d%s",
-               (gint) gtk_spin_button_get_value (
+       e_content_editor_h_rule_set_width (
+               cnt_editor,
+               gtk_spin_button_get_value_as_int (
                        GTK_SPIN_BUTTON (dialog->priv->width_edit)),
-               units);
-
-       webkit_dom_htmlhr_element_set_width (dialog->priv->hr_element, width);
-
-       g_free (units);
-       g_free (width);
+               (gtk_combo_box_get_active (
+                       GTK_COMBO_BOX (dialog->priv->unit_combo)) == 0) ?
+                               E_CONTENT_EDITOR_UNIT_PIXEL :
+                               E_CONTENT_EDITOR_UNIT_PERCENTAGE);
 }
 
 static void
 html_editor_hrule_dialog_get_width (EHTMLEditorHRuleDialog *dialog)
 {
-       gchar *width;
-       const gchar *units;
-       gint width_int = 0;
-
-       g_return_if_fail (WEBKIT_DOM_IS_HTMLHR_ELEMENT (dialog->priv->hr_element));
-
-       width = webkit_dom_htmlhr_element_get_width (dialog->priv->hr_element);
-       if (width && *width) {
-               width_int = atoi (width);
-
-               if (strstr (width, "%") != NULL) {
-                       units = "units-percent";
-               } else {
-                       units = "units-px";
-               }
-       }
+       EHTMLEditor *editor;
+       EContentEditor *cnt_editor;
+       EContentEditorUnit unit;
+       gint value;
 
-       if (width_int == 0) {
-               width_int = 100;
-               units = "units-percent";
-       }
+       editor = e_html_editor_dialog_get_editor (E_HTML_EDITOR_DIALOG (dialog));
+       cnt_editor = e_html_editor_get_content_editor (editor);
 
+       value = e_content_editor_h_rule_get_width (cnt_editor, &unit);
        gtk_spin_button_set_value (
-               GTK_SPIN_BUTTON (dialog->priv->width_edit), (gdouble) width_int);
+               GTK_SPIN_BUTTON (dialog->priv->width_edit),
+               value == 0 && unit == E_CONTENT_EDITOR_UNIT_PERCENTAGE ? 100 : value);
        gtk_combo_box_set_active_id (
-               GTK_COMBO_BOX (dialog->priv->unit_combo), units);
-
-       g_free (width);
+               GTK_COMBO_BOX (dialog->priv->unit_combo),
+               unit == E_CONTENT_EDITOR_UNIT_PIXEL ? "units-px" : "units-percent");
 }
 
 static void
 html_editor_hrule_dialog_set_shading (EHTMLEditorHRuleDialog *dialog)
 {
-       gboolean no_shade;
+       EHTMLEditor *editor;
+       EContentEditor *cnt_editor;
+       gboolean value;
 
-       g_return_if_fail (WEBKIT_DOM_IS_HTMLHR_ELEMENT (dialog->priv->hr_element));
+       editor = e_html_editor_dialog_get_editor (E_HTML_EDITOR_DIALOG (dialog));
+       cnt_editor = e_html_editor_get_content_editor (editor);
 
-       no_shade = !gtk_toggle_button_get_active (
-                       GTK_TOGGLE_BUTTON (dialog->priv->shaded_check));
+       value = !gtk_toggle_button_get_active (
+               GTK_TOGGLE_BUTTON (dialog->priv->shaded_check));
 
-       webkit_dom_htmlhr_element_set_no_shade (dialog->priv->hr_element, no_shade);
+       e_content_editor_h_rule_set_no_shade (cnt_editor, value);
 }
 
 static void
 html_editor_hrule_dialog_get_shading (EHTMLEditorHRuleDialog *dialog)
 {
-       g_return_if_fail (WEBKIT_DOM_IS_HTMLHR_ELEMENT (dialog->priv->hr_element));
+       EHTMLEditor *editor;
+       EContentEditor *cnt_editor;
+       gboolean value = FALSE;
+
+       editor = e_html_editor_dialog_get_editor (E_HTML_EDITOR_DIALOG (dialog));
+       cnt_editor = e_html_editor_get_content_editor (editor);
 
+       value = e_content_editor_h_rule_get_no_shade (cnt_editor);
        gtk_toggle_button_set_active (
-               GTK_TOGGLE_BUTTON (dialog->priv->shaded_check),
-               !webkit_dom_htmlhr_element_get_no_shade (dialog->priv->hr_element));
+               GTK_TOGGLE_BUTTON (dialog->priv->shaded_check), !value);
 }
 
 static void
 html_editor_hrule_dialog_hide (GtkWidget *widget)
 {
-       EHTMLEditorHRuleDialogPrivate *priv;
-       EHTMLEditorViewHistoryEvent *ev;
-
-       priv = E_HTML_EDITOR_HRULE_DIALOG_GET_PRIVATE (widget);
-       ev = priv->history_event;
-
-       if (ev) {
-               EHTMLEditorHRuleDialog *dialog;
-               EHTMLEditor *editor;
-               EHTMLEditorSelection *selection;
-               EHTMLEditorView *view;
-
-               dialog = E_HTML_EDITOR_HRULE_DIALOG (widget);
-               editor = e_html_editor_dialog_get_editor (E_HTML_EDITOR_DIALOG (dialog));
-               view = e_html_editor_get_view (editor);
-               selection = e_html_editor_view_get_selection (view);
-
-               ev->data.dom.to = webkit_dom_node_clone_node (
-                       WEBKIT_DOM_NODE (priv->hr_element), FALSE);
-
-               if (!webkit_dom_node_is_equal_node (ev->data.dom.from, ev->data.dom.to)) {
-                       e_html_editor_selection_get_selection_coordinates (
-                               selection, &ev->after.start.x, &ev->after.start.y, &ev->after.end.x, 
&ev->after.end.y);
-                       e_html_editor_view_insert_new_history_event (view, ev);
-
-                       if (!ev->data.dom.from)
-                               g_object_unref (priv->hr_element);
-               } else {
-                       g_object_unref (ev->data.dom.from);
-                       g_object_unref (ev->data.dom.to);
-                       g_free (ev);
-               }
-       }
+       EHTMLEditor *editor;
+       EHTMLEditorHRuleDialog *dialog;
+       EContentEditor *cnt_editor;
+
+       dialog = E_HTML_EDITOR_HRULE_DIALOG (widget);
+       editor = e_html_editor_dialog_get_editor (E_HTML_EDITOR_DIALOG (dialog));
+       cnt_editor = e_html_editor_get_content_editor (editor);
 
-       priv->hr_element = NULL;
+       e_content_editor_on_h_rule_dialog_close (cnt_editor);
 
        GTK_WIDGET_CLASS (e_html_editor_hrule_dialog_parent_class)->hide (widget);
 }
@@ -243,55 +199,21 @@ html_editor_hrule_dialog_show (GtkWidget *widget)
 {
        EHTMLEditorHRuleDialog *dialog;
        EHTMLEditor *editor;
-       EHTMLEditorSelection *selection;
-       EHTMLEditorView *view;
-
-       WebKitDOMDocument *document;
+       EContentEditor *cnt_editor;
+       gboolean created_new_h_rule = FALSE;
 
        dialog = E_HTML_EDITOR_HRULE_DIALOG (widget);
        editor = e_html_editor_dialog_get_editor (E_HTML_EDITOR_DIALOG (dialog));
-       view = e_html_editor_get_view (editor);
-       selection = e_html_editor_view_get_selection (view);
-
-       if (!e_html_editor_view_is_undo_redo_in_progress (view)) {
-               EHTMLEditorViewHistoryEvent *ev;
-
-               ev = g_new0 (EHTMLEditorViewHistoryEvent, 1);
-               ev->type = HISTORY_HRULE_DIALOG;
-
-               e_html_editor_selection_get_selection_coordinates (
-                       selection, &ev->before.start.x, &ev->before.start.y, &ev->before.end.x, 
&ev->before.end.y);
-               if (dialog->priv->hr_element)
-                       ev->data.dom.from = webkit_dom_node_clone_node (
-                               WEBKIT_DOM_NODE (dialog->priv->hr_element), FALSE);
-               else
-                       ev->data.dom.from = NULL;
-               dialog->priv->history_event = ev;
-       }
-
-       if (!dialog->priv->hr_element) {
-               WebKitDOMElement *selection_start, *parent, *rule;
-
-               document = webkit_web_view_get_dom_document (WEBKIT_WEB_VIEW (view));
-               e_html_editor_selection_save (selection);
-
-               selection_start = webkit_dom_document_get_element_by_id (
-                       document, "-x-evo-selection-start-marker");
-               parent = get_parent_block_element (WEBKIT_DOM_NODE (selection_start));
-
-               rule = webkit_dom_document_create_element (document, "HR", NULL);
-
-               /* Insert horizontal rule into body below the caret */
-               webkit_dom_node_insert_before (
-                       webkit_dom_node_get_parent_node (WEBKIT_DOM_NODE (parent)),
-                       WEBKIT_DOM_NODE (rule),
-                       webkit_dom_node_get_next_sibling (WEBKIT_DOM_NODE (parent)),
-                       NULL);
-
-               e_html_editor_selection_restore (selection);
+       cnt_editor = e_html_editor_get_content_editor (editor);
 
-               dialog->priv->hr_element = WEBKIT_DOM_HTMLHR_ELEMENT (rule);
+       created_new_h_rule = e_content_editor_on_h_rule_dialog_open (cnt_editor);
 
+       if (!created_new_h_rule) {
+               html_editor_hrule_dialog_get_alignment (dialog);
+               html_editor_hrule_dialog_get_size (dialog);
+               html_editor_hrule_dialog_get_width (dialog);
+               html_editor_hrule_dialog_get_shading (dialog);
+       } else {
                /* For new rule reset the values to default */
                gtk_spin_button_set_value (
                        GTK_SPIN_BUTTON (dialog->priv->width_edit), 100.0);
@@ -309,12 +231,7 @@ html_editor_hrule_dialog_show (GtkWidget *widget)
                html_editor_hrule_dialog_set_alignment (dialog);
                html_editor_hrule_dialog_set_shading (dialog);
 
-               e_html_editor_view_set_changed (view, TRUE);
-       } else {
-               html_editor_hrule_dialog_get_alignment (dialog);
-               html_editor_hrule_dialog_get_size (dialog);
-               html_editor_hrule_dialog_get_width (dialog);
-               html_editor_hrule_dialog_get_shading (dialog);
+               e_content_editor_set_changed (cnt_editor, TRUE);
        }
 
        /* Chain up to parent implementation */
@@ -448,17 +365,3 @@ e_html_editor_hrule_dialog_new (EHTMLEditor *editor)
                        "title", _("Rule properties"),
                        NULL));
 }
-
-void
-e_html_editor_hrule_dialog_show (EHTMLEditorHRuleDialog *dialog,
-                                 WebKitDOMNode *rule)
-{
-       EHTMLEditorHRuleDialogClass *class;
-
-       g_return_if_fail (E_IS_HTML_EDITOR_HRULE_DIALOG (dialog));
-
-       dialog->priv->hr_element = rule ? WEBKIT_DOM_HTMLHR_ELEMENT (rule) : NULL;
-
-       class = E_HTML_EDITOR_HRULE_DIALOG_GET_CLASS (dialog);
-       GTK_WIDGET_CLASS (class)->show (GTK_WIDGET (dialog));
-}
diff --git a/e-util/e-html-editor-hrule-dialog.h b/e-util/e-html-editor-hrule-dialog.h
index b616da9..876fc25 100644
--- a/e-util/e-html-editor-hrule-dialog.h
+++ b/e-util/e-html-editor-hrule-dialog.h
@@ -64,8 +64,6 @@ struct _EHTMLEditorHRuleDialogClass {
 GType          e_html_editor_hrule_dialog_get_type
                                                (void) G_GNUC_CONST;
 GtkWidget *    e_html_editor_hrule_dialog_new  (EHTMLEditor *editor);
-void           e_html_editor_hrule_dialog_show (EHTMLEditorHRuleDialog *dialog,
-                                                WebKitDOMNode *rule);
 
 G_END_DECLS
 
diff --git a/e-util/e-html-editor-image-dialog.c b/e-util/e-html-editor-image-dialog.c
index 5eb7909..50db162 100644
--- a/e-util/e-html-editor-image-dialog.c
+++ b/e-util/e-html-editor-image-dialog.c
@@ -27,7 +27,6 @@
 #include <stdlib.h>
 #include <glib/gi18n-lib.h>
 
-#include "e-html-editor-utils.h"
 #include "e-image-chooser-dialog.h"
 
 #define E_HTML_EDITOR_IMAGE_DIALOG_GET_PRIVATE(obj) \
@@ -50,10 +49,6 @@ struct _EHTMLEditorImageDialogPrivate {
 
        GtkWidget *url_edit;
        GtkWidget *test_url_button;
-
-       WebKitDOMHTMLImageElement *image;
-
-       EHTMLEditorViewHistoryEvent *history_event;
 };
 
 G_DEFINE_TYPE (
@@ -65,19 +60,16 @@ static void
 html_editor_image_dialog_set_src (EHTMLEditorImageDialog *dialog)
 {
        EHTMLEditor *editor;
-       EHTMLEditorSelection *editor_selection;
-       EHTMLEditorView *view;
+       EContentEditor *cnt_editor;
        gchar *uri;
 
        editor = e_html_editor_dialog_get_editor (E_HTML_EDITOR_DIALOG (dialog));
-       view = e_html_editor_get_view (editor);
-       editor_selection = e_html_editor_view_get_selection (view);
+       cnt_editor = e_html_editor_get_content_editor (editor);
 
        uri = gtk_file_chooser_get_uri (
                GTK_FILE_CHOOSER (dialog->priv->file_chooser));
 
-       e_html_editor_selection_replace_image_src (
-               editor_selection, WEBKIT_DOM_ELEMENT (dialog->priv->image), uri);
+       e_content_editor_image_set_src (cnt_editor, uri);
 
        g_free (uri);
 }
@@ -85,22 +77,34 @@ html_editor_image_dialog_set_src (EHTMLEditorImageDialog *dialog)
 static void
 html_editor_image_dialog_set_alt (EHTMLEditorImageDialog *dialog)
 {
-       webkit_dom_html_image_element_set_alt (
-               dialog->priv->image,
-               gtk_entry_get_text (GTK_ENTRY (dialog->priv->description_edit)));
+       EHTMLEditor *editor;
+       EContentEditor *cnt_editor;
+       const gchar *value;
+
+       editor = e_html_editor_dialog_get_editor (E_HTML_EDITOR_DIALOG (dialog));
+       cnt_editor = e_html_editor_get_content_editor (editor);
+
+       value = gtk_entry_get_text (GTK_ENTRY (dialog->priv->description_edit));
+
+       e_content_editor_image_set_alt (cnt_editor, value);
 }
 
 static void
 html_editor_image_dialog_set_width (EHTMLEditorImageDialog *dialog)
 {
+       EHTMLEditor *editor;
+       EContentEditor *cnt_editor;
        gint requested;
-       gulong natural;
+       gint32 natural = 0;
        gint width;
 
-       natural = webkit_dom_html_image_element_get_natural_width (
-                       dialog->priv->image);
+       editor = e_html_editor_dialog_get_editor (E_HTML_EDITOR_DIALOG (dialog));
+       cnt_editor = e_html_editor_get_content_editor (editor);
+
+       natural = e_content_editor_image_get_natural_width (cnt_editor);
+
        requested = gtk_spin_button_get_value_as_int (
-                       GTK_SPIN_BUTTON (dialog->priv->width_edit));
+               GTK_SPIN_BUTTON (dialog->priv->width_edit));
 
        switch (gtk_combo_box_get_active (GTK_COMBO_BOX (dialog->priv->width_units))) {
                case 0: /* px */
@@ -119,18 +123,23 @@ html_editor_image_dialog_set_width (EHTMLEditorImageDialog *dialog)
                        return;
        }
 
-       webkit_dom_html_image_element_set_width (dialog->priv->image, width);
+       e_content_editor_image_set_width (cnt_editor, width);
 }
 
 static void
 html_editor_image_dialog_set_width_units (EHTMLEditorImageDialog *dialog)
 {
+       EHTMLEditor *editor;
+       EContentEditor *cnt_editor;
        gint requested;
-       gulong natural;
+       gint32 natural = 0;
        gint width = 0;
 
-       natural = webkit_dom_html_image_element_get_natural_width (
-                       dialog->priv->image);
+       editor = e_html_editor_dialog_get_editor (E_HTML_EDITOR_DIALOG (dialog));
+       cnt_editor = e_html_editor_get_content_editor (editor);
+
+       natural = e_content_editor_image_get_natural_width (cnt_editor);
+
        requested = gtk_spin_button_get_value_as_int (
                        GTK_SPIN_BUTTON (dialog->priv->width_edit));
 
@@ -143,8 +152,6 @@ html_editor_image_dialog_set_width_units (EHTMLEditorImageDialog *dialog)
                        } else {
                                width = natural;
                        }
-                       webkit_dom_element_remove_attribute (
-                               WEBKIT_DOM_ELEMENT (dialog->priv->image), "style");
                        gtk_widget_set_sensitive (dialog->priv->width_edit, TRUE);
                        break;
 
@@ -154,38 +161,38 @@ html_editor_image_dialog_set_width_units (EHTMLEditorImageDialog *dialog)
                        } else {
                                width = 100;
                        }
-                       webkit_dom_element_remove_attribute (
-                               WEBKIT_DOM_ELEMENT (dialog->priv->image), "style");
                        gtk_widget_set_sensitive (dialog->priv->width_edit, TRUE);
                        break;
 
                case 2: /* follow */
-                       webkit_dom_element_set_attribute (
-                               WEBKIT_DOM_ELEMENT (dialog->priv->image),
-                               "style",
-                               "width: auto;",
-                               NULL);
                        gtk_widget_set_sensitive (dialog->priv->width_edit, FALSE);
                        break;
        }
 
-       if (width != 0) {
+       e_content_editor_image_set_width_follow (
+               cnt_editor, !gtk_widget_get_sensitive (dialog->priv->width_edit));
+
+       if (width != 0)
                gtk_spin_button_set_value (
                        GTK_SPIN_BUTTON (dialog->priv->width_edit), width);
-       }
 }
 
 static void
 html_editor_image_dialog_set_height (EHTMLEditorImageDialog *dialog)
 {
+       EHTMLEditor *editor;
+       EContentEditor *cnt_editor;
        gint requested;
-       gulong natural;
+       gint32 natural = 0;
        gint height;
 
-       natural = webkit_dom_html_image_element_get_natural_height (
-                       dialog->priv->image);
+       editor = e_html_editor_dialog_get_editor (E_HTML_EDITOR_DIALOG (dialog));
+       cnt_editor = e_html_editor_get_content_editor (editor);
+
+       natural = e_content_editor_image_get_natural_height (cnt_editor);
+
        requested = gtk_spin_button_get_value_as_int (
-                       GTK_SPIN_BUTTON (dialog->priv->height_edit));
+               GTK_SPIN_BUTTON (dialog->priv->height_edit));
 
        switch (gtk_combo_box_get_active (GTK_COMBO_BOX (dialog->priv->height_units))) {
                case 0: /* px */
@@ -204,20 +211,25 @@ html_editor_image_dialog_set_height (EHTMLEditorImageDialog *dialog)
                        return;
        }
 
-       webkit_dom_html_image_element_set_height (dialog->priv->image, height);
+       e_content_editor_image_set_height (cnt_editor, height);
 }
 
 static void
 html_editor_image_dialog_set_height_units (EHTMLEditorImageDialog *dialog)
 {
+       EHTMLEditor *editor;
+       EContentEditor *cnt_editor;
        gint requested;
-       gulong natural;
+       gulong natural = 0;
        gint height = -1;
 
-       natural = webkit_dom_html_image_element_get_natural_height (
-                       dialog->priv->image);
+       editor = e_html_editor_dialog_get_editor (E_HTML_EDITOR_DIALOG (dialog));
+       cnt_editor = e_html_editor_get_content_editor (editor);
+
+       natural = e_content_editor_image_get_natural_height (cnt_editor);
+
        requested = gtk_spin_button_get_value_as_int (
-                       GTK_SPIN_BUTTON (dialog->priv->height_edit));
+               GTK_SPIN_BUTTON (dialog->priv->height_edit));
 
        switch (gtk_combo_box_get_active (
                GTK_COMBO_BOX (dialog->priv->height_units))) {
@@ -228,8 +240,6 @@ html_editor_image_dialog_set_height_units (EHTMLEditorImageDialog *dialog)
                        } else {
                                height = natural;
                        }
-                       webkit_dom_element_remove_attribute (
-                               WEBKIT_DOM_ELEMENT (dialog->priv->image), "style");
                        gtk_widget_set_sensitive (dialog->priv->height_edit, TRUE);
                        break;
 
@@ -239,116 +249,95 @@ html_editor_image_dialog_set_height_units (EHTMLEditorImageDialog *dialog)
                        } else {
                                height = 100;
                        }
-                       webkit_dom_element_remove_attribute (
-                               WEBKIT_DOM_ELEMENT (dialog->priv->image), "style");
                        gtk_widget_set_sensitive (dialog->priv->height_edit, TRUE);
                        break;
 
                case 2: /* follow */
-                       webkit_dom_element_set_attribute (
-                               WEBKIT_DOM_ELEMENT (dialog->priv->image),
-                               "style",
-                               "height: auto;",
-                               NULL);
                        gtk_widget_set_sensitive (dialog->priv->height_edit, FALSE);
                        break;
        }
 
-       if (height != -1) {
+       e_content_editor_image_set_height_follow (
+               cnt_editor, !gtk_widget_get_sensitive (dialog->priv->height_edit));
+
+       if (height != -1)
                gtk_spin_button_set_value (
                        GTK_SPIN_BUTTON (dialog->priv->height_edit), height);
-       }
 }
 
 static void
 html_editor_image_dialog_set_alignment (EHTMLEditorImageDialog *dialog)
 {
-       webkit_dom_html_image_element_set_align (
-               dialog->priv->image,
-               gtk_combo_box_get_active_id (
-                       GTK_COMBO_BOX (dialog->priv->alignment)));
+       EHTMLEditor *editor;
+       EContentEditor *cnt_editor;
+       const gchar *value;
+
+       editor = e_html_editor_dialog_get_editor (E_HTML_EDITOR_DIALOG (dialog));
+       cnt_editor = e_html_editor_get_content_editor (editor);
+
+       value = gtk_combo_box_get_active_id (GTK_COMBO_BOX (dialog->priv->alignment));
+       e_content_editor_image_set_align (cnt_editor, value);
 }
 
 static void
 html_editor_image_dialog_set_x_padding (EHTMLEditorImageDialog *dialog)
 {
-       webkit_dom_html_image_element_set_hspace (
-               dialog->priv->image,
-               gtk_spin_button_get_value_as_int (
-                       GTK_SPIN_BUTTON (dialog->priv->x_padding_edit)));
+       EHTMLEditor *editor;
+       EContentEditor *cnt_editor;
+       gint value;
+
+       editor = e_html_editor_dialog_get_editor (E_HTML_EDITOR_DIALOG (dialog));
+       cnt_editor = e_html_editor_get_content_editor (editor);
+
+       value = gtk_spin_button_get_value_as_int (
+               GTK_SPIN_BUTTON (dialog->priv->x_padding_edit));
+       e_content_editor_image_set_hspace (cnt_editor, value);
 }
 
 static void
 html_editor_image_dialog_set_y_padding (EHTMLEditorImageDialog *dialog)
 {
-       webkit_dom_html_image_element_set_vspace (
-               dialog->priv->image,
-               gtk_spin_button_get_value_as_int (
-                       GTK_SPIN_BUTTON (dialog->priv->y_padding_edit)));
+       EHTMLEditor *editor;
+       EContentEditor *cnt_editor;
+       gint value;
+
+       editor = e_html_editor_dialog_get_editor (E_HTML_EDITOR_DIALOG (dialog));
+       cnt_editor = e_html_editor_get_content_editor (editor);
+
+       value = gtk_spin_button_get_value_as_int (
+               GTK_SPIN_BUTTON (dialog->priv->y_padding_edit));
+       e_content_editor_image_set_vspace (cnt_editor, value);
 }
 
 static void
 html_editor_image_dialog_set_border (EHTMLEditorImageDialog *dialog)
 {
-       gchar *val;
+       EHTMLEditor *editor;
+       EContentEditor *cnt_editor;
+       gint value;
 
-       val = g_strdup_printf (
-               "%d", gtk_spin_button_get_value_as_int (
-                       GTK_SPIN_BUTTON (dialog->priv->border_edit)));
+       editor = e_html_editor_dialog_get_editor (E_HTML_EDITOR_DIALOG (dialog));
+       cnt_editor = e_html_editor_get_content_editor (editor);
 
-       webkit_dom_html_image_element_set_border (dialog->priv->image, val);
+       value = gtk_spin_button_get_value_as_int (
+               GTK_SPIN_BUTTON (dialog->priv->border_edit));
 
-       g_free (val);
+       e_content_editor_image_set_border (cnt_editor, value);
 }
 
 static void
 html_editor_image_dialog_set_url (EHTMLEditorImageDialog *dialog)
 {
-       WebKitDOMElement *link;
-       const gchar *url;
-
-       url = gtk_entry_get_text (GTK_ENTRY (dialog->priv->url_edit));
-       link = e_html_editor_dom_node_find_parent_element (
-               WEBKIT_DOM_NODE (dialog->priv->image), "A");
-
-       if (link) {
-               if (!url || !*url) {
-                       webkit_dom_node_insert_before (
-                               webkit_dom_node_get_parent_node (
-                                       WEBKIT_DOM_NODE (link)),
-                               WEBKIT_DOM_NODE (dialog->priv->image),
-                               WEBKIT_DOM_NODE (link), NULL);
-                       webkit_dom_node_remove_child (
-                               webkit_dom_node_get_parent_node (
-                                       WEBKIT_DOM_NODE (link)),
-                               WEBKIT_DOM_NODE (link), NULL);
-               } else {
-                       webkit_dom_html_anchor_element_set_href (
-                               WEBKIT_DOM_HTML_ANCHOR_ELEMENT (link), url);
-               }
-       } else {
-               if (url && *url) {
-                       WebKitDOMDocument *document;
-
-                       document = webkit_dom_node_get_owner_document (
-                                       WEBKIT_DOM_NODE (dialog->priv->image));
-                       link = webkit_dom_document_create_element (
-                                       document, "A", NULL);
-
-                       webkit_dom_html_anchor_element_set_href (
-                               WEBKIT_DOM_HTML_ANCHOR_ELEMENT (link), url);
-
-                       webkit_dom_node_insert_before (
-                               webkit_dom_node_get_parent_node (
-                                       WEBKIT_DOM_NODE (dialog->priv->image)),
-                               WEBKIT_DOM_NODE (link),
-                               WEBKIT_DOM_NODE (dialog->priv->image), NULL);
-
-                       webkit_dom_node_append_child (
-                               WEBKIT_DOM_NODE (link),
-                               WEBKIT_DOM_NODE (dialog->priv->image), NULL);
-               }
-       }
+       EHTMLEditor *editor;
+       EContentEditor *cnt_editor;
+       const gchar *value;
+
+       editor = e_html_editor_dialog_get_editor (E_HTML_EDITOR_DIALOG (dialog));
+       cnt_editor = e_html_editor_get_content_editor (editor);
+
+       value = gtk_entry_get_text (GTK_ENTRY (dialog->priv->url_edit));
+
+       e_content_editor_image_set_url (cnt_editor, value);
 }
 
 static void
@@ -364,42 +353,21 @@ html_editor_image_dialog_test_url (EHTMLEditorImageDialog *dialog)
 static void
 html_editor_image_dialog_show (GtkWidget *widget)
 {
-       EHTMLEditorImageDialog *dialog;
        EHTMLEditor *editor;
-       EHTMLEditorSelection *selection;
-       EHTMLEditorView *view;
-       WebKitDOMElement *link;
-       gchar *tmp;
-       glong val;
+       EHTMLEditorImageDialog *dialog;
+       EContentEditor *cnt_editor;
+       gchar *value;
 
        dialog = E_HTML_EDITOR_IMAGE_DIALOG (widget);
-
-       if (!dialog->priv->image) {
-               return;
-       }
-
        editor = e_html_editor_dialog_get_editor (E_HTML_EDITOR_DIALOG (dialog));
-       view = e_html_editor_get_view (editor);
-       selection = e_html_editor_view_get_selection (view);
+       cnt_editor = e_html_editor_get_content_editor (editor);
 
-       if (!e_html_editor_view_is_undo_redo_in_progress (view)) {
-               EHTMLEditorViewHistoryEvent *ev;
+       e_content_editor_on_image_dialog_open (cnt_editor);
 
-               ev = g_new0 (EHTMLEditorViewHistoryEvent, 1);
-               ev->type = HISTORY_IMAGE_DIALOG;
-
-               e_html_editor_selection_get_selection_coordinates (
-                       selection, &ev->before.start.x, &ev->before.start.y, &ev->before.end.x, 
&ev->before.end.y);
-               ev->data.dom.from = webkit_dom_node_clone_node (
-                       WEBKIT_DOM_NODE (dialog->priv->image), FALSE);
-               dialog->priv->history_event = ev;
-       }
-
-       tmp = webkit_dom_element_get_attribute (
-               WEBKIT_DOM_ELEMENT (dialog->priv->image), "data-uri");
-       if (tmp && *tmp) {
+       value = e_content_editor_image_get_src (cnt_editor);
+       if (value && *value) {
                gtk_file_chooser_set_uri (
-                       GTK_FILE_CHOOSER (dialog->priv->file_chooser), tmp);
+                       GTK_FILE_CHOOSER (dialog->priv->file_chooser), value);
                gtk_widget_set_sensitive (
                        GTK_WIDGET (dialog->priv->file_chooser), TRUE);
        } else {
@@ -408,56 +376,49 @@ html_editor_image_dialog_show (GtkWidget *widget)
                gtk_widget_set_sensitive (
                        GTK_WIDGET (dialog->priv->file_chooser), FALSE);
        }
-       g_free (tmp);
+       g_free (value);
 
-       tmp = webkit_dom_html_image_element_get_alt (dialog->priv->image);
-       gtk_entry_set_text (GTK_ENTRY (dialog->priv->description_edit), tmp ? tmp : "");
-       g_free (tmp);
+       value = e_content_editor_image_get_alt (cnt_editor);
+       gtk_entry_set_text (
+               GTK_ENTRY (dialog->priv->description_edit), value ? value : "");
+       g_free (value);
 
-       val = webkit_dom_html_image_element_get_width (dialog->priv->image);
        gtk_spin_button_set_value (
-               GTK_SPIN_BUTTON (dialog->priv->width_edit), val);
+               GTK_SPIN_BUTTON (dialog->priv->width_edit),
+               e_content_editor_image_get_width (cnt_editor));
+
        gtk_combo_box_set_active_id (
                GTK_COMBO_BOX (dialog->priv->width_units), "units-px");
 
-       val = webkit_dom_html_image_element_get_height (dialog->priv->image);
        gtk_spin_button_set_value (
-               GTK_SPIN_BUTTON (dialog->priv->height_edit), val);
+               GTK_SPIN_BUTTON (dialog->priv->height_edit),
+               e_content_editor_image_get_height (cnt_editor));
+
        gtk_combo_box_set_active_id (
                GTK_COMBO_BOX (dialog->priv->height_units), "units-px");
 
-       tmp = webkit_dom_html_image_element_get_border (dialog->priv->image);
-       if (tmp && *tmp) {
-               gint border;
-
-               border = atoi (tmp);
-               gtk_spin_button_set_value (
-                       GTK_SPIN_BUTTON (dialog->priv->border_edit), border);
-       }
-       g_free (tmp);
+       gtk_spin_button_set_value (
+               GTK_SPIN_BUTTON (dialog->priv->border_edit),
+               e_content_editor_image_get_border (cnt_editor));
 
-       tmp = webkit_dom_html_image_element_get_align (dialog->priv->image);
+       value = e_content_editor_image_get_align (cnt_editor);
        gtk_combo_box_set_active_id (
                GTK_COMBO_BOX (dialog->priv->alignment),
-               (tmp && *tmp) ? tmp : "bottom");
-       g_free (tmp);
+               (value && *value) ? value : "bottom");
+       g_free (value);
 
-       val = webkit_dom_html_image_element_get_hspace (dialog->priv->image);
        gtk_spin_button_set_value (
-               GTK_SPIN_BUTTON (dialog->priv->x_padding_edit), val);
+               GTK_SPIN_BUTTON (dialog->priv->y_padding_edit),
+               e_content_editor_image_get_hspace (cnt_editor));
 
-       val = webkit_dom_html_image_element_get_vspace (dialog->priv->image);
        gtk_spin_button_set_value (
-               GTK_SPIN_BUTTON (dialog->priv->y_padding_edit), val);
-
-       link = e_html_editor_dom_node_find_parent_element (
-                       WEBKIT_DOM_NODE (dialog->priv->image), "A");
-       if (link) {
-               tmp = webkit_dom_html_anchor_element_get_href (
-                               WEBKIT_DOM_HTML_ANCHOR_ELEMENT (link));
-               gtk_entry_set_text (GTK_ENTRY (dialog->priv->url_edit), tmp);
-               g_free (tmp);
-       }
+               GTK_SPIN_BUTTON (dialog->priv->y_padding_edit),
+               e_content_editor_image_get_vspace (cnt_editor));
+
+       value = e_content_editor_image_get_url (cnt_editor);
+       if (value && *value)
+               gtk_entry_set_text (GTK_ENTRY (dialog->priv->url_edit), value);
+       g_free (value);
 
        /* Chain up to parent implementation */
        GTK_WIDGET_CLASS (e_html_editor_image_dialog_parent_class)->show (widget);
@@ -466,33 +427,15 @@ html_editor_image_dialog_show (GtkWidget *widget)
 static void
 html_editor_image_dialog_hide (GtkWidget *widget)
 {
-       EHTMLEditorImageDialogPrivate *priv;
-       EHTMLEditorViewHistoryEvent *ev;
-
-       priv = E_HTML_EDITOR_IMAGE_DIALOG_GET_PRIVATE (widget);
-       ev = priv->history_event;
-
-       if (ev) {
-               EHTMLEditorImageDialog *dialog;
-               EHTMLEditor *editor;
-               EHTMLEditorSelection *selection;
-               EHTMLEditorView *view;
-
-               dialog = E_HTML_EDITOR_IMAGE_DIALOG (widget);
-               editor = e_html_editor_dialog_get_editor (E_HTML_EDITOR_DIALOG (dialog));
-               view = e_html_editor_get_view (editor);
-               selection = e_html_editor_view_get_selection (view);
-
-               ev->data.dom.to = webkit_dom_node_clone_node (
-                       WEBKIT_DOM_NODE (priv->image), FALSE);
+       EHTMLEditor *editor;
+       EHTMLEditorImageDialog *dialog;
+       EContentEditor *cnt_editor;
 
-               e_html_editor_selection_get_selection_coordinates (
-                       selection, &ev->after.start.x, &ev->after.start.y, &ev->after.end.x, 
&ev->after.end.y);
-               e_html_editor_view_insert_new_history_event (view, ev);
-       }
+       dialog = E_HTML_EDITOR_IMAGE_DIALOG (widget);
+       editor = e_html_editor_dialog_get_editor (E_HTML_EDITOR_DIALOG (dialog));
+       cnt_editor = e_html_editor_get_content_editor (editor);
 
-       g_object_unref (priv->image);
-       priv->image = NULL;
+       e_content_editor_on_image_dialog_close (cnt_editor);
 
        GTK_WIDGET_CLASS (e_html_editor_image_dialog_parent_class)->hide (widget);
 }
@@ -742,19 +685,12 @@ e_html_editor_image_dialog_new (EHTMLEditor *editor)
 }
 
 void
-e_html_editor_image_dialog_show (EHTMLEditorImageDialog *dialog,
-                                 WebKitDOMNode *image)
+e_html_editor_image_dialog_show (EHTMLEditorImageDialog *dialog)
 {
        EHTMLEditorImageDialogClass *class;
 
        g_return_if_fail (E_IS_HTML_EDITOR_IMAGE_DIALOG (dialog));
 
-       if (image) {
-               dialog->priv->image = WEBKIT_DOM_HTML_IMAGE_ELEMENT (image);
-       } else {
-               dialog->priv->image = NULL;
-       }
-
        class = E_HTML_EDITOR_IMAGE_DIALOG_GET_CLASS (dialog);
        GTK_WIDGET_CLASS (class)->show (GTK_WIDGET (dialog));
 }
diff --git a/e-util/e-html-editor-image-dialog.h b/e-util/e-html-editor-image-dialog.h
index efdbaf9..a4d0304 100644
--- a/e-util/e-html-editor-image-dialog.h
+++ b/e-util/e-html-editor-image-dialog.h
@@ -64,8 +64,7 @@ struct _EHTMLEditorImageDialogClass {
 GType          e_html_editor_image_dialog_get_type
                                                (void) G_GNUC_CONST;
 GtkWidget *    e_html_editor_image_dialog_new  (EHTMLEditor *editor);
-void           e_html_editor_image_dialog_show (EHTMLEditorImageDialog *dialog,
-                                                WebKitDOMNode *image);
+void           e_html_editor_image_dialog_show (EHTMLEditorImageDialog *dialog);
 
 G_END_DECLS
 
diff --git a/e-util/e-html-editor-link-dialog.c b/e-util/e-html-editor-link-dialog.c
index 78bc97d..5db93ed 100644
--- a/e-util/e-html-editor-link-dialog.c
+++ b/e-util/e-html-editor-link-dialog.c
@@ -23,10 +23,6 @@
 #endif
 
 #include "e-html-editor-link-dialog.h"
-#include "e-html-editor-selection.h"
-#include "e-html-editor-utils.h"
-#include "e-html-editor-view.h"
-#include "e-web-view.h"
 
 #include <glib/gi18n-lib.h>
 
@@ -48,11 +44,6 @@ struct _EHTMLEditorLinkDialogPrivate {
        GtkWidget *ok_button;
 
        gboolean label_autofill;
-       gboolean unlinking;
-
-       WebKitDOMElement *link_element;
-
-       EHTMLEditorViewHistoryEvent *history_event;
 };
 
 static void
@@ -94,14 +85,12 @@ static void
 html_editor_link_dialog_remove_link (EHTMLEditorLinkDialog *dialog)
 {
        EHTMLEditor *editor;
-       EHTMLEditorView *view;
-       EHTMLEditorSelection *selection;
+       EContentEditor *cnt_editor;
 
        editor = e_html_editor_dialog_get_editor (E_HTML_EDITOR_DIALOG (dialog));
-       view = e_html_editor_get_view (editor);
-       selection = e_html_editor_view_get_selection (view);
-       e_html_editor_selection_unlink (selection);
-       dialog->priv->unlinking = TRUE;
+       cnt_editor = e_html_editor_get_content_editor (editor);
+
+       e_content_editor_selection_unlink (cnt_editor);
 
        gtk_widget_hide (GTK_WIDGET (dialog));
 }
@@ -110,108 +99,15 @@ static void
 html_editor_link_dialog_ok (EHTMLEditorLinkDialog *dialog)
 {
        EHTMLEditor *editor;
-       EHTMLEditorSelection *selection;
-       EHTMLEditorView *view;
-       WebKitDOMDocument *document;
+       EContentEditor *cnt_editor;
 
        editor = e_html_editor_dialog_get_editor (E_HTML_EDITOR_DIALOG (dialog));
-       view = e_html_editor_get_view (editor);
-       selection = e_html_editor_view_get_selection (view);
-       document = webkit_web_view_get_dom_document (WEBKIT_WEB_VIEW (view));
-
-       if (dialog->priv->link_element) {
-               WebKitDOMElement *element;
-
-               webkit_dom_html_anchor_element_set_href (
-                       WEBKIT_DOM_HTML_ANCHOR_ELEMENT (dialog->priv->link_element),
-                       gtk_entry_get_text (GTK_ENTRY (dialog->priv->url_edit)));
-               webkit_dom_html_element_set_inner_html (
-                       WEBKIT_DOM_HTML_ELEMENT (dialog->priv->link_element),
-                       gtk_entry_get_text (GTK_ENTRY (dialog->priv->label_edit)),
-                       NULL);
-
-               element = webkit_dom_document_create_element (document, "SPAN", NULL);
-               webkit_dom_element_set_id (element, "-x-evo-selection-end-marker");
-               webkit_dom_node_insert_before (
-                       webkit_dom_node_get_parent_node (WEBKIT_DOM_NODE (dialog->priv->link_element)),
-                       WEBKIT_DOM_NODE (element),
-                       webkit_dom_node_get_next_sibling (WEBKIT_DOM_NODE (dialog->priv->link_element)),
-                       NULL);
-
-               element = webkit_dom_document_create_element (document, "SPAN", NULL);
-               webkit_dom_element_set_id (element, "-x-evo-selection-start-marker");
-               webkit_dom_node_insert_before (
-                       webkit_dom_node_get_parent_node (WEBKIT_DOM_NODE (dialog->priv->link_element)),
-                       WEBKIT_DOM_NODE (element),
-                       webkit_dom_node_get_next_sibling (WEBKIT_DOM_NODE (dialog->priv->link_element)),
-                       NULL);
-
-               e_html_editor_selection_restore (selection);
-       } else {
-               WebKitDOMDOMWindow *dom_window;
-               WebKitDOMDOMSelection *dom_selection;
-               WebKitDOMRange *range;
-
-               dom_window = webkit_dom_document_get_default_view (document);
-               dom_selection = webkit_dom_dom_window_get_selection (dom_window);
-               g_object_unref (dom_window);
-
-               e_html_editor_selection_restore (selection);
-               range = webkit_dom_dom_selection_get_range_at (dom_selection, 0, NULL);
-               if (webkit_dom_range_get_collapsed (range, NULL)) {
-                       WebKitDOMElement *selection_marker;
-                       WebKitDOMElement *anchor;
-
-                       e_html_editor_selection_save (selection);
-                       selection_marker = webkit_dom_document_get_element_by_id (
-                               document, "-x-evo-selection-start-marker");
-                       anchor = webkit_dom_document_create_element (document, "A", NULL);
-                       webkit_dom_element_set_attribute (
-                               anchor, "href", gtk_entry_get_text (GTK_ENTRY (dialog->priv->url_edit)), 
NULL);
-                       webkit_dom_html_element_set_inner_text (
-                               WEBKIT_DOM_HTML_ELEMENT (anchor),
-                               gtk_entry_get_text (
-                                       GTK_ENTRY (dialog->priv->label_edit)),
-                               NULL);
-                       dialog->priv->link_element = anchor;
-
-                       webkit_dom_node_insert_before (
-                               webkit_dom_node_get_parent_node (
-                                       WEBKIT_DOM_NODE (selection_marker)),
-                               WEBKIT_DOM_NODE (anchor),
-                               WEBKIT_DOM_NODE (selection_marker),
-                               NULL);
-                       e_html_editor_selection_restore (selection);
-               } else {
-                       gchar *text;
-
-                       text = webkit_dom_range_get_text (range);
-                       if (text && *text) {
-                               WebKitDOMElement *selection_marker;
-                               WebKitDOMNode *parent;
-
-                               e_html_editor_selection_create_link (
-                                       selection, gtk_entry_get_text (GTK_ENTRY (dialog->priv->url_edit)));
-
-                               dialog->priv->history_event->data.dom.from =
-                                       WEBKIT_DOM_NODE (webkit_dom_document_create_text_node (document, 
text));
-
-                               e_html_editor_selection_save (selection);
-                               selection_marker = webkit_dom_document_get_element_by_id (
-                                       document, "-x-evo-selection-start-marker");
-                               parent = webkit_dom_node_get_parent_node (
-                                       WEBKIT_DOM_NODE (selection_marker));
-                               if (WEBKIT_DOM_IS_HTML_ANCHOR_ELEMENT (parent))
-                                       dialog->priv->link_element = WEBKIT_DOM_ELEMENT (parent);
-                               e_html_editor_selection_restore (selection);
-                               webkit_dom_dom_selection_collapse_to_end (dom_selection, NULL);
-                       }
-                       g_free (text);
-               }
-
-               g_object_unref (range);
-               g_object_unref (dom_selection);
-       }
+       cnt_editor = e_html_editor_get_content_editor (editor);
+
+       e_content_editor_link_set_values (
+               cnt_editor,
+               gtk_entry_get_text (GTK_ENTRY (dialog->priv->url_edit)),
+               gtk_entry_get_text (GTK_ENTRY (dialog->priv->label_edit)));
 
        gtk_widget_hide (GTK_WIDGET (dialog));
 }
@@ -220,7 +116,7 @@ static gboolean
 html_editor_link_dialog_entry_key_pressed (EHTMLEditorLinkDialog *dialog,
                                            GdkEventKey *event)
 {
-       /* We can't do thins in key_released, because then you could not open
+       /* We can't do things in key_released, because then you could not open
         * this dialog from main menu by pressing enter on Insert->Link action */
        if (event->keyval == GDK_KEY_Return) {
                html_editor_link_dialog_ok (dialog);
@@ -231,139 +127,66 @@ html_editor_link_dialog_entry_key_pressed (EHTMLEditorLinkDialog *dialog,
 }
 
 static void
+html_editor_link_dialog_hide (GtkWidget *widget)
+{
+       EHTMLEditor *editor;
+       EHTMLEditorLinkDialog *dialog;
+       EContentEditor *cnt_editor;
+
+       dialog = E_HTML_EDITOR_LINK_DIALOG (widget);
+       editor = e_html_editor_dialog_get_editor (E_HTML_EDITOR_DIALOG (dialog));
+       cnt_editor = e_html_editor_get_content_editor (editor);
+
+       e_content_editor_on_link_dialog_close (cnt_editor);
+
+       /* Chain up to parent implementation */
+       GTK_WIDGET_CLASS (e_html_editor_link_dialog_parent_class)->hide (widget);
+}
+
+static void
 html_editor_link_dialog_show (GtkWidget *widget)
 {
        EHTMLEditor *editor;
        EHTMLEditorLinkDialog *dialog;
-       EHTMLEditorSelection *selection;
-       EHTMLEditorView *view;
+       EContentEditor *cnt_editor;
+       gchar *href = NULL, *text = NULL;
 
        dialog = E_HTML_EDITOR_LINK_DIALOG (widget);
        editor = e_html_editor_dialog_get_editor (E_HTML_EDITOR_DIALOG (dialog));
-       view = e_html_editor_get_view (editor);
-       selection = e_html_editor_view_get_selection (view);
+       cnt_editor = e_html_editor_get_content_editor (editor);
 
        /* Reset to default values */
        gtk_entry_set_text (GTK_ENTRY (dialog->priv->url_edit), "http://";);
        gtk_entry_set_text (GTK_ENTRY (dialog->priv->label_edit), "");
        gtk_widget_set_sensitive (dialog->priv->label_edit, TRUE);
        gtk_widget_set_sensitive (dialog->priv->remove_link_button, TRUE);
-       dialog->priv->label_autofill = TRUE;
-       dialog->priv->unlinking = FALSE;
-
-       if (!e_html_editor_view_is_undo_redo_in_progress (view)) {
-               EHTMLEditorViewHistoryEvent *ev;
-
-               ev = g_new0 (EHTMLEditorViewHistoryEvent, 1);
-               ev->type = HISTORY_LINK_DIALOG;
-
-               e_html_editor_selection_get_selection_coordinates (
-                       selection, &ev->before.start.x, &ev->before.start.y, &ev->before.end.x, 
&ev->before.end.y);
-               if (dialog->priv->link_element)
-                       ev->data.dom.from = webkit_dom_node_clone_node (
-                               WEBKIT_DOM_NODE (dialog->priv->link_element), TRUE);
-               else
-                       ev->data.dom.from = NULL;
-               dialog->priv->history_event = ev;
-       }
 
-       if (dialog->priv->link_element) {
-               gchar *href, *text;
+       dialog->priv->label_autofill = TRUE;
 
-               href = webkit_dom_element_get_attribute (dialog->priv->link_element, "href");
-               text = webkit_dom_html_element_get_inner_text (
-                       WEBKIT_DOM_HTML_ELEMENT (dialog->priv->link_element));
+       e_content_editor_on_link_dialog_open (cnt_editor);
 
+       e_content_editor_link_get_values (cnt_editor, &href, &text);
+       if (href && *href)
                gtk_entry_set_text (
                        GTK_ENTRY (dialog->priv->url_edit), href);
+       else
+               gtk_widget_set_sensitive (
+                       dialog->priv->remove_link_button, FALSE);
+
+       g_free (href);
+
+       if (text && *text) {
                gtk_entry_set_text (
                        GTK_ENTRY (dialog->priv->label_edit), text);
-
-               g_free (text);
-               g_free (href);
-       } else {
-               gchar *text;
-               WebKitDOMDocument *document;
-               WebKitDOMDOMWindow *dom_window;
-               WebKitDOMDOMSelection *dom_selection;
-               WebKitDOMRange *range;
-
-               document = webkit_web_view_get_dom_document (WEBKIT_WEB_VIEW (view));
-               dom_window = webkit_dom_document_get_default_view (document);
-               dom_selection = webkit_dom_dom_window_get_selection (dom_window);
-               g_object_unref (dom_window);
-
-               /* No selection at all */
-               if (!dom_selection || webkit_dom_dom_selection_get_range_count (dom_selection) < 1)
-                       gtk_widget_set_sensitive (dialog->priv->remove_link_button, FALSE);
-
-               range = webkit_dom_dom_selection_get_range_at (dom_selection, 0, NULL);
-               text = webkit_dom_range_get_text (range);
-               if (text && *text) {
-                       gtk_entry_set_text (
-                               GTK_ENTRY (dialog->priv->label_edit), text);
-                       gtk_widget_set_sensitive (
-                               dialog->priv->label_edit, FALSE);
-                       gtk_widget_set_sensitive (
-                               dialog->priv->remove_link_button, FALSE);
-               }
-               g_free (text);
-
-               g_object_unref (range);
-               g_object_unref (dom_selection);
-
-               e_html_editor_selection_save (selection);
+               dialog->priv->label_autofill = FALSE;
        }
+       g_free (text);
 
        /* Chain up to parent implementation */
        GTK_WIDGET_CLASS (e_html_editor_link_dialog_parent_class)->show (widget);
 }
 
 static void
-html_editor_link_dialog_hide (GtkWidget *widget)
-{
-       EHTMLEditorLinkDialogPrivate *priv;
-       EHTMLEditorViewHistoryEvent *ev;
-
-       priv = E_HTML_EDITOR_LINK_DIALOG_GET_PRIVATE (widget);
-       ev = priv->history_event;
-
-       if (priv->unlinking || !priv->link_element) {
-               g_clear_object (&ev->data.dom.from);
-               g_free (ev);
-       } else if (ev) {
-               EHTMLEditorLinkDialog *dialog;
-               EHTMLEditor *editor;
-               EHTMLEditorSelection *selection;
-               EHTMLEditorView *view;
-
-               dialog = E_HTML_EDITOR_LINK_DIALOG (widget);
-               editor = e_html_editor_dialog_get_editor (E_HTML_EDITOR_DIALOG (dialog));
-               view = e_html_editor_get_view (editor);
-               selection = e_html_editor_view_get_selection (view);
-
-               ev->data.dom.to = webkit_dom_node_clone_node (
-                       WEBKIT_DOM_NODE (priv->link_element), TRUE);
-
-               if (!webkit_dom_node_is_equal_node (ev->data.dom.from, ev->data.dom.to)) {
-                       e_html_editor_selection_get_selection_coordinates (
-                               selection, &ev->after.start.x, &ev->after.start.y, &ev->after.end.x, 
&ev->after.end.y);
-                       e_html_editor_view_insert_new_history_event (view, ev);
-                       if (!ev->data.dom.from)
-                               g_object_unref (priv->link_element);
-               } else {
-                       g_object_unref (ev->data.dom.from);
-                       g_object_unref (ev->data.dom.to);
-                       g_free (ev);
-               }
-       }
-
-       priv->link_element = NULL;
-
-       GTK_WIDGET_CLASS (e_html_editor_link_dialog_parent_class)->hide (widget);
-}
-
-static void
 e_html_editor_link_dialog_class_init (EHTMLEditorLinkDialogClass *class)
 {
        GtkWidgetClass *widget_class;
@@ -383,7 +206,6 @@ e_html_editor_link_dialog_init (EHTMLEditorLinkDialog *dialog)
        GtkWidget *widget;
 
        dialog->priv = E_HTML_EDITOR_LINK_DIALOG_GET_PRIVATE (dialog);
-       dialog->priv->link_element = NULL;
 
        main_layout = e_html_editor_dialog_get_container (E_HTML_EDITOR_DIALOG (dialog));
 
@@ -454,17 +276,3 @@ e_html_editor_link_dialog_new (EHTMLEditor *editor)
                        "title", _("Link Properties"),
                        NULL));
 }
-
-void
-e_html_editor_link_dialog_show (EHTMLEditorLinkDialog *dialog,
-                                WebKitDOMNode *link)
-{
-       EHTMLEditorLinkDialogClass *class;
-
-       g_return_if_fail (E_IS_HTML_EDITOR_LINK_DIALOG (dialog));
-
-       dialog->priv->link_element = link ? WEBKIT_DOM_ELEMENT (link) : NULL;
-
-       class = E_HTML_EDITOR_LINK_DIALOG_GET_CLASS (dialog);
-       GTK_WIDGET_CLASS (class)->show (GTK_WIDGET (dialog));
-}
diff --git a/e-util/e-html-editor-link-dialog.h b/e-util/e-html-editor-link-dialog.h
index c3a9709..a1e426f 100644
--- a/e-util/e-html-editor-link-dialog.h
+++ b/e-util/e-html-editor-link-dialog.h
@@ -65,8 +65,6 @@ GType         e_html_editor_link_dialog_get_type
                                                (void) G_GNUC_CONST;
 GtkWidget *    e_html_editor_link_dialog_new   (EHTMLEditor *editor);
 
-void           e_html_editor_link_dialog_show  (EHTMLEditorLinkDialog *dialog,
-                                                WebKitDOMNode *link);
 G_END_DECLS
 
 #endif /* E_HTML_EDITOR_LINK_DIALOG_H */
diff --git a/e-util/e-html-editor-manager.ui b/e-util/e-html-editor-manager.ui
index 97a4861..fa7bc0b 100644
--- a/e-util/e-html-editor-manager.ui
+++ b/e-util/e-html-editor-manager.ui
@@ -27,7 +27,7 @@
     <placeholder name='pre-insert-menu'>
       <menu action='view-menu'>
         <placeholder name='view-menu-top'/>
-        <menuitem action='webkit-inspector'/>
+        <placeholder name='view-menu-custom'/>
         <separator/>
       </menu>
     </placeholder>
@@ -69,7 +69,6 @@
         <menuitem action='style-normal'/>
         <menuitem action='style-preformat'/>
         <menuitem action='style-address'/>
-        <menuitem action='style-blockquote'/>
         <separator/>
         <menuitem action='style-h1'/>
         <menuitem action='style-h2'/>
@@ -164,8 +163,6 @@
     </menu>
     <separator/>
     <menu action='context-insert-table-menu'>
-      <menuitem action='context-insert-table'/>
-      <separator/>
       <menuitem action='context-insert-row-above'/>
       <menuitem action='context-insert-row-below'/>
       <separator/>
@@ -178,7 +175,5 @@
       <menuitem action='context-delete-column'/>
       <menuitem action='context-delete-cell'/>
     </menu>
-    <separator/>
-    <menu action='context-input-methods-menu'/>
   </popup>
 </ui>
diff --git a/e-util/e-html-editor-page-dialog.c b/e-util/e-html-editor-page-dialog.c
index 9033ff3..2399566 100644
--- a/e-util/e-html-editor-page-dialog.c
+++ b/e-util/e-html-editor-page-dialog.c
@@ -44,8 +44,6 @@ struct _EHTMLEditorPageDialogPrivate {
        GtkWidget *background_image_filechooser;
 
        GtkWidget *remove_image_button;
-
-       EHTMLEditorViewHistoryEvent *history_event;
 };
 
 typedef struct _Template {
@@ -150,85 +148,64 @@ static void
 html_editor_page_dialog_set_text_color (EHTMLEditorPageDialog *dialog)
 {
        EHTMLEditor *editor;
-       EHTMLEditorView *view;
-       WebKitDOMDocument *document;
-       WebKitDOMHTMLElement *body;
+       EContentEditor *cnt_editor;
        GdkRGBA rgba;
-       gchar *color;
 
        editor = e_html_editor_dialog_get_editor (E_HTML_EDITOR_DIALOG (dialog));
-       view = e_html_editor_get_view (editor);
-       document = webkit_web_view_get_dom_document (WEBKIT_WEB_VIEW (view));
-       body = webkit_dom_document_get_body (document);
+       cnt_editor = e_html_editor_get_content_editor (editor);
 
        e_color_combo_get_current_color (
                E_COLOR_COMBO (dialog->priv->text_color_picker), &rgba);
 
-       color = g_strdup_printf ("#%06x", e_rgba_to_value (&rgba));
-       webkit_dom_html_body_element_set_text (
-               WEBKIT_DOM_HTML_BODY_ELEMENT (body), color);
-
-       g_free (color);
+       e_content_editor_page_set_text_color (cnt_editor, &rgba);
 }
 
 static void
 html_editor_page_dialog_set_link_color (EHTMLEditorPageDialog *dialog)
 {
        EHTMLEditor *editor;
-       EHTMLEditorView *view;
+       EContentEditor *cnt_editor;
        GdkRGBA rgba;
 
        editor = e_html_editor_dialog_get_editor (E_HTML_EDITOR_DIALOG (dialog));
-       view = e_html_editor_get_view (editor);
+       cnt_editor = e_html_editor_get_content_editor (editor);
 
        e_color_combo_get_current_color (
                E_COLOR_COMBO (dialog->priv->link_color_picker), &rgba);
 
-       e_html_editor_view_set_link_color (view, &rgba);
+       e_content_editor_page_set_link_color (cnt_editor, &rgba);
 }
 
 static void
 html_editor_page_dialog_set_visited_link_color (EHTMLEditorPageDialog *dialog)
 {
        EHTMLEditor *editor;
-       EHTMLEditorView *view;
+       EContentEditor *cnt_editor;
        GdkRGBA rgba;
 
        editor = e_html_editor_dialog_get_editor (E_HTML_EDITOR_DIALOG (dialog));
-       view = e_html_editor_get_view (editor);
+       cnt_editor = e_html_editor_get_content_editor (editor);
 
        e_color_combo_get_current_color (
                E_COLOR_COMBO (dialog->priv->visited_link_color_picker), &rgba);
 
-       e_html_editor_view_set_visited_link_color (view, &rgba);
+       e_content_editor_page_set_visited_link_color (cnt_editor, &rgba);
 }
 
 static void
 html_editor_page_dialog_set_background_color (EHTMLEditorPageDialog *dialog)
 {
        EHTMLEditor *editor;
-       EHTMLEditorView *view;
-       WebKitDOMDocument *document;
-       WebKitDOMHTMLElement *body;
+       EContentEditor *cnt_editor;
        GdkRGBA rgba;
-       gchar *color;
 
        editor = e_html_editor_dialog_get_editor (E_HTML_EDITOR_DIALOG (dialog));
-       view = e_html_editor_get_view (editor);
-       document = webkit_web_view_get_dom_document (WEBKIT_WEB_VIEW (view));
-       body = webkit_dom_document_get_body (document);
+       cnt_editor = e_html_editor_get_content_editor (editor);
 
        e_color_combo_get_current_color (
                E_COLOR_COMBO (dialog->priv->background_color_picker), &rgba);
-       if (rgba.alpha != 0.0)
-               color = g_strdup_printf ("#%06x", e_rgba_to_value (&rgba));
-       else
-               color = g_strdup ("");
-
-       webkit_dom_html_body_element_set_bg_color (
-               WEBKIT_DOM_HTML_BODY_ELEMENT (body), color);
 
-       g_free (color);
+       e_content_editor_page_set_background_color (cnt_editor, &rgba);
 }
 
 static void
@@ -271,28 +248,16 @@ static void
 html_editor_page_dialog_set_background_image (EHTMLEditorPageDialog *dialog)
 {
        EHTMLEditor *editor;
-       EHTMLEditorView *view;
-       WebKitDOMDocument *document;
-       WebKitDOMHTMLElement *body;
+       EContentEditor *cnt_editor;
        gchar *uri;
 
        editor = e_html_editor_dialog_get_editor (E_HTML_EDITOR_DIALOG (dialog));
-       view = e_html_editor_get_view (editor);
-       document = webkit_web_view_get_dom_document (WEBKIT_WEB_VIEW (view));
-       body = webkit_dom_document_get_body (document);
+       cnt_editor = e_html_editor_get_content_editor (editor);
 
        uri = gtk_file_chooser_get_uri (
-                       GTK_FILE_CHOOSER (
-                               dialog->priv->background_image_filechooser));
-
-       if (uri && *uri)
-               e_html_editor_selection_replace_image_src (
-                       e_html_editor_view_get_selection (view),
-                       WEBKIT_DOM_ELEMENT (body),
-                       uri);
-       else
-               remove_image_attributes_from_element (
-                       WEBKIT_DOM_ELEMENT (body));
+               GTK_FILE_CHOOSER (dialog->priv->background_image_filechooser));
+
+       e_content_editor_page_set_background_image_uri (cnt_editor, uri);
 
        gtk_widget_set_sensitive (dialog->priv->remove_image_button, uri && *uri);
 
@@ -303,16 +268,12 @@ static void
 html_editor_page_dialog_remove_image (EHTMLEditorPageDialog *dialog)
 {
        EHTMLEditor *editor;
-       EHTMLEditorView *view;
-       WebKitDOMDocument *document;
-       WebKitDOMHTMLElement *body;
+       EContentEditor *cnt_editor;
 
        editor = e_html_editor_dialog_get_editor (E_HTML_EDITOR_DIALOG (dialog));
-       view = e_html_editor_get_view (editor);
-       document = webkit_web_view_get_dom_document (WEBKIT_WEB_VIEW (view));
-       body = webkit_dom_document_get_body (document);
+       cnt_editor = e_html_editor_get_content_editor (editor);
 
-       remove_image_attributes_from_element (WEBKIT_DOM_ELEMENT (body));
+       e_content_editor_page_set_background_image_uri (cnt_editor, NULL);
 
        gtk_file_chooser_unselect_all (
                GTK_FILE_CHOOSER (dialog->priv->background_image_filechooser));
@@ -324,44 +285,21 @@ static void
 html_editor_page_dialog_show (GtkWidget *widget)
 {
        EHTMLEditor *editor;
-       EHTMLEditorView *view;
+       EContentEditor *cnt_editor;
        EHTMLEditorPageDialog *dialog;
-       WebKitDOMDocument *document;
-       WebKitDOMHTMLElement *body;
-       gchar *tmp;
        GdkRGBA rgba;
+       gchar *uri;
 
        dialog = E_HTML_EDITOR_PAGE_DIALOG (widget);
        editor = e_html_editor_dialog_get_editor (E_HTML_EDITOR_DIALOG (dialog));
-       view = e_html_editor_get_view (editor);
+       cnt_editor = e_html_editor_get_content_editor (editor);
 
-       /* We have to block the style changes of the view as otherwise the colors
-        * will be changed when this dialog will be shown (as the view will be
-        * unfocused). */
-       e_html_editor_view_block_style_updated_callbacks (view);
-
-       document = webkit_web_view_get_dom_document (WEBKIT_WEB_VIEW (view));
-       body = webkit_dom_document_get_body (document);
-
-       if (!e_html_editor_view_is_undo_redo_in_progress (view)) {
-               EHTMLEditorSelection *selection;
-               EHTMLEditorViewHistoryEvent *ev;
-
-               ev = g_new0 (EHTMLEditorViewHistoryEvent, 1);
-               ev->type = HISTORY_PAGE_DIALOG;
-
-               selection = e_html_editor_view_get_selection (view);
-               e_html_editor_selection_get_selection_coordinates (
-                       selection, &ev->before.start.x, &ev->before.start.y, &ev->before.end.x, 
&ev->before.end.y);
-               ev->data.dom.from = webkit_dom_node_clone_node (WEBKIT_DOM_NODE (body), FALSE);
-               dialog->priv->history_event = ev;
-       }
+       e_content_editor_on_page_dialog_open (cnt_editor);
 
-       tmp = webkit_dom_element_get_attribute (
-               WEBKIT_DOM_ELEMENT (body), "data-uri");
-       if (tmp && *tmp) {
+       uri = e_content_editor_page_get_background_image_uri (cnt_editor);
+       if (uri && *uri) {
                gint ii;
-               gchar *fname = g_filename_from_uri (tmp, NULL, NULL);
+               gchar *fname = g_filename_from_uri (uri, NULL, NULL);
                for (ii = 0; ii < G_N_ELEMENTS (templates); ii++) {
                        const Template *tmplt = &templates[ii];
 
@@ -377,140 +315,39 @@ html_editor_page_dialog_show (GtkWidget *widget)
                gtk_combo_box_set_active (
                        GTK_COMBO_BOX (dialog->priv->background_template_combo), 0);
        }
-       g_free (tmp);
+       g_free (uri);
 
-       tmp = webkit_dom_html_body_element_get_text (
-                       WEBKIT_DOM_HTML_BODY_ELEMENT (body));
-       if (!tmp || !*tmp || !gdk_rgba_parse (&rgba, tmp))
-               e_utils_get_theme_color (widget, "theme_text_color,theme_fg_color", 
E_UTILS_DEFAULT_THEME_TEXT_COLOR, &rgba);
-       g_free (tmp);
+       e_content_editor_page_get_text_color (cnt_editor, &rgba);
        e_color_combo_set_current_color (
                E_COLOR_COMBO (dialog->priv->text_color_picker), &rgba);
 
-       tmp = webkit_dom_html_body_element_get_link (
-                       WEBKIT_DOM_HTML_BODY_ELEMENT (body));
-       if (!gdk_rgba_parse (&rgba, tmp)) {
-               rgba.alpha = 1;
-               rgba.red = 0;
-               rgba.green = 0;
-               rgba.blue = 1;
-       }
-       g_free (tmp);
+       e_content_editor_page_get_link_color (cnt_editor, &rgba);
        e_color_combo_set_current_color (
                E_COLOR_COMBO (dialog->priv->link_color_picker), &rgba);
 
-       tmp = webkit_dom_html_body_element_get_v_link (
-                       WEBKIT_DOM_HTML_BODY_ELEMENT (body));
-       if (!gdk_rgba_parse (&rgba, tmp)) {
-               rgba.alpha = 1;
-               rgba.red = 1;
-               rgba.green = 0;
-               rgba.blue = 0;
-       }
-       g_free (tmp);
+       e_content_editor_page_get_visited_link_color (cnt_editor, &rgba);
        e_color_combo_set_current_color (
                E_COLOR_COMBO (dialog->priv->visited_link_color_picker), &rgba);
 
-       tmp = webkit_dom_html_body_element_get_bg_color (
-                       WEBKIT_DOM_HTML_BODY_ELEMENT (body));
-       if (!tmp || !*tmp || !gdk_rgba_parse (&rgba, tmp))
-               e_utils_get_theme_color (widget, "theme_base_color", E_UTILS_DEFAULT_THEME_BASE_COLOR, &rgba);
-       g_free (tmp);
+       e_content_editor_page_get_background_color (cnt_editor, &rgba);
        e_color_combo_set_current_color (
                E_COLOR_COMBO (dialog->priv->background_color_picker), &rgba);
 
        GTK_WIDGET_CLASS (e_html_editor_page_dialog_parent_class)->show (widget);
 }
 
-static gboolean
-user_changed_content (EHTMLEditorViewHistoryEvent *event)
-{
-       WebKitDOMElement *original, *current;
-       gchar *original_value, *current_value;
-       gboolean changed = TRUE;
-
-       original = WEBKIT_DOM_ELEMENT (event->data.dom.from);
-       current = WEBKIT_DOM_ELEMENT (event->data.dom.to);
-
-       original_value = webkit_dom_element_get_attribute (original, "bgcolor");
-       current_value = webkit_dom_element_get_attribute (current, "bgcolor");
-       changed = g_strcmp0 (original_value, current_value) != 0;
-       g_free (original_value);
-       g_free (current_value);
-       if (changed)
-               return TRUE;
-
-       original_value = webkit_dom_element_get_attribute (original, "text");
-       current_value = webkit_dom_element_get_attribute (current, "text");
-       changed = g_strcmp0 (original_value, current_value) != 0;
-       g_free (original_value);
-       g_free (current_value);
-       if (changed)
-               return TRUE;
-
-       original_value = webkit_dom_element_get_attribute (original, "link");
-       current_value = webkit_dom_element_get_attribute (current, "link");
-       changed = g_strcmp0 (original_value, current_value) != 0;
-       g_free (original_value);
-       g_free (current_value);
-       if (changed)
-               return TRUE;
-
-       original_value = webkit_dom_element_get_attribute (original, "vlink");
-       current_value = webkit_dom_element_get_attribute (current, "vlink");
-       changed = g_strcmp0 (original_value, current_value) != 0;
-       g_free (original_value);
-       g_free (current_value);
-
-       return changed;
-}
-
 static void
 html_editor_page_dialog_hide (GtkWidget *widget)
 {
-       EHTMLEditorPageDialogPrivate *priv;
-       EHTMLEditorViewHistoryEvent *ev;
-       EHTMLEditorPageDialog *dialog;
        EHTMLEditor *editor;
-       EHTMLEditorView *view;
+       EContentEditor *cnt_editor;
+       EHTMLEditorPageDialog *dialog;
 
        dialog = E_HTML_EDITOR_PAGE_DIALOG (widget);
        editor = e_html_editor_dialog_get_editor (E_HTML_EDITOR_DIALOG (dialog));
-       view = e_html_editor_get_view (editor);
-       priv = E_HTML_EDITOR_PAGE_DIALOG_GET_PRIVATE (widget);
-       ev = priv->history_event;
-
-       if (ev) {
-               EHTMLEditorSelection *selection;
-               WebKitDOMDocument *document;
-               WebKitDOMHTMLElement *body;
-
-               selection = e_html_editor_view_get_selection (view);
-
-               document = webkit_web_view_get_dom_document (WEBKIT_WEB_VIEW (view));
-               body = webkit_dom_document_get_body (document);
-
-               ev->data.dom.to = webkit_dom_node_clone_node (WEBKIT_DOM_NODE (body), FALSE);
-
-               /* If user changed any of page colors we have to mark it to send
-                * the correct colors and to disable the color changes when the
-                * view i.e. not focused (at it would overwrite these user set colors. */
-               if (user_changed_content (ev))
-                       webkit_dom_element_set_attribute (
-                               WEBKIT_DOM_ELEMENT (body), "data-user-colors", "", NULL);
-
-               if (!webkit_dom_node_is_equal_node (ev->data.dom.from, ev->data.dom.to)) {
-                       e_html_editor_selection_get_selection_coordinates (
-                               selection, &ev->after.start.x, &ev->after.start.y, &ev->after.end.x, 
&ev->after.end.y);
-                       e_html_editor_view_insert_new_history_event (view, ev);
-               } else {
-                       g_object_unref (ev->data.dom.from);
-                       g_object_unref (ev->data.dom.to);
-                       g_free (ev);
-               }
-       }
+       cnt_editor = e_html_editor_get_content_editor (editor);
 
-       e_html_editor_view_unblock_style_updated_callbacks (view);
+       e_content_editor_on_page_dialog_close (cnt_editor);
 
        GTK_WIDGET_CLASS (e_html_editor_page_dialog_parent_class)->hide (widget);
 }
diff --git a/e-util/e-html-editor-private.h b/e-util/e-html-editor-private.h
index 60f6706..37523a5 100644
--- a/e-util/e-html-editor-private.h
+++ b/e-util/e-html-editor-private.h
@@ -36,7 +36,6 @@
 #include <e-html-editor-spell-check-dialog.h>
 #include <e-html-editor-table-dialog.h>
 #include <e-html-editor-text-dialog.h>
-#include <e-html-editor-view.h>
 
 #ifdef HAVE_XFREE
 #include <X11/XF86keysym.h>
@@ -84,21 +83,24 @@ struct _EHTMLEditorPrivate {
        GtkWidget *style_combo_box;
        GtkWidget *scrolled_window;
 
-       EHTMLEditorView *html_editor_view;
-       EHTMLEditorSelection *selection;
+       GHashTable *content_editors;
+       EContentEditor *use_content_editor;
+
+       EContentEditorNodeFlags node_flags;
 
        gchar *filename;
 
        guint spell_suggestions_merge_id;
 
-       WebKitDOMNode *image;
-       WebKitDOMNode *table_cell;
-       WebKitDOMNode *current_node;
-
        gint editor_layout_row;
+
+       gboolean is_testing;
 };
 
 void           editor_actions_init             (EHTMLEditor *editor);
+void           editor_actions_bind             (EHTMLEditor *editor);
+const gchar *  e_html_editor_get_content_editor_name
+                                               (EHTMLEditor *editor);
 
 G_END_DECLS
 
diff --git a/e-util/e-html-editor-replace-dialog.c b/e-util/e-html-editor-replace-dialog.c
index d21bef2..59b69a7 100644
--- a/e-util/e-html-editor-replace-dialog.c
+++ b/e-util/e-html-editor-replace-dialog.c
@@ -49,7 +49,9 @@ struct _EHTMLEditorReplaceDialogPrivate {
        GtkWidget *replace_button;
        GtkWidget *replace_all_button;
 
-       EHTMLEditor *editor;
+       EContentEditor *cnt_editor;
+       gulong find_done_handler_id;
+       gulong replace_all_done_handler_id;
 };
 
 enum {
@@ -57,130 +59,111 @@ enum {
        PROP_EDITOR
 };
 
-static gboolean
-jump (EHTMLEditorReplaceDialog *dialog)
+static void
+content_editor_find_done_cb (EContentEditor *cnt_editor,
+                            guint match_count,
+                            EHTMLEditorReplaceDialog *dialog)
 {
-       EHTMLEditor *editor;
-       WebKitWebView *web_view;
-       gboolean found;
+       if (match_count) {
+               gtk_widget_hide (dialog->priv->result_label);
+       } else {
+               gtk_label_set_label (GTK_LABEL (dialog->priv->result_label), _("No match found"));
+               gtk_widget_show (dialog->priv->result_label);
+       }
+}
 
-       editor = e_html_editor_dialog_get_editor (E_HTML_EDITOR_DIALOG (dialog));
-       web_view = WEBKIT_WEB_VIEW (
-                       e_html_editor_get_view (editor));
+static void
+replace_occurance (EHTMLEditorReplaceDialog *dialog)
+{
+       gtk_widget_hide (dialog->priv->result_label);
 
-       found = webkit_web_view_search_text (
-               web_view,
-               gtk_entry_get_text (GTK_ENTRY (dialog->priv->search_entry)),
-               gtk_toggle_button_get_active (
-                       GTK_TOGGLE_BUTTON (dialog->priv->case_sensitive)),
-               !gtk_toggle_button_get_active (
-                       GTK_TOGGLE_BUTTON (dialog->priv->backwards)),
-               gtk_toggle_button_get_active (
-                       GTK_TOGGLE_BUTTON (dialog->priv->wrap)));
-
-       return found;
+       e_content_editor_replace (
+               dialog->priv->cnt_editor,
+               gtk_entry_get_text (GTK_ENTRY (dialog->priv->replace_entry)));
 }
 
 static void
-html_editor_replace_dialog_skip_cb (EHTMLEditorReplaceDialog *dialog)
+content_editor_replace_all_done_cb (EContentEditor *cnt_editor,
+                                   guint replaced_count,
+                                   EHTMLEditorReplaceDialog *dialog)
 {
-       if (!jump (dialog)) {
-               gtk_label_set_label (
-                       GTK_LABEL (dialog->priv->result_label),
-                       N_("No match found"));
-               gtk_widget_show (dialog->priv->result_label);
-       } else {
-               gtk_widget_hide (dialog->priv->result_label);
-       }
+       gchar *result;
+
+       result = g_strdup_printf (ngettext("%d occurrence replaced",
+                                          "%d occurrences replaced",
+                                          replaced_count),
+                                replaced_count);
+
+       gtk_label_set_label (GTK_LABEL (dialog->priv->result_label), result);
+       gtk_widget_show (dialog->priv->result_label);
+       g_free (result);
 }
 
-static void
-html_editor_replace_dialog_replace_cb (EHTMLEditorReplaceDialog *dialog)
+static guint32
+replace_dialog_get_find_flags (EHTMLEditorReplaceDialog *dialog)
 {
-       EHTMLEditor *editor;
-       EHTMLEditorView *view;
-       EHTMLEditorSelection *selection;
+       guint32 flags = E_CONTENT_EDITOR_FIND_NEXT;
 
-       /* Jump to next matching word */
-       if (!jump (dialog)) {
-               gtk_label_set_label (
-                       GTK_LABEL (dialog->priv->result_label),
-                       N_("No match found"));
-               gtk_widget_show (dialog->priv->result_label);
-               return;
-       } else {
-               gtk_widget_hide (dialog->priv->result_label);
-       }
+       if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (dialog->priv->backwards)))
+               flags |= E_CONTENT_EDITOR_FIND_MODE_BACKWARDS;
 
-       editor = e_html_editor_dialog_get_editor (E_HTML_EDITOR_DIALOG (dialog));
-       view = e_html_editor_get_view (editor);
-       selection = e_html_editor_view_get_selection (view);
+       if (!gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (dialog->priv->case_sensitive)))
+               flags |= E_CONTENT_EDITOR_FIND_CASE_INSENSITIVE;
 
-       e_html_editor_selection_replace (
-               selection,
-               gtk_entry_get_text (GTK_ENTRY (dialog->priv->replace_entry)));
+       if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (dialog->priv->wrap)))
+               flags |= E_CONTENT_EDITOR_FIND_WRAP_AROUND;
+
+       return flags;
 }
 
 static void
-html_editor_replace_dialog_replace_all_cb (EHTMLEditorReplaceDialog *dialog)
+search (EHTMLEditorReplaceDialog *dialog)
 {
-       gint i = 0;
-       gchar *result;
-       EHTMLEditor *editor;
-       EHTMLEditorView *view;
-       EHTMLEditorViewHistoryEvent *ev = NULL;
-       EHTMLEditorSelection *selection;
-       const gchar *replacement, *search_text;
 
-       editor = e_html_editor_dialog_get_editor (E_HTML_EDITOR_DIALOG (dialog));
-       view = e_html_editor_get_view (editor);
-       selection = e_html_editor_view_get_selection (view);
-       replacement = gtk_entry_get_text (GTK_ENTRY (dialog->priv->replace_entry));
-       search_text = gtk_entry_get_text (GTK_ENTRY (dialog->priv->search_entry));
-
-       while (jump (dialog)) {
-               e_html_editor_selection_replace (selection, replacement);
-               i++;
-
-               /* Jump behind the word */
-               e_html_editor_selection_move (
-                       selection, TRUE, E_HTML_EDITOR_SELECTION_GRANULARITY_WORD);
-       }
+       e_content_editor_find (
+               dialog->priv->cnt_editor,
+               replace_dialog_get_find_flags (dialog),
+               gtk_entry_get_text (GTK_ENTRY (dialog->priv->search_entry)));
+}
 
-       if (i != 0) {
-               if (!e_html_editor_view_is_undo_redo_in_progress (view)) {
-                       ev = g_new0 (EHTMLEditorViewHistoryEvent, 1);
-                       ev->type = HISTORY_REPLACE_ALL;
+static void
+html_editor_replace_dialog_skip_cb (EHTMLEditorReplaceDialog *dialog)
+{
+       search (dialog);
+}
 
-                       ev->data.string.from = g_strdup (search_text);
-                       ev->data.string.to = g_strdup (replacement);
+static void
+html_editor_replace_dialog_replace_cb (EHTMLEditorReplaceDialog *dialog)
+{
+       replace_occurance (dialog);
 
-                       e_html_editor_view_insert_new_history_event (view, ev);
-               }
-               e_html_editor_view_force_spell_check_in_viewport (view);
-       }
+       /* Jump to next matching word */
+       search (dialog);
+}
 
-       result = g_strdup_printf (ngettext("%d occurrence replaced",
-                                          "%d occurrences replaced",
-                                          i),
-                                 i);
-       gtk_label_set_label (GTK_LABEL (dialog->priv->result_label), result);
-       gtk_widget_show (dialog->priv->result_label);
-       g_free (result);
+static void
+html_editor_replace_dialog_replace_all_cb (EHTMLEditorReplaceDialog *dialog)
+{
+       e_content_editor_replace_all (
+               dialog->priv->cnt_editor,
+               replace_dialog_get_find_flags (dialog),
+               gtk_entry_get_text (GTK_ENTRY (dialog->priv->search_entry)),
+               gtk_entry_get_text (GTK_ENTRY (dialog->priv->replace_entry)));
 }
 
 static void
 html_editor_replace_dialog_entry_changed (EHTMLEditorReplaceDialog *dialog)
 {
        gboolean ready;
-       ready = ((gtk_entry_get_text_length (
-                       GTK_ENTRY (dialog->priv->search_entry)) != 0) &&
-                (gtk_entry_get_text_length (
-                        GTK_ENTRY (dialog->priv->replace_entry)) != 0));
+
+       ready = gtk_entry_get_text_length (GTK_ENTRY (dialog->priv->search_entry)) != 0;
 
        gtk_widget_set_sensitive (dialog->priv->skip_button, ready);
        gtk_widget_set_sensitive (dialog->priv->replace_button, ready);
        gtk_widget_set_sensitive (dialog->priv->replace_all_button, ready);
+
+       if (ready)
+               search (dialog);
 }
 
 static void
@@ -188,6 +171,8 @@ html_editor_replace_dialog_show (GtkWidget *widget)
 {
        EHTMLEditorReplaceDialog *dialog = E_HTML_EDITOR_REPLACE_DIALOG (widget);
 
+       e_content_editor_on_replace_dialog_open (dialog->priv->cnt_editor);
+
        gtk_widget_grab_focus (dialog->priv->search_entry);
        gtk_widget_hide (dialog->priv->result_label);
 
@@ -196,14 +181,82 @@ html_editor_replace_dialog_show (GtkWidget *widget)
 }
 
 static void
+html_editor_replace_dialog_hide (GtkWidget *widget)
+{
+       EHTMLEditorReplaceDialog *dialog = E_HTML_EDITOR_REPLACE_DIALOG (widget);
+
+       e_content_editor_on_replace_dialog_close (dialog->priv->cnt_editor);
+
+       /* Chain up to parent implementation */
+       GTK_WIDGET_CLASS (e_html_editor_replace_dialog_parent_class)->hide (widget);
+}
+
+static void
+html_editor_replace_dialog_constructed (GObject *object)
+{
+       EContentEditor *cnt_editor;
+       EHTMLEditor *editor;
+       EHTMLEditorReplaceDialog *dialog;
+
+       dialog = E_HTML_EDITOR_REPLACE_DIALOG (object);
+       dialog->priv = E_HTML_EDITOR_REPLACE_DIALOG_GET_PRIVATE (dialog);
+
+       editor = e_html_editor_dialog_get_editor (E_HTML_EDITOR_DIALOG (dialog));
+       cnt_editor = e_html_editor_get_content_editor (editor);
+
+       dialog->priv->find_done_handler_id = g_signal_connect (
+               cnt_editor, "find-done",
+               G_CALLBACK (content_editor_find_done_cb), dialog);
+
+       dialog->priv->replace_all_done_handler_id = g_signal_connect (
+               cnt_editor, "replace-all-done",
+               G_CALLBACK (content_editor_replace_all_done_cb), dialog);
+
+       dialog->priv->cnt_editor = cnt_editor;
+
+       G_OBJECT_CLASS (e_html_editor_replace_dialog_parent_class)->constructed (object);
+}
+
+static void
+html_editor_replace_dialog_dispose (GObject *object)
+{
+       EHTMLEditorReplaceDialogPrivate *priv;
+
+       priv = E_HTML_EDITOR_REPLACE_DIALOG_GET_PRIVATE (object);
+
+       if (priv->find_done_handler_id > 0) {
+               g_signal_handler_disconnect (
+                       priv->cnt_editor,
+                       priv->find_done_handler_id);
+               priv->find_done_handler_id = 0;
+       }
+
+       if (priv->replace_all_done_handler_id > 0) {
+               g_signal_handler_disconnect (
+                       priv->cnt_editor,
+                       priv->replace_all_done_handler_id);
+               priv->replace_all_done_handler_id = 0;
+       }
+
+       /* Chain up to parent's dispose() method. */
+       G_OBJECT_CLASS (e_html_editor_replace_dialog_parent_class)->dispose (object);
+}
+
+static void
 e_html_editor_replace_dialog_class_init (EHTMLEditorReplaceDialogClass *class)
 {
+       GObjectClass *object_class;
        GtkWidgetClass *widget_class;
 
        g_type_class_add_private (class, sizeof (EHTMLEditorReplaceDialogPrivate));
 
+       object_class = G_OBJECT_CLASS (class);
+       object_class->constructed = html_editor_replace_dialog_constructed;
+       object_class->dispose = html_editor_replace_dialog_dispose;
+
        widget_class = GTK_WIDGET_CLASS (class);
        widget_class->show = html_editor_replace_dialog_show;
+       widget_class->hide = html_editor_replace_dialog_hide;
 }
 
 static void
@@ -232,9 +285,6 @@ e_html_editor_replace_dialog_init (EHTMLEditorReplaceDialog *dialog)
        widget = gtk_entry_new ();
        gtk_grid_attach (main_layout, widget, 1, 1, 2, 1);
        dialog->priv->replace_entry = widget;
-       g_signal_connect_swapped (
-               widget, "notify::text-length",
-               G_CALLBACK (html_editor_replace_dialog_entry_changed), dialog);
 
        widget = gtk_label_new_with_mnemonic (_("_With:"));
        gtk_label_set_mnemonic_widget (GTK_LABEL (widget), dialog->priv->replace_entry);
diff --git a/e-util/e-html-editor-spell-check-dialog.c b/e-util/e-html-editor-spell-check-dialog.c
index 5d83ec2..d7447e0 100644
--- a/e-util/e-html-editor-spell-check-dialog.c
+++ b/e-util/e-html-editor-spell-check-dialog.c
@@ -27,7 +27,6 @@
 #include <glib/gi18n-lib.h>
 #include <enchant/enchant.h>
 
-#include "e-html-editor-view.h"
 #include "e-spell-checker.h"
 #include "e-spell-dictionary.h"
 
@@ -48,8 +47,6 @@ struct _EHTMLEditorSpellCheckDialogPrivate {
        GtkWidget *suggestion_label;
        GtkWidget *tree_view;
 
-       WebKitDOMDOMSelection *selection;
-
        gchar *word;
        ESpellDictionary *current_dict;
 };
@@ -70,7 +67,7 @@ html_editor_spell_check_dialog_set_word (EHTMLEditorSpellCheckDialog *dialog,
                                          const gchar *word)
 {
        EHTMLEditor *editor;
-       EHTMLEditorView *view;
+       EContentEditor *cnt_editor;
        GtkTreeView *tree_view;
        GtkListStore *store;
        gchar *markup;
@@ -127,97 +124,27 @@ html_editor_spell_check_dialog_set_word (EHTMLEditorSpellCheckDialog *dialog,
         * given to WebKit, because this dialog is modal, but it satisfies
         * it in a way that it paints the selection :) */
        editor = e_html_editor_dialog_get_editor (E_HTML_EDITOR_DIALOG (dialog));
-       view = e_html_editor_get_view (editor);
-       gtk_widget_grab_focus (GTK_WIDGET (view));
-}
-
-static gboolean
-select_next_word (EHTMLEditorSpellCheckDialog *dialog)
-{
-       WebKitDOMNode *anchor, *focus;
-       gulong anchor_offset, focus_offset;
-
-       anchor = webkit_dom_dom_selection_get_anchor_node (dialog->priv->selection);
-       anchor_offset = webkit_dom_dom_selection_get_anchor_offset (dialog->priv->selection);
-
-       focus = webkit_dom_dom_selection_get_focus_node (dialog->priv->selection);
-       focus_offset = webkit_dom_dom_selection_get_focus_offset (dialog->priv->selection);
-
-       /* Jump _behind_ next word */
-       webkit_dom_dom_selection_modify (
-               dialog->priv->selection, "move", "forward", "word");
-       /* Jump before the word */
-       webkit_dom_dom_selection_modify (
-               dialog->priv->selection, "move", "backward", "word");
-       /* Select it */
-       webkit_dom_dom_selection_modify (
-               dialog->priv->selection, "extend", "forward", "word");
-
-       /* If the selection didn't change, then we have most probably
-        * reached the end of document - return FALSE */
-       return !((anchor == webkit_dom_dom_selection_get_anchor_node (
-                               dialog->priv->selection)) &&
-                (anchor_offset == webkit_dom_dom_selection_get_anchor_offset (
-                               dialog->priv->selection)) &&
-                (focus == webkit_dom_dom_selection_get_focus_node (
-                               dialog->priv->selection)) &&
-                (focus_offset == webkit_dom_dom_selection_get_focus_offset (
-                               dialog->priv->selection)));
+       cnt_editor = e_html_editor_get_content_editor (editor);
+       gtk_widget_grab_focus (GTK_WIDGET (cnt_editor));
 }
 
 static gboolean
 html_editor_spell_check_dialog_next (EHTMLEditorSpellCheckDialog *dialog)
 {
-       WebKitDOMNode *start = NULL, *end = NULL;
-       gulong start_offset = 0, end_offset = 0;
-
-       if (dialog->priv->word == NULL) {
-               webkit_dom_dom_selection_modify (
-                       dialog->priv->selection, "move", "left", "documentboundary");
-       } else {
-               /* Remember last selected word */
-               start = webkit_dom_dom_selection_get_anchor_node (
-                       dialog->priv->selection);
-               end = webkit_dom_dom_selection_get_focus_node (
-                       dialog->priv->selection);
-               start_offset = webkit_dom_dom_selection_get_anchor_offset (
-                       dialog->priv->selection);
-               end_offset = webkit_dom_dom_selection_get_focus_offset (
-                       dialog->priv->selection);
-       }
+       EHTMLEditor *editor;
+       EContentEditor *cnt_editor;
+       gchar *next_word;
 
-       while (select_next_word (dialog)) {
-               WebKitDOMRange *range;
-               WebKitSpellChecker *checker;
-               gint loc, len;
-               gchar *word;
-
-               range = webkit_dom_dom_selection_get_range_at (
-                       dialog->priv->selection, 0, NULL);
-               word = webkit_dom_range_get_text (range);
-               g_object_unref (range);
-
-               checker = WEBKIT_SPELL_CHECKER (webkit_get_text_checker ());
-               webkit_spell_checker_check_spelling_of_string (
-                       checker, word, &loc, &len);
-
-               /* Found misspelled word! */
-               if (loc != -1) {
-                       html_editor_spell_check_dialog_set_word (dialog, word);
-                       g_free (word);
-                       return TRUE;
-               }
-
-               g_free (word);
-       }
+       editor = e_html_editor_dialog_get_editor (E_HTML_EDITOR_DIALOG (dialog));
+       cnt_editor = e_html_editor_get_content_editor (editor);
 
-       /* Restore the selection to contain the last misspelled word. This is
-        * reached only when we reach the end of the document */
-       if (start && end) {
-               webkit_dom_dom_selection_set_base_and_extent (
-                       dialog->priv->selection, start, start_offset,
-                       end, end_offset, NULL);
+       next_word = e_content_editor_spell_check_next_word (cnt_editor, dialog->priv->word);
+       if (next_word && *next_word) {
+               html_editor_spell_check_dialog_set_word (dialog, next_word);
+               g_free (next_word);
+               return TRUE;
        }
+       g_free (next_word);
 
        /* Close the dialog */
        gtk_widget_hide (GTK_WIDGET (dialog));
@@ -225,117 +152,53 @@ html_editor_spell_check_dialog_next (EHTMLEditorSpellCheckDialog *dialog)
 }
 
 static gboolean
-select_previous_word (EHTMLEditorSpellCheckDialog *dialog)
-{
-       WebKitDOMNode *old_anchor_node;
-       WebKitDOMNode *new_anchor_node;
-       gulong old_anchor_offset;
-       gulong new_anchor_offset;
-
-       old_anchor_node = webkit_dom_dom_selection_get_anchor_node (
-               dialog->priv->selection);
-       old_anchor_offset = webkit_dom_dom_selection_get_anchor_offset (
-               dialog->priv->selection);
-
-       /* Jump on the beginning of current word */
-       webkit_dom_dom_selection_modify (
-               dialog->priv->selection, "move", "backward", "word");
-       /* Jump before previous word */
-       webkit_dom_dom_selection_modify (
-               dialog->priv->selection, "move", "backward", "word");
-       /* Select it */
-       webkit_dom_dom_selection_modify (
-               dialog->priv->selection, "extend", "forward", "word");
-
-       /* If the selection start didn't change, then we have most probably
-        * reached the beginnig of document. Return FALSE */
-
-       new_anchor_node = webkit_dom_dom_selection_get_anchor_node (
-               dialog->priv->selection);
-       new_anchor_offset = webkit_dom_dom_selection_get_anchor_offset (
-               dialog->priv->selection);
-
-       return (new_anchor_node != old_anchor_node) ||
-               (new_anchor_offset != old_anchor_offset);
-}
-
-static gboolean
 html_editor_spell_check_dialog_prev (EHTMLEditorSpellCheckDialog *dialog)
 {
-       WebKitDOMNode *start = NULL, *end = NULL;
-       gulong start_offset = 0, end_offset = 0;
-
-       if (dialog->priv->word == NULL) {
-               webkit_dom_dom_selection_modify (
-                       dialog->priv->selection,
-                       "move", "right", "documentboundary");
-               webkit_dom_dom_selection_modify (
-                       dialog->priv->selection,
-                       "extend", "backward", "word");
-       } else {
-               /* Remember last selected word */
-               start = webkit_dom_dom_selection_get_anchor_node (
-                       dialog->priv->selection);
-               end = webkit_dom_dom_selection_get_focus_node (
-                       dialog->priv->selection);
-               start_offset = webkit_dom_dom_selection_get_anchor_offset (
-                       dialog->priv->selection);
-               end_offset = webkit_dom_dom_selection_get_focus_offset (
-                       dialog->priv->selection);
-       }
+       EHTMLEditor *editor;
+       EContentEditor *cnt_editor;
+       gchar *prev_word;
 
-       while (select_previous_word (dialog)) {
-               WebKitDOMRange *range;
-               WebKitSpellChecker *checker;
-               gint loc, len;
-               gchar *word;
-
-               range = webkit_dom_dom_selection_get_range_at (
-                       dialog->priv->selection, 0, NULL);
-               word = webkit_dom_range_get_text (range);
-               g_object_unref (range);
-
-               checker = WEBKIT_SPELL_CHECKER (webkit_get_text_checker ());
-               webkit_spell_checker_check_spelling_of_string (
-                       checker, word, &loc, &len);
-
-               /* Found misspelled word! */
-               if (loc != -1) {
-                       html_editor_spell_check_dialog_set_word (dialog, word);
-                       g_free (word);
-                       return TRUE;
-               }
-
-               g_free (word);
-       }
+       editor = e_html_editor_dialog_get_editor (E_HTML_EDITOR_DIALOG (dialog));
+       cnt_editor = e_html_editor_get_content_editor (editor);
 
-       /* Restore the selection to contain the last misspelled word. This is
-        * reached only when we reach the beginning of the document */
-       if (start && end) {
-               webkit_dom_dom_selection_set_base_and_extent (
-                       dialog->priv->selection, start, start_offset,
-                       end, end_offset, NULL);
+       prev_word = e_content_editor_spell_check_prev_word (cnt_editor, dialog->priv->word);
+       if (prev_word && *prev_word) {
+               html_editor_spell_check_dialog_set_word (dialog, prev_word);
+               g_free (prev_word);
+               return TRUE;
        }
+       g_free (prev_word);
 
        /* Close the dialog */
        gtk_widget_hide (GTK_WIDGET (dialog));
        return FALSE;
 }
 
+static gboolean
+html_editor_spell_check_dialog_next_idle_cb (gpointer user_data)
+{
+       EHTMLEditorSpellCheckDialog *dialog = user_data;
+
+       g_return_val_if_fail (E_IS_HTML_EDITOR_SPELL_CHECK_DIALOG (dialog), FALSE);
+
+       html_editor_spell_check_dialog_next (dialog);
+       g_object_unref (dialog);
+
+       return FALSE;
+}
+
 static void
 html_editor_spell_check_dialog_replace (EHTMLEditorSpellCheckDialog *dialog)
 {
        EHTMLEditor *editor;
-       EHTMLEditorView *view;
-       EHTMLEditorSelection *editor_selection;
+       EContentEditor *cnt_editor;
        GtkTreeModel *model;
        GtkTreeSelection *selection;
        GtkTreeIter iter;
        gchar *replacement;
 
        editor = e_html_editor_dialog_get_editor (E_HTML_EDITOR_DIALOG (dialog));
-       view = e_html_editor_get_view (editor);
-       editor_selection = e_html_editor_view_get_selection (view);
+       cnt_editor = e_html_editor_get_content_editor (editor);
 
        selection = gtk_tree_view_get_selection (
                GTK_TREE_VIEW (dialog->priv->tree_view));
@@ -343,47 +206,43 @@ html_editor_spell_check_dialog_replace (EHTMLEditorSpellCheckDialog *dialog)
                return;
        gtk_tree_model_get (model, &iter, 0, &replacement, -1);
 
-       e_html_editor_selection_insert_html (
-               editor_selection, replacement);
+       e_content_editor_insert_content (
+               cnt_editor,
+               replacement,
+               E_CONTENT_EDITOR_INSERT_TEXT_PLAIN);
 
        g_free (replacement);
-       html_editor_spell_check_dialog_next (dialog);
+
+       g_idle_add (html_editor_spell_check_dialog_next_idle_cb, g_object_ref (dialog));
 }
 
 static void
 html_editor_spell_check_dialog_replace_all (EHTMLEditorSpellCheckDialog *dialog)
 {
        EHTMLEditor *editor;
-       EHTMLEditorView *view;
-       EHTMLEditorSelection *editor_selection;
+       EContentEditor *cnt_editor;
        GtkTreeModel *model;
        GtkTreeSelection *selection;
        GtkTreeIter iter;
        gchar *replacement;
 
-       editor = e_html_editor_dialog_get_editor (E_HTML_EDITOR_DIALOG (dialog));
-       view = e_html_editor_get_view (editor);
-       editor_selection = e_html_editor_view_get_selection (view);
-
        selection = gtk_tree_view_get_selection (
                GTK_TREE_VIEW (dialog->priv->tree_view));
        if (!gtk_tree_selection_get_selected (selection, &model, &iter))
                return;
        gtk_tree_model_get (model, &iter, 0, &replacement, -1);
 
-       /* Repeatedly search for 'word', then replace selection by
-        * 'replacement'. Repeat until there's at least one occurrence of
-        * 'word' in the document */
-       while (webkit_web_view_search_text (
-                       WEBKIT_WEB_VIEW (view), dialog->priv->word,
-                       FALSE, TRUE, TRUE)) {
+       editor = e_html_editor_dialog_get_editor (E_HTML_EDITOR_DIALOG (dialog));
+       cnt_editor = e_html_editor_get_content_editor (editor);
 
-               e_html_editor_selection_insert_html (
-                       editor_selection, replacement);
-       }
+       e_content_editor_replace_all (
+               cnt_editor,
+               E_CONTENT_EDITOR_FIND_CASE_INSENSITIVE |
+               E_CONTENT_EDITOR_FIND_WRAP_AROUND,
+               dialog->priv->word,
+               replacement);
 
-       g_free (replacement);
-       html_editor_spell_check_dialog_next (dialog);
+       g_idle_add (html_editor_spell_check_dialog_next_idle_cb, g_object_ref (dialog));
 }
 
 static void
@@ -391,7 +250,6 @@ html_editor_spell_check_dialog_ignore (EHTMLEditorSpellCheckDialog *dialog)
 {
        if (dialog->priv->word == NULL)
                return;
-
        e_spell_dictionary_ignore_word (
                dialog->priv->current_dict, dialog->priv->word, -1);
 
@@ -404,8 +262,7 @@ html_editor_spell_check_dialog_learn (EHTMLEditorSpellCheckDialog *dialog)
        if (dialog->priv->word == NULL)
                return;
 
-       e_spell_dictionary_learn_word (
-               dialog->priv->current_dict, dialog->priv->word, -1);
+       e_spell_dictionary_learn_word (dialog->priv->current_dict, dialog->priv->word, -1);
 
        html_editor_spell_check_dialog_next (dialog);
 }
@@ -434,45 +291,54 @@ html_editor_spell_check_dialog_set_dictionary (EHTMLEditorSpellCheckDialog *dial
 static void
 html_editor_spell_check_dialog_show (GtkWidget *widget)
 {
-       EHTMLEditor *editor;
-       EHTMLEditorView *view;
        EHTMLEditorSpellCheckDialog *dialog;
-       WebKitDOMDocument *document;
-       WebKitDOMDOMWindow *dom_window;
 
        dialog = E_HTML_EDITOR_SPELL_CHECK_DIALOG (widget);
 
        g_free (dialog->priv->word);
        dialog->priv->word = NULL;
 
-       editor = e_html_editor_dialog_get_editor (E_HTML_EDITOR_DIALOG (dialog));
-       view = e_html_editor_get_view (editor);
-
-       document = webkit_web_view_get_dom_document (WEBKIT_WEB_VIEW (view));
-       dom_window = webkit_dom_document_get_default_view (document);
-       dialog->priv->selection = webkit_dom_dom_window_get_selection (dom_window);
-       g_object_unref (dom_window);
-
        /* Select the first word or quit */
        if (html_editor_spell_check_dialog_next (dialog)) {
-               GTK_WIDGET_CLASS (e_html_editor_spell_check_dialog_parent_class)->
-                       show (widget);
+               EHTMLEditor *editor;
+               EContentEditor *cnt_editor;
+
+               editor = e_html_editor_dialog_get_editor (E_HTML_EDITOR_DIALOG (dialog));
+               cnt_editor = e_html_editor_get_content_editor (editor);
+
+               e_content_editor_on_spell_check_dialog_open (cnt_editor);
+
+               GTK_WIDGET_CLASS (e_html_editor_spell_check_dialog_parent_class)->show (widget);
        }
 }
 
 static void
+html_editor_spell_check_dialog_hide (GtkWidget *widget)
+{
+       EContentEditor *cnt_editor;
+       EHTMLEditor *editor;
+       EHTMLEditorSpellCheckDialog *dialog = E_HTML_EDITOR_SPELL_CHECK_DIALOG (widget);
+
+       editor = e_html_editor_dialog_get_editor (E_HTML_EDITOR_DIALOG (dialog));
+       cnt_editor = e_html_editor_get_content_editor (editor);
+
+       e_content_editor_on_spell_check_dialog_close (cnt_editor);
+
+       /* Chain up to parent implementation */
+       GTK_WIDGET_CLASS (e_html_editor_spell_check_dialog_parent_class)->hide (widget);
+}
+
+static void
 html_editor_spell_check_dialog_finalize (GObject *object)
 {
        EHTMLEditorSpellCheckDialogPrivate *priv;
 
        priv = E_HTML_EDITOR_SPELL_CHECK_DIALOG_GET_PRIVATE (object);
 
-       g_clear_object (&priv->selection);
        g_free (priv->word);
 
        /* Chain up to parent's finalize() method. */
-       G_OBJECT_CLASS (e_html_editor_spell_check_dialog_parent_class)->
-               finalize (object);
+       G_OBJECT_CLASS (e_html_editor_spell_check_dialog_parent_class)->finalize (object);
 }
 
 static void
@@ -484,6 +350,7 @@ html_editor_spell_check_dialog_constructed (GObject *object)
        G_OBJECT_CLASS (e_html_editor_spell_check_dialog_parent_class)->constructed (object);
 
        dialog = E_HTML_EDITOR_SPELL_CHECK_DIALOG (object);
+
        e_html_editor_spell_check_dialog_update_dictionaries (dialog);
 }
 
@@ -502,6 +369,7 @@ e_html_editor_spell_check_dialog_class_init (EHTMLEditorSpellCheckDialogClass *c
 
        widget_class = GTK_WIDGET_CLASS (class);
        widget_class->show = html_editor_spell_check_dialog_show;
+       widget_class->hide = html_editor_spell_check_dialog_hide;
 }
 
 static void
@@ -644,10 +512,10 @@ void
 e_html_editor_spell_check_dialog_update_dictionaries (EHTMLEditorSpellCheckDialog *dialog)
 {
        EHTMLEditor *editor;
-       EHTMLEditorView *view;
+       EContentEditor *cnt_editor;
        ESpellChecker *spell_checker;
        GtkComboBox *combo_box;
-       GtkListStore *store;
+       GtkListStore *store = NULL;
        GQueue queue = G_QUEUE_INIT;
        gchar **languages;
        guint n_languages = 0;
@@ -656,8 +524,8 @@ e_html_editor_spell_check_dialog_update_dictionaries (EHTMLEditorSpellCheckDialo
        g_return_if_fail (E_IS_HTML_EDITOR_SPELL_CHECK_DIALOG (dialog));
 
        editor = e_html_editor_dialog_get_editor (E_HTML_EDITOR_DIALOG (dialog));
-       view = e_html_editor_get_view (editor);
-       spell_checker = e_html_editor_view_get_spell_checker (view);
+       cnt_editor = e_html_editor_get_content_editor (editor);
+       spell_checker = e_content_editor_ref_spell_checker (cnt_editor);
 
        languages = e_spell_checker_list_active_languages (
                spell_checker, &n_languages);
@@ -684,7 +552,7 @@ e_html_editor_spell_check_dialog_update_dictionaries (EHTMLEditorSpellCheckDialo
        while (!g_queue_is_empty (&queue)) {
                ESpellDictionary *dictionary;
                GtkTreeIter iter;
-               const gchar *name;
+               const gchar *name = NULL;
 
                dictionary = g_queue_pop_head (&queue);
                name = e_spell_dictionary_get_name (dictionary);
@@ -705,5 +573,6 @@ e_html_editor_spell_check_dialog_update_dictionaries (EHTMLEditorSpellCheckDialo
        gtk_combo_box_set_active (combo_box, 0);
 
        g_object_unref (store);
+       g_clear_object (&spell_checker);
 }
 
diff --git a/e-util/e-html-editor-table-dialog.c b/e-util/e-html-editor-table-dialog.c
index f7155d6..370b72f 100644
--- a/e-util/e-html-editor-table-dialog.c
+++ b/e-util/e-html-editor-table-dialog.c
@@ -28,7 +28,6 @@
 
 #include "e-color-combo.h"
 #include "e-dialog-widgets.h"
-#include "e-html-editor-utils.h"
 #include "e-image-chooser-dialog.h"
 #include "e-misc-utils.h"
 
@@ -50,15 +49,11 @@ struct _EHTMLEditorTableDialogPrivate {
 
        GtkWidget *alignment_combo;
 
-       GtkWidget *background_color_button;
-       GtkWidget *background_image_button;
+       GtkWidget *background_color_picker;
+       GtkWidget *background_image_chooser;
        GtkWidget *image_chooser_dialog;
 
        GtkWidget *remove_image_button;
-
-       WebKitDOMHTMLTableElement *table_element;
-
-       EHTMLEditorViewHistoryEvent *history_event;
 };
 
 static GdkRGBA transparent = { 0, 0, 0, 0 };
@@ -68,267 +63,145 @@ G_DEFINE_TYPE (
        e_html_editor_table_dialog,
        E_TYPE_HTML_EDITOR_DIALOG);
 
-static WebKitDOMElement *
-html_editor_table_dialog_create_table (EHTMLEditorTableDialog *dialog)
+static void
+html_editor_table_dialog_set_row_count (EHTMLEditorTableDialog *dialog)
 {
        EHTMLEditor *editor;
-       EHTMLEditorSelection *editor_selection;
-       EHTMLEditorView *view;
-       gint i;
-       gchar *text_content;
-       gboolean empty = FALSE;
-       WebKitDOMDocument *document;
-       WebKitDOMElement *table, *br, *caret, *element, *cell;
-       WebKitDOMNode *clone;
+       EContentEditor *cnt_editor;
 
        editor = e_html_editor_dialog_get_editor (E_HTML_EDITOR_DIALOG (dialog));
-       view = e_html_editor_get_view (editor);
-       editor_selection = e_html_editor_view_get_selection (view);
-
-       document = webkit_web_view_get_dom_document (WEBKIT_WEB_VIEW (view));
-
-       /* Default 3x3 table */
-       table = webkit_dom_document_create_element (document, "TABLE", NULL);
-       for (i = 0; i < 3; i++) {
-               WebKitDOMHTMLElement *row;
-               gint j;
-
-               row = webkit_dom_html_table_element_insert_row (
-                       WEBKIT_DOM_HTML_TABLE_ELEMENT (table), -1, NULL);
-
-               for (j = 0; j < 3; j++) {
-                       webkit_dom_html_table_row_element_insert_cell (
-                               WEBKIT_DOM_HTML_TABLE_ROW_ELEMENT (row), -1, NULL);
-               }
-       }
+       cnt_editor = e_html_editor_get_content_editor (editor);
 
-       e_html_editor_selection_save (editor_selection);
-       caret = webkit_dom_document_get_element_by_id (
-               document, "-x-evo-selection-end-marker");
-
-       element = get_parent_block_element (WEBKIT_DOM_NODE (caret));
-       text_content = webkit_dom_node_get_text_content (WEBKIT_DOM_NODE (element));
-       empty = text_content && !*text_content;
-       g_free (text_content);
-
-       clone = webkit_dom_node_clone_node (WEBKIT_DOM_NODE (element), FALSE);
-       br = webkit_dom_document_create_element (document, "BR", NULL);
-       webkit_dom_node_append_child (clone, WEBKIT_DOM_NODE (br), NULL);
-       webkit_dom_node_insert_before (
-               webkit_dom_node_get_parent_node (WEBKIT_DOM_NODE (element)),
-               clone,
-               webkit_dom_node_get_next_sibling (WEBKIT_DOM_NODE (element)),
-               NULL);
-
-       /* Move caret to the first cell */
-       cell = webkit_dom_element_query_selector (table, "td", NULL);
-       webkit_dom_node_append_child (
-               WEBKIT_DOM_NODE (cell), WEBKIT_DOM_NODE (caret), NULL);
-       caret = webkit_dom_document_get_element_by_id (
-               document, "-x-evo-selection-start-marker");
-       webkit_dom_node_insert_before (
-               WEBKIT_DOM_NODE (cell),
-               WEBKIT_DOM_NODE (caret),
-               webkit_dom_node_get_last_child (WEBKIT_DOM_NODE (cell)),
-               NULL);
-
-       /* Insert the table into body unred the current block (if current block is not empty)
-        * otherwise replace the current block. */
-       if (empty) {
-               webkit_dom_node_replace_child (
-                       webkit_dom_node_get_parent_node (WEBKIT_DOM_NODE (element)),
-                       WEBKIT_DOM_NODE (table),
-                       WEBKIT_DOM_NODE (element),
-                       NULL);
-       } else {
-               webkit_dom_node_insert_before (
-                       webkit_dom_node_get_parent_node (WEBKIT_DOM_NODE (element)),
-                       WEBKIT_DOM_NODE (table),
-                       webkit_dom_node_get_next_sibling (WEBKIT_DOM_NODE (element)),
-                       NULL);
-       }
-
-       e_html_editor_selection_restore (editor_selection);
-
-       e_html_editor_view_set_changed (view, TRUE);
-
-       return table;
-}
-
-static void
-html_editor_table_dialog_set_row_count (EHTMLEditorTableDialog *dialog)
-{
-       WebKitDOMHTMLCollection *rows;
-       gulong ii, current_count, expected_count;
-
-       g_return_if_fail (dialog->priv->table_element);
-
-       rows = webkit_dom_html_table_element_get_rows (dialog->priv->table_element);
-       current_count = webkit_dom_html_collection_get_length (rows);
-       expected_count = gtk_spin_button_get_value (
-                               GTK_SPIN_BUTTON (dialog->priv->rows_edit));
-
-       if (current_count < expected_count) {
-               for (ii = 0; ii < expected_count - current_count; ii++) {
-                       webkit_dom_html_table_element_insert_row (
-                               dialog->priv->table_element, -1, NULL);
-               }
-       } else if (current_count > expected_count) {
-               for (ii = 0; ii < current_count - expected_count; ii++) {
-                       webkit_dom_html_table_element_delete_row (
-                               dialog->priv->table_element, -1, NULL);
-               }
-       }
-       g_object_unref (rows);
+       e_content_editor_table_set_row_count (
+               cnt_editor,
+               gtk_spin_button_get_value (
+                       GTK_SPIN_BUTTON (dialog->priv->rows_edit)));
 }
 
 static void
 html_editor_table_dialog_get_row_count (EHTMLEditorTableDialog *dialog)
 {
-       WebKitDOMHTMLCollection *rows;
-
-       g_return_if_fail (dialog->priv->table_element);
+       EHTMLEditor *editor;
+       EContentEditor *cnt_editor;
 
-       rows = webkit_dom_html_table_element_get_rows (dialog->priv->table_element);
+       editor = e_html_editor_dialog_get_editor (E_HTML_EDITOR_DIALOG (dialog));
+       cnt_editor = e_html_editor_get_content_editor (editor);
 
        gtk_spin_button_set_value (
                GTK_SPIN_BUTTON (dialog->priv->rows_edit),
-               webkit_dom_html_collection_get_length (rows));
-       g_object_unref (rows);
+               e_content_editor_table_get_row_count (cnt_editor));
 }
 
 static void
 html_editor_table_dialog_set_column_count (EHTMLEditorTableDialog *dialog)
 {
-       WebKitDOMHTMLCollection *rows;
-       gulong ii, row_count, expected_columns;
-
-       g_return_if_fail (dialog->priv->table_element);
-
-       rows = webkit_dom_html_table_element_get_rows (dialog->priv->table_element);
-       row_count = webkit_dom_html_collection_get_length (rows);
-       expected_columns = gtk_spin_button_get_value (
-                       GTK_SPIN_BUTTON (dialog->priv->columns_edit));
-
-       for (ii = 0; ii < row_count; ii++) {
-               WebKitDOMHTMLTableRowElement *row;
-               WebKitDOMHTMLCollection *cells;
-               gulong jj, current_columns;
-
-               row = WEBKIT_DOM_HTML_TABLE_ROW_ELEMENT (
-                       webkit_dom_html_collection_item (rows, ii));
-
-               cells = webkit_dom_html_table_row_element_get_cells (row);
-               current_columns = webkit_dom_html_collection_get_length (cells);
-
-               if (current_columns < expected_columns) {
-                       for (jj = 0; jj < expected_columns - current_columns; jj++) {
-                               webkit_dom_html_table_row_element_insert_cell (
-                                       row, -1, NULL);
-                       }
-               } else if (expected_columns < current_columns) {
-                       for (jj = 0; jj < current_columns - expected_columns; jj++) {
-                               webkit_dom_html_table_row_element_delete_cell (
-                                       row, -1, NULL);
-                       }
-               }
-               g_object_unref (row);
-               g_object_unref (cells);
-       }
-       g_object_unref (rows);
+       EHTMLEditor *editor;
+       EContentEditor *cnt_editor;
+
+       editor = e_html_editor_dialog_get_editor (E_HTML_EDITOR_DIALOG (dialog));
+       cnt_editor = e_html_editor_get_content_editor (editor);
+
+       e_content_editor_table_set_column_count (
+               cnt_editor,
+               gtk_spin_button_get_value (
+                       GTK_SPIN_BUTTON (dialog->priv->columns_edit)));
 }
 
 static void
 html_editor_table_dialog_get_column_count (EHTMLEditorTableDialog *dialog)
 {
-       WebKitDOMHTMLCollection *rows, *columns;
-       WebKitDOMNode *row;
-
-       g_return_if_fail (dialog->priv->table_element);
-
-       rows = webkit_dom_html_table_element_get_rows (dialog->priv->table_element);
-       row = webkit_dom_html_collection_item (rows, 0);
+       EHTMLEditor *editor;
+       EContentEditor *cnt_editor;
 
-       columns = webkit_dom_html_table_row_element_get_cells (
-                               WEBKIT_DOM_HTML_TABLE_ROW_ELEMENT (row));
+       editor = e_html_editor_dialog_get_editor (E_HTML_EDITOR_DIALOG (dialog));
+       cnt_editor = e_html_editor_get_content_editor (editor);
 
        gtk_spin_button_set_value (
                GTK_SPIN_BUTTON (dialog->priv->columns_edit),
-               webkit_dom_html_collection_get_length (columns));
-       g_object_unref (row);
-       g_object_unref (rows);
-       g_object_unref (columns);
+               e_content_editor_table_get_column_count (cnt_editor));
 }
 
 static void
 html_editor_table_dialog_set_width (EHTMLEditorTableDialog *dialog)
 {
-       gchar *width;
+       EHTMLEditor *editor;
+       EContentEditor *cnt_editor;
 
-       g_return_if_fail (dialog->priv->table_element);
+       editor = e_html_editor_dialog_get_editor (E_HTML_EDITOR_DIALOG (dialog));
+       cnt_editor = e_html_editor_get_content_editor (editor);
 
        if (gtk_toggle_button_get_active (
                        GTK_TOGGLE_BUTTON (dialog->priv->width_check))) {
-               gchar *units;
 
-               units = gtk_combo_box_text_get_active_text (
-                               GTK_COMBO_BOX_TEXT (dialog->priv->width_units));
-               width = g_strdup_printf (
-                       "%d%s",
+               e_content_editor_table_set_width (
+                       cnt_editor,
                        gtk_spin_button_get_value_as_int (
                                GTK_SPIN_BUTTON (dialog->priv->width_edit)),
-                       units);
-               g_free (units);
+                       (gtk_combo_box_get_active (
+                               GTK_COMBO_BOX (dialog->priv->width_units)) == 0) ?
+                                       E_CONTENT_EDITOR_UNIT_PIXEL :
+                                       E_CONTENT_EDITOR_UNIT_PERCENTAGE);
 
                gtk_widget_set_sensitive (dialog->priv->width_edit, TRUE);
                gtk_widget_set_sensitive (dialog->priv->width_units, TRUE);
        } else {
-               width = g_strdup ("auto");
+               e_content_editor_table_set_width (
+                       cnt_editor, 0, E_CONTENT_EDITOR_UNIT_AUTO);
 
                gtk_widget_set_sensitive (dialog->priv->width_edit, FALSE);
                gtk_widget_set_sensitive (dialog->priv->width_units, FALSE);
        }
+}
 
-       webkit_dom_html_table_element_set_width (
-               dialog->priv->table_element, width);
-       g_free (width);
+static void
+html_editor_table_dialog_width_units_changed (GtkWidget *widget,
+                                              EHTMLEditorTableDialog *dialog)
+{
+       if (gtk_combo_box_get_active (GTK_COMBO_BOX (dialog->priv->width_units)) == 0) {
+               gtk_spin_button_set_range (
+                       GTK_SPIN_BUTTON (dialog->priv->width_edit), 0, G_MAXUINT);
+       } else
+               gtk_spin_button_set_range (
+                       GTK_SPIN_BUTTON (dialog->priv->width_edit), 0, 100);
+
+       html_editor_table_dialog_set_width (dialog);
 }
 
 static void
 html_editor_table_dialog_get_width (EHTMLEditorTableDialog *dialog)
 {
-       gchar *width;
-
-       width = webkit_dom_html_table_element_get_width (dialog->priv->table_element);
-       if (!width || !*width || g_ascii_strncasecmp (width, "auto", 4) == 0) {
-               gtk_toggle_button_set_active (
-                       GTK_TOGGLE_BUTTON (dialog->priv->width_check), FALSE);
-               gtk_spin_button_set_value (
-                       GTK_SPIN_BUTTON (dialog->priv->width_edit), 100);
-               gtk_combo_box_set_active_id (
-                       GTK_COMBO_BOX (dialog->priv->width_units), "units-percent");
-       } else {
-               gint width_int = atoi (width);
-
-               gtk_toggle_button_set_active (
-                       GTK_TOGGLE_BUTTON (dialog->priv->width_check), TRUE);
-               gtk_spin_button_set_value (
-                       GTK_SPIN_BUTTON (dialog->priv->width_edit), width_int);
-               gtk_combo_box_set_active_id (
-                       GTK_COMBO_BOX (dialog->priv->width_units),
-                       ((strstr (width, "%") == NULL) ?
-                               "units-px" : "units-percent"));
-       }
-       g_free (width);
+       EHTMLEditor *editor;
+       EContentEditor *cnt_editor;
+       EContentEditorUnit unit;
+       gint width;
+
+       editor = e_html_editor_dialog_get_editor (E_HTML_EDITOR_DIALOG (dialog));
+       cnt_editor = e_html_editor_get_content_editor (editor);
+
+       width = e_content_editor_table_get_width (cnt_editor, &unit);
+
+       gtk_toggle_button_set_active (
+               GTK_TOGGLE_BUTTON (dialog->priv->width_check),
+               unit != E_CONTENT_EDITOR_UNIT_AUTO);
+       gtk_spin_button_set_value (
+               GTK_SPIN_BUTTON (dialog->priv->width_edit),
+               unit == E_CONTENT_EDITOR_UNIT_AUTO ? 100 : width);
+       gtk_combo_box_set_active_id (
+               GTK_COMBO_BOX (dialog->priv->width_units),
+               unit == E_CONTENT_EDITOR_UNIT_PIXEL ? "units-px" : "units-percent");
 }
 
 static void
 html_editor_table_dialog_set_alignment (EHTMLEditorTableDialog *dialog)
 {
-       g_return_if_fail (dialog->priv->table_element);
+       EHTMLEditor *editor;
+       EContentEditor *cnt_editor;
 
-       webkit_dom_html_table_element_set_align (
-               dialog->priv->table_element,
+       editor = e_html_editor_dialog_get_editor (E_HTML_EDITOR_DIALOG (dialog));
+       cnt_editor = e_html_editor_get_content_editor (editor);
+
+       e_content_editor_table_set_align (
+               cnt_editor,
                gtk_combo_box_get_active_id (
                        GTK_COMBO_BOX (dialog->priv->alignment_combo)));
 }
@@ -336,208 +209,150 @@ html_editor_table_dialog_set_alignment (EHTMLEditorTableDialog *dialog)
 static void
 html_editor_table_dialog_get_alignment (EHTMLEditorTableDialog *dialog)
 {
-       gchar *alignment;
-
-       g_return_if_fail (dialog->priv->table_element);
+       EHTMLEditor *editor;
+       EContentEditor *cnt_editor;
+       gchar *value;
 
-       alignment = webkit_dom_html_table_element_get_align (
-                       dialog->priv->table_element);
+       editor = e_html_editor_dialog_get_editor (E_HTML_EDITOR_DIALOG (dialog));
+       cnt_editor = e_html_editor_get_content_editor (editor);
 
+       value = e_content_editor_table_get_align (cnt_editor);
        gtk_combo_box_set_active_id (
-               GTK_COMBO_BOX (dialog->priv->alignment_combo), alignment);
-
-       g_free (alignment);
+               GTK_COMBO_BOX (dialog->priv->alignment_combo), value);
+       g_free (value);
 }
 
 static void
 html_editor_table_dialog_set_padding (EHTMLEditorTableDialog *dialog)
 {
-       gchar *padding;
-
-       g_return_if_fail (dialog->priv->table_element);
-
-       padding = g_strdup_printf (
-               "%d",
-                       gtk_spin_button_get_value_as_int (
-                               GTK_SPIN_BUTTON (dialog->priv->padding_edit)));
+       EHTMLEditor *editor;
+       EContentEditor *cnt_editor;
 
-       webkit_dom_html_table_element_set_cell_padding (
-               dialog->priv->table_element, padding);
+       editor = e_html_editor_dialog_get_editor (E_HTML_EDITOR_DIALOG (dialog));
+       cnt_editor = e_html_editor_get_content_editor (editor);
 
-       g_free (padding);
+       e_content_editor_table_set_padding (
+               cnt_editor,
+               gtk_spin_button_get_value_as_int (
+                       GTK_SPIN_BUTTON (dialog->priv->padding_edit)));
 }
 
 static void
 html_editor_table_dialog_get_padding (EHTMLEditorTableDialog *dialog)
 {
-       gchar *padding;
-       gint padding_int;
-
-       g_return_if_fail (dialog->priv->table_element);
+       EHTMLEditor *editor;
+       EContentEditor *cnt_editor;
 
-       padding = webkit_dom_html_table_element_get_cell_padding (
-                       dialog->priv->table_element);
-       if (!padding || !*padding) {
-               padding_int = 0;
-       } else {
-               padding_int = atoi (padding);
-       }
+       editor = e_html_editor_dialog_get_editor (E_HTML_EDITOR_DIALOG (dialog));
+       cnt_editor = e_html_editor_get_content_editor (editor);
 
        gtk_spin_button_set_value (
-               GTK_SPIN_BUTTON (dialog->priv->padding_edit), padding_int);
-
-       g_free (padding);
+               GTK_SPIN_BUTTON (dialog->priv->padding_edit),
+               e_content_editor_table_get_padding (cnt_editor));
 }
 
 static void
 html_editor_table_dialog_set_spacing (EHTMLEditorTableDialog *dialog)
 {
-       gchar *spacing;
-
-       g_return_if_fail (dialog->priv->table_element);
-
-       spacing = g_strdup_printf (
-               "%d",
-                       gtk_spin_button_get_value_as_int (
-                               GTK_SPIN_BUTTON (dialog->priv->spacing_edit)));
+       EHTMLEditor *editor;
+       EContentEditor *cnt_editor;
 
-       webkit_dom_html_table_element_set_cell_spacing (
-               dialog->priv->table_element, spacing);
+       editor = e_html_editor_dialog_get_editor (E_HTML_EDITOR_DIALOG (dialog));
+       cnt_editor = e_html_editor_get_content_editor (editor);
 
-       g_free (spacing);
+       e_content_editor_table_set_spacing (
+               cnt_editor,
+               gtk_spin_button_get_value_as_int (
+                       GTK_SPIN_BUTTON (dialog->priv->spacing_edit)));
 }
 
 static void
 html_editor_table_dialog_get_spacing (EHTMLEditorTableDialog *dialog)
 {
-       gchar *spacing;
-       gint spacing_int;
-
-       g_return_if_fail (dialog->priv->table_element);
+       EHTMLEditor *editor;
+       EContentEditor *cnt_editor;
 
-       spacing = webkit_dom_html_table_element_get_cell_spacing (
-                       dialog->priv->table_element);
-       if (!spacing || !*spacing) {
-               spacing_int = 0;
-       } else {
-               spacing_int = atoi (spacing);
-       }
+       editor = e_html_editor_dialog_get_editor (E_HTML_EDITOR_DIALOG (dialog));
+       cnt_editor = e_html_editor_get_content_editor (editor);
 
        gtk_spin_button_set_value (
-               GTK_SPIN_BUTTON (dialog->priv->spacing_edit), spacing_int);
-
-       g_free (spacing);
+               GTK_SPIN_BUTTON (dialog->priv->spacing_edit),
+               e_content_editor_table_get_spacing (cnt_editor));
 }
 
 static void
 html_editor_table_dialog_set_border (EHTMLEditorTableDialog *dialog)
 {
-       gchar *border;
-
-       g_return_if_fail (dialog->priv->table_element);
-
-       border = g_strdup_printf (
-               "%d",
-                       gtk_spin_button_get_value_as_int (
-                               GTK_SPIN_BUTTON (dialog->priv->border_edit)));
+       EHTMLEditor *editor;
+       EContentEditor *cnt_editor;
 
-       webkit_dom_html_table_element_set_border (
-               dialog->priv->table_element, border);
+       editor = e_html_editor_dialog_get_editor (E_HTML_EDITOR_DIALOG (dialog));
+       cnt_editor = e_html_editor_get_content_editor (editor);
 
-       g_free (border);
+       e_content_editor_table_set_border (
+               cnt_editor,
+               gtk_spin_button_get_value_as_int (
+                       GTK_SPIN_BUTTON (dialog->priv->border_edit)));
 }
 
 static void
 html_editor_table_dialog_get_border (EHTMLEditorTableDialog *dialog)
 {
-       gchar *border;
-       gint border_int;
-
-       g_return_if_fail (dialog->priv->table_element);
+       EHTMLEditor *editor;
+       EContentEditor *cnt_editor;
 
-       border = webkit_dom_html_table_element_get_border (
-                       dialog->priv->table_element);
-       if (!border || !*border) {
-               border_int = 0;
-       } else {
-               border_int = atoi (border);
-       }
+       editor = e_html_editor_dialog_get_editor (E_HTML_EDITOR_DIALOG (dialog));
+       cnt_editor = e_html_editor_get_content_editor (editor);
 
        gtk_spin_button_set_value (
-               GTK_SPIN_BUTTON (dialog->priv->border_edit), border_int);
-
-       g_free (border);
+               GTK_SPIN_BUTTON (dialog->priv->border_edit),
+               e_content_editor_table_get_border (cnt_editor));
 }
 
 static void
 html_editor_table_dialog_set_background_color (EHTMLEditorTableDialog *dialog)
 {
-       gchar *color;
+       EHTMLEditor *editor;
+       EContentEditor *cnt_editor;
        GdkRGBA rgba;
 
-       g_return_if_fail (dialog->priv->table_element);
+       editor = e_html_editor_dialog_get_editor (E_HTML_EDITOR_DIALOG (dialog));
+       cnt_editor = e_html_editor_get_content_editor (editor);
 
        e_color_combo_get_current_color (
-               E_COLOR_COMBO (dialog->priv->background_color_button), &rgba);
-
-       if (rgba.alpha != 0.0)
-               color = g_strdup_printf ("#%06x", e_rgba_to_value (&rgba));
-       else
-               color = g_strdup ("");
-
-       webkit_dom_html_table_element_set_bg_color (
-               dialog->priv->table_element, color);
-
-       g_free (color);
+               E_COLOR_COMBO (dialog->priv->background_color_picker), &rgba);
+       e_content_editor_table_set_background_color (cnt_editor, &rgba);
 }
 
 static void
 html_editor_table_dialog_get_background_color (EHTMLEditorTableDialog *dialog)
 {
-       gchar *color;
+       EHTMLEditor *editor;
+       EContentEditor *cnt_editor;
        GdkRGBA rgba;
 
-       g_return_if_fail (dialog->priv->table_element);
-
-       color = webkit_dom_html_table_element_get_bg_color (
-                       dialog->priv->table_element);
-
-       if (color && *color) {
-               gdk_rgba_parse (&rgba, color);
-
-               e_color_combo_set_current_color (
-                       E_COLOR_COMBO (dialog->priv->background_color_button), &rgba);
-       } else {
-               e_color_combo_set_current_color (
-                       E_COLOR_COMBO (dialog->priv->background_color_button), &transparent);
-       }
+       editor = e_html_editor_dialog_get_editor (E_HTML_EDITOR_DIALOG (dialog));
+       cnt_editor = e_html_editor_get_content_editor (editor);
 
-       g_free (color);
+       e_content_editor_table_get_background_color (cnt_editor, &rgba);
+       e_color_combo_set_current_color (
+               E_COLOR_COMBO (dialog->priv->background_color_picker), &rgba);
 }
 
 static void
 html_editor_table_dialog_set_background_image (EHTMLEditorTableDialog *dialog)
 {
        EHTMLEditor *editor;
-       EHTMLEditorView *view;
+       EContentEditor *cnt_editor;
        gchar *uri;
 
-       g_return_if_fail (dialog->priv->table_element);
-
        editor = e_html_editor_dialog_get_editor (E_HTML_EDITOR_DIALOG (dialog));
-       view = e_html_editor_get_view (editor);
+       cnt_editor = e_html_editor_get_content_editor (editor);
 
        uri = gtk_file_chooser_get_uri (
-               GTK_FILE_CHOOSER (dialog->priv->background_image_button));
+               GTK_FILE_CHOOSER (dialog->priv->background_image_chooser));
 
-       if (uri && *uri)
-               e_html_editor_selection_replace_image_src (
-                       e_html_editor_view_get_selection (view),
-                       WEBKIT_DOM_ELEMENT (dialog->priv->table_element),
-                       uri);
-       else
-               remove_image_attributes_from_element (
-                       WEBKIT_DOM_ELEMENT (dialog->priv->table_element));
+       e_content_editor_table_set_background_image_uri (cnt_editor, uri);
 
        gtk_widget_set_sensitive (dialog->priv->remove_image_button, uri && *uri);
 
@@ -547,27 +362,22 @@ html_editor_table_dialog_set_background_image (EHTMLEditorTableDialog *dialog)
 static void
 html_editor_table_dialog_get_background_image (EHTMLEditorTableDialog *dialog)
 {
-       g_return_if_fail (dialog->priv->table_element);
-
-
-       if (!webkit_dom_element_has_attribute (
-               WEBKIT_DOM_ELEMENT (dialog->priv->table_element), "background")) {
-
-               gtk_file_chooser_unselect_all (
-                       GTK_FILE_CHOOSER (dialog->priv->background_image_button));
-               return;
-       } else {
-               gchar *value;
+       EHTMLEditor *editor;
+       EContentEditor *cnt_editor;
+       gchar *uri;
 
-               value = webkit_dom_element_get_attribute (
-                       WEBKIT_DOM_ELEMENT (dialog->priv->table_element), "data-uri");
+       editor = e_html_editor_dialog_get_editor (E_HTML_EDITOR_DIALOG (dialog));
+       cnt_editor = e_html_editor_get_content_editor (editor);
 
+       uri = e_content_editor_table_get_background_image_uri (cnt_editor);
+       if (uri && *uri)
                gtk_file_chooser_set_uri (
-                       GTK_FILE_CHOOSER (dialog->priv->background_image_button),
-                       value);
+                       GTK_FILE_CHOOSER (dialog->priv->background_image_chooser), uri);
+       else
+               gtk_file_chooser_unselect_all (
+                       GTK_FILE_CHOOSER (dialog->priv->background_image_chooser));
 
-               g_free (value);
-       }
+       g_free (uri);
 }
 
 static void
@@ -609,10 +419,10 @@ html_editor_table_dialog_reset_values (EHTMLEditorTableDialog *dialog)
                GTK_SPIN_BUTTON (dialog->priv->border_edit), 1);
 
        e_color_combo_set_current_color (
-               E_COLOR_COMBO (dialog->priv->background_color_button), &transparent);
+               E_COLOR_COMBO (dialog->priv->background_color_picker), &transparent);
 
        gtk_file_chooser_unselect_all (
-               GTK_FILE_CHOOSER (dialog->priv->background_image_button));
+               GTK_FILE_CHOOSER (dialog->priv->background_image_chooser));
 
        html_editor_table_dialog_set_row_count (dialog);
        html_editor_table_dialog_set_column_count (dialog);
@@ -630,56 +440,16 @@ html_editor_table_dialog_show (GtkWidget *widget)
 {
        EHTMLEditorTableDialog *dialog;
        EHTMLEditor *editor;
-       EHTMLEditorView *view;
-       WebKitDOMDocument *document;
-       WebKitDOMDOMWindow *dom_window;
-       WebKitDOMDOMSelection *dom_selection;
+       EContentEditor *cnt_editor;
 
        dialog = E_HTML_EDITOR_TABLE_DIALOG (widget);
        editor = e_html_editor_dialog_get_editor (E_HTML_EDITOR_DIALOG (dialog));
-       view = e_html_editor_get_view (editor);
-
-       document = webkit_web_view_get_dom_document (WEBKIT_WEB_VIEW (view));
-       dom_window = webkit_dom_document_get_default_view (document);
-       dom_selection = webkit_dom_dom_window_get_selection (dom_window);
-       g_object_unref (dom_window);
-       if (dom_selection && (webkit_dom_dom_selection_get_range_count (dom_selection) > 0)) {
-               WebKitDOMElement *table;
-               WebKitDOMRange *range;
-
-               range = webkit_dom_dom_selection_get_range_at (dom_selection, 0, NULL);
-               table = e_html_editor_dom_node_find_parent_element (
-                       webkit_dom_range_get_start_container (range, NULL), "TABLE");
-               g_object_unref (range);
-
-               if (!table) {
-                       dialog->priv->table_element = WEBKIT_DOM_HTML_TABLE_ELEMENT (
-                               html_editor_table_dialog_create_table (dialog));
-                       html_editor_table_dialog_reset_values (dialog);
-               } else {
-                       dialog->priv->table_element =
-                               WEBKIT_DOM_HTML_TABLE_ELEMENT (table);
-                       html_editor_table_dialog_get_values (dialog);
-               }
-
-               if (!e_html_editor_view_is_undo_redo_in_progress (view)) {
-                       EHTMLEditorViewHistoryEvent *ev;
-
-                       ev = g_new0 (EHTMLEditorViewHistoryEvent, 1);
-                       ev->type = HISTORY_TABLE_DIALOG;
-
-                       e_html_editor_selection_get_selection_coordinates (
-                               e_html_editor_view_get_selection (view),
-                               &ev->before.start.x, &ev->before.start.y,
-                               &ev->before.end.x, &ev->before.end.y);
-                       if (table)
-                               ev->data.dom.from = webkit_dom_node_clone_node (
-                                       WEBKIT_DOM_NODE (table), TRUE);
-                       dialog->priv->history_event = ev;
-               }
-       }
+       cnt_editor = e_html_editor_get_content_editor (editor);
 
-       g_object_unref (dom_selection);
+       if (e_content_editor_on_table_dialog_open (cnt_editor))
+               html_editor_table_dialog_reset_values (dialog);
+       else
+               html_editor_table_dialog_get_values (dialog);
 
        /* Chain up to parent implementation */
        GTK_WIDGET_CLASS (e_html_editor_table_dialog_parent_class)->show (widget);
@@ -688,11 +458,16 @@ html_editor_table_dialog_show (GtkWidget *widget)
 static void
 html_editor_table_dialog_remove_image (EHTMLEditorTableDialog *dialog)
 {
-       remove_image_attributes_from_element (
-               WEBKIT_DOM_ELEMENT (dialog->priv->table_element));
+       EHTMLEditor *editor;
+       EContentEditor *cnt_editor;
+
+       editor = e_html_editor_dialog_get_editor (E_HTML_EDITOR_DIALOG (dialog));
+       cnt_editor = e_html_editor_get_content_editor (editor);
+
+       e_content_editor_table_set_background_image_uri (cnt_editor, NULL);
 
        gtk_file_chooser_unselect_all (
-               GTK_FILE_CHOOSER (dialog->priv->background_image_button));
+               GTK_FILE_CHOOSER (dialog->priv->background_image_chooser));
 
        gtk_widget_set_sensitive (dialog->priv->remove_image_button, FALSE);
 }
@@ -700,39 +475,15 @@ html_editor_table_dialog_remove_image (EHTMLEditorTableDialog *dialog)
 static void
 html_editor_table_dialog_hide (GtkWidget *widget)
 {
-       EHTMLEditorTableDialogPrivate *priv;
-       EHTMLEditorViewHistoryEvent *ev;
-
-       priv = E_HTML_EDITOR_TABLE_DIALOG_GET_PRIVATE (widget);
-       ev = priv->history_event;
-
-       if (ev) {
-               EHTMLEditorTableDialog *dialog;
-               EHTMLEditor *editor;
-               EHTMLEditorSelection *selection;
-               EHTMLEditorView *view;
-
-               dialog = E_HTML_EDITOR_TABLE_DIALOG (widget);
-               editor = e_html_editor_dialog_get_editor (E_HTML_EDITOR_DIALOG (dialog));
-               view = e_html_editor_get_view (editor);
-               selection = e_html_editor_view_get_selection (view);
-
-               ev->data.dom.to = webkit_dom_node_clone_node (
-                       WEBKIT_DOM_NODE (priv->table_element), TRUE);
-
-               if (!webkit_dom_node_is_equal_node (ev->data.dom.from, ev->data.dom.to)) {
-                       e_html_editor_selection_get_selection_coordinates (
-                               selection, &ev->after.start.x, &ev->after.start.y, &ev->after.end.x, 
&ev->after.end.y);
-                       e_html_editor_view_insert_new_history_event (view, ev);
-               } else {
-                       g_object_unref (ev->data.dom.from);
-                       g_object_unref (ev->data.dom.to);
-                       g_free (ev);
-               }
-       }
+       EHTMLEditorTableDialog *dialog;
+       EHTMLEditor *editor;
+       EContentEditor *cnt_editor;
+
+       dialog = E_HTML_EDITOR_TABLE_DIALOG (widget);
+       editor = e_html_editor_dialog_get_editor (E_HTML_EDITOR_DIALOG (dialog));
+       cnt_editor = e_html_editor_get_content_editor (editor);
 
-       g_object_unref (priv->table_element);
-       priv->table_element = NULL;
+       e_content_editor_on_table_dialog_close (cnt_editor);
 
        GTK_WIDGET_CLASS (e_html_editor_table_dialog_parent_class)->hide (widget);
 }
@@ -839,9 +590,9 @@ e_html_editor_table_dialog_init (EHTMLEditorTableDialog *dialog)
        gtk_combo_box_text_append (GTK_COMBO_BOX_TEXT (widget), "units-px", "px");
        gtk_combo_box_text_append (GTK_COMBO_BOX_TEXT (widget), "units-percent", "%");
        gtk_grid_attach (grid, widget, 2, 0, 1, 1);
-       g_signal_connect_swapped (
+       g_signal_connect (
                widget, "changed",
-               G_CALLBACK (html_editor_table_dialog_set_width), dialog);
+               G_CALLBACK (html_editor_table_dialog_width_units_changed), dialog);
        dialog->priv->width_units = widget;
 
        /* Spacing */
@@ -932,12 +683,12 @@ e_html_editor_table_dialog_init (EHTMLEditorTableDialog *dialog)
        g_signal_connect_swapped (
                widget, "notify::current-color",
                G_CALLBACK (html_editor_table_dialog_set_background_color), dialog);
-       dialog->priv->background_color_button = widget;
+       dialog->priv->background_color_picker = widget;
 
        widget = gtk_label_new_with_mnemonic (_("_Color:"));
        gtk_label_set_justify (GTK_LABEL (widget), GTK_JUSTIFY_RIGHT);
        gtk_label_set_mnemonic_widget (
-               GTK_LABEL (widget), dialog->priv->background_color_button);
+               GTK_LABEL (widget), dialog->priv->background_color_picker);
        gtk_grid_attach (grid, widget, 0, 0, 1, 1);
 
        /* Image */
@@ -958,12 +709,12 @@ e_html_editor_table_dialog_init (EHTMLEditorTableDialog *dialog)
        g_signal_connect_swapped (
                widget, "file-set",
                G_CALLBACK (html_editor_table_dialog_set_background_image), dialog);
-       dialog->priv->background_image_button = widget;
+       dialog->priv->background_image_chooser = widget;
 
        widget =gtk_label_new_with_mnemonic (_("Image:"));
        gtk_label_set_justify (GTK_LABEL (widget), GTK_JUSTIFY_RIGHT);
        gtk_label_set_mnemonic_widget (
-               GTK_LABEL (widget), dialog->priv->background_image_button);
+               GTK_LABEL (widget), dialog->priv->background_image_chooser);
        gtk_grid_attach (grid, widget, 0, 1, 1, 1);
 
        box = e_html_editor_dialog_get_button_box (E_HTML_EDITOR_DIALOG (dialog));
@@ -990,4 +741,3 @@ e_html_editor_table_dialog_new (EHTMLEditor *editor)
                        "title", _("Table Properties"),
                        NULL));
 }
-
diff --git a/e-util/e-html-editor-text-dialog.c b/e-util/e-html-editor-text-dialog.c
index 9edb8e5..f944338 100644
--- a/e-util/e-html-editor-text-dialog.c
+++ b/e-util/e-html-editor-text-dialog.c
@@ -51,15 +51,13 @@ static void
 html_editor_text_dialog_set_bold (EHTMLEditorTextDialog *dialog)
 {
        EHTMLEditor *editor;
-       EHTMLEditorView *view;
-       EHTMLEditorSelection *selection;
+       EContentEditor *cnt_editor;
 
        editor = e_html_editor_dialog_get_editor (E_HTML_EDITOR_DIALOG (dialog));
-       view = e_html_editor_get_view (editor);
-       selection = e_html_editor_view_get_selection (view);
+       cnt_editor = e_html_editor_get_content_editor (editor);
 
-       e_html_editor_selection_set_bold (
-               selection,
+       e_content_editor_set_bold (
+               cnt_editor,
                gtk_toggle_button_get_active (
                        GTK_TOGGLE_BUTTON (dialog->priv->bold_check)));
 }
@@ -68,15 +66,13 @@ static void
 html_editor_text_dialog_set_italic (EHTMLEditorTextDialog *dialog)
 {
        EHTMLEditor *editor;
-       EHTMLEditorView *view;
-       EHTMLEditorSelection *selection;
+       EContentEditor *cnt_editor;
 
        editor = e_html_editor_dialog_get_editor (E_HTML_EDITOR_DIALOG (dialog));
-       view = e_html_editor_get_view (editor);
-       selection = e_html_editor_view_get_selection (view);
+       cnt_editor = e_html_editor_get_content_editor (editor);
 
-       e_html_editor_selection_set_italic (
-               selection,
+       e_content_editor_set_italic (
+               cnt_editor,
                gtk_toggle_button_get_active (
                        GTK_TOGGLE_BUTTON (dialog->priv->italic_check)));
 }
@@ -85,15 +81,13 @@ static void
 html_editor_text_dialog_set_underline (EHTMLEditorTextDialog *dialog)
 {
        EHTMLEditor *editor;
-       EHTMLEditorView *view;
-       EHTMLEditorSelection *selection;
+       EContentEditor *cnt_editor;
 
        editor = e_html_editor_dialog_get_editor (E_HTML_EDITOR_DIALOG (dialog));
-       view = e_html_editor_get_view (editor);
-       selection = e_html_editor_view_get_selection (view);
+       cnt_editor = e_html_editor_get_content_editor (editor);
 
-       e_html_editor_selection_set_underline (
-               selection,
+       e_content_editor_set_underline (
+               cnt_editor,
                gtk_toggle_button_get_active (
                        GTK_TOGGLE_BUTTON (dialog->priv->underline_check)));
 }
@@ -102,15 +96,13 @@ static void
 html_editor_text_dialog_set_strikethrough (EHTMLEditorTextDialog *dialog)
 {
        EHTMLEditor *editor;
-       EHTMLEditorView *view;
-       EHTMLEditorSelection *selection;
+       EContentEditor *cnt_editor;
 
        editor = e_html_editor_dialog_get_editor (E_HTML_EDITOR_DIALOG (dialog));
-       view = e_html_editor_get_view (editor);
-       selection = e_html_editor_view_get_selection (view);
+       cnt_editor = e_html_editor_get_content_editor (editor);
 
-       e_html_editor_selection_set_strikethrough (
-               selection,
+       e_content_editor_set_strikethrough (
+               cnt_editor,
                gtk_toggle_button_get_active (
                        GTK_TOGGLE_BUTTON (dialog->priv->strikethrough_check)));
 }
@@ -119,33 +111,29 @@ static void
 html_editor_text_dialog_set_color (EHTMLEditorTextDialog *dialog)
 {
        EHTMLEditor *editor;
-       EHTMLEditorView *view;
-       EHTMLEditorSelection *selection;
+       EContentEditor *cnt_editor;
        GdkRGBA rgba;
 
        editor = e_html_editor_dialog_get_editor (E_HTML_EDITOR_DIALOG (dialog));
-       view = e_html_editor_get_view (editor);
-       selection = e_html_editor_view_get_selection (view);
+       cnt_editor = e_html_editor_get_content_editor (editor);
 
        e_color_combo_get_current_color (
                E_COLOR_COMBO (dialog->priv->color_check), &rgba);
-       e_html_editor_selection_set_font_color (selection, &rgba);
+       e_content_editor_set_font_color (cnt_editor, &rgba);
 }
 
 static void
 html_editor_text_dialog_set_size (EHTMLEditorTextDialog *dialog)
 {
        EHTMLEditor *editor;
-       EHTMLEditorView *view;
-       EHTMLEditorSelection *selection;
+       EContentEditor *cnt_editor;
        gint size;
 
        editor = e_html_editor_dialog_get_editor (E_HTML_EDITOR_DIALOG (dialog));
-       view = e_html_editor_get_view (editor);
-       selection = e_html_editor_view_get_selection (view);
+       cnt_editor = e_html_editor_get_content_editor (editor);
        size = gtk_combo_box_get_active (GTK_COMBO_BOX (dialog->priv->size_check));
 
-       e_html_editor_selection_set_font_size (selection, size + 1);
+       e_content_editor_set_font_size (cnt_editor, size + 1);
 }
 
 static void
@@ -153,35 +141,36 @@ html_editor_text_dialog_show (GtkWidget *widget)
 {
        EHTMLEditorTextDialog *dialog;
        EHTMLEditor *editor;
-       EHTMLEditorView *view;
-       EHTMLEditorSelection *selection;
-       GdkRGBA rgba;
+       EContentEditor *cnt_editor;
+       GdkRGBA *rgba;
 
        dialog = E_HTML_EDITOR_TEXT_DIALOG (widget);
        editor = e_html_editor_dialog_get_editor (E_HTML_EDITOR_DIALOG (dialog));
-       view = e_html_editor_get_view (editor);
-       selection = e_html_editor_view_get_selection (view);
+       cnt_editor = e_html_editor_get_content_editor (editor);
 
        gtk_toggle_button_set_active (
                GTK_TOGGLE_BUTTON (dialog->priv->bold_check),
-               e_html_editor_selection_is_bold (selection));
+               e_content_editor_is_bold (cnt_editor));
        gtk_toggle_button_set_active (
                GTK_TOGGLE_BUTTON (dialog->priv->italic_check),
-               e_html_editor_selection_is_italic (selection));
+               e_content_editor_is_italic (cnt_editor));
        gtk_toggle_button_set_active (
                GTK_TOGGLE_BUTTON (dialog->priv->underline_check),
-               e_html_editor_selection_is_underline (selection));
+               e_content_editor_is_underline (cnt_editor));
        gtk_toggle_button_set_active (
                GTK_TOGGLE_BUTTON (dialog->priv->strikethrough_check),
-               e_html_editor_selection_is_strikethrough (selection));
+               e_content_editor_is_strikethrough (cnt_editor));
 
        gtk_combo_box_set_active (
                GTK_COMBO_BOX (dialog->priv->size_check),
-               e_html_editor_selection_get_font_size (selection) - 1);
-
-       e_html_editor_selection_get_font_color (selection, &rgba);
-       e_color_combo_set_current_color (
-               E_COLOR_COMBO (dialog->priv->color_check), &rgba);
+               e_content_editor_get_font_size (cnt_editor) - 1);
+
+       rgba = e_content_editor_dup_font_color (cnt_editor);
+       if (rgba) {
+               e_color_combo_set_current_color (
+                       E_COLOR_COMBO (dialog->priv->color_check), rgba);
+               gdk_rgba_free (rgba);
+       }
 
        GTK_WIDGET_CLASS (e_html_editor_text_dialog_parent_class)->show (widget);
 }
diff --git a/e-util/e-html-editor.c b/e-util/e-html-editor.c
index 90c8069..bb6af69 100644
--- a/e-util/e-html-editor.c
+++ b/e-util/e-html-editor.c
@@ -32,8 +32,9 @@
 #include "e-alert-dialog.h"
 #include "e-alert-sink.h"
 #include "e-html-editor-private.h"
-#include "e-html-editor-utils.h"
-#include "e-html-editor-selection.h"
+#include "e-content-editor.h"
+#include "e-misc-utils.h"
+#include "e-simple-async-result.h"
 
 #define E_HTML_EDITOR_GET_PRIVATE(obj) \
        (G_TYPE_INSTANCE_GET_PRIVATE \
@@ -42,8 +43,8 @@
 /**
  * EHTMLEditor:
  *
- * #EHTMLEditor provides GUI for manipulating with properties of #EHTMLEditorView and
- * its #EHTMLEditorSelection - i.e. toolbars and actions.
+ * #EHTMLEditor provides GUI for manipulating with properties of
+ * #EContentEditor i.e. toolbars and actions.
  */
 
 /* This controls how spelling suggestions are divided between the primary
@@ -88,6 +89,8 @@ G_DEFINE_TYPE_WITH_CODE (
        e_html_editor,
        GTK_TYPE_GRID,
        G_IMPLEMENT_INTERFACE (
+               E_TYPE_EXTENSIBLE, NULL)
+       G_IMPLEMENT_INTERFACE (
                E_TYPE_ALERT_SINK,
                e_html_editor_alert_sink_init))
 
@@ -97,25 +100,21 @@ static void
 action_context_spell_suggest_cb (GtkAction *action,
                                  EHTMLEditor *editor)
 {
-       EHTMLEditorView *view;
-       EHTMLEditorSelection *selection;
+       EContentEditor *cnt_editor;
        const gchar *word;
 
        word = g_object_get_data (G_OBJECT (action), "word");
        g_return_if_fail (word != NULL);
 
-       view = e_html_editor_get_view (editor);
-       selection = e_html_editor_view_get_selection (view);
-
-       e_html_editor_selection_replace_caret_word (selection, word);
+       cnt_editor = e_html_editor_get_content_editor (editor);
+       e_content_editor_replace_caret_word (cnt_editor, word);
 }
 
 static void
 html_editor_inline_spelling_suggestions (EHTMLEditor *editor)
 {
-       EHTMLEditorView *view;
-       EHTMLEditorSelection *selection;
-       WebKitSpellChecker *checker;
+       EContentEditor *cnt_editor;
+       ESpellChecker *spell_checker;
        GtkActionGroup *action_group;
        GtkUIManager *manager;
        gchar **suggestions;
@@ -127,15 +126,13 @@ html_editor_inline_spelling_suggestions (EHTMLEditor *editor)
        guint threshold;
        gint ii;
 
-       view = e_html_editor_get_view (editor);
-       selection = e_html_editor_view_get_selection (view);
-       checker = WEBKIT_SPELL_CHECKER (webkit_get_text_checker ());
-
-       word = e_html_editor_selection_get_caret_word (selection);
+       cnt_editor = e_html_editor_get_content_editor (editor);
+       word = e_content_editor_get_caret_word (cnt_editor);
        if (word == NULL || *word == '\0')
                return;
 
-       suggestions = webkit_spell_checker_get_guesses_for_word (checker, word, NULL);
+       spell_checker = e_content_editor_ref_spell_checker (cnt_editor);
+       suggestions = e_spell_checker_get_guesses_for_word (spell_checker, word);
 
        path = "/context-menu/context-spell-suggest/";
        manager = e_html_editor_get_ui_manager (editor);
@@ -205,6 +202,7 @@ html_editor_inline_spelling_suggestions (EHTMLEditor *editor)
 
        g_free (word);
        g_strfreev (suggestions);
+       g_clear_object (&spell_checker);
 }
 
 /* Helper for html_editor_update_actions() */
@@ -212,10 +210,9 @@ static void
 html_editor_spell_checkers_foreach (EHTMLEditor *editor,
                                     const gchar *language_code)
 {
-       EHTMLEditorView *view;
-       EHTMLEditorSelection *selection;
+       EContentEditor *cnt_editor;
        ESpellChecker *spell_checker;
-       ESpellDictionary *dictionary;
+       ESpellDictionary *dictionary = NULL;
        GtkActionGroup *action_group;
        GtkUIManager *manager;
        GList *list, *link;
@@ -224,14 +221,13 @@ html_editor_spell_checkers_foreach (EHTMLEditor *editor,
        gint ii = 0;
        guint merge_id;
 
-       view = e_html_editor_get_view (editor);
-       selection = e_html_editor_view_get_selection (view);
-       spell_checker = e_html_editor_view_get_spell_checker (view);
-
-       word = e_html_editor_selection_get_caret_word (selection);
+       cnt_editor = e_html_editor_get_content_editor (editor);
+       word = e_content_editor_get_caret_word (cnt_editor);
        if (word == NULL || *word == '\0')
                return;
 
+       spell_checker = e_content_editor_ref_spell_checker (cnt_editor);
+
        dictionary = e_spell_checker_ref_dictionary (
                spell_checker, language_code);
        if (dictionary != NULL) {
@@ -298,7 +294,7 @@ html_editor_spell_checkers_foreach (EHTMLEditor *editor,
        }
 
        g_list_free_full (list, (GDestroyNotify) g_free);
-
+       g_clear_object (&spell_checker);
        g_free (path);
        g_free (word);
 }
@@ -306,124 +302,100 @@ html_editor_spell_checkers_foreach (EHTMLEditor *editor,
 void
 e_html_editor_update_spell_actions (EHTMLEditor *editor)
 {
-       ESpellChecker *checker;
-       EHTMLEditorView *view;
+       ESpellChecker *spell_checker;
+       EContentEditor *cnt_editor;
        guint count;
 
-       view = e_html_editor_get_view (editor);
-       checker = e_html_editor_view_get_spell_checker (view);
+       cnt_editor = e_html_editor_get_content_editor (editor);
+       spell_checker = e_content_editor_ref_spell_checker (cnt_editor);
 
-       count = e_spell_checker_count_active_languages (checker);
+       count = e_spell_checker_count_active_languages (spell_checker);
 
        gtk_action_set_visible (ACTION (CONTEXT_SPELL_ADD), count == 1);
        gtk_action_set_visible (ACTION (CONTEXT_SPELL_ADD_MENU), count > 1);
        gtk_action_set_visible (ACTION (CONTEXT_SPELL_IGNORE), count > 0);
 
-       gtk_action_set_visible (ACTION (SPELL_CHECK), count > 0);
-       gtk_action_set_visible (ACTION (LANGUAGE_MENU), count > 0);
+       gtk_action_set_sensitive (ACTION (SPELL_CHECK), count > 0);
+       gtk_action_set_sensitive (ACTION (LANGUAGE_MENU), e_spell_checker_count_available_dicts 
(spell_checker) > 0);
+
+       g_clear_object (&spell_checker);
 }
 
 static void
-html_editor_update_actions (EHTMLEditor *editor,
-                            GdkEventButton *event)
+action_set_visible_and_sensitive (GtkAction *action,
+                                  gboolean value)
 {
-       WebKitWebView *web_view;
-       WebKitSpellChecker *checker;
-       WebKitHitTestResult *hit_test;
-       WebKitHitTestResultContext context;
-       WebKitDOMNode *node;
-       EHTMLEditorSelection *selection;
-       EHTMLEditorView *view;
+       gtk_action_set_visible (action, value);
+       gtk_action_set_sensitive (action, value);
+}
+
+static void
+html_editor_update_actions (EHTMLEditor *editor)
+{
+       EContentEditor *cnt_editor;
+       EContentEditorNodeFlags flags = editor->priv->node_flags;
        ESpellChecker *spell_checker;
        GtkUIManager *manager;
        GtkActionGroup *action_group;
        GList *list;
-       gchar **languages;
+       gchar **languages = NULL;
        guint ii, n_languages;
        gboolean visible;
        guint merge_id;
-       gint loc, len;
 
-       view = e_html_editor_get_view (editor);
-       selection = e_html_editor_view_get_selection (view);
-       spell_checker = e_html_editor_view_get_spell_checker (view);
+       cnt_editor = e_html_editor_get_content_editor (editor);
 
-       web_view = WEBKIT_WEB_VIEW (view);
-       manager = e_html_editor_get_ui_manager (editor);
+       if (camel_debug ("wex"))
+               printf ("%s: flags:%d(%x)\n", G_STRFUNC, flags, flags);
 
-       g_clear_object (&editor->priv->table_cell);
-       g_clear_object (&editor->priv->image);
-       g_clear_object (&editor->priv->current_node);
-
-       /* Update context menu item visibility. */
-       hit_test = webkit_web_view_get_hit_test_result (web_view, event);
-       g_object_get (
-               G_OBJECT (hit_test),
-               "context", &context,
-               "inner-node", &node, NULL);
-       g_object_unref (hit_test);
-
-       editor->priv->current_node = g_object_ref (node);
-       visible = (context & WEBKIT_HIT_TEST_RESULT_CONTEXT_IMAGE);
-       gtk_action_set_visible (ACTION (CONTEXT_PROPERTIES_IMAGE), visible);
-       if (visible)
-               editor->priv->image = g_object_ref (node);
+       visible = (flags & E_CONTENT_EDITOR_NODE_IS_IMAGE);
+       action_set_visible_and_sensitive (ACTION (CONTEXT_PROPERTIES_IMAGE), visible);
 
-       visible = (context & WEBKIT_HIT_TEST_RESULT_CONTEXT_LINK);
-       if (visible) {
-               g_object_unref (editor->priv->current_node);
-               editor->priv->current_node = webkit_dom_node_get_parent_node (node);
-               gtk_action_set_visible (ACTION (CONTEXT_INSERT_LINK), FALSE);
-       }
-       gtk_action_set_visible (ACTION (CONTEXT_PROPERTIES_LINK), visible);
+       visible = (flags & E_CONTENT_EDITOR_NODE_IS_ANCHOR);
+       if (visible)
+               action_set_visible_and_sensitive (ACTION (CONTEXT_INSERT_LINK), visible);
+       action_set_visible_and_sensitive (ACTION (CONTEXT_PROPERTIES_LINK), visible);
 
-       visible = (WEBKIT_DOM_IS_HTMLHR_ELEMENT (node));
-       gtk_action_set_visible (ACTION (CONTEXT_PROPERTIES_RULE), visible);
+       visible = (flags & E_CONTENT_EDITOR_NODE_IS_H_RULE);
+       action_set_visible_and_sensitive (ACTION (CONTEXT_PROPERTIES_RULE), visible);
 
-       visible = (WEBKIT_DOM_IS_TEXT (node));
+       visible = (flags & E_CONTENT_EDITOR_NODE_IS_TEXT);
        /* Only display the text properties dialog when some text is selected. */
-       gtk_action_set_visible (
+       action_set_visible_and_sensitive (
                ACTION (CONTEXT_PROPERTIES_TEXT),
-               visible && !e_html_editor_selection_is_collapsed (selection));
+               visible && !(flags & E_CONTENT_EDITOR_NODE_IS_TEXT_COLLAPSED));
 
        visible =
                gtk_action_get_visible (ACTION (CONTEXT_PROPERTIES_IMAGE)) ||
                gtk_action_get_visible (ACTION (CONTEXT_PROPERTIES_LINK)) ||
                visible; /* text node under caret */
-       gtk_action_set_visible (ACTION (CONTEXT_PROPERTIES_PARAGRAPH), visible);
+       action_set_visible_and_sensitive (ACTION (CONTEXT_PROPERTIES_PARAGRAPH), visible);
 
        /* Set to visible if any of these are true:
         *   - Selection is active and contains a link.
         *   - Cursor is on a link.
         *   - Cursor is on an image that has a URL or target.
         */
-       visible = (WEBKIT_DOM_IS_HTML_ANCHOR_ELEMENT (node) ||
-               (e_html_editor_dom_node_find_parent_element (node, "A") != NULL));
-       gtk_action_set_visible (ACTION (CONTEXT_REMOVE_LINK), visible);
-
-       visible = (WEBKIT_DOM_IS_HTML_TABLE_CELL_ELEMENT (node) ||
-               (e_html_editor_dom_node_find_parent_element (node, "TD") != NULL) ||
-               (e_html_editor_dom_node_find_parent_element (node, "TH") != NULL));
-       gtk_action_set_visible (ACTION (CONTEXT_DELETE_CELL), visible);
-       gtk_action_set_visible (ACTION (CONTEXT_DELETE_COLUMN), visible);
-       gtk_action_set_visible (ACTION (CONTEXT_DELETE_ROW), visible);
-       gtk_action_set_visible (ACTION (CONTEXT_DELETE_TABLE), visible);
-       gtk_action_set_visible (ACTION (CONTEXT_INSERT_COLUMN_AFTER), visible);
-       gtk_action_set_visible (ACTION (CONTEXT_INSERT_COLUMN_BEFORE), visible);
-       gtk_action_set_visible (ACTION (CONTEXT_INSERT_ROW_ABOVE), visible);
-       gtk_action_set_visible (ACTION (CONTEXT_INSERT_ROW_BELOW), visible);
-       gtk_action_set_visible (ACTION (CONTEXT_INSERT_TABLE), visible);
-       gtk_action_set_visible (ACTION (CONTEXT_PROPERTIES_CELL), visible);
-       if (visible)
-               editor->priv->table_cell = g_object_ref (node);
-
-       /* Note the |= (cursor must be in a table cell). */
-       visible |= (WEBKIT_DOM_IS_HTML_TABLE_ELEMENT (node) ||
-               (e_html_editor_dom_node_find_parent_element (node, "TABLE") != NULL));
-       gtk_action_set_visible (ACTION (CONTEXT_PROPERTIES_TABLE), visible);
+       visible = (flags & E_CONTENT_EDITOR_NODE_IS_ANCHOR);
+       action_set_visible_and_sensitive (ACTION (CONTEXT_REMOVE_LINK), visible);
+
+       visible = (flags & E_CONTENT_EDITOR_NODE_IS_TABLE_CELL);
+       action_set_visible_and_sensitive (ACTION (CONTEXT_DELETE_CELL), visible);
+       action_set_visible_and_sensitive (ACTION (CONTEXT_DELETE_COLUMN), visible);
+       action_set_visible_and_sensitive (ACTION (CONTEXT_DELETE_ROW), visible);
+       action_set_visible_and_sensitive (ACTION (CONTEXT_DELETE_TABLE), visible);
+       action_set_visible_and_sensitive (ACTION (CONTEXT_INSERT_COLUMN_AFTER), visible);
+       action_set_visible_and_sensitive (ACTION (CONTEXT_INSERT_COLUMN_BEFORE), visible);
+       action_set_visible_and_sensitive (ACTION (CONTEXT_INSERT_ROW_ABOVE), visible);
+       action_set_visible_and_sensitive (ACTION (CONTEXT_INSERT_ROW_BELOW), visible);
+       action_set_visible_and_sensitive (ACTION (CONTEXT_PROPERTIES_CELL), visible);
+
+       visible = (flags & E_CONTENT_EDITOR_NODE_IS_TABLE);
+       action_set_visible_and_sensitive (ACTION (CONTEXT_PROPERTIES_TABLE), visible);
 
        /********************** Spell Check Suggestions **********************/
 
+       manager = e_html_editor_get_ui_manager (editor);
        action_group = editor->priv->suggestion_actions;
 
        /* Remove the old content from the context menu. */
@@ -442,18 +414,16 @@ html_editor_update_actions (EHTMLEditor *editor,
                list = g_list_delete_link (list, list);
        }
 
+       spell_checker = e_content_editor_ref_spell_checker (cnt_editor);
        languages = e_spell_checker_list_active_languages (
                spell_checker, &n_languages);
 
        /* Decide if we should show spell checking items. */
-       checker = WEBKIT_SPELL_CHECKER (webkit_get_text_checker ());
        visible = FALSE;
        if (n_languages > 0) {
-               gchar *word = e_html_editor_selection_get_caret_word (selection);
+               gchar *word = e_content_editor_get_caret_word (cnt_editor);
                if (word && *word) {
-                       webkit_spell_checker_check_spelling_of_string (
-                               checker, word, &loc, &len);
-                       visible = (loc > -1);
+                       visible = !e_spell_checker_check_word (spell_checker, word, -1);
                } else {
                        visible = FALSE;
                }
@@ -463,6 +433,8 @@ html_editor_update_actions (EHTMLEditor *editor,
        action_group = editor->priv->spell_check_actions;
        gtk_action_group_set_visible (action_group, visible);
 
+       g_clear_object (&spell_checker);
+
        /* Exit early if spell checking items are invisible. */
        if (!visible) {
                g_strfreev (languages);
@@ -493,64 +465,60 @@ html_editor_update_actions (EHTMLEditor *editor,
 static void
 html_editor_spell_languages_changed (EHTMLEditor *editor)
 {
-       EHTMLEditorView *view;
+       EContentEditor *cnt_editor;
        ESpellChecker *spell_checker;
-       WebKitWebSettings *settings;
-       gchar *comma_separated;
        gchar **languages;
 
-       view = e_html_editor_get_view (editor);
-       spell_checker = e_html_editor_view_get_spell_checker (view);
+       cnt_editor = e_html_editor_get_content_editor (editor);
+       spell_checker = e_content_editor_ref_spell_checker (cnt_editor);
 
        languages = e_spell_checker_list_active_languages (spell_checker, NULL);
-       comma_separated = g_strjoinv (",", languages);
-       g_strfreev (languages);
 
        /* Set the languages for webview to highlight misspelled words */
-       settings = webkit_web_view_get_settings (
-               WEBKIT_WEB_VIEW (editor->priv->html_editor_view));
-
-       g_object_set (
-               G_OBJECT (settings),
-               "spell-checking-languages", comma_separated,
-               NULL);
+       e_content_editor_set_spell_checking_languages (cnt_editor, (const gchar **) languages);
 
        if (editor->priv->spell_check_dialog != NULL)
                e_html_editor_spell_check_dialog_update_dictionaries (
                        E_HTML_EDITOR_SPELL_CHECK_DIALOG (
                        editor->priv->spell_check_dialog));
 
-       if (*comma_separated)
-               e_html_editor_view_force_spell_check (editor->priv->html_editor_view);
-       else
-               e_html_editor_view_turn_spell_check_off (editor->priv->html_editor_view);
+       e_content_editor_set_spell_check_enabled (cnt_editor, languages && *languages);
 
-       g_free (comma_separated);
+       g_clear_object (&spell_checker);
+       g_strfreev (languages);
 }
 
 static gboolean
-html_editor_show_popup (EHTMLEditor *editor,
-                        GdkEventButton *event,
-                        gpointer user_data)
+html_editor_context_menu_requested_cb (EContentEditor *cnt_editor,
+                                       EContentEditorNodeFlags flags,
+                                       GdkEvent *event,
+                                       EHTMLEditor *editor)
 {
        GtkWidget *menu;
 
+       /* COUNT FLAGS */
        menu = e_html_editor_get_managed_widget (editor, "/context-menu");
 
-       g_signal_emit (editor, signals[UPDATE_ACTIONS], 0, event);
+       editor->priv->node_flags = flags;
+       g_signal_emit (editor, signals[UPDATE_ACTIONS], 0, flags);
 
        if (!gtk_menu_get_attach_widget (GTK_MENU (menu)))
                gtk_menu_attach_to_widget (GTK_MENU (menu),
                                           GTK_WIDGET (editor),
                                           NULL);
-       if (event != NULL)
+
+       if (event)
                gtk_menu_popup (
                        GTK_MENU (menu), NULL, NULL, NULL,
-                       user_data, event->button, event->time);
+                       GTK_WIDGET (cnt_editor),
+                       ((GdkEventButton*) event)->button,
+                       ((GdkEventButton*) event)->time);
        else
                gtk_menu_popup (
                        GTK_MENU (menu), NULL, NULL, NULL,
-                       user_data, 0, gtk_get_current_event_time ());
+                       GTK_WIDGET (cnt_editor),
+                       0,
+                       gtk_get_current_event_time ());
 
        return TRUE;
 }
@@ -635,7 +603,6 @@ html_editor_constructed (GObject *object)
 {
        EHTMLEditor *editor = E_HTML_EDITOR (object);
        EHTMLEditorPrivate *priv = editor->priv;
-       GtkIMMulticontext *im_context;
        GtkWidget *widget;
        GtkToolbar *toolbar;
        GtkToolItem *tool_item;
@@ -643,6 +610,17 @@ html_editor_constructed (GObject *object)
        /* Chain up to parent's method. */
        G_OBJECT_CLASS (e_html_editor_parent_class)->constructed (object);
 
+       e_extensible_load_extensions (E_EXTENSIBLE (object));
+
+       editor_actions_init (editor);
+       priv->editor_layout_row = 2;
+
+       /* Tweak the main-toolbar style. */
+       widget = e_html_editor_get_managed_widget (editor, "/main-toolbar");
+       gtk_style_context_add_class (
+               gtk_widget_get_style_context (widget),
+               GTK_STYLE_CLASS_PRIMARY_TOOLBAR);
+
        /* Construct the editing toolbars. */
 
        widget = e_html_editor_get_managed_widget (editor, "/edit-toolbar");
@@ -675,25 +653,29 @@ html_editor_constructed (GObject *object)
        /* EAlertBar controls its own visibility. */
 
        /* Construct the main editing area. */
+       widget = GTK_WIDGET (e_html_editor_get_content_editor (editor));
 
-       widget = gtk_scrolled_window_new (NULL, NULL);
-       gtk_scrolled_window_set_policy (
-               GTK_SCROLLED_WINDOW (widget),
-               GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
-       gtk_scrolled_window_set_shadow_type (
-               GTK_SCROLLED_WINDOW (widget), GTK_SHADOW_IN);
-       gtk_widget_set_hexpand (widget, TRUE);
-       gtk_widget_set_vexpand (widget, TRUE);
-       gtk_grid_attach (GTK_GRID (editor), widget, 0, 4, 1, 1);
-       priv->scrolled_window = g_object_ref (widget);
-       gtk_widget_show (widget);
+       /* Pack editors which implement GtkScrollable in a scrolled window */
+       if (GTK_IS_SCROLLABLE (widget)) {
+               GtkWidget *scrolled_window;
+
+               scrolled_window = gtk_scrolled_window_new (NULL, NULL);
+               gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled_window),
+                       GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
+               gtk_widget_show (scrolled_window);
+
+               gtk_grid_attach (GTK_GRID (editor), scrolled_window, 0, 4, 1, 1);
+
+               gtk_container_add (GTK_CONTAINER (scrolled_window), widget);
+       } else {
+               gtk_grid_attach (GTK_GRID (editor), widget, 0, 4, 1, 1);
+       }
 
-       widget = GTK_WIDGET (e_html_editor_get_view (editor));
-       gtk_container_add (GTK_CONTAINER (priv->scrolled_window), widget);
        gtk_widget_show (widget);
-       g_signal_connect_swapped (
-               widget, "popup-event",
-               G_CALLBACK (html_editor_show_popup), editor);
+
+       g_signal_connect (
+               widget, "context-menu-requested",
+               G_CALLBACK (html_editor_context_menu_requested_cb), editor);
 
        /* Add some combo boxes to the "edit" toolbar. */
 
@@ -734,14 +716,6 @@ html_editor_constructed (GObject *object)
        gtk_toolbar_insert (toolbar, tool_item, 0);
        priv->color_combo_box = g_object_ref (widget);
        gtk_widget_show_all (GTK_WIDGET (tool_item));
-       e_binding_bind_property (
-               priv->color_combo_box, "current-color",
-               priv->selection, "font-color",
-               G_BINDING_SYNC_CREATE | G_BINDING_BIDIRECTIONAL);
-       e_binding_bind_property (
-               priv->html_editor_view, "editable",
-               priv->color_combo_box, "sensitive",
-               G_BINDING_SYNC_CREATE);
 
        tool_item = gtk_tool_item_new ();
        widget = e_action_combo_box_new_with_action (
@@ -752,16 +726,6 @@ html_editor_constructed (GObject *object)
        gtk_toolbar_insert (toolbar, tool_item, 0);
        priv->size_combo_box = g_object_ref (widget);
        gtk_widget_show_all (GTK_WIDGET (tool_item));
-
-       /* Add input methods to the context menu. */
-       widget = e_html_editor_get_managed_widget (
-               editor, "/context-menu/context-input-methods-menu");
-       widget = gtk_menu_item_get_submenu (GTK_MENU_ITEM (widget));
-       g_object_get (
-               G_OBJECT (priv->html_editor_view), "im-context", &im_context, NULL);
-       gtk_im_multicontext_append_menuitems (
-               GTK_IM_MULTICONTEXT (im_context),
-               GTK_MENU_SHELL (widget));
 }
 
 static void
@@ -793,19 +757,23 @@ html_editor_dispose (GObject *object)
        g_clear_object (&priv->mode_combo_box);
        g_clear_object (&priv->size_combo_box);
        g_clear_object (&priv->style_combo_box);
-       g_clear_object (&priv->scrolled_window);
-
-       g_clear_object (&priv->table_cell);
-       g_clear_object (&priv->current_node);
-       g_clear_object (&priv->image);
-
-       g_clear_object (&priv->html_editor_view);
 
        /* Chain up to parent's dispose() method. */
        G_OBJECT_CLASS (e_html_editor_parent_class)->dispose (object);
 }
 
 static void
+html_editor_finalize (GObject *object)
+{
+       EHTMLEditor *editor = E_HTML_EDITOR (object);
+
+       g_hash_table_destroy (editor->priv->content_editors);
+
+       /* Chain up to parent's method. */
+       G_OBJECT_CLASS (e_html_editor_parent_class)->finalize (object);
+}
+
+static void
 html_editor_submit_alert (EAlertSink *alert_sink,
                           EAlert *alert)
 {
@@ -851,6 +819,7 @@ e_html_editor_class_init (EHTMLEditorClass *class)
        object_class->get_property = html_editor_get_property;
        object_class->constructed = html_editor_constructed;
        object_class->dispose = html_editor_dispose;
+       object_class->finalize = html_editor_finalize;
 
        widget_class = GTK_WIDGET_CLASS (class);
        widget_class->parent_set = html_editor_parent_changed;
@@ -877,7 +846,7 @@ e_html_editor_class_init (EHTMLEditorClass *class)
                NULL, NULL,
                g_cclosure_marshal_VOID__BOXED,
                G_TYPE_NONE, 1,
-               GDK_TYPE_EVENT | G_SIGNAL_TYPE_STATIC_SCOPE);
+               G_TYPE_UINT);
 
        signals[SPELL_LANGUAGES_CHANGED] = g_signal_new (
                "spell-languages-changed",
@@ -899,7 +868,6 @@ static void
 e_html_editor_init (EHTMLEditor *editor)
 {
        EHTMLEditorPrivate *priv;
-       GtkWidget *widget;
        gchar *filename;
        GError *error = NULL;
 
@@ -916,8 +884,7 @@ e_html_editor_init (EHTMLEditor *editor)
        priv->language_actions = gtk_action_group_new ("language");
        priv->spell_check_actions = gtk_action_group_new ("spell-check");
        priv->suggestion_actions = gtk_action_group_new ("suggestion");
-       priv->html_editor_view = g_object_ref_sink (e_html_editor_view_new ());
-       priv->selection = e_html_editor_view_get_selection (priv->html_editor_view);
+       priv->content_editors = g_hash_table_new_full (camel_strcase_hash, camel_strcase_equal, g_free, NULL);
 
        filename = html_editor_find_ui_file ("e-html-editor-manager.ui");
        if (!gtk_ui_manager_add_ui_from_file (priv->manager, filename, &error)) {
@@ -925,42 +892,186 @@ e_html_editor_init (EHTMLEditor *editor)
                g_clear_error (&error);
        }
        g_free (filename);
+}
 
-       editor_actions_init (editor);
-       priv->editor_layout_row = 2;
+static void
+e_html_editor_content_editor_initialized (EContentEditor *content_editor,
+                                         gpointer user_data)
+{
+       ESimpleAsyncResult *async_result = user_data;
+       EHTMLEditor *html_editor;
 
-       /* Tweak the main-toolbar style. */
-       widget = e_html_editor_get_managed_widget (editor, "/main-toolbar");
-       gtk_style_context_add_class (
-               gtk_widget_get_style_context (widget),
-               GTK_STYLE_CLASS_PRIMARY_TOOLBAR);
+       g_return_if_fail (E_IS_SIMPLE_ASYNC_RESULT (async_result));
+
+       html_editor = e_simple_async_result_get_user_data (async_result);
+       g_return_if_fail (E_IS_HTML_EDITOR (html_editor));
+       g_return_if_fail (content_editor == e_html_editor_get_content_editor (html_editor));
+
+       e_binding_bind_property (
+               html_editor->priv->color_combo_box, "current-color",
+               content_editor, "font-color",
+               G_BINDING_SYNC_CREATE | G_BINDING_BIDIRECTIONAL);
+       e_binding_bind_property (
+               content_editor, "editable",
+               html_editor->priv->color_combo_box, "sensitive",
+               G_BINDING_SYNC_CREATE);
+       editor_actions_bind (html_editor);
+
+       g_object_set (G_OBJECT (content_editor),
+               "halign", GTK_ALIGN_FILL,
+               "hexpand", TRUE,
+               "valign", GTK_ALIGN_FILL,
+               "vexpand", TRUE,
+               "changed", FALSE,
+               NULL);
+
+       e_simple_async_result_complete (async_result);
+
+       g_object_unref (async_result);
 }
 
 /**
  * e_html_editor_new:
+ * @callback: a callback to be called when the editor is ready
+ * @user_data: a used data passed into the @callback
  *
- * Constructs a new #EHTMLEditor.
+ * Constructs a new #EHTMLEditor asynchronously. The result is returned
+ * by e_html_editor_new_finish(), which should be called inside @callback.
  *
- * Returns: A newly created widget. [transfer-full]
- */
+ * Since: 3.22
+ **/
+void
+e_html_editor_new (GAsyncReadyCallback callback,
+                  gpointer user_data)
+{
+       EHTMLEditor *html_editor;
+       EContentEditor *content_editor;
+       ESimpleAsyncResult *async_result;
+
+       g_return_if_fail (callback != NULL);
+
+       html_editor = g_object_new (E_TYPE_HTML_EDITOR, NULL);
+       async_result = e_simple_async_result_new (NULL, callback, user_data, e_html_editor_new);
+       e_simple_async_result_set_user_data (async_result, html_editor, g_object_unref);
+
+       content_editor = e_html_editor_get_content_editor (html_editor);
+       e_content_editor_initialize (content_editor, e_html_editor_content_editor_initialized, async_result);
+}
+
+/**
+ * e_html_editor_new_finish:
+ * @result: a #GAsyncResult passed to callback from e_html_editor_new()
+ * @error: an optional #GError
+ *
+ * Finishes the call of e_html_editor_new().
+ *
+ * Returns: (transfer-full): A newly created #EHTMLEditor.
+ *
+ * Since: 3.22
+ **/
 GtkWidget *
-e_html_editor_new (void)
+e_html_editor_new_finish (GAsyncResult *result,
+                         GError **error)
 {
-       return g_object_new (E_TYPE_HTML_EDITOR, NULL);
+       ESimpleAsyncResult *eresult;
+
+       g_return_val_if_fail (E_IS_SIMPLE_ASYNC_RESULT (result), NULL);
+       g_return_val_if_fail (g_async_result_is_tagged (result, e_html_editor_new), NULL);
+
+       eresult = E_SIMPLE_ASYNC_RESULT (result);
+
+       return e_simple_async_result_steal_user_data (eresult);
 }
 
 /**
- * e_html_editor_get_view:
+ * e_html_editor_get_content_editor:
  * @editor: an #EHTMLEditor
  *
- * Returns instance of #EHTMLEditorView used in the @editor.
+ * Returns instance of #EContentEditor used in the @editor.
  */
-EHTMLEditorView *
-e_html_editor_get_view (EHTMLEditor *editor)
+EContentEditor *
+e_html_editor_get_content_editor (EHTMLEditor *editor)
 {
        g_return_val_if_fail (E_IS_HTML_EDITOR (editor), NULL);
 
-       return editor->priv->html_editor_view;
+       if (!editor->priv->use_content_editor) {
+               GSettings *settings;
+               gchar *name;
+
+               if (!g_hash_table_size (editor->priv->content_editors))
+                       return NULL;
+
+               settings = e_util_ref_settings ("org.gnome.evolution.mail");
+               name = g_settings_get_string (settings, "composer-editor");
+               g_clear_object (&settings);
+
+               if (name)
+                       editor->priv->use_content_editor = g_hash_table_lookup 
(editor->priv->content_editors, name);
+
+               g_free (name);
+
+               if (!editor->priv->use_content_editor)
+                       editor->priv->use_content_editor = g_hash_table_lookup 
(editor->priv->content_editors, DEFAULT_CONTENT_EDITOR_NAME);
+
+               if (!editor->priv->use_content_editor) {
+                       GHashTableIter iter;
+                       gpointer key, value;
+
+                       g_hash_table_iter_init (&iter, editor->priv->content_editors);
+                       if (g_hash_table_iter_next (&iter, &key, &value)) {
+                               editor->priv->use_content_editor = value;
+                       }
+               }
+
+               if (editor->priv->use_content_editor)
+                       e_content_editor_setup_editor (editor->priv->use_content_editor, editor);
+       }
+
+       return editor->priv->use_content_editor;
+}
+
+/* Private function */
+const gchar *
+e_html_editor_get_content_editor_name (EHTMLEditor *editor)
+{
+       EContentEditor *cnt_editor;
+       GHashTableIter iter;
+       gpointer key, value;
+
+       g_return_val_if_fail (E_IS_HTML_EDITOR (editor), NULL);
+
+       cnt_editor = e_html_editor_get_content_editor (editor);
+       if (!cnt_editor)
+               return NULL;
+
+       g_hash_table_iter_init (&iter, editor->priv->content_editors);
+       if (g_hash_table_iter_next (&iter, &key, &value)) {
+               if (value == cnt_editor)
+                       return key;
+       }
+
+       return NULL;
+}
+
+void
+e_html_editor_register_content_editor (EHTMLEditor *editor,
+                                      const gchar *name,
+                                       EContentEditor *cnt_editor)
+{
+       EContentEditor *already_taken;
+
+       g_return_if_fail (E_IS_HTML_EDITOR (editor));
+       g_return_if_fail (name != NULL);
+       g_return_if_fail (E_IS_CONTENT_EDITOR (cnt_editor));
+
+       already_taken = g_hash_table_lookup (editor->priv->content_editors, name);
+
+       if (already_taken) {
+               g_warning ("%s: Cannot register %s with name '%s', because it's already taken by %s",
+                       G_STRFUNC, G_OBJECT_TYPE_NAME (cnt_editor), name, G_OBJECT_TYPE_NAME (already_taken));
+       } else {
+               g_hash_table_insert (editor->priv->content_editors, g_strdup (name), cnt_editor);
+       }
 }
 
 /**
@@ -1175,7 +1286,7 @@ e_html_editor_pack_above (EHTMLEditor *editor,
  * @as_html: whether the content should be saved as HTML or plain text
  * @error:[out] a #GError
  *
- * Saves current content of the #EHTMLEditorView into given file. When @as_html
+ * Saves current content of the #EContentEditor into given file. When @as_html
  * is @FALSE, the content is first converted into plain text.
  *
  * Returns: @TRUE when content is succesfully saved, @FALSE otherwise.
@@ -1186,6 +1297,7 @@ e_html_editor_save (EHTMLEditor *editor,
                     gboolean as_html,
                     GError **error)
 {
+       EContentEditor *cnt_editor;
        GFile *file;
        GFileOutputStream *stream;
        gchar *content;
@@ -1197,12 +1309,20 @@ e_html_editor_save (EHTMLEditor *editor,
        if ((error && *error) || !stream)
                return FALSE;
 
+       cnt_editor = e_html_editor_get_content_editor (editor);
+
        if (as_html)
-               content = e_html_editor_view_get_text_html (
-                       E_HTML_EDITOR_VIEW (editor), NULL, NULL);
+               content = e_content_editor_get_content (
+                       cnt_editor,
+                       E_CONTENT_EDITOR_GET_TEXT_HTML |
+                       E_CONTENT_EDITOR_GET_PROCESSED,
+                       NULL, NULL);
        else
-               content = e_html_editor_view_get_text_plain (
-                       E_HTML_EDITOR_VIEW (editor));
+               content = e_content_editor_get_content (
+                       cnt_editor,
+                       E_CONTENT_EDITOR_GET_TEXT_PLAIN |
+                       E_CONTENT_EDITOR_GET_PROCESSED,
+                       NULL, NULL);
 
        if (!content || !*content) {
                g_set_error (
diff --git a/e-util/e-html-editor.h b/e-util/e-html-editor.h
index c78d33d..e1381da 100644
--- a/e-util/e-html-editor.h
+++ b/e-util/e-html-editor.h
@@ -28,7 +28,7 @@
 #include <gtk/gtk.h>
 #include <e-util/e-activity.h>
 #include <e-util/e-activity-bar.h>
-#include <e-util/e-html-editor-view.h>
+#include <e-util/e-content-editor.h>
 
 /* Standard GObject macros */
 #define E_TYPE_HTML_EDITOR \
@@ -63,16 +63,24 @@ struct _EHTMLEditor {
 struct _EHTMLEditorClass {
        GtkGridClass parent_class;
 
-       void            (*update_actions)       (EHTMLEditor *editor,
-                                                GdkEventButton *event);
+       void            (*update_actions)       (EHTMLEditor *editor);
+
        void            (*spell_languages_changed)
                                                (EHTMLEditor *editor);
 };
 
 GType          e_html_editor_get_type          (void) G_GNUC_CONST;
-GtkWidget *    e_html_editor_new               (void);
-EHTMLEditorView *
-               e_html_editor_get_view          (EHTMLEditor *editor);
+void           e_html_editor_new               (GAsyncReadyCallback callback,
+                                                gpointer user_data);
+GtkWidget *    e_html_editor_new_finish        (GAsyncResult *result,
+                                                GError **error);
+EContentEditor *
+               e_html_editor_get_content_editor
+                                               (EHTMLEditor *editor);
+void           e_html_editor_register_content_editor
+                                               (EHTMLEditor *editor,
+                                                const gchar *name,
+                                                EContentEditor *cnt_editor);
 GtkBuilder *   e_html_editor_get_builder       (EHTMLEditor *editor);
 GtkUIManager * e_html_editor_get_ui_manager    (EHTMLEditor *editor);
 GtkAction *    e_html_editor_get_action        (EHTMLEditor *editor,
diff --git a/e-util/e-mail-signature-editor.c b/e-util/e-mail-signature-editor.c
index ae67296..45c5f7a 100644
--- a/e-util/e-mail-signature-editor.c
+++ b/e-util/e-mail-signature-editor.c
@@ -24,6 +24,7 @@
 #include "e-alert-dialog.h"
 #include "e-alert-sink.h"
 #include "e-alert-bar.h"
+#include "e-simple-async-result.h"
 
 #define E_MAIL_SIGNATURE_EDITOR_GET_PRIVATE(obj) \
        (G_TYPE_INSTANCE_GET_PRIVATE \
@@ -101,7 +102,7 @@ mail_signature_editor_loaded_cb (GObject *object,
                                  gpointer user_data)
 {
        EHTMLEditor *editor;
-       EHTMLEditorView *view;
+       EContentEditor *cnt_editor;
        ESource *source;
        EMailSignatureEditor *window;
        ESourceMailSignature *extension;
@@ -144,17 +145,24 @@ mail_signature_editor_loaded_cb (GObject *object,
        is_html = (g_strcmp0 (mime_type, "text/html") == 0);
 
        editor = e_mail_signature_editor_get_editor (window);
-       view = e_html_editor_get_view (editor);
-       e_html_editor_view_set_html_mode (view, is_html);
+       cnt_editor = e_html_editor_get_content_editor (editor);
+       e_content_editor_set_html_mode (cnt_editor, is_html);
 
        if (is_html) {
-               if (strstr (contents, "data-evo-signature-plain-text-mode")) {
-                       e_html_editor_view_set_html_mode (view, FALSE);
-                       e_html_editor_view_set_is_message_from_draft (view, TRUE);
-               }
-               e_html_editor_view_set_text_html (view, contents);
+               if (strstr (contents, "data-evo-signature-plain-text-mode"))
+                       e_content_editor_set_html_mode (cnt_editor, FALSE);
+
+               e_content_editor_insert_content (
+                       cnt_editor,
+                       contents,
+                       E_CONTENT_EDITOR_INSERT_TEXT_HTML |
+                       E_CONTENT_EDITOR_INSERT_REPLACE_ALL);
        } else
-               e_html_editor_view_set_text_plain (view, contents);
+               e_content_editor_insert_content (
+                       cnt_editor,
+                       contents,
+                       E_CONTENT_EDITOR_INSERT_TEXT_PLAIN |
+                       E_CONTENT_EDITOR_INSERT_REPLACE_ALL);
 
        g_free (contents);
 
@@ -180,18 +188,18 @@ action_close_cb (GtkAction *action,
                  EMailSignatureEditor *window)
 {
        EHTMLEditor *editor;
-       EHTMLEditorView *view;
+       EContentEditor *cnt_editor;
        gboolean something_changed = FALSE;
        const gchar *original_name;
        const gchar *signature_name;
 
+       editor = e_mail_signature_editor_get_editor (window);
+       cnt_editor = e_html_editor_get_content_editor (editor);
+
        original_name = window->priv->original_name;
        signature_name = gtk_entry_get_text (GTK_ENTRY (window->priv->entry));
 
-       editor = e_mail_signature_editor_get_editor (window);
-       view = e_html_editor_get_view (editor);
-
-       something_changed |= e_html_editor_view_can_undo (view);
+       something_changed |= e_content_editor_can_undo (cnt_editor);
        something_changed |= (strcmp (signature_name, original_name) != 0);
 
        if (something_changed) {
@@ -202,6 +210,7 @@ action_close_cb (GtkAction *action,
                        "widgets:ask-signature-changed", NULL);
                if (response == GTK_RESPONSE_YES) {
                        GtkActionGroup *action_group;
+                       GtkAction *action;
 
                        action_group = window->priv->action_group;
                        action = gtk_action_group_get_action (
@@ -317,6 +326,16 @@ static GtkActionEntry entries[] = {
 };
 
 static void
+mail_signature_editor_set_editor (EMailSignatureEditor *editor,
+                                 EHTMLEditor *html_editor)
+{
+       g_return_if_fail (E_IS_HTML_EDITOR (html_editor));
+       g_return_if_fail (editor->priv->editor == NULL);
+
+       editor->priv->editor = g_object_ref (html_editor);
+}
+
+static void
 mail_signature_editor_set_registry (EMailSignatureEditor *editor,
                                     ESourceRegistry *registry)
 {
@@ -366,6 +385,12 @@ mail_signature_editor_set_property (GObject *object,
                                     GParamSpec *pspec)
 {
        switch (property_id) {
+               case PROP_EDITOR:
+                       mail_signature_editor_set_editor (
+                               E_MAIL_SIGNATURE_EDITOR (object),
+                               g_value_get_object (value));
+                       return;
+
                case PROP_REGISTRY:
                        mail_signature_editor_set_registry (
                                E_MAIL_SIGNATURE_EDITOR (object),
@@ -485,7 +510,7 @@ mail_signature_editor_constructed (GObject *object)
        GtkActionGroup *action_group;
        EFocusTracker *focus_tracker;
        EHTMLEditor *editor;
-       EHTMLEditorView *view;
+       EContentEditor *cnt_editor;
        GtkUIManager *ui_manager;
        GDBusObject *dbus_object;
        ESource *source;
@@ -501,7 +526,7 @@ mail_signature_editor_constructed (GObject *object)
 
        window = E_MAIL_SIGNATURE_EDITOR (object);
        editor = e_mail_signature_editor_get_editor (window);
-       view = e_html_editor_get_view (editor);
+       cnt_editor = e_html_editor_get_content_editor (editor);
 
        ui_manager = e_html_editor_get_ui_manager (editor);
 
@@ -612,7 +637,7 @@ mail_signature_editor_constructed (GObject *object)
        if (source == NULL) {
                gtk_widget_grab_focus (window->priv->entry);
        } else {
-               gtk_widget_grab_focus (GTK_WIDGET (view));
+               gtk_widget_grab_focus (GTK_WIDGET (cnt_editor));
        }
 
        /* Load file content only for an existing signature.
@@ -659,7 +684,8 @@ e_mail_signature_editor_class_init (EMailSignatureEditorClass *class)
                        NULL,
                        NULL,
                        E_TYPE_HTML_EDITOR,
-                       G_PARAM_READABLE |
+                       G_PARAM_READWRITE |
+                       G_PARAM_CONSTRUCT_ONLY |
                        G_PARAM_STATIC_STRINGS));
 
        g_object_class_install_property (
@@ -702,23 +728,95 @@ static void
 e_mail_signature_editor_init (EMailSignatureEditor *editor)
 {
        editor->priv = E_MAIL_SIGNATURE_EDITOR_GET_PRIVATE (editor);
+}
 
-       editor->priv->editor = g_object_ref_sink (e_html_editor_new ());
+typedef struct _CreateEditorData {
+       ESourceRegistry *registry;
+       ESource *source;
+} CreateEditorData;
+
+static void
+create_editor_data_free (gpointer ptr)
+{
+       CreateEditorData *ced = ptr;
+
+       if (ced) {
+               g_clear_object (&ced->registry);
+               g_clear_object (&ced->source);
+               g_free (ced);
+       }
 }
 
-GtkWidget *
+static void
+mail_signature_editor_html_editor_created_cb (GObject *source_object,
+                                             GAsyncResult *async_result,
+                                             gpointer user_data)
+{
+       GtkWidget *html_editor, *signature_editor;
+       ESimpleAsyncResult *eresult = user_data;
+       CreateEditorData *ced;
+       GError *error = NULL;
+
+       g_return_if_fail (E_IS_SIMPLE_ASYNC_RESULT (eresult));
+
+       ced = e_simple_async_result_get_user_data (eresult);
+       g_return_if_fail (ced != NULL);
+
+       html_editor = e_html_editor_new_finish (async_result, &error);
+       if (error) {
+               g_warning ("%s: Failed to create HTML editor: %s", G_STRFUNC, error->message);
+               g_clear_error (&error);
+       }
+
+       signature_editor = g_object_new (E_TYPE_MAIL_SIGNATURE_EDITOR,
+               "registry", ced->registry,
+               "source", ced->source,
+               "editor", html_editor,
+               NULL);
+
+       e_simple_async_result_set_op_pointer (eresult, signature_editor);
+
+       e_simple_async_result_complete (eresult);
+
+       g_object_unref (eresult);
+}
+
+void
 e_mail_signature_editor_new (ESourceRegistry *registry,
-                             ESource *source)
+                            ESource *source,
+                            GAsyncReadyCallback callback,
+                            gpointer user_data)
 {
-       g_return_val_if_fail (E_IS_SOURCE_REGISTRY (registry), NULL);
+       ESimpleAsyncResult *eresult;
+       CreateEditorData *ced;
+
+       g_return_if_fail (E_IS_SOURCE_REGISTRY (registry));
 
        if (source != NULL)
-               g_return_val_if_fail (E_IS_SOURCE (source), NULL);
+               g_return_if_fail (E_IS_SOURCE (source));
+
+       ced = g_new0 (CreateEditorData, 1);
+       ced->registry = g_object_ref (registry);
+       ced->source = source ? g_object_ref (source) : NULL;
+
+       eresult = e_simple_async_result_new (NULL, callback, user_data, e_mail_signature_editor_new);
+       e_simple_async_result_set_user_data (eresult, ced, create_editor_data_free);
+
+       e_html_editor_new (mail_signature_editor_html_editor_created_cb, eresult);
+}
+
+GtkWidget *
+e_mail_signature_editor_new_finish (GAsyncResult *result,
+                                   GError **error)
+{
+       ESimpleAsyncResult *eresult;
+
+       g_return_val_if_fail (E_IS_SIMPLE_ASYNC_RESULT (result), NULL);
+       g_return_val_if_fail (g_async_result_is_tagged (result, e_mail_signature_editor_new), NULL);
+
+       eresult = E_SIMPLE_ASYNC_RESULT (result);
 
-       return g_object_new (
-               E_TYPE_MAIL_SIGNATURE_EDITOR,
-               "registry", registry,
-               "source", source, NULL);
+       return e_simple_async_result_get_op_pointer (eresult);
 }
 
 EHTMLEditor *
@@ -825,7 +923,7 @@ e_mail_signature_editor_commit (EMailSignatureEditor *window,
        const gchar *mime_type;
        gchar *contents;
        EHTMLEditor *editor;
-       EHTMLEditorView *view;
+       EContentEditor *cnt_editor;
 
        g_return_if_fail (E_IS_MAIL_SIGNATURE_EDITOR (window));
 
@@ -833,10 +931,14 @@ e_mail_signature_editor_commit (EMailSignatureEditor *window,
        source = e_mail_signature_editor_get_source (window);
 
        editor = e_mail_signature_editor_get_editor (window);
-       view = e_html_editor_get_view (editor);
+       cnt_editor = e_html_editor_get_content_editor (editor);
 
        mime_type = "text/html";
-       contents = e_html_editor_view_get_body_text_html_for_drafts (view);
+       contents = e_content_editor_get_content (
+               cnt_editor,
+               E_CONTENT_EDITOR_GET_TEXT_HTML |
+               E_CONTENT_EDITOR_GET_BODY,
+               NULL, NULL);
 
        extension_name = E_SOURCE_EXTENSION_MAIL_SIGNATURE;
        extension = e_source_get_extension (source, extension_name);
diff --git a/e-util/e-mail-signature-editor.h b/e-util/e-mail-signature-editor.h
index 1b8622d..5cb4b72 100644
--- a/e-util/e-mail-signature-editor.h
+++ b/e-util/e-mail-signature-editor.h
@@ -63,8 +63,13 @@ struct _EMailSignatureEditorClass {
 
 GType          e_mail_signature_editor_get_type
                                                (void) G_GNUC_CONST;
-GtkWidget *    e_mail_signature_editor_new     (ESourceRegistry *registry,
-                                                ESource *source);
+void           e_mail_signature_editor_new     (ESourceRegistry *registry,
+                                                ESource *source,
+                                                GAsyncReadyCallback callback,
+                                                gpointer user_data);
+GtkWidget *    e_mail_signature_editor_new_finish
+                                               (GAsyncResult *result,
+                                                GError **error);
 EHTMLEditor *  e_mail_signature_editor_get_editor
                                                (EMailSignatureEditor *editor);
 EFocusTracker *        e_mail_signature_editor_get_focus_tracker
diff --git a/e-util/e-mail-signature-manager.c b/e-util/e-mail-signature-manager.c
index 1ebb9a5..e202039 100644
--- a/e-util/e-mail-signature-manager.c
+++ b/e-util/e-mail-signature-manager.c
@@ -44,6 +44,7 @@ struct _EMailSignatureManagerPrivate {
        GtkWidget *edit_button;         /* not referenced */
        GtkWidget *remove_button;       /* not referenced */
        GtkWidget *preview;             /* not referenced */
+       GtkWidget *preview_frame;       /* not referenced */
 
        gboolean prefer_html;
 };
@@ -376,13 +377,10 @@ mail_signature_manager_constructed (GObject *object)
 
        container = GTK_WIDGET (manager);
 
-       widget = gtk_scrolled_window_new (NULL, NULL);
-       gtk_scrolled_window_set_policy (
-               GTK_SCROLLED_WINDOW (widget),
-               GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
-       gtk_scrolled_window_set_shadow_type (
-               GTK_SCROLLED_WINDOW (widget), GTK_SHADOW_IN);
+       widget = gtk_frame_new (NULL);
+       gtk_frame_set_shadow_type (GTK_FRAME (widget), GTK_SHADOW_IN);
        gtk_paned_pack2 (GTK_PANED (container), widget, FALSE, FALSE);
+       manager->priv->preview_frame = widget;  /* not referenced */
        gtk_widget_show (widget);
 
        container = widget;
@@ -396,26 +394,46 @@ mail_signature_manager_constructed (GObject *object)
 }
 
 static void
-mail_signature_manager_add_signature (EMailSignatureManager *manager)
+mail_signature_manager_editor_created_add_signature_cb (GObject *source_object,
+                                                       GAsyncResult *result,
+                                                       gpointer user_data)
 {
+       EMailSignatureManager *manager = user_data;
        EHTMLEditor *editor;
-       EHTMLEditorView *view;
-       ESourceRegistry *registry;
+       EContentEditor *cnt_editor;
        GtkWidget *widget;
+       GError *error = NULL;
 
-       registry = e_mail_signature_manager_get_registry (manager);
+       g_return_if_fail (E_IS_MAIL_SIGNATURE_MANAGER (manager));
 
-       widget = e_mail_signature_editor_new (registry, NULL);
+       widget = e_mail_signature_editor_new_finish (result, &error);
+       if (error) {
+               g_warning ("%s: Failed to create signature editor: %s", G_STRFUNC, error->message);
+               g_clear_error (&error);
+               g_clear_object (&manager);
+               return;
+       }
 
-       editor = e_mail_signature_editor_get_editor (
-               E_MAIL_SIGNATURE_EDITOR (widget));
-       view = e_html_editor_get_view (editor);
-       e_html_editor_view_set_html_mode (
-               view, manager->priv->prefer_html);
+       editor = e_mail_signature_editor_get_editor (E_MAIL_SIGNATURE_EDITOR (widget));
+       cnt_editor = e_html_editor_get_content_editor (editor);
+       e_content_editor_set_html_mode (cnt_editor, manager->priv->prefer_html);
 
        mail_signature_manager_emit_editor_created (manager, widget);
 
        gtk_widget_grab_focus (manager->priv->tree_view);
+
+       g_clear_object (&manager);
+}
+
+static void
+mail_signature_manager_add_signature (EMailSignatureManager *manager)
+{
+       ESourceRegistry *registry;
+
+       registry = e_mail_signature_manager_get_registry (manager);
+
+       e_mail_signature_editor_new (registry, NULL,
+               mail_signature_manager_editor_created_add_signature_cb, g_object_ref (manager));
 }
 
 static void
@@ -447,12 +465,35 @@ mail_signature_manager_editor_created (EMailSignatureManager *manager,
 }
 
 static void
+mail_signature_manager_editor_created_edit_signature_cb (GObject *source_object,
+                                                        GAsyncResult *result,
+                                                        gpointer user_data)
+{
+       EMailSignatureManager *manager = user_data;
+       GtkWidget *widget;
+       GError *error = NULL;
+
+       g_return_if_fail (E_IS_MAIL_SIGNATURE_MANAGER (manager));
+
+       widget = e_mail_signature_editor_new_finish (result, &error);
+       if (error) {
+               g_warning ("%s: Failed to create signature editor: %s", G_STRFUNC, error->message);
+               g_clear_error (&error);
+               g_clear_object (&manager);
+               return;
+       }
+
+       mail_signature_manager_emit_editor_created (manager, widget);
+
+       g_clear_object (&manager);
+}
+
+static void
 mail_signature_manager_edit_signature (EMailSignatureManager *manager)
 {
        EMailSignatureTreeView *tree_view;
        ESourceMailSignature *extension;
        ESourceRegistry *registry;
-       GtkWidget *editor;
        ESource *source;
        GFileInfo *file_info;
        GFile *file;
@@ -488,8 +529,8 @@ mail_signature_manager_edit_signature (EMailSignatureManager *manager)
        if (g_file_info_get_attribute_boolean (file_info, attribute))
                goto script;
 
-       editor = e_mail_signature_editor_new (registry, source);
-       mail_signature_manager_emit_editor_created (manager, editor);
+       e_mail_signature_editor_new (registry, source,
+               mail_signature_manager_editor_created_edit_signature_cb, g_object_ref (manager));
 
        goto exit;
 
diff --git a/e-util/e-mail-signature-preview.c b/e-util/e-mail-signature-preview.c
index 32acfb0..d8e4553 100644
--- a/e-util/e-mail-signature-preview.c
+++ b/e-util/e-mail-signature-preview.c
@@ -56,78 +56,6 @@ G_DEFINE_TYPE (
        E_TYPE_WEB_VIEW)
 
 static void
-replace_local_image_links (WebKitDOMDocument *document)
-{
-       gint ii, length;
-       WebKitDOMNodeList *list;
-
-       list = webkit_dom_document_query_selector_all (
-               document, "img[src^=\"file://\"]", NULL);
-       length = webkit_dom_node_list_get_length (list);
-
-       for (ii = 0; ii < length; ii++) {
-               gchar *src, *new_src;
-               WebKitDOMHTMLImageElement *img;
-
-               img = WEBKIT_DOM_HTML_IMAGE_ELEMENT (
-                       webkit_dom_node_list_item (list, ii));
-               src = webkit_dom_html_image_element_get_src (img);
-
-               /* this forms "evo-file://", which can be loaded,
-                * while "file://" cannot be, due to WebKit policy */
-               new_src = g_strconcat ("evo-", src, NULL);
-               webkit_dom_html_image_element_set_src (img, new_src);
-               g_free (new_src);
-               g_free (src);
-               g_object_unref (img);
-       }
-       g_object_unref (list);
-
-       list = webkit_dom_document_get_elements_by_tag_name ( document, "iframe");
-       length = webkit_dom_node_list_get_length (list);
-       for (ii = 0; ii < length; ii++) {
-               WebKitDOMDocument *content_document;
-               WebKitDOMHTMLIFrameElement *iframe;
-
-               iframe = WEBKIT_DOM_HTML_IFRAME_ELEMENT (
-                       webkit_dom_node_list_item (list, ii));
-
-               content_document =
-                       webkit_dom_html_iframe_element_get_content_document (iframe);
-
-               if (content_document && WEBKIT_DOM_IS_DOCUMENT (content_document))
-                       replace_local_image_links (content_document);
-               g_object_unref (iframe);
-       }
-       g_object_unref (list);
-}
-
-static void
-signature_preview_document_loaded_cb (WebKitWebView *web_view,
-                                      WebKitWebFrame *web_frame,
-                                      gpointer user_data)
-{
-       WebKitDOMDocument *document;
-
-       document = webkit_web_view_get_dom_document (web_view);
-       replace_local_image_links (document);
-
-       if ((webkit_dom_document_query_selector (
-               document, "[data-evo-signature-plain-text-mode]", NULL))) {
-
-               WebKitDOMHTMLElement *body;
-
-               body = webkit_dom_document_get_body (document);
-
-               webkit_dom_element_set_attribute (
-                       WEBKIT_DOM_ELEMENT (body),
-                       "style",
-                       "font-family: Monospace;",
-                       NULL);
-       }
-}
-
-static void
 mail_signature_preview_load_cb (ESource *source,
                                 GAsyncResult *result,
                                 EMailSignaturePreview *preview)
@@ -166,16 +94,14 @@ mail_signature_preview_load_cb (ESource *source,
        mime_type = e_source_mail_signature_get_mime_type (extension);
 
        if (g_strcmp0 (mime_type, "text/html") == 0) {
-               webkit_web_view_load_string (
-                       WEBKIT_WEB_VIEW (preview), contents,
-                       "text/html", "UTF-8", "file:///");
+               webkit_web_view_load_html (
+                       WEBKIT_WEB_VIEW (preview), contents, "file:///");
        } else {
                gchar *string;
 
                string = g_markup_printf_escaped ("<pre>%s</pre>", contents);
-               webkit_web_view_load_string (
-                       WEBKIT_WEB_VIEW (preview), string,
-                       "text/html", "UTF-8", "file:///");
+               webkit_web_view_load_html (
+                       WEBKIT_WEB_VIEW (preview), string, "file:///");
                g_free (string);
        }
 
@@ -378,10 +304,6 @@ static void
 e_mail_signature_preview_init (EMailSignaturePreview *preview)
 {
        preview->priv = E_MAIL_SIGNATURE_PREVIEW_GET_PRIVATE (preview);
-
-       g_signal_connect (
-               preview, "document-load-finished",
-               G_CALLBACK (signature_preview_document_loaded_cb), NULL);
 }
 
 GtkWidget *
diff --git a/e-util/e-marshal.list b/e-util/e-marshal.list
index ee2106f..1ccafed 100644
--- a/e-util/e-marshal.list
+++ b/e-util/e-marshal.list
@@ -1,6 +1,7 @@
 BOOLEAN:BOXED
 BOOLEAN:BOXED,STRING
 BOOLEAN:INT,INT,BOXED
+BOOLEAN:INT,BOXED
 BOOLEAN:INT,INT,OBJECT,INT,INT,UINT
 BOOLEAN:INT,POINTER,INT,BOXED
 BOOLEAN:INT,POINTER,INT,OBJECT,INT,INT,UINT
diff --git a/e-util/e-misc-utils.c b/e-util/e-misc-utils.c
index df04561..3dcc7f0 100644
--- a/e-util/e-misc-utils.c
+++ b/e-util/e-misc-utils.c
@@ -3202,6 +3202,42 @@ e_util_cleanup_settings (void)
        g_mutex_unlock (&settings_hash_lock);
 }
 
+static gdouble
+get_screen_dpi (GdkScreen *screen)
+{
+       gdouble dpi;
+       gdouble dp, di;
+
+       dpi = gdk_screen_get_resolution (screen);
+       if (dpi != -1)
+               return dpi;
+
+       dp = hypot (gdk_screen_get_width (screen), gdk_screen_get_height (screen));
+       di = hypot (gdk_screen_get_width_mm (screen), gdk_screen_get_height_mm (screen)) / 25.4;
+
+       return dp / di;
+}
+
+guint
+e_util_normalize_font_size (GtkWidget *widget,
+                            gdouble font_size)
+{
+       /* WebKit2 uses font sizes in pixels. */
+       GdkScreen *screen;
+       gdouble dpi;
+
+       if (widget) {
+               screen = gtk_widget_has_screen (widget) ?
+                       gtk_widget_get_screen (widget) : gdk_screen_get_default ();
+       } else {
+               screen = gdk_screen_get_default ();
+       }
+
+       dpi = screen ? get_screen_dpi (screen) : 96;
+
+       return font_size / 72.0 * dpi;
+}
+
 /**
  * e_util_prompt_user:
  * @parent: parent window
@@ -3405,6 +3441,22 @@ e_util_set_entry_issue_hint (GtkWidget *entry,
        }
 }
 
+static GThread *main_thread = NULL;
+
+void
+e_util_init_main_thread (GThread *thread)
+{
+       g_return_if_fail (main_thread == NULL);
+
+       main_thread = thread ? thread : g_thread_self ();
+}
+
+gboolean
+e_util_is_main_thread (GThread *thread)
+{
+       return thread ? thread == main_thread : g_thread_self () == main_thread;
+}
+
 /**
  * e_util_save_image_from_clipboard:
  * @clipboard: a #GtkClipboard
@@ -3446,7 +3498,7 @@ e_util_save_image_from_clipboard (GtkClipboard *clipboard)
        /* Convert the filename to a URI. */
        uri = g_filename_to_uri (filename, NULL, &error);
 
-exit:
+ exit:
        if (error != NULL) {
                g_warning ("%s", error->message);
                g_error_free (error);
diff --git a/e-util/e-misc-utils.h b/e-util/e-misc-utils.h
index bde194a..9afe95c 100644
--- a/e-util/e-misc-utils.h
+++ b/e-util/e-misc-utils.h
@@ -287,9 +287,13 @@ void               e_util_run_simple_async_result_in_thread
                                                 GSimpleAsyncThreadFunc func,
                                                 GCancellable *cancellable);
 gboolean       e_util_is_running_gnome         (void);
-
 void           e_util_set_entry_issue_hint     (GtkWidget *entry,
                                                 const gchar *hint);
+
+guint          e_util_normalize_font_size      (GtkWidget *widget,
+                                                gdouble font_size);
+void           e_util_init_main_thread         (GThread *thread);
+gboolean       e_util_is_main_thread           (GThread *thread);
 gchar *                e_util_save_image_from_clipboard
                                                (GtkClipboard *clipboard);
 
diff --git a/e-util/e-search-bar.c b/e-util/e-search-bar.c
index 759956a..b5ebdde 100644
--- a/e-util/e-search-bar.c
+++ b/e-util/e-search-bar.c
@@ -44,9 +44,11 @@ struct _ESearchBarPrivate {
        GtkWidget *prev_button;
        GtkWidget *next_button;
 
+       WebKitFindController *find_controller;
+
        gchar *active_search;
 
-       guint rerun_search : 1;
+       gboolean search_forward;
 };
 
 enum {
@@ -77,7 +79,6 @@ search_bar_update_matches (ESearchBar *search_bar,
        GtkWidget *matches_label;
        gchar *text;
 
-       search_bar->priv->rerun_search = FALSE;
        matches_label = search_bar->priv->matches_label;
 
        text = g_strdup_printf (_("Matches: %u"), matches);
@@ -86,14 +87,88 @@ search_bar_update_matches (ESearchBar *search_bar,
        g_free (text);
 }
 
+ static void
+webkit_find_controller_found_text_cb (WebKitFindController *find_controller,
+                                      guint match_count,
+                                      ESearchBar *search_bar)
+{
+       GtkWidget *widget;
+       WebKitFindOptions options;
+       gboolean wrapped = FALSE;
+
+       search_bar_update_matches (search_bar, match_count);
+
+       g_free (search_bar->priv->active_search);
+       search_bar->priv->active_search =
+               g_strdup (webkit_find_controller_get_search_text (find_controller));
+
+       gtk_widget_set_sensitive (search_bar->priv->next_button, TRUE);
+       gtk_widget_set_sensitive (search_bar->priv->prev_button, TRUE);
+
+       g_object_notify (G_OBJECT (search_bar), "active-search");
+
+       options = webkit_find_controller_get_options (find_controller);
+
+       if (options & WEBKIT_FIND_OPTIONS_WRAP_AROUND)
+               wrapped = TRUE;
+
+       /* Update wrapped label visibility. */
+       widget = search_bar->priv->wrapped_next_box;
+
+       if (wrapped && search_bar->priv->search_forward)
+               gtk_widget_show (widget);
+       else
+               gtk_widget_hide (widget);
+
+       widget = search_bar->priv->wrapped_prev_box;
+
+       if (wrapped && !search_bar->priv->search_forward)
+               gtk_widget_show (widget);
+       else
+               gtk_widget_hide (widget);
+}
+
 static void
-search_bar_update_highlights (ESearchBar *search_bar)
+webkit_find_controller_failed_to_found_text_cb (WebKitFindController *find_controller,
+                                                ESearchBar *search_bar)
 {
-       EWebView *web_view;
+       WebKitFindOptions options;
+       GtkWidget *widget;
+
+       options = webkit_find_controller_get_options (find_controller);
+
+       /* If we didn't find anything, try from the beggining with WRAP_AROUND option */
+       if (!(options & WEBKIT_FIND_OPTIONS_WRAP_AROUND)) {
+               webkit_find_controller_search (
+                       find_controller,
+                       webkit_find_controller_get_search_text (find_controller),
+                       options | WEBKIT_FIND_OPTIONS_WRAP_AROUND,
+                       G_MAXUINT);
+       }
+
+       search_bar_update_matches (search_bar, 0);
+
+       g_free (search_bar->priv->active_search);
+       search_bar->priv->active_search =
+               g_strdup (webkit_find_controller_get_search_text (find_controller));
 
-       web_view = e_search_bar_get_web_view (search_bar);
+       gtk_widget_set_sensitive (search_bar->priv->next_button, FALSE);
+       gtk_widget_set_sensitive (search_bar->priv->prev_button, FALSE);
 
-       webkit_web_view_unmark_text_matches (WEBKIT_WEB_VIEW (web_view));
+       g_object_notify (G_OBJECT (search_bar), "active-search");
+
+       /* Update wrapped label visibility. */
+       widget = search_bar->priv->wrapped_next_box;
+       gtk_widget_hide (widget);
+
+       widget = search_bar->priv->wrapped_prev_box;
+       gtk_widget_hide (widget);
+}
+
+static void
+search_bar_update_highlights (ESearchBar *search_bar)
+{
+       webkit_find_controller_search_finish (search_bar->priv->find_controller);
 
        e_search_bar_changed (search_bar);
 }
@@ -102,15 +177,13 @@ static void
 search_bar_find (ESearchBar *search_bar,
                  gboolean search_forward)
 {
-       EWebView *web_view;
-       GtkWidget *widget;
+       WebKitFindController *find_controller;
        gboolean case_sensitive;
-       gboolean wrapped = FALSE;
-       gboolean success;
        gchar *text;
-       guint matches;
 
-       web_view = e_search_bar_get_web_view (search_bar);
+       find_controller = search_bar->priv->find_controller;
+       search_bar->priv->search_forward = search_forward;
+
        case_sensitive = e_search_bar_get_case_sensitive (search_bar);
        text = e_search_bar_get_text (search_bar);
 
@@ -120,46 +193,14 @@ search_bar_find (ESearchBar *search_bar,
                return;
        }
 
-       webkit_web_view_unmark_text_matches (
-               WEBKIT_WEB_VIEW (web_view));
-       matches = webkit_web_view_mark_text_matches (
-               WEBKIT_WEB_VIEW (web_view),
-               text, case_sensitive, 0);
-       webkit_web_view_set_highlight_text_matches (
-               WEBKIT_WEB_VIEW (web_view), TRUE);
-       search_bar_update_matches (search_bar, matches);
-
-       success = webkit_web_view_search_text (
-               WEBKIT_WEB_VIEW (web_view),
-               text, case_sensitive, search_forward, FALSE);
-
-       if (!success)
-               wrapped = webkit_web_view_search_text (
-                       WEBKIT_WEB_VIEW (web_view),
-                       text, case_sensitive, search_forward, TRUE);
-
-       g_free (search_bar->priv->active_search);
-       search_bar->priv->active_search = text;
-
-       gtk_widget_set_sensitive (search_bar->priv->next_button, matches != 0);
-       gtk_widget_set_sensitive (search_bar->priv->prev_button, matches != 0);
-
-       g_object_notify (G_OBJECT (search_bar), "active-search");
-
-       /* Update wrapped label visibility. */
-       widget = search_bar->priv->wrapped_next_box;
-
-       if (wrapped && search_forward)
-               gtk_widget_show (widget);
-       else
-               gtk_widget_hide (widget);
-
-       widget = search_bar->priv->wrapped_prev_box;
+       webkit_find_controller_search_finish (find_controller);
+       webkit_find_controller_search (
+               find_controller,
+               text,
+               case_sensitive ? WEBKIT_FIND_OPTIONS_NONE : WEBKIT_FIND_OPTIONS_CASE_INSENSITIVE,
+               G_MAXUINT);
 
-       if (wrapped && !search_forward)
-               gtk_widget_show (widget);
-       else
-               gtk_widget_hide (widget);
+       g_free (text);
 }
 
 static void
@@ -205,43 +246,48 @@ search_bar_toggled_cb (ESearchBar *search_bar)
 }
 
 static void
-web_view_load_status_changed_cb (WebKitWebView *webkit_web_view,
-                                 GParamSpec *pspec,
-                                 gpointer user_data)
+web_view_load_changed_cb (WebKitWebView *webkit_web_view,
+                          WebKitLoadEvent load_event,
+                          ESearchBar *search_bar)
 {
-       WebKitLoadStatus status;
-       ESearchBar *search_bar;
-
-       status = webkit_web_view_get_load_status (webkit_web_view);
-       if (status != WEBKIT_LOAD_FINISHED)
-               return;
-
-       if (!user_data)
+       if (load_event != WEBKIT_LOAD_FINISHED)
                return;
 
-       search_bar = E_SEARCH_BAR (user_data);
-
        if (gtk_widget_get_visible (GTK_WIDGET (search_bar))) {
                if (search_bar->priv->active_search != NULL) {
                       search_bar_find (search_bar, TRUE);
                }
-       } else {
+       } else
                e_web_view_update_highlights (search_bar->priv->web_view);
-       }
 }
 
 static void
 search_bar_set_web_view (ESearchBar *search_bar,
                          EWebView *web_view)
 {
+       WebKitFindController *find_controller;
+
        g_return_if_fail (E_IS_WEB_VIEW (web_view));
        g_return_if_fail (search_bar->priv->web_view == NULL);
 
        search_bar->priv->web_view = g_object_ref (web_view);
 
-       e_signal_connect_notify (
-               web_view, "notify::load-status",
-               G_CALLBACK (web_view_load_status_changed_cb), search_bar);
+       find_controller =
+               webkit_web_view_get_find_controller (WEBKIT_WEB_VIEW (web_view));
+
+       search_bar->priv->find_controller = find_controller;
+
+       g_signal_connect (
+               web_view, "load-changed",
+               G_CALLBACK (web_view_load_changed_cb), search_bar);
+
+       g_signal_connect (
+               find_controller, "found-text",
+               G_CALLBACK (webkit_find_controller_found_text_cb), search_bar);
+
+       g_signal_connect (
+               find_controller, "failed-to-find-text",
+               G_CALLBACK (webkit_find_controller_failed_to_found_text_cb), search_bar);
 }
 
 static void
@@ -393,7 +439,6 @@ static void
 search_bar_show (GtkWidget *widget)
 {
        ESearchBar *search_bar;
-       EWebView *web_view;
 
        search_bar = E_SEARCH_BAR (widget);
 
@@ -402,8 +447,7 @@ search_bar_show (GtkWidget *widget)
 
        gtk_widget_grab_focus (search_bar->priv->entry);
 
-       web_view = e_search_bar_get_web_view (search_bar);
-       webkit_web_view_unmark_text_matches (WEBKIT_WEB_VIEW (web_view));
+       webkit_find_controller_search_finish (search_bar->priv->find_controller);
 
        search_bar_find (search_bar, TRUE);
 }
@@ -442,8 +486,6 @@ search_bar_key_press_event (GtkWidget *widget,
 static void
 search_bar_clear (ESearchBar *search_bar)
 {
-       WebKitWebView *web_view;
-
        g_free (search_bar->priv->active_search);
        search_bar->priv->active_search = NULL;
 
@@ -455,9 +497,6 @@ search_bar_clear (ESearchBar *search_bar)
 
        search_bar_update_highlights (search_bar);
 
-       web_view = WEBKIT_WEB_VIEW (search_bar->priv->web_view);
-       webkit_web_view_unmark_text_matches (web_view);
-
        g_object_notify (G_OBJECT (search_bar), "active-search");
 }
 
diff --git a/e-util/e-simple-async-result.c b/e-util/e-simple-async-result.c
new file mode 100644
index 0000000..76e6cf6
--- /dev/null
+++ b/e-util/e-simple-async-result.c
@@ -0,0 +1,221 @@
+/*
+ * Copyright (C) 2016 Red Hat, Inc. (www.redhat.com)
+ *
+ * This library 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 library 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 Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <gio/gio.h>
+
+#include "e-simple-async-result.h"
+
+struct _ESimpleAsyncResultPrivate {
+       GObject *source_object;
+       GAsyncReadyCallback callback;
+       gpointer callback_user_data;
+       gpointer source_tag;
+
+       gpointer user_data;
+       GDestroyNotify destroy_user_data;
+
+       gpointer op_pointer;
+};
+
+static void e_simple_async_result_iface_init (GAsyncResultIface *iface);
+
+G_DEFINE_TYPE_WITH_CODE (ESimpleAsyncResult, e_simple_async_result, G_TYPE_OBJECT,
+       G_IMPLEMENT_INTERFACE (G_TYPE_ASYNC_RESULT, e_simple_async_result_iface_init))
+
+static gpointer
+e_simple_async_result_iface_get_user_data (GAsyncResult *result)
+{
+       ESimpleAsyncResult *eresult;
+
+       g_return_val_if_fail (E_IS_SIMPLE_ASYNC_RESULT (result), NULL);
+
+       eresult = E_SIMPLE_ASYNC_RESULT (result);
+
+       return eresult->priv->callback_user_data;
+}
+
+static GObject *
+e_simple_async_result_iface_get_source_object (GAsyncResult *result)
+{
+       ESimpleAsyncResult *eresult;
+
+       g_return_val_if_fail (E_IS_SIMPLE_ASYNC_RESULT (result), NULL);
+
+       eresult = E_SIMPLE_ASYNC_RESULT (result);
+
+       return eresult->priv->source_object;
+}
+
+static gboolean
+e_simple_async_result_iface_is_tagged (GAsyncResult *result,
+                                      gpointer source_tag)
+{
+       ESimpleAsyncResult *eresult;
+
+       g_return_val_if_fail (E_IS_SIMPLE_ASYNC_RESULT (result), FALSE);
+
+       eresult = E_SIMPLE_ASYNC_RESULT (result);
+
+       return eresult && eresult->priv->source_tag == source_tag;
+}
+
+static void
+e_simple_async_result_iface_init (GAsyncResultIface *iface)
+{
+       iface->get_user_data = e_simple_async_result_iface_get_user_data;
+       iface->get_source_object = e_simple_async_result_iface_get_source_object;
+       iface->is_tagged = e_simple_async_result_iface_is_tagged;
+}
+
+static void
+e_simple_async_result_finalize (GObject *object)
+{
+       ESimpleAsyncResult *result = E_SIMPLE_ASYNC_RESULT (object);
+
+       if (result->priv->user_data && result->priv->destroy_user_data)
+               result->priv->destroy_user_data (result->priv->user_data);
+
+       result->priv->destroy_user_data = NULL;
+       result->priv->user_data = NULL;
+
+       g_clear_object (&result->priv->source_object);
+
+       /* Chain up to parent's method */
+       G_OBJECT_CLASS (e_simple_async_result_parent_class)->finalize (object);
+}
+
+static void
+e_simple_async_result_class_init (ESimpleAsyncResultClass *class)
+{
+       GObjectClass *object_class;
+
+       g_type_class_add_private (class, sizeof (ESimpleAsyncResultPrivate));
+
+       object_class = G_OBJECT_CLASS (class);
+       object_class->finalize = e_simple_async_result_finalize;
+}
+
+static void
+e_simple_async_result_init (ESimpleAsyncResult *result)
+{
+       result->priv = G_TYPE_INSTANCE_GET_PRIVATE (result, E_TYPE_SIMPLE_ASYNC_RESULT, 
ESimpleAsyncResultPrivate);
+}
+
+ESimpleAsyncResult *
+e_simple_async_result_new (GObject *source_object,
+                          GAsyncReadyCallback callback,
+                          gpointer user_data,
+                          gpointer source_tag)
+{
+       ESimpleAsyncResult *result;
+
+       g_return_val_if_fail (callback != NULL, NULL);
+       if (source_object)
+               g_return_val_if_fail (G_IS_OBJECT (source_object), NULL);
+
+       result = g_object_new (E_TYPE_SIMPLE_ASYNC_RESULT, NULL);
+
+       result->priv->source_object = source_object ? g_object_ref (source_object) : NULL;
+       result->priv->callback = callback;
+       result->priv->callback_user_data = user_data;
+       result->priv->source_tag = source_tag;
+
+       return result;
+}
+
+void
+e_simple_async_result_set_user_data (ESimpleAsyncResult *result,
+                                    gpointer user_data,
+                                    GDestroyNotify destroy_user_data)
+{
+       ESimpleAsyncResult *eresult;
+
+       g_return_if_fail (E_IS_SIMPLE_ASYNC_RESULT (result));
+
+       eresult = E_SIMPLE_ASYNC_RESULT (result);
+
+       if (eresult->priv->user_data == user_data)
+               return;
+
+       if (eresult->priv->user_data && eresult->priv->destroy_user_data)
+               eresult->priv->destroy_user_data (eresult->priv->user_data);
+
+       eresult->priv->user_data = user_data;
+       eresult->priv->destroy_user_data = destroy_user_data;
+}
+
+gpointer
+e_simple_async_result_get_user_data (ESimpleAsyncResult *result)
+{
+       ESimpleAsyncResult *eresult;
+
+       g_return_val_if_fail (E_IS_SIMPLE_ASYNC_RESULT (result), NULL);
+
+       eresult = E_SIMPLE_ASYNC_RESULT (result);
+
+       return eresult->priv->user_data;
+}
+
+gpointer
+e_simple_async_result_steal_user_data (ESimpleAsyncResult *result)
+{
+       ESimpleAsyncResult *eresult;
+       gpointer user_data;
+
+       g_return_val_if_fail (E_IS_SIMPLE_ASYNC_RESULT (result), NULL);
+
+       eresult = E_SIMPLE_ASYNC_RESULT (result);
+
+       user_data = eresult->priv->user_data;
+
+       eresult->priv->user_data = NULL;
+       eresult->priv->destroy_user_data = NULL;
+
+       return user_data;
+}
+
+void
+e_simple_async_result_set_op_pointer (ESimpleAsyncResult *result,
+                                     gpointer ptr)
+{
+       g_return_if_fail (E_IS_SIMPLE_ASYNC_RESULT (result));
+
+       result->priv->op_pointer = ptr;
+}
+
+gpointer
+e_simple_async_result_get_op_pointer (ESimpleAsyncResult *result)
+{
+       g_return_val_if_fail (E_IS_SIMPLE_ASYNC_RESULT (result), NULL);
+
+       return result->priv->op_pointer;
+}
+
+void
+e_simple_async_result_complete (ESimpleAsyncResult *result)
+{
+       g_return_if_fail (E_IS_SIMPLE_ASYNC_RESULT (result));
+
+       g_object_ref (result);
+
+       result->priv->callback (result->priv->source_object, G_ASYNC_RESULT (result), 
result->priv->callback_user_data);
+
+       g_object_unref (result);
+}
diff --git a/e-util/e-simple-async-result.h b/e-util/e-simple-async-result.h
new file mode 100644
index 0000000..3800de1
--- /dev/null
+++ b/e-util/e-simple-async-result.h
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2016 Red Hat, Inc. (www.redhat.com)
+ *
+ * This library 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 library 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 Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#if !defined (__E_UTIL_H_INSIDE__) && !defined (LIBEUTIL_COMPILATION)
+#error "Only <e-util/e-util.h> should be included directly."
+#endif
+
+#ifndef E_SIMPLE_ASYNC_RESULT_H
+#define E_SIMPLE_ASYNC_RESULT_H
+
+#include <gio/gio.h>
+
+/* Standard GObject macros */
+#define E_TYPE_SIMPLE_ASYNC_RESULT \
+       (e_simple_async_result_get_type ())
+#define E_SIMPLE_ASYNC_RESULT(obj) \
+       (G_TYPE_CHECK_INSTANCE_CAST \
+       ((obj), E_TYPE_SIMPLE_ASYNC_RESULT, ESimpleAsyncResult))
+#define E_SIMPLE_ASYNC_RESULT_CLASS(cls) \
+       (G_TYPE_CHECK_CLASS_CAST \
+       ((cls), E_TYPE_SIMPLE_ASYNC_RESULT, ESimpleAsyncResultClass))
+#define E_IS_SIMPLE_ASYNC_RESULT(obj) \
+       (G_TYPE_CHECK_INSTANCE_TYPE \
+       ((obj), E_TYPE_SIMPLE_ASYNC_RESULT))
+#define E_IS_SIMPLE_ASYNC_RESULT_CLASS(cls) \
+       (G_TYPE_CHECK_CLASS_TYPE \
+       ((cls), E_TYPE_SIMPLE_ASYNC_RESULT))
+#define E_SIMPLE_ASYNC_RESULT_GET_CLASS(obj) \
+       (G_TYPE_INSTANCE_GET_CLASS \
+       ((obj), E_TYPE_SIMPLE_ASYNC_RESULT, ESimpleAsyncResultClass))
+
+G_BEGIN_DECLS
+
+typedef struct _ESimpleAsyncResult ESimpleAsyncResult;
+typedef struct _ESimpleAsyncResultClass ESimpleAsyncResultClass;
+typedef struct _ESimpleAsyncResultPrivate ESimpleAsyncResultPrivate;
+
+/**
+ * ESimpleAsyncResult:
+ *
+ * Contains only private data that should be read and manipulated using the
+ * functions below.
+ **/
+struct _ESimpleAsyncResult {
+       GObject parent;
+
+       ESimpleAsyncResultPrivate *priv;
+};
+
+struct _ESimpleAsyncResultClass {
+       GObjectClass parent_class;
+};
+
+GType          e_simple_async_result_get_type  (void) G_GNUC_CONST;
+ESimpleAsyncResult *
+               e_simple_async_result_new       (GObject *source_object,
+                                                GAsyncReadyCallback callback,
+                                                gpointer user_data,
+                                                gpointer source_tag);
+void           e_simple_async_result_set_user_data
+                                               (ESimpleAsyncResult *result,
+                                                gpointer user_data,
+                                                GDestroyNotify destroy_user_data);
+gpointer       e_simple_async_result_get_user_data
+                                               (ESimpleAsyncResult *result);
+gpointer       e_simple_async_result_steal_user_data
+                                               (ESimpleAsyncResult *result);
+void           e_simple_async_result_set_op_pointer
+                                               (ESimpleAsyncResult *result,
+                                                gpointer ptr);
+gpointer       e_simple_async_result_get_op_pointer
+                                               (ESimpleAsyncResult *result);
+void           e_simple_async_result_complete  (ESimpleAsyncResult *result);
+
+G_END_DECLS
+
+#endif /* E_SIMPLE_ASYNC_RESULT_H */
diff --git a/e-util/e-spell-checker.c b/e-util/e-spell-checker.c
index 6cfccf6..dbdd895 100644
--- a/e-util/e-spell-checker.c
+++ b/e-util/e-spell-checker.c
@@ -24,7 +24,6 @@
 #include "e-spell-dictionary.h"
 
 #include <libebackend/libebackend.h>
-#include <webkit/webkitspellchecker.h>
 #include <pango/pango.h>
 #include <gtk/gtk.h>
 #include <string.h>
@@ -45,27 +44,19 @@ enum {
        PROP_ACTIVE_LANGUAGES
 };
 
-/* Forward Declarations */
-static void    e_spell_checker_init_webkit_checker
-                               (WebKitSpellCheckerInterface *interface);
-
 G_DEFINE_TYPE_EXTENDED (
        ESpellChecker,
        e_spell_checker,
        G_TYPE_OBJECT,
        0,
        G_IMPLEMENT_INTERFACE (
-               E_TYPE_EXTENSIBLE, NULL)
-       G_IMPLEMENT_INTERFACE (
-               WEBKIT_TYPE_SPELL_CHECKER,
-               e_spell_checker_init_webkit_checker))
+               E_TYPE_EXTENSIBLE, NULL))
 
 /**
  * ESpellChecker:
  *
  * #ESpellChecker represents a spellchecker in Evolution. It can be used as a
- * provider for dictionaries. It also implements #WebKitSpellCheckerInterface,
- * so it can be set as a default spell-checker to WebKit editors
+ * provider for dictionaries.
  */
 
 
@@ -92,237 +83,6 @@ spell_checker_enchant_dicts_foreach_cb (gpointer key,
 }
 
 static void
-wksc_check_spelling (WebKitSpellChecker *webkit_checker,
-                     const gchar *word,
-                     gint *misspelling_location,
-                     gint *misspelling_length)
-{
-       ESpellChecker *checker = E_SPELL_CHECKER (webkit_checker);
-       GHashTable *active_dictionaries;
-       PangoLanguage *language;
-       PangoLogAttr *attrs;
-       gint length, ii;
-
-       active_dictionaries = checker->priv->active_dictionaries;
-       if (g_hash_table_size (active_dictionaries) == 0)
-               return;
-
-       length = g_utf8_strlen (word, -1);
-
-       language = pango_language_get_default ();
-       attrs = g_new (PangoLogAttr, length + 1);
-
-       pango_get_log_attrs (word, -1, -1, language, attrs, length + 1);
-
-       for (ii = 0; ii < length + 1; ii++) {
-               /* We go through each character until we find an is_word_start,
-                * then we get into an inner loop to find the is_word_end
-                * corresponding */
-               if (attrs[ii].is_word_start) {
-                       gboolean word_recognized;
-                       gint start = ii;
-                       gint end = ii;
-                       gint word_length;
-                       gchar *cstart;
-                       gint bytes;
-                       gchar *new_word;
-
-                       while (attrs[end].is_word_end < 1)
-                               end++;
-
-                       word_length = end - start;
-                       /* Set the iterator to be at the current word
-                        * end, so we don't check characters twice. */
-                       ii = end;
-
-                       cstart = g_utf8_offset_to_pointer (word, start);
-                       bytes = g_utf8_offset_to_pointer (word, end) - cstart;
-                       new_word = g_new0 (gchar, bytes + 1);
-
-                       g_utf8_strncpy (new_word, cstart, word_length);
-
-                       word_recognized = e_spell_checker_check_word (
-                               checker, new_word, strlen (new_word));
-
-                       if (word_recognized) {
-                               if (misspelling_location != NULL)
-                                       *misspelling_location = -1;
-                               if (misspelling_length != NULL)
-                                       *misspelling_length = 0;
-                       } else {
-                               if (misspelling_location != NULL)
-                                       *misspelling_location = start;
-                               if (misspelling_length != NULL)
-                                       *misspelling_length = word_length;
-                       }
-
-                       g_free (new_word);
-               }
-       }
-
-       g_free (attrs);
-}
-
-static gchar **
-wksc_get_guesses (WebKitSpellChecker *webkit_checker,
-                  const gchar *word,
-                  const gchar *context)
-{
-       ESpellChecker *checker = E_SPELL_CHECKER (webkit_checker);
-       GHashTable *active_dictionaries;
-       GList *list, *link;
-       gchar ** guesses;
-       gint ii = 0;
-
-       guesses = g_new0 (gchar *, MAX_SUGGESTIONS + 1);
-
-       active_dictionaries = checker->priv->active_dictionaries;
-       list = g_hash_table_get_keys (active_dictionaries);
-
-       for (link = list; link != NULL; link = g_list_next (link)) {
-               ESpellDictionary *dictionary;
-               GList *suggestions;
-
-               dictionary = E_SPELL_DICTIONARY (link->data);
-               suggestions = e_spell_dictionary_get_suggestions (
-                       dictionary, word, -1);
-
-               while (suggestions != NULL && ii < MAX_SUGGESTIONS) {
-                       guesses[ii++] = suggestions->data;
-                       suggestions->data = NULL;
-
-                       suggestions = g_list_delete_link (
-                               suggestions, suggestions);
-               }
-
-               g_list_free_full (suggestions, (GDestroyNotify) g_free);
-
-               if (ii >= MAX_SUGGESTIONS)
-                       break;
-       }
-
-       g_list_free (list);
-
-       return guesses;
-}
-
-static gchar *
-wksc_get_autocorrect_suggestions (WebKitSpellChecker *webkit_checker,
-                                  const gchar *word)
-{
-       /* Not supported/needed */
-       return NULL;
-}
-
-static void
-spell_checker_learn_word (WebKitSpellChecker *webkit_checker,
-                          const gchar *word)
-{
-       /* Carefully, this will add the word to all active dictionaries! */
-
-       ESpellChecker *checker;
-       GHashTable *active_dictionaries;
-       GList *list, *link;
-
-       checker = E_SPELL_CHECKER (webkit_checker);
-       active_dictionaries = checker->priv->active_dictionaries;
-       list = g_hash_table_get_keys (active_dictionaries);
-
-       for (link = list; link != NULL; link = g_list_next (link)) {
-               ESpellDictionary *dictionary;
-
-               dictionary = E_SPELL_DICTIONARY (link->data);
-               e_spell_dictionary_learn_word (dictionary, word, -1);
-       }
-
-       g_list_free (list);
-}
-
-static void
-spell_checker_ignore_word (WebKitSpellChecker *webkit_checker,
-                           const gchar *word)
-{
-       /* Carefully, this will add the word to all active dictionaries */
-
-       ESpellChecker *checker;
-       GHashTable *active_dictionaries;
-       GList *list, *link;
-
-       checker = E_SPELL_CHECKER (webkit_checker);
-       active_dictionaries = checker->priv->active_dictionaries;
-       list = g_hash_table_get_keys (active_dictionaries);
-
-       for (link = list; link != NULL; link = g_list_next (link)) {
-               ESpellDictionary *dictionary;
-
-               dictionary = E_SPELL_DICTIONARY (link->data);
-               e_spell_dictionary_ignore_word (dictionary, word, -1);
-       }
-
-       g_list_free (list);
-}
-
-static void
-wksc_update_languages (WebKitSpellChecker *webkit_checker,
-                       const gchar *languages)
-{
-       ESpellChecker *checker;
-       GHashTable *active_dictionaries;
-       GQueue queue = G_QUEUE_INIT;
-
-       checker = E_SPELL_CHECKER (webkit_checker);
-       active_dictionaries = checker->priv->active_dictionaries;
-
-       if (languages != NULL) {
-               gchar **langs;
-               gint ii;
-
-               langs = g_strsplit (languages, ",", -1);
-               for (ii = 0; langs[ii] != NULL; ii++) {
-                       ESpellDictionary *dictionary;
-
-                       dictionary = e_spell_checker_ref_dictionary (
-                               checker, langs[ii]);
-                       if (dictionary != NULL)
-                               g_queue_push_tail (&queue, dictionary);
-               }
-               g_strfreev (langs);
-       } else {
-               ESpellDictionary *dictionary;
-               PangoLanguage *pango_language;
-               const gchar *language;
-
-               pango_language = gtk_get_default_language ();
-               language = pango_language_to_string (pango_language);
-               dictionary = e_spell_checker_ref_dictionary (checker, language);
-
-               if (dictionary == NULL) {
-                       GList *list;
-
-                       list = e_spell_checker_list_available_dicts (checker);
-                       if (list != NULL) {
-                               dictionary = g_object_ref (list->data);
-                               g_list_free (list);
-                       }
-               }
-
-               if (dictionary != NULL)
-                       g_queue_push_tail (&queue, dictionary);
-       }
-
-       g_hash_table_remove_all (active_dictionaries);
-
-       while (!g_queue_is_empty (&queue)) {
-               ESpellDictionary *dictionary;
-
-               dictionary = g_queue_pop_head (&queue);
-               g_hash_table_add (active_dictionaries, dictionary);
-       }
-
-       g_object_notify (G_OBJECT (checker), "active-languages");
-}
-
-static void
 spell_checker_get_property (GObject *object,
                             guint property_id,
                             GValue *value,
@@ -403,18 +163,6 @@ e_spell_checker_class_init (ESpellCheckerClass *class)
 }
 
 static void
-e_spell_checker_init_webkit_checker (WebKitSpellCheckerInterface *interface)
-{
-       interface->check_spelling_of_string = wksc_check_spelling;
-       interface->get_autocorrect_suggestions_for_misspelled_word =
-               wksc_get_autocorrect_suggestions;
-       interface->get_guesses_for_word = wksc_get_guesses;
-       interface->ignore_word = spell_checker_ignore_word;
-       interface->learn_word = spell_checker_learn_word;
-       interface->update_spell_checking_languages = wksc_update_languages;
-}
-
-static void
 e_spell_checker_init (ESpellChecker *checker)
 {
        GHashTable *active_dictionaries;
@@ -573,6 +321,25 @@ e_spell_checker_list_available_dicts (ESpellChecker *checker)
 }
 
 /**
+ * e_spell_checker_count_available_dicts:
+ * @checker: An #ESpellChecker
+ *
+ * Returns: Count of available dictionaries.
+ **/
+guint
+e_spell_checker_count_available_dicts (ESpellChecker *checker)
+{
+       g_return_val_if_fail (E_IS_SPELL_CHECKER (checker), 0);
+
+       if (g_hash_table_size (checker->priv->dictionaries_cache) == 0) {
+               e_spell_checker_init_global_memory ();
+               g_hash_table_foreach (global_language_tags, copy_enchant_dicts, checker);
+       }
+
+       return g_hash_table_size (checker->priv->dictionaries_cache);
+}
+
+/**
  * e_spell_checker_ref_dictionary:
  * @checker: an #ESpellChecker
  * @language_code: (allow-none): language code of a dictionary, or %NULL
@@ -703,6 +470,42 @@ e_spell_checker_set_language_active (ESpellChecker *checker,
 }
 
 /**
+ * e_spell_checker_set_active_languages:
+ * @checker: An #ESpellChecker
+ * @languages: A list of languages to have activated
+ *
+ * Activates only the languages from @languages, all others will
+ * be deactivated after this function is finished.
+ **/
+void
+e_spell_checker_set_active_languages (ESpellChecker *checker,
+                                     const gchar * const *languages)
+{
+       gint ii;
+
+       g_return_if_fail (E_IS_SPELL_CHECKER (checker));
+
+       g_object_freeze_notify (G_OBJECT (checker));
+
+       for (ii = 0; languages && languages[ii]; ii++) {
+               e_spell_checker_set_language_active (checker, languages[ii], TRUE);
+       }
+
+       if (ii == g_hash_table_size (checker->priv->active_dictionaries)) {
+               g_object_thaw_notify (G_OBJECT (checker));
+               return;
+       }
+
+       g_hash_table_remove_all (checker->priv->active_dictionaries);
+       for (ii = 0; languages && languages[ii]; ii++) {
+               e_spell_checker_set_language_active (checker, languages[ii], TRUE);
+       }
+
+       g_object_notify (G_OBJECT (checker), "active-languages");
+       g_object_thaw_notify (G_OBJECT (checker));
+}
+
+/**
  * e_spell_checker_list_active_languages:
  * @checker: an #ESpellChecker
  * @n_languages: return location for the number of active languages, or %NULL
@@ -816,12 +619,24 @@ void
 e_spell_checker_ignore_word (ESpellChecker *checker,
                              const gchar *word)
 {
-       WebKitSpellCheckerInterface *interface;
+       /* Carefully, this will add the word to all active dictionaries */
+
+       GHashTable *active_dictionaries;
+       GList *list, *link;
 
        g_return_if_fail (E_IS_SPELL_CHECKER (checker));
 
-       interface = WEBKIT_SPELL_CHECKER_GET_IFACE (checker);
-       interface->ignore_word (WEBKIT_SPELL_CHECKER (checker), word);
+       active_dictionaries = checker->priv->active_dictionaries;
+       list = g_hash_table_get_keys (active_dictionaries);
+
+       for (link = list; link != NULL; link = g_list_next (link)) {
+               ESpellDictionary *dictionary;
+
+               dictionary = E_SPELL_DICTIONARY (link->data);
+               e_spell_dictionary_ignore_word (dictionary, word, -1);
+       }
+
+       g_list_free (list);
 }
 
 /**
@@ -836,10 +651,74 @@ void
 e_spell_checker_learn_word (ESpellChecker *checker,
                             const gchar *word)
 {
-       WebKitSpellCheckerInterface *interface;
+       /* Carefully, this will add the word to all active dictionaries! */
+
+       GHashTable *active_dictionaries;
+       GList *list, *link;
 
        g_return_if_fail (E_IS_SPELL_CHECKER (checker));
 
-       interface = WEBKIT_SPELL_CHECKER_GET_IFACE (checker);
-       interface->learn_word (WEBKIT_SPELL_CHECKER (checker), word);
+       active_dictionaries = checker->priv->active_dictionaries;
+       list = g_hash_table_get_keys (active_dictionaries);
+
+       for (link = list; link != NULL; link = g_list_next (link)) {
+               ESpellDictionary *dictionary;
+
+               dictionary = E_SPELL_DICTIONARY (link->data);
+               e_spell_dictionary_learn_word (dictionary, word, -1);
+       }
+
+       g_list_free (list);
+}
+
+/**
+ * e_spell_checker_get_guesses_for_word:
+ * @checker: an #ESpellChecker
+ * @word: word to get guesses for
+ *
+ * Returns: a NULL-terminated array of guesses for the @word. Free the returned
+ *    pointer with g_strfreev() when done with it.
+ **/
+gchar **
+e_spell_checker_get_guesses_for_word (ESpellChecker *checker,
+                                     const gchar *word)
+{
+       GHashTable *active_dictionaries;
+       GList *list, *link;
+       gchar **guesses;
+       gint ii = 0;
+
+       g_return_val_if_fail (E_IS_SPELL_CHECKER (checker), NULL);
+       g_return_val_if_fail (word != NULL, NULL);
+
+       guesses = g_new0 (gchar *, MAX_SUGGESTIONS + 1);
+
+       active_dictionaries = checker->priv->active_dictionaries;
+       list = g_hash_table_get_keys (active_dictionaries);
+
+       for (link = list; link != NULL; link = g_list_next (link)) {
+               ESpellDictionary *dictionary;
+               GList *suggestions;
+
+               dictionary = E_SPELL_DICTIONARY (link->data);
+               suggestions = e_spell_dictionary_get_suggestions (
+                       dictionary, word, -1);
+
+               while (suggestions != NULL && ii < MAX_SUGGESTIONS) {
+                       guesses[ii++] = suggestions->data;
+                       suggestions->data = NULL;
+
+                       suggestions = g_list_delete_link (
+                               suggestions, suggestions);
+               }
+
+               g_list_free_full (suggestions, (GDestroyNotify) g_free);
+
+               if (ii >= MAX_SUGGESTIONS)
+                       break;
+       }
+
+       g_list_free (list);
+
+       return guesses;
 }
diff --git a/e-util/e-spell-checker.h b/e-util/e-spell-checker.h
index d986e84..690401b 100644
--- a/e-util/e-spell-checker.h
+++ b/e-util/e-spell-checker.h
@@ -66,6 +66,8 @@ void          e_spell_checker_free_global_memory
 ESpellChecker *        e_spell_checker_new             (void);
 GList *                e_spell_checker_list_available_dicts
                                                (ESpellChecker *checker);
+guint          e_spell_checker_count_available_dicts
+                                               (ESpellChecker *checker);
 ESpellDictionary *
                e_spell_checker_ref_dictionary  (ESpellChecker *checker,
                                                 const gchar *language_code);
@@ -79,6 +81,9 @@ void          e_spell_checker_set_language_active
                                                (ESpellChecker *checker,
                                                 const gchar *language_code,
                                                 gboolean active);
+void           e_spell_checker_set_active_languages
+                                               (ESpellChecker *checker,
+                                                const gchar * const *languages);
 gchar **       e_spell_checker_list_active_languages
                                                (ESpellChecker *checker,
                                                 guint *n_languages);
@@ -91,6 +96,9 @@ void          e_spell_checker_learn_word      (ESpellChecker *checker,
                                                 const gchar *word);
 void           e_spell_checker_ignore_word     (ESpellChecker *checker,
                                                 const gchar *word);
+gchar **       e_spell_checker_get_guesses_for_word
+                                               (ESpellChecker *checker,
+                                                const gchar *word);
 
 G_END_DECLS
 
diff --git a/e-util/e-stock-request.c b/e-util/e-stock-request.c
index 5d70fa3..c550a2c 100644
--- a/e-util/e-stock-request.c
+++ b/e-util/e-stock-request.c
@@ -15,56 +15,87 @@
  *
  */
 
-#define LIBSOUP_USE_UNSTABLE_REQUEST_API
-
-#include "e-stock-request.h"
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
 
 #include <stdlib.h>
+#include <string.h>
+
 #include <gtk/gtk.h>
 #include <libsoup/soup.h>
 
-#include <string.h>
+#include <libedataserver/libedataserver.h>
 
-#define E_STOCK_REQUEST_GET_PRIVATE(obj) \
-       (G_TYPE_INSTANCE_GET_PRIVATE \
-       ((obj), E_TYPE_STOCK_REQUEST, EStockRequestPrivate))
+#include "e-misc-utils.h"
+#include "e-stock-request.h"
 
 struct _EStockRequestPrivate {
-       gchar *content_type;
-       gint content_length;
+       gint dummy;
 };
 
-static const gchar *data_schemes[] = { "gtk-stock", NULL };
+static void e_stock_request_content_request_init (EContentRequestInterface *iface);
 
-G_DEFINE_TYPE (EStockRequest, e_stock_request, SOUP_TYPE_REQUEST)
+G_DEFINE_TYPE_WITH_CODE (EStockRequest, e_stock_request, G_TYPE_OBJECT,
+       G_IMPLEMENT_INTERFACE (E_TYPE_CONTENT_REQUEST, e_stock_request_content_request_init))
 
 static gboolean
-handle_stock_request_idle_cb (gpointer user_data)
+e_stock_request_can_process_uri (EContentRequest *request,
+                                const gchar *uri)
 {
-       EStockRequestPrivate *priv;
-       GSimpleAsyncResult *simple;
-       GObject *object;
-       SoupURI *uri;
+       g_return_val_if_fail (E_IS_STOCK_REQUEST (request), FALSE);
+       g_return_val_if_fail (uri != NULL, FALSE);
+
+       return g_ascii_strncasecmp (uri, "gtk-stock:", 10) == 0;
+}
+
+typedef struct _StockIdleData
+{
+       EContentRequest *request;
+       const gchar *uri;
+       GObject *requester;
+       GInputStream **out_stream;
+       gint64 *out_stream_length;
+       gchar **out_mime_type;
+       GCancellable *cancellable;
+       GError **error;
+
+       gboolean success;
+       EFlag *flag;
+} StockIdleData;
+
+static gboolean
+process_stock_request_idle_cb (gpointer user_data)
+{
+       StockIdleData *sid = user_data;
+       SoupURI *suri;
        GHashTable *query = NULL;
        GtkStyleContext *context;
        GtkWidgetPath *path;
        GtkIconSet *icon_set;
        gssize size = GTK_ICON_SIZE_BUTTON;
        gchar *a_size;
-       gchar *buffer = NULL;
+       gchar *buffer = NULL, *mime_type = NULL;
        gsize buff_len = 0;
        GError *local_error = NULL;
 
-       simple = G_SIMPLE_ASYNC_RESULT (user_data);
+       g_return_val_if_fail (sid != NULL, FALSE);
+       g_return_val_if_fail (E_IS_STOCK_REQUEST (sid->request), FALSE);
+       g_return_val_if_fail (sid->uri != NULL, FALSE);
+       g_return_val_if_fail (sid->flag != NULL, FALSE);
 
-       /* This returns a new reference. */
-       object = g_async_result_get_source_object (G_ASYNC_RESULT (simple));
+       if (g_cancellable_set_error_if_cancelled (sid->cancellable, sid->error)) {
+               sid->success = FALSE;
+               e_flag_set (sid->flag);
 
-       priv = E_STOCK_REQUEST_GET_PRIVATE (object);
+               return FALSE;
+       }
+
+       suri = soup_uri_new (sid->uri);
+       g_return_val_if_fail (suri != NULL, FALSE);
 
-       uri = soup_request_get_uri (SOUP_REQUEST (object));
-       if (uri->query != NULL)
-               query = soup_form_decode (uri->query);
+       if (suri->query != NULL)
+               query = soup_form_decode (suri->query);
 
        if (query != NULL) {
                a_size = g_hash_table_lookup (query, "size");
@@ -81,7 +112,7 @@ handle_stock_request_idle_cb (gpointer user_data)
        gtk_style_context_set_path (context, path);
        gtk_widget_path_free (path);
 
-       icon_set = gtk_style_context_lookup_icon_set (context, uri->host);
+       icon_set = gtk_style_context_lookup_icon_set (context, suri->host);
        if (icon_set != NULL) {
                GdkPixbuf *pixbuf;
 
@@ -97,11 +128,19 @@ handle_stock_request_idle_cb (gpointer user_data)
                GtkIconTheme *icon_theme;
                GtkIconInfo *icon_info;
                const gchar *filename;
+               gint icon_width, icon_height;
+
+               if (!gtk_icon_size_lookup (size, &icon_width, &icon_height)) {
+                       icon_width = size;
+                       icon_height = size;
+               }
+
+               size = MAX (icon_width, icon_height);
 
                icon_theme = gtk_icon_theme_get_default ();
 
                icon_info = gtk_icon_theme_lookup_icon (
-                       icon_theme, uri->host, size,
+                       icon_theme, suri->host, size,
                        GTK_ICON_LOOKUP_USE_BUILTIN);
 
                /* Some icons can be missing in the theme */
@@ -113,9 +152,7 @@ handle_stock_request_idle_cb (gpointer user_data)
                                        buffer = NULL;
                                        buff_len = 0;
                                }
-                               priv->content_type =
-                                       g_content_type_guess (filename, NULL, 0, NULL);
-
+                               mime_type = g_content_type_guess (filename, NULL, 0, NULL);
                        } else {
                                GdkPixbuf *pixbuf;
 
@@ -129,156 +166,141 @@ handle_stock_request_idle_cb (gpointer user_data)
                        }
 
                        gtk_icon_info_free (icon_info);
-               }
-       }
-
-       /* Sanity check */
-       g_warn_if_fail (
-               ((buffer != NULL) && (local_error == NULL)) ||
-               ((buffer == NULL) && (local_error != NULL)));
-
-       if (priv->content_type == NULL)
-               priv->content_type = g_strdup ("image/png");
-       priv->content_length = buff_len;
-
-       if (buffer != NULL) {
-               GInputStream *stream;
-
-               stream = g_memory_input_stream_new_from_data (
-                       buffer, buff_len, (GDestroyNotify) g_free);
-               g_simple_async_result_set_op_res_gpointer (
-                       simple, g_object_ref (stream),
-                       (GDestroyNotify) g_object_unref);
-               g_object_unref (stream);
-       }
-
-       if (local_error != NULL)
-               g_simple_async_result_take_error (simple, local_error);
-
-       g_simple_async_result_complete_in_idle (simple);
-
-       g_object_unref (context);
-       g_object_unref (object);
+               } else if (g_strcmp0 (suri->host, "x-evolution-arrow-down") == 0) {
+                       GdkPixbuf *pixbuf;
+                       GdkRGBA rgba;
+                       guchar *data;
+                       gint stride;
+                       cairo_surface_t *surface;
+                       cairo_t *cr;
 
-       return FALSE;
-}
+                       stride = cairo_format_stride_for_width (CAIRO_FORMAT_RGB24, size);
+                       buff_len = stride * size;
+                       data = g_malloc0 (buff_len);
+                       surface = cairo_image_surface_create_for_data (data, CAIRO_FORMAT_RGB24, size, size, 
stride);
 
-static void
-stock_request_finalize (GObject *object)
-{
-       EStockRequestPrivate *priv;
+                       cr = cairo_create (surface);
 
-       priv = E_STOCK_REQUEST_GET_PRIVATE (object);
+                       if (gtk_style_context_lookup_color (context, "color", &rgba))
+                               gdk_cairo_set_source_rgba (cr, &rgba);
+                       else
+                               cairo_set_source_rgba (cr, 0.0, 0.0, 0.0, 1.0);
 
-       g_free (priv->content_type);
+                       gtk_render_background (context, cr, 0, 0, size, size);
+                       gtk_render_arrow (context, cr, G_PI, 0, 0, size);
 
-       /* Chain up to parent's finalize() method. */
-       G_OBJECT_CLASS (e_stock_request_parent_class)->finalize (object);
-}
+                       cairo_destroy (cr);
 
-static gboolean
-stock_request_check_uri (SoupRequest *request,
-                         SoupURI *uri,
-                         GError **error)
-{
-       return (strcmp (uri->scheme, "gtk-stock") == 0);
-}
+                       cairo_surface_flush (surface);
 
-static void
-stock_request_send_async (SoupRequest *request,
-                          GCancellable *cancellable,
-                          GAsyncReadyCallback callback,
-                          gpointer user_data)
-{
-       GSimpleAsyncResult *simple;
+                       pixbuf = gdk_pixbuf_new_from_data (data, GDK_COLORSPACE_RGB, TRUE, 8, size, size, 
stride, NULL, NULL);
+                       gdk_pixbuf_save_to_buffer (
+                               pixbuf, &buffer, &buff_len,
+                               "png", &local_error, NULL);
+                       g_object_unref (pixbuf);
 
-       simple = g_simple_async_result_new (
-               G_OBJECT (request), callback, user_data,
-               stock_request_send_async);
+                       cairo_surface_destroy (surface);
+                       g_free (data);
+               }
+       }
 
-       g_simple_async_result_set_check_cancellable (simple, cancellable);
+       /* Sanity check */
+       g_warn_if_fail (
+               ((buffer != NULL) && (local_error == NULL)) ||
+               ((buffer == NULL) && (local_error != NULL)));
 
-       /* Need to run this operation in an idle callback rather
-        * than a worker thread, since we're making all kinds of
-        * GdkPixbuf/GTK+ calls. */
-       g_idle_add_full (
-               G_PRIORITY_HIGH_IDLE,
-               handle_stock_request_idle_cb,
-               g_object_ref (simple),
-               (GDestroyNotify) g_object_unref);
+       if (!mime_type)
+               mime_type = g_strdup ("image/png");
 
-       g_object_unref (simple);
-}
+       if (buffer != NULL) {
+               *sid->out_stream = g_memory_input_stream_new_from_data (buffer, buff_len, g_free);;
+               *sid->out_stream_length = buff_len;
+               *sid->out_mime_type = mime_type;
 
-static GInputStream *
-stock_request_send_finish (SoupRequest *request,
-                           GAsyncResult *result,
-                           GError **error)
-{
-       GSimpleAsyncResult *simple;
-       GInputStream *stream;
+               sid->success = TRUE;
+       } else {
+               g_free (mime_type);
 
-       simple = G_SIMPLE_ASYNC_RESULT (result);
-       stream = g_simple_async_result_get_op_res_gpointer (simple);
+               if (local_error)
+                       g_propagate_error (sid->error, local_error);
 
-       if (g_simple_async_result_propagate_error (simple, error))
-               return NULL;
+               sid->success = FALSE;
+       }
 
-       /* Reset the stream before passing it back to WebKit. */
-       if (G_IS_SEEKABLE (stream))
-               g_seekable_seek (
-                       G_SEEKABLE (stream), 0,
-                       G_SEEK_SET, NULL, NULL);
+       soup_uri_free (suri);
+       g_object_unref (context);
 
-       if (stream != NULL)
-               return g_object_ref (stream);
+       e_flag_set (sid->flag);
 
-       return g_memory_input_stream_new ();
+       return FALSE;
 }
 
-static goffset
-stock_request_get_content_length (SoupRequest *request)
+static gboolean
+e_stock_request_process_sync (EContentRequest *request,
+                             const gchar *uri,
+                             GObject *requester,
+                             GInputStream **out_stream,
+                             gint64 *out_stream_length,
+                             gchar **out_mime_type,
+                             GCancellable *cancellable,
+                             GError **error)
 {
-       EStockRequestPrivate *priv;
+       StockIdleData sid;
+
+       g_return_val_if_fail (E_IS_STOCK_REQUEST (request), FALSE);
+       g_return_val_if_fail (uri != NULL, FALSE);
+
+       sid.request = request;
+       sid.uri = uri;
+       sid.requester = requester;
+       sid.out_stream = out_stream;
+       sid.out_stream_length = out_stream_length;
+       sid.out_mime_type = out_mime_type;
+       sid.cancellable = cancellable;
+       sid.error = error;
+       sid.flag = e_flag_new ();
+       sid.success = FALSE;
+
+       if (e_util_is_main_thread (NULL)) {
+               process_stock_request_idle_cb (&sid);
+       } else {
+               /* Need to run this operation in an idle callback rather
+                * than a worker thread, since we're making all kinds of
+                * GdkPixbuf/GTK+ calls. */
+               g_idle_add_full (
+                       G_PRIORITY_HIGH_IDLE,
+                       process_stock_request_idle_cb,
+                       &sid, NULL);
+
+               e_flag_wait (sid.flag);
+       }
 
-       priv = E_STOCK_REQUEST_GET_PRIVATE (request);
+       e_flag_free (sid.flag);
 
-       return priv->content_length;
+       return sid.success;
 }
 
-static const gchar *
-stock_request_get_content_type (SoupRequest *request)
+static void
+e_stock_request_content_request_init (EContentRequestInterface *iface)
 {
-       EStockRequestPrivate *priv;
-
-       priv = E_STOCK_REQUEST_GET_PRIVATE (request);
-
-       return priv->content_type;
+       iface->can_process_uri = e_stock_request_can_process_uri;
+       iface->process_sync = e_stock_request_process_sync;
 }
 
 static void
 e_stock_request_class_init (EStockRequestClass *class)
 {
-       GObjectClass *object_class;
-       SoupRequestClass *request_class;
-
        g_type_class_add_private (class, sizeof (EStockRequestPrivate));
-
-       object_class = G_OBJECT_CLASS (class);
-       object_class->finalize = stock_request_finalize;
-
-       request_class = SOUP_REQUEST_CLASS (class);
-       request_class->schemes = data_schemes;
-       request_class->check_uri = stock_request_check_uri;
-       request_class->send_async = stock_request_send_async;
-       request_class->send_finish = stock_request_send_finish;
-       request_class->get_content_length = stock_request_get_content_length;
-       request_class->get_content_type = stock_request_get_content_type;
 }
 
 static void
 e_stock_request_init (EStockRequest *request)
 {
-       request->priv = E_STOCK_REQUEST_GET_PRIVATE (request);
+       request->priv = G_TYPE_INSTANCE_GET_PRIVATE (request, E_TYPE_STOCK_REQUEST, EStockRequestPrivate);
 }
 
+EContentRequest *
+e_stock_request_new (void)
+{
+       return g_object_new (E_TYPE_STOCK_REQUEST, NULL);
+}
diff --git a/e-util/e-stock-request.h b/e-util/e-stock-request.h
index be6222b..09800d0 100644
--- a/e-util/e-stock-request.h
+++ b/e-util/e-stock-request.h
@@ -22,10 +22,7 @@
 #ifndef E_STOCK_REQUEST_H
 #define E_STOCK_REQUEST_H
 
-#define LIBSOUP_USE_UNSTABLE_REQUEST_API
-
-#include <libsoup/soup.h>
-#include <libsoup/soup-request.h>
+#include <e-util/e-content-request.h>
 
 /* Standard GObject macros */
 #define E_TYPE_STOCK_REQUEST \
@@ -53,15 +50,17 @@ typedef struct _EStockRequestClass EStockRequestClass;
 typedef struct _EStockRequestPrivate EStockRequestPrivate;
 
 struct _EStockRequest {
-       SoupRequest parent;
+       GObject parent;
        EStockRequestPrivate *priv;
 };
 
 struct _EStockRequestClass {
-       SoupRequestClass parent;
+       GObjectClass parent;
 };
 
 GType          e_stock_request_get_type        (void) G_GNUC_CONST;
+EContentRequest *
+               e_stock_request_new             (void);
 
 G_END_DECLS
 
diff --git a/e-util/e-util-enums.h b/e-util/e-util-enums.h
index d24aebd..884c041 100644
--- a/e-util/e-util-enums.h
+++ b/e-util/e-util-enums.h
@@ -124,226 +124,415 @@ typedef enum {
        E_DURATION_DAYS
 } EDurationType;
 
+/**
+ * EImageLoadingPolicy:
+ * @E_IMAGE_LOADING_POLICY_NEVER:
+ *   Never load images from a remote server.
+ * @E_IMAGE_LOADING_POLICY_SOMETIMES:
+ *   Only load images from a remote server if the sender is a known contact.
+ * @E_IMAGE_LOADING_POLICY_ALWAYS:
+ *   Always load images from a remote server.
+ *
+ * Policy for loading remote image URLs in email.  Allowing images to be
+ * loaded from a remote server may have privacy implications.
+ **/
+typedef enum {
+       E_IMAGE_LOADING_POLICY_NEVER,
+       E_IMAGE_LOADING_POLICY_SOMETIMES,
+       E_IMAGE_LOADING_POLICY_ALWAYS
+} EImageLoadingPolicy;
+
+/**
+ * EContentEditorInsertContentFlags:
+ * @E_CONTENT_EDITOR_INSERT_NONE:
+ * @E_CONTENT_EDITOR_INSERT_CONVERT:
+ * @E_CONTENT_EDITOR_INSERT_QUOTE_CONTENT:
+ * @E_CONTENT_EDITOR_INSERT_REPLACE_ALL:
+ * @E_CONTENT_EDITOR_INSERT_TEXT_HTML:
+ * @E_CONTENT_EDITOR_INSERT_TEXT_PLAIN:
+ *
+ * Since: 3.22
+ **/
+typedef enum {
+       E_CONTENT_EDITOR_INSERT_NONE            = 0,
+       E_CONTENT_EDITOR_INSERT_CONVERT         = 1 << 0,
+       E_CONTENT_EDITOR_INSERT_QUOTE_CONTENT   = 1 << 1,
+       E_CONTENT_EDITOR_INSERT_REPLACE_ALL     = 1 << 2,
+       E_CONTENT_EDITOR_INSERT_TEXT_HTML       = 1 << 3,
+       E_CONTENT_EDITOR_INSERT_TEXT_PLAIN      = 1 << 4,
+} EContentEditorInsertContentFlags;
+
+/**
+ * EContentEditorGetContentFlags:
+ * @E_CONTENT_EDITOR_GET_BODY:
+ * @E_CONTENT_EDITOR_GET_INLINE_IMAGES:
+ * @E_CONTENT_EDITOR_GET_PROCESSED: raw or processed
+ * @E_CONTENT_EDITOR_GET_TEXT_HTML:
+ * @E_CONTENT_EDITOR_GET_TEXT_PLAIN:
+ * @E_CONTENT_EDITOR_GET_EXCLUDE_SIGNATURE:
+ *
+ * Since: 3.22
+ **/
+typedef enum {
+       E_CONTENT_EDITOR_GET_BODY               = 1 << 0,
+       E_CONTENT_EDITOR_GET_INLINE_IMAGES      = 1 << 1,
+       E_CONTENT_EDITOR_GET_PROCESSED          = 1 << 2, /* raw or processed */
+       E_CONTENT_EDITOR_GET_TEXT_HTML          = 1 << 3,
+       E_CONTENT_EDITOR_GET_TEXT_PLAIN         = 1 << 4,
+       E_CONTENT_EDITOR_GET_EXCLUDE_SIGNATURE  = 1 << 5
+} EContentEditorGetContentFlags;
+
+/**
+ * EContentEditorNodeFlags:
+ * @E_CONTENT_EDITOR_NODE_UNKNOWN: None from the below, aka when cannot determine.
+ * @E_CONTENT_EDITOR_NODE_IS_ANCHOR:
+ * @E_CONTENT_EDITOR_NODE_IS_H_RULE:
+ * @E_CONTENT_EDITOR_NODE_IS_IMAGE:
+ * @E_CONTENT_EDITOR_NODE_IS_TABLE:
+ * @E_CONTENT_EDITOR_NODE_IS_TABLE_CELL:
+ * @E_CONTENT_EDITOR_NODE_IS_TEXT:
+ * @E_CONTENT_EDITOR_NODE_IS_TEXT_COLLAPSED:
+ *
+ * Since: 3.22
+ **/
+typedef enum {
+       E_CONTENT_EDITOR_NODE_UNKNOWN           = 0,
+       E_CONTENT_EDITOR_NODE_IS_ANCHOR         = 1 << 0,
+       E_CONTENT_EDITOR_NODE_IS_H_RULE         = 1 << 1,
+       E_CONTENT_EDITOR_NODE_IS_IMAGE          = 1 << 2,
+       E_CONTENT_EDITOR_NODE_IS_TABLE          = 1 << 3,
+       E_CONTENT_EDITOR_NODE_IS_TABLE_CELL     = 1 << 4,
+       E_CONTENT_EDITOR_NODE_IS_TEXT           = 1 << 5,
+       E_CONTENT_EDITOR_NODE_IS_TEXT_COLLAPSED = 1 << 6
+} EContentEditorNodeFlags;
+
+/**
+ * EContentEditorStyleFlags:
+ * @E_CONTENT_EDITOR_STYLE_NONE: None from the below.
+ * @E_CONTENT_EDITOR_STYLE_IS_BOLD:
+ * @E_CONTENT_EDITOR_STYLE_IS_ITALIC:
+ * @E_CONTENT_EDITOR_STYLE_IS_UNDERLINE:
+ * @E_CONTENT_EDITOR_STYLE_IS_STRIKETHROUGH:
+ * @E_CONTENT_EDITOR_STYLE_IS_MONOSPACE:
+ * @E_CONTENT_EDITOR_STYLE_IS_SUBSCRIPT:
+ * @E_CONTENT_EDITOR_STYLE_IS_SUPERSCRIPT:
+ *
+ * Since: 3.22
+ **/
+typedef enum {
+       E_CONTENT_EDITOR_STYLE_NONE             = 0,
+       E_CONTENT_EDITOR_STYLE_IS_BOLD          = 1 << 0,
+       E_CONTENT_EDITOR_STYLE_IS_ITALIC        = 1 << 1,
+       E_CONTENT_EDITOR_STYLE_IS_UNDERLINE     = 1 << 2,
+       E_CONTENT_EDITOR_STYLE_IS_STRIKETHROUGH = 1 << 3,
+       E_CONTENT_EDITOR_STYLE_IS_MONOSPACE     = 1 << 4,
+       E_CONTENT_EDITOR_STYLE_IS_SUBSCRIPT     = 1 << 5,
+       E_CONTENT_EDITOR_STYLE_IS_SUPERSCRIPT   = 1 << 6
+} EContentEditorStyleFlags;
+
+/**
+ * EContentEditorBlockFormat:
+ * @E_CONTENT_EDITOR_BLOCK_FORMAT_NONE:
+ * @E_CONTENT_EDITOR_BLOCK_FORMAT_PARAGRAPH:
+ * @E_CONTENT_EDITOR_BLOCK_FORMAT_PRE:
+ * @E_CONTENT_EDITOR_BLOCK_FORMAT_ADDRESS:
+ * @E_CONTENT_EDITOR_BLOCK_FORMAT_H1:
+ * @E_CONTENT_EDITOR_BLOCK_FORMAT_H2:
+ * @E_CONTENT_EDITOR_BLOCK_FORMAT_H3:
+ * @E_CONTENT_EDITOR_BLOCK_FORMAT_H4:
+ * @E_CONTENT_EDITOR_BLOCK_FORMAT_H5:
+ * @E_CONTENT_EDITOR_BLOCK_FORMAT_H6:
+ * @E_CONTENT_EDITOR_BLOCK_FORMAT_UNORDERED_LIST:
+ * @E_CONTENT_EDITOR_BLOCK_FORMAT_ORDERED_LIST:
+ * @E_CONTENT_EDITOR_BLOCK_FORMAT_ORDERED_LIST_ROMAN:
+ * @E_CONTENT_EDITOR_BLOCK_FORMAT_ORDERED_LIST_ALPHA:
+ *
+ * Since: 3.22
+ **/
 typedef enum {
-       E_HTML_EDITOR_SELECTION_BLOCK_FORMAT_NONE = 0,
-       E_HTML_EDITOR_SELECTION_BLOCK_FORMAT_PARAGRAPH,
-       E_HTML_EDITOR_SELECTION_BLOCK_FORMAT_PRE,
-       E_HTML_EDITOR_SELECTION_BLOCK_FORMAT_ADDRESS,
-       E_HTML_EDITOR_SELECTION_BLOCK_FORMAT_BLOCKQUOTE,
-       E_HTML_EDITOR_SELECTION_BLOCK_FORMAT_H1,
-       E_HTML_EDITOR_SELECTION_BLOCK_FORMAT_H2,
-       E_HTML_EDITOR_SELECTION_BLOCK_FORMAT_H3,
-       E_HTML_EDITOR_SELECTION_BLOCK_FORMAT_H4,
-       E_HTML_EDITOR_SELECTION_BLOCK_FORMAT_H5,
-       E_HTML_EDITOR_SELECTION_BLOCK_FORMAT_H6,
-       E_HTML_EDITOR_SELECTION_BLOCK_FORMAT_UNORDERED_LIST,
-       E_HTML_EDITOR_SELECTION_BLOCK_FORMAT_ORDERED_LIST,
-       E_HTML_EDITOR_SELECTION_BLOCK_FORMAT_ORDERED_LIST_ROMAN,
-       E_HTML_EDITOR_SELECTION_BLOCK_FORMAT_ORDERED_LIST_ALPHA
-} EHTMLEditorSelectionBlockFormat;
+       E_CONTENT_EDITOR_BLOCK_FORMAT_NONE = 0,
+       E_CONTENT_EDITOR_BLOCK_FORMAT_PARAGRAPH,
+       E_CONTENT_EDITOR_BLOCK_FORMAT_PRE,
+       E_CONTENT_EDITOR_BLOCK_FORMAT_ADDRESS,
+       E_CONTENT_EDITOR_BLOCK_FORMAT_H1,
+       E_CONTENT_EDITOR_BLOCK_FORMAT_H2,
+       E_CONTENT_EDITOR_BLOCK_FORMAT_H3,
+       E_CONTENT_EDITOR_BLOCK_FORMAT_H4,
+       E_CONTENT_EDITOR_BLOCK_FORMAT_H5,
+       E_CONTENT_EDITOR_BLOCK_FORMAT_H6,
+       E_CONTENT_EDITOR_BLOCK_FORMAT_UNORDERED_LIST,
+       E_CONTENT_EDITOR_BLOCK_FORMAT_ORDERED_LIST,
+       E_CONTENT_EDITOR_BLOCK_FORMAT_ORDERED_LIST_ROMAN,
+       E_CONTENT_EDITOR_BLOCK_FORMAT_ORDERED_LIST_ALPHA
+} EContentEditorBlockFormat;
 
-/* The values match the actual size in <font size="n"> */
+/**
+ * EContentEditorFontSize:
+ * @E_CONTENT_EDITOR_FONT_SIZE_TINY:
+ * @E_CONTENT_EDITOR_FONT_SIZE_SMALL:
+ * @E_CONTENT_EDITOR_FONT_SIZE_NORMAL:
+ * @E_CONTENT_EDITOR_FONT_SIZE_BIG:
+ * @E_CONTENT_EDITOR_FONT_SIZE_BIGGER:
+ * @E_CONTENT_EDITOR_FONT_SIZE_LARGE:
+ * @E_CONTENT_EDITOR_FONT_SIZE_VERY_LARGE:
+ *
+ * Note: The values match the actual size in <font size="n">
+ *
+ * Since: 3.22
+ **/
 typedef enum {
-       E_HTML_EDITOR_SELECTION_FONT_SIZE_TINY          = 1,
-       E_HTML_EDITOR_SELECTION_FONT_SIZE_SMALL         = 2,
-       E_HTML_EDITOR_SELECTION_FONT_SIZE_NORMAL        = 3,
-       E_HTML_EDITOR_SELECTION_FONT_SIZE_BIG           = 4,
-       E_HTML_EDITOR_SELECTION_FONT_SIZE_BIGGER        = 5,
-       E_HTML_EDITOR_SELECTION_FONT_SIZE_LARGE         = 6,
-       E_HTML_EDITOR_SELECTION_FONT_SIZE_VERY_LARGE    = 7
-} EHTMLEditorSelectionFontSize;
+       E_CONTENT_EDITOR_FONT_SIZE_TINY         = 1,
+       E_CONTENT_EDITOR_FONT_SIZE_SMALL        = 2,
+       E_CONTENT_EDITOR_FONT_SIZE_NORMAL       = 3,
+       E_CONTENT_EDITOR_FONT_SIZE_BIG          = 4,
+       E_CONTENT_EDITOR_FONT_SIZE_BIGGER       = 5,
+       E_CONTENT_EDITOR_FONT_SIZE_LARGE        = 6,
+       E_CONTENT_EDITOR_FONT_SIZE_VERY_LARGE   = 7
+} EContentEditorFontSize;
 
+/**
+ * EContentEditorAlignment:
+ * @E_CONTENT_EDITOR_ALIGNMENT_LEFT:
+ * @E_CONTENT_EDITOR_ALIGNMENT_CENTER:
+ * @E_CONTENT_EDITOR_ALIGNMENT_RIGHT:
+ *
+ * Since: 3.22
+ **/
 typedef enum {
-       E_HTML_EDITOR_SELECTION_ALIGNMENT_LEFT,
-       E_HTML_EDITOR_SELECTION_ALIGNMENT_CENTER,
-       E_HTML_EDITOR_SELECTION_ALIGNMENT_RIGHT
-} EHTMLEditorSelectionAlignment;
+       E_CONTENT_EDITOR_ALIGNMENT_LEFT = 0,
+       E_CONTENT_EDITOR_ALIGNMENT_CENTER,
+       E_CONTENT_EDITOR_ALIGNMENT_RIGHT
+} EContentEditorAlignment;
 
+/**
+ * EContentEditorGranularity:
+ * @E_CONTENT_EDITOR_GRANULARITY_CHARACTER:
+ * @E_CONTENT_EDITOR_GRANULARITY_WORD:
+ *
+ * Since: 3.22
+ **/
 typedef enum {
-       E_HTML_EDITOR_SELECTION_GRANULARITY_CHARACTER,
-       E_HTML_EDITOR_SELECTION_GRANULARITY_WORD
-} EHTMLEditorSelectionGranularity;
+       E_CONTENT_EDITOR_GRANULARITY_CHARACTER = 0,
+       E_CONTENT_EDITOR_GRANULARITY_WORD
+} EContentEditorGranularity;
 
 /**
- * EHTMLEditorViewCommand:
- * @E_HTML_EDITOR_VIEW_COMMAND_BACKGROUND_COLOR:
+ * EContentEditorCommand:
+ * @E_CONTENT_EDITOR_COMMAND_BACKGROUND_COLOR:
  *   Sets background color to given value.
- * @E_HTML_EDITOR_VIEW_COMMAND_BOLD:
+ * @E_CONTENT_EDITOR_COMMAND_BOLD:
  *   Toggles bold formatting of current selection.
- * @E_HTML_EDITOR_VIEW_COMMAND_COPY:
+ * @E_CONTENT_EDITOR_COMMAND_COPY:
  *   Copies current selection to clipboard.
- * @E_HTML_EDITOR_VIEW_COMMAND_CREATE_LINK:
+ * @E_CONTENT_EDITOR_COMMAND_CREATE_LINK:
  *   Converts current selection to a link that points to URL in value
- * @E_HTML_EDITOR_VIEW_COMMAND_CUT:
+ * @E_CONTENT_EDITOR_COMMAND_CUT:
  *   Cuts current selection to clipboard.
- * @E_HTML_EDITOR_VIEW_COMMAND_DEFAULT_PARAGRAPH_SEPARATOR:
+ * @E_CONTENT_EDITOR_COMMAND_DEFAULT_PARAGRAPH_SEPARATOR:
  *   (XXX Explain me!)
- * @E_HTML_EDITOR_VIEW_COMMAND_DELETE:
+ * @E_CONTENT_EDITOR_COMMAND_DELETE:
  *   Deletes current selection.
- * @E_HTML_EDITOR_VIEW_COMMAND_FIND_STRING:
+ * @E_CONTENT_EDITOR_COMMAND_FIND_STRING:
  *   Highlights given string.
- * @E_HTML_EDITOR_VIEW_COMMAND_FONT_NAME:
+ * @E_CONTENT_EDITOR_COMMAND_FONT_NAME:
  *   Sets font name to given value.
- * @E_HTML_EDITOR_VIEW_COMMAND_FONT_SIZE:
+ * @E_CONTENT_EDITOR_COMMAND_FONT_SIZE:
  *   Sets font point size to given value (no units, just number)
- * @E_HTML_EDITOR_VIEW_COMMAND_FONT_SIZE_DELTA:
+ * @E_CONTENT_EDITOR_COMMAND_FONT_SIZE_DELTA:
  *   Changes font size by given delta value (no units, just number)
- * @E_HTML_EDITOR_VIEW_COMMAND_FORE_COLOR:
+ * @E_CONTENT_EDITOR_COMMAND_FORE_COLOR:
  *   Sets font color to given value
- * @E_HTML_EDITOR_VIEW_COMMAND_FORMAT_BLOCK:
+ * @E_CONTENT_EDITOR_COMMAND_FORMAT_BLOCK:
  *   Sets block type of current paragraph to given format. Allowed formats
  *   are "BLOCKQUOTE", "H1", "H2", "H3", "H4", "H5", "H6", "P", "PRE" and
  *   "ADDRESS".
- * @E_HTML_EDITOR_VIEW_COMMAND_FORWARD_DELETE:
+ * @E_CONTENT_EDITOR_COMMAND_FORWARD_DELETE:
  *   (XXX Explain me!)
- * @E_HTML_EDITOR_VIEW_COMMAND_HILITE_COLOR:
+ * @E_CONTENT_EDITOR_COMMAND_HILITE_COLOR:
  *   Sets color in which results of "FindString" command should be
  *   highlighted to given value.
- * @E_HTML_EDITOR_VIEW_COMMAND_INDENT:
+ * @E_CONTENT_EDITOR_COMMAND_INDENT:
  *   Indents current paragraph by one level.
- * @E_HTML_EDITOR_VIEW_COMMAND_INSERT_HTML:
+ * @E_CONTENT_EDITOR_COMMAND_INSERT_HTML:
  *   Inserts give HTML code into document.
- * @E_HTML_EDITOR_VIEW_COMMAND_INSERT_HORIZONTAL_RULE:
+ * @E_CONTENT_EDITOR_COMMAND_INSERT_HORIZONTAL_RULE:
  *   Inserts a horizontal rule (&lt;HR&gt;) on current line.
- * @E_HTML_EDITOR_VIEW_COMMAND_INSERT_IMAGE:
+ * @E_CONTENT_EDITOR_COMMAND_INSERT_IMAGE:
  *   Inserts an image with given source file.
- * @E_HTML_EDITOR_VIEW_COMMAND_INSERT_LINE_BREAK:
+ * @E_CONTENT_EDITOR_COMMAND_INSERT_LINE_BREAK:
  *   Breaks line at current cursor position.
- * @E_HTML_EDITOR_VIEW_COMMAND_INSERT_NEW_LINE_IN_QUOTED_CONTENT:
+ * @E_CONTENT_EDITOR_COMMAND_INSERT_NEW_LINE_IN_QUOTED_CONTENT:
  *   Breaks citation at current cursor position.
- * @E_HTML_EDITOR_VIEW_COMMAND_INSERT_ORDERED_LIST:
+ * @E_CONTENT_EDITOR_COMMAND_INSERT_ORDERED_LIST:
  *   Creates an ordered list environment at current cursor position.
- * @E_HTML_EDITOR_VIEW_COMMAND_INSERT_PARAGRAPH:
+ * @E_CONTENT_EDITOR_COMMAND_INSERT_PARAGRAPH:
  *   Inserts a new paragraph at current cursor position.
- * @E_HTML_EDITOR_VIEW_COMMAND_INSERT_TEXT:
+ * @E_CONTENT_EDITOR_COMMAND_INSERT_TEXT:
  *   Inserts given text at current cursor position.
- * @E_HTML_EDITOR_VIEW_COMMAND_INSERT_UNORDERED_LIST:
+ * @E_CONTENT_EDITOR_COMMAND_INSERT_UNORDERED_LIST:
  *   Creates an undordered list environment at current cursor position.
- * @E_HTML_EDITOR_VIEW_COMMAND_ITALIC:
+ * @E_CONTENT_EDITOR_COMMAND_ITALIC:
  *   Toggles italic formatting of current selection.
- * @E_HTML_EDITOR_VIEW_COMMAND_JUSTIFY_CENTER:
+ * @E_CONTENT_EDITOR_COMMAND_JUSTIFY_CENTER:
  *   Aligns current paragraph to center.
- * @E_HTML_EDITOR_VIEW_COMMAND_JUSTIFY_FULL:
+ * @E_CONTENT_EDITOR_COMMAND_JUSTIFY_FULL:
  *   Justifies current paragraph to block.
- * @E_HTML_EDITOR_VIEW_COMMAND_JUSTIFY_NONE:
+ * @E_CONTENT_EDITOR_COMMAND_JUSTIFY_NONE:
  *   Removes any justification or alignment of current paragraph.
- * @E_HTML_EDITOR_VIEW_COMMAND_JUSTIFY_RIGHT:
+ * @E_CONTENT_EDITOR_COMMAND_JUSTIFY_RIGHT:
  *   Aligns current paragraph to right.
- * @E_HTML_EDITOR_VIEW_COMMAND_OUTDENT:
+ * @E_CONTENT_EDITOR_COMMAND_OUTDENT:
  *   Outdents current paragraph by one level.
- * @E_HTML_EDITOR_VIEW_COMMAND_PASTE:
+ * @E_CONTENT_EDITOR_COMMAND_PASTE:
  *   Pastes clipboard content at current cursor position.
- * @E_HTML_EDITOR_VIEW_COMMAND_PASTE_AND_MATCH_STYLE:
+ * @E_CONTENT_EDITOR_COMMAND_PASTE_AND_MATCH_STYLE:
  *   Pastes clipboard content and matches its style to style at current
  *   cursor position.
- * @E_HTML_EDITOR_VIEW_COMMAND_PASTE_AS_PLAIN_TEXT:
+ * @E_CONTENT_EDITOR_COMMAND_PASTE_AS_PLAIN_TEXT:
  *   Pastes clipboard content at current cursor position removing any HTML
  *   formatting.
- * @E_HTML_EDITOR_VIEW_COMMAND_PRINT:
+ * @E_CONTENT_EDITOR_COMMAND_PRINT:
  *   Print current document.
- * @E_HTML_EDITOR_VIEW_COMMAND_REDO:
+ * @E_CONTENT_EDITOR_COMMAND_REDO:
  *   Redoes last action.
- * @E_HTML_EDITOR_VIEW_COMMAND_REMOVE_FORMAT:
+ * @E_CONTENT_EDITOR_COMMAND_REMOVE_FORMAT:
  *   Removes any formatting of current selection.
- * @E_HTML_EDITOR_VIEW_COMMAND_SELECT_ALL:
+ * @E_CONTENT_EDITOR_COMMAND_SELECT_ALL:
  *   Extends selects to the entire document.
- * @E_HTML_EDITOR_VIEW_COMMAND_STRIKETHROUGH:
+ * @E_CONTENT_EDITOR_COMMAND_STRIKETHROUGH:
  *   Toggles strikethrough formatting.
- * @E_HTML_EDITOR_VIEW_COMMAND_STYLE_WITH_CSS:
+ * @E_CONTENT_EDITOR_COMMAND_STYLE_WITH_CSS:
  *   Toggles whether style should be defined in CSS "style" attribute of
  *   elements or whether to use deprecated &lt;FONT&gt; tags. Depends on
  *   whether given value is "true" or "false".
- * @E_HTML_EDITOR_VIEW_COMMAND_SUBSCRIPT:
+ * @E_CONTENT_EDITOR_COMMAND_SUBSCRIPT:
  *   Toggles subscript of current selection.
- * @E_HTML_EDITOR_VIEW_COMMAND_SUPERSCRIPT:
+ * @E_CONTENT_EDITOR_COMMAND_SUPERSCRIPT:
  *   Toggles superscript of current selection.
- * @E_HTML_EDITOR_VIEW_COMMAND_TRANSPOSE:
+ * @E_CONTENT_EDITOR_COMMAND_TRANSPOSE:
  *   (XXX Explain me!)
- * @E_HTML_EDITOR_VIEW_COMMAND_UNDERLINE:
+ * @E_CONTENT_EDITOR_COMMAND_UNDERLINE:
  *   Toggles underline formatting of current selection.
- * @E_HTML_EDITOR_VIEW_COMMAND_UNDO:
+ * @E_CONTENT_EDITOR_COMMAND_UNDO:
  *   Undoes last action.
- * @E_HTML_EDITOR_VIEW_COMMAND_UNLINK:
+ * @E_CONTENT_EDITOR_COMMAND_UNLINK:
  *   Removes active links (&lt;A&gt;) from current selection (if there's any).
- * @E_HTML_EDITOR_VIEW_COMMAND_UNSELECT:
+ * @E_CONTENT_EDITOR_COMMAND_UNSELECT:
  *   Cancels current selection.
- * @E_HTML_EDITOR_VIEW_COMMAND_USE_CSS:
+ * @E_CONTENT_EDITOR_COMMAND_USE_CSS:
  *   Whether to allow use of CSS or not depending on whether given value is
  *   "true" or "false".
  *
  * Specifies the DOM command to execute in e_editor_widget_exec_command().
  * Some commands require value to be passed in, which is always stated in the
  * documentation.
- */
+ *
+ * Since: 3.22
+ **/
 typedef enum {
-       E_HTML_EDITOR_VIEW_COMMAND_BACKGROUND_COLOR,
-       E_HTML_EDITOR_VIEW_COMMAND_BOLD,
-       E_HTML_EDITOR_VIEW_COMMAND_COPY,
-       E_HTML_EDITOR_VIEW_COMMAND_CREATE_LINK,
-       E_HTML_EDITOR_VIEW_COMMAND_CUT,
-       E_HTML_EDITOR_VIEW_COMMAND_DEFAULT_PARAGRAPH_SEPARATOR,
-       E_HTML_EDITOR_VIEW_COMMAND_DELETE,
-       E_HTML_EDITOR_VIEW_COMMAND_FIND_STRING,
-       E_HTML_EDITOR_VIEW_COMMAND_FONT_NAME,
-       E_HTML_EDITOR_VIEW_COMMAND_FONT_SIZE,
-       E_HTML_EDITOR_VIEW_COMMAND_FONT_SIZE_DELTA,
-       E_HTML_EDITOR_VIEW_COMMAND_FORE_COLOR,
-       E_HTML_EDITOR_VIEW_COMMAND_FORMAT_BLOCK,
-       E_HTML_EDITOR_VIEW_COMMAND_FORWARD_DELETE,
-       E_HTML_EDITOR_VIEW_COMMAND_HILITE_COLOR,
-       E_HTML_EDITOR_VIEW_COMMAND_INDENT,
-       E_HTML_EDITOR_VIEW_COMMAND_INSERT_HTML,
-       E_HTML_EDITOR_VIEW_COMMAND_INSERT_HORIZONTAL_RULE,
-       E_HTML_EDITOR_VIEW_COMMAND_INSERT_IMAGE,
-       E_HTML_EDITOR_VIEW_COMMAND_INSERT_LINE_BREAK,
-       E_HTML_EDITOR_VIEW_COMMAND_INSERT_NEW_LINE_IN_QUOTED_CONTENT,
-       E_HTML_EDITOR_VIEW_COMMAND_INSERT_ORDERED_LIST,
-       E_HTML_EDITOR_VIEW_COMMAND_INSERT_PARAGRAPH,
-       E_HTML_EDITOR_VIEW_COMMAND_INSERT_TEXT,
-       E_HTML_EDITOR_VIEW_COMMAND_INSERT_UNORDERED_LIST,
-       E_HTML_EDITOR_VIEW_COMMAND_ITALIC,
-       E_HTML_EDITOR_VIEW_COMMAND_JUSTIFY_CENTER,
-       E_HTML_EDITOR_VIEW_COMMAND_JUSTIFY_FULL,
-       E_HTML_EDITOR_VIEW_COMMAND_JUSTIFY_LEFT,
-       E_HTML_EDITOR_VIEW_COMMAND_JUSTIFY_NONE,
-       E_HTML_EDITOR_VIEW_COMMAND_JUSTIFY_RIGHT,
-       E_HTML_EDITOR_VIEW_COMMAND_OUTDENT,
-       E_HTML_EDITOR_VIEW_COMMAND_PASTE,
-       E_HTML_EDITOR_VIEW_COMMAND_PASTE_AND_MATCH_STYLE,
-       E_HTML_EDITOR_VIEW_COMMAND_PASTE_AS_PLAIN_TEXT,
-       E_HTML_EDITOR_VIEW_COMMAND_PRINT,
-       E_HTML_EDITOR_VIEW_COMMAND_REDO,
-       E_HTML_EDITOR_VIEW_COMMAND_REMOVE_FORMAT,
-       E_HTML_EDITOR_VIEW_COMMAND_SELECT_ALL,
-       E_HTML_EDITOR_VIEW_COMMAND_STRIKETHROUGH,
-       E_HTML_EDITOR_VIEW_COMMAND_STYLE_WITH_CSS,
-       E_HTML_EDITOR_VIEW_COMMAND_SUBSCRIPT,
-       E_HTML_EDITOR_VIEW_COMMAND_SUPERSCRIPT,
-       E_HTML_EDITOR_VIEW_COMMAND_TRANSPOSE,
-       E_HTML_EDITOR_VIEW_COMMAND_UNDERLINE,
-       E_HTML_EDITOR_VIEW_COMMAND_UNDO,
-       E_HTML_EDITOR_VIEW_COMMAND_UNLINK,
-       E_HTML_EDITOR_VIEW_COMMAND_UNSELECT,
-       E_HTML_EDITOR_VIEW_COMMAND_USE_CSS
-} EHTMLEditorViewCommand;
+       E_CONTENT_EDITOR_COMMAND_BACKGROUND_COLOR,
+       E_CONTENT_EDITOR_COMMAND_BOLD,
+       E_CONTENT_EDITOR_COMMAND_COPY,
+       E_CONTENT_EDITOR_COMMAND_CREATE_LINK,
+       E_CONTENT_EDITOR_COMMAND_CUT,
+       E_CONTENT_EDITOR_COMMAND_DEFAULT_PARAGRAPH_SEPARATOR,
+       E_CONTENT_EDITOR_COMMAND_DELETE,
+       E_CONTENT_EDITOR_COMMAND_FIND_STRING,
+       E_CONTENT_EDITOR_COMMAND_FONT_NAME,
+       E_CONTENT_EDITOR_COMMAND_FONT_SIZE,
+       E_CONTENT_EDITOR_COMMAND_FONT_SIZE_DELTA,
+       E_CONTENT_EDITOR_COMMAND_FORE_COLOR,
+       E_CONTENT_EDITOR_COMMAND_FORMAT_BLOCK,
+       E_CONTENT_EDITOR_COMMAND_FORWARD_DELETE,
+       E_CONTENT_EDITOR_COMMAND_HILITE_COLOR,
+       E_CONTENT_EDITOR_COMMAND_INDENT,
+       E_CONTENT_EDITOR_COMMAND_INSERT_HTML,
+       E_CONTENT_EDITOR_COMMAND_INSERT_HORIZONTAL_RULE,
+       E_CONTENT_EDITOR_COMMAND_INSERT_IMAGE,
+       E_CONTENT_EDITOR_COMMAND_INSERT_LINE_BREAK,
+       E_CONTENT_EDITOR_COMMAND_INSERT_NEW_LINE_IN_QUOTED_CONTENT,
+       E_CONTENT_EDITOR_COMMAND_INSERT_ORDERED_LIST,
+       E_CONTENT_EDITOR_COMMAND_INSERT_PARAGRAPH,
+       E_CONTENT_EDITOR_COMMAND_INSERT_TEXT,
+       E_CONTENT_EDITOR_COMMAND_INSERT_UNORDERED_LIST,
+       E_CONTENT_EDITOR_COMMAND_ITALIC,
+       E_CONTENT_EDITOR_COMMAND_JUSTIFY_CENTER,
+       E_CONTENT_EDITOR_COMMAND_JUSTIFY_FULL,
+       E_CONTENT_EDITOR_COMMAND_JUSTIFY_LEFT,
+       E_CONTENT_EDITOR_COMMAND_JUSTIFY_NONE,
+       E_CONTENT_EDITOR_COMMAND_JUSTIFY_RIGHT,
+       E_CONTENT_EDITOR_COMMAND_OUTDENT,
+       E_CONTENT_EDITOR_COMMAND_PASTE,
+       E_CONTENT_EDITOR_COMMAND_PASTE_AND_MATCH_STYLE,
+       E_CONTENT_EDITOR_COMMAND_PASTE_AS_PLAIN_TEXT,
+       E_CONTENT_EDITOR_COMMAND_PRINT,
+       E_CONTENT_EDITOR_COMMAND_REDO,
+       E_CONTENT_EDITOR_COMMAND_REMOVE_FORMAT,
+       E_CONTENT_EDITOR_COMMAND_SELECT_ALL,
+       E_CONTENT_EDITOR_COMMAND_STRIKETHROUGH,
+       E_CONTENT_EDITOR_COMMAND_STYLE_WITH_CSS,
+       E_CONTENT_EDITOR_COMMAND_SUBSCRIPT,
+       E_CONTENT_EDITOR_COMMAND_SUPERSCRIPT,
+       E_CONTENT_EDITOR_COMMAND_TRANSPOSE,
+       E_CONTENT_EDITOR_COMMAND_UNDERLINE,
+       E_CONTENT_EDITOR_COMMAND_UNDO,
+       E_CONTENT_EDITOR_COMMAND_UNLINK,
+       E_CONTENT_EDITOR_COMMAND_UNSELECT,
+       E_CONTENT_EDITOR_COMMAND_USE_CSS
+} EContentEditorCommand;
 
 /**
- * EImageLoadingPolicy:
- * @E_IMAGE_LOADING_POLICY_NEVER:
- *   Never load images from a remote server.
- * @E_IMAGE_LOADING_POLICY_SOMETIMES:
- *   Only load images from a remote server if the sender is a known contact.
- * @E_IMAGE_LOADING_POLICY_ALWAYS:
- *   Always load images from a remote server.
+ * EContentEditorScope:
+ * @E_CONTENT_EDITOR_SCOPE_CELL:
+ * @E_CONTENT_EDITOR_SCOPE_ROW:
+ * @E_CONTENT_EDITOR_SCOPE_COLUMN:
+ * @E_CONTENT_EDITOR_SCOPE_TABLE:
  *
- * Policy for loading remote image URLs in email.  Allowing images to be
- * loaded from a remote server may have privacy implications.
+ * Since: 3.22
  **/
 typedef enum {
-       E_IMAGE_LOADING_POLICY_NEVER,
-       E_IMAGE_LOADING_POLICY_SOMETIMES,
-       E_IMAGE_LOADING_POLICY_ALWAYS
-} EImageLoadingPolicy;
+       E_CONTENT_EDITOR_SCOPE_CELL = 0,
+       E_CONTENT_EDITOR_SCOPE_ROW,
+       E_CONTENT_EDITOR_SCOPE_COLUMN,
+       E_CONTENT_EDITOR_SCOPE_TABLE
+} EContentEditorScope;
+
+/**
+ * EContentEditorUnit:
+ * @E_CONTENT_EDITOR_UNIT_AUTO:
+ * @E_CONTENT_EDITOR_UNIT_PIXEL:
+ * @E_CONTENT_EDITOR_UNIT_PERCENTAGE:
+ *
+ * Since: 3.22
+ **/
+typedef enum {
+       E_CONTENT_EDITOR_UNIT_AUTO = 0,
+       E_CONTENT_EDITOR_UNIT_PIXEL,
+       E_CONTENT_EDITOR_UNIT_PERCENTAGE
+} EContentEditorUnit;
+
+/**
+ * EContentEditorCommand:
+ * @E_CONTENT_EDITOR_FIND_NEXT: Search for the next occurrence of the text.
+ *    This is the default. It's mutually exclusive with @E_CONTENT_EDITOR_FIND_PREVIOUS.
+ * @E_CONTENT_EDITOR_FIND_PREVIOUS: Search for the previous occurrence of the text.
+ *    It's mutually exclusive with @E_CONTENT_EDITOR_FIND_NEXT.
+ * @E_CONTENT_EDITOR_FIND_MODE_BACKWARDS: The search mode is backwards. If not set,
+ *    then the mode is forward.
+ * @E_CONTENT_EDITOR_FIND_CASE_INSENSITIVE: Search case insensitively.
+ * @E_CONTENT_EDITOR_FIND_WRAP_AROUND: Wrap around when searching.
+ *
+ * Flags to use to modify behaviour of the search for the text.
+ *
+ * Since: 3.22
+ **/
+typedef enum {
+       E_CONTENT_EDITOR_FIND_NEXT              = 1 << 0,
+       E_CONTENT_EDITOR_FIND_PREVIOUS          = 1 << 1,
+       E_CONTENT_EDITOR_FIND_MODE_BACKWARDS    = 1 << 2,
+       E_CONTENT_EDITOR_FIND_CASE_INSENSITIVE  = 1 << 3,
+       E_CONTENT_EDITOR_FIND_WRAP_AROUND       = 1 << 4
+} EContentEditorFindFlags;
 
 G_END_DECLS
 
diff --git a/e-util/e-util.h b/e-util/e-util.h
index aa4bf14..8b3d163 100644
--- a/e-util/e-util.h
+++ b/e-util/e-util.h
@@ -32,7 +32,6 @@
 #include <e-util/e-alert-sink.h>
 #include <e-util/e-alert.h>
 #include <e-util/e-attachment-bar.h>
-#include <e-util/e-attachment-button.h>
 #include <e-util/e-attachment-dialog.h>
 #include <e-util/e-attachment-handler-image.h>
 #include <e-util/e-attachment-handler.h>
@@ -86,6 +85,8 @@
 #include <e-util/e-config.h>
 #include <e-util/e-conflict-search-selector.h>
 #include <e-util/e-contact-store.h>
+#include <e-util/e-content-editor.h>
+#include <e-util/e-content-request.h>
 #include <e-util/e-data-capture.h>
 #include <e-util/e-dateedit.h>
 #include <e-util/e-datetime-format.h>
@@ -111,6 +112,7 @@
 #include <e-util/e-filter-part.h>
 #include <e-util/e-filter-rule.h>
 #include <e-util/e-focus-tracker.h>
+#ifndef E_UTIL_INCLUDE_WITHOUT_WEBKIT
 #include <e-util/e-html-editor-actions.h>
 #include <e-util/e-html-editor-cell-dialog.h>
 #include <e-util/e-html-editor-dialog.h>
@@ -121,13 +123,11 @@
 #include <e-util/e-html-editor-page-dialog.h>
 #include <e-util/e-html-editor-paragraph-dialog.h>
 #include <e-util/e-html-editor-replace-dialog.h>
-#include <e-util/e-html-editor-selection.h>
 #include <e-util/e-html-editor-spell-check-dialog.h>
 #include <e-util/e-html-editor-table-dialog.h>
 #include <e-util/e-html-editor-text-dialog.h>
-#include <e-util/e-html-editor-utils.h>
-#include <e-util/e-html-editor-view.h>
 #include <e-util/e-html-editor.h>
+#endif
 #include <e-util/e-html-utils.h>
 #include <e-util/e-icon-factory.h>
 #include <e-util/e-image-chooser.h>
@@ -137,9 +137,11 @@
 #include <e-util/e-interval-chooser.h>
 #include <e-util/e-mail-identity-combo-box.h>
 #include <e-util/e-mail-signature-combo-box.h>
+#ifndef E_UTIL_INCLUDE_WITHOUT_WEBKIT
 #include <e-util/e-mail-signature-editor.h>
 #include <e-util/e-mail-signature-manager.h>
 #include <e-util/e-mail-signature-preview.h>
+#endif
 #include <e-util/e-mail-signature-script-dialog.h>
 #include <e-util/e-mail-signature-tree-view.h>
 #include <e-util/e-map.h>
@@ -165,7 +167,9 @@
 #include <e-util/e-popup-menu.h>
 #include <e-util/e-port-entry.h>
 #include <e-util/e-preferences-window.h>
+#ifndef E_UTIL_INCLUDE_WITHOUT_WEBKIT
 #include <e-util/e-preview-pane.h>
+#endif
 #include <e-util/e-print.h>
 #include <e-util/e-printable.h>
 #include <e-util/e-proxy-combo-box.h>
@@ -177,13 +181,16 @@
 #include <e-util/e-reflow.h>
 #include <e-util/e-rule-context.h>
 #include <e-util/e-rule-editor.h>
+#ifndef E_UTIL_INCLUDE_WITHOUT_WEBKIT
 #include <e-util/e-search-bar.h>
+#endif
 #include <e-util/e-selectable.h>
 #include <e-util/e-selection-model-array.h>
 #include <e-util/e-selection-model-simple.h>
 #include <e-util/e-selection-model.h>
 #include <e-util/e-selection.h>
 #include <e-util/e-send-options.h>
+#include <e-util/e-simple-async-result.h>
 #include <e-util/e-sorter-array.h>
 #include <e-util/e-sorter.h>
 #include <e-util/e-source-combo-box.h>
@@ -194,6 +201,8 @@
 #include <e-util/e-source-selector-dialog.h>
 #include <e-util/e-source-selector.h>
 #include <e-util/e-source-util.h>
+#include <e-util/e-spell-checker.h>
+#include <e-util/e-spell-dictionary.h>
 #include <e-util/e-spell-entry.h>
 #include <e-util/e-spell-text-view.h>
 #include <e-util/e-spinner.h>
@@ -248,8 +257,10 @@
 #include <e-util/e-url-entry.h>
 #include <e-util/e-util-enums.h>
 #include <e-util/e-util-enumtypes.h>
+#ifndef E_UTIL_INCLUDE_WITHOUT_WEBKIT
 #include <e-util/e-web-view-preview.h>
 #include <e-util/e-web-view.h>
+#endif
 #include <e-util/e-widget-undo.h>
 #include <e-util/e-xml-utils.h>
 #include <e-util/ea-cell-table.h>
diff --git a/e-util/e-web-view-preview.c b/e-util/e-web-view-preview.c
index 4ad30a0..13fd32d 100644
--- a/e-util/e-web-view-preview.c
+++ b/e-util/e-web-view-preview.c
@@ -173,7 +173,7 @@ in_scrolled_window (GtkWidget *widget)
 static void
 e_web_view_preview_init (EWebViewPreview *preview)
 {
-       GtkWidget *tree_view_sw, *web_view_sw;
+       GtkWidget *tree_view_sw, *web_view;
 
        preview->priv = E_WEB_VIEW_PREVIEW_GET_PRIVATE (preview);
        preview->priv->escape_values = TRUE;
@@ -181,13 +181,13 @@ e_web_view_preview_init (EWebViewPreview *preview)
        gtk_orientable_set_orientation (GTK_ORIENTABLE (preview), GTK_ORIENTATION_VERTICAL);
 
        tree_view_sw = in_scrolled_window (gtk_tree_view_new ());
-       web_view_sw = in_scrolled_window (e_web_view_new ());
+       web_view = e_web_view_new ();
 
        gtk_widget_hide (tree_view_sw);
-       gtk_widget_show (web_view_sw);
+       gtk_widget_show (web_view);
 
        gtk_paned_pack1 (GTK_PANED (preview), tree_view_sw, FALSE, TRUE);
-       gtk_paned_pack2 (GTK_PANED (preview), web_view_sw, TRUE, TRUE);
+       gtk_paned_pack2 (GTK_PANED (preview), web_view, TRUE, TRUE);
 
        /* rawly 3 lines of a text plus a little bit more */
        if (gtk_paned_get_position (GTK_PANED (preview)) < 85)
@@ -213,7 +213,7 @@ e_web_view_preview_get_preview (EWebViewPreview *preview)
 {
        g_return_val_if_fail (E_IS_WEB_VIEW_PREVIEW (preview), NULL);
 
-       return gtk_bin_get_child (GTK_BIN (gtk_paned_get_child2 (GTK_PANED (preview))));
+       return gtk_paned_get_child2 (GTK_PANED (preview));
 }
 
 void
@@ -225,7 +225,7 @@ e_web_view_preview_set_preview (EWebViewPreview *preview,
        g_return_if_fail (E_IS_WEB_VIEW_PREVIEW (preview));
        g_return_if_fail (GTK_IS_WIDGET (preview_widget));
 
-       old_child = gtk_bin_get_child (GTK_BIN (gtk_paned_get_child2 (GTK_PANED (preview))));
+       old_child = gtk_paned_get_child2 (GTK_PANED (preview));
        if (old_child) {
                g_return_if_fail (old_child != preview_widget);
                gtk_widget_destroy (old_child);
diff --git a/e-util/e-web-view.c b/e-util/e-web-view.c
index 42af88f..011ac27 100644
--- a/e-util/e-web-view.c
+++ b/e-util/e-web-view.c
@@ -40,18 +40,19 @@
 #include "e-selectable.h"
 #include "e-stock-request.h"
 
+#include <web-extensions/e-web-extension-names.h>
+
 #define E_WEB_VIEW_GET_PRIVATE(obj) \
        (G_TYPE_INSTANCE_GET_PRIVATE \
        ((obj), E_TYPE_WEB_VIEW, EWebViewPrivate))
 
-typedef enum {
-       E_WEB_VIEW_ZOOM_HACK_STATE_NONE,
-       E_WEB_VIEW_ZOOM_HACK_STATE_ZOOMED_IN,
-       E_WEB_VIEW_ZOOM_HACK_STATE_ZOOMED_OUT
-} EWebViewZoomHackState;
-
 typedef struct _AsyncContext AsyncContext;
 
+typedef struct _ElementClickedData {
+       EWebViewElementClickedFunc callback;
+       gpointer user_data;
+} ElementClickedData;
+
 struct _EWebViewPrivate {
        GtkUIManager *ui_manager;
        gchar *selected_uri;
@@ -78,17 +79,27 @@ struct _EWebViewPrivate {
 
        GHashTable *old_settings;
 
-       /* To workaround webkit bug:
-        * https://bugs.webkit.org/show_bug.cgi?id=89553 */
-       EWebViewZoomHackState zoom_hack_state;
+       GDBusProxy *web_extension;
+       guint web_extension_watch_name_id;
+
+       WebKitFindController *find_controller;
+       gulong found_text_handler_id;
+       gulong failed_to_find_text_handler_id;
 
        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 {
        EActivity *activity;
        GFile *destination;
        GInputStream *input_stream;
+       EContentRequest *content_request;
+       gchar *uri;
 };
 
 enum {
@@ -99,6 +110,7 @@ enum {
        PROP_DISABLE_PRINTING,
        PROP_DISABLE_SAVE_TO_DISK,
        PROP_OPEN_PROXY,
+       PROP_PASTE_TARGET_LIST,
        PROP_PRINT_PROXY,
        PROP_SAVE_AS_PROXY,
        PROP_SELECTED_URI
@@ -111,11 +123,11 @@ enum {
        STOP_LOADING,
        UPDATE_ACTIONS,
        PROCESS_MAILTO,
+       URI_REQUESTED,
        LAST_SIGNAL
 };
 
 static guint signals[LAST_SIGNAL];
-static GOnce disable_webkit_3rd_party_plugins_once = G_ONCE_INIT;
 
 static const gchar *ui =
 "<ui>"
@@ -161,11 +173,18 @@ G_DEFINE_TYPE_WITH_CODE (
                e_web_view_selectable_init))
 
 static void
-async_context_free (AsyncContext *async_context)
+async_context_free (gpointer ptr)
 {
+       AsyncContext *async_context = ptr;
+
+       if (!async_context)
+               return;
+
        g_clear_object (&async_context->activity);
        g_clear_object (&async_context->destination);
        g_clear_object (&async_context->input_stream);
+       g_clear_object (&async_context->content_request);
+       g_free (async_context->uri);
 
        g_slice_free (AsyncContext, async_context);
 }
@@ -393,38 +412,6 @@ static GtkActionEntry standard_entries[] = {
 };
 
 static void
-web_view_init_web_settings (WebKitWebView *web_view)
-{
-       WebKitWebSettings *web_settings;
-
-       web_settings = webkit_web_settings_new ();
-
-       g_object_set (
-               G_OBJECT (web_settings),
-               "default-encoding", "UTF-8",
-               "enable-dns-prefetching", FALSE,
-               "enable-frame-flattening", TRUE,
-               "enable-java-applet", FALSE,
-               "enable-html5-database", FALSE,
-               "enable-html5-local-storage", FALSE,
-               "enable-offline-web-application-cache", FALSE,
-               "enable-site-specific-quirks", TRUE,
-               "enable-scripts", FALSE,
-               "respect-image-orientation", TRUE,
-               NULL);
-
-       e_binding_bind_property (
-               web_settings, "enable-caret-browsing",
-               web_view, "caret-mode",
-               G_BINDING_BIDIRECTIONAL |
-               G_BINDING_SYNC_CREATE);
-
-       webkit_web_view_set_settings (web_view, web_settings);
-
-       g_object_unref (web_settings);
-}
-
-static void
 web_view_menu_item_select_cb (EWebView *web_view,
                               GtkWidget *widget)
 {
@@ -443,20 +430,51 @@ web_view_menu_item_select_cb (EWebView *web_view,
 }
 
 static void
+webkit_find_controller_found_text_cb (WebKitFindController *find_controller,
+                                      guint match_count,
+                                      EWebView *web_view)
+{
+}
+
+static void
+webkit_find_controller_failed_to_found_text_cb (WebKitFindController *find_controller,
+                                                EWebView *web_view)
+{
+}
+
+static void
+web_view_set_find_controller (EWebView *web_view)
+{
+       WebKitFindController *find_controller;
+
+       find_controller =
+               webkit_web_view_get_find_controller (WEBKIT_WEB_VIEW (web_view));
+
+       web_view->priv->found_text_handler_id = g_signal_connect (
+               find_controller, "found-text",
+               G_CALLBACK (webkit_find_controller_found_text_cb), web_view);
+
+       web_view->priv->failed_to_find_text_handler_id = g_signal_connect (
+               find_controller, "failed-to-find-text",
+               G_CALLBACK (webkit_find_controller_failed_to_found_text_cb), web_view);
+
+       web_view->priv->find_controller = find_controller;
+}
+
+static void
 web_view_update_document_highlights (EWebView *web_view)
 {
-       WebKitWebView *webkit_web_view;
        GList *head, *link;
 
-       webkit_web_view = WEBKIT_WEB_VIEW (web_view);
-
        head = g_queue_peek_head_link (&web_view->priv->highlights);
 
-       for (link = head; link != NULL; link = g_list_next (link))
-               webkit_web_view_mark_text_matches (
-                       webkit_web_view, link->data, FALSE, 0);
-
-       webkit_web_view_set_highlight_text_matches (webkit_web_view, TRUE);
+       for (link = head; link != NULL; link = g_list_next (link)) {
+               webkit_find_controller_search (
+                       web_view->priv->find_controller,
+                       link->data,
+                       WEBKIT_FIND_OPTIONS_NONE,
+                       G_MAXUINT);
+       }
 }
 
 static void
@@ -484,9 +502,10 @@ web_view_connect_proxy_cb (EWebView *web_view,
 
 static gboolean
 web_view_context_menu_cb (WebKitWebView *webkit_web_view,
-                          GtkWidget *default_menu,
+                          WebKitContextMenu *context_menu,
+                          GdkEvent *event,
                           WebKitHitTestResult *hit_test_result,
-                          gboolean triggered_with_keyboard)
+                          gpointer user_data)
 {
        WebKitHitTestResultContext context;
        EWebView *web_view;
@@ -501,7 +520,7 @@ web_view_context_menu_cb (WebKitWebView *webkit_web_view,
        if (hit_test_result == NULL)
                return FALSE;
 
-       g_object_get (hit_test_result, "context", &context, NULL);
+       context = webkit_hit_test_result_get_context (hit_test_result);
 
        if (context & WEBKIT_HIT_TEST_RESULT_CONTEXT_IMAGE) {
                gchar *image_uri = NULL;
@@ -527,30 +546,17 @@ web_view_context_menu_cb (WebKitWebView *webkit_web_view,
        return event_handled;
 }
 
-static GtkWidget *
-web_view_create_plugin_widget_cb (EWebView *web_view,
-                                  const gchar *mime_type,
-                                  const gchar *uri,
-                                  GHashTable *param)
-{
-       EWebViewClass *class;
-
-       /* XXX WebKitWebView does not provide a class method for
-        *     this signal, so we do so we can override the default
-        *     behavior from subclasses for special URI types. */
-
-       class = E_WEB_VIEW_GET_CLASS (web_view);
-       g_return_val_if_fail (class->create_plugin_widget != NULL, NULL);
-
-       return class->create_plugin_widget (web_view, mime_type, uri, param);
-}
-
 static void
-web_view_hovering_over_link_cb (EWebView *web_view,
-                                const gchar *title,
-                                const gchar *uri)
+web_view_mouse_target_changed_cb (EWebView *web_view,
+                                  WebKitHitTestResult *hit_test_result,
+                                  guint modifiers,
+                                  gpointer user_data)
 {
        EWebViewClass *class;
+       const gchar *title, *uri;
+
+       title = webkit_hit_test_result_get_link_title (hit_test_result);
+       uri = webkit_hit_test_result_get_link_uri (hit_test_result);
 
        web_view->priv->has_hover_link = uri && *uri;
 
@@ -565,55 +571,60 @@ web_view_hovering_over_link_cb (EWebView *web_view,
 }
 
 static gboolean
-web_view_navigation_policy_decision_requested_cb (EWebView *web_view,
-                                                  WebKitWebFrame *frame,
-                                                  WebKitNetworkRequest *request,
-                                                  WebKitWebNavigationAction *navigation_action,
-                                                  WebKitWebPolicyDecision *policy_decision)
+web_view_decide_policy_cb (EWebView *web_view,
+                           WebKitPolicyDecision *decision,
+                           WebKitPolicyDecisionType type)
 {
        EWebViewClass *class;
-       WebKitWebNavigationReason reason;
-       const gchar *uri, *frame_uri;
+       WebKitNavigationPolicyDecision *navigation_decision;
+       WebKitNavigationAction *navigation_action;
+       WebKitNavigationType navigation_type;
+       WebKitURIRequest *request;
+       const gchar *uri, *view_uri;
+
+       if (type != WEBKIT_POLICY_DECISION_TYPE_NAVIGATION_ACTION &&
+           type != WEBKIT_POLICY_DECISION_TYPE_NEW_WINDOW_ACTION)
+               return FALSE;
+
+       navigation_decision = WEBKIT_NAVIGATION_POLICY_DECISION (decision);
+       navigation_action = webkit_navigation_policy_decision_get_navigation_action (navigation_decision);
+       navigation_type = webkit_navigation_action_get_navigation_type (navigation_action);
 
-       reason = webkit_web_navigation_action_get_reason (navigation_action);
-       if (reason != WEBKIT_WEB_NAVIGATION_REASON_LINK_CLICKED)
+       if (navigation_type != WEBKIT_NAVIGATION_TYPE_LINK_CLICKED)
                return FALSE;
 
-       uri = webkit_network_request_get_uri (request);
-       frame_uri = webkit_web_frame_get_uri (frame);
+       request = webkit_navigation_action_get_request (navigation_action);
+       uri = webkit_uri_request_get_uri (request);
+       view_uri = webkit_web_view_get_uri (WEBKIT_WEB_VIEW (web_view));
 
        /* Allow navigation through fragments in page */
-       if (uri && *uri && frame_uri && *frame_uri) {
-               SoupURI *uri_link, *uri_frame;
+       if (uri && *uri && view_uri && *view_uri) {
+               SoupURI *uri_link, *uri_view;
 
                uri_link = soup_uri_new (uri);
-               uri_frame = soup_uri_new (frame_uri);
-               if (uri_link && uri_frame) {
+               uri_view = soup_uri_new (view_uri);
+               if (uri_link && uri_view) {
                        const gchar *tmp1, *tmp2;
 
                        tmp1 = soup_uri_get_scheme (uri_link);
-                       tmp2 = soup_uri_get_scheme (uri_frame);
+                       tmp2 = soup_uri_get_scheme (uri_view);
 
                        /* The scheme on both URIs should be the same */
-                       if (tmp1 && tmp2) {
-                               if (g_ascii_strcasecmp (tmp1, tmp2) != 0)
-                                       goto free_uris;
-                       }
+                       if (tmp1 && tmp2 && g_ascii_strcasecmp (tmp1, tmp2) != 0)
+                               goto free_uris;
 
                        tmp1 = soup_uri_get_host (uri_link);
-                       tmp2 = soup_uri_get_host (uri_frame);
+                       tmp2 = soup_uri_get_host (uri_view);
 
                        /* The host on both URIs should be the same */
-                       if (tmp1 && tmp2) {
-                               if (g_ascii_strcasecmp (tmp1, tmp2) != 0)
-                                       goto free_uris;
-                       }
+                       if (tmp1 && tmp2 && g_ascii_strcasecmp (tmp1, tmp2) != 0)
+                               goto free_uris;
 
                        /* URI from link should have fragment set - could be empty */
                        if (soup_uri_get_fragment (uri_link)) {
                                soup_uri_free (uri_link);
-                               soup_uri_free (uri_frame);
-                               webkit_web_policy_decision_use (policy_decision);
+                               soup_uri_free (uri_view);
+                               webkit_policy_decision_use (decision);
                                return TRUE;
                        }
                }
@@ -621,8 +632,8 @@ web_view_navigation_policy_decision_requested_cb (EWebView *web_view,
  free_uris:
                if (uri_link)
                        soup_uri_free (uri_link);
-               if (uri_frame)
-                       soup_uri_free (uri_frame);
+               if (uri_view)
+                       soup_uri_free (uri_view);
        }
 
        /* XXX WebKitWebView does not provide a class method for
@@ -632,7 +643,7 @@ web_view_navigation_policy_decision_requested_cb (EWebView *web_view,
        class = E_WEB_VIEW_GET_CLASS (web_view);
        g_return_val_if_fail (class->link_clicked != NULL, FALSE);
 
-       webkit_web_policy_decision_ignore (policy_decision);
+       webkit_policy_decision_ignore (decision);
 
        class->link_clicked (web_view, uri);
 
@@ -665,7 +676,7 @@ style_updated_cb (EWebView *web_view)
 
        e_web_view_add_css_rule_into_style_sheet (
                web_view,
-               "-e-web-view-css-sheet",
+               "-e-web-view-style-sheet",
                ".-e-web-view-background-color",
                style);
 
@@ -684,7 +695,7 @@ style_updated_cb (EWebView *web_view)
 
        e_web_view_add_css_rule_into_style_sheet (
                web_view,
-               "-e-web-view-css-sheet",
+               "-e-web-view-style-sheet",
                ".-e-web-view-text-color",
                style);
 
@@ -693,60 +704,63 @@ style_updated_cb (EWebView *web_view)
 }
 
 static void
-web_view_load_status_changed_cb (WebKitWebView *webkit_web_view,
-                                 GParamSpec *pspec,
-                                 gpointer user_data)
+web_view_load_changed_cb (WebKitWebView *webkit_web_view,
+                          WebKitLoadEvent load_event,
+                          gpointer user_data)
 {
-       WebKitLoadStatus status;
        EWebView *web_view;
 
        web_view = E_WEB_VIEW (webkit_web_view);
 
-       status = webkit_web_view_get_load_status (webkit_web_view);
-
-       if (web_view->priv->zoom_hack_state == E_WEB_VIEW_ZOOM_HACK_STATE_NONE &&
-           status == WEBKIT_LOAD_COMMITTED) {
-               if (webkit_web_view_get_zoom_level (WEBKIT_WEB_VIEW (web_view)) > 0.9999) {
-                       e_web_view_zoom_out (web_view);
-                       web_view->priv->zoom_hack_state = E_WEB_VIEW_ZOOM_HACK_STATE_ZOOMED_OUT;
-               } else {
-                       e_web_view_zoom_in (web_view);
-                       web_view->priv->zoom_hack_state = E_WEB_VIEW_ZOOM_HACK_STATE_ZOOMED_IN;
-               }
-       } else if (web_view->priv->zoom_hack_state != E_WEB_VIEW_ZOOM_HACK_STATE_NONE &&
-                  status == WEBKIT_LOAD_FAILED) {
-               if (web_view->priv->zoom_hack_state == E_WEB_VIEW_ZOOM_HACK_STATE_ZOOMED_IN)
-                       e_web_view_zoom_out (web_view);
-               else
-                       e_web_view_zoom_in (web_view);
-
-               web_view->priv->zoom_hack_state = E_WEB_VIEW_ZOOM_HACK_STATE_NONE;
-       }
+       if (load_event == WEBKIT_LOAD_STARTED)
+               g_hash_table_remove_all (web_view->priv->element_clicked_cbs);
 
-       if (status != WEBKIT_LOAD_FINISHED)
+       if (load_event != WEBKIT_LOAD_FINISHED)
                return;
 
        style_updated_cb (web_view);
 
        web_view_update_document_highlights (web_view);
+}
 
-       if (web_view->priv->zoom_hack_state == E_WEB_VIEW_ZOOM_HACK_STATE_NONE) {
-               /* This may not happen, but just in case keep it here. */
-               if (webkit_web_view_get_zoom_level (WEBKIT_WEB_VIEW (web_view)) > 0.9999) {
-                       e_web_view_zoom_out (web_view);
-                       e_web_view_zoom_in (web_view);
-               } else {
-                       e_web_view_zoom_in (web_view);
-                       e_web_view_zoom_out (web_view);
-               }
-       } else {
-               if (web_view->priv->zoom_hack_state == E_WEB_VIEW_ZOOM_HACK_STATE_ZOOMED_IN)
-                       e_web_view_zoom_out (web_view);
-               else
-                       e_web_view_zoom_in (web_view);
+static GObjectConstructParam*
+find_property (guint n_properties,
+               GObjectConstructParam* properties,
+               GParamSpec* param_spec)
+{
+       while (n_properties--) {
+               if (properties->pspec == param_spec)
+                       return properties;
+               properties++;
+       }
+
+       return NULL;
+}
 
-               web_view->priv->zoom_hack_state = E_WEB_VIEW_ZOOM_HACK_STATE_NONE;
+static GObject*
+web_view_constructor (GType type,
+                      guint n_construct_properties,
+                      GObjectConstructParam *construct_properties)
+{
+       GObjectClass* object_class;
+       GParamSpec* param_spec;
+       GObjectConstructParam *param = NULL;
+
+       object_class = G_OBJECT_CLASS (g_type_class_ref(type));
+       g_return_val_if_fail (object_class != NULL, NULL);
+
+       if (construct_properties && n_construct_properties != 0) {
+               param_spec = g_object_class_find_property (object_class, "settings");
+               if ((param = find_property (n_construct_properties, construct_properties, param_spec)))
+                       g_value_take_object (param->value, e_web_view_get_default_webkit_settings ());
+               param_spec = g_object_class_find_property(object_class, "user-content-manager");
+               if ((param = find_property (n_construct_properties, construct_properties, param_spec)))
+                       g_value_take_object (param->value, webkit_user_content_manager_new ());
        }
+
+       g_type_class_unref (object_class);
+
+       return G_OBJECT_CLASS (e_web_view_parent_class)->constructor(type, n_construct_properties, 
construct_properties);
 }
 
 static void
@@ -762,6 +776,11 @@ web_view_set_property (GObject *object,
                                g_value_get_boolean (value));
                        return;
 
+               case PROP_COPY_TARGET_LIST:
+                       /* This is a fake property. */
+                       g_warning ("%s: EWebView::copy-target-list not used", G_STRFUNC);
+                       return;
+
                case PROP_CURSOR_IMAGE_SRC:
                        e_web_view_set_cursor_image_src (
                                E_WEB_VIEW (object),
@@ -786,6 +805,11 @@ web_view_set_property (GObject *object,
                                g_value_get_object (value));
                        return;
 
+               case PROP_PASTE_TARGET_LIST:
+                       /* This is a fake property. */
+                       g_warning ("%s: EWebView::paste-target-list not used", G_STRFUNC);
+                       return;
+
                case PROP_PRINT_PROXY:
                        e_web_view_set_print_proxy (
                                E_WEB_VIEW (object),
@@ -820,6 +844,11 @@ web_view_get_property (GObject *object,
                                E_WEB_VIEW (object)));
                        return;
 
+               case PROP_COPY_TARGET_LIST:
+                       /* This is a fake property. */
+                       g_value_set_boxed (value, NULL);
+                       return;
+
                case PROP_CURSOR_IMAGE_SRC:
                        g_value_set_string (
                                value, e_web_view_get_cursor_image_src (
@@ -844,6 +873,11 @@ web_view_get_property (GObject *object,
                                E_WEB_VIEW (object)));
                        return;
 
+               case PROP_PASTE_TARGET_LIST:
+                       /* This is a fake property. */
+                       g_value_set_boxed (value, NULL);
+                       return;
+
                case PROP_PRINT_PROXY:
                        g_value_set_object (
                                value, e_web_view_get_print_proxy (
@@ -895,12 +929,44 @@ web_view_dispose (GObject *object)
                priv->antialiasing_changed_handler_id = 0;
        }
 
+       if (priv->web_extension_watch_name_id > 0) {
+               g_bus_unwatch_name (priv->web_extension_watch_name_id);
+               priv->web_extension_watch_name_id = 0;
+       }
+
+       if (priv->found_text_handler_id > 0) {
+               g_signal_handler_disconnect (
+                       priv->find_controller,
+                       priv->found_text_handler_id);
+               priv->found_text_handler_id = 0;
+       }
+
+       if (priv->failed_to_find_text_handler_id > 0) {
+               g_signal_handler_disconnect (
+                       priv->find_controller,
+                       priv->failed_to_find_text_handler_id);
+               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;
+
        g_clear_object (&priv->ui_manager);
        g_clear_object (&priv->open_proxy);
        g_clear_object (&priv->print_proxy);
        g_clear_object (&priv->save_as_proxy);
        g_clear_object (&priv->aliasing_settings);
        g_clear_object (&priv->font_settings);
+       g_clear_object (&priv->web_extension);
 
        /* Chain up to parent's dispose() method. */
        G_OBJECT_CLASS (e_web_view_parent_class)->dispose (object);
@@ -924,13 +990,149 @@ 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);
 }
 
+
+static void
+web_view_uri_request_done_cb (GObject *source_object,
+                             GAsyncResult *result,
+                             gpointer user_data)
+{
+       WebKitURISchemeRequest *request = user_data;
+       GInputStream *stream = NULL;
+       gint64 stream_length = -1;
+       gchar *mime_type = NULL;
+       GError *error = NULL;
+
+       g_return_if_fail (E_IS_CONTENT_REQUEST (source_object));
+       g_return_if_fail (WEBKIT_IS_URI_SCHEME_REQUEST (request));
+
+       if (!e_content_request_process_finish (E_CONTENT_REQUEST (source_object),
+               result, &stream, &stream_length, &mime_type, &error)) {
+               webkit_uri_scheme_request_finish_error (request, error);
+       } else {
+               webkit_uri_scheme_request_finish (request, stream, stream_length, mime_type);
+
+               g_clear_object (&stream);
+               g_free (mime_type);
+       }
+
+       g_object_unref (request);
+}
+
+static void
+web_view_process_uri_request_cb (WebKitURISchemeRequest *request,
+                                gpointer user_data)
+{
+       EContentRequest *content_request = user_data;
+       const gchar *uri;
+       gchar *redirect_to_uri = NULL;
+       GObject *requester;
+
+       g_return_if_fail (WEBKIT_IS_URI_SCHEME_REQUEST (request));
+       g_return_if_fail (E_IS_CONTENT_REQUEST (content_request));
+
+       uri = webkit_uri_scheme_request_get_uri (request);
+       requester = G_OBJECT (webkit_uri_scheme_request_get_web_view (request));
+
+       g_return_if_fail (e_content_request_can_process_uri (content_request, uri));
+
+       if (E_IS_WEB_VIEW (requester)) {
+               /* Expects an empty string to abandon the request,
+                  or NULL to keep the passed-in uri,
+                  or a new uri to load instead. */
+               g_signal_emit (requester, signals[URI_REQUESTED], 0, uri, &redirect_to_uri);
+
+               if (redirect_to_uri && *redirect_to_uri) {
+                       uri = redirect_to_uri;
+               } else if (redirect_to_uri) {
+                       GError *error;
+
+                       g_free (redirect_to_uri);
+
+                       error = g_error_new_literal (G_IO_ERROR, G_IO_ERROR_CANCELLED, "Cancelled");
+
+                       webkit_uri_scheme_request_finish_error (request, error);
+                       return;
+               }
+       }
+
+       e_content_request_process (content_request, uri, requester, NULL,
+               web_view_uri_request_done_cb, g_object_ref (request));
+
+       g_free (redirect_to_uri);
+}
+
+/* 'scheme' is like "file", not "file:" */
+void
+e_web_view_register_content_request_for_scheme (EWebView *web_view,
+                                               const gchar *scheme,
+                                               EContentRequest *content_request)
+{
+       WebKitWebContext *web_context;
+
+       g_return_if_fail (E_IS_WEB_VIEW (web_view));
+       g_return_if_fail (E_IS_CONTENT_REQUEST (content_request));
+       g_return_if_fail (scheme != NULL);
+
+       web_context = webkit_web_view_get_context (WEBKIT_WEB_VIEW (web_view));
+
+       webkit_web_context_register_uri_scheme (web_context, scheme, web_view_process_uri_request_cb,
+               g_object_ref (content_request), g_object_unref);
+
+       if (!g_slist_find (web_view->priv->content_requests, content_request)) {
+               web_view->priv->content_requests = g_slist_prepend (
+                       web_view->priv->content_requests,
+                       g_object_ref (content_request));
+       }
+}
+
+static void
+web_view_initialize (WebKitWebView *web_view)
+{
+       WebKitWebContext *web_context;
+       EContentRequest *content_request;
+       const gchar *id = "org.gnome.settings-daemon.plugins.xsettings";
+       GSettings *settings = NULL, *font_settings;
+       GSettingsSchema *settings_schema;
+
+       web_context = webkit_web_view_get_context (web_view);
+
+       webkit_web_context_set_cache_model (web_context, WEBKIT_CACHE_MODEL_DOCUMENT_VIEWER);
+
+       content_request = e_file_request_new ();
+       e_web_view_register_content_request_for_scheme (E_WEB_VIEW (web_view), "evo-file", content_request);
+       g_object_unref (content_request);
+
+       content_request = e_stock_request_new ();
+       e_web_view_register_content_request_for_scheme (E_WEB_VIEW (web_view), "gtk-stock", content_request);
+       g_object_unref (content_request);
+
+       /* Optional schema */
+       settings_schema = g_settings_schema_source_lookup (
+               g_settings_schema_source_get_default (), id, FALSE);
+
+       if (settings_schema)
+               settings = e_util_ref_settings (id);
+
+       font_settings = e_util_ref_settings ("org.gnome.desktop.interface");
+       e_web_view_update_fonts_settings (
+               font_settings, settings, NULL, NULL, GTK_WIDGET (web_view));
+
+       g_object_unref (font_settings);
+       if (settings)
+               g_object_unref (settings);
+}
+
+
 static void
 web_view_constructed (GObject *object)
 {
+       WebKitSettings *web_settings;
 #ifndef G_OS_WIN32
        GSettings *settings;
 
@@ -953,6 +1155,23 @@ web_view_constructed (GObject *object)
 
        /* Chain up to parent's constructed() method. */
        G_OBJECT_CLASS (e_web_view_parent_class)->constructed (object);
+
+       web_settings = webkit_web_view_get_settings (WEBKIT_WEB_VIEW (object));
+
+       g_object_set (
+               G_OBJECT (web_settings),
+               "default-charset", "UTF-8",
+               NULL);
+
+       e_binding_bind_property (
+               web_settings, "enable-caret-browsing",
+               E_WEB_VIEW (object), "caret-mode",
+               G_BINDING_BIDIRECTIONAL |
+               G_BINDING_SYNC_CREATE);
+
+       web_view_initialize (WEBKIT_WEB_VIEW (object));
+
+       web_view_set_find_controller (E_WEB_VIEW (object));
 }
 
 static gboolean
@@ -992,7 +1211,8 @@ web_view_scroll_event (GtkWidget *widget,
                }
        }
 
-       return FALSE;
+       return GTK_WIDGET_CLASS (e_web_view_parent_class)->
+               scroll_event (widget, event);
 }
 
 static gboolean
@@ -1146,9 +1366,8 @@ web_view_load_string (EWebView *web_view,
        if (string == NULL)
                string = "";
 
-       webkit_web_view_load_string (
-               WEBKIT_WEB_VIEW (web_view),
-               string, "text/html", "UTF-8", "evo-file:///");
+       webkit_web_view_load_html (
+               WEBKIT_WEB_VIEW (web_view), string, "evo-file:///");
 }
 
 static void
@@ -1162,13 +1381,6 @@ web_view_load_uri (EWebView *web_view,
 }
 
 static gchar *
-web_view_redirect_uri (EWebView *web_view,
-                       const gchar *uri)
-{
-       return g_strdup (uri);
-}
-
-static gchar *
 web_view_suggest_filename (EWebView *web_view,
                            const gchar *uri)
 {
@@ -1204,8 +1416,164 @@ web_view_stop_loading (EWebView *web_view)
 }
 
 static void
-web_view_update_actions (EWebView *web_view)
+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)
+{
+       GError *error = NULL;
+
+       web_view->priv->web_extension = g_dbus_proxy_new_finish (result, &error);
+       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);
+       }
+}
+
+static void
+web_extension_appeared_cb (GDBusConnection *connection,
+                           const gchar *name,
+                           const gchar *name_owner,
+                           EWebView *web_view)
+{
+       g_dbus_proxy_new (
+               connection,
+               G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START |
+               G_DBUS_PROXY_FLAGS_DO_NOT_CONNECT_SIGNALS,
+               NULL,
+               name,
+               E_WEB_EXTENSION_OBJECT_PATH,
+               E_WEB_EXTENSION_INTERFACE,
+               NULL,
+               (GAsyncReadyCallback) web_extension_proxy_created_cb,
+               web_view);
+}
+
+static void
+web_extension_vanished_cb (GDBusConnection *connection,
+                           const gchar *name,
+                           EWebView *web_view)
+{
+       g_clear_object (&web_view->priv->web_extension);
+}
+
+static void
+web_view_watch_web_extension (EWebView *web_view)
+{
+       web_view->priv->web_extension_watch_name_id =
+               g_bus_watch_name (
+                       G_BUS_TYPE_SESSION,
+                       E_WEB_EXTENSION_SERVICE_NAME,
+                       G_BUS_NAME_WATCHER_FLAGS_NONE,
+                       (GBusNameAppearedCallback) web_extension_appeared_cb,
+                       (GBusNameVanishedCallback) web_extension_vanished_cb,
+                       web_view,
+                       NULL);
+}
+
+GDBusProxy *
+e_web_view_get_web_extension_proxy (EWebView *web_view)
+{
+       g_return_val_if_fail (E_IS_WEB_VIEW (web_view), NULL);
+
+       return web_view->priv->web_extension;
+}
+
+static void
+web_view_update_actions_cb (WebKitWebView *webkit_web_view,
+                            GAsyncResult *result,
+                            gpointer user_data)
+{
+       EWebView *web_view;
        GtkActionGroup *action_group;
        gboolean can_copy;
        gboolean scheme_is_http = FALSE;
@@ -1216,8 +1584,11 @@ web_view_update_actions (EWebView *web_view)
        const gchar *group_name;
        const gchar *uri;
 
+       web_view = E_WEB_VIEW (webkit_web_view);
+
        uri = e_web_view_get_selected_uri (web_view);
-       can_copy = webkit_web_view_can_copy_clipboard (WEBKIT_WEB_VIEW (web_view));
+       can_copy = webkit_web_view_can_execute_editing_command_finish (
+               webkit_web_view, result, NULL);
        cursor_image_src = e_web_view_get_cursor_image_src (web_view);
 
        /* Parse the URI early so we know if the actions will work. */
@@ -1301,6 +1672,17 @@ web_view_update_actions (EWebView *web_view)
 }
 
 static void
+web_view_update_actions (EWebView *web_view)
+{
+       webkit_web_view_can_execute_editing_command (
+               WEBKIT_WEB_VIEW (web_view),
+               WEBKIT_EDITING_COMMAND_COPY,
+               NULL, /* cancellable */
+               (GAsyncReadyCallback) web_view_update_actions_cb,
+               NULL);
+}
+
+static void
 web_view_submit_alert (EAlertSink *alert_sink,
                        EAlert *alert)
 {
@@ -1397,6 +1779,19 @@ web_view_submit_alert (EAlertSink *alert_sink,
 }
 
 static void
+web_view_can_execute_editing_command_cb (WebKitWebView *webkit_web_view,
+                                         GAsyncResult *result,
+                                         GtkAction *action)
+{
+       gboolean can_do_command;
+
+       can_do_command = webkit_web_view_can_execute_editing_command_finish (
+               webkit_web_view, result, NULL);
+
+       gtk_action_set_sensitive (action, can_do_command);
+}
+
+static void
 web_view_selectable_update_actions (ESelectable *selectable,
                                     EFocusTracker *focus_tracker,
                                     GdkAtom *clipboard_targets,
@@ -1410,21 +1805,33 @@ web_view_selectable_update_actions (ESelectable *selectable,
        web_view = WEBKIT_WEB_VIEW (selectable);
 
        action = e_focus_tracker_get_cut_clipboard_action (focus_tracker);
-       sensitive = webkit_web_view_can_cut_clipboard (web_view);
+       webkit_web_view_can_execute_editing_command (
+               WEBKIT_WEB_VIEW (web_view),
+               WEBKIT_EDITING_COMMAND_CUT,
+               NULL, /* cancellable */
+               (GAsyncReadyCallback) web_view_can_execute_editing_command_cb,
+               action);
        tooltip = _("Cut the selection");
-       gtk_action_set_sensitive (action, sensitive);
        gtk_action_set_tooltip (action, tooltip);
 
        action = e_focus_tracker_get_copy_clipboard_action (focus_tracker);
-       sensitive = webkit_web_view_can_copy_clipboard (web_view);
+       webkit_web_view_can_execute_editing_command (
+               WEBKIT_WEB_VIEW (web_view),
+               WEBKIT_EDITING_COMMAND_COPY,
+               NULL, /* cancellable */
+               (GAsyncReadyCallback) web_view_can_execute_editing_command_cb,
+               action);
        tooltip = _("Copy the selection");
-       gtk_action_set_sensitive (action, sensitive);
        gtk_action_set_tooltip (action, tooltip);
 
        action = e_focus_tracker_get_paste_clipboard_action (focus_tracker);
-       sensitive = webkit_web_view_can_paste_clipboard (web_view);
+       webkit_web_view_can_execute_editing_command (
+               WEBKIT_WEB_VIEW (web_view),
+               WEBKIT_EDITING_COMMAND_PASTE,
+               NULL, /* cancellable */
+               (GAsyncReadyCallback) web_view_can_execute_editing_command_cb,
+               action);
        tooltip = _("Paste the clipboard");
-       gtk_action_set_sensitive (action, sensitive);
        gtk_action_set_tooltip (action, tooltip);
 
        action = e_focus_tracker_get_select_all_action (focus_tracker);
@@ -1480,30 +1887,6 @@ e_web_view_test_change_and_update_fonts_cb (EWebView *web_view,
        }
 }
 
-static gpointer
-web_view_disable_webkit_3rd_party_plugins (gpointer unused)
-{
-       WebKitWebPluginDatabase *database;
-       GSList *installed_plugins, *iterator;
-
-       database = webkit_get_web_plugin_database ();
-
-       if (!database)
-               return NULL;
-
-       installed_plugins = webkit_web_plugin_database_get_plugins (database);
-
-       if (!installed_plugins)
-               return NULL;
-
-       for (iterator = installed_plugins; iterator; iterator = iterator->next)
-               webkit_web_plugin_set_enabled (iterator->data, FALSE);
-
-       webkit_web_plugin_database_plugins_list_free (installed_plugins);
-
-       return NULL;
-}
-
 static void
 web_view_toplevel_event_after_cb (GtkWidget *widget,
                                  GdkEvent *event,
@@ -1564,6 +1947,7 @@ e_web_view_class_init (EWebViewClass *class)
        g_type_class_add_private (class, sizeof (EWebViewPrivate));
 
        object_class = G_OBJECT_CLASS (class);
+       object_class->constructor = web_view_constructor;
        object_class->set_property = web_view_set_property;
        object_class->get_property = web_view_get_property;
        object_class->dispose = web_view_dispose;
@@ -1581,7 +1965,6 @@ e_web_view_class_init (EWebViewClass *class)
        class->link_clicked = web_view_link_clicked;
        class->load_string = web_view_load_string;
        class->load_uri = web_view_load_uri;
-       class->redirect_uri = web_view_redirect_uri;
        class->suggest_filename = web_view_suggest_filename;
        class->popup_event = web_view_popup_event;
        class->stop_loading = web_view_stop_loading;
@@ -1597,6 +1980,18 @@ e_web_view_class_init (EWebViewClass *class)
                        FALSE,
                        G_PARAM_READWRITE));
 
+       /* Inherited from ESelectableInterface; just a fake property here */
+       g_object_class_override_property (
+               object_class,
+               PROP_COPY_TARGET_LIST,
+               "copy-target-list");
+
+       /* Inherited from ESelectableInterface; just a fake property here */
+       g_object_class_override_property (
+               object_class,
+               PROP_PASTE_TARGET_LIST,
+               "paste-target-list");
+
        g_object_class_install_property (
                object_class,
                PROP_CURSOR_IMAGE_SRC,
@@ -1726,9 +2121,16 @@ e_web_view_class_init (EWebViewClass *class)
                e_marshal_BOOLEAN__STRING,
                G_TYPE_BOOLEAN, 1, G_TYPE_STRING);
 
-       webkit_set_cache_model (WEBKIT_CACHE_MODEL_DOCUMENT_VIEWER);
-       webkit_set_default_web_database_quota (0);
-       webkit_application_cache_set_maximum_size (0);
+       /* Expects an empty string to abandon the request,
+          or NULL to keep the passed-in uri,
+          or a new uri to load instead. */
+       signals[URI_REQUESTED] = g_signal_new (
+               "uri-requested",
+               G_TYPE_FROM_CLASS (class),
+               G_SIGNAL_RUN_LAST,
+               G_STRUCT_OFFSET (EWebViewClass, uri_requested),
+               NULL, NULL, NULL,
+               G_TYPE_NONE, 2, G_TYPE_STRING, G_TYPE_POINTER);
 }
 
 static void
@@ -1748,6 +2150,14 @@ e_web_view_selectable_init (ESelectableInterface *iface)
 }
 
 static void
+initialize_web_extensions_cb (WebKitWebContext *web_context)
+{
+       /* Set the web extensions dir before the process is launched */
+       webkit_web_context_set_web_extensions_directory (
+               web_context, EVOLUTION_WEB_EXTENSIONS_DIR);
+}
+
+static void
 e_web_view_init (EWebView *web_view)
 {
        GtkUIManager *ui_manager;
@@ -1760,48 +2170,35 @@ e_web_view_init (EWebView *web_view)
        gulong handler_id;
        GError *error = NULL;
 
-       g_once (
-               &disable_webkit_3rd_party_plugins_once,
-               web_view_disable_webkit_3rd_party_plugins, NULL);
-
        web_view->priv = E_WEB_VIEW_GET_PRIVATE (web_view);
 
        web_view->priv->old_settings = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, 
(GDestroyNotify) g_variant_unref);
-       web_view->priv->zoom_hack_state = E_WEB_VIEW_ZOOM_HACK_STATE_NONE;
-
-       /* XXX No WebKitWebView class method pointers to
-        *     override so we have to use signal handlers. */
-
-       g_signal_connect (
-               web_view, "create-plugin-widget",
-               G_CALLBACK (web_view_create_plugin_widget_cb), NULL);
 
        g_signal_connect (
                web_view, "context-menu",
                G_CALLBACK (web_view_context_menu_cb), NULL);
 
        g_signal_connect (
-               web_view, "hovering-over-link",
-               G_CALLBACK (web_view_hovering_over_link_cb), NULL);
+               web_view, "mouse-target-changed",
+               G_CALLBACK (web_view_mouse_target_changed_cb), NULL);
 
        g_signal_connect (
-               web_view, "navigation-policy-decision-requested",
-               G_CALLBACK (web_view_navigation_policy_decision_requested_cb),
+               web_view, "decide-policy",
+               G_CALLBACK (web_view_decide_policy_cb),
                NULL);
 
        g_signal_connect (
-               web_view, "new-window-policy-decision-requested",
-               G_CALLBACK (web_view_navigation_policy_decision_requested_cb),
-               NULL);
+               webkit_web_context_get_default (), "initialize-web-extensions",
+               G_CALLBACK (initialize_web_extensions_cb), NULL);
 
        g_signal_connect (
+               web_view, "load-changed",
+               G_CALLBACK (web_view_load_changed_cb), NULL);
+/* FIXME WK2
+       g_signal_connect (
                web_view, "document-load-finished",
                G_CALLBACK (style_updated_cb), NULL);
-
-       e_signal_connect_notify (
-               web_view, "notify::load-status",
-               G_CALLBACK (web_view_load_status_changed_cb), NULL);
-
+*/
        g_signal_connect (
                web_view, "style-updated",
                G_CALLBACK (style_updated_cb), NULL);
@@ -1817,10 +2214,7 @@ e_web_view_init (EWebView *web_view)
                ui_manager, "connect-proxy",
                G_CALLBACK (web_view_connect_proxy_cb), web_view);
 
-       web_view_init_web_settings (WEBKIT_WEB_VIEW (web_view));
-
-       e_web_view_install_request_handler (web_view, E_TYPE_FILE_REQUEST);
-       e_web_view_install_request_handler (web_view, E_TYPE_STOCK_REQUEST);
+       web_view_watch_web_extension (web_view);
 
        settings = e_util_ref_settings ("org.gnome.desktop.interface");
        web_view->priv->font_settings = g_object_ref (settings);
@@ -1849,8 +2243,6 @@ e_web_view_init (EWebView *web_view)
                g_settings_schema_unref (settings_schema);
        }
 
-       e_web_view_update_fonts (web_view);
-
        action_group = gtk_action_group_new ("uri");
        gtk_action_group_set_translation_domain (action_group, domain);
        gtk_ui_manager_insert_action_group (ui_manager, action_group, 0);
@@ -1958,13 +2350,15 @@ e_web_view_init (EWebView *web_view)
        e_plugin_ui_register_manager (ui_manager, id, web_view);
        e_plugin_ui_enable_manager (ui_manager, id);
 
-       e_web_view_clear (E_WEB_VIEW (web_view));
+       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 *
 e_web_view_new (void)
 {
-       return g_object_new (E_TYPE_WEB_VIEW, NULL);
+       return g_object_new (
+               E_TYPE_WEB_VIEW,
+               NULL);
 }
 
 void
@@ -1972,7 +2366,7 @@ e_web_view_clear (EWebView *web_view)
 {
        g_return_if_fail (E_IS_WEB_VIEW (web_view));
 
-       webkit_web_view_load_html_string (
+       webkit_web_view_load_html (
                WEBKIT_WEB_VIEW (web_view),
                "<html>"
                "<head></head>"
@@ -2010,37 +2404,6 @@ e_web_view_load_uri (EWebView *web_view,
 }
 
 /**
- * e_web_view_redirect_uri:
- * @web_view: an #EWebView
- * @uri: the requested URI
- *
- * Replaces @uri with a redirected URI as necessary, primarily for use
- * with custom #SoupRequest handlers.  Typically this function would be
- * called just prior to handing a request off to a #SoupSession, such as
- * from a #WebKitWebView #WebKitWebView::resource-request-starting signal
- * handler.
- *
- * A newly-allocated URI string is always returned, whether the @uri was
- * redirected or not.  Free the returned string with g_free().
- *
- * Returns: the redirected URI or a copy of @uri
- **/
-gchar *
-e_web_view_redirect_uri (EWebView *web_view,
-                         const gchar *uri)
-{
-       EWebViewClass *class;
-
-       g_return_val_if_fail (E_IS_WEB_VIEW (web_view), NULL);
-       g_return_val_if_fail (uri != NULL, NULL);
-
-       class = E_WEB_VIEW_GET_CLASS (web_view);
-       g_return_val_if_fail (class->redirect_uri != NULL, NULL);
-
-       return class->redirect_uri (web_view, uri);
-}
-
-/**
  * e_web_view_suggest_filename:
  * @web_view: an #EWebView
  * @uri: a URI string
@@ -2089,19 +2452,101 @@ e_web_view_reload (EWebView *web_view)
        webkit_web_view_reload (WEBKIT_WEB_VIEW (web_view));
 }
 
+static void
+get_document_content_html_cb (GDBusProxy *web_extension,
+                              GAsyncResult *result,
+                              GTask *task)
+{
+       GVariant *result_variant;
+       gchar *html_content = NULL;
+
+       result_variant = g_dbus_proxy_call_finish (web_extension, result, NULL);
+       if (result_variant)
+               g_variant_get (result_variant, "(s)", &html_content);
+       g_variant_unref (result_variant);
+
+       g_task_return_pointer (task, html_content, g_free);
+       g_object_unref (task);
+}
+
+void
+e_web_view_get_content_html (EWebView *web_view,
+                             GCancellable *cancellable,
+                             GAsyncReadyCallback callback,
+                             gpointer user_data)
+{
+       GDBusProxy *web_extension;
+       GTask *task;
+
+       g_return_if_fail (E_IS_WEB_VIEW (web_view));
+
+       task = g_task_new (web_view, cancellable, callback, user_data);
+
+       web_extension = e_web_view_get_web_extension_proxy (web_view);
+       if (web_extension) {
+               g_dbus_proxy_call (
+                       web_extension,
+                       "GetDocumentContentHTML",
+                       g_variant_new (
+                               "(t)",
+                               webkit_web_view_get_page_id (
+                                       WEBKIT_WEB_VIEW (web_view))),
+                       G_DBUS_CALL_FLAGS_NONE,
+                       -1,
+                       cancellable,
+                       (GAsyncReadyCallback) get_document_content_html_cb,
+                       g_object_ref (task));
+       } else
+               g_task_return_pointer (task, NULL, NULL);
+}
+
 gchar *
-e_web_view_get_html (EWebView *web_view)
+e_web_view_get_content_html_finish (EWebView *web_view,
+                                    GAsyncResult *result,
+                                    GError **error)
 {
-       WebKitDOMDocument *document;
-       WebKitDOMElement *element;
+       g_return_val_if_fail (E_IS_WEB_VIEW (web_view), NULL);
+       g_return_val_if_fail (g_task_is_valid (result, web_view), FALSE);
+
+       return g_task_propagate_pointer (G_TASK (result), error);
+}
+
+gchar *
+e_web_view_get_content_html_sync (EWebView *web_view,
+                                  GCancellable *cancellable,
+                                  GError **error)
+{
+       GDBusProxy *web_extension;
 
        g_return_val_if_fail (E_IS_WEB_VIEW (web_view), NULL);
 
-       document = webkit_web_view_get_dom_document (WEBKIT_WEB_VIEW (web_view));
-       element = webkit_dom_document_get_document_element (document);
+       web_extension = e_web_view_get_web_extension_proxy (web_view);
+       if (web_extension) {
+               GVariant *result;
+
+               result = g_dbus_proxy_call_sync (
+                               web_extension,
+                               "GetDocumentContentHTML",
+                               g_variant_new (
+                                       "(t)",
+                                       webkit_web_view_get_page_id (
+                                               WEBKIT_WEB_VIEW (web_view))),
+                               G_DBUS_CALL_FLAGS_NONE,
+                               -1,
+                               cancellable,
+                               error);
+
+               if (result) {
+                       gchar *html_content = NULL;
+
+                       g_variant_get (result, "(s)", &html_content);
+                       g_variant_unref (result);
+
+                       return html_content;
+               }
+       }
 
-       return webkit_dom_html_element_get_outer_html (
-               WEBKIT_DOM_HTML_ELEMENT (element));
+       return NULL;
 }
 
 gboolean
@@ -2131,8 +2576,10 @@ e_web_view_get_copy_target_list (EWebView *web_view)
 {
        g_return_val_if_fail (E_IS_WEB_VIEW (web_view), NULL);
 
-       return webkit_web_view_get_copy_target_list (
-               WEBKIT_WEB_VIEW (web_view));
+       return NULL;
+       /* FIXME WK2 */
+       /*return webkit_web_view_get_copy_target_list (
+               WEBKIT_WEB_VIEW (web_view));*/
 }
 
 gboolean
@@ -2184,7 +2631,7 @@ e_web_view_get_editable (EWebView *web_view)
 {
        g_return_val_if_fail (E_IS_WEB_VIEW (web_view), FALSE);
 
-       return webkit_web_view_get_editable (WEBKIT_WEB_VIEW (web_view));
+       return webkit_web_view_is_editable (WEBKIT_WEB_VIEW (web_view));
 }
 
 void
@@ -2277,8 +2724,10 @@ e_web_view_get_paste_target_list (EWebView *web_view)
 {
        g_return_val_if_fail (E_IS_WEB_VIEW (web_view), NULL);
 
+       /* FIXME WK2
        return webkit_web_view_get_paste_target_list (
-               WEBKIT_WEB_VIEW (web_view));
+               WEBKIT_WEB_VIEW (web_view)); */
+       return NULL;
 }
 
 GtkAction *
@@ -2352,11 +2801,11 @@ e_web_view_add_highlight (EWebView *web_view,
                &web_view->priv->highlights,
                g_strdup (highlight));
 
-       webkit_web_view_mark_text_matches (
-               WEBKIT_WEB_VIEW (web_view), highlight, FALSE, 0);
-
-       webkit_web_view_set_highlight_text_matches (
-               WEBKIT_WEB_VIEW (web_view), TRUE);
+       webkit_find_controller_search (
+               web_view->priv->find_controller,
+               highlight,
+               WEBKIT_FIND_OPTIONS_NONE,
+               G_MAXUINT);
 }
 
 void
@@ -2364,7 +2813,7 @@ e_web_view_clear_highlights (EWebView *web_view)
 {
        g_return_if_fail (E_IS_WEB_VIEW (web_view));
 
-       webkit_web_view_unmark_text_matches (WEBKIT_WEB_VIEW (web_view));
+       webkit_find_controller_search_finish (web_view->priv->find_controller);
 
        while (!g_queue_is_empty (&web_view->priv->highlights))
                g_free (g_queue_pop_head (&web_view->priv->highlights));
@@ -2411,7 +2860,8 @@ e_web_view_copy_clipboard (EWebView *web_view)
 {
        g_return_if_fail (E_IS_WEB_VIEW (web_view));
 
-       webkit_web_view_copy_clipboard (WEBKIT_WEB_VIEW (web_view));
+       webkit_web_view_execute_editing_command (
+               WEBKIT_WEB_VIEW (web_view), WEBKIT_EDITING_COMMAND_COPY);
 }
 
 void
@@ -2419,15 +2869,43 @@ e_web_view_cut_clipboard (EWebView *web_view)
 {
        g_return_if_fail (E_IS_WEB_VIEW (web_view));
 
-       webkit_web_view_cut_clipboard (WEBKIT_WEB_VIEW (web_view));
+       webkit_web_view_execute_editing_command (
+               WEBKIT_WEB_VIEW (web_view), WEBKIT_EDITING_COMMAND_CUT);
 }
 
 gboolean
 e_web_view_is_selection_active (EWebView *web_view)
 {
+       GDBusProxy *web_extension;
+
        g_return_val_if_fail (E_IS_WEB_VIEW (web_view), FALSE);
 
-       return webkit_web_view_has_selection (WEBKIT_WEB_VIEW (web_view));
+       web_extension = e_web_view_get_web_extension_proxy (web_view);
+       if (web_extension) {
+               GVariant *result;
+
+               result = g_dbus_proxy_call_sync (
+                               web_extension,
+                               "DocumentHasSelection",
+                               g_variant_new (
+                                       "(t)",
+                                       webkit_web_view_get_page_id (
+                                               WEBKIT_WEB_VIEW (web_view))),
+                               G_DBUS_CALL_FLAGS_NONE,
+                               -1,
+                               NULL,
+                               NULL);
+
+               if (result) {
+                       gboolean value = FALSE;
+
+                       g_variant_get (result, "(b)", &value);
+                       g_variant_unref (result);
+                       return value;
+               }
+       }
+
+       return FALSE;
 }
 
 void
@@ -2435,17 +2913,18 @@ e_web_view_paste_clipboard (EWebView *web_view)
 {
        g_return_if_fail (E_IS_WEB_VIEW (web_view));
 
-       webkit_web_view_paste_clipboard (WEBKIT_WEB_VIEW (web_view));
+       webkit_web_view_execute_editing_command (
+               WEBKIT_WEB_VIEW (web_view), WEBKIT_EDITING_COMMAND_PASTE);
 }
 
 gboolean
 e_web_view_scroll_forward (EWebView *web_view)
 {
        g_return_val_if_fail (E_IS_WEB_VIEW (web_view), FALSE);
-
+/* FIXME WK2
        webkit_web_view_move_cursor (
                WEBKIT_WEB_VIEW (web_view), GTK_MOVEMENT_PAGES, 1);
-
+*/
        return TRUE;  /* XXX This means nothing. */
 }
 
@@ -2453,10 +2932,10 @@ gboolean
 e_web_view_scroll_backward (EWebView *web_view)
 {
        g_return_val_if_fail (E_IS_WEB_VIEW (web_view), FALSE);
-
+/* FIXME WK2
        webkit_web_view_move_cursor (
                WEBKIT_WEB_VIEW (web_view), GTK_MOVEMENT_PAGES, -1);
-
+*/
        return TRUE;  /* XXX This means nothing. */
 }
 
@@ -2465,7 +2944,8 @@ e_web_view_select_all (EWebView *web_view)
 {
        g_return_if_fail (E_IS_WEB_VIEW (web_view));
 
-       webkit_web_view_select_all (WEBKIT_WEB_VIEW (web_view));
+       webkit_web_view_execute_editing_command (
+               WEBKIT_WEB_VIEW (web_view), WEBKIT_EDITING_COMMAND_SELECT_ALL);
 }
 
 void
@@ -2489,19 +2969,31 @@ e_web_view_zoom_100 (EWebView *web_view)
 void
 e_web_view_zoom_in (EWebView *web_view)
 {
+       gdouble zoom_level;
+
        g_return_if_fail (E_IS_WEB_VIEW (web_view));
 
-       if (webkit_web_view_get_zoom_level (WEBKIT_WEB_VIEW (web_view)) < 4.9999)
-               webkit_web_view_zoom_in (WEBKIT_WEB_VIEW (web_view));
+       /* There is no webkit_web_view_zoom_in function in WK2, so emulate it */
+       zoom_level = webkit_web_view_get_zoom_level (WEBKIT_WEB_VIEW (web_view));
+       /* zoom-step in WK1 was 0.1 */
+       zoom_level += 0.1;
+       if (zoom_level < 4.9999)
+               webkit_web_view_set_zoom_level (WEBKIT_WEB_VIEW (web_view), zoom_level);
 }
 
 void
 e_web_view_zoom_out (EWebView *web_view)
 {
+       gdouble zoom_level;
+
        g_return_if_fail (E_IS_WEB_VIEW (web_view));
 
-       if (webkit_web_view_get_zoom_level (WEBKIT_WEB_VIEW (web_view)) > 0.7999)
-               webkit_web_view_zoom_out (WEBKIT_WEB_VIEW (web_view));
+       /* There is no webkit_web_view_zoom_out function in WK2, so emulate it */
+       zoom_level = webkit_web_view_get_zoom_level (WEBKIT_WEB_VIEW (web_view));
+       /* zoom-step in WK1 was 0.1 */
+       zoom_level -= 0.1;
+       if (zoom_level > 0.7999)
+               webkit_web_view_set_zoom_level (WEBKIT_WEB_VIEW (web_view), zoom_level);
 }
 
 GtkUIManager *
@@ -2609,132 +3101,98 @@ e_web_view_update_actions (EWebView *web_view)
        g_signal_emit (web_view, signals[UPDATE_ACTIONS], 0);
 }
 
-static gboolean
-element_is_in_pre_tag (WebKitDOMNode *node)
+static void
+get_selection_content_html_cb (GDBusProxy *web_extension,
+                               GAsyncResult *result,
+                               GTask *task)
 {
-       WebKitDOMElement *element;
-
-       if (!node)
-               return FALSE;
+       GVariant *result_variant;
+       gchar *html_content = NULL;
 
-       while (element = webkit_dom_node_get_parent_element (node), element) {
-               node = WEBKIT_DOM_NODE (element);
+       result_variant = g_dbus_proxy_call_finish (web_extension, result, NULL);
+       if (result_variant)
+               g_variant_get (result_variant, "(s)", &html_content);
+       g_variant_unref (result_variant);
 
-               if (WEBKIT_DOM_IS_HTML_PRE_ELEMENT (element)) {
-                       return TRUE;
-               } else if (WEBKIT_DOM_IS_HTML_IFRAME_ELEMENT (element)) {
-                       break;
-               }
-       }
-
-       return FALSE;
+       g_task_return_pointer (task, html_content, g_free);
+       g_object_unref (task);
 }
 
-static gchar *
-web_view_get_frame_selection_html (WebKitDOMElement *iframe)
-{
-       WebKitDOMDocument *document;
-       WebKitDOMDOMWindow *dom_window;
-       WebKitDOMDOMSelection *dom_selection;
-       WebKitDOMNodeList *frames;
-       gulong ii, length;
-
-       document = webkit_dom_html_iframe_element_get_content_document (
-               WEBKIT_DOM_HTML_IFRAME_ELEMENT (iframe));
-       dom_window = webkit_dom_document_get_default_view (document);
-       dom_selection = webkit_dom_dom_window_get_selection (dom_window);
-       g_object_unref (dom_window);
-       if (dom_selection && (webkit_dom_dom_selection_get_range_count (dom_selection) > 0)) {
-               WebKitDOMRange *range;
-               WebKitDOMElement *element;
-               WebKitDOMDocumentFragment *fragment;
-
-               range = webkit_dom_dom_selection_get_range_at (dom_selection, 0, NULL);
-               if (range != NULL) {
-                       gchar *inner_html;
-                       WebKitDOMNode *node;
-
-                       fragment = webkit_dom_range_clone_contents (
-                               range, NULL);
-
-                       element = webkit_dom_document_create_element (
-                               document, "DIV", NULL);
-                       webkit_dom_node_append_child (
-                               WEBKIT_DOM_NODE (element),
-                               WEBKIT_DOM_NODE (fragment), NULL);
-
-                       inner_html = webkit_dom_html_element_get_inner_html (
-                               WEBKIT_DOM_HTML_ELEMENT (element));
-                       node = webkit_dom_range_get_start_container (range, NULL);
-                       if (element_is_in_pre_tag (node)) {
-                               gchar *tmp = inner_html;
-                               inner_html = g_strconcat ("<pre>", tmp, "</pre>", NULL);
-                               g_free (tmp);
-                       }
-
-                       g_object_unref (range);
-                       g_object_unref (dom_selection);
-                       return inner_html;
-               }
-       }
-
-       g_object_unref (dom_selection);
-
-       frames = webkit_dom_document_get_elements_by_tag_name (
-               document, "IFRAME");
-       length = webkit_dom_node_list_get_length (frames);
-       for (ii = 0; ii < length; ii++) {
-               WebKitDOMNode *node;
-               gchar *text;
-
-               node = webkit_dom_node_list_item (frames, ii);
-
-               text = web_view_get_frame_selection_html (
-                       WEBKIT_DOM_ELEMENT (node));
+void
+e_web_view_get_selection_content_html (EWebView *web_view,
+                                       GCancellable *cancellable,
+                                       GAsyncReadyCallback callback,
+                                       gpointer user_data)
+{
+       GDBusProxy *web_extension;
+       GTask *task;
 
-               g_object_unref (node);
-               if (text != NULL) {
-                       g_object_unref (frames);
-                       return text;
-               }
-       }
-       g_object_unref (frames);
+       g_return_if_fail (E_IS_WEB_VIEW (web_view));
 
-       return NULL;
+       task = g_task_new (web_view, cancellable, callback, user_data);
+
+       web_extension = e_web_view_get_web_extension_proxy (web_view);
+       if (web_extension) {
+               g_dbus_proxy_call (
+                       web_extension,
+                       "GetSelectionContentHTML",
+                       g_variant_new (
+                               "(t)",
+                               webkit_web_view_get_page_id (
+                                       WEBKIT_WEB_VIEW (web_view))),
+                       G_DBUS_CALL_FLAGS_NONE,
+                       -1,
+                       cancellable,
+                       (GAsyncReadyCallback) get_selection_content_html_cb,
+                       g_object_ref (task));
+       } else
+               g_task_return_pointer (task, NULL, NULL);
 }
 
 gchar *
-e_web_view_get_selection_html (EWebView *web_view)
+e_web_view_get_selection_content_html_finish (EWebView *web_view,
+                                              GAsyncResult *result,
+                                              GError **error)
 {
-       WebKitDOMDocument *document;
-       WebKitDOMNodeList *frames;
-       gulong ii, length;
-
        g_return_val_if_fail (E_IS_WEB_VIEW (web_view), NULL);
+       g_return_val_if_fail (g_task_is_valid (result, web_view), FALSE);
 
-       if (!webkit_web_view_has_selection (WEBKIT_WEB_VIEW (web_view)))
-               return NULL;
-
-       document = webkit_web_view_get_dom_document (WEBKIT_WEB_VIEW (web_view));
-       frames = webkit_dom_document_get_elements_by_tag_name (document, "IFRAME");
-       length = webkit_dom_node_list_get_length (frames);
-
-       for (ii = 0; ii < length; ii++) {
-               gchar *text;
-               WebKitDOMNode *node;
+       return g_task_propagate_pointer (G_TASK (result), error);
+}
 
-               node = webkit_dom_node_list_item (frames, ii);
+gchar *
+e_web_view_get_selection_content_html_sync (EWebView *web_view,
+                                            GCancellable *cancellable,
+                                            GError **error)
+{
+       GDBusProxy *web_extension;
 
-               text = web_view_get_frame_selection_html (
-                       WEBKIT_DOM_ELEMENT (node));
+       g_return_val_if_fail (E_IS_WEB_VIEW (web_view), NULL);
 
-               g_object_unref (node);
-               if (text != NULL) {
-                       g_object_unref (frames);
-                       return text;
+       web_extension = e_web_view_get_web_extension_proxy (web_view);
+       if (web_extension) {
+               GVariant *result;
+
+               result = g_dbus_proxy_call_sync (
+                               web_extension,
+                               "GetSelectionContentHTML",
+                               g_variant_new (
+                                       "(t)",
+                                       webkit_web_view_get_page_id (
+                                               WEBKIT_WEB_VIEW (web_view))),
+                               G_DBUS_CALL_FLAGS_NONE,
+                               -1,
+                               cancellable,
+                               error);
+
+               if (result) {
+                       gchar *html_content = NULL;
+
+                       g_variant_get (result, "(s)", &html_content);
+                       g_variant_unref (result);
+                       return html_content;
                }
        }
-       g_object_unref (frames);
 
        return NULL;
 }
@@ -2757,60 +3215,61 @@ e_web_view_get_citation_color_for_level (gint level)
 }
 
 void
-e_web_view_update_fonts (EWebView *web_view)
+e_web_view_update_fonts_settings (GSettings *font_settings,
+                                  GSettings *aliasing_settings,
+                                  PangoFontDescription *ms_font,
+                                  PangoFontDescription *vw_font,
+                                  GtkWidget *view_widget)
 {
-       EWebViewClass *class;
-       GString *stylesheet;
-       gchar *base64;
+       gboolean clean_ms = FALSE, clean_vw = FALSE;
        gchar *aa = NULL;
-       WebKitWebSettings *settings;
-       PangoFontDescription *min_size, *ms, *vw;
        const gchar *styles[] = { "normal", "oblique", "italic" };
        const gchar *smoothing = NULL;
-       GtkStyleContext *context;
        GdkColor *link = NULL;
        GdkColor *visited = NULL;
+       GString *stylesheet;
+       GtkStyleContext *context;
+       PangoFontDescription *min_size, *ms, *vw;
+       WebKitSettings *wk_settings;
+       WebKitUserContentManager *manager;
+       WebKitUserStyleSheet *style_sheet;
 
-       g_return_if_fail (E_IS_WEB_VIEW (web_view));
-
-       ms = NULL;
-       vw = NULL;
-
-       class = E_WEB_VIEW_GET_CLASS (web_view);
-       if (class->set_fonts != NULL)
-               class->set_fonts (web_view, &ms, &vw);
-
-       if (ms == NULL) {
+       if (!ms_font) {
                gchar *font;
 
                font = g_settings_get_string (
-                       web_view->priv->font_settings,
+                       font_settings,
                        "monospace-font-name");
 
                ms = pango_font_description_from_string (
                        (font != NULL) ? font : "monospace 10");
 
+               clean_ms = TRUE;
+
                g_free (font);
-       }
+       } else
+               ms = ms_font;
 
-       if (vw == NULL) {
+       if (!vw_font) {
                gchar *font;
 
                font = g_settings_get_string (
-                       web_view->priv->font_settings,
+                       font_settings,
                        "font-name");
 
                vw = pango_font_description_from_string (
                        (font != NULL) ? font : "serif 10");
 
+               clean_vw = TRUE;
+
                g_free (font);
-       }
+       } else
+               vw = vw_font;
 
-       if (pango_font_description_get_size (ms) < pango_font_description_get_size (vw)) {
+       if (pango_font_description_get_size (ms) < pango_font_description_get_size (vw))
                min_size = ms;
-       } else {
+       else
                min_size = vw;
-       }
 
        stylesheet = g_string_new ("");
        g_string_append_printf (
@@ -2825,9 +3284,9 @@ e_web_view_update_fonts (EWebView *web_view)
                pango_font_description_get_weight (vw),
                styles[pango_font_description_get_style (vw)]);
 
-       if (web_view->priv->aliasing_settings != NULL)
+       if (aliasing_settings != NULL)
                aa = g_settings_get_string (
-                       web_view->priv->aliasing_settings, "antialiasing");
+                       aliasing_settings, "antialiasing");
 
        if (g_strcmp0 (aa, "none") == 0)
                smoothing = "none";
@@ -2854,176 +3313,229 @@ e_web_view_update_fonts (EWebView *web_view)
                "  font-weight: %d;\n"
                "  font-style: %s;\n"
                "  margin: 0px;\n"
-               "}",
+               "}\n",
                pango_font_description_get_family (ms),
                pango_font_description_get_size (ms) / PANGO_SCALE,
                pango_font_description_get_weight (ms),
                styles[pango_font_description_get_style (ms)]);
 
-       context = gtk_widget_get_style_context (GTK_WIDGET (web_view));
-       gtk_style_context_get_style (
-               context,
-               "link-color", &link,
-               "visited-link-color", &visited,
-               NULL);
-
-       if (link == NULL) {
-               #if GTK_CHECK_VERSION(3,12,0)
-               GdkRGBA rgba;
-               GtkStateFlags state;
-               #endif
-
-               link = g_slice_new0 (GdkColor);
-               link->blue = G_MAXINT16;
+       if (view_widget) {
+               context = gtk_widget_get_style_context (view_widget);
+               gtk_style_context_get_style (
+                       context,
+                       "link-color", &link,
+                       "visited-link-color", &visited,
+                       NULL);
 
-               #if GTK_CHECK_VERSION(3,12,0)
-               rgba.alpha = 1;
-               rgba.red = 0;
-               rgba.green = 0;
-               rgba.blue = 1;
+               if (link == NULL) {
+                       #if GTK_CHECK_VERSION(3,12,0)
+                       GdkRGBA rgba;
+                       GtkStateFlags state;
+                       #endif
 
-               state = gtk_style_context_get_state (context);
-               state = state & (~(GTK_STATE_FLAG_VISITED | GTK_STATE_FLAG_LINK));
-               state = state | GTK_STATE_FLAG_LINK;
+                       link = g_slice_new0 (GdkColor);
+                       link->blue = G_MAXINT16;
 
-               gtk_style_context_save (context);
-               gtk_style_context_set_state (context, state);
-               gtk_style_context_get_color (context, state, &rgba);
-               gtk_style_context_restore (context);
+                       #if GTK_CHECK_VERSION(3,12,0)
+                       rgba.alpha = 1;
+                       rgba.red = 0;
+                       rgba.green = 0;
+                       rgba.blue = 1;
 
-               e_rgba_to_color (&rgba, link);
-               #endif
-       }
+                       state = gtk_style_context_get_state (context);
+                       state = state & (~(GTK_STATE_FLAG_VISITED | GTK_STATE_FLAG_LINK));
+                       state = state | GTK_STATE_FLAG_LINK;
 
-       if (visited == NULL) {
-               #if GTK_CHECK_VERSION(3,12,0)
-               GdkRGBA rgba;
-               GtkStateFlags state;
-               #endif
+                       gtk_style_context_save (context);
+                       gtk_style_context_set_state (context, state);
+                       gtk_style_context_get_color (context, state, &rgba);
+                       gtk_style_context_restore (context);
 
-               visited = g_slice_new0 (GdkColor);
-               visited->red = G_MAXINT16;
-
-               #if GTK_CHECK_VERSION(3,12,0)
-               rgba.alpha = 1;
-               rgba.red = 1;
-               rgba.green = 0;
-               rgba.blue = 0;
+                       e_rgba_to_color (&rgba, link);
+                       #endif
+               }
 
-               state = gtk_style_context_get_state (context);
-               state = state & (~(GTK_STATE_FLAG_VISITED | GTK_STATE_FLAG_LINK));
-               state = state | GTK_STATE_FLAG_VISITED;
+               if (visited == NULL) {
+                       #if GTK_CHECK_VERSION(3,12,0)
+                       GdkRGBA rgba;
+                       GtkStateFlags state;
+                       #endif
 
-               gtk_style_context_save (context);
-               gtk_style_context_set_state (context, state);
-               gtk_style_context_get_color (context, state, &rgba);
-               gtk_style_context_restore (context);
+                       visited = g_slice_new0 (GdkColor);
+                       visited->red = G_MAXINT16;
 
-               e_rgba_to_color (&rgba, visited);
-               #endif
-       }
+                       #if GTK_CHECK_VERSION(3,12,0)
+                       rgba.alpha = 1;
+                       rgba.red = 1;
+                       rgba.green = 0;
+                       rgba.blue = 0;
 
-       g_string_append_printf (
-               stylesheet,
-               "a {\n"
-               "  color: #%06x;\n"
-               "}\n"
-               "a:visited {\n"
-               "  color: #%06x;\n"
-               "}\n",
-               e_color_to_value (link),
-               e_color_to_value (visited));
+                       state = gtk_style_context_get_state (context);
+                       state = state & (~(GTK_STATE_FLAG_VISITED | GTK_STATE_FLAG_LINK));
+                       state = state | GTK_STATE_FLAG_VISITED;
 
-       gdk_color_free (link);
-       gdk_color_free (visited);
+                       gtk_style_context_save (context);
+                       gtk_style_context_set_state (context, state);
+                       gtk_style_context_get_color (context, state, &rgba);
+                       gtk_style_context_restore (context);
 
-       g_string_append (
-               stylesheet,
-               "blockquote[type=cite]:not(.-x-evo-plaintext-quoted) "
-               "{\n"
-               "  padding: 0ch 1ch 0ch 1ch;\n"
-               "  margin: 0ch;\n"
-               "  border-width: 0px 2px 0px 2px;\n"
-               "  border-style: none solid none solid;\n"
-               "  border-radius: 2px;\n"
-               "}\n");
+                       e_rgba_to_color (&rgba, visited);
+                       #endif
+               }
 
-       g_string_append_printf (
-               stylesheet,
-               "blockquote[type=cite]:not(.-x-evo-plaintext-quoted) "
-               "{\n"
-               "  border-color: %s;\n"
-               "}\n",
-               e_web_view_get_citation_color_for_level (1));
+               g_string_append_printf (
+                       stylesheet,
+                       "a {\n"
+                       "  color: #%06x;\n"
+                       "}\n"
+                       "a:visited {\n"
+                       "  color: #%06x;\n"
+                       "}\n",
+                       e_color_to_value (link),
+                       e_color_to_value (visited));
+
+               gdk_color_free (link);
+               gdk_color_free (visited);
+
+               g_string_append (
+                       stylesheet,
+                       "blockquote[type=cite]:not(.-x-evo-plaintext-quoted) "
+                       "{\n"
+                       "  padding: 0ch 1ch 0ch 1ch;\n"
+                       "  margin: 0ch;\n"
+                       "  border-width: 0px 2px 0px 2px;\n"
+                       "  border-style: none solid none solid;\n"
+                       "  border-radius: 2px;\n"
+                       "}\n");
 
-       g_string_append_printf (
-               stylesheet,
-               "blockquote[type=cite]:not(.-x-evo-plaintext-quoted) "
-               "blockquote[type=cite]:not(.-x-evo-plaintext-quoted) "
-               "{\n"
-               "  border-color: %s;\n"
-               "}\n",
-               e_web_view_get_citation_color_for_level (2));
+               g_string_append_printf (
+                       stylesheet,
+                       "blockquote[type=cite]:not(.-x-evo-plaintext-quoted) "
+                       "{\n"
+                       "  border-color: %s;\n"
+                       "}\n",
+                       e_web_view_get_citation_color_for_level (1));
 
-       g_string_append_printf (
-               stylesheet,
-               "blockquote[type=cite]:not(.-x-evo-plaintext-quoted) "
-               "blockquote[type=cite]:not(.-x-evo-plaintext-quoted) "
-               "blockquote[type=cite]:not(.-x-evo-plaintext-quoted) "
-               "{\n"
-               "  border-color: %s;\n"
-               "}\n",
-               e_web_view_get_citation_color_for_level (3));
+               g_string_append_printf (
+                       stylesheet,
+                       "blockquote[type=cite]:not(.-x-evo-plaintext-quoted) "
+                       "blockquote[type=cite]:not(.-x-evo-plaintext-quoted) "
+                       "{\n"
+                       "  border-color: %s;\n"
+                       "}\n",
+                       e_web_view_get_citation_color_for_level (2));
 
-       g_string_append_printf (
-               stylesheet,
-               "blockquote[type=cite]:not(.-x-evo-plaintext-quoted) "
-               "blockquote[type=cite]:not(.-x-evo-plaintext-quoted) "
-               "blockquote[type=cite]:not(.-x-evo-plaintext-quoted) "
-               "blockquote[type=cite]:not(.-x-evo-plaintext-quoted) "
-               "{\n"
-               "  border-color: %s;\n"
-               "}\n",
-               e_web_view_get_citation_color_for_level (4));
+               g_string_append_printf (
+                       stylesheet,
+                       "blockquote[type=cite]:not(.-x-evo-plaintext-quoted) "
+                       "blockquote[type=cite]:not(.-x-evo-plaintext-quoted) "
+                       "blockquote[type=cite]:not(.-x-evo-plaintext-quoted) "
+                       "{\n"
+                       "  border-color: %s;\n"
+                       "}\n",
+                       e_web_view_get_citation_color_for_level (3));
 
-       g_string_append_printf (
-               stylesheet,
-               "blockquote[type=cite]:not(.-x-evo-plaintext-quoted) "
-               "blockquote[type=cite]:not(.-x-evo-plaintext-quoted) "
-               "blockquote[type=cite]:not(.-x-evo-plaintext-quoted) "
-               "blockquote[type=cite]:not(.-x-evo-plaintext-quoted) "
-               "blockquote[type=cite]:not(.-x-evo-plaintext-quoted) "
-               "{\n"
-               "  border-color: %s;\n"
-               "}\n",
-               e_web_view_get_citation_color_for_level (5));
+               g_string_append_printf (
+                       stylesheet,
+                       "blockquote[type=cite]:not(.-x-evo-plaintext-quoted) "
+                       "blockquote[type=cite]:not(.-x-evo-plaintext-quoted) "
+                       "blockquote[type=cite]:not(.-x-evo-plaintext-quoted) "
+                       "blockquote[type=cite]:not(.-x-evo-plaintext-quoted) "
+                       "{\n"
+                       "  border-color: %s;\n"
+                       "}\n",
+                       e_web_view_get_citation_color_for_level (4));
 
-       base64 = g_base64_encode ((guchar *) stylesheet->str, stylesheet->len);
-       g_string_free (stylesheet, TRUE);
+               g_string_append_printf (
+                       stylesheet,
+                       "blockquote[type=cite]:not(.-x-evo-plaintext-quoted) "
+                       "blockquote[type=cite]:not(.-x-evo-plaintext-quoted) "
+                       "blockquote[type=cite]:not(.-x-evo-plaintext-quoted) "
+                       "blockquote[type=cite]:not(.-x-evo-plaintext-quoted) "
+                       "blockquote[type=cite]:not(.-x-evo-plaintext-quoted) "
+                       "{\n"
+                       "  border-color: %s;\n"
+                       "}\n",
+                       e_web_view_get_citation_color_for_level (5));
+       }
 
-       stylesheet = g_string_new ("data:text/css;charset=utf-8;base64,");
-       g_string_append (stylesheet, base64);
-       g_free (base64);
+       wk_settings = webkit_web_view_get_settings (WEBKIT_WEB_VIEW (view_widget));
 
-       settings = webkit_web_view_get_settings (WEBKIT_WEB_VIEW (web_view));
        g_object_set (
-               G_OBJECT (settings),
+               wk_settings,
                "default-font-size",
-               pango_font_description_get_size (vw) / PANGO_SCALE,
+               e_util_normalize_font_size (
+                       view_widget, pango_font_description_get_size (vw) / PANGO_SCALE),
                "default-font-family",
                pango_font_description_get_family (vw),
                "monospace-font-family",
                pango_font_description_get_family (ms),
                "default-monospace-font-size",
-               pango_font_description_get_size (ms) / PANGO_SCALE,
+               e_util_normalize_font_size (
+                       view_widget, pango_font_description_get_size (ms) / PANGO_SCALE),
                "minimum-font-size",
-               pango_font_description_get_size (min_size) / PANGO_SCALE,
-               "user-stylesheet-uri",
+               e_util_normalize_font_size (
+                       view_widget, pango_font_description_get_size (min_size) / PANGO_SCALE),
+               NULL);
+
+       manager = webkit_web_view_get_user_content_manager (WEBKIT_WEB_VIEW (view_widget));
+       webkit_user_content_manager_remove_all_style_sheets (manager);
+
+       style_sheet = webkit_user_style_sheet_new (
                stylesheet->str,
+               WEBKIT_USER_CONTENT_INJECT_ALL_FRAMES,
+               WEBKIT_USER_STYLE_LEVEL_USER,
+               NULL,
                NULL);
 
+       webkit_user_content_manager_add_style_sheet (manager, style_sheet);
+
+       webkit_user_style_sheet_unref (style_sheet);
+
        g_string_free (stylesheet, TRUE);
 
+       if (clean_ms)
+               pango_font_description_free (ms);
+       if (clean_vw)
+               pango_font_description_free (vw);
+}
+
+WebKitSettings *
+e_web_view_get_default_webkit_settings (void)
+{
+       return webkit_settings_new_with_settings (
+               "auto-load-images", TRUE,
+               "default-charset", "utf-8",
+               "enable-html5-database", FALSE,
+               "enable-dns-prefetching", FALSE,
+               "enable-html5-local-storage", FALSE,
+               "enable-java", FALSE,
+               "enable-javascript", FALSE,
+               "enable-offline-web-application-cache", FALSE,
+               "enable-page-cache", FALSE,
+               "enable-plugins", FALSE,
+               "enable-smooth-scrolling", FALSE,
+               "media-playback-allows-inline", FALSE,
+               NULL);
+}
+
+void
+e_web_view_update_fonts (EWebView *web_view)
+{
+       EWebViewClass *class;
+       PangoFontDescription *ms = NULL, *vw = NULL;
+
+       g_return_if_fail (E_IS_WEB_VIEW (web_view));
+
+       class = E_WEB_VIEW_GET_CLASS (web_view);
+       if (class->set_fonts != NULL)
+               class->set_fonts (web_view, &ms, &vw);
+
+       e_web_view_update_fonts_settings (
+               web_view->priv->font_settings,
+               web_view->priv->aliasing_settings,
+               ms, vw, GTK_WIDGET (web_view));
+
        pango_font_description_free (ms);
        pango_font_description_free (vw);
 }
@@ -3387,24 +3899,25 @@ e_web_view_cursor_image_save (EWebView *web_view)
 
 /* Helper for e_web_view_request() */
 static void
-web_view_request_send_cb (GObject *source_object,
-                          GAsyncResult *result,
-                          gpointer user_data)
+web_view_request_process_thread (GTask *task,
+                                gpointer source_object,
+                                gpointer task_data,
+                                GCancellable *cancellable)
 {
-       GSimpleAsyncResult *simple;
-       AsyncContext *async_context;
+       AsyncContext *async_context = task_data;
+       gint64 stream_length = -1;
+       gchar *mime_type = NULL;
        GError *local_error = NULL;
 
-       simple = G_SIMPLE_ASYNC_RESULT (user_data);
-       async_context = g_simple_async_result_get_op_res_gpointer (simple);
-
-       async_context->input_stream = soup_request_send_finish (
-               SOUP_REQUEST (source_object), result, &local_error);
-
-       if (local_error != NULL)
-               g_simple_async_result_take_error (simple, local_error);
+       if (!e_content_request_process_sync (async_context->content_request,
+               async_context->uri, source_object, &async_context->input_stream,
+               &stream_length, &mime_type, cancellable, &local_error)) {
+               g_task_return_error (task, local_error);
+       } else {
+               g_task_return_boolean (task, TRUE);
+       }
 
-       g_simple_async_result_complete (simple);
+       g_free (mime_type);
 }
 
 /**
@@ -3415,9 +3928,7 @@ web_view_request_send_cb (GObject *source_object,
  * @callback: a #GAsyncReadyCallback to call when the request is satisfied
  * @user_data: data to pass to the callback function
  *
- * Asynchronously requests data at @uri by way of a #SoupRequest to WebKit's
- * default #SoupSession, incorporating both e_web_view_redirect_uri() and the
- * custom request handlers installed via e_web_view_install_request_handler().
+ * Asynchronously requests data at @uri as displaed in the @web_view.
  *
  * When the operation is finished, @callback will be called.  You can then
  * call e_web_view_request_finish() to get the result of the operation.
@@ -3429,52 +3940,41 @@ e_web_view_request (EWebView *web_view,
                     GAsyncReadyCallback callback,
                     gpointer user_data)
 {
-       SoupSession *session;
-       SoupRequest *request;
-       gchar *real_uri;
-       GSimpleAsyncResult *simple;
+       EContentRequest *content_request = NULL;
        AsyncContext *async_context;
-       GError *local_error = NULL;
+       GSList *link;
+       GTask *task;
 
        g_return_if_fail (E_IS_WEB_VIEW (web_view));
        g_return_if_fail (uri != NULL);
 
-       session = webkit_get_default_session ();
-
-       async_context = g_slice_new0 (AsyncContext);
-
-       simple = g_simple_async_result_new (
-               G_OBJECT (web_view), callback,
-               user_data, e_web_view_request);
+       for (link = web_view->priv->content_requests; link; link = g_slist_next (link)) {
+               EContentRequest *adept = link->data;
 
-       g_simple_async_result_set_check_cancellable (simple, cancellable);
+               if (!E_IS_CONTENT_REQUEST (adept) ||
+                   !e_content_request_can_process_uri (adept, uri))
+                       continue;
 
-       g_simple_async_result_set_op_res_gpointer (
-               simple, async_context, (GDestroyNotify) async_context_free);
-
-       real_uri = e_web_view_redirect_uri (web_view, uri);
-       request = soup_session_request (session, real_uri, &local_error);
-       g_free (real_uri);
-
-       /* Sanity check. */
-       g_return_if_fail (
-               ((request != NULL) && (local_error == NULL)) ||
-               ((request == NULL) && (local_error != NULL)));
+               content_request = adept;
+               break;
+       }
 
-       if (request != NULL) {
-               soup_request_send_async (
-                       request, cancellable,
-                       web_view_request_send_cb,
-                       g_object_ref (simple));
+       async_context = g_slice_new0 (AsyncContext);
+       async_context->uri = g_strdup (uri);
 
-               g_object_unref (request);
+       task = g_task_new (web_view, cancellable, callback, user_data);
+       g_task_set_task_data (task, async_context, async_context_free);
+       g_task_set_check_cancellable (task, TRUE);
 
+       if (content_request) {
+               async_context->content_request = g_object_ref (content_request);
+               g_task_run_in_thread (task, web_view_request_process_thread);
        } else {
-               g_simple_async_result_take_error (simple, local_error);
-               g_simple_async_result_complete_in_idle (simple);
+               g_task_return_new_error (task, G_IO_ERROR, G_IO_ERROR_FAILED,
+                       _("Cannot get URI '%s', do not know how to download it."), uri);
        }
 
-       g_object_unref (simple);
+       g_object_unref (task);
 }
 
 /**
@@ -3496,204 +3996,435 @@ e_web_view_request_finish (EWebView *web_view,
                            GAsyncResult *result,
                            GError **error)
 {
-       GSimpleAsyncResult *simple;
        AsyncContext *async_context;
 
-       g_return_val_if_fail (
-               g_simple_async_result_is_valid (
-               result, G_OBJECT (web_view), e_web_view_request), NULL);
-
-       simple = G_SIMPLE_ASYNC_RESULT (result);
-       async_context = g_simple_async_result_get_op_res_gpointer (simple);
+       g_return_val_if_fail (g_task_is_valid (result, web_view), NULL);
 
-       if (g_simple_async_result_propagate_error (simple, error))
+       if (!g_task_propagate_boolean (G_TASK (result), error))
                return NULL;
 
+       async_context = g_task_get_task_data (G_TASK (result));
+
        g_return_val_if_fail (async_context->input_stream != NULL, NULL);
 
        return g_object_ref (async_context->input_stream);
 }
 
+/**
+ * e_web_view_create_and_add_css_style_sheet:
+ * @web_view: an #EWebView
+ * @style_sheet_id: CSS style sheet's id
+ *
+ * Creates new CSS style sheet with given @style_sheel_id and inserts
+ * it into given @web_view document.
+ **/
 void
-e_web_view_install_request_handler (EWebView *web_view,
-                                    GType handler_type)
+e_web_view_create_and_add_css_style_sheet (EWebView *web_view,
+                                           const gchar *style_sheet_id)
 {
-       SoupSession *session;
+       GDBusProxy *web_extension;
+
+       g_return_if_fail (E_IS_WEB_VIEW (web_view));
+       g_return_if_fail (style_sheet_id && *style_sheet_id);
 
-       session = webkit_get_default_session ();
-       soup_session_add_feature_by_type (session, handler_type);
+       web_extension = e_web_view_get_web_extension_proxy (web_view);
+       if (web_extension) {
+               g_dbus_proxy_call (
+                       web_extension,
+                       "CreateAndAddCSSStyleSheet",
+                       g_variant_new (
+                               "(ts)",
+                               webkit_web_view_get_page_id (
+                                       WEBKIT_WEB_VIEW (web_view)),
+                               style_sheet_id),
+                       G_DBUS_CALL_FLAGS_NONE,
+                       -1,
+                       NULL,
+                       NULL,
+                       NULL);
+       }
 }
 
+/**
+ * e_web_view_add_css_rule_into_style_sheet:
+ * @web_view: an #EWebView
+ * @style_sheet_id: CSS style sheet's id
+ * @selector: CSS selector
+ * @style: style for given selector
+ *
+ * Insert new CSS rule (defined with @selector and @style) into CSS style sheet
+ * with given @style_sheet_id. If style sheet doesn't exist, it's created.
+ *
+ * The rule is inserted to every DOM document that is in page. That means also
+ * into DOM documents inside iframe elements.
+ **/
 void
-e_web_view_create_and_add_css_style_sheet (WebKitDOMDocument *document,
-                                           const gchar *style_sheet_id)
+e_web_view_add_css_rule_into_style_sheet (EWebView *web_view,
+                                          const gchar *style_sheet_id,
+                                          const gchar *selector,
+                                          const gchar *style)
 {
-       WebKitDOMElement *style_element;
+       GDBusProxy *web_extension;
 
-       style_element = webkit_dom_document_get_element_by_id (document, style_sheet_id);
+       g_return_if_fail (E_IS_WEB_VIEW (web_view));
+       g_return_if_fail (style_sheet_id && *style_sheet_id);
+       g_return_if_fail (selector && *selector);
+       g_return_if_fail (style && *style);
 
-       if (!style_element) {
-               WebKitDOMText *dom_text;
-               WebKitDOMHTMLHeadElement *head;
+       web_extension = e_web_view_get_web_extension_proxy (web_view);
+       if (web_extension) {
+               g_dbus_proxy_call (
+                       web_extension,
+                       "AddCSSRuleIntoStyleSheet",
+                       g_variant_new (
+                               "(tsss)",
+                               webkit_web_view_get_page_id (
+                                       WEBKIT_WEB_VIEW (web_view)),
+                               style_sheet_id,
+                               selector,
+                               style),
+                       G_DBUS_CALL_FLAGS_NONE,
+                       -1,
+                       NULL,
+                       NULL,
+                       NULL);
+       }
+}
 
-               dom_text = webkit_dom_document_create_text_node (document, "");
+/**
+ * e_web_view_get_document_uri_from_point:
+ * @web_view: an #EWebView
+ * @x: x-coordinate
+ * @y: y-coordinate
+ *
+ * Returns: A document URI which is under the @x, @y coordinates or %NULL,
+ * if there is none. Free the returned pointer with g_free() when done with it.
+ *
+ * Since: 3.22
+ **/
+gchar *
+e_web_view_get_document_uri_from_point (EWebView *web_view,
+                                       gint32 x,
+                                       gint32 y)
+{
+       GDBusProxy *web_extension;
+       GVariant *result;
+       GError *local_error = NULL;
 
-               /* Create new <style> element */
-               style_element = webkit_dom_document_create_element (document, "style", NULL);
-               webkit_dom_element_set_id (
-                       WEBKIT_DOM_ELEMENT (style_element),
-                       style_sheet_id);
-               webkit_dom_html_style_element_set_media (
-                       WEBKIT_DOM_HTML_STYLE_ELEMENT (style_element),
-                       "screen");
-               webkit_dom_node_append_child (
-                       WEBKIT_DOM_NODE (style_element),
-                       /* WebKit hack - we have to insert empty TextNode into style element */
-                       WEBKIT_DOM_NODE (dom_text),
-                       NULL);
+       g_return_val_if_fail (E_IS_WEB_VIEW (web_view), NULL);
 
-               head = webkit_dom_document_get_head (document);
+       web_extension = e_web_view_get_web_extension_proxy (web_view);
+       if (!web_extension)
+               return NULL;
 
-               webkit_dom_node_append_child (
-                       WEBKIT_DOM_NODE (head),
-                       WEBKIT_DOM_NODE (style_element),
-                       NULL);
+       result = g_dbus_proxy_call_sync (
+               web_extension,
+               "GetDocumentURIFromPoint",
+               g_variant_new (
+                       "(tii)",
+                       webkit_web_view_get_page_id (WEBKIT_WEB_VIEW (web_view)),
+                       x,
+                       y),
+               G_DBUS_CALL_FLAGS_NONE,
+               -1,
+               NULL,
+               &local_error);
+
+       if (local_error)
+               g_warning ("%s: Failed with error: %s", G_STRFUNC, local_error->message);
+
+       g_clear_error (&local_error);
+
+       if (result) {
+               gchar *uri = NULL;
+
+               g_variant_get (result, "(s)", &uri);
+               g_variant_unref (result);
+
+               if (g_strcmp0 (uri, "") == 0) {
+                       g_free (uri);
+                       uri = NULL;
+               }
 
-               g_object_unref (head);
-               g_object_unref (dom_text);
-               g_object_unref (style_element);
+               return uri;
        }
+
+       return NULL;
 }
 
 static void
-add_css_rule_into_style_sheet (WebKitDOMDocument *document,
-                               const gchar *style_sheet_id,
-                               const gchar *selector,
-                               const gchar *style)
+e_web_view_set_document_iframe_src_done_cb (GObject *source_object,
+                                           GAsyncResult *result,
+                                           gpointer user_data)
 {
-       WebKitDOMElement *style_element;
-       WebKitDOMStyleSheet *sheet;
-       WebKitDOMCSSRuleList *rules_list;
-       gint length, ii, selector_length;
-       gboolean removed = FALSE;
+       GVariant *variant;
+       GError *local_error = NULL;
 
-       g_return_if_fail (selector != NULL);
+       variant = g_dbus_proxy_call_finish (G_DBUS_PROXY (source_object), result, &local_error);
+       if (variant)
+               g_variant_unref (variant);
 
-       selector_length = strlen (selector);
-       style_element = webkit_dom_document_get_element_by_id (document, style_sheet_id);
+       if (local_error)
+               g_warning ("%s: Failed with error: %s", G_STRFUNC, local_error->message);
 
-       if (!style_element) {
-               e_web_view_create_and_add_css_style_sheet (document, style_sheet_id);
-               style_element = webkit_dom_document_get_element_by_id (document, style_sheet_id);
-       }
+       g_clear_error (&local_error);
+}
 
-       /* Get sheet that is associated with style element */
-       sheet = webkit_dom_html_style_element_get_sheet (WEBKIT_DOM_HTML_STYLE_ELEMENT (style_element));
+/**
+ * e_web_view_set_document_iframe_src:
+ * @web_view: an #EWebView
+ * @document_uri: a document URI for whose IFrame change the source
+ * @new_iframe_src: the source to change the IFrame to
+ *
+ * Change IFrame source for the given @document_uri IFrame
+ * to the @new_iframe_src.
+ *
+ * Since: 3.22
+ **/
+void
+e_web_view_set_document_iframe_src (EWebView *web_view,
+                                   const gchar *document_uri,
+                                   const gchar *new_iframe_src)
+{
+       GDBusProxy *web_extension;
 
-       rules_list = webkit_dom_css_style_sheet_get_css_rules (WEBKIT_DOM_CSS_STYLE_SHEET (sheet));
-       length = webkit_dom_css_rule_list_get_length (rules_list);
+       g_return_if_fail (E_IS_WEB_VIEW (web_view));
 
-       /* Check if rule exists */
-       for (ii = 0; ii < length && !removed; ii++) {
-               WebKitDOMCSSRule *rule;
-               gchar *rule_text = NULL;
+       web_extension = e_web_view_get_web_extension_proxy (web_view);
+       if (!web_extension)
+               return;
 
-               rule = webkit_dom_css_rule_list_item (rules_list, ii);
+       /* Cannot call this synchronously, blocking the local main loop, because the reload
+          can on the WebProcess side can be asking for a redirection policy, waiting
+          for a response which may be waiting in the blocked main loop. */
+       g_dbus_proxy_call (
+               web_extension,
+               "SetDocumentIFrameSrc",
+               g_variant_new (
+                       "(tss)",
+                       webkit_web_view_get_page_id (WEBKIT_WEB_VIEW (web_view)),
+                       document_uri,
+                       new_iframe_src),
+               G_DBUS_CALL_FLAGS_NONE,
+               -1,
+               NULL,
+               e_web_view_set_document_iframe_src_done_cb, NULL);
+}
 
-               g_return_if_fail (WEBKIT_DOM_IS_CSS_RULE (rule));
+/**
+ * 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_position is the place of the element within the web page, already
+ * accounting scrollbar positions.
+ *
+ * See: e_web_view_register_element_clicked, e_web_view_unregister_element_clicked
+ *
+ * Since: 3.22
+ **/
 
-               rule_text = webkit_dom_css_rule_get_css_text (rule);
+/**
+ * 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;
 
-               /* Find the start of the style => end of the selector */
-               if (rule_text && selector && g_str_has_prefix (rule_text, selector) &&
-                   rule_text[selector_length] == ' ' && rule_text[selector_length + 1] == '{') {
-                       /* If exists remove it */
-                       webkit_dom_css_style_sheet_remove_rule (
-                               WEBKIT_DOM_CSS_STYLE_SHEET (sheet),
-                               ii, NULL);
-                       length--;
-                       removed = TRUE;
+       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) {
+                               /* Callback is already registered, but re-register it, in case the page
+                                  was changed dynamically and new elements with the given call are added. */
+                               web_view_register_element_clicked_hfunc ((gpointer) element_class, cbs, 
web_view);
+                               return;
+                       }
                }
+       }
+
+       ecd = g_new0 (ElementClickedData, 1);
+       ecd->callback = callback;
+       ecd->user_data = user_data;
+
+       if (!cbs) {
+               cbs = g_ptr_array_new_full (1, g_free);
+               g_ptr_array_add (cbs, ecd);
 
-               g_free (rule_text);
-               g_object_unref (rule);
+               g_hash_table_insert (web_view->priv->element_clicked_cbs, g_strdup (element_class), cbs);
+       } else {
+               g_ptr_array_add (cbs, ecd);
        }
 
-       g_object_unref (rules_list);
+       /* Dynamically changing page can call this multiple times; re-register all classes */
+       g_hash_table_foreach (web_view->priv->element_clicked_cbs, web_view_register_element_clicked_hfunc, 
web_view);
+}
+
+/**
+ * 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;
 
-       /* Insert the rule at the end, so it will override previously inserted */
-       webkit_dom_css_style_sheet_add_rule (
-               WEBKIT_DOM_CSS_STYLE_SHEET (sheet), selector, style, length, NULL);
+       for (ii = 0; ii < cbs->len; ii++) {
+               ecd = g_ptr_array_index (cbs, ii);
 
-       g_object_unref (sheet);
-       g_object_unref (style_element);
+               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;
+               }
+       }
 }
 
-static void
-add_css_rule_into_style_sheet_recursive (WebKitDOMDocument *document,
-                                         const gchar *style_sheet_id,
-                                         const gchar *selector,
-                                         const gchar *style)
+void
+e_web_view_set_element_hidden (EWebView *web_view,
+                              const gchar *element_id,
+                              gboolean hidden)
 {
-       WebKitDOMNodeList *frames;
-       gint ii, length;
+       GDBusProxy *web_extension;
 
-       /* Add rule to document */
-       add_css_rule_into_style_sheet (
-               document,
-               style_sheet_id,
-               selector,
-               style);
+       g_return_if_fail (E_IS_WEB_VIEW (web_view));
+       g_return_if_fail (element_id && *element_id);
 
-       frames = webkit_dom_document_query_selector_all (document, "iframe", NULL);
-       length = webkit_dom_node_list_get_length (frames);
+       web_extension = e_web_view_get_web_extension_proxy (web_view);
+       if (!web_extension)
+               return;
 
-       /* Add rules to every sub document */
-       for (ii = 0; ii < length; ii++) {
-               WebKitDOMDocument *iframe_document;
-               WebKitDOMNode *node;
+       g_dbus_proxy_call (
+               web_extension,
+               "SetElementHidden",
+               g_variant_new (
+                       "(tsb)",
+                       webkit_web_view_get_page_id (WEBKIT_WEB_VIEW (web_view)),
+                       element_id,
+                       hidden),
+               G_DBUS_CALL_FLAGS_NONE,
+               -1,
+               NULL,
+               NULL,
+               NULL);
+}
 
-               node = webkit_dom_node_list_item (frames, ii);
-               iframe_document = webkit_dom_html_iframe_element_get_content_document (
-                       WEBKIT_DOM_HTML_IFRAME_ELEMENT (node));
+void
+e_web_view_set_element_style_property (EWebView *web_view,
+                                      const gchar *element_id,
+                                      const gchar *property_name,
+                                      const gchar *value,
+                                      const gchar *priority)
+{
+       GDBusProxy *web_extension;
 
-               add_css_rule_into_style_sheet_recursive (
-                       iframe_document,
-                       style_sheet_id,
-                       selector,
-                       style);
-               g_object_unref (node);
-       }
-       g_object_unref (frames);
+       g_return_if_fail (E_IS_WEB_VIEW (web_view));
+       g_return_if_fail (element_id && *element_id);
+       g_return_if_fail (property_name && *property_name);
+
+       web_extension = e_web_view_get_web_extension_proxy (web_view);
+       if (!web_extension)
+               return;
+
+       g_dbus_proxy_call (
+               web_extension,
+               "SetElementStyleProperty",
+               g_variant_new (
+                       "(tssss)",
+                       webkit_web_view_get_page_id (WEBKIT_WEB_VIEW (web_view)),
+                       element_id,
+                       property_name,
+                       value ? value : "",
+                       priority ? priority : ""),
+               G_DBUS_CALL_FLAGS_NONE,
+               -1,
+               NULL,
+               NULL,
+               NULL);
 }
 
-/**
- * e_web_view_add_css_rule_into_style_sheet:
- * @web_view: an #EWebView
- * @style_sheet_id: CSS style sheet's id
- * @selector: CSS selector
- * @style: style for given selector
- *
- * Insert new CSS rule (defined with @selector and @style) into CSS style sheet
- * with given @style_sheet_id. If style sheet doesn't exist, it's created.
- *
- * The rule is inserted to every DOM document that is in page. That means also
- * into DOM documents inside iframe elements.
- **/
 void
-e_web_view_add_css_rule_into_style_sheet (EWebView *view,
-                                          const gchar *style_sheet_id,
-                                          const gchar *selector,
-                                          const gchar *style)
+e_web_view_set_element_attribute (EWebView *web_view,
+                                 const gchar *element_id,
+                                 const gchar *namespace_uri,
+                                 const gchar *qualified_name,
+                                 const gchar *value)
 {
-       g_return_if_fail (E_IS_WEB_VIEW (view));
-       g_return_if_fail (style_sheet_id && *style_sheet_id);
-       g_return_if_fail (selector && *selector);
-       g_return_if_fail (style && *style);
+       GDBusProxy *web_extension;
 
-       add_css_rule_into_style_sheet_recursive (
-               webkit_web_view_get_dom_document (WEBKIT_WEB_VIEW (view)),
-               style_sheet_id,
-               selector,
-               style);
+       g_return_if_fail (E_IS_WEB_VIEW (web_view));
+       g_return_if_fail (element_id && *element_id);
+       g_return_if_fail (qualified_name && *qualified_name);
+
+       web_extension = e_web_view_get_web_extension_proxy (web_view);
+       if (!web_extension)
+               return;
+
+       g_dbus_proxy_call (
+               web_extension,
+               "SetElementAttribute",
+               g_variant_new (
+                       "(tssss)",
+                       webkit_web_view_get_page_id (WEBKIT_WEB_VIEW (web_view)),
+                       element_id,
+                       namespace_uri ? namespace_uri : "",
+                       qualified_name,
+                       value ? value : ""),
+               G_DBUS_CALL_FLAGS_NONE,
+               -1,
+               NULL,
+               NULL,
+               NULL);
 }
diff --git a/e-util/e-web-view.h b/e-util/e-web-view.h
index 545c3bb..62203d2 100644
--- a/e-util/e-web-view.h
+++ b/e-util/e-web-view.h
@@ -28,8 +28,9 @@
 #ifndef E_WEB_VIEW_H
 #define E_WEB_VIEW_H
 
-#include <webkit/webkit.h>
+#include <webkit2/webkit2.h>
 #include <e-util/e-activity.h>
+#include <e-util/e-content-request.h>
 
 /* Standard GObject macros */
 #define E_TYPE_WEB_VIEW \
@@ -56,6 +57,21 @@ typedef struct _EWebView EWebView;
 typedef struct _EWebViewClass EWebViewClass;
 typedef struct _EWebViewPrivate EWebViewPrivate;
 
+typedef enum {
+       CID_URI_SCHEME,
+       FILE_URI_SCHEME,
+       MAIL_URI_SCHEME,
+       EVO_HTTP_URI_SCHEME,
+       EVO_HTTPS_URI_SCHEME,
+       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;
@@ -78,8 +94,6 @@ struct _EWebViewClass {
                                                 const gchar *load_string);
        void            (*load_uri)             (EWebView *web_view,
                                                 const gchar *load_uri);
-       gchar *         (*redirect_uri)         (EWebView *web_view,
-                                                const gchar *uri);
        gchar *         (*suggest_filename)     (EWebView *web_view,
                                                 const gchar *uri);
        void            (*set_fonts)            (EWebView *web_view,
@@ -97,21 +111,48 @@ struct _EWebViewClass {
        void            (*update_actions)       (EWebView *web_view);
        gboolean        (*process_mailto)       (EWebView *web_view,
                                                 const gchar *mailto_uri);
+       void            (*uri_requested)        (EWebView *web_view,
+                                                const gchar *uri,
+                                                gchar **redirect_to_uri);
 };
 
 GType          e_web_view_get_type             (void) G_GNUC_CONST;
 GtkWidget *    e_web_view_new                  (void);
+WebKitSettings *
+               e_web_view_get_default_webkit_settings
+                                               (void);
+void           e_web_view_register_content_request_for_scheme
+                                               (EWebView *web_view,
+                                                const gchar *scheme,
+                                                EContentRequest *content_request);
+void           e_web_view_update_fonts_settings
+                                               (GSettings *font_settings,
+                                                GSettings *aliasing_settings,
+                                                PangoFontDescription *ms_font,
+                                                PangoFontDescription *vw_font,
+                                                GtkWidget *view_widget);
 void           e_web_view_clear                (EWebView *web_view);
 void           e_web_view_load_string          (EWebView *web_view,
                                                 const gchar *string);
 void           e_web_view_load_uri             (EWebView *web_view,
                                                 const gchar *uri);
-gchar *                e_web_view_redirect_uri         (EWebView *web_view,
-                                                const gchar *uri);
 gchar *                e_web_view_suggest_filename     (EWebView *web_view,
                                                 const gchar *uri);
 void           e_web_view_reload               (EWebView *web_view);
-gchar *                e_web_view_get_html             (EWebView *web_view);
+void           e_web_view_get_content_html     (EWebView *web_view,
+                                                GCancellable *cancellable,
+                                                GAsyncReadyCallback callback,
+                                                gpointer user_data);
+gchar *                e_web_view_get_content_html_finish
+                                               (EWebView *web_view,
+                                                GAsyncResult *result,
+                                                GError **error);
+gchar *                e_web_view_get_content_html_sync
+                                               (EWebView *web_view,
+                                                GCancellable *cancellable,
+                                                GError **error);
+GDBusProxy *   e_web_view_get_web_extension_proxy
+                                               (EWebView *web_view);
 gboolean       e_web_view_get_caret_mode       (EWebView *web_view);
 void           e_web_view_set_caret_mode       (EWebView *web_view,
                                                 gboolean caret_mode);
@@ -180,7 +221,19 @@ void               e_web_view_status_message       (EWebView *web_view,
                                                 const gchar *status_message);
 void           e_web_view_stop_loading         (EWebView *web_view);
 void           e_web_view_update_actions       (EWebView *web_view);
-gchar *                e_web_view_get_selection_html   (EWebView *web_view);
+void           e_web_view_get_selection_content_html
+                                               (EWebView *web_view,
+                                                GCancellable *cancellable,
+                                                GAsyncReadyCallback callback,
+                                                gpointer user_data);
+gchar *                e_web_view_get_selection_content_html_finish
+                                               (EWebView *web_view,
+                                                GAsyncResult *result,
+                                                GError **error);
+gchar *                e_web_view_get_selection_content_html_sync
+                                               (EWebView *web_view,
+                                                GCancellable *cancellable,
+                                                GError **error);
 void           e_web_view_update_fonts         (EWebView *web_view);
 void           e_web_view_cursor_image_copy    (EWebView *web_view);
 void           e_web_view_cursor_image_save    (EWebView *web_view);
@@ -196,7 +249,7 @@ void                e_web_view_install_request_handler
                                                (EWebView *web_view,
                                                 GType handler_type);
 void           e_web_view_create_and_add_css_style_sheet
-                                               (WebKitDOMDocument* document,
+                                               (EWebView *web_view,
                                                 const gchar *style_sheet_id);
 void           e_web_view_add_css_rule_into_style_sheet
                                                (EWebView *web_view,
@@ -205,6 +258,39 @@ void               e_web_view_add_css_rule_into_style_sheet
                                                 const gchar *style);
 const gchar *  e_web_view_get_citation_color_for_level
                                                (gint level);
+gchar *                e_web_view_get_document_uri_from_point
+                                               (EWebView *web_view,
+                                                gint32 x,
+                                                gint32 y);
+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);
+void           e_web_view_set_element_hidden   (EWebView *web_view,
+                                                const gchar *element_id,
+                                                gboolean hidden);
+void           e_web_view_set_element_style_property
+                                               (EWebView *web_view,
+                                                const gchar *element_id,
+                                                const gchar *property_name,
+                                                const gchar *value,
+                                                const gchar *priority);
+void           e_web_view_set_element_attribute
+                                               (EWebView *web_view,
+                                                const gchar *element_id,
+                                                const gchar *namespace_uri,
+                                                const gchar *qualified_name,
+                                                const gchar *value);
 G_END_DECLS
 
 #endif /* E_WEB_VIEW_H */
diff --git a/e-util/test-html-editor-units-utils.c b/e-util/test-html-editor-units-utils.c
new file mode 100644
index 0000000..911cc98
--- /dev/null
+++ b/e-util/test-html-editor-units-utils.c
@@ -0,0 +1,1045 @@
+/*
+ * Copyright (C) 2016 Red Hat, Inc. (www.redhat.com)
+ *
+ * This library 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 library 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 Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <string.h>
+#include <stdlib.h>
+
+#include "e-util/e-util.h"
+
+#include "test-html-editor-units-utils.h"
+
+static guint event_processing_delay_ms = 5;
+
+void
+test_utils_set_event_processing_delay_ms (guint value)
+{
+       event_processing_delay_ms = value;
+}
+
+guint
+test_utils_get_event_processing_delay_ms (void)
+{
+       return event_processing_delay_ms;
+}
+
+typedef struct _UndoContent {
+       gchar *html;
+       gchar *plain;
+} UndoContent;
+
+static UndoContent *
+undo_content_new (TestFixture *fixture)
+{
+       EContentEditor *cnt_editor;
+       UndoContent *uc;
+
+       g_return_val_if_fail (fixture != NULL, NULL);
+       g_return_val_if_fail (E_IS_HTML_EDITOR (fixture->editor), NULL);
+
+       cnt_editor = e_html_editor_get_content_editor (fixture->editor);
+
+       uc = g_new0 (UndoContent, 1);
+       uc->html = e_content_editor_get_content (cnt_editor, E_CONTENT_EDITOR_GET_PROCESSED | 
E_CONTENT_EDITOR_GET_TEXT_HTML, NULL, NULL);
+       uc->plain = e_content_editor_get_content (cnt_editor, E_CONTENT_EDITOR_GET_PROCESSED | 
E_CONTENT_EDITOR_GET_TEXT_PLAIN, NULL, NULL);
+
+       g_warn_if_fail (uc->html != NULL);
+       g_warn_if_fail (uc->plain != NULL);
+
+       return uc;
+}
+
+static void
+undo_content_free (gpointer ptr)
+{
+       UndoContent *uc = ptr;
+
+       if (uc) {
+               g_free (uc->html);
+               g_free (uc->plain);
+               g_free (uc);
+       }
+}
+
+static gboolean
+undo_content_test (TestFixture *fixture,
+                  const UndoContent *uc,
+                  gint cmd_index)
+{
+       EContentEditor *cnt_editor;
+       gchar *text;
+
+       g_return_val_if_fail (fixture != NULL, FALSE);
+       g_return_val_if_fail (E_IS_HTML_EDITOR (fixture->editor), FALSE);
+       g_return_val_if_fail (uc != NULL, FALSE);
+
+       cnt_editor = e_html_editor_get_content_editor (fixture->editor);
+
+       text = e_content_editor_get_content (cnt_editor, E_CONTENT_EDITOR_GET_PROCESSED | 
E_CONTENT_EDITOR_GET_TEXT_HTML, NULL, NULL);
+       g_return_val_if_fail (text != NULL, FALSE);
+
+       if (!test_utils_html_equal (fixture, text, uc->html)) {
+               g_warning ("%s: returned HTML\n---%s---\n and expected HTML\n---%s---\n do not match at 
command %d", G_STRFUNC, text, uc->html, cmd_index);
+               g_free (text);
+               return FALSE;
+       }
+
+       g_free (text);
+
+       text = e_content_editor_get_content (cnt_editor, E_CONTENT_EDITOR_GET_PROCESSED | 
E_CONTENT_EDITOR_GET_TEXT_PLAIN, NULL, NULL);
+       g_return_val_if_fail (text != NULL, FALSE);
+
+       if (!test_utils_html_equal (fixture, text, uc->plain)) {
+               g_warning ("%s: returned Plain\n---%s---\n and expected Plain\n---%s---\n do not match at 
command %d", G_STRFUNC, text, uc->plain, cmd_index);
+               g_free (text);
+               return FALSE;
+       }
+
+       g_free (text);
+
+       return TRUE;
+}
+
+static gboolean
+test_utils_web_process_crashed_cb (WebKitWebView *web_view,
+                                  gpointer user_data)
+{
+       g_warning ("%s:", G_STRFUNC);
+
+       return FALSE;
+}
+
+typedef struct _CreateData {
+       gpointer async_data;
+       TestFixture *fixture;
+} CreateData;
+
+static void
+test_utils_html_editor_created_cb (GObject *source_object,
+                                  GAsyncResult *result,
+                                  gpointer user_data)
+{
+       CreateData *create_data = user_data;
+       TestFixture *fixture;
+       EContentEditor *cnt_editor;
+       GtkWidget *html_editor;
+       GError *error = NULL;
+
+       g_return_if_fail (create_data != NULL);
+
+       fixture = create_data->fixture;
+
+       html_editor = e_html_editor_new_finish (result, &error);
+       if (error) {
+               g_warning ("%s: Failed to create editor: %s", G_STRFUNC, error->message);
+               g_clear_error (&error);
+               return;
+       }
+
+       fixture->editor = E_HTML_EDITOR (html_editor);
+
+       g_object_set (G_OBJECT (fixture->editor),
+               "halign", GTK_ALIGN_FILL,
+               "hexpand", TRUE,
+               "valign", GTK_ALIGN_FILL,
+               "vexpand", TRUE,
+               NULL);
+       gtk_widget_show (GTK_WIDGET (fixture->editor));
+       gtk_container_add (GTK_CONTAINER (fixture->window), GTK_WIDGET (fixture->editor));
+
+       /* Make sure this is off */
+       test_utils_fixture_change_setting_boolean (fixture,
+               "org.gnome.evolution.mail", "prompt-on-composer-mode-switch", FALSE);
+
+       cnt_editor = e_html_editor_get_content_editor (fixture->editor);
+       g_object_set (G_OBJECT (cnt_editor),
+               "halign", GTK_ALIGN_FILL,
+               "hexpand", TRUE,
+               "valign", GTK_ALIGN_FILL,
+               "vexpand", TRUE,
+               "height-request", 150,
+               NULL);
+
+       g_signal_connect (cnt_editor, "web-process-crashed",
+               G_CALLBACK (test_utils_web_process_crashed_cb), NULL);
+
+       gtk_window_set_focus (GTK_WINDOW (fixture->window), GTK_WIDGET (cnt_editor));
+       gtk_widget_show (fixture->window);
+
+       test_utils_async_call_finish (create_data->async_data);
+}
+
+void
+test_utils_fixture_set_up (TestFixture *fixture,
+                          gconstpointer user_data)
+{
+       CreateData create_data;
+
+       fixture->window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
+       fixture->undo_stack = NULL;
+       fixture->key_state = 0;
+
+       create_data.async_data = test_utils_async_call_prepare ();
+       create_data.fixture = fixture;
+
+       e_html_editor_new (test_utils_html_editor_created_cb, &create_data);
+
+       test_utils_async_call_wait (create_data.async_data, 5);
+
+       g_warn_if_fail (fixture->editor != NULL);
+       g_warn_if_fail (E_IS_HTML_EDITOR (fixture->editor));
+}
+
+static void
+free_old_settings (gpointer ptr)
+{
+       TestSettings *data = ptr;
+
+       if (data) {
+               GSettings *settings;
+
+               settings = e_util_ref_settings (data->schema);
+               g_settings_set_value (settings, data->key, data->old_value);
+               g_clear_object (&settings);
+
+               g_variant_unref (data->old_value);
+               g_free (data->schema);
+               g_free (data->key);
+               g_free (data);
+       }
+}
+
+void
+test_utils_fixture_tear_down (TestFixture *fixture,
+                             gconstpointer user_data)
+{
+       gtk_widget_destroy (GTK_WIDGET (fixture->window));
+       fixture->editor = NULL;
+
+       g_slist_free_full (fixture->settings, free_old_settings);
+       fixture->settings = NULL;
+
+       g_slist_free_full (fixture->undo_stack, undo_content_free);
+       fixture->undo_stack = NULL;
+}
+
+void
+test_utils_fixture_change_setting (TestFixture *fixture,
+                                  const gchar *schema,
+                                  const gchar *key,
+                                  GVariant *value)
+{
+       TestSettings *data;
+       GSettings *settings;
+
+       g_return_if_fail (fixture != NULL);
+       g_return_if_fail (schema != NULL);
+       g_return_if_fail (key != NULL);
+       g_return_if_fail (value != NULL);
+
+       g_variant_ref_sink (value);
+
+       settings = e_util_ref_settings (schema);
+
+       data = g_new0 (TestSettings, 1);
+       data->schema = g_strdup (schema);
+       data->key = g_strdup (key);
+       data->old_value = g_variant_ref_sink (g_settings_get_value (settings, key));
+
+       /* Use prepend, thus the restore comes in the opposite order, thus a change
+          of the same key is not a problem. */
+       fixture->settings = g_slist_prepend (fixture->settings, data);
+
+       g_settings_set_value (settings, key, value);
+
+       g_clear_object (&settings);
+       g_variant_unref (value);
+}
+
+void
+test_utils_fixture_change_setting_boolean (TestFixture *fixture,
+                                          const gchar *schema,
+                                          const gchar *key,
+                                          gboolean value)
+{
+       test_utils_fixture_change_setting (fixture, schema, key, g_variant_new_boolean (value));
+}
+
+void
+test_utils_fixture_change_setting_int32 (TestFixture *fixture,
+                                        const gchar *schema,
+                                        const gchar *key,
+                                        gint value)
+{
+       test_utils_fixture_change_setting (fixture, schema, key, g_variant_new_int32 (value));
+}
+
+void
+test_utils_fixture_change_setting_string (TestFixture *fixture,
+                                         const gchar *schema,
+                                         const gchar *key,
+                                         const gchar *value)
+{
+       test_utils_fixture_change_setting (fixture, schema, key, g_variant_new_string (value));
+}
+
+static void
+test_utils_flush_main_context (void)
+{
+       GMainContext *main_context;
+
+       main_context = g_main_context_default ();
+
+       while (g_main_context_pending (main_context)) {
+               g_main_context_iteration (main_context, FALSE);
+       }
+}
+
+gpointer
+test_utils_async_call_prepare (void)
+{
+       return g_main_loop_new (NULL, FALSE);
+}
+
+typedef struct _AsynCallData {
+       GMainLoop *loop;
+       gboolean timeout_reached;
+} AsyncCallData;
+
+static gboolean
+test_utils_async_call_timeout_reached_cb (gpointer user_data)
+{
+       AsyncCallData *async_call_data = user_data;
+
+       g_return_val_if_fail (async_call_data != NULL, FALSE);
+       g_return_val_if_fail (async_call_data->loop != NULL, FALSE);
+       g_return_val_if_fail (!async_call_data->timeout_reached, FALSE);
+
+       if (!g_source_is_destroyed (g_main_current_source ())) {
+               async_call_data->timeout_reached = TRUE;
+               g_main_loop_quit (async_call_data->loop);
+       }
+
+       return FALSE;
+}
+
+gboolean
+test_utils_async_call_wait (gpointer async_data,
+                           guint timeout_seconds)
+{
+       GMainLoop *loop = async_data;
+       AsyncCallData async_call_data;
+       GSource *source = NULL;
+
+       g_return_val_if_fail (loop != NULL, FALSE);
+
+       async_call_data.loop = loop;
+       async_call_data.timeout_reached = FALSE;
+
+       /* 0 is to wait forever */
+       if (timeout_seconds > 0) {
+               source = g_timeout_source_new_seconds (timeout_seconds);
+               g_source_set_callback (source, test_utils_async_call_timeout_reached_cb, &async_call_data, 
NULL);
+               g_source_attach (source, NULL);
+       }
+
+       g_main_loop_run (loop);
+
+       if (source) {
+               g_source_destroy (source);
+               g_source_unref (source);
+       }
+
+       test_utils_flush_main_context ();
+
+       g_main_loop_unref (loop);
+
+       return !async_call_data.timeout_reached;
+}
+
+gboolean
+test_utils_async_call_finish (gpointer async_data)
+{
+       GMainLoop *loop = async_data;
+
+       g_return_val_if_fail (loop != NULL, FALSE);
+
+       g_main_loop_quit (loop);
+
+       return FALSE;
+}
+
+gboolean
+test_utils_wait_milliseconds (guint milliseconds)
+{
+       gpointer async_data;
+
+       async_data = test_utils_async_call_prepare ();
+       g_timeout_add (milliseconds, test_utils_async_call_finish, async_data);
+
+       return test_utils_async_call_wait (async_data, milliseconds / 1000 + 1);
+}
+
+static void
+test_utils_send_key_event (GtkWidget *widget,
+                          GdkEventType type,
+                          guint keyval,
+                          guint state)
+{
+       GdkKeymap *keymap;
+       GdkKeymapKey *keys = NULL;
+       gint n_keys;
+       GdkEvent *event;
+
+       g_return_if_fail (GTK_IS_WIDGET (widget));
+
+       event = gdk_event_new (type);
+       event->key.is_modifier =
+               keyval == GDK_KEY_Shift_L ||
+               keyval == GDK_KEY_Shift_R ||
+               keyval == GDK_KEY_Control_L ||
+               keyval == GDK_KEY_Control_R ||
+               keyval == GDK_KEY_Alt_L ||
+               keyval == GDK_KEY_Alt_R;
+       event->key.keyval = keyval;
+       event->key.state = state;
+       event->key.window = g_object_ref (gtk_widget_get_window (widget));
+       event->key.send_event = TRUE;
+       event->key.length = 0;
+       event->key.string = NULL;
+       event->key.hardware_keycode = 0;
+       event->key.group = 0;
+       event->key.time = GDK_CURRENT_TIME;
+
+       gdk_event_set_device (event, gdk_seat_get_keyboard (gdk_display_get_default_seat 
(gtk_widget_get_display (widget))));
+
+       keymap = gdk_keymap_get_for_display (gtk_widget_get_display (widget));
+       if (gdk_keymap_get_entries_for_keyval (keymap, keyval, &keys, &n_keys)) {
+               if (n_keys > 0) {
+                       event->key.hardware_keycode = keys[0].keycode;
+                       event->key.group = keys[0].group;
+               }
+
+               g_free (keys);
+       }
+
+       gtk_main_do_event (event);
+
+       test_utils_wait_milliseconds (event_processing_delay_ms);
+
+       gdk_event_free (event);
+}
+
+gboolean
+test_utils_type_text (TestFixture *fixture,
+                     const gchar *text)
+{
+       GtkWidget *widget;
+
+       g_return_val_if_fail (fixture != NULL, FALSE);
+       g_return_val_if_fail (E_IS_HTML_EDITOR (fixture->editor), FALSE);
+
+       widget = GTK_WIDGET (e_html_editor_get_content_editor (fixture->editor));
+       g_return_val_if_fail (GTK_IS_WIDGET (widget), FALSE);
+
+       g_return_val_if_fail (text != NULL, FALSE);
+       g_return_val_if_fail (g_utf8_validate (text, -1, NULL), FALSE);
+
+       while (*text) {
+               guint keyval;
+               gunichar unichar;
+
+               unichar = g_utf8_get_char (text);
+               text = g_utf8_next_char (text);
+
+               switch (unichar) {
+               case '\n':
+                       keyval = GDK_KEY_Return;
+                       break;
+               case '\t':
+                       keyval = GDK_KEY_Tab;
+                       break;
+               case '\b':
+                       keyval = GDK_KEY_BackSpace;
+                       break;
+               default:
+                       keyval = gdk_unicode_to_keyval (unichar);
+                       break;
+               }
+
+               test_utils_send_key_event (widget, GDK_KEY_PRESS, keyval, fixture->key_state);
+               test_utils_send_key_event (widget, GDK_KEY_RELEASE, keyval, fixture->key_state);
+       }
+
+       test_utils_wait_milliseconds (event_processing_delay_ms);
+
+       return TRUE;
+}
+
+static void
+sync_wrapper_result_callback (GObject *source_object,
+                             GAsyncResult *result,
+                             gpointer user_data)
+{
+       GAsyncResult **out_async_result = user_data;
+
+       g_return_if_fail (out_async_result != NULL);
+       g_return_if_fail (*out_async_result == NULL);
+
+       *out_async_result = g_object_ref (result);
+}
+
+/* Wraps GDBusProxy synchronous call into an asynchronous without blocking
+   the main context, thus there is no freeze when this is called in the UI
+   process and the WebProcess also does its own IPC call. */
+static GVariant *
+g_dbus_proxy_call_sync_wrapper (GDBusProxy *proxy,
+                               const gchar *method_name,
+                               GVariant *parameters,
+                               GDBusCallFlags flags,
+                               gint timeout_msec,
+                               GCancellable *cancellable,
+                               GError **error)
+{
+       GAsyncResult *async_result = NULL;
+       GVariant *var_result;
+
+       g_return_val_if_fail (G_IS_DBUS_PROXY (proxy), NULL);
+       g_return_val_if_fail (method_name != NULL, NULL);
+
+       g_dbus_proxy_call (
+               proxy, method_name, parameters, flags, timeout_msec, cancellable,
+               sync_wrapper_result_callback, &async_result);
+
+       while (!async_result) {
+               g_main_context_iteration (NULL, TRUE);
+       }
+
+       var_result = g_dbus_proxy_call_finish (proxy, async_result, error);
+
+       g_clear_object (&async_result);
+
+       return var_result;
+}
+
+gboolean
+test_utils_html_equal (TestFixture *fixture,
+                      const gchar *html1,
+                      const gchar *html2)
+{
+       EContentEditor *cnt_editor;
+       GDBusProxy *web_extension = NULL;
+       GVariant *result;
+       GError *error = NULL;
+       gboolean html_equal = FALSE;
+
+       g_return_val_if_fail (fixture != NULL, FALSE);
+       g_return_val_if_fail (E_IS_HTML_EDITOR (fixture->editor), FALSE);
+       g_return_val_if_fail (html1 != NULL, FALSE);
+       g_return_val_if_fail (html2 != NULL, FALSE);
+
+       cnt_editor = e_html_editor_get_content_editor (fixture->editor);
+       g_return_val_if_fail (cnt_editor != NULL, FALSE);
+
+       g_object_get (cnt_editor, "web-extension", &web_extension, NULL);
+
+       g_return_val_if_fail (G_IS_DBUS_PROXY (web_extension), FALSE);
+
+       result = g_dbus_proxy_call_sync_wrapper (
+               web_extension,
+               "TestHTMLEqual",
+               g_variant_new ("(tss)", webkit_web_view_get_page_id (WEBKIT_WEB_VIEW (cnt_editor)), html1, 
html2),
+               G_DBUS_CALL_FLAGS_NONE,
+               -1,
+               NULL,
+               &error);
+       g_assert_no_error (error);
+
+       g_clear_error (&error);
+
+       g_return_val_if_fail (result != NULL, FALSE);
+
+       g_variant_get (result, "(b)", &html_equal);
+       g_variant_unref (result);
+
+       return html_equal;
+}
+
+static gboolean
+test_utils_process_sequence (TestFixture *fixture,
+                            const gchar *sequence)
+{
+       GtkWidget *widget;
+       const gchar *seq;
+       guint keyval;
+       gboolean success = TRUE;
+
+       g_return_val_if_fail (fixture != NULL, FALSE);
+       g_return_val_if_fail (E_IS_HTML_EDITOR (fixture->editor), FALSE);
+       g_return_val_if_fail (sequence != NULL, FALSE);
+
+       widget = GTK_WIDGET (e_html_editor_get_content_editor (fixture->editor));
+       g_return_val_if_fail (GTK_IS_WIDGET (widget), FALSE);
+
+       for (seq = sequence; *seq && success; seq++) {
+               gboolean call_press = TRUE, call_release = TRUE;
+               guint change_state = fixture->key_state;
+
+               switch (*seq) {
+               case 'S': /* Shift key press */
+                       keyval = GDK_KEY_Shift_L;
+
+                       if ((fixture->key_state & GDK_SHIFT_MASK) != 0) {
+                               success = FALSE;
+                               g_warning ("%s: Shift is already pressed", G_STRFUNC);
+                       } else {
+                               change_state |= GDK_SHIFT_MASK;
+                       }
+                       call_release = FALSE;
+                       break;
+               case 's': /* Shift key release */
+                       keyval = GDK_KEY_Shift_L;
+
+                       if ((fixture->key_state & GDK_SHIFT_MASK) == 0) {
+                               success = FALSE;
+                               g_warning ("%s: Shift is already released", G_STRFUNC);
+                       } else {
+                               change_state &= ~GDK_SHIFT_MASK;
+                       }
+                       call_press = FALSE;
+                       break;
+               case 'C': /* Ctrl key press */
+                       keyval = GDK_KEY_Control_L;
+
+                       if ((fixture->key_state & GDK_CONTROL_MASK) != 0) {
+                               success = FALSE;
+                               g_warning ("%s: Control is already pressed", G_STRFUNC);
+                       } else {
+                               change_state |= GDK_CONTROL_MASK;
+                       }
+                       call_release = FALSE;
+                       break;
+               case 'c': /* Ctrl key release */
+                       keyval = GDK_KEY_Control_L;
+
+                       if ((fixture->key_state & GDK_CONTROL_MASK) == 0) {
+                               success = FALSE;
+                               g_warning ("%s: Control is already released", G_STRFUNC);
+                       } else {
+                               change_state &= ~GDK_CONTROL_MASK;
+                       }
+                       call_press = FALSE;
+                       break;
+               case 'A': /* Alt key press */
+                       keyval = GDK_KEY_Alt_L;
+
+                       if ((fixture->key_state & GDK_MOD1_MASK) != 0) {
+                               success = FALSE;
+                               g_warning ("%s: Alt is already pressed", G_STRFUNC);
+                       } else {
+                               change_state |= GDK_MOD1_MASK;
+                       }
+                       call_release = FALSE;
+                       break;
+               case 'a': /* Alt key release */
+                       keyval = GDK_KEY_Alt_L;
+
+                       if ((fixture->key_state & GDK_MOD1_MASK) == 0) {
+                               success = FALSE;
+                               g_warning ("%s: Alt is already released", G_STRFUNC);
+                       } else {
+                               change_state &= ~GDK_MOD1_MASK;
+                       }
+                       call_press = FALSE;
+                       break;
+               case 'h': /* Home key press + release */
+                       keyval = GDK_KEY_Home;
+                       break;
+               case 'e': /* End key press + release */
+                       keyval = GDK_KEY_End;
+                       break;
+               case 'P': /* Page-Up key press + release */
+                       keyval = GDK_KEY_Page_Up;
+                       break;
+               case 'p': /* Page-Down key press + release */
+                       keyval = GDK_KEY_Page_Down;
+                       break;
+               case 'l': /* Arrow-Left key press + release */
+                       keyval = GDK_KEY_Left;
+                       break;
+               case 'r': /* Arrow-Right key press + release */
+                       keyval = GDK_KEY_Right;
+                       break;
+               case 'u': /* Arrow-Up key press + release */
+                       keyval = GDK_KEY_Up;
+                       break;
+               case 'd': /* Arrow-Down key press + release */
+                       keyval = GDK_KEY_Down;
+                       break;
+               case 'D': /* Delete key press + release */
+                       keyval = GDK_KEY_Delete;
+                       break;
+               case 'b': /* Backspace key press + release */
+                       keyval = GDK_KEY_BackSpace;
+                       break;
+               case 't': /* Tab key press + release */
+                       keyval = GDK_KEY_Tab;
+                       break;
+               case 'n': /* Return key press + release */
+                       keyval = GDK_KEY_Return;
+                       break;
+               default:
+                       success = FALSE;
+                       g_warning ("%s: Unknown sequence command '%c' in sequence '%s'", G_STRFUNC, *seq, 
sequence);
+                       break;
+               }
+
+               if (success) {
+                       if (call_press)
+                               test_utils_send_key_event (widget, GDK_KEY_PRESS, keyval, fixture->key_state);
+
+                       if (call_release)
+                               test_utils_send_key_event (widget, GDK_KEY_RELEASE, keyval, 
fixture->key_state);
+               }
+
+               fixture->key_state = change_state;
+       }
+
+       test_utils_wait_milliseconds (event_processing_delay_ms);
+
+       return success;
+}
+
+static gboolean
+test_utils_execute_action (TestFixture *fixture,
+                          const gchar *action_name)
+{
+       GtkAction *action;
+
+       g_return_val_if_fail (fixture != NULL, FALSE);
+       g_return_val_if_fail (E_IS_HTML_EDITOR (fixture->editor), FALSE);
+       g_return_val_if_fail (action_name != NULL, FALSE);
+
+       action = e_html_editor_get_action (fixture->editor, action_name);
+       if (action) {
+               gtk_action_activate (action);
+       } else {
+               g_warning ("%s: Failed to find action '%s'", G_STRFUNC, action_name);
+               return FALSE;
+       }
+
+       return TRUE;
+}
+
+/* Expects only the part like "undo" [ ":" number ] */
+static gint
+test_utils_maybe_extract_undo_number (const gchar *command)
+{
+       const gchar *ptr;
+       gint number;
+
+       g_return_val_if_fail (command != NULL, -1);
+
+       ptr = strchr (command, ':');
+       if (!ptr)
+               return 1;
+
+       number = atoi (ptr + 1);
+       g_return_val_if_fail (number > 0, -1);
+
+       return number;
+}
+
+static const UndoContent *
+test_utils_pick_undo_content (const GSList *undo_stack,
+                             gint number)
+{
+       const GSList *link;
+
+       g_return_val_if_fail (undo_stack != NULL, NULL);
+
+       number--;
+       for (link = undo_stack; link && number > 0; link = g_slist_next (link)) {
+               number--;
+       }
+
+       g_return_val_if_fail (link != NULL, NULL);
+       g_return_val_if_fail (link->data != NULL, NULL);
+
+       return link->data;
+}
+
+/* Each line of 'commands' contains one command.
+
+   commands  = command *("\n" command)
+
+   command   = actioncmd ; Execute an action
+             / modecmd   ; Change editor mode to HTML or Plain Text
+             / seqcmd    ; Sequence of special key strokes
+             / typecmd   ; Type a text
+             / undocmd   ; Undo/redo commands
+             / waitcmd   ; Wait command
+
+   actioncmd = "action:" name
+
+   actioncmd = "mode:" ("html" / "plain")
+
+   seqcmd    = "seq:" sequence
+
+   sequence  = "S" ; Shift key press
+             / "s" ; Shift key release
+             / "C" ; Ctrl key press
+             / "c" ; Ctrl key release
+             / "A" ; Alt key press
+             / "a" ; Alt key release
+             / "h" ; Home key press + release
+             / "e" ; End key press + release
+             / "P" ; Page-Up key press + release
+             / "p" ; Page-Down key press + release
+             / "l" ; Arrow-Left key press + release
+             / "r" ; Arrow-Right key press + release
+             / "u" ; Arrow-Up key press + release
+             / "d" ; Arrow-Down key press + release
+             / "D" ; Delete key press + release
+             / "b" ; Backspace key press + release
+             / "t" ; Tab key press + release
+             / "n" ; Return key press + release
+
+   typecmd   = "type:" text ; the 'text' can contain escaped letters with a backslash, like "\\n" transforms 
into "\n"
+
+   undocmd   = "undo:" undotype
+
+   undotype  = "undo" [ ":" number ] ; Call 'undo', number-times; if 'number' is not provided, then call it 
exactly once
+             / "redo" [ ":" number ] ; Call 'redo', number-times; if 'number' is not provided, then call it 
exactly once
+            / "save"                ; Save current content of the editor for later tests
+            / "drop" [ ":" number ] ; Forgets saved content, if 'number' is provided, then top number saves 
are forgotten
+            / "test" [ ":" number ] ; Tests current editor content against any previously saved state; the 
optional
+                                     ; 'number' argument can be used to specify which exact previous state 
to use
+
+   waitcmd   = "wait:" milliseconds  ; waits for 'milliseconds'
+ */
+gboolean
+test_utils_process_commands (TestFixture *fixture,
+                            const gchar *commands)
+{
+       gchar **cmds;
+       gint cc;
+       gboolean success = TRUE;
+
+       g_return_val_if_fail (fixture != NULL, FALSE);
+       g_return_val_if_fail (E_IS_HTML_EDITOR (fixture->editor), FALSE);
+       g_return_val_if_fail (commands != NULL, FALSE);
+
+       cmds = g_strsplit (commands, "\n", -1);
+       for (cc = 0; cmds && cmds[cc] && success; cc++) {
+               const gchar *command = cmds[cc];
+
+               if (g_str_has_prefix (command, "action:")) {
+                       test_utils_execute_action (fixture, command + 7);
+               } else if (g_str_has_prefix (command, "mode:")) {
+                       const gchar *mode_change = command + 5;
+
+                       if (g_str_equal (mode_change, "html")) {
+                               test_utils_execute_action (fixture, "mode-html");
+                       } else if (g_str_equal (mode_change, "plain")) {
+                               test_utils_execute_action (fixture, "mode-plain");
+                       } else {
+                               success = FALSE;
+                               g_warning ("%s: Unknown mode '%s'", G_STRFUNC, mode_change);
+                       }
+               } else if (g_str_has_prefix (command, "seq:")) {
+                       success = test_utils_process_sequence (fixture, command + 4);
+               } else if (g_str_has_prefix (command, "type:")) {
+                       gchar *text;
+
+                       text = g_strcompress (command + 5);
+                       success = test_utils_type_text (fixture, text);
+                       if (!success)
+                               g_warning ("%s: Failed to type text '%s'", G_STRFUNC, text);
+                       g_free (text);
+               } else if (g_str_has_prefix (command, "undo:")) {
+                       gint number;
+
+                       command += 5;
+
+                       if (g_str_equal (command, "undo") || g_str_has_prefix (command, "undo:")) {
+                               number = test_utils_maybe_extract_undo_number (command);
+                               while (number > 0 && success) {
+                                       success = test_utils_execute_action (fixture, "undo");
+                                       number--;
+                               }
+                       } else if (g_str_has_prefix (command, "redo") || g_str_has_prefix (command, "redo:")) 
{
+                               number = test_utils_maybe_extract_undo_number (command);
+                               while (number > 0 && success) {
+                                       success = test_utils_execute_action (fixture, "redo");
+                                       number--;
+                               }
+                       } else if (g_str_equal (command, "save")) {
+                               UndoContent *uc;
+
+                               uc = undo_content_new (fixture);
+                               fixture->undo_stack = g_slist_prepend (fixture->undo_stack, uc);
+                       } else if (g_str_equal (command, "drop") || g_str_has_prefix (command, "drop:")) {
+                               number = test_utils_maybe_extract_undo_number (command);
+                               g_warn_if_fail (number <= g_slist_length (fixture->undo_stack));
+
+                               while (number > 0 && fixture->undo_stack) {
+                                       UndoContent *uc = fixture->undo_stack->data;
+
+                                       fixture->undo_stack = g_slist_remove (fixture->undo_stack, uc);
+                                       undo_content_free (uc);
+                                       number--;
+                               }
+                       } else if (g_str_equal (command, "test") || g_str_has_prefix (command, "test:")) {
+                               const UndoContent *uc;
+
+                               number = test_utils_maybe_extract_undo_number (command);
+                               uc = test_utils_pick_undo_content (fixture->undo_stack, number);
+                               success = uc && undo_content_test (fixture, uc, cc);
+                       } else {
+                               g_warning ("%s: Unknown command 'undo:%s'", G_STRFUNC, command);
+                               success = FALSE;
+                       }
+
+                       test_utils_wait_milliseconds (event_processing_delay_ms);
+               } else if (g_str_has_prefix (command, "wait:")) {
+                       test_utils_wait_milliseconds (atoi (command + 5));
+               } else if (*command) {
+                       g_warning ("%s: Unknown command '%s'", G_STRFUNC, command);
+                       success = FALSE;
+               }
+
+               /* Wait at least 100 ms, to give a chance to move the cursor and
+                  other things for WebKit, for example before executing actions. */
+               test_utils_wait_milliseconds (MAX (event_processing_delay_ms, 100));
+       }
+
+       g_strfreev (cmds);
+
+       if (success) {
+               /* Give the editor some time to finish any ongoing async operations */
+               test_utils_wait_milliseconds (MAX (event_processing_delay_ms, 100));
+       }
+
+       return success;
+}
+
+gboolean
+test_utils_run_simple_test (TestFixture *fixture,
+                           const gchar *commands,
+                           const gchar *expected_html,
+                           const gchar *expected_plain)
+{
+       EContentEditor *cnt_editor;
+       gchar *text;
+
+       g_return_val_if_fail (fixture != NULL, FALSE);
+       g_return_val_if_fail (E_IS_HTML_EDITOR (fixture->editor), FALSE);
+       g_return_val_if_fail (commands != NULL, FALSE);
+
+       cnt_editor = e_html_editor_get_content_editor (fixture->editor);
+
+       if (!test_utils_process_commands (fixture, commands))
+               return FALSE;
+
+       if (expected_html) {
+               text = e_content_editor_get_content (cnt_editor, E_CONTENT_EDITOR_GET_PROCESSED | 
E_CONTENT_EDITOR_GET_TEXT_HTML, NULL, NULL);
+               g_return_val_if_fail (text != NULL, FALSE);
+
+               if (!test_utils_html_equal (fixture, text, expected_html)) {
+                       g_warning ("%s: returned HTML\n---%s---\n and expected HTML\n---%s---\n do not 
match", G_STRFUNC, text, expected_html);
+                       g_free (text);
+                       return FALSE;
+               }
+
+               g_free (text);
+       }
+
+       if (expected_plain) {
+               text = e_content_editor_get_content (cnt_editor, E_CONTENT_EDITOR_GET_PROCESSED | 
E_CONTENT_EDITOR_GET_TEXT_PLAIN, NULL, NULL);
+               g_return_val_if_fail (text != NULL, FALSE);
+
+               if (!test_utils_html_equal (fixture, text, expected_plain)) {
+                       g_warning ("%s: returned Plain\n---%s---\n and expected Plain\n---%s---\n do not 
match", G_STRFUNC, text, expected_plain);
+                       g_free (text);
+                       return FALSE;
+               }
+
+               g_free (text);
+       }
+
+       return TRUE;
+}
+
+void
+test_utils_insert_content (TestFixture *fixture,
+                          const gchar *content,
+                          EContentEditorInsertContentFlags flags)
+{
+       EContentEditor *cnt_editor;
+
+       g_return_if_fail (fixture != NULL);
+       g_return_if_fail (E_IS_HTML_EDITOR (fixture->editor));
+       g_return_if_fail (content != NULL);
+
+       cnt_editor = e_html_editor_get_content_editor (fixture->editor);
+       e_content_editor_insert_content (cnt_editor, content, flags);
+}
+
+void
+test_utils_set_clipboard_text (const gchar *text,
+                              gboolean is_html)
+{
+       GtkClipboard *clipboard;
+
+       g_return_if_fail (text != NULL);
+
+       clipboard = gtk_clipboard_get (GDK_SELECTION_CLIPBOARD);
+       g_return_if_fail (clipboard != NULL);
+
+       gtk_clipboard_clear (clipboard);
+
+       if (is_html) {
+               e_clipboard_set_html (clipboard, text, -1);
+       } else {
+               gtk_clipboard_set_text (clipboard, text, -1);
+       }
+}
+
+gchar *
+test_utils_get_clipboard_text (gboolean request_html)
+{
+       GtkClipboard *clipboard;
+       gchar *text;
+
+       clipboard = gtk_clipboard_get (GDK_SELECTION_CLIPBOARD);
+       g_return_val_if_fail (clipboard != NULL, NULL);
+
+       if (request_html) {
+               g_return_val_if_fail (e_clipboard_wait_is_html_available (clipboard), NULL);
+               text = e_clipboard_wait_for_html (clipboard);
+       } else {
+               g_return_val_if_fail (gtk_clipboard_wait_is_text_available (clipboard), NULL);
+               text = gtk_clipboard_wait_for_text (clipboard);
+       }
+
+       g_return_val_if_fail (text != NULL, NULL);
+
+       return text;
+}
diff --git a/e-util/test-html-editor-units-utils.h b/e-util/test-html-editor-units-utils.h
new file mode 100644
index 0000000..f9d045f
--- /dev/null
+++ b/e-util/test-html-editor-units-utils.h
@@ -0,0 +1,89 @@
+/*
+ * Copyright (C) 2016 Red Hat, Inc. (www.redhat.com)
+ *
+ * This library 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 library 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 Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef TEST_HTML_EDITOR_UNITS_UTILS_H
+#define TEST_HTML_EDITOR_UNITS_UTILS_H
+
+#include <glib.h>
+#include <e-util/e-util.h>
+
+typedef struct _TestSettings {
+       gchar *schema;
+       gchar *key;
+       GVariant *old_value;
+} TestSettings;
+
+typedef struct _TestFixture {
+       GtkWidget *window;
+       EHTMLEditor *editor;
+       GSList *settings; /* TestSettings * */
+       guint key_state;
+
+       GSList *undo_stack; /* UndoContent * */
+} TestFixture;
+
+void           test_utils_set_event_processing_delay_ms
+                                               (guint value);
+guint          test_utils_get_event_processing_delay_ms
+                                               (void);
+void           test_utils_fixture_set_up       (TestFixture *fixture,
+                                                gconstpointer user_data);
+void           test_utils_fixture_tear_down    (TestFixture *fixture,
+                                                gconstpointer user_data);
+void           test_utils_fixture_change_setting
+                                               (TestFixture *fixture,
+                                                const gchar *schema,
+                                                const gchar *key,
+                                                GVariant *value);
+void           test_utils_fixture_change_setting_boolean
+                                               (TestFixture *fixture,
+                                                const gchar *schema,
+                                                const gchar *key,
+                                                gboolean value);
+void           test_utils_fixture_change_setting_int32
+                                               (TestFixture *fixture,
+                                                const gchar *schema,
+                                                const gchar *key,
+                                                gint value);
+void           test_utils_fixture_change_setting_string
+                                               (TestFixture *fixture,
+                                                const gchar *schema,
+                                                const gchar *key,
+                                                const gchar *value);
+gpointer       test_utils_async_call_prepare   (void);
+gboolean       test_utils_async_call_wait      (gpointer async_data,
+                                                guint timeout_seconds);
+gboolean       test_utils_async_call_finish    (gpointer async_data);
+gboolean       test_utils_wait_milliseconds    (guint milliseconds);
+gboolean       test_utils_type_text            (TestFixture *fixture,
+                                                const gchar *text);
+gboolean       test_utils_html_equal           (TestFixture *fixture,
+                                                const gchar *html1,
+                                                const gchar *html2);
+gboolean       test_utils_process_commands     (TestFixture *fixture,
+                                                const gchar *commands);
+gboolean       test_utils_run_simple_test      (TestFixture *fixture,
+                                                const gchar *commands,
+                                                const gchar *expected_html,
+                                                const gchar *expected_plain);
+void           test_utils_insert_content       (TestFixture *fixture,
+                                                const gchar *content,
+                                                EContentEditorInsertContentFlags flags);
+void           test_utils_set_clipboard_text   (const gchar *text,
+                                                gboolean is_html);
+gchar *                test_utils_get_clipboard_text   (gboolean request_html);
+
+#endif /* TEST_HTML_EDITOR_UNITS_UTILS_H */
diff --git a/e-util/test-html-editor-units.c b/e-util/test-html-editor-units.c
new file mode 100644
index 0000000..c72a9a2
--- /dev/null
+++ b/e-util/test-html-editor-units.c
@@ -0,0 +1,2823 @@
+/*
+ * Copyright (C) 2016 Red Hat, Inc. (www.redhat.com)
+ *
+ * This library 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 library 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 Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <locale.h>
+#include <e-util/e-util.h>
+
+#include "e-html-editor-private.h"
+#include "test-html-editor-units-utils.h"
+
+#define HTML_PREFIX "<html><head></head><body>"
+#define HTML_PREFIX_PLAIN "<html><head></head><body style=\"font-family: Monospace;\">"
+#define HTML_SUFFIX "</body></html>"
+
+/* The tests do not use the 'user_data' argument, thus the functions avoid them and the typecast is needed. 
*/
+typedef void (* ETestFixtureFunc) (TestFixture *fixture, gconstpointer user_data);
+
+static void
+test_create_editor (TestFixture *fixture)
+{
+       g_assert (fixture->editor != NULL);
+       g_assert_cmpstr (e_html_editor_get_content_editor_name (fixture->editor), ==, 
DEFAULT_CONTENT_EDITOR_NAME);
+
+       /* test of the test function */
+       g_assert (test_utils_html_equal (fixture, "<span>a</span>", "<sPaN>a</spaN>"));
+       g_assert (!test_utils_html_equal (fixture, "<span>A</span>", "<sPaN>a</spaN>"));
+}
+
+static void
+test_style_bold_selection (TestFixture *fixture)
+{
+       if (!test_utils_run_simple_test (fixture,
+               "mode:html\n"
+               "type:some bold text\n"
+               "seq:hCrcrCSrsc\n"
+               "action:bold\n",
+               HTML_PREFIX "<p>some <b>bold</b> text</p>" HTML_SUFFIX,
+               "some bold text"))
+               g_test_fail ();
+}
+
+static void
+test_style_bold_typed (TestFixture *fixture)
+{
+       if (!test_utils_run_simple_test (fixture,
+               "mode:html\n"
+               "type:some \n"
+               "action:bold\n"
+               "type:bold\n"
+               "action:bold\n"
+               "type: text\n",
+               HTML_PREFIX "<p>some <b>bold</b> text</p>" HTML_SUFFIX,
+               "some bold text"))
+               g_test_fail ();
+}
+
+static void
+test_style_italic_selection (TestFixture *fixture)
+{
+       if (!test_utils_run_simple_test (fixture,
+               "mode:html\n"
+               "type:some italic text\n"
+               "seq:hCrcrCSrsc\n"
+               "action:italic\n",
+               HTML_PREFIX "<p>some <i>italic</i> text</p>" HTML_SUFFIX,
+               "some italic text"))
+               g_test_fail ();
+}
+
+static void
+test_style_italic_typed (TestFixture *fixture)
+{
+       if (!test_utils_run_simple_test (fixture,
+               "mode:html\n"
+               "type:some \n"
+               "action:italic\n"
+               "type:italic\n"
+               "action:italic\n"
+               "type: text\n",
+               HTML_PREFIX "<p>some <i>italic</i> text</p>" HTML_SUFFIX,
+               "some italic text"))
+               g_test_fail ();
+}
+
+static void
+test_style_underline_selection (TestFixture *fixture)
+{
+       if (!test_utils_run_simple_test (fixture,
+               "mode:html\n"
+               "type:some underline text\n"
+               "seq:hCrcrCSrsc\n"
+               "action:underline\n",
+               HTML_PREFIX "<p>some <u>underline</u> text</p>" HTML_SUFFIX,
+               "some underline text"))
+               g_test_fail ();
+}
+
+static void
+test_style_underline_typed (TestFixture *fixture)
+{
+       if (!test_utils_run_simple_test (fixture,
+               "mode:html\n"
+               "type:some \n"
+               "action:underline\n"
+               "type:underline\n"
+               "action:underline\n"
+               "type: text\n",
+               HTML_PREFIX "<p>some <u>underline</u> text</p>" HTML_SUFFIX,
+               "some underline text"))
+               g_test_fail ();
+}
+
+static void
+test_style_strikethrough_selection (TestFixture *fixture)
+{
+       if (!test_utils_run_simple_test (fixture,
+               "mode:html\n"
+               "type:some strikethrough text\n"
+               "seq:hCrcrCSrsc\n"
+               "action:strikethrough\n",
+               HTML_PREFIX "<p>some <strike>strikethrough</strike> text</p>" HTML_SUFFIX,
+               "some strikethrough text"))
+               g_test_fail ();
+}
+
+static void
+test_style_strikethrough_typed (TestFixture *fixture)
+{
+       if (!test_utils_run_simple_test (fixture,
+               "mode:html\n"
+               "type:some \n"
+               "action:strikethrough\n"
+               "type:strikethrough\n"
+               "action:strikethrough\n"
+               "type: text\n",
+               HTML_PREFIX "<p>some <strike>strikethrough</strike> text</p>" HTML_SUFFIX,
+               "some strikethrough text"))
+               g_test_fail ();
+}
+
+static void
+test_style_monospace_selection (TestFixture *fixture)
+{
+       if (!test_utils_run_simple_test (fixture,
+               "mode:html\n"
+               "type:some monospace text\n"
+               "seq:hCrcrCSrsc\n"
+               "action:monospaced\n",
+               HTML_PREFIX "<p>some <font face=\"monospace\" size=\"3\">monospace</font> text</p>" 
HTML_SUFFIX,
+               "some monospace text"))
+               g_test_fail ();
+}
+
+static void
+test_style_monospace_typed (TestFixture *fixture)
+{
+       if (!test_utils_run_simple_test (fixture,
+               "mode:html\n"
+               "type:some \n"
+               "action:monospaced\n"
+               "type:monospace\n"
+               "action:monospaced\n"
+               "type: text\n",
+               HTML_PREFIX "<p>some <font face=\"monospace\" size=\"3\">monospace</font> text</p>" 
HTML_SUFFIX,
+               "some monospace text"))
+               g_test_fail ();
+}
+
+static void
+test_justify_selection (TestFixture *fixture)
+{
+       if (!test_utils_run_simple_test (fixture,
+               "mode:html\n"
+               "type:center\\n\n"
+               "type:right\\n\n"
+               "type:left\\n\n"
+               "seq:uuu\n"
+               "action:justify-center\n"
+               "seq:d\n"
+               "action:justify-right\n"
+               "seq:d\n"
+               "action:justify-left\n",
+               HTML_PREFIX
+                       "<p style=\"text-align: center\">center</p>"
+                       "<p style=\"text-align: right\">right</p>"
+                       "<p>left</p><p><br></p>"
+               HTML_SUFFIX,
+               "                                center\n"
+               "                                                                  right\n"
+               "left\n"))
+               g_test_fail ();
+}
+
+static void
+test_justify_typed (TestFixture *fixture)
+{
+       if (!test_utils_run_simple_test (fixture,
+               "mode:html\n"
+               "action:justify-center\n"
+               "type:center\\n\n"
+               "action:justify-right\n"
+               "type:right\\n\n"
+               "action:justify-left\n"
+               "type:left\\n\n",
+               HTML_PREFIX
+                       "<p style=\"text-align: center\">center</p>"
+                       "<p style=\"text-align: right\">right</p>"
+                       "<p>left</p><p><br></p>"
+               HTML_SUFFIX,
+               "                                center\n"
+               "                                                                  right\n"
+               "left\n"))
+               g_test_fail ();
+}
+
+static void
+test_indent_selection (TestFixture *fixture)
+{
+       if (!test_utils_run_simple_test (fixture,
+               "mode:html\n"
+               "type:level 0\\n\n"
+               "type:level 1\\n\n"
+               "type:level 2\\n\n"
+               "type:level 1\\n\n"
+               "seq:uuu\n"
+               "action:indent\n"
+               "seq:d\n"
+               "action:indent\n"
+               "action:indent\n"
+               "seq:d\n"
+               "action:indent\n"
+               "action:indent\n" /* just to try whether the unindent will work too */
+               "action:unindent\n",
+               HTML_PREFIX
+                       "<p>level 0</p>"
+                       "<div style=\"margin-left: 3ch;\">"
+                               "<p>level 1</p>"
+                               "<div style=\"margin-left: 3ch;\"><p>level 2</p></div>"
+                               "<p>level 1</p>"
+                       "</div><p><br></p>"
+               HTML_SUFFIX,
+               "level 0\n"
+               "    level 1\n"
+               "        level 2\n"
+               "    level 1\n"))
+               g_test_fail ();
+}
+
+static void
+test_indent_typed (TestFixture *fixture)
+{
+       if (!test_utils_run_simple_test (fixture,
+               "mode:html\n"
+               "type:level 0\\n\n"
+               "action:indent\n"
+               "type:level 1\\n\n"
+               "action:indent\n"
+               "type:level 2\\n\n"
+               "action:unindent\n"
+               "type:level 1\\n\n"
+               "action:unindent\n",
+               HTML_PREFIX
+                       "<p>level 0</p>"
+                       "<div style=\"margin-left: 3ch;\">"
+                               "<p>level 1</p>"
+                               "<div style=\"margin-left: 3ch;\"><p>level 2</p></div>"
+                               "<p>level 1</p>"
+                       "</div><p><br></p>"
+               HTML_SUFFIX,
+               "level 0\n"
+               "    level 1\n"
+               "        level 2\n"
+               "    level 1\n"))
+               g_test_fail ();
+}
+
+static void
+test_font_size_selection (TestFixture *fixture)
+{
+       if (!test_utils_run_simple_test (fixture,
+               "mode:html\n"
+               "type:FontM2 FontM1 Font0 FontP1 FontP2 FontP3 FontP4\n"
+               "seq:hCSrsc\n"
+               "action:size-minus-two\n"
+               "seq:rrCSrcs\n"
+               "action:size-minus-one\n"
+               "seq:rrCSrcs\n"
+               "action:size-plus-zero\n"
+               "seq:rrCSrcs\n"
+               "action:size-plus-one\n"
+               "seq:rrCSrcs\n"
+               "action:size-plus-two\n"
+               "seq:rrCSrcs\n"
+               "action:size-plus-three\n"
+               "seq:rrCSrcs\n"
+               "action:size-plus-four\n",
+               HTML_PREFIX "<p><font size=\"1\">FontM2</font> <font size=\"2\">FontM1</font> Font0 <font 
size=\"4\">FontP1</font> "
+               "<font size=\"5\">FontP2</font> <font size=\"6\">FontP3</font> <font 
size=\"7\">FontP4</font></p>" HTML_SUFFIX,
+               "FontM2 FontM1 Font0 FontP1 FontP2 FontP3 FontP4"))
+               g_test_fail ();
+}
+
+static void
+test_font_size_typed (TestFixture *fixture)
+{
+       if (!test_utils_run_simple_test (fixture,
+               "mode:html\n"
+               "action:size-minus-two\n"
+               "type:FontM2\n"
+               "action:size-plus-zero\n"
+               "type: \n"
+               "action:size-minus-one\n"
+               "type:FontM1\n"
+               "action:size-plus-zero\n"
+               "type: \n"
+               "type:Font0\n"
+               "action:size-plus-zero\n"
+               "type: \n"
+               "action:size-plus-one\n"
+               "type:FontP1\n"
+               "action:size-plus-zero\n"
+               "type: \n"
+               "action:size-plus-two\n"
+               "type:FontP2\n"
+               "action:size-plus-zero\n"
+               "type: \n"
+               "action:size-plus-three\n"
+               "type:FontP3\n"
+               "action:size-plus-zero\n"
+               "type: \n"
+               "action:size-plus-four\n"
+               "type:FontP4\n"
+               "action:size-plus-zero\n",
+               HTML_PREFIX "<p><font size=\"1\">FontM2</font> <font size=\"2\">FontM1</font> Font0 <font 
size=\"4\">FontP1</font> "
+               "<font size=\"5\">FontP2</font> <font size=\"6\">FontP3</font> <font 
size=\"7\">FontP4</font></p>" HTML_SUFFIX,
+               "FontM2 FontM1 Font0 FontP1 FontP2 FontP3 FontP4"))
+               g_test_fail ();
+}
+
+static void
+test_font_color_selection (TestFixture *fixture)
+{
+       EContentEditor *cnt_editor;
+       GdkRGBA rgba;
+
+       g_return_if_fail (fixture != NULL);
+       g_return_if_fail (E_IS_HTML_EDITOR (fixture->editor));
+
+       cnt_editor = e_html_editor_get_content_editor (fixture->editor);
+       g_return_if_fail (cnt_editor != NULL);
+
+       if (!test_utils_process_commands (fixture,
+               "mode:html\n"
+               "type:default red green blue\n"
+               "seq:hCrcrCSrsc\n")) {
+               g_test_fail ();
+               return;
+       }
+
+       rgba.red = 1.0;
+       rgba.green = 0.0;
+       rgba.blue = 0.0;
+       rgba.alpha = 1.0;
+
+       e_content_editor_set_font_color (cnt_editor, &rgba);
+
+       if (!test_utils_process_commands (fixture,
+               "seq:rrCSrcs\n")) {
+               g_test_fail ();
+               return;
+       }
+
+       rgba.red = 0.0;
+       rgba.green = 1.0;
+       rgba.blue = 0.0;
+       rgba.alpha = 1.0;
+
+       e_content_editor_set_font_color (cnt_editor, &rgba);
+
+       if (!test_utils_process_commands (fixture,
+               "seq:rrCSrcs\n")) {
+               g_test_fail ();
+               return;
+       }
+
+       rgba.red = 0.0;
+       rgba.green = 0.0;
+       rgba.blue = 1.0;
+       rgba.alpha = 1.0;
+
+       e_content_editor_set_font_color (cnt_editor, &rgba);
+
+       if (!test_utils_run_simple_test (fixture, "",
+               HTML_PREFIX "<p>default <font color=\"#ff0000\">red</font> <font 
color=\"#00ff00\">green</font> "
+               "<font color=\"#0000ff\">blue</font></p>" HTML_SUFFIX,
+               "default red green blue"))
+               g_test_fail ();
+}
+
+static void
+test_font_color_typed (TestFixture *fixture)
+{
+       EContentEditor *cnt_editor;
+       GdkRGBA rgba;
+
+       g_return_if_fail (fixture != NULL);
+       g_return_if_fail (E_IS_HTML_EDITOR (fixture->editor));
+
+       cnt_editor = e_html_editor_get_content_editor (fixture->editor);
+       g_return_if_fail (cnt_editor != NULL);
+
+       if (!test_utils_process_commands (fixture,
+               "mode:html\n"
+               "type:default \n")) {
+               g_test_fail ();
+               return;
+       }
+
+       rgba.red = 1.0;
+       rgba.green = 0.0;
+       rgba.blue = 0.0;
+       rgba.alpha = 1.0;
+
+       e_content_editor_set_font_color (cnt_editor, &rgba);
+
+       if (!test_utils_process_commands (fixture,
+               "type:red \n")) {
+               g_test_fail ();
+               return;
+       }
+
+       rgba.red = 0.0;
+       rgba.green = 1.0;
+       rgba.blue = 0.0;
+       rgba.alpha = 1.0;
+
+       e_content_editor_set_font_color (cnt_editor, &rgba);
+
+       if (!test_utils_process_commands (fixture,
+               "type:green \n")) {
+               g_test_fail ();
+               return;
+       }
+
+       rgba.red = 0.0;
+       rgba.green = 0.0;
+       rgba.blue = 1.0;
+       rgba.alpha = 1.0;
+
+       e_content_editor_set_font_color (cnt_editor, &rgba);
+
+       if (!test_utils_process_commands (fixture,
+               "type:blue\n")) {
+               g_test_fail ();
+               return;
+       }
+
+       if (!test_utils_run_simple_test (fixture, "",
+               HTML_PREFIX "<p>default <font color=\"#ff0000\">red </font><font color=\"#00ff00\">green 
</font>"
+               "<font color=\"#0000ff\">blue</font></p>" HTML_SUFFIX,
+               "default red green blue"))
+               g_test_fail ();
+}
+
+static void
+test_list_bullet_plain (TestFixture *fixture)
+{
+       if (!test_utils_run_simple_test (fixture,
+               "mode:plain\n"
+               "action:style-list-bullet\n"
+               "type:item 1\\n\n"
+               "type:item 2\\n\n"
+               "type:item 3\\n\n"
+               "type:\\n\n"
+               "type:text\n",
+               NULL,
+               " * item 1\n"
+               " * item 2\n"
+               " * item 3\n"
+               "text"))
+               g_test_fail ();
+}
+
+static void
+test_list_bullet_html (TestFixture *fixture)
+{
+       if (!test_utils_run_simple_test (fixture,
+               "mode:html\n"
+               "action:style-list-bullet\n"
+               "type:item 1\\n\n"
+               "action:indent\n"
+               "type:item 2\\n\n"
+               "action:unindent\n"
+               "type:item 3\\n\n"
+               "type:\\n\n"
+               "type:text\n",
+               HTML_PREFIX
+                       "<ul>"
+                               "<li>item 1</li>"
+                               "<ul>"
+                                       "<li>item 2</li>"
+                               "</ul>"
+                               "<li>item 3</li>"
+                       "</ul>"
+                       "<p>text</p>"
+               HTML_SUFFIX,
+               " * item 1\n"
+               "    * item 2\n"
+               " * item 3\n"
+               "text"))
+               g_test_fail ();
+}
+
+static void
+test_list_bullet_html_from_block (TestFixture *fixture)
+{
+       if (!test_utils_run_simple_test (fixture,
+               "mode:html\n"
+               "type:item 1\\n\n"
+               "type:item 2\n"
+               "action:style-list-roman\n"
+               "type:\\n\n"
+               "action:style-preformat\n"
+               "type:item 3\\n\n"
+               "action:select-all\n"
+               "action:style-list-bullet\n",
+               HTML_PREFIX
+                       "<ul>"
+                               "<li>item 1</li>"
+                               "<li>item 2</li>"
+                               "<li>item 3</li>"
+                               "<li><br></li>"
+                       "</ul>"
+               HTML_SUFFIX,
+               " * item 1\n"
+               " * item 2\n"
+               " * item 3\n"
+               " * "))
+               g_test_fail ();
+}
+
+static void
+test_list_alpha_html (TestFixture *fixture)
+{
+       if (!test_utils_run_simple_test (fixture,
+               "mode:html\n"
+               "action:style-list-alpha\n"
+               "type:item 1\\n\n"
+               "action:indent\n"
+               "type:item 2\\n\n"
+               "action:unindent\n"
+               "type:item 3\\n\n"
+               "type:\\n\n"
+               "type:text\n",
+               HTML_PREFIX
+                       "<ol type=\"A\">"
+                               "<li>item 1</li>"
+                               "<ol type=\"A\">"
+                                       "<li>item 2</li>"
+                               "</ol>"
+                               "<li>item 3</li>"
+                       "</ol>"
+                       "<p>text</p>"
+               HTML_SUFFIX,
+               "   A. item 1\n"
+               "      A. item 2\n"
+               "   B. item 3\n"
+               "text"))
+               g_test_fail ();
+}
+
+static void
+test_list_alpha_plain (TestFixture *fixture)
+{
+       if (!test_utils_run_simple_test (fixture,
+               "mode:plain\n"
+               "action:style-list-alpha\n"
+               "type:item 1\\n\n"
+               "action:indent\n"
+               "type:item 2\\n\n"
+               "action:unindent\n"
+               "type:item 3\\n\n"
+               "type:\\n\n"
+               "type:text\n",
+               NULL,
+               "   A. item 1\n"
+               "      A. item 2\n"
+               "   B. item 3\n"
+               "text"))
+               g_test_fail ();
+}
+
+static void
+test_list_roman_html (TestFixture *fixture)
+{
+       if (!test_utils_run_simple_test (fixture,
+               "mode:html\n"
+               "action:style-list-roman\n"
+               "type:1\\n\n"
+               "type:2\\n\n"
+               "type:3\\n\n"
+               "type:4\\n\n"
+               "type:5\\n\n"
+               "type:6\\n\n"
+               "type:7\\n\n"
+               "type:8\\n\n"
+               "type:9\\n\n"
+               "type:10\\n\n"
+               "type:11\\n\n"
+               "type:12\\n\n"
+               "type:13\\n\n"
+               "type:14\\n\n"
+               "type:15\\n\n"
+               "type:16\\n\n"
+               "type:17\\n\n"
+               "type:18\n",
+               HTML_PREFIX "<ol type=\"I\">"
+               "<li>1</li><li>2</li><li>3</li><li>4</li><li>5</li><li>6</li>"
+               "<li>7</li><li>8</li><li>9</li><li>10</li><li>11</li><li>12</li>"
+               "<li>13</li><li>14</li><li>15</li><li>16</li><li>17</li><li>18</li>"
+               "</ol>" HTML_SUFFIX,
+               "   I. 1\n"
+               "  II. 2\n"
+               " III. 3\n"
+               "  IV. 4\n"
+               "   V. 5\n"
+               "  VI. 6\n"
+               " VII. 7\n"
+               "VIII. 8\n"
+               "  IX. 9\n"
+               "   X. 10\n"
+               "  XI. 11\n"
+               " XII. 12\n"
+               "XIII. 13\n"
+               " XIV. 14\n"
+               "  XV. 15\n"
+               " XVI. 16\n"
+               "XVII. 17\n"
+               "XVIII. 18"))
+               g_test_fail ();
+}
+
+static void
+test_list_roman_plain (TestFixture *fixture)
+{
+       if (!test_utils_run_simple_test (fixture,
+               "mode:plain\n"
+               "action:style-list-roman\n"
+               "type:1\\n\n"
+               "type:2\\n\n"
+               "type:3\\n\n"
+               "type:4\\n\n"
+               "type:5\\n\n"
+               "type:6\\n\n"
+               "type:7\\n\n"
+               "type:8\\n\n"
+               "type:9\\n\n"
+               "type:10\\n\n"
+               "type:11\\n\n"
+               "type:12\\n\n"
+               "type:13\\n\n"
+               "type:14\\n\n"
+               "type:15\\n\n"
+               "type:16\\n\n"
+               "type:17\\n\n"
+               "type:18\n",
+               NULL,
+               "   I. 1\n"
+               "  II. 2\n"
+               " III. 3\n"
+               "  IV. 4\n"
+               "   V. 5\n"
+               "  VI. 6\n"
+               " VII. 7\n"
+               "VIII. 8\n"
+               "  IX. 9\n"
+               "   X. 10\n"
+               "  XI. 11\n"
+               " XII. 12\n"
+               "XIII. 13\n"
+               " XIV. 14\n"
+               "  XV. 15\n"
+               " XVI. 16\n"
+               "XVII. 17\n"
+               "XVIII. 18"))
+               g_test_fail ();
+}
+
+static void
+test_list_multi_html (TestFixture *fixture)
+{
+       if (!test_utils_run_simple_test (fixture,
+               "mode:html\n"
+               "action:style-list-bullet\n"
+               "type:item 1\\n\n"
+               "type:item 2\\n\n"
+               "type:\\n\n"
+               "action:style-list-roman\n"
+               "type:item 3\\n\n"
+               "type:item 4\\n\n",
+               HTML_PREFIX
+                       "<ul>"
+                               "<li>item 1</li>"
+                               "<li>item 2</li>"
+                       "</ul>"
+                       "<ol type=\"I\">"
+                               "<li>item 3</li>"
+                               "<li>item 4</li>"
+                               "<li><br></li>"
+                       "</ol>"
+               HTML_SUFFIX,
+               " * item 1\n"
+               " * item 2\n"
+               "   I. item 3\n"
+               "  II. item 4\n"
+               " III. "))
+               g_test_fail ();
+}
+
+static void
+test_list_multi_plain (TestFixture *fixture)
+{
+       if (!test_utils_run_simple_test (fixture,
+               "mode:plain\n"
+               "action:style-list-bullet\n"
+               "type:item 1\\n\n"
+               "type:item 2\\n\n"
+               "type:\\n\n"
+               "action:style-list-roman\n"
+               "type:item 3\\n\n"
+               "type:item 4\\n\n",
+               NULL,
+               " * item 1\n"
+               " * item 2\n"
+               "   I. item 3\n"
+               "  II. item 4\n"
+               " III. "))
+               g_test_fail ();
+}
+
+static void
+test_list_multi_change_html (TestFixture *fixture)
+{
+       if (!test_utils_run_simple_test (fixture,
+               "mode:html\n"
+               "action:style-list-bullet\n"
+               "type:item 1\\n\n"
+               "type:item 2\\n\n"
+               "type:\\n\n"
+               "action:style-list-roman\n"
+               "type:item 3\\n\n"
+               "type:item 4\\n\n"
+               "action:select-all\n"
+               "action:style-list-number\n",
+               HTML_PREFIX
+                       "<ol>"
+                               "<li>item 1</li>"
+                               "<li>item 2</li>"
+                               "<li>item 3</li>"
+                               "<li>item 4</li>"
+                               "<li><br></li>"
+                       "</ol>"
+               HTML_SUFFIX,
+               "   1. item 1\n"
+               "   2. item 2\n"
+               "   3. item 3\n"
+               "   4. item 4\n"
+               "   5. "))
+               g_test_fail ();
+}
+
+static void
+test_list_multi_change_plain (TestFixture *fixture)
+{
+       if (!test_utils_run_simple_test (fixture,
+               "mode:plain\n"
+               "action:style-list-bullet\n"
+               "type:item 1\\n\n"
+               "type:item 2\\n\n"
+               "type:\\n\n"
+               "action:style-list-roman\n"
+               "type:item 3\\n\n"
+               "type:item 4\\n\n"
+               "action:select-all\n"
+               "action:style-list-number\n",
+               NULL,
+               "   1. item 1\n"
+               "   2. item 2\n"
+               "   3. item 3\n"
+               "   4. item 4\n"
+               "   5. "))
+               g_test_fail ();
+}
+
+static void
+test_link_insert_dialog (TestFixture *fixture)
+{
+       if (!test_utils_run_simple_test (fixture,
+               "mode:html\n"
+               "type:a link example: \n"
+               "action:insert-link\n"
+               "type:http://www.gnome.org\n";
+               "seq:n\n",
+               HTML_PREFIX "<p>a link example: <a 
href=\"http://www.gnome.org\";>http://www.gnome.org</a></p>" HTML_SUFFIX,
+               "a link example: http://www.gnome.org";))
+               g_test_fail ();
+}
+
+static void
+test_link_insert_dialog_selection (TestFixture *fixture)
+{
+       if (!test_utils_run_simple_test (fixture,
+               "mode:html\n"
+               "type:a link example: GNOME\n"
+               "seq:CSlsc\n"
+               "action:insert-link\n"
+               "type:http://www.gnome.org\n";
+               "seq:n\n",
+               HTML_PREFIX "<p>a link example: <a href=\"http://www.gnome.org\";>GNOME</a></p>" HTML_SUFFIX,
+               "a link example: GNOME"))
+               g_test_fail ();
+}
+
+static void
+test_link_insert_typed (TestFixture *fixture)
+{
+       if (!test_utils_run_simple_test (fixture,
+               "mode:html\n"
+               "type:www.gnome.org \n",
+               HTML_PREFIX "<p><a href=\"http://www.gnome.org\";>www.gnome.org</a> </p>" HTML_SUFFIX,
+               "www.gnome.org "))
+               g_test_fail ();
+}
+
+static void
+test_link_insert_typed_change_description (TestFixture *fixture)
+{
+       if (!test_utils_run_simple_test (fixture,
+               "mode:html\n"
+               "type:www.gnome.org \n"
+               "seq:ll\n"
+               "action:insert-link\n"
+               "seq:tt\n" /* Jump to the description */
+               "type:GNOME\n"
+               "seq:n\n",
+               HTML_PREFIX "<p><a href=\"http://www.gnome.org\";>GNOME</a> </p>" HTML_SUFFIX,
+               "GNOME "))
+               g_test_fail ();
+}
+
+static void
+test_link_insert_dialog_remove_link (TestFixture *fixture)
+{
+       if (!test_utils_run_simple_test (fixture,
+               "mode:html\n"
+               "type:www.gnome.org \n"
+               "seq:ll\n"
+               "action:insert-link\n"
+               "seq:tttt\n" /* Jump to 'Remove Link' */
+               "seq:n\n", /* Press the button */
+               HTML_PREFIX "<p>www.gnome.org </p>" HTML_SUFFIX,
+               "www.gnome.org "))
+               g_test_fail ();
+}
+
+static void
+test_link_insert_typed_append (TestFixture *fixture)
+{
+       if (!test_utils_run_simple_test (fixture,
+               "mode:html\n"
+               "type:www.gnome.org \n"
+               "seq:l\n"
+               "type:/about\n",
+               HTML_PREFIX "<p><a href=\"http://www.gnome.org/about\";>www.gnome.org/about</a> </p>" 
HTML_SUFFIX,
+               "www.gnome.org/about "))
+               g_test_fail ();
+}
+
+static void
+test_link_insert_typed_remove (TestFixture *fixture)
+{
+       if (!test_utils_run_simple_test (fixture,
+               "mode:html\n"
+               "type:www.gnome.org \n"
+               "seq:bbb\n",
+               HTML_PREFIX "<p><a href=\"http://www.gnome.org\";>www.gnome.o</a></p>" HTML_SUFFIX,
+               "www.gnome.o"))
+               g_test_fail ();
+}
+
+static void
+test_h_rule_insert (TestFixture *fixture)
+{
+       if (!test_utils_run_simple_test (fixture,
+               "mode:html\n"
+               "type:text\n"
+               "action:insert-rule\n"
+               "seq:tttttn\n", /* Move to the Close button and press it */
+               HTML_PREFIX "<p>text</p><hr align=\"left\" size=\"2\" noshade=\"\">" HTML_SUFFIX,
+               "text"))
+               g_test_fail ();
+}
+
+static void
+test_h_rule_insert_text_after (TestFixture *fixture)
+{
+       if (!test_utils_run_simple_test (fixture,
+               "mode:html\n"
+               "type:above\n"
+               "action:insert-rule\n"
+               "seq:tttttn\n" /* Move to the Close button and press it */
+               "seq:den\n"
+               "type:below\n",
+               HTML_PREFIX "<p>above</p><hr align=\"left\" size=\"2\" noshade=\"\"><p>below</p>" HTML_SUFFIX,
+               "above\nbelow"))
+               g_test_fail ();
+}
+
+static void
+test_emoticon_insert_typed (TestFixture *fixture)
+{
+       if (!test_utils_run_simple_test (fixture,
+               "mode:html\n"
+               "type:before :)after\n",
+               HTML_PREFIX "<p>before <img src=\"data:image/png;base64,"
+               "iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABHNCSVQICAgIfA"
+               "hkiAAAAAlwSFlzAAAN1wAADdcBQiibeAAAABl0RVh0U29mdHdhcmUAd3d3Lmlu"
+               "a3NjYXBlLm9yZ5vuPBoAAAAXdEVYdEF1dGhvcgBMYXBvIENhbGFtYW5kcmVp35"
+               "EaKgAAACl0RVh0RGVzY3JpcHRpb24AQmFzZWQgb2YgSmFrdWIgU3RlaW5lciBk"
+               "ZXNpZ26ghAVzAAADRklEQVQ4jWWTXWiVdQCHn//7ec4+zmrbcZ+usT4wy23ilj"
+               "K3cmIRIl10GYGgVCMovOjjMsSrwGEFSXohMchCKBBUWEFqrQ10yTbHNh2bp03n"
+               "vrdztvN+/t/330U3C3/XD8/FDx6hlGLrLnzeXq0L/R1bVwfQxB4AYvW3H4kbkY"
+               "ouHvuyb24rL7YKej7rOJYsKv7m5Vf22eW1VUZheRqUIr+8yNLsYzk00O/nNzc/"
+               "fu9M/4UnBD982nat9oXdnS0HX08I/w7CWgRLAgp8E8JKIrOJvt6rXmZ8+HrXt7"
+               "cPA2gA33/S3lXz3K7O1vb9iWj6PFd+vcaVgQxaugyRLoMSxenvfuTu72d47eCh"
+               "RE3Diwe632/pAtDOn+io061k9562NxLe3XPE0QK3J/LcHllH2UmwCxAFRfw1km"
+               "Po3gze6FlePXQkqZt298mjLXVaKMPjzc177XjmOnHuAQJI2BoJWwcVI5QCFZOw"
+               "DCxLIHMZePgH+/d12FEkjxuRDDrLKrbrwUQvQsTg+xxpT6OltyG83H9PbWZ596"
+               "16GlPrCG+N4NEtKp49qkcy6DQCpTcWPVXB+uI0q2jUFHrsrHLRyx3ihVkEArWx"
+               "wZsvScIFH5mTLMxPUbwjRSC1RiOUwvCyC+ihz9OFNlf716mtlWyvd6moKsQ0BM"
+               "r1eTiT5d5Ejrl5l+ZSRaWlEUTKMFzXGc3cH9pba5RgyU0O7ypnaDnBz79lWd2Y"
+               "oyChIaRCKEFjXQmdO1OkYsnU2C183xs1vIib45P3W9OFBZrlriCyKzSlK9nd2A"
+               "ZFNfjuBpZdjB75+JkB5KNxfLue4fHx2JPcNGSkekamFj+qbtCTOyIF+KhonnC1"
+               "F19LEGOioggt2MCWEt2PeSBN+kcyfiC1Hn1gbHHpcs/X1j8rbmupuW6mlESPFF"
+               "ocEwcBkesifAc7DFFOQMaBS2Oak3Pj0z/dmL6kAVTkq09lA3Py8ly1M7hmMJ8L"
+               "8bMu5qZDgeti5F3WciF3VjUuTpY6yw6TS/rMqf+18EFLi5lPrZxUSp14piiXqE"
+               "n6ojLpA/DYsZh1bDWVLfIU4qtyb9sX5wYHwydqBHi7o6FJI/xQINqjWDwPoGtq"
+               "UqH6Ysyzv/w5PbyV/xd0ZaEGG/mx/wAAAABJRU5ErkJggg==\" alt=\":-)\">"
+               "after</p>" HTML_SUFFIX,
+               "before :-)after"))
+               g_test_fail ();
+}
+
+static void
+test_emoticon_insert_typed_dash (TestFixture *fixture)
+{
+       if (!test_utils_run_simple_test (fixture,
+               "mode:html\n"
+               "type:before :-)after\n",
+               HTML_PREFIX "<p>before <img src=\"data:image/png;base64,"
+               "iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABHNCSVQICAgIfA"
+               "hkiAAAAAlwSFlzAAAN1wAADdcBQiibeAAAABl0RVh0U29mdHdhcmUAd3d3Lmlu"
+               "a3NjYXBlLm9yZ5vuPBoAAAAXdEVYdEF1dGhvcgBMYXBvIENhbGFtYW5kcmVp35"
+               "EaKgAAACl0RVh0RGVzY3JpcHRpb24AQmFzZWQgb2YgSmFrdWIgU3RlaW5lciBk"
+               "ZXNpZ26ghAVzAAADRklEQVQ4jWWTXWiVdQCHn//7ec4+zmrbcZ+usT4wy23ilj"
+               "K3cmIRIl10GYGgVCMovOjjMsSrwGEFSXohMchCKBBUWEFqrQ10yTbHNh2bp03n"
+               "vrdztvN+/t/330U3C3/XD8/FDx6hlGLrLnzeXq0L/R1bVwfQxB4AYvW3H4kbkY"
+               "ouHvuyb24rL7YKej7rOJYsKv7m5Vf22eW1VUZheRqUIr+8yNLsYzk00O/nNzc/"
+               "fu9M/4UnBD982nat9oXdnS0HX08I/w7CWgRLAgp8E8JKIrOJvt6rXmZ8+HrXt7"
+               "cPA2gA33/S3lXz3K7O1vb9iWj6PFd+vcaVgQxaugyRLoMSxenvfuTu72d47eCh"
+               "RE3Diwe632/pAtDOn+io061k9562NxLe3XPE0QK3J/LcHllH2UmwCxAFRfw1km"
+               "Po3gze6FlePXQkqZt298mjLXVaKMPjzc177XjmOnHuAQJI2BoJWwcVI5QCFZOw"
+               "DCxLIHMZePgH+/d12FEkjxuRDDrLKrbrwUQvQsTg+xxpT6OltyG83H9PbWZ596"
+               "16GlPrCG+N4NEtKp49qkcy6DQCpTcWPVXB+uI0q2jUFHrsrHLRyx3ihVkEArWx"
+               "wZsvScIFH5mTLMxPUbwjRSC1RiOUwvCyC+ihz9OFNlf716mtlWyvd6moKsQ0BM"
+               "r1eTiT5d5Ejrl5l+ZSRaWlEUTKMFzXGc3cH9pba5RgyU0O7ypnaDnBz79lWd2Y"
+               "oyChIaRCKEFjXQmdO1OkYsnU2C183xs1vIib45P3W9OFBZrlriCyKzSlK9nd2A"
+               "ZFNfjuBpZdjB75+JkB5KNxfLue4fHx2JPcNGSkekamFj+qbtCTOyIF+KhonnC1"
+               "F19LEGOioggt2MCWEt2PeSBN+kcyfiC1Hn1gbHHpcs/X1j8rbmupuW6mlESPFF"
+               "ocEwcBkesifAc7DFFOQMaBS2Oak3Pj0z/dmL6kAVTkq09lA3Py8ly1M7hmMJ8L"
+               "8bMu5qZDgeti5F3WciF3VjUuTpY6yw6TS/rMqf+18EFLi5lPrZxUSp14piiXqE"
+               "n6ojLpA/DYsZh1bDWVLfIU4qtyb9sX5wYHwydqBHi7o6FJI/xQINqjWDwPoGtq"
+               "UqH6Ysyzv/w5PbyV/xd0ZaEGG/mx/wAAAABJRU5ErkJggg==\" alt=\":-)\">"
+               "after</p>" HTML_SUFFIX,
+               "before :-)after"))
+               g_test_fail ();
+}
+
+static void
+test_paragraph_normal_selection (TestFixture *fixture)
+{
+       if (!test_utils_run_simple_test (fixture,
+               "mode:html\n"
+               "action:style-preformat\n"
+               "type:Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer nec odio. Praesent 
libero.\\n\n"
+               "type:Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer nec odio. Praesent 
libero.\n"
+               "seq:hu\n"
+               "action:style-normal\n",
+               HTML_PREFIX "<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer nec odio. 
Praesent libero.</p>"
+               "<pre>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer nec odio. Praesent 
libero.</pre>" HTML_SUFFIX,
+               "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer nec\n"
+               "odio. Praesent libero.\n"
+               "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer nec odio. Praesent 
libero.")) {
+               g_test_fail ();
+               return;
+       }
+
+       if (!test_utils_run_simple_test (fixture,
+               "mode:plain\n",
+               HTML_PREFIX_PLAIN "<p style=\"width: 71ch;\">Lorem ipsum dolor sit amet, consectetur 
adipiscing elit. Integer nec odio. Praesent libero.</p>"
+               "<pre>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer nec odio. Praesent 
libero.</pre>" HTML_SUFFIX,
+               "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer nec\n"
+               "odio. Praesent libero.\n"
+               "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer nec odio. Praesent 
libero.")) {
+               g_test_fail ();
+               return;
+       }
+
+       if (!test_utils_run_simple_test (fixture,
+               "mode:html\n",
+               HTML_PREFIX "<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer nec odio. 
Praesent libero.</p>"
+               "<pre>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer nec odio. Praesent 
libero.</pre>" HTML_SUFFIX,
+               "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer nec\n"
+               "odio. Praesent libero.\n"
+               "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer nec odio. Praesent 
libero.")) {
+               g_test_fail ();
+               return;
+       }
+}
+
+static void
+test_paragraph_normal_typed (TestFixture *fixture)
+{
+       if (!test_utils_run_simple_test (fixture,
+               "mode:html\n"
+               "action:style-normal\n"
+               "type:Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer nec odio. Praesent 
libero.\\n\n"
+               "type:Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer nec odio. Praesent 
libero.\n",
+               HTML_PREFIX "<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer nec odio. 
Praesent libero.</p>"
+               "<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer nec odio. Praesent 
libero.</p>" HTML_SUFFIX,
+               "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer nec\n"
+               "odio. Praesent libero.\n"
+               "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer nec\n"
+               "odio. Praesent libero.")) {
+               g_test_fail ();
+               return;
+       }
+
+       if (!test_utils_run_simple_test (fixture,
+               "mode:plain\n",
+               HTML_PREFIX_PLAIN "<p style=\"width: 71ch;\">Lorem ipsum dolor sit amet, consectetur 
adipiscing elit. Integer nec odio. Praesent libero.</p>"
+               "<p style=\"width: 71ch;\">Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer 
nec odio. Praesent libero.</p>" HTML_SUFFIX,
+               "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer nec\n"
+               "odio. Praesent libero.\n"
+               "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer nec\n"
+               "odio. Praesent libero.")) {
+               g_test_fail ();
+               return;
+       }
+
+       if (!test_utils_run_simple_test (fixture,
+               "mode:html\n",
+               HTML_PREFIX "<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer nec odio. 
Praesent libero.</p>"
+               "<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer nec odio. Praesent 
libero.</p>" HTML_SUFFIX,
+               "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer nec\n"
+               "odio. Praesent libero.\n"
+               "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer nec\n"
+               "odio. Praesent libero.")) {
+               g_test_fail ();
+               return;
+       }
+}
+
+static void
+test_paragraph_preformatted_selection (TestFixture *fixture)
+{
+       if (!test_utils_run_simple_test (fixture,
+               "mode:html\n"
+               "action:style-normal\n"
+               "type:Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer nec odio. Praesent 
libero.\\n\n"
+               "type:Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer nec odio. Praesent 
libero.\n"
+               "seq:Chc\n"
+               "action:style-preformat\n",
+               HTML_PREFIX "<pre>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer nec odio. 
Praesent libero.</pre>"
+               "<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer nec odio. Praesent 
libero.</p>" HTML_SUFFIX,
+               "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer nec odio. Praesent 
libero.\n"
+               "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer nec\n"
+               "odio. Praesent libero.")) {
+               g_test_fail ();
+               return;
+       }
+
+       if (!test_utils_run_simple_test (fixture,
+               "mode:plain\n",
+               HTML_PREFIX_PLAIN "<pre>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer nec 
odio. Praesent libero.</pre>"
+               "<p style=\"width: 71ch;\">Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer 
nec odio. Praesent libero.</p>" HTML_SUFFIX,
+               "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer nec odio. Praesent 
libero.\n"
+               "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer nec\n"
+               "odio. Praesent libero.")) {
+               g_test_fail ();
+               return;
+       }
+
+       if (!test_utils_run_simple_test (fixture,
+               "mode:html\n",
+               HTML_PREFIX "<pre>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer nec odio. 
Praesent libero.</pre>"
+               "<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer nec odio. Praesent 
libero.</p>" HTML_SUFFIX,
+               "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer nec odio. Praesent 
libero.\n"
+               "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer nec\n"
+               "odio. Praesent libero.")) {
+               g_test_fail ();
+               return;
+       }
+}
+
+static void
+test_paragraph_preformatted_typed (TestFixture *fixture)
+{
+       if (!test_utils_run_simple_test (fixture,
+               "mode:html\n"
+               "action:style-preformat\n"
+               "type:Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer nec odio. Praesent 
libero. \n"
+               "type:Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer nec odio. Praesent 
libero.\n",
+               HTML_PREFIX "<pre>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer nec odio. 
Praesent libero."
+               " Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer nec odio. Praesent 
libero.</pre>" HTML_SUFFIX,
+               "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer nec odio. Praesent libero. "
+               "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer nec odio. Praesent 
libero.")) {
+               g_test_fail ();
+               return;
+       }
+
+       if (!test_utils_run_simple_test (fixture,
+               "mode:plain\n",
+               HTML_PREFIX_PLAIN "<pre>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer nec 
odio. Praesent libero."
+               " Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer nec odio. Praesent 
libero.</pre>" HTML_SUFFIX,
+               "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer nec odio. Praesent libero. "
+               "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer nec odio. Praesent 
libero.")) {
+               g_test_fail ();
+               return;
+       }
+
+       if (!test_utils_run_simple_test (fixture,
+               "mode:html\n",
+               HTML_PREFIX "<pre>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer nec odio. 
Praesent libero."
+               " Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer nec odio. Praesent 
libero.</pre>" HTML_SUFFIX,
+               "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer nec odio. Praesent libero. "
+               "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer nec odio. Praesent 
libero.")) {
+               g_test_fail ();
+               return;
+       }
+}
+
+static void
+test_paragraph_address_selection (TestFixture *fixture)
+{
+       if (!test_utils_run_simple_test (fixture,
+               "mode:html\n"
+               "action:style-normal\n"
+               "type:normal text\\n\n"
+               "type:address line 1\\n\n"
+               "type:address line 2\\n\n"
+               "type:address line 3\\n\n"
+               "type:\\n\n"
+               "type:normal text\n"
+               "seq:huuuuSddrs\n"
+               "action:style-address\n",
+               HTML_PREFIX "<p>normal text</p>"
+               "<address>address line 1</address>"
+               "<address>address line 2</address>"
+               "<address>address line 3</address>"
+               "<p><br></p>"
+               "<p>normal text</p>" HTML_SUFFIX,
+               "normal text\n"
+               "address line 1\n"
+               "address line 2\n"
+               "address line 3\n"
+               "\n"
+               "normal text"))
+               g_test_fail ();
+}
+
+static void
+test_paragraph_address_typed (TestFixture *fixture)
+{
+       if (!test_utils_run_simple_test (fixture,
+               "mode:html\n"
+               "action:style-normal\n"
+               "type:normal text\\n\n"
+               "action:style-address\n"
+               "type:address line 1\\n\n"
+               "type:address line 2\\n\n"
+               "type:address line 3\\n\n"
+               "action:style-normal\n"
+               "type:\\n\n"
+               "type:normal text\n",
+               HTML_PREFIX "<p>normal text</p>"
+               "<address>address line 1</address>"
+               "<address>address line 2</address>"
+               "<address>address line 3</address>"
+               "<p><br></p>"
+               "<p>normal text</p>" HTML_SUFFIX,
+               "normal text\n"
+               "address line 1\n"
+               "address line 2\n"
+               "address line 3\n"
+               "normal text"))
+               g_test_fail ();
+}
+
+static gboolean
+test_paragraph_header_n_selection (TestFixture *fixture,
+                                  gint header_n)
+{
+       gchar *actions, *expected_html, *expected_plain;
+       gboolean success;
+
+       actions = g_strdup_printf (
+               "mode:html\n"
+               "action:style-normal\n"
+               "type:normal text\\n\n"
+               "type:header %d\\n\n"
+               "type:normal text\n"
+               "seq:hu\n"
+               "action:style-h%d\n",
+               header_n, header_n);
+
+       expected_html = g_strdup_printf (
+               HTML_PREFIX "<p>normal text</p>"
+               "<h%d>header %d</h%d>"
+               "<p>normal text</p>" HTML_SUFFIX,
+               header_n, header_n, header_n);
+
+       expected_plain = g_strdup_printf (
+               "normal text\n"
+               "header %d\n"
+               "normal text",
+               header_n);
+
+       success = test_utils_run_simple_test (fixture, actions, expected_html, expected_plain);
+
+       g_free (expected_plain);
+       g_free (expected_html);
+       g_free (actions);
+
+       if (!success)
+               return success;
+
+       expected_html = g_strdup_printf (
+               HTML_PREFIX "<p>normal text</p>"
+               "<h%d>header %d</h%d>"
+               "<p><br></p>"
+               "<p>normal text</p>" HTML_SUFFIX,
+               header_n, header_n, header_n);
+
+       expected_plain = g_strdup_printf (
+               "normal text\n"
+               "header %d\n"
+               "\n"
+               "normal text",
+               header_n);
+
+       success = test_utils_run_simple_test (fixture,
+               "seq:h\n"
+               "type:\\n\n",
+               expected_html, expected_plain);
+
+       g_free (expected_plain);
+       g_free (expected_html);
+
+       return success;
+}
+
+static gboolean
+test_paragraph_header_n_typed (TestFixture *fixture,
+                              gint header_n)
+{
+       gchar *actions, *expected_html, *expected_plain;
+       gboolean success;
+
+       actions = g_strdup_printf (
+               "mode:html\n"
+               "action:style-normal\n"
+               "type:normal text\\n\n"
+               "action:style-h%d\n"
+               "type:header %d\\n\n"
+               "action:style-normal\n"
+               "type:normal text\n",
+               header_n, header_n);
+
+       expected_html = g_strdup_printf (
+               HTML_PREFIX "<p>normal text</p>"
+               "<h%d>header %d</h%d>"
+               "<p>normal text</p>" HTML_SUFFIX,
+               header_n, header_n, header_n);
+
+       expected_plain = g_strdup_printf (
+               "normal text\n"
+               "header %d\n"
+               "normal text",
+               header_n);
+
+       success = test_utils_run_simple_test (fixture, actions, expected_html, expected_plain);
+
+       g_free (expected_plain);
+       g_free (expected_html);
+       g_free (actions);
+
+       if (!success)
+               return success;
+
+       expected_html = g_strdup_printf (
+               HTML_PREFIX "<p>normal text</p>"
+               "<h%d>header %d</h%d>"
+               "<p><br></p>"
+               "<p>normal text</p>" HTML_SUFFIX,
+               header_n, header_n, header_n);
+
+       expected_plain = g_strdup_printf (
+               "normal text\n"
+               "header %d\n"
+               "\n"
+               "normal text",
+               header_n);
+
+       success = test_utils_run_simple_test (fixture,
+               "seq:h\n"
+               "type:\\n\n",
+               expected_html, expected_plain);
+
+       g_free (expected_plain);
+       g_free (expected_html);
+
+       return success;
+}
+
+static void
+test_paragraph_header1_selection (TestFixture *fixture)
+{
+       if (!test_paragraph_header_n_selection (fixture, 1))
+               g_test_fail ();
+}
+
+static void
+test_paragraph_header1_typed (TestFixture *fixture)
+{
+       if (!test_paragraph_header_n_typed (fixture, 1))
+               g_test_fail ();
+}
+
+static void
+test_paragraph_header2_selection (TestFixture *fixture)
+{
+       if (!test_paragraph_header_n_selection (fixture, 2))
+               g_test_fail ();
+}
+
+static void
+test_paragraph_header2_typed (TestFixture *fixture)
+{
+       if (!test_paragraph_header_n_typed (fixture, 2))
+               g_test_fail ();
+}
+
+static void
+test_paragraph_header3_selection (TestFixture *fixture)
+{
+       if (!test_paragraph_header_n_selection (fixture, 3))
+               g_test_fail ();
+}
+
+static void
+test_paragraph_header3_typed (TestFixture *fixture)
+{
+       if (!test_paragraph_header_n_typed (fixture, 3))
+               g_test_fail ();
+}
+
+static void
+test_paragraph_header4_selection (TestFixture *fixture)
+{
+       if (!test_paragraph_header_n_selection (fixture, 4))
+               g_test_fail ();
+}
+
+static void
+test_paragraph_header4_typed (TestFixture *fixture)
+{
+       if (!test_paragraph_header_n_typed (fixture, 4))
+               g_test_fail ();
+}
+
+static void
+test_paragraph_header5_selection (TestFixture *fixture)
+{
+       if (!test_paragraph_header_n_selection (fixture, 5))
+               g_test_fail ();
+}
+
+static void
+test_paragraph_header5_typed (TestFixture *fixture)
+{
+       if (!test_paragraph_header_n_typed (fixture, 5))
+               g_test_fail ();
+}
+
+static void
+test_paragraph_header6_selection (TestFixture *fixture)
+{
+       if (!test_paragraph_header_n_selection (fixture, 6))
+               g_test_fail ();
+}
+
+static void
+test_paragraph_header6_typed (TestFixture *fixture)
+{
+       if (!test_paragraph_header_n_typed (fixture, 6))
+               g_test_fail ();
+}
+
+static void
+test_paragraph_wrap_lines (TestFixture *fixture)
+{
+       if (!test_utils_run_simple_test (fixture,
+               "mode:html\n"
+               "type:Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer nec odio. Praesent 
libero.\\n\n"
+               "type:Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer nec odio. Praesent 
libero.\n"
+               "action:select-all\n"
+               "action:wrap-lines\n",
+               HTML_PREFIX "<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer nec<br>odio. 
Praesent libero.</p>"
+               "<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer nec<br>odio. Praesent 
libero.</p>" HTML_SUFFIX,
+               "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer nec\n" "odio. Praesent 
libero.\n"
+               "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer nec\n" "odio. Praesent 
libero."))
+               g_test_fail ();
+}
+
+static void
+test_paste_singleline_html2html (TestFixture *fixture)
+{
+       test_utils_set_clipboard_text ("<html><body>some <b>bold</b> text</body></html>", TRUE);
+
+       if (!test_utils_run_simple_test (fixture,
+               "mode:html\n"
+               "type:text before \n"
+               "action:paste\n"
+               "type: text after\n",
+               HTML_PREFIX "<p>text before some <b>bold</b> text text after</p>" HTML_SUFFIX,
+               "text before some bold text text after"))
+               g_test_fail ();
+}
+
+static void
+test_paste_singleline_html2plain (TestFixture *fixture)
+{
+       test_utils_set_clipboard_text ("<html><body>some <b>bold</b> text</body></html>", TRUE);
+
+       if (!test_utils_run_simple_test (fixture,
+               "mode:plain\n"
+               "type:text before \n"
+               "action:paste\n"
+               "type: text after\n",
+               HTML_PREFIX_PLAIN "<p style=\"width: 71ch;\">text before some bold text text after</p>" 
HTML_SUFFIX,
+               "text before some bold text text after"))
+               g_test_fail ();
+}
+
+static void
+test_paste_singleline_plain2html (TestFixture *fixture)
+{
+       test_utils_set_clipboard_text ("some plain text", FALSE);
+
+       if (!test_utils_run_simple_test (fixture,
+               "mode:html\n"
+               "type:text before \n"
+               "action:paste\n"
+               "type: text after\n",
+               HTML_PREFIX "<p>text before some plain text text after</p>" HTML_SUFFIX,
+               "text before some plain text text after"))
+               g_test_fail ();
+}
+
+static void
+test_paste_singleline_plain2plain (TestFixture *fixture)
+{
+       test_utils_set_clipboard_text ("some plain text", FALSE);
+
+       if (!test_utils_run_simple_test (fixture,
+               "mode:plain\n"
+               "type:text before \n"
+               "action:paste\n"
+               "type: text after\n",
+               HTML_PREFIX_PLAIN "<p style=\"width: 71ch;\">text before some plain text text after</p>" 
HTML_SUFFIX,
+               "text before some plain text text after"))
+               g_test_fail ();
+}
+
+static void
+test_paste_multiline_html2html (TestFixture *fixture)
+{
+       test_utils_set_clipboard_text ("<html><body><b>bold</b> text<br><i>italic</i> 
text<br><u>underline</u> text<br></body></html>", TRUE);
+
+       if (!test_utils_run_simple_test (fixture,
+               "mode:html\n"
+               "type:text before \n"
+               "action:paste\n"
+               "type:text after\n",
+               HTML_PREFIX "<p>text before <b>bold</b> text</p><p><i>italic</i> text</p><p><u>underline</u> 
text</p><p>text after</p>" HTML_SUFFIX,
+               "text before bold text\nitalic text\nunderline text\ntext after"))
+               g_test_fail ();
+}
+
+static void
+test_paste_multiline_html2plain (TestFixture *fixture)
+{
+       test_utils_set_clipboard_text ("<html><body><b>bold</b> text<br><i>italic</i> 
text<br><u>underline</u> text</body></html>", TRUE);
+
+       if (!test_utils_run_simple_test (fixture,
+               "mode:plain\n"
+               "type:text before \n"
+               "action:paste\n"
+               "type:\\ntext after\n",
+               HTML_PREFIX_PLAIN "<p style=\"width: 71ch;\">text before bold text</p>"
+               "<p style=\"width: 71ch;\">italic text</p>"
+               "<p style=\"width: 71ch;\">underline text</p>"
+               "<p style=\"width: 71ch;\">text after</p>" HTML_SUFFIX,
+               "text before bold text\nitalic text\nunderline text\ntext after"))
+               g_test_fail ();
+}
+
+static void
+test_paste_multiline_div_html2html (TestFixture *fixture)
+{
+       test_utils_set_clipboard_text ("<html><body><div><b>bold</b> text</div><div><i>italic</i> 
text</div><div><u>underline</u> text</div><div></div></body></html>", TRUE);
+
+       if (!test_utils_run_simple_test (fixture,
+               "mode:html\n"
+               "type:text before \n"
+               "action:paste\n"
+               "type:text after\n",
+               HTML_PREFIX "<p>text before <b>bold</b> text</p><p><i>italic</i> text</p><p><u>underline</u> 
text</p><p>text after</p>" HTML_SUFFIX,
+               "text before bold text\nitalic text\nunderline text\ntext after"))
+               g_test_fail ();
+}
+
+static void
+test_paste_multiline_div_html2plain (TestFixture *fixture)
+{
+       test_utils_set_clipboard_text ("<html><body><div><b>bold</b> text</div><div><i>italic</i> 
text</div><div><u>underline</u> text</div></body></html>", TRUE);
+
+       if (!test_utils_run_simple_test (fixture,
+               "mode:plain\n"
+               "type:text before \n"
+               "action:paste\n"
+               "type:\\ntext after\n",
+               HTML_PREFIX_PLAIN "<p style=\"width: 71ch;\">text before bold text</p>"
+               "<p style=\"width: 71ch;\">italic text</p>"
+               "<p style=\"width: 71ch;\">underline text</p>"
+               "<p style=\"width: 71ch;\">text after</p>" HTML_SUFFIX,
+               "text before bold text\nitalic text\nunderline text\ntext after"))
+               g_test_fail ();
+}
+
+static void
+test_paste_multiline_p_html2html (TestFixture *fixture)
+{
+       test_utils_set_clipboard_text ("<html><body><p><b>bold</b> text</p><p><i>italic</i> 
text</p><p><u>underline</u> text</p><p></p></body></html>", TRUE);
+
+       if (!test_utils_run_simple_test (fixture,
+               "mode:html\n"
+               "type:text before \n"
+               "action:paste\n"
+               "type:text after\n",
+               HTML_PREFIX "<p>text before <b>bold</b> text</p><p><i>italic</i> text</p><p><u>underline</u> 
text</p><p>text after</p>" HTML_SUFFIX,
+               "text before bold text\nitalic text\nunderline text\ntext after"))
+               g_test_fail ();
+}
+
+static void
+test_paste_multiline_p_html2plain (TestFixture *fixture)
+{
+       test_utils_set_clipboard_text ("<html><body><p><b>bold</b> text</p><p><i>italic</i> 
text</p><p><u>underline</u> text</p></body></html>", TRUE);
+
+       if (!test_utils_run_simple_test (fixture,
+               "mode:plain\n"
+               "type:text before \n"
+               "action:paste\n"
+               "type:\\ntext after\n",
+               HTML_PREFIX_PLAIN "<p style=\"width: 71ch;\">text before bold text</p>"
+               "<p style=\"width: 71ch;\">italic text</p>"
+               "<p style=\"width: 71ch;\">underline text</p>"
+               "<p style=\"width: 71ch;\">text after</p>" HTML_SUFFIX,
+               "text before bold text\nitalic text\nunderline text\ntext after"))
+               g_test_fail ();
+}
+
+static void
+test_paste_multiline_plain2html (TestFixture *fixture)
+{
+       test_utils_set_clipboard_text ("line 1\nline 2\nline 3\n", FALSE);
+
+       if (!test_utils_run_simple_test (fixture,
+               "mode:html\n"
+               "type:text before \n"
+               "action:paste\n"
+               "type:text after\n",
+               HTML_PREFIX "<p>text before line 1</p><p>line 2</p><p>line 3</p><p>text after</p>" 
HTML_SUFFIX,
+               "text before line 1\nline 2\nline 3\ntext after"))
+               g_test_fail ();
+}
+
+static void
+test_paste_multiline_plain2plain (TestFixture *fixture)
+{
+       test_utils_set_clipboard_text ("line 1\nline 2\nline 3", FALSE);
+
+       if (!test_utils_run_simple_test (fixture,
+               "mode:plain\n"
+               "type:text before \n"
+               "action:paste\n"
+               "type:\\ntext after\n",
+               HTML_PREFIX_PLAIN "<p style=\"width: 71ch;\">text before line 1</p>"
+               "<p style=\"width: 71ch;\">line 2</p>"
+               "<p style=\"width: 71ch;\">line 3</p>"
+               "<p style=\"width: 71ch;\">text after</p>" HTML_SUFFIX,
+               "text before line 1\nline 2\nline 3\ntext after"))
+               g_test_fail ();
+}
+
+static void
+test_paste_quoted_singleline_html2html (TestFixture *fixture)
+{
+       test_utils_set_clipboard_text ("<html><body>some <b>bold</b> text</body></html>", TRUE);
+
+       if (!test_utils_run_simple_test (fixture,
+               "mode:html\n"
+               "type:text before \n"
+               "action:paste-quote\n"
+               "type:\\n\n" /* stop quotting */
+               "type:text after\n",
+               HTML_PREFIX "<p>text before </p>"
+               "<blockquote type=\"cite\"><p>some <b>bold</b> text</p></blockquote>"
+               "<p>text after</p>" HTML_SUFFIX,
+               "text before \n"
+               "> some bold text\n"
+               "text after"))
+               g_test_fail ();
+}
+
+static void
+test_paste_quoted_singleline_html2plain (TestFixture *fixture)
+{
+       test_utils_set_clipboard_text ("<html><body>some <b>bold</b> text</body></html>", TRUE);
+
+       if (!test_utils_run_simple_test (fixture,
+               "mode:plain\n"
+               "type:text before \n"
+               "action:paste-quote\n"
+               "type:\\n\n" /* stop quotting */
+               "type:text after\n",
+               HTML_PREFIX_PLAIN "<p style=\"width: 71ch;\">text before </p>"
+               "<blockquote type=\"cite\"><p style=\"width: 71ch;\">&gt; some <b>bold</b> 
text</p></blockquote>"
+               "<p style=\"width: 71ch;\">text after</p>" HTML_SUFFIX,
+               "text before \n"
+               "> some bold text\n"
+               "text after"))
+               g_test_fail ();
+}
+
+static void
+test_paste_quoted_singleline_plain2html (TestFixture *fixture)
+{
+       test_utils_set_clipboard_text ("some plain text", FALSE);
+
+       if (!test_utils_run_simple_test (fixture,
+               "mode:html\n"
+               "type:text before \n"
+               "action:paste-quote\n"
+               "type:\\n\n" /* stop quotting */
+               "type:text after\n",
+               HTML_PREFIX "<p>text before </p>"
+               "<blockquote type=\"cite\"><p>some plain text</p></blockquote>"
+               "<p>text after</p>" HTML_SUFFIX,
+               "text before \n"
+               "> some plain text\n"
+               "text after"))
+               g_test_fail ();
+}
+
+static void
+test_paste_quoted_singleline_plain2plain (TestFixture *fixture)
+{
+       test_utils_set_clipboard_text ("some plain text", FALSE);
+
+       if (!test_utils_run_simple_test (fixture,
+               "mode:plain\n"
+               "type:text before \n"
+               "action:paste-quote\n"
+               "type:\\n\n" /* stop quotting */
+               "type:text after\n",
+               HTML_PREFIX_PLAIN "<p style=\"width: 71ch;\">text before </p>"
+               "<blockquote type=\"cite\"><p style=\"width: 71ch;\">&gt; some plain text</p></blockquote>"
+               "<p style=\"width: 71ch;\">text after</p>" HTML_SUFFIX,
+               "text before \n"
+               "> some plain text\n"
+               "text after"))
+               g_test_fail ();
+}
+
+static void
+test_paste_quoted_multiline_html2html (TestFixture *fixture)
+{
+       test_utils_set_clipboard_text ("<html><body><b>bold</b> text<br><i>italic</i> 
text<br><u>underline</u> text<br></body></html>", TRUE);
+
+       if (!test_utils_run_simple_test (fixture,
+               "mode:html\n"
+               "type:text before \n"
+               "action:paste-quote\n"
+               "seq:b\n" /* stop quotting */
+               "type:text after\n",
+               HTML_PREFIX "<p>text before </p>"
+               "<blockquote type=\"cite\">&gt; <b>bold</b> text</p>"
+               "<p>&gt; <i>italic</i> text</p>"
+               "<p>&gt; <u>underline</u> text</p></blockquote>"
+               "<p>text after</p>" HTML_SUFFIX,
+               "text before \n"
+               "> bold text\n"
+               "> italic text\n"
+               "> underline text\n"
+               "text after"))
+               g_test_fail ();
+}
+
+static void
+test_paste_quoted_multiline_html2plain (TestFixture *fixture)
+{
+       test_utils_set_clipboard_text ("<html><body><b>bold</b> text<br><i>italic</i> 
text<br><u>underline</u> text</body></html>", TRUE);
+
+       if (!test_utils_run_simple_test (fixture,
+               "mode:plain\n"
+               "type:text before \n"
+               "action:paste-quote\n"
+               "type:\\n\n" /* stop quotting */
+               "type:text after\n",
+               HTML_PREFIX_PLAIN "<p style=\"width: 71ch;\">text before </p>"
+               "<blockquote type=\"cite\"><p>&gt; bold text</p>"
+               "<p style=\"width: 71ch;\">&gt; italic text</p>"
+               "<p style=\"width: 71ch;\">&gt; underline text</p></blockquote>"
+               "<p style=\"width: 71ch;\">&gt; text after</p>" HTML_SUFFIX,
+               "text before \n"
+               "> bold text\n"
+               "> italic text\n"
+               "> underline text\n"
+               "text after"))
+               g_test_fail ();
+}
+
+static void
+test_paste_quoted_multiline_plain2html (TestFixture *fixture)
+{
+       test_utils_set_clipboard_text ("line 1\nline 2\nline 3\n", FALSE);
+
+       if (!test_utils_run_simple_test (fixture,
+               "mode:html\n"
+               "type:text before \n"
+               "action:paste-quote\n"
+               "seq:b\n" /* stop quotting */
+               "type:text after\n",
+               HTML_PREFIX "<p>text before </p>"
+               "<blockquote type=\"cite\"><p>line 1</p>"
+               "<p>line 2</p>"
+               "<p>line 3</p></blockquote>"
+               "<p>text after</p>" HTML_SUFFIX,
+               "text before \n"
+               "> line 1\n"
+               "> line 2\n"
+               "> line 3\n"
+               "text after"))
+               g_test_fail ();
+}
+
+static void
+test_paste_quoted_multiline_plain2plain (TestFixture *fixture)
+{
+       test_utils_set_clipboard_text ("line 1\nline 2\nline 3", FALSE);
+
+       if (!test_utils_run_simple_test (fixture,
+               "mode:plain\n"
+               "type:text before \n"
+               "action:paste-quote\n"
+               "type:\\n\n" /* stop quotting */
+               "type:text after\n",
+               HTML_PREFIX_PLAIN "<p style=\"width: 71ch;\">text before </p>"
+               "<blockquote type=\"cite\"><p style=\"width: 71ch;\">&gt; line 1</p>"
+               "<p style=\"width: 71ch;\">&gt; line 2</p>"
+               "<p style=\"width: 71ch;\">&gt; line 3</p></blockquote>"
+               "<p style=\"width: 71ch;\">text after</p>" HTML_SUFFIX,
+               "text before\n"
+               "> line 1\n"
+               "> line 2\n"
+               "> line 3\n"
+               "text after"))
+               g_test_fail ();
+}
+
+static void
+test_cite_html2plain (TestFixture *fixture)
+{
+       if (!test_utils_process_commands (fixture,
+               "mode:html\n")) {
+               g_test_fail ();
+               return;
+       }
+
+       test_utils_insert_content (fixture,
+               "<html><head></head><body>"
+               "<blockquote type=\"cite\">"
+               "<p>level 1</p>"
+               "<p><br></p>"
+               "<p>level 1</p>"
+               "<blockquote type=\"cite\">"
+               "<p>level 2</p>"
+               "</blockquote>"
+               "<p>back in level 1</p>"
+               "</blockquote>"
+               "<p><br></p>"
+               "<p>out of the citation</p>"
+               "</body></html>",
+               E_CONTENT_EDITOR_INSERT_REPLACE_ALL | E_CONTENT_EDITOR_INSERT_TEXT_HTML);
+
+       /* Just check the content was read properly */
+       if (!test_utils_run_simple_test (fixture,
+               "",
+               HTML_PREFIX "<blockquote type=\"cite\"><p>level 1</p><p><br></p><p>level 1</p>"
+               "<blockquote type=\"cite\"><p>level 2</p></blockquote><p>back in level 1</p></blockquote>"
+               "<p><br></p><p>out of the citation</p>" HTML_SUFFIX,
+               "> level 1\n"
+               ">\n"
+               "> level 1\n"
+               "> > level 2\n"
+               "> back in level 1\n"
+               "\n"
+               "out of the citation")) {
+               g_test_fail ();
+               return;
+       }
+
+       if (!test_utils_run_simple_test (fixture,
+               "mode:plain\n",
+               HTML_PREFIX_PLAIN ,
+               "> level 1\n"
+               ">\n"
+               "> level 1\n"
+               "> > level 2\n"
+               "> back in level 1\n"
+               "\n"
+               "out of the citation")) {
+               g_test_fail ();
+       }
+}
+
+static void
+test_cite_shortline (TestFixture *fixture)
+{
+       if (!test_utils_process_commands (fixture,
+               "mode:html\n")) {
+               g_test_fail ();
+               return;
+       }
+
+       test_utils_insert_content (fixture,
+               "<html><head></head><body><blockquote type=\"cite\">"
+               "<p>Just one short line.</p>"
+               "</blockquote></body></html>",
+               E_CONTENT_EDITOR_INSERT_REPLACE_ALL | E_CONTENT_EDITOR_INSERT_TEXT_HTML);
+
+       if (!test_utils_run_simple_test (fixture,
+               "",
+               HTML_PREFIX "<blockquote type=\"cite\">"
+               "<p>Just one short line.</p>"
+               "</blockquote>" HTML_SUFFIX,
+               "> Just one short line.")) {
+               g_test_fail ();
+               return;
+       }
+
+       if (!test_utils_process_commands (fixture,
+               "seq:C\n"
+               "type:a\n"
+               "seq:cD\n")) {
+               g_test_fail ();
+               return;
+       }
+
+       test_utils_insert_content (fixture,
+               "<html><head></head><body><blockquote type=\"cite\">"
+               "<p>Just one short line.</p>"
+               "</blockquote></body></html>",
+               E_CONTENT_EDITOR_INSERT_TEXT_HTML);
+
+       if (!test_utils_run_simple_test (fixture,
+               "",
+               HTML_PREFIX "<blockquote type=\"cite\">"
+               "<p>Just one short line.</p>"
+               "</blockquote>" HTML_SUFFIX,
+               "> Just one short line.")) {
+               g_test_fail ();
+               return;
+       }
+
+       test_utils_insert_content (fixture,
+               "<html><head></head><body><blockquote type=\"cite\">"
+               "<p>short line 1</p>"
+               "<p>short line 2</p>"
+               "<p>short line 3</p>"
+               "</blockquote></body></html>",
+               E_CONTENT_EDITOR_INSERT_REPLACE_ALL | E_CONTENT_EDITOR_INSERT_TEXT_HTML);
+
+       if (!test_utils_run_simple_test (fixture,
+               "",
+               HTML_PREFIX "<blockquote type=\"cite\">"
+               "<p>short line 1</p>"
+               "<p>short line 2</p>"
+               "<p>short line 3</p>"
+               "</blockquote>" HTML_SUFFIX,
+               "> short line 1\n"
+               "> short line 2\n"
+               "> short line 3")) {
+               g_test_fail ();
+               return;
+       }
+
+       if (!test_utils_process_commands (fixture,
+               "seq:C\n"
+               "type:a\n"
+               "seq:cD\n")) {
+               g_test_fail ();
+               return;
+       }
+
+       test_utils_insert_content (fixture,
+               "<html><head></head><body><blockquote type=\"cite\">"
+               "<p>short line 1</p>"
+               "<p>short line 2</p>"
+               "<p>short line 3</p>"
+               "</blockquote></body></html>",
+               E_CONTENT_EDITOR_INSERT_TEXT_HTML);
+
+       if (!test_utils_run_simple_test (fixture,
+               "",
+               HTML_PREFIX "<blockquote type=\"cite\">"
+               "<p>short line 1</p>"
+               "<p>short line 2</p>"
+               "<p>short line 3</p>"
+               "</blockquote>" HTML_SUFFIX,
+               "> short line 1\n"
+               "> short line 2\n"
+               "> short line 3")) {
+               g_test_fail ();
+               return;
+       }
+}
+
+static void
+test_cite_longline (TestFixture *fixture)
+{
+       if (!test_utils_process_commands (fixture,
+               "mode:html\n")) {
+               g_test_fail ();
+               return;
+       }
+
+       test_utils_insert_content (fixture,
+               "<html><head></head><body><blockquote type=\"cite\">"
+               "<p>This is the first paragraph of a quoted text which has some long text to test. It has the 
second sentence as well.</p>"
+               "</blockquote></body></html>",
+               E_CONTENT_EDITOR_INSERT_REPLACE_ALL | E_CONTENT_EDITOR_INSERT_TEXT_HTML);
+
+       if (!test_utils_run_simple_test (fixture,
+               "",
+               HTML_PREFIX "<blockquote type=\"cite\">"
+               "<p>This is the first paragraph of a quoted text which has some long text to test. It has the 
second sentence as well.</p>"
+               "</blockquote>" HTML_SUFFIX,
+               "> This is the first paragraph of a quoted text which has some long\n"
+               "> text to test. It has the second sentence as well.\n")) {
+               g_test_fail ();
+               return;
+       }
+
+       if (!test_utils_process_commands (fixture,
+               "seq:C\n"
+               "type:a\n"
+               "seq:cD\n")) {
+               g_test_fail ();
+               return;
+       }
+
+       test_utils_insert_content (fixture,
+               "<html><head></head><body><blockquote type=\"cite\">"
+               "<p>This is the first paragraph of a quoted text which has some long text to test. It has the 
second sentence as well.</p>"
+               "</blockquote></body></html>",
+               E_CONTENT_EDITOR_INSERT_TEXT_HTML);
+
+       if (!test_utils_run_simple_test (fixture,
+               "",
+               HTML_PREFIX "<blockquote type=\"cite\">"
+               "<p>This is the first paragraph of a quoted text which has some long text to test. It has the 
second sentence as well.</p>"
+               "</blockquote>" HTML_SUFFIX,
+               "> This is the first paragraph of a quoted text which has some long\n"
+               "> text to test. It has the second sentence as well.\n")) {
+               g_test_fail ();
+               return;
+       }
+
+       test_utils_insert_content (fixture,
+               "<html><head></head><body><blockquote type=\"cite\">"
+               "<p>This is the first paragraph of a quoted text which has some long text to test. It has the 
second sentence as well.</p>"
+               "<p>This is the second paragraph of a quoted text which has some long text to test. It has 
the second sentence as well.</p>"
+               "<p>This is the third paragraph of a quoted text which has some long text to test. It has the 
second sentence as well.</p>"
+               "</blockquote><br>after quote</body></html>",
+               E_CONTENT_EDITOR_INSERT_REPLACE_ALL | E_CONTENT_EDITOR_INSERT_TEXT_HTML);
+
+       if (!test_utils_run_simple_test (fixture,
+               "",
+               HTML_PREFIX "<blockquote type=\"cite\">"
+               "<p>This is the first paragraph of a quoted text which has some long text to test. It has the 
second sentence as well.</p>"
+               "<p>This is the second paragraph of a quoted text which has some long text to test. It has 
the second sentence as well.</p>"
+               "<p>This is the third paragraph of a quoted text which has some long text to test. It has the 
second sentence as well.</p>"
+               "</blockquote><br>after quote" HTML_SUFFIX,
+               "> This is the first paragraph of a quoted text which has some long\n"
+               "> text to test. It has the second sentence as well.\n"
+               "> This is the econd paragraph of a quoted text which has some long\n"
+               "> text to test. It has the second sentence as well.\n"
+               "> This is the third paragraph of a quoted text which has some long\n"
+               "> text to test. It has the second sentence as well.\n"
+               "\nafter quote")) {
+               g_test_fail ();
+               return;
+       }
+}
+
+static void
+test_cite_reply_html (TestFixture *fixture)
+{
+       if (!test_utils_process_commands (fixture,
+               "mode:html\n")) {
+               g_test_fail ();
+               return;
+       }
+
+       test_utils_insert_content (fixture,
+               "<pre>line 1\n"
+               "line 2\n"
+               "</pre><span class=\"-x-evo-to-body\" data-credits=\"On Today, User wrote:\"></span>"
+               "<span class=\"-x-evo-cite-body\"></span>",
+               E_CONTENT_EDITOR_INSERT_REPLACE_ALL | E_CONTENT_EDITOR_INSERT_TEXT_HTML);
+
+       if (!test_utils_run_simple_test (fixture,
+               "",
+               HTML_PREFIX "<p>On Today, User wrote:</p>"
+               "<blockquote type=\"cite\"><pre>line 1\n"
+               "line 2\n"
+               "</pre></blockquote>" HTML_SUFFIX,
+               "On Today, Use wrote:\n"
+               "> line 1\n"
+               "> line 2\n"))
+               g_test_fail ();
+
+}
+
+static void
+test_cite_reply_plain (TestFixture *fixture)
+{
+       if (!test_utils_process_commands (fixture,
+               "mode:plain\n")) {
+               g_test_fail ();
+               return;
+       }
+
+       test_utils_insert_content (fixture,
+               "<pre>line 1\n"
+               "line 2\n"
+               "</pre><span class=\"-x-evo-to-body\" data-credits=\"On Today, User wrote:\"></span>"
+               "<span class=\"-x-evo-cite-body\"></span>",
+               E_CONTENT_EDITOR_INSERT_REPLACE_ALL | E_CONTENT_EDITOR_INSERT_TEXT_HTML);
+
+       if (!test_utils_run_simple_test (fixture,
+               "",
+               HTML_PREFIX_PLAIN "<p style=\"width: 71ch;\">On Today, User wrote:</p>"
+               "<blockquote type=\"cite\"><p style=\"width: 71ch;\">&gt; line 1</p>"
+               "<p style=\"width: 71ch;\">&gt; line 2</p>"
+               "<p style=\"width: 71ch;\">&gt; <br></p></blockquote>" HTML_SUFFIX,
+               "On Today, Use wrote:\n"
+               "> line 1\n"
+               "> line 2\n"
+               "> "))
+               g_test_fail ();
+}
+
+static void
+test_undo_text_typed (TestFixture *fixture)
+{
+       if (!test_utils_run_simple_test (fixture,
+               "mode:html\n"
+               "type:some te\n"
+               "undo:save\n"   /* 1 */
+               "type:tz\n"
+               "undo:save\n"   /* 2 */
+               "undo:undo\n"
+               "undo:undo\n"
+               "undo:test:2\n"
+               "undo:redo\n"
+               "undo:redo\n"
+               "undo:test\n"
+               "undo:undo:2\n"
+               "undo:drop\n"
+               "type:xt\n",
+               HTML_PREFIX "<p>some text</p>" HTML_SUFFIX,
+               "some text"))
+               g_test_fail ();
+}
+
+static void
+test_undo_text_forward_delete (TestFixture *fixture)
+{
+       if (!test_utils_run_simple_test (fixture,
+               "mode:html\n"
+               "type:some text to delete\n"
+               "seq:hCrcrCDc\n"
+               "undo:undo\n"
+               "undo:redo\n"
+               "undo:undo\n",
+               HTML_PREFIX "<p>some text to delete</p>" HTML_SUFFIX,
+               "some text to delete"))
+               g_test_fail ();
+}
+
+static void
+test_undo_text_backward_delete (TestFixture *fixture)
+{
+       if (!test_utils_run_simple_test (fixture,
+               "mode:html\n"
+               "type:some text to delete\n"
+               "seq:hCrcrCbc\n"
+               "undo:undo\n"
+               "undo:redo\n"
+               "undo:undo\n",
+               HTML_PREFIX "<p>some text to delete</p>" HTML_SUFFIX,
+               "some text to delete"))
+               g_test_fail ();
+}
+
+static void
+test_undo_text_cut (TestFixture *fixture)
+{
+       if (!test_utils_run_simple_test (fixture,
+               "mode:plain\n"
+               "type:some text to delete\n"
+               "seq:CSllsc\n"
+               "action:cut\n"
+               "undo:undo\n",
+               NULL,
+               "some text to delete"))
+               g_test_fail ();
+}
+
+static void
+test_undo_style (TestFixture *fixture)
+{
+       if (!test_utils_run_simple_test (fixture,
+               "mode:html\n"
+               "type:The first paragraph text\\n\n"
+               "undo:save\n" /* 1 */
+
+               "action:bold\n"
+               "type:bold\n"
+               "undo:save\n" /* 2 */
+               "undo:undo:4\n"
+               "undo:test:2\n"
+               "undo:redo:4\n"
+               "undo:test\n"
+               "undo:drop\n" /* drop the save 2 */
+               "undo:undo:4\n"
+               "type:bold\n"
+               "seq:CSlsc\n"
+               "action:bold\n"
+               "undo:save:\n" /* 2 */
+               "undo:undo:4\n"
+               "undo:test:2\n"
+               "undo:redo:4\n"
+               "undo:test\n"
+               "undo:drop\n" /* drop the save 2 */
+               "undo:undo:4\n"
+
+               "action:italic\n"
+               "type:italic\n"
+               "undo:save\n" /* 2 */
+               "undo:undo:6\n"
+               "undo:test:2\n"
+               "undo:redo:6\n"
+               "undo:test\n"
+               "undo:drop\n" /* drop the save 2 */
+               "undo:undo:6\n"
+               "type:italic\n"
+               "seq:CSlsc\n"
+               "action:italic\n"
+               "undo:save:\n" /* 2 */
+               "undo:undo:6\n"
+               "undo:test:2\n"
+               "undo:redo:6\n"
+               "undo:test\n"
+               "undo:drop\n" /* drop the save 2 */
+               "undo:undo:6\n"
+
+               "action:underline\n"
+               "type:underline\n"
+               "undo:save\n" /* 2 */
+               "undo:undo:9\n"
+               "undo:test:2\n"
+               "undo:redo:9\n"
+               "undo:test\n"
+               "undo:drop\n" /* drop the save 2 */
+               "undo:undo:9\n"
+               "type:italic\n"
+               "seq:CSlsc\n"
+               "action:underline\n"
+               "undo:save:\n" /* 2 */
+               "undo:undo:9\n"
+               "undo:test:2\n"
+               "undo:redo:9\n"
+               "undo:test\n"
+               "undo:drop\n" /* drop the save 2 */
+               "undo:undo:9\n"
+
+               "action:strikethrough\n"
+               "type:strikethrough\n"
+               "undo:save\n" /* 2 */
+               "undo:undo:13\n"
+               "undo:test:2\n"
+               "undo:redo:13\n"
+               "undo:test\n"
+               "undo:drop\n" /* drop the save 2 */
+               "undo:undo:13\n"
+               "type:strikethrough\n"
+               "seq:CSlsc\n"
+               "action:strikethrough\n"
+               "undo:save:\n" /* 2 */
+               "undo:undo:13\n"
+               "undo:test:2\n"
+               "undo:redo:13\n"
+               "undo:test\n"
+               "undo:drop\n" /* drop the save 2 */
+               "undo:undo:13\n"
+
+               "action:monospaced\n"
+               "type:monospaced\n"
+               "undo:save\n" /* 2 */
+               "undo:undo:10\n"
+               "undo:test:2\n"
+               "undo:redo:10\n"
+               "undo:test\n"
+               "undo:drop\n" /* drop the save 2 */
+               "undo:undo:10\n"
+               "type:monospaced\n"
+               "seq:CSlsc\n"
+               "action:monospaced\n"
+               "undo:save:\n" /* 2 */
+               "undo:undo:10\n"
+               "undo:test:2\n"
+               "undo:redo:10\n"
+               "undo:test\n"
+               "undo:drop\n" /* drop the save 2 */
+               "undo:undo:10\n",
+               HTML_PREFIX "<p>The first paragraph</p><p><br></p>" HTML_SUFFIX,
+               "The first paragraph\n"))
+               g_test_fail ();
+}
+
+static void
+test_undo_justify (TestFixture *fixture)
+{
+       if (!test_utils_run_simple_test (fixture,
+               "mode:html\n"
+               "type:The first paragraph text\\n\n"
+               "undo:save\n" /* 1 */
+
+               "action:justify-left\n"
+               "type:left\n"
+               "undo:save\n" /* 2 */
+               "undo:undo:4\n"
+               "undo:test:2\n"
+               "undo:redo:4\n"
+               "undo:test\n"
+               "undo:drop\n" /* drop the save 2 */
+               "undo:undo:4\n"
+               "type:left\n"
+               "seq:CSlsc\n"
+               "action:justify-left\n"
+               "undo:save:\n" /* 2 */
+               "undo:undo:4\n"
+               "undo:test:2\n"
+               "undo:redo:4\n"
+               "undo:test\n"
+               "undo:drop\n" /* drop the save 2 */
+               "undo:undo:4\n"
+
+               "action:justify-center\n"
+               "type:center\n"
+               "undo:save\n" /* 2 */
+               "undo:undo:6\n"
+               "undo:test:2\n"
+               "undo:redo:6\n"
+               "undo:test\n"
+               "undo:drop\n" /* drop the save 2 */
+               "undo:undo:6\n"
+               "type:center\n"
+               "seq:CSlsc\n"
+               "action:justify-center\n"
+               "undo:save:\n" /* 2 */
+               "undo:undo:6\n"
+               "undo:test:2\n"
+               "undo:redo:6\n"
+               "undo:test\n"
+               "undo:drop\n" /* drop the save 2 */
+               "undo:undo:6\n"
+
+               "action:justify-right\n"
+               "type:right\n"
+               "undo:save\n" /* 2 */
+               "undo:undo:5\n"
+               "undo:test:2\n"
+               "undo:redo:5\n"
+               "undo:test\n"
+               "undo:drop\n" /* drop the save 2 */
+               "undo:undo:5\n"
+               "type:right\n"
+               "seq:CSlsc\n"
+               "action:justify-right\n"
+               "undo:save:\n" /* 2 */
+               "undo:undo:5\n"
+               "undo:test:2\n"
+               "undo:redo:5\n"
+               "undo:test\n"
+               "undo:drop\n" /* drop the save 2 */
+               "undo:undo:5\n",
+
+               HTML_PREFIX "<p>The first paragraph</p><p><br></p>" HTML_SUFFIX,
+               "The first paragraph\n"))
+               g_test_fail ();
+}
+
+static void
+test_undo_indent (TestFixture *fixture)
+{
+       if (!test_utils_run_simple_test (fixture,
+               "mode:html\n"
+               "type:The first paragraph text\\n\n"
+               "undo:save\n" /* 1 */
+
+               "action:indent\n"
+               "type:text\n"
+               "undo:save\n" /* 2 */
+               "undo:undo:5\n"
+               "undo:test:2\n"
+               "undo:redo:5\n"
+               "undo:test\n"
+               "undo:drop\n" /* drop the save 2 */
+               "undo:undo:5\n"
+               "type:text\n"
+               "seq:CSlsc\n"
+               "action:indent\n"
+               "undo:save:\n" /* 2 */
+               "undo:undo:5\n"
+               "undo:test:2\n"
+               "undo:redo:5\n"
+               "undo:test\n"
+               "undo:drop\n" /* drop the save 2 */
+               "undo:undo:5\n"
+
+               "type:text\n"
+               "undo:save\n" /* 2 */
+               "action:indent\n"
+               "undo:save\n" /* 3 */
+               "action:unindent\n"
+               "undo:test:2\n"
+               "action:indent\n"
+               "undo:test\n"
+               "undo:save\n" /* 4 */
+               "undo:undo:2\n"
+               "undo:test:2\n"
+               "undo:redo:2\n"
+               "undo:test\n"
+               "undo:drop:2\n" /* drop the save 4 and 3 */
+               "undo:undo:3\n"
+               "undo:test\n"
+               "undo:drop\n" /* drop the save 2 */
+               "undo:undo:4\n"
+               "undo:test\n"
+
+               "type:level 1\\n\n"
+               "type:level 2\\n\n"
+               "type:level 3\\n\n"
+               "seq:uuu\n"
+               "action:indent\n"
+               "undo:save\n" /* 2 */
+               "seq:d\n"
+               "action:indent\n"
+               "action:indent\n"
+               "undo:save\n" /* 3 */
+               "undo:undo:2\n"
+               "undo:test:2\n"
+               "undo:redo:2\n"
+               "undo:test\n"
+               "undo:drop:2\n" /* drop the save 3 and 2 */
+               "seq:d\n"
+
+               "action:indent\n"
+               "undo:save\n" /* 2 */
+               "action:indent\n"
+               "action:indent\n"
+               "undo:save\n" /* 3 */
+               "undo:undo:2\n"
+               "undo:test:2\n"
+               "undo:redo:2\n"
+               "undo:test\n"
+               "undo:drop:2\n" /* drop the save 3 and 2 */
+
+               "undo:save\n" /* 2 */
+               "undo:undo:30\n" /* 6x action:indent, 24x type "level X\\n" */
+               "undo:test:2\n"
+               "undo:redo:30\n"
+               "undo:test\n"
+               "undo:drop\n" /* drop the save 2 */
+               "undo:undo:30\n",
+
+               HTML_PREFIX "<p>The first paragraph</p><p><br></p>" HTML_SUFFIX,
+               "The first paragraph\n"))
+               g_test_fail ();
+}
+
+static void
+test_undo_link_paste_html (TestFixture *fixture)
+{
+       test_utils_set_clipboard_text ("http://www.gnome.org";, FALSE);
+
+       if (!test_utils_run_simple_test (fixture,
+               "mode:html\n"
+               "type:URL:\\n\n"
+               "undo:save\n" /* 1 */
+               "action:paste\n"
+               "type:\\n\n"
+               "undo:save\n" /* 2 */
+               "undo:undo:2\n"
+               "undo:test:2\n"
+               "undo:undo:5\n"
+               "undo:redo:7\n"
+               "undo:test\n",
+               HTML_PREFIX "<p>URL:</p><p><a 
href=\"http://www.gnome.org\";>http://www.gnome.org</a></p><p><br></p>" HTML_SUFFIX,
+               "URL:\nhttp://www.gnome.org\n";))
+               g_test_fail ();
+}
+
+static void
+test_undo_link_paste_plain (TestFixture *fixture)
+{
+       test_utils_set_clipboard_text ("http://www.gnome.org";, FALSE);
+
+       if (!test_utils_run_simple_test (fixture,
+               "mode:plain\n"
+               "type:URL:\\n\n"
+               "undo:save\n" /* 1 */
+               "action:paste\n"
+               "type:\\n\n"
+               "undo:save\n" /* 2 */
+               "undo:undo:2\n"
+               "undo:test:2\n"
+               "undo:undo:5\n"
+               "undo:redo:7\n"
+               "undo:test\n",
+               HTML_PREFIX_PLAIN "<p style=\"width: 71ch;\">URL:</p>"
+               "<p style=\"width: 71ch;\"><a href=\"http://www.gnome.org\";>http://www.gnome.org</a></p>"
+               "<p style=\"width: 71ch;\"><br></p>" HTML_SUFFIX,
+               "URL:\nhttp://www.gnome.org\n";))
+               g_test_fail ();
+}
+
+static void
+test_bug_726548 (TestFixture *fixture)
+{
+       gboolean success;
+       gchar *text;
+       const gchar *expected_plain =
+               "aaa\n"
+               "   1. a\n"
+               "   2. b\n"
+               "   3. c\n";
+
+       if (!test_utils_run_simple_test (fixture,
+               "mode:plain\n"
+               "type:aaa\\n\n"
+               "action:style-list-number\n"
+               "type:a\\nb\\nc\\n\\n\n"
+               "seq:C\n"
+               "type:ac\n"
+               "seq:c\n",
+               HTML_PREFIX_PLAIN "<p style=\"width: 71ch;\">aaa</p>"
+               "<ol data-evo-paragraph=\"\" style=\"width: 65ch;\">"
+               "<li>a</li><li>b</li><li>c</li></ol>"
+               "<p style=\"width: 71ch;\"><br></p>" HTML_SUFFIX,
+               expected_plain)) {
+               g_test_fail ();
+               return;
+       }
+
+       text = test_utils_get_clipboard_text (FALSE);
+       success = test_utils_html_equal (fixture, text, expected_plain);
+
+       if (!success) {
+               g_warning ("%s: clipboard Plain text \n---%s---\n does not match expected Plain\n---%s---",
+                       G_STRFUNC, text, expected_plain);
+               g_free (text);
+               g_test_fail ();
+       } else {
+               g_free (text);
+       }
+}
+
+static void
+test_bug_750657 (TestFixture *fixture)
+{
+       if (!test_utils_process_commands (fixture,
+               "mode:html\n")) {
+               g_test_fail ();
+               return;
+       }
+
+       test_utils_insert_content (fixture,
+               "<html><head></head><body>\n"
+               "<blockquote type=\"cite\">\n"
+               "<p>This is the first paragraph of a quoted text which has some long text to test. It has the 
second sentence as well.</p>\n"
+               "<p><br></p>\n"
+               "<p>This is the third paragraph of a quoted text which has some long text to test. It has the 
second sentence as well.</p>\n"
+               "<blockquote type=\"cite\">\n"
+               "<p>This is the first paragraph of a sub-quoted text which has some long text to test. It has 
the second sentence as well.</p>\n"
+               "<br>\n"
+               "</blockquote>\n"
+               "<p>This is the fourth paragraph of a quoted text which has some long text to test. It has 
the second sentence as well.</p>\n"
+               "</blockquote>\n"
+               "<p><br></p>\n"
+               "</body></html>\n",
+               E_CONTENT_EDITOR_INSERT_TEXT_HTML);
+
+       if (!test_utils_run_simple_test (fixture,
+               "seq:uuuSuusD\n",
+               HTML_PREFIX "\n"
+               "<blockquote type=\"cite\">\n"
+               "<p>This is the first paragraph of a quoted text which has some long text to test. It has the 
second sentence as well.</p>\n"
+               "<p><br></p>\n"
+               "<p>This is the third paragraph of a quoted text which has some long text to test. It has the 
second sentence as well.</p>\n"
+               "<p>This is the fourth paragraph of a quoted text which has some long text to test. It has 
the second sentence as well.</p>\n"
+               "</blockquote>\n"
+               "<p><br></p>\n"
+               HTML_SUFFIX,
+               NULL)) {
+               g_test_fail ();
+               return;
+       }
+}
+
+static void
+test_bug_760989 (TestFixture *fixture)
+{
+       if (!test_utils_process_commands (fixture,
+               "mode:html\n"
+               "type:a\n")) {
+               g_test_fail ();
+               return;
+       }
+
+       test_utils_insert_content (fixture,
+               "<html><head></head><body>\n"
+               "One line before quotation<br>\n"
+               "<blockquote type=\"cite\">\n"
+               "<p>Single line quoted.</p>\n"
+               "</blockquote>\n"
+               "</body></html>",
+               E_CONTENT_EDITOR_INSERT_TEXT_HTML);
+
+       if (!test_utils_run_simple_test (fixture,
+               "seq:ChcD\n",
+               HTML_PREFIX "<p>One line before quotation</p>\n"
+               "<blockquote type=\"cite\">\n"
+               "<p>Single line quoted.</p>\n"
+               "</blockquote>" HTML_SUFFIX,
+               "One line before quotation\n"
+               "> Single line quoted.")) {
+               g_test_fail ();
+               return;
+       }
+
+       if (!test_utils_run_simple_test (fixture,
+               "seq:Cecb\n",
+               HTML_PREFIX "<p>One line before quotation</p>\n"
+               "<blockquote type=\"cite\">\n"
+               "<p>Single line quoted</p>\n"
+               "</blockquote>" HTML_SUFFIX,
+               "One line before quotation\n"
+               "> Single line quoted")) {
+               g_test_fail ();
+               return;
+       }
+}
+
+static void
+test_bug_769708 (TestFixture *fixture)
+{
+       if (!test_utils_process_commands (fixture,
+               "mode:html\n")) {
+               g_test_fail ();
+               return;
+       }
+
+       test_utils_insert_content (fixture,
+               "<html><head><style id=\"-x-evo-quote-style\" type=\"text/css\">.-x-evo-quoted { 
-webkit-user-select: none; }</style>"
+               "<style id=\"-x-evo-style-a\" type=\"text/css\">a { cursor: text; }</style></head>"
+               "<body data-evo-plain-text=\"\" spellcheck=\"true\">"
+               "<p data-evo-paragraph=\"\" class=\"\" id=\"-x-evo-input-start\">aaa</p>"
+               "<div class=\"-x-evo-signature-wrapper\"><span class=\"-x-evo-signature\" 
id=\"autogenerated\"><pre>-- <br></pre>"
+               "<p data-evo-paragraph=\"\" class=\"\">user &lt;user@no.where&gt;</p>"
+               "</span></div></body></html>",
+               E_CONTENT_EDITOR_INSERT_REPLACE_ALL | E_CONTENT_EDITOR_INSERT_TEXT_HTML);
+
+       if (!test_utils_process_commands (fixture,
+               "mode:plain\n")) {
+               g_test_fail ();
+               return;
+       }
+
+       if (!test_utils_run_simple_test (fixture,
+               "",
+               HTML_PREFIX_PLAIN "<p style=\"width: 71ch;\">aaa</p><div><span><p style=\"width: 
71ch;\">--&nbsp;</p>"
+               "<p style=\"width: 71ch;\"<br></p><p style=\"width: 71ch;\">user 
&lt;user@no.where&gt;</p></span></div>" HTML_SUFFIX,
+               "aaa\n"
+               "-- \n"
+               "user <user@no.where>"))
+               g_test_fail ();
+}
+
+gint
+main (gint argc,
+      gchar *argv[])
+{
+       gint cmd_delay = -1;
+       GOptionEntry entries[] = {
+               { "cmd-delay", '\0', 0,
+                 G_OPTION_ARG_INT, &cmd_delay,
+                 "Specify delay, in milliseconds, to use during processing commands. Default is 5 ms.",
+                 NULL },
+               { NULL }
+       };
+       GOptionContext *context;
+       GError *error = NULL;
+       GList *modules;
+       gint res;
+
+       setlocale (LC_ALL, "");
+
+       /* Force the memory GSettings backend, to not overwrite user settings
+          when playing with them. It also ensures that the test will run with
+          default settings, until changed. */
+       g_setenv ("GSETTINGS_BACKEND", "memory", TRUE);
+
+       g_test_init (&argc, &argv, NULL);
+       g_test_bug_base ("http://bugzilla.gnome.org/show_bug.cgi?id=";);
+
+       gtk_init (&argc, &argv);
+
+       context = g_option_context_new (NULL);
+       g_option_context_add_main_entries (context, entries, GETTEXT_PACKAGE);
+       if (!g_option_context_parse (context, &argc, &argv, &error)) {
+               g_warning ("Failed to parse arguments: %s\n", error ? error->message : "Unknown error");
+               g_option_context_free (context);
+               g_clear_error (&error);
+               return -1;
+       }
+
+       g_option_context_free (context);
+
+       if (cmd_delay > 0)
+               test_utils_set_event_processing_delay_ms ((guint) cmd_delay);
+
+       e_util_init_main_thread (NULL);
+       e_passwords_init ();
+
+       modules = e_module_load_all_in_directory (EVOLUTION_MODULEDIR);
+       g_list_free_full (modules, (GDestroyNotify) g_type_module_unuse);
+
+       #define add_test(_name, _func)  \
+               g_test_add (_name, TestFixture, NULL, \
+                       test_utils_fixture_set_up, (ETestFixtureFunc) _func, test_utils_fixture_tear_down)
+
+       add_test ("/create/editor", test_create_editor);
+       add_test ("/style/bold/selection", test_style_bold_selection);
+       add_test ("/style/bold/typed", test_style_bold_typed);
+       add_test ("/style/italic/selection", test_style_italic_selection);
+       add_test ("/style/italic/typed", test_style_italic_typed);
+       add_test ("/style/underline/selection", test_style_underline_selection);
+       add_test ("/style/underline/typed", test_style_underline_typed);
+       add_test ("/style/strikethrough/selection", test_style_strikethrough_selection);
+       add_test ("/style/strikethrough/typed", test_style_strikethrough_typed);
+       add_test ("/style/monospace/selection", test_style_monospace_selection);
+       add_test ("/style/monospace/typed", test_style_monospace_typed);
+       add_test ("/justify/selection", test_justify_selection);
+       add_test ("/justify/typed", test_justify_typed);
+       add_test ("/indent/selection", test_indent_selection);
+       add_test ("/indent/typed", test_indent_typed);
+       add_test ("/font/size/selection", test_font_size_selection);
+       add_test ("/font/size/typed", test_font_size_typed);
+       add_test ("/font/color/selection", test_font_color_selection);
+       add_test ("/font/color/typed", test_font_color_typed);
+       add_test ("/list/bullet/plain", test_list_bullet_plain);
+       add_test ("/list/bullet/html", test_list_bullet_html);
+       add_test ("/list/bullet/html/from-block", test_list_bullet_html_from_block);
+       add_test ("/list/alpha/html", test_list_alpha_html);
+       add_test ("/list/alpha/plain", test_list_alpha_plain);
+       add_test ("/list/roman/html", test_list_roman_html);
+       add_test ("/list/roman/plain", test_list_roman_plain);
+       add_test ("/list/multi/html", test_list_multi_html);
+       add_test ("/list/multi/plain", test_list_multi_plain);
+       add_test ("/list/multi/change/html", test_list_multi_change_html);
+       add_test ("/list/multi/change/plain", test_list_multi_change_plain);
+       add_test ("/link/insert/dialog", test_link_insert_dialog);
+       add_test ("/link/insert/dialog/selection", test_link_insert_dialog_selection);
+       add_test ("/link/insert/dialog/remove-link", test_link_insert_dialog_remove_link);
+       add_test ("/link/insert/typed", test_link_insert_typed);
+       add_test ("/link/insert/typed/change-description", test_link_insert_typed_change_description);
+       add_test ("/link/insert/typed/append", test_link_insert_typed_append);
+       add_test ("/link/insert/typed/remove", test_link_insert_typed_remove);
+       add_test ("/h-rule/insert", test_h_rule_insert);
+       add_test ("/h-rule/insert-text-after", test_h_rule_insert_text_after);
+       add_test ("/emoticon/insert/typed", test_emoticon_insert_typed);
+       add_test ("/emoticon/insert/typed-dash", test_emoticon_insert_typed_dash);
+       add_test ("/paragraph/normal/selection", test_paragraph_normal_selection);
+       add_test ("/paragraph/normal/typed", test_paragraph_normal_typed);
+       add_test ("/paragraph/preformatted/selection", test_paragraph_preformatted_selection);
+       add_test ("/paragraph/preformatted/typed", test_paragraph_preformatted_typed);
+       add_test ("/paragraph/address/selection", test_paragraph_address_selection);
+       add_test ("/paragraph/address/typed", test_paragraph_address_typed);
+       add_test ("/paragraph/header1/selection", test_paragraph_header1_selection);
+       add_test ("/paragraph/header1/typed", test_paragraph_header1_typed);
+       add_test ("/paragraph/header2/selection", test_paragraph_header2_selection);
+       add_test ("/paragraph/header2/typed", test_paragraph_header2_typed);
+       add_test ("/paragraph/header3/selection", test_paragraph_header3_selection);
+       add_test ("/paragraph/header3/typed", test_paragraph_header3_typed);
+       add_test ("/paragraph/header4/selection", test_paragraph_header4_selection);
+       add_test ("/paragraph/header4/typed", test_paragraph_header4_typed);
+       add_test ("/paragraph/header5/selection", test_paragraph_header5_selection);
+       add_test ("/paragraph/header5/typed", test_paragraph_header5_typed);
+       add_test ("/paragraph/header6/selection", test_paragraph_header6_selection);
+       add_test ("/paragraph/header6/typed", test_paragraph_header6_typed);
+       add_test ("/paragraph/wrap-lines", test_paragraph_wrap_lines);
+       add_test ("/paste/singleline/html2html", test_paste_singleline_html2html);
+       add_test ("/paste/singleline/html2plain", test_paste_singleline_html2plain);
+       add_test ("/paste/singleline/plain2html", test_paste_singleline_plain2html);
+       add_test ("/paste/singleline/plain2plain", test_paste_singleline_plain2plain);
+       add_test ("/paste/multiline/html2html", test_paste_multiline_html2html);
+       add_test ("/paste/multiline/html2plain", test_paste_multiline_html2plain);
+       add_test ("/paste/multiline/div/html2html", test_paste_multiline_div_html2html);
+       add_test ("/paste/multiline/div/html2plain", test_paste_multiline_div_html2plain);
+       add_test ("/paste/multiline/p/html2html", test_paste_multiline_p_html2html);
+       add_test ("/paste/multiline/p/html2plain", test_paste_multiline_p_html2plain);
+       add_test ("/paste/multiline/plain2html", test_paste_multiline_plain2html);
+       add_test ("/paste/multiline/plain2plain", test_paste_multiline_plain2plain);
+       add_test ("/paste/quoted/singleline/html2html", test_paste_quoted_singleline_html2html);
+       add_test ("/paste/quoted/singleline/html2plain", test_paste_quoted_singleline_html2plain);
+       add_test ("/paste/quoted/singleline/plain2html", test_paste_quoted_singleline_plain2html);
+       add_test ("/paste/quoted/singleline/plain2plain", test_paste_quoted_singleline_plain2plain);
+       add_test ("/paste/quoted/multiline/html2html", test_paste_quoted_multiline_html2html);
+       add_test ("/paste/quoted/multiline/html2plain", test_paste_quoted_multiline_html2plain);
+       add_test ("/paste/quoted/multiline/plain2html", test_paste_quoted_multiline_plain2html);
+       add_test ("/paste/quoted/multiline/plain2plain", test_paste_quoted_multiline_plain2plain);
+       add_test ("/cite/html2plain", test_cite_html2plain);
+       add_test ("/cite/shortline", test_cite_shortline);
+       add_test ("/cite/longline", test_cite_longline);
+       add_test ("/cite/reply/html", test_cite_reply_html);
+       add_test ("/cite/reply/plain", test_cite_reply_plain);
+       add_test ("/undo/text/typed", test_undo_text_typed);
+       add_test ("/undo/text/forward-delete", test_undo_text_forward_delete);
+       add_test ("/undo/text/backward-delete", test_undo_text_backward_delete);
+       add_test ("/undo/text/cut", test_undo_text_cut);
+       add_test ("/undo/style", test_undo_style);
+       add_test ("/undo/justify", test_undo_justify);
+       add_test ("/undo/indent", test_undo_indent);
+       add_test ("/undo/link-paste/html", test_undo_link_paste_html);
+       add_test ("/undo/link-paste/plain", test_undo_link_paste_plain);
+       add_test ("/bug/726548", test_bug_726548);
+       add_test ("/bug/750657", test_bug_750657);
+       add_test ("/bug/760989", test_bug_760989);
+       add_test ("/bug/769708", test_bug_769708);
+
+       #undef add_test
+
+       res = g_test_run ();
+
+       e_util_cleanup_settings ();
+       e_spell_checker_free_global_memory ();
+
+       return res;
+}
diff --git a/e-util/test-html-editor.c b/e-util/test-html-editor.c
index e393f87..86502c4 100644
--- a/e-util/test-html-editor.c
+++ b/e-util/test-html-editor.c
@@ -27,15 +27,25 @@
 
 #include <glib/gi18n-lib.h>
 
+/* Enable it, once printing is implemented (it doesn't work to do it
+   on a WebKit side, because the EContentEditor can be a different
+   structure. That might be why EMsgComposer uses a "print" signal,
+   which prints a constructed message, like within the message preview. */
+/* #define ENABLE_PRINT */
+
 static const gchar *file_ui =
 "<ui>\n"
 "  <menubar name='main-menu'>\n"
 "    <menu action='file-menu'>\n"
+"     <menuitem action='new-editor'/>\n"
+"     <separator/>\n"
 "     <menuitem action='save'/>\n"
 "     <menuitem action='save-as'/>\n"
+#ifdef ENABLE_PRINT
 "     <separator/>\n"
 "     <menuitem action='print-preview'/>\n"
 "     <menuitem action='print'/>\n"
+#endif /* ENABLE_PRINT */
 "     <separator/>\n"
 "     <menuitem action='disable-editor'/>\n"
 "     <separator/>\n"
@@ -51,11 +61,14 @@ static const gchar *view_ui =
 "     <menuitem action='view-html-output'/>\n"
 "     <menuitem action='view-html-source'/>\n"
 "     <menuitem action='view-plain-source'/>\n"
-"     <menuitem action='view-inspector'/>\n"
+"     <separator/>\n"
+"     <menuitem action='view-webkit-inspector'/>\n"
 "    </menu>\n"
 "  </menubar>\n"
 "</ui>";
 
+static void create_new_editor (void);
+
 static void
 handle_error (GError **error)
 {
@@ -65,7 +78,8 @@ handle_error (GError **error)
        }
 }
 
-static GtkPrintOperationResult
+#ifdef ENABLE_PRINT
+static void
 print (EHTMLEditor *editor,
        GtkPrintOperationAction action)
 {
@@ -78,13 +92,12 @@ print (EHTMLEditor *editor,
 
        frame = webkit_web_view_get_main_frame (
                WEBKIT_WEB_VIEW (e_html_editor_get_view (editor)));
-       result = webkit_web_frame_print_full (frame, operation, action, NULL);
+       webkit_web_frame_print_full (frame, operation, action, NULL);
 
        g_object_unref (operation);
        handle_error (&error);
-
-       return result;
 }
+#endif
 
 static gint
 save_dialog (EHTMLEditor *editor)
@@ -139,8 +152,11 @@ view_source_dialog (EHTMLEditor *editor,
        GtkWidget *content;
        GtkWidget *content_area;
        GtkWidget *scrolled_window;
+       EContentEditor *cnt_editor;
        gchar * html;
 
+       cnt_editor = e_html_editor_get_content_editor (editor);
+
        dialog = gtk_dialog_new_with_buttons (
                title,
                GTK_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (editor))),
@@ -165,15 +181,17 @@ view_source_dialog (EHTMLEditor *editor,
        gtk_window_set_default_size (GTK_WINDOW (dialog), 400, 300);
 
        if (plain_text) {
-               html = e_html_editor_view_get_text_plain (
-                       e_html_editor_get_view (editor));
+               html = e_content_editor_get_content (cnt_editor,
+                       E_CONTENT_EDITOR_GET_PROCESSED | E_CONTENT_EDITOR_GET_TEXT_PLAIN,
+                       NULL, NULL);
        } else {
-               GList *inline_images;
+               GSList *inline_images = NULL;
 
-               html = e_html_editor_view_get_text_html (
-                       e_html_editor_get_view (editor), "test-domain", &inline_images);
+               html = e_content_editor_get_content (cnt_editor,
+                       E_CONTENT_EDITOR_GET_PROCESSED | E_CONTENT_EDITOR_GET_TEXT_HTML | 
E_CONTENT_EDITOR_GET_INLINE_IMAGES,
+                       "test-domain", &inline_images);
 
-               g_list_free_full (inline_images, g_object_unref);
+               g_slist_free_full (inline_images, g_object_unref);
        }
 
        if (show_source || plain_text) {
@@ -185,8 +203,7 @@ view_source_dialog (EHTMLEditor *editor,
                gtk_text_view_set_editable (GTK_TEXT_VIEW (content), FALSE);
        } else {
                content = webkit_web_view_new ();
-               webkit_web_view_load_string (
-                       WEBKIT_WEB_VIEW (content), html, NULL, NULL, NULL);
+               webkit_web_view_load_html (WEBKIT_WEB_VIEW (content), html, "evo-file://");
        }
        g_free (html);
 
@@ -198,6 +215,14 @@ view_source_dialog (EHTMLEditor *editor,
 }
 
 static void
+action_new_editor_cb (GtkAction *action,
+                     EHTMLEditor *editor)
+{
+       create_new_editor ();
+}
+
+#ifdef ENABLE_PRINT
+static void
 action_print_cb (GtkAction *action,
                  EHTMLEditor *editor)
 {
@@ -210,6 +235,7 @@ action_print_preview_cb (GtkAction *action,
 {
        print (editor, GTK_PRINT_OPERATION_ACTION_PREVIEW);
 }
+#endif /* ENABLE_PRINT */
 
 static void
 action_quit_cb (GtkAction *action,
@@ -231,7 +257,7 @@ action_save_cb (GtkAction *action,
                        return;
 
        filename = e_html_editor_get_filename (editor);
-       as_html = (e_html_editor_view_get_html_mode (e_html_editor_get_view (editor)));
+       as_html = (e_content_editor_get_html_mode (e_html_editor_get_content_editor (editor)));
 
        e_html_editor_save (editor, filename, as_html, &error);
        handle_error (&error);
@@ -249,7 +275,7 @@ action_save_as_cb (GtkAction *action,
                return;
 
        filename = e_html_editor_get_filename (editor);
-       as_html = (e_html_editor_view_get_html_mode (e_html_editor_get_view (editor)));
+       as_html = (e_content_editor_get_html_mode (e_html_editor_get_content_editor (editor)));
 
        e_html_editor_save (editor, filename, as_html, &error);
        handle_error (&error);
@@ -259,12 +285,10 @@ static void
 action_toggle_editor (GtkAction *action,
                       EHTMLEditor *editor)
 {
-       EHTMLEditorView *view;
+       EContentEditor *cnt_editor;
 
-       view = e_html_editor_get_view (editor);
-       webkit_web_view_set_editable (
-               WEBKIT_WEB_VIEW (view),
-               ! webkit_web_view_get_editable (WEBKIT_WEB_VIEW (view)));
+       cnt_editor = e_html_editor_get_content_editor (editor);
+       e_content_editor_set_editable (cnt_editor, !e_content_editor_is_editable (cnt_editor));
 }
 
 static void
@@ -293,16 +317,26 @@ action_view_inspector (GtkAction *action,
                        EHTMLEditor *editor)
 {
        WebKitWebInspector *inspector;
-       EHTMLEditorView *view;
+       EContentEditor *cnt_editor;
 
-       view = e_html_editor_get_view (editor);
-       inspector = webkit_web_view_get_inspector (WEBKIT_WEB_VIEW (view));
-
-       webkit_web_inspector_show (inspector);
+       cnt_editor = e_html_editor_get_content_editor (editor);
+       if (WEBKIT_IS_WEB_VIEW (cnt_editor)) {
+               inspector = webkit_web_view_get_inspector (WEBKIT_WEB_VIEW (cnt_editor));
+               webkit_web_inspector_show (inspector);
+       } else {
+               g_print ("Cannot show the inspector, the content editor is not a WebKitWebView descendant\n");
+       }
 }
 
 static GtkActionEntry file_entries[] = {
+       { "new-editor",
+         "document-new",
+         N_("_New editor"),
+         "<Control>N",
+         NULL,
+         G_CALLBACK (action_new_editor_cb) },
 
+#ifdef ENABLE_PRINT
        { "print",
          "document-print",
          N_("_Print..."),
@@ -316,6 +350,7 @@ static GtkActionEntry file_entries[] = {
          "<Control><Shift>p",
          NULL,
          G_CALLBACK (action_print_preview_cb) },
+#endif /* ENABLE_PRINT */
 
        { "quit",
          "application-exit",
@@ -376,11 +411,11 @@ static GtkActionEntry view_entries[] = {
          NULL,
          G_CALLBACK (action_view_plain_source) },
 
-       { "view-inspector",
+       { "view-webkit-inspector",
          NULL,
          N_("Inspector"),
          NULL,
-         NULL,
+         "<Control><Shift>I",
          G_CALLBACK (action_view_inspector) },
 
        { "view-menu",
@@ -391,53 +426,64 @@ static GtkActionEntry view_entries[] = {
          NULL }
 };
 
-static WebKitWebView *
-open_inspector (WebKitWebInspector *inspector,
-                WebKitWebView *webview,
-                gpointer user_data)
-{
-       GtkWidget *window;
-       GtkWidget *inspector_view;
-
-       window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
-       inspector_view = webkit_web_view_new ();
+static guint glob_editors = 0;
 
-       gtk_container_add (GTK_CONTAINER (window), GTK_WIDGET (inspector_view));
+static void
+editor_destroyed_cb (GtkWidget *editor)
+{
+       g_return_if_fail (glob_editors > 0);
 
-       gtk_widget_set_size_request (window, 600, 480);
-       gtk_widget_show (window);
+       glob_editors--;
 
-       return WEBKIT_WEB_VIEW (inspector_view);
+       if (!glob_editors)
+               gtk_main_quit ();
 }
 
-gint
-main (gint argc,
-      gchar **argv)
+static void
+create_new_editor_cb (GObject *source_object,
+                     GAsyncResult *result,
+                     gpointer user_data)
 {
        GtkActionGroup *action_group;
        GtkUIManager *manager;
        GtkWidget *container;
        GtkWidget *widget;
        EHTMLEditor *editor;
-       EHTMLEditorView *view;
-       WebKitWebInspector *inspector;
-
+       EContentEditor *cnt_editor;
        GError *error = NULL;
 
-       bindtextdomain (GETTEXT_PACKAGE, EVOLUTION_LOCALEDIR);
-       bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
-       textdomain (GETTEXT_PACKAGE);
+       widget = e_html_editor_new_finish (result, &error);
+       if (error) {
+               g_warning ("%s: Failed to create HTML editor: %s", G_STRFUNC, error->message);
+               g_clear_error (&error);
+               editor_destroyed_cb (NULL);
+               return;
+       }
 
-       gtk_init (&argc, &argv);
+       editor = E_HTML_EDITOR (widget);
+       cnt_editor = e_html_editor_get_content_editor (editor);
+
+       g_object_set (G_OBJECT (editor),
+               "halign", GTK_ALIGN_FILL,
+               "hexpand", TRUE,
+               "valign", GTK_ALIGN_FILL,
+               "vexpand", TRUE,
+               NULL);
 
-       editor = g_object_ref_sink (e_html_editor_new ());
-       view = e_html_editor_get_view (editor);
+       g_object_set (G_OBJECT (cnt_editor),
+               "halign", GTK_ALIGN_FILL,
+               "hexpand", TRUE,
+               "valign", GTK_ALIGN_FILL,
+               "vexpand", TRUE,
+               NULL);
+
+       if (WEBKIT_IS_WEB_VIEW (cnt_editor)) {
+               WebKitSettings *web_settings;
 
-       inspector = webkit_web_view_get_inspector (
-               WEBKIT_WEB_VIEW (view));
-       g_signal_connect (
-               inspector, "inspect-web-view",
-               G_CALLBACK (open_inspector), NULL);
+               web_settings = webkit_web_view_get_settings (WEBKIT_WEB_VIEW (cnt_editor));
+               webkit_settings_set_allow_file_access_from_file_urls (web_settings, TRUE);
+               webkit_settings_set_enable_developer_extras (web_settings, TRUE);
+       }
 
        widget = gtk_window_new (GTK_WINDOW_TOPLEVEL);
        gtk_widget_set_size_request (widget, 600, 400);
@@ -445,7 +491,7 @@ main (gint argc,
 
        g_signal_connect_swapped (
                widget, "destroy",
-               G_CALLBACK (gtk_main_quit), NULL);
+               G_CALLBACK (editor_destroyed_cb), NULL);
 
        container = widget;
 
@@ -467,6 +513,8 @@ main (gint argc,
        gtk_box_pack_start (GTK_BOX (container), widget, TRUE, TRUE, 0);
        gtk_widget_show (widget);
 
+       gtk_widget_grab_focus (GTK_WIDGET (cnt_editor));
+
        manager = e_html_editor_get_ui_manager (editor);
 
        gtk_ui_manager_add_ui_from_string (manager, file_ui, -1, &error);
@@ -491,16 +539,48 @@ main (gint argc,
                G_N_ELEMENTS (view_entries), editor);
        gtk_ui_manager_insert_action_group (manager, action_group, 0);
 
+       if (!WEBKIT_IS_WEB_VIEW (cnt_editor)) {
+               GtkAction *action;
+
+               action = e_html_editor_get_action (editor, "view-webkit-inspector");
+               gtk_action_set_visible (action, FALSE);
+       }
+
        gtk_ui_manager_ensure_update (manager);
+}
+
+static void
+create_new_editor (void)
+{
+       glob_editors++;
+
+       e_html_editor_new (create_new_editor_cb, NULL);
+}
+
+gint
+main (gint argc,
+      gchar **argv)
+{
+       GList *modules;
+
+       bindtextdomain (GETTEXT_PACKAGE, EVOLUTION_LOCALEDIR);
+       bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
+       textdomain (GETTEXT_PACKAGE);
+
+       gtk_init (&argc, &argv);
+
+       e_util_init_main_thread (NULL);
+       e_passwords_init ();
+
+       modules = e_module_load_all_in_directory (EVOLUTION_MODULEDIR);
+       g_list_free_full (modules, (GDestroyNotify) g_type_module_unuse);
 
-       g_signal_connect (
-               editor, "destroy",
-               G_CALLBACK (gtk_main_quit), NULL);
+       create_new_editor ();
 
        gtk_main ();
 
-       g_object_unref (editor);
        e_util_cleanup_settings ();
+       e_spell_checker_free_global_memory ();
 
        return 0;
 }
diff --git a/em-format/Makefile.am b/em-format/Makefile.am
index 7827d79..3f2c438 100644
--- a/em-format/Makefile.am
+++ b/em-format/Makefile.am
@@ -34,11 +34,11 @@ evolution_mail_formatter_include_HEADERS =          \
        e-mail-parser.h                                 \
        e-mail-part.h                                   \
        e-mail-part-attachment.h                        \
-       e-mail-part-attachment-bar.h                    \
        e-mail-part-audio.h                             \
        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
 
@@ -69,7 +69,6 @@ libevolution_mail_formatter_la_SOURCES =              \
        e-mail-formatter-quote.c                        \
        e-mail-formatter-utils.c                        \
        e-mail-formatter-attachment.c                   \
-       e-mail-formatter-attachment-bar.c               \
        e-mail-formatter-audio.c                        \
        e-mail-formatter-enumtypes.c                    \
        e-mail-formatter-error.c                        \
@@ -83,7 +82,6 @@ libevolution_mail_formatter_la_SOURCES =              \
        e-mail-formatter-text-html.c                    \
        e-mail-formatter-text-plain.c                   \
        e-mail-formatter-print-headers.c                \
-       e-mail-formatter-quote-attachment.c             \
        e-mail-formatter-quote-headers.c                \
        e-mail-formatter-quote-message-rfc822.c         \
        e-mail-formatter-quote-text-enriched.c          \
@@ -92,7 +90,6 @@ libevolution_mail_formatter_la_SOURCES =              \
        e-mail-parser-extension.c                       \
        e-mail-parser.c                                 \
        e-mail-parser-application-mbox.c                \
-       e-mail-parser-attachment-bar.c                  \
        e-mail-parser-audio.c                           \
        e-mail-parser-headers.c                         \
        e-mail-parser-image.c                           \
@@ -116,11 +113,11 @@ libevolution_mail_formatter_la_SOURCES =          \
        e-mail-parser-text-plain.c                      \
        e-mail-part.c                                   \
        e-mail-part-attachment.c                        \
-       e-mail-part-attachment-bar.c                    \
        e-mail-part-audio.c                             \
        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-attachment.c b/em-format/e-mail-formatter-attachment.c
index 2a2f2c9..86a39ef 100644
--- a/em-format/e-mail-formatter-attachment.c
+++ b/em-format/e-mail-formatter-attachment.c
@@ -26,7 +26,6 @@
 
 #include "e-mail-formatter-extension.h"
 #include "e-mail-inline-filter.h"
-#include "e-mail-part-attachment-bar.h"
 #include "e-mail-part-attachment.h"
 #include "e-mail-part-utils.h"
 
@@ -44,70 +43,10 @@ G_DEFINE_TYPE (
 
 static const gchar *formatter_mime_types[] = {
        E_MAIL_PART_ATTACHMENT_MIME_TYPE,
-       "application/vnd.evolution.widget.attachment-button",
+       "application/vnd.evolution.attachment-button",
        NULL
 };
 
-static EAttachmentStore *
-find_attachment_store (EMailPartList *part_list,
-                       EMailPart *start)
-{
-       EAttachmentStore *store = NULL;
-       GQueue queue = G_QUEUE_INIT;
-       GList *head, *link;
-       const gchar *start_id;
-       gchar *tmp, *pos;
-       EMailPart *part;
-       gchar *id;
-
-       start_id = e_mail_part_get_id (start);
-
-       e_mail_part_list_queue_parts (part_list, NULL, &queue);
-
-       head = g_queue_peek_head_link (&queue);
-
-       id = g_strconcat (start_id, ".attachment-bar", NULL);
-       tmp = g_strdup (id);
-       part = NULL;
-       do {
-               d (printf ("Looking up attachment bar as %s\n", id));
-
-               for (link = head; link != NULL; link = g_list_next (link)) {
-                       EMailPart *p = link->data;
-                       const gchar *p_id;
-
-                       p_id = e_mail_part_get_id (p);
-
-                       if (g_strcmp0 (p_id, id) == 0) {
-                               part = p;
-                               break;
-                       }
-               }
-
-               pos = g_strrstr (tmp, ".");
-               if (!pos)
-                       break;
-
-               g_free (id);
-               g_free (tmp);
-               tmp = g_strndup (start_id, pos - tmp);
-               id = g_strdup_printf ("%s.attachment-bar", tmp);
-
-       } while (pos && !part);
-
-       g_free (id);
-       g_free (tmp);
-
-       if (part != NULL)
-               store = e_mail_part_attachment_bar_get_store (
-                       E_MAIL_PART_ATTACHMENT_BAR (part));
-
-       while (!g_queue_is_empty (&queue))
-               g_object_unref (g_queue_pop_head (&queue));
-
-       return store;
-}
-
 static gboolean
 emfe_attachment_format (EMailFormatterExtension *extension,
                         EMailFormatter *formatter,
@@ -118,13 +57,16 @@ emfe_attachment_format (EMailFormatterExtension *extension,
 {
        gchar *text, *html;
        gchar *button_id;
-       EAttachmentStore *store;
        EMailExtensionRegistry *registry;
        GQueue *extensions;
        EMailPartAttachment *empa;
        CamelMimePart *mime_part;
        CamelMimeFilterToHTMLFlags flags;
+       GOutputStream *content_stream = NULL;
        GString *buffer;
+       gint icon_width, icon_height;
+       gchar *icon_uri;
+       gpointer attachment_ptr = NULL;
        const gchar *attachment_part_id;
        const gchar *part_id;
 
@@ -139,8 +81,8 @@ emfe_attachment_format (EMailFormatterExtension *extension,
                EAttachment *attachment;
                GList *head, *link;
 
-               attachment = e_mail_part_attachment_ref_attachment (
-                       E_MAIL_PART_ATTACHMENT (part));
+               attachment = e_mail_part_attachment_ref_attachment (E_MAIL_PART_ATTACHMENT (part));
+               attachment_ptr = attachment;
 
                head = g_queue_peek_head_link (&part->validities);
 
@@ -161,17 +103,9 @@ emfe_attachment_format (EMailFormatterExtension *extension,
                                        pair->validity->encrypt.status);
                }
 
-               store = find_attachment_store (context->part_list, part);
-               if (store) {
-                       GList *attachments = e_attachment_store_get_attachments (store);
-                       if (!g_list_find (attachments, attachment)) {
-                               e_attachment_store_add_attachment (
-                                       store, attachment);
-                       }
-                       g_list_free_full (attachments, g_object_unref);
-               } else {
-                       g_warning ("Failed to locate attachment-bar for %s", part_id);
-               }
+               e_attachment_set_initially_shown (attachment, e_mail_part_should_show_inline (part));
+
+               e_mail_formatter_claim_attachment (formatter, attachment);
 
                g_object_unref (attachment);
        }
@@ -255,43 +189,29 @@ emfe_attachment_format (EMailFormatterExtension *extension,
        g_free (text);
        g_object_unref (mime_part);
 
-       if (empa->attachment_view_part_id)
-               attachment_part_id = empa->attachment_view_part_id;
+       if (empa->part_id_with_attachment)
+               attachment_part_id = empa->part_id_with_attachment;
        else
                attachment_part_id = part_id;
 
        button_id = g_strconcat (attachment_part_id, ".attachment_button", NULL);
 
-       /* XXX Wild guess at the initial size. */
-       buffer = g_string_sized_new (8192);
-
-       g_string_append_printf (
-               buffer,
-               "<div class=\"attachment\">"
-               "<table width=\"100%%\" border=\"0\">"
-               "<tr valign=\"middle\">"
-               "<td align=\"left\" width=\"100\">"
-               "<object type=\"application/vnd.evolution.widget.attachment-button\" "
-               "height=\"20\" width=\"100\" data=\"%s\" id=\"%s\"></object>"
-               "</td>"
-               "<td align=\"left\">%s</td>"
-               "</tr>", part_id, button_id, html);
-
-       g_free (button_id);
-       g_free (html);
+       if (!gtk_icon_size_lookup (GTK_ICON_SIZE_BUTTON, &icon_width, &icon_height)) {
+               icon_width = 16;
+               icon_height = 16;
+       }
 
        if (extensions != NULL) {
-               GOutputStream *content_stream;
                gboolean success = FALSE;
 
                content_stream = g_memory_output_stream_new_resizable ();
 
-               if (empa->attachment_view_part_id != NULL) {
+               if (empa->part_id_with_attachment != NULL) {
                        EMailPart *attachment_view_part;
 
                        attachment_view_part = e_mail_part_list_ref_part (
                                context->part_list,
-                               empa->attachment_view_part_id);
+                               empa->part_id_with_attachment);
 
                        /* Avoid recursion. */
                        if (attachment_view_part == part)
@@ -322,51 +242,85 @@ emfe_attachment_format (EMailFormatterExtension *extension,
                        }
                }
 
-               if (success) {
-                       gchar *wrapper_element_id;
-                       gconstpointer data;
-                       gsize size;
+               e_mail_part_attachment_set_expandable (empa, success);
+       }
 
-                       wrapper_element_id = g_strconcat (
-                               attachment_part_id, ".wrapper", NULL);
+       icon_uri = e_mail_part_build_uri (
+               e_mail_part_list_get_folder (context->part_list),
+               e_mail_part_list_get_message_uid (context->part_list),
+               "part_id", G_TYPE_STRING, part_id,
+               "attachment_icon", G_TYPE_POINTER, attachment_ptr,
+               "size", G_TYPE_INT, icon_width,
+               NULL);
 
-                       data = g_memory_output_stream_get_data (
-                               G_MEMORY_OUTPUT_STREAM (content_stream));
-                       size = g_memory_output_stream_get_data_size (
-                               G_MEMORY_OUTPUT_STREAM (content_stream));
+       /* XXX Wild guess at the initial size. */
+       buffer = g_string_sized_new (8192);
 
-                       g_string_append_printf (
-                               buffer,
-                               "<tr><td colspan=\"2\">"
-                               "<div class=\"attachment-wrapper\" id=\"%s\"",
-                               wrapper_element_id);
+       g_string_append_printf (
+               buffer,
+               "<div class=\"attachment\">"
+               "<table width=\"100%%\" border=\"0\">"
+               "<tr valign=\"middle\">"
+               "<td align=\"left\" width=\"1px\" style=\"white-space:pre;\">"
+               "<button type=\"button\" class=\"attachment-expander\" id=\"%s\" value=\"%p\" data=\"%s\" 
style=\"vertical-align:middle; margin:0px;\" %s>"
+               "<img id=\"attachment-expander-img-%p\" src=\"gtk-stock://%s?size=%d\" width=\"%dpx\" 
height=\"%dpx\" style=\"vertical-align:middle;\">"
+               "<img src=\"%s\" width=\"%dpx\" height=\"%dpx\" style=\"vertical-align:middle;\">"
+               "</button>"
+               "<button type=\"button\" class=\"attachment-menu\" id=\"%s\" value=\"%p\" 
style=\"vertical-align:middle; margin:0px;\">"
+               "<img src=\"gtk-stock://x-evolution-arrow-down?size=%d\" width=\"%dpx\" height=\"%dpx\" 
style=\"vertical-align:middle;\">"
+               "</button>"
+               "</td><td align=\"left\">%s</td></tr>",
+               part_id, attachment_ptr, html, e_mail_part_attachment_get_expandable (empa) ? "" : "disabled",
+               attachment_ptr, e_mail_part_should_show_inline (part) ? "go-down" : "go-next", 
GTK_ICON_SIZE_BUTTON, icon_width, icon_height,
+               icon_uri, icon_width, icon_height,
+               part_id, attachment_ptr, GTK_ICON_SIZE_BUTTON, icon_width, icon_height,
+               html);
+
+       g_free (icon_uri);
+       g_free (button_id);
+       g_free (html);
 
-                       if (e_mail_part_should_show_inline (part)) {
-                               g_string_append (buffer, ">");
-                               g_string_append_len (buffer, data, size);
-                       } else {
-                               gchar *inner_html_data;
+       if (content_stream && e_mail_part_attachment_get_expandable (empa)) {
+               gchar *wrapper_element_id;
+               gconstpointer data;
+               gsize size;
 
-                               inner_html_data = g_markup_escape_text (data, size);
+               wrapper_element_id = g_strdup_printf ("attachment-wrapper-%p", attachment_ptr);
 
-                               g_string_append_printf (
-                                       buffer,
-                                       " inner-html-data=\"%s\">",
-                                       inner_html_data);
+               data = g_memory_output_stream_get_data (
+                       G_MEMORY_OUTPUT_STREAM (content_stream));
+               size = g_memory_output_stream_get_data_size (
+                       G_MEMORY_OUTPUT_STREAM (content_stream));
 
-                               g_free (inner_html_data);
-                       }
+               g_string_append_printf (
+                       buffer,
+                       "<tr><td colspan=\"2\">"
+                       "<div class=\"attachment-wrapper\" id=\"%s\"",
+                       wrapper_element_id);
 
-                       g_string_append (buffer, "</div></td></tr>");
+               if (e_mail_part_should_show_inline (part)) {
+                       g_string_append (buffer, ">");
+                       g_string_append_len (buffer, data, size);
+               } else {
+                       gchar *inner_html_data;
 
-                       e_mail_part_attachment_set_expandable (empa, TRUE);
+                       inner_html_data = g_markup_escape_text (data, size);
 
-                       g_free (wrapper_element_id);
+                       g_string_append_printf (
+                               buffer,
+                               " inner-html-data=\"%s\">",
+                               inner_html_data);
+
+                       g_free (inner_html_data);
                }
 
-               g_object_unref (content_stream);
+               g_string_append (buffer, "</div></td></tr>");
+
+               g_free (wrapper_element_id);
        }
 
+       g_clear_object (&content_stream);
+
        g_string_append (buffer, "</table></div>");
 
        g_output_stream_write_all (
@@ -377,47 +331,6 @@ emfe_attachment_format (EMailFormatterExtension *extension,
        return TRUE;
 }
 
-static GtkWidget *
-emfe_attachment_get_widget (EMailFormatterExtension *extension,
-                            EMailPartList *context,
-                            EMailPart *part,
-                            GHashTable *params)
-{
-       EAttachment *attachment;
-       EAttachmentStore *store;
-       EAttachmentView *view;
-       GtkWidget *widget;
-       const gchar *part_id;
-
-       g_return_val_if_fail (E_IS_MAIL_PART_ATTACHMENT (part), NULL);
-
-       attachment = e_mail_part_attachment_ref_attachment (
-               E_MAIL_PART_ATTACHMENT (part));
-
-       part_id = e_mail_part_get_id (part);
-
-       store = find_attachment_store (context, part);
-       widget = e_attachment_button_new ();
-       g_object_set_data_full (
-               G_OBJECT (widget),
-               "uri", g_strdup (part_id),
-               (GDestroyNotify) g_free);
-       e_attachment_button_set_attachment (
-               E_ATTACHMENT_BUTTON (widget), attachment);
-
-       view = g_object_get_data (G_OBJECT (store), "attachment-bar");
-       if (view != NULL)
-               e_attachment_button_set_view (
-                       E_ATTACHMENT_BUTTON (widget), view);
-
-       gtk_widget_set_can_focus (widget, TRUE);
-       gtk_widget_show (widget);
-
-       g_object_unref (attachment);
-
-       return widget;
-}
-
 static void
 e_mail_formatter_attachment_class_init (EMailFormatterExtensionClass *class)
 {
@@ -426,7 +339,6 @@ e_mail_formatter_attachment_class_init (EMailFormatterExtensionClass *class)
        class->mime_types = formatter_mime_types;
        class->priority = G_PRIORITY_LOW;
        class->format = emfe_attachment_format;
-       class->get_widget = emfe_attachment_get_widget;
 }
 
 static void
diff --git a/em-format/e-mail-formatter-audio.c b/em-format/e-mail-formatter-audio.c
index cf980f8..2837000 100644
--- a/em-format/e-mail-formatter-audio.c
+++ b/em-format/e-mail-formatter-audio.c
@@ -40,7 +40,7 @@ G_DEFINE_TYPE (
        E_TYPE_MAIL_FORMATTER_EXTENSION)
 
 static const gchar *formatter_mime_types[] = {
-       "application/vnd.evolution.widget.audio",
+       "application/vnd.evolution.audio",
        "audio/ac3",
        "audio/x-ac3",
        "audio/basic",
diff --git a/em-format/e-mail-formatter-enums.h b/em-format/e-mail-formatter-enums.h
index e7b1868..de64568 100644
--- a/em-format/e-mail-formatter-enums.h
+++ b/em-format/e-mail-formatter-enums.h
@@ -49,7 +49,6 @@ typedef enum {
        E_MAIL_FORMATTER_MODE_NORMAL = 0,
        E_MAIL_FORMATTER_MODE_SOURCE,
        E_MAIL_FORMATTER_MODE_RAW,
-       E_MAIL_FORMATTER_MODE_CID,
        E_MAIL_FORMATTER_MODE_PRINTING,
        E_MAIL_FORMATTER_MODE_ALL_HEADERS
 } EMailFormatterMode;
diff --git a/em-format/e-mail-formatter-extension.c b/em-format/e-mail-formatter-extension.c
index 55b481b..3a2dd52 100644
--- a/em-format/e-mail-formatter-extension.c
+++ b/em-format/e-mail-formatter-extension.c
@@ -78,64 +78,3 @@ e_mail_formatter_extension_format (EMailFormatterExtension *extension,
        return class->format (
                extension, formatter, context, part, stream, cancellable);
 }
-
-/**
- * e_mail_formatter_extension_has_widget:
- * @extension: an #EMailFormatterExtension
- *
- * Returns whether the extension can provide a GtkWidget.
- *
- * Returns: Returns %TRUE when @extension reimplements get_widget(), %FALSE
- * otherwise.
- */
-gboolean
-e_mail_formatter_extension_has_widget (EMailFormatterExtension *extension)
-{
-       EMailFormatterExtensionClass *class;
-
-       g_return_val_if_fail (E_IS_MAIL_FORMATTER_EXTENSION (extension), FALSE);
-
-       class = E_MAIL_FORMATTER_EXTENSION_GET_CLASS (extension);
-
-       return (class->get_widget != NULL);
-}
-
-/**
- * e_mail_formatter_extension_get_widget:
- * @extension: an #EMailFormatterExtension
- * @part: an #EMailPart
- * @params: a #GHashTable
- *
- * A virtual function reimplemented in some mail formatter extensions. The
- * function should construct a #GtkWidget for given @part. The @params hash
- * table can contain additional parameters listed in the &lt;object&gt; HTML
- * element that has requested the widget.
- *
- * When @bind_dom_func is not %NULL, the callee will set a callback function
- * which should be called when the webpage is completely rendered to setup
- * bindings between DOM events and the widget.
- *
- * Returns: Returns a #GtkWidget or %NULL, when error occurs or given
- * @extension does not reimplement this method.
- */
-GtkWidget *
-e_mail_formatter_extension_get_widget (EMailFormatterExtension *extension,
-                                       EMailPartList *context,
-                                       EMailPart *part,
-                                       GHashTable *params)
-{
-       EMailFormatterExtensionClass *class;
-       GtkWidget *widget = NULL;
-
-       g_return_val_if_fail (E_IS_MAIL_FORMATTER_EXTENSION (extension), NULL);
-       g_return_val_if_fail (part != NULL, NULL);
-       g_return_val_if_fail (params != NULL, NULL);
-
-       class = E_MAIL_FORMATTER_EXTENSION_GET_CLASS (extension);
-
-       if (class->get_widget != NULL)
-               widget = class->get_widget (extension, context, part, params);
-
-       return widget;
-}
-
diff --git a/em-format/e-mail-formatter-extension.h b/em-format/e-mail-formatter-extension.h
index adfe98f..8e46deb 100644
--- a/em-format/e-mail-formatter-extension.h
+++ b/em-format/e-mail-formatter-extension.h
@@ -83,10 +83,6 @@ struct _EMailFormatterExtensionClass {
                                         EMailPart *part,
                                         GOutputStream *stream,
                                         GCancellable *cancellable);
-       GtkWidget *     (*get_widget)   (EMailFormatterExtension *extension,
-                                        EMailPartList *context,
-                                        EMailPart *part,
-                                        GHashTable *params);
 };
 
 GType          e_mail_formatter_extension_get_type
@@ -98,13 +94,6 @@ gboolean     e_mail_formatter_extension_format
                                                 EMailPart *part,
                                                 GOutputStream *stream,
                                                 GCancellable *cancellable);
-gboolean       e_mail_formatter_extension_has_widget
-                                               (EMailFormatterExtension *extension);
-GtkWidget *    e_mail_formatter_extension_get_widget
-                                               (EMailFormatterExtension *extension,
-                                                EMailPartList *context,
-                                                EMailPart *part,
-                                                GHashTable *params);
 
 G_END_DECLS
 
diff --git a/em-format/e-mail-formatter-headers.c b/em-format/e-mail-formatter-headers.c
index 42d2e9e..2affbe1 100644
--- a/em-format/e-mail-formatter-headers.c
+++ b/em-format/e-mail-formatter-headers.c
@@ -522,7 +522,7 @@ emfe_headers_format (EMailFormatterExtension *extension,
        g_string_append_printf (
                buffer,
                "%s id=\"%s\"><table class=\"-e-mail-formatter-header-color\" border=\"0\" width=\"100%%\" "
-               "style=\"direction: %s\">"
+               "style=\"direction: %s; border-spacing: 0px\">"
                "<tr>",
                (context->mode != E_MAIL_FORMATTER_MODE_PRINTING) ?
                        "<div class=\"headers -e-mail-formatter-body-color\"" :
@@ -533,7 +533,7 @@ emfe_headers_format (EMailFormatterExtension *extension,
        if (is_collapsable)
                g_string_append_printf (
                        buffer,
-                       "<td valign=\"top\" width=\"16\">"
+                       "<td valign=\"top\" width=\"16\" style=\"padding-left: 0px\">"
                        "<img src=\"evo-file://%s/%s\" class=\"navigable\" "
                        "     id=\"__evo-collapse-headers-img\" />"
                        "</td>",
diff --git a/em-format/e-mail-formatter-message-rfc822.c b/em-format/e-mail-formatter-message-rfc822.c
index 8884cbc..7f8d6ef 100644
--- a/em-format/e-mail-formatter-message-rfc822.c
+++ b/em-format/e-mail-formatter-message-rfc822.c
@@ -157,10 +157,6 @@ emfe_message_rfc822_format (EMailFormatterExtension *extension,
                        EMailPart *p = link->data;
                        const gchar *p_id;
 
-                       /* Skip attachment bar */
-                       if (e_mail_part_id_has_suffix (part, ".attachment-bar"))
-                               continue;
-
                        p_id = e_mail_part_get_id (p);
 
                        /* Check for nested rfc822 messages */
diff --git a/em-format/e-mail-formatter-quote-message-rfc822.c 
b/em-format/e-mail-formatter-quote-message-rfc822.c
index 180a283..7ed0bd4 100644
--- a/em-format/e-mail-formatter-quote-message-rfc822.c
+++ b/em-format/e-mail-formatter-quote-message-rfc822.c
@@ -89,10 +89,6 @@ emfqe_message_rfc822_format (EMailFormatterExtension *extension,
 
                p_id = e_mail_part_get_id (p);
 
-               /* Skip attachment bar */
-               if (e_mail_part_id_has_suffix (p, ".attachment-bar"))
-                       continue;
-
                if (e_mail_part_id_has_suffix (p, ".headers.")) {
                        if (qc->qf_flags & E_MAIL_FORMATTER_QUOTE_FLAG_HEADERS) {
                                e_mail_formatter_format_as (
diff --git a/em-format/e-mail-formatter-quote.c b/em-format/e-mail-formatter-quote.c
index adbe223..24abcbb 100644
--- a/em-format/e-mail-formatter-quote.c
+++ b/em-format/e-mail-formatter-quote.c
@@ -38,7 +38,6 @@ struct _EMailFormatterQuotePrivate {
 };
 
 /* internal formatter extensions */
-GType e_mail_formatter_quote_attachment_get_type (void);
 GType e_mail_formatter_quote_headers_get_type (void);
 GType e_mail_formatter_quote_message_rfc822_get_type (void);
 GType e_mail_formatter_quote_text_enriched_get_type (void);
@@ -161,7 +160,6 @@ static void
 e_mail_formatter_quote_base_init (EMailFormatterQuoteClass *class)
 {
        /* Register internal extensions. */
-       g_type_ensure (e_mail_formatter_quote_attachment_get_type ());
        g_type_ensure (e_mail_formatter_quote_headers_get_type ());
        g_type_ensure (e_mail_formatter_quote_message_rfc822_get_type ());
        g_type_ensure (e_mail_formatter_quote_text_enriched_get_type ());
diff --git a/em-format/e-mail-formatter-secure-button.c b/em-format/e-mail-formatter-secure-button.c
index d4f6a96..80cfb09 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
 };
 
@@ -76,139 +71,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;
+/* 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 (CamelCipherValidityEncrypt status);
+const gchar *e_mail_formatter_secure_button_get_sign_description (CamelCipherValiditySign status);
 
-       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;
-}
-
-#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 (CamelCipherValiditySign 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));
+       g_return_val_if_fail (status >= 0 && status < G_N_ELEMENTS (smime_sign_table), NULL);
 
-       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 : "");
-       }
+       return _(smime_sign_table[status].description);
 }
-#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)
+const gchar *
+e_mail_formatter_secure_button_get_encrypt_description (CamelCipherValidityEncrypt 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;
-               }
-
-               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++;
-               }
-       }
+       g_return_val_if_fail (status >= 0 && status < G_N_ELEMENTS (smime_encrypt_table), NULL);
 
-       gtk_container_add (GTK_CONTAINER (grid), GTK_WIDGET (table));
+       return _(smime_encrypt_table[status].description);
 }
 
 static void
@@ -246,9 +127,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') {
@@ -263,130 +144,22 @@ 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_label_set_max_width_chars (GTK_LABEL (w), 80);
-       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_label_set_max_width_chars (GTK_LABEL (w), 80);
-       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
 add_photo_cb (gpointer data,
              gpointer user_data)
 {
        CamelCipherCertInfo *cert_info = data;
        gint width, height;
-       GtkWidget *image;
-       GdkPixbuf *pixbuf, *scaled;
-       GtkBox *box = user_data;
+       GString *html = user_data;
        const gchar *photo_filename;
+       gchar *uri;
 
        g_return_if_fail (cert_info != NULL);
-       g_return_if_fail (GTK_IS_BOX (box));
+       g_return_if_fail (html != NULL);
 
        photo_filename = camel_cipher_certinfo_get_property (cert_info, 
CAMEL_CIPHER_CERT_INFO_PROPERTY_PHOTO_FILENAME);
        if (!photo_filename || !g_file_test (photo_filename, G_FILE_TEST_EXISTS | G_FILE_TEST_IS_REGULAR))
                return;
 
-       pixbuf = gdk_pixbuf_new_from_file (photo_filename, NULL);
-       if (!pixbuf)
-               return;
-
        if (!gtk_icon_size_lookup (GTK_ICON_SIZE_DND, &width, &height)) {
                width = 32;
                height = 32;
@@ -397,30 +170,25 @@ add_photo_cb (gpointer data,
        if (height < 32)
                height = 32;
 
-       scaled = e_icon_factory_pixbuf_scale (pixbuf, width, height);
-       g_object_unref (pixbuf);
+       uri = g_filename_to_uri (photo_filename, NULL, NULL);
 
-       if (!scaled)
-               return;
-
-       image = gtk_image_new_from_pixbuf (scaled);
-       g_object_unref (scaled);
+       g_string_append_printf (html, "<img src=\"evo-%s\" width=\"%dpx\" height=\"%dpx\" 
style=\"vertical-align:middle; margin-right:4px;\">",
+               uri, width, height);
 
-       if (!image)
-               return;
-
-       gtk_box_pack_start (box, image, FALSE, FALSE, 0);
+       g_free (uri);
 }
 
-static GtkWidget *
-secure_button_get_widget_for_validity (CamelCipherValidity *validity)
+static void
+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 ("");
 
@@ -441,7 +209,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;
@@ -451,90 +219,70 @@ 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);
-
-       g_queue_foreach (&validity->sign.signers, add_photo_cb, layout);
-       g_queue_foreach (&validity->encrypt.encrypters, add_photo_cb, layout);
-
-       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;\">",
+               part, validity, icon_name, GTK_ICON_SIZE_LARGE_TOOLBAR,
+               icon_width, icon_height, e_rgba_to_value (&smime_sign_colour[5]));
+
+       g_queue_foreach (&validity->sign.signers, add_photo_cb, html);
+       g_queue_foreach (&validity->encrypt.encrypters, add_photo_cb, html);
+
+       g_string_append_printf (html, "%s</span></td></tr></table>\n", description);
 
-       return box;
+       g_free (description);
 }
 
-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
@@ -543,7 +291,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-formatter.c b/em-format/e-mail-formatter.c
index 668b2ef..13e68c5 100644
--- a/em-format/e-mail-formatter.c
+++ b/em-format/e-mail-formatter.c
@@ -34,7 +34,7 @@
 
 #define E_MAIL_FORMATTER_GET_PRIVATE(obj) \
        (G_TYPE_INSTANCE_GET_PRIVATE \
-       ((obj), E_TYPE_MAIL_FORMATTER, EMailFormatterPrivate))\
+       ((obj), E_TYPE_MAIL_FORMATTER, EMailFormatterPrivate))
 
 #define STYLESHEET_URI \
        "evo-file://" EVOLUTION_PRIVDATADIR "/theme/webview.css"
@@ -63,7 +63,6 @@ struct _AsyncContext {
 
 /* internal formatter extensions */
 GType e_mail_formatter_attachment_get_type (void);
-GType e_mail_formatter_attachment_bar_get_type (void);
 GType e_mail_formatter_audio_get_type (void);
 GType e_mail_formatter_error_get_type (void);
 GType e_mail_formatter_headers_get_type (void);
@@ -96,6 +95,7 @@ enum {
 
 enum {
        NEED_REDRAW,
+       CLAIM_ATTACHMENT,
        LAST_SIGNAL
 };
 
@@ -451,8 +451,7 @@ mail_formatter_run (EMailFormatter *formatter,
 
                if (!ok) {
                        /* We don't want to source these */
-                       if (e_mail_part_id_has_suffix (part, ".headers") ||
-                           e_mail_part_id_has_suffix (part, "attachment-bar"))
+                       if (e_mail_part_id_has_suffix (part, ".headers"))
                                continue;
 
                        e_mail_formatter_format_as (
@@ -560,7 +559,6 @@ e_mail_formatter_base_init (EMailFormatterClass *class)
 
        /* Register internal extensions. */
        g_type_ensure (e_mail_formatter_attachment_get_type ());
-       g_type_ensure (e_mail_formatter_attachment_bar_get_type ());
        g_type_ensure (e_mail_formatter_audio_get_type ());
        g_type_ensure (e_mail_formatter_error_get_type ());
        g_type_ensure (e_mail_formatter_headers_get_type ());
@@ -773,6 +771,14 @@ e_mail_formatter_class_init (EMailFormatterClass *class)
                        G_PARAM_READWRITE |
                        G_PARAM_STATIC_STRINGS));
 
+       signals[CLAIM_ATTACHMENT] = g_signal_new (
+               "claim-attachment",
+               E_TYPE_MAIL_FORMATTER,
+               G_SIGNAL_RUN_FIRST,
+               G_STRUCT_OFFSET (EMailFormatterClass, claim_attachment),
+               NULL, NULL, NULL,
+               G_TYPE_NONE, 1, E_TYPE_ATTACHMENT);
+
        signals[NEED_REDRAW] = g_signal_new (
                "need-redraw",
                E_TYPE_MAIL_FORMATTER,
@@ -837,6 +843,16 @@ e_mail_formatter_get_type (void)
 }
 
 void
+e_mail_formatter_claim_attachment (EMailFormatter *formatter,
+                                  EAttachment *attachment)
+{
+       g_return_if_fail (E_IS_MAIL_FORMATTER (formatter));
+       g_return_if_fail (E_IS_ATTACHMENT (attachment));
+
+       g_signal_emit (formatter, signals[CLAIM_ATTACHMENT], 0, attachment);
+}
+
+void
 e_mail_formatter_format_sync (EMailFormatter *formatter,
                               EMailPartList *part_list,
                               GOutputStream *stream,
@@ -1117,7 +1133,6 @@ e_mail_formatter_format_text (EMailFormatter *formatter,
        g_output_stream_flush (stream, cancellable, NULL);
 
        g_object_unref (stream);
-
        g_clear_object (&windows);
        g_clear_object (&mime_part);
 }
@@ -1131,9 +1146,9 @@ e_mail_formatter_get_sub_html_header (EMailFormatter *formatter)
                "<meta name=\"generator\" content=\"Evolution Mail\"/>\n"
                "<title>Evolution Mail Display</title>\n"
                "<link type=\"text/css\" rel=\"stylesheet\" "
-               "      href=\"" STYLESHEET_URI "\"/>\n"
+               " href=\"" STYLESHEET_URI "\"/>\n"
                "<style type=\"text/css\">\n"
-               "  table th { font-weight: bold; }\n"
+               " table th { font-weight: bold; }\n"
                "</style>\n"
                "</head>"
                "<body class=\"-e-web-view-background-color -e-web-view-text-color\">";
@@ -1149,9 +1164,9 @@ e_mail_formatter_get_html_header (EMailFormatter *formatter)
                "<meta name=\"generator\" content=\"Evolution Mail\"/>\n"
                "<title>Evolution Mail Display</title>\n"
                "<link type=\"text/css\" rel=\"stylesheet\" "
-               "      href=\"" STYLESHEET_URI "\"/>\n"
+               " href=\"" STYLESHEET_URI "\"/>\n"
                "<style type=\"text/css\">\n"
-               "  table th { font-weight: bold; }\n"
+               " table th { font-weight: bold; }\n"
                "</style>\n"
                "</head>"
                "<body class=\"-e-mail-formatter-body-color "
diff --git a/em-format/e-mail-formatter.h b/em-format/e-mail-formatter.h
index a05755c..777e31a 100644
--- a/em-format/e-mail-formatter.h
+++ b/em-format/e-mail-formatter.h
@@ -85,13 +85,17 @@ struct _EMailFormatterClass {
 
        /* Signals */
        void            (*need_redraw)          (EMailFormatter *formatter);
+       void            (*claim_attachment)     (EMailFormatter *formatter,
+                                                EAttachment *attachment);
 };
 
 GType          e_mail_formatter_get_type       (void);
 
 EMailFormatter *
                e_mail_formatter_new            (void);
-
+void           e_mail_formatter_claim_attachment
+                                               (EMailFormatter *formatter,
+                                                EAttachment *attachment);
 void           e_mail_formatter_format_sync    (EMailFormatter *formatter,
                                                 EMailPartList *part_list,
                                                 GOutputStream *stream,
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-message.c b/em-format/e-mail-parser-message.c
index 282cef4..0a3b77f 100644
--- a/em-format/e-mail-parser-message.c
+++ b/em-format/e-mail-parser-message.c
@@ -63,12 +63,6 @@ empe_message_parse (EMailParserExtension *extension,
                "application/vnd.evolution.headers",
                cancellable, out_mail_parts);
 
-       /* Attachment Bar */
-       e_mail_parser_parse_part_as (
-               parser, part, part_id,
-               "application/vnd.evolution.widget.attachment-bar",
-               cancellable, out_mail_parts);
-
        ct = camel_mime_part_get_content_type (part);
        mime_type = camel_content_type_simple (ct);
 
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-parser-text-plain.c b/em-format/e-mail-parser-text-plain.c
index ccb371d..aa5c55a 100644
--- a/em-format/e-mail-parser-text-plain.c
+++ b/em-format/e-mail-parser-text-plain.c
@@ -185,7 +185,7 @@ empe_text_plain_parse (EMailParserExtension *extension,
 
                                empa->shown = FALSE;
                                attachment = e_mail_part_attachment_ref_attachment (empa);
-                               e_attachment_set_shown (attachment, FALSE);
+                               e_attachment_set_initially_shown (attachment, FALSE);
                                e_attachment_set_can_show (attachment, FALSE);
 
                                att_part = e_attachment_ref_mime_part (attachment);
diff --git a/em-format/e-mail-parser.c b/em-format/e-mail-parser.c
index c78cb70..b5fe91e 100644
--- a/em-format/e-mail-parser.c
+++ b/em-format/e-mail-parser.c
@@ -53,7 +53,6 @@ enum {
 
 /* internal parser extensions */
 GType e_mail_parser_application_mbox_get_type (void);
-GType e_mail_parser_attachment_bar_get_type (void);
 GType e_mail_parser_audio_get_type (void);
 GType e_mail_parser_headers_get_type (void);
 GType e_mail_parser_message_get_type (void);
@@ -221,7 +220,6 @@ e_mail_parser_base_init (EMailParserClass *class)
 
        /* Register internal extensions. */
        g_type_ensure (e_mail_parser_application_mbox_get_type ());
-       g_type_ensure (e_mail_parser_attachment_bar_get_type ());
        g_type_ensure (e_mail_parser_audio_get_type ());
        g_type_ensure (e_mail_parser_headers_get_type ());
        g_type_ensure (e_mail_parser_message_get_type ());
@@ -714,13 +712,13 @@ e_mail_parser_wrap_as_attachment (EMailParser *parser,
        first_part = g_queue_peek_head (parts_queue);
        if (first_part != NULL) {
                const gchar *id = e_mail_part_get_id (first_part);
-               empa->attachment_view_part_id = g_strdup (id);
+               empa->part_id_with_attachment = g_strdup (id);
                first_part->is_hidden = TRUE;
        }
 
        attachment = e_mail_part_attachment_ref_attachment (empa);
 
-       e_attachment_set_shown (attachment, empa->shown);
+       e_attachment_set_initially_shown (attachment, empa->shown);
        e_attachment_set_can_show (
                attachment,
                extensions && !g_queue_is_empty (extensions));
diff --git a/em-format/e-mail-part-attachment.c b/em-format/e-mail-part-attachment.c
index e12e555..3490c3d 100644
--- a/em-format/e-mail-part-attachment.c
+++ b/em-format/e-mail-part-attachment.c
@@ -98,7 +98,7 @@ mail_part_attachment_finalize (GObject *object)
 {
        EMailPartAttachment *part = E_MAIL_PART_ATTACHMENT (object);
 
-       g_free (part->attachment_view_part_id);
+       g_free (part->part_id_with_attachment);
 
        /* Chain up to parent's finalize() method. */
        G_OBJECT_CLASS (e_mail_part_attachment_parent_class)->
diff --git a/em-format/e-mail-part-attachment.h b/em-format/e-mail-part-attachment.h
index bdf859c..818ab30 100644
--- a/em-format/e-mail-part-attachment.h
+++ b/em-format/e-mail-part-attachment.h
@@ -52,7 +52,7 @@ struct _EMailPartAttachment {
        EMailPart parent;
        EMailPartAttachmentPrivate *priv;
 
-       gchar *attachment_view_part_id;
+       gchar *part_id_with_attachment;
 
        gboolean shown;
        const gchar *snoop_mime_type;
diff --git a/em-format/e-mail-part-headers.c b/em-format/e-mail-part-headers.c
index 5c494cd..1cc4ebb 100644
--- a/em-format/e-mail-part-headers.c
+++ b/em-format/e-mail-part-headers.c
@@ -218,29 +218,21 @@ mail_part_headers_constructed (GObject *object)
 
 static void
 mail_part_headers_bind_dom_element (EMailPart *part,
-                                    WebKitDOMElement *element)
+                                    GDBusProxy *web_extension,
+                                    guint64 page_id,
+                                    const gchar *element_id)
 {
-       WebKitDOMDocument *document;
-       WebKitDOMElement *photo;
-       gchar *addr, *uri;
-
-       document = webkit_dom_node_get_owner_document (
-               WEBKIT_DOM_NODE (element));
-       photo = webkit_dom_document_get_element_by_id (
-               document, "__evo-contact-photo");
-
-       /* Contact photos disabled, the <img> tag is not there. */
-       if (photo == NULL)
-               return;
-
-       addr = webkit_dom_element_get_attribute (photo, "data-mailaddr");
-       uri = g_strdup_printf ("mail://contact-photo?mailaddr=%s", addr);
-
-       webkit_dom_html_image_element_set_src (
-               WEBKIT_DOM_HTML_IMAGE_ELEMENT (photo), uri);
-
-       g_free (addr);
-       g_free (uri);
+       if (web_extension) {
+               g_dbus_proxy_call (
+                       web_extension,
+                       "EMailPartHeadersBindDOMElement",
+                       g_variant_new ("(ts)", page_id, element_id),
+                       G_DBUS_CALL_FLAGS_NONE,
+                       -1,
+                       NULL,
+                       NULL,
+                       NULL);
+       }
 }
 
 static void
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..2455303
--- /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 (CamelCipherValidityEncrypt status);
+const gchar *e_mail_formatter_secure_button_get_sign_description (CamelCipherValiditySign 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-utils.c b/em-format/e-mail-part-utils.c
index b0899ee..0568a35 100644
--- a/em-format/e-mail-part-utils.c
+++ b/em-format/e-mail-part-utils.c
@@ -488,6 +488,11 @@ e_mail_part_build_uri (CamelFolder *folder,
                                g_free (escaped);
                                break;
                        }
+                       case G_TYPE_POINTER: {
+                               gpointer val = va_arg (ap, gpointer);
+                               tmp2 = g_strdup_printf ("%s%c%s=%p", tmp, separator, name, val);
+                               break;
+                       }
                        default:
                                g_warning ("Invalid param type %s", g_type_name (type));
                                va_end (ap);
diff --git a/em-format/e-mail-part.c b/em-format/e-mail-part.c
index 432481c..fbfb332 100644
--- a/em-format/e-mail-part.c
+++ b/em-format/e-mail-part.c
@@ -585,17 +585,36 @@ e_mail_part_set_is_attachment (EMailPart *part,
 
 void
 e_mail_part_bind_dom_element (EMailPart *part,
-                              WebKitDOMElement *element)
+                              GDBusProxy *web_extension,
+                              guint64 page_id,
+                              const gchar *element_id)
 {
        EMailPartClass *class;
 
        g_return_if_fail (E_IS_MAIL_PART (part));
-       g_return_if_fail (WEBKIT_DOM_IS_ELEMENT (element));
+       g_return_if_fail (web_extension);
+       g_return_if_fail (page_id != 0);
+       g_return_if_fail (element_id && *element_id);
 
        class = E_MAIL_PART_GET_CLASS (part);
 
        if (class->bind_dom_element != NULL)
-               class->bind_dom_element (part, element);
+               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 *
diff --git a/em-format/e-mail-part.h b/em-format/e-mail-part.h
index cb46042..72e17ad 100644
--- a/em-format/e-mail-part.h
+++ b/em-format/e-mail-part.h
@@ -19,7 +19,6 @@
 #define E_MAIL_PART_H
 
 #include <camel/camel.h>
-#include <webkit/webkitdom.h>
 
 #include <e-util/e-util.h>
 
@@ -87,7 +86,11 @@ struct _EMailPartClass {
        GObjectClass parent_class;
 
        void            (*bind_dom_element)     (EMailPart *part,
-                                                WebKitDOMElement *element);
+                                                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;
@@ -121,7 +124,11 @@ gboolean   e_mail_part_get_is_attachment   (EMailPart *part);
 void           e_mail_part_set_is_attachment   (EMailPart *part,
                                                 gboolean is_attachment);
 void           e_mail_part_bind_dom_element    (EMailPart *part,
-                                                WebKitDOMElement *element);
+                                                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/Makefile.am b/mail/Makefile.am
index 06aa259..acf784f 100644
--- a/mail/Makefile.am
+++ b/mail/Makefile.am
@@ -52,6 +52,7 @@ libevolution_mail_la_CPPFLAGS =                               \
        $(NULL)
 
 mailinclude_HEADERS =                                  \
+       e-cid-request.h                                 \
        e-http-request.h                                \
        e-mail.h                                        \
        e-mail-account-manager.h                        \
@@ -135,6 +136,7 @@ mailinclude_HEADERS =                                       \
        message-list.h
 
 libevolution_mail_la_SOURCES =                         \
+       e-cid-request.c                                 \
        e-http-request.c                                \
        e-mail-account-manager.c                        \
        e-mail-account-store.c                          \
diff --git a/mail/e-cid-request.c b/mail/e-cid-request.c
new file mode 100644
index 0000000..e7529f7
--- /dev/null
+++ b/mail/e-cid-request.c
@@ -0,0 +1,146 @@
+/*
+ * Copyright (C) 2016 Red Hat, Inc. (www.redhat.com)
+ *
+ * This library 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 library 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 Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <string.h>
+
+#include "e-mail-display.h"
+#include "e-cid-request.h"
+
+struct _ECidRequestPrivate {
+       gint dummy;
+};
+
+static void e_cid_request_content_request_init (EContentRequestInterface *iface);
+
+G_DEFINE_TYPE_WITH_CODE (ECidRequest, e_cid_request, G_TYPE_OBJECT,
+       G_IMPLEMENT_INTERFACE (E_TYPE_CONTENT_REQUEST, e_cid_request_content_request_init))
+
+static gboolean
+e_cid_request_can_process_uri (EContentRequest *request,
+                               const gchar *uri)
+{
+       g_return_val_if_fail (E_IS_CID_REQUEST (request), FALSE);
+       g_return_val_if_fail (uri != NULL, FALSE);
+
+       return g_ascii_strncasecmp (uri, "cid:", 4) == 0;
+}
+
+static gboolean
+e_cid_request_process_sync (EContentRequest *request,
+                           const gchar *uri,
+                           GObject *requester,
+                           GInputStream **out_stream,
+                           gint64 *out_stream_length,
+                           gchar **out_mime_type,
+                           GCancellable *cancellable,
+                           GError **error)
+{
+       EMailDisplay *display;
+       EMailPartList *part_list;
+       EMailPart *part;
+       GByteArray *byte_array;
+       CamelStream *output_stream;
+       CamelDataWrapper *dw;
+       CamelMimePart *mime_part;
+       gboolean success = FALSE;
+
+       g_return_val_if_fail (E_IS_CID_REQUEST (request), FALSE);
+       g_return_val_if_fail (uri != NULL, FALSE);
+
+       if (g_cancellable_set_error_if_cancelled (cancellable, error))
+               return FALSE;
+
+       if (!E_IS_MAIL_DISPLAY (requester))
+               return FALSE;
+
+       display = E_MAIL_DISPLAY (requester);
+
+       part_list = e_mail_display_get_part_list (display);
+       if (!part_list)
+               return FALSE;
+
+       part = e_mail_part_list_ref_part (part_list, uri);
+       if (!part)
+               return FALSE;
+
+       mime_part = e_mail_part_ref_mime_part (part);
+       dw = camel_medium_get_content (CAMEL_MEDIUM (mime_part));
+
+       g_return_val_if_fail (dw != NULL, FALSE);
+
+       byte_array = g_byte_array_new ();
+       output_stream = camel_stream_mem_new ();
+
+       /* We retain ownership of the byte array. */
+       camel_stream_mem_set_byte_array (CAMEL_STREAM_MEM (output_stream), byte_array);
+
+       if (camel_data_wrapper_decode_to_stream_sync (dw, output_stream, cancellable, error)) {
+               GBytes *bytes;
+               gchar *mime_type;
+
+               bytes = g_byte_array_free_to_bytes (byte_array);
+
+               success = TRUE;
+
+               *out_stream = g_memory_input_stream_new_from_bytes (bytes);
+               *out_stream_length = g_bytes_get_size (bytes);
+
+               mime_type = camel_data_wrapper_get_mime_type (dw);
+               if (mime_type && *mime_type)
+                       *out_mime_type = mime_type;
+               else {
+                       g_free (mime_type);
+                       *out_mime_type = g_strdup (e_mail_part_get_mime_type (part));
+               }
+
+               g_bytes_unref (bytes);
+       }
+
+       g_object_unref (mime_part);
+       g_object_unref (part);
+
+       return success;
+}
+
+static void
+e_cid_request_content_request_init (EContentRequestInterface *iface)
+{
+       iface->can_process_uri = e_cid_request_can_process_uri;
+       iface->process_sync = e_cid_request_process_sync;
+}
+
+static void
+e_cid_request_class_init (ECidRequestClass *class)
+{
+       g_type_class_add_private (class, sizeof (ECidRequestPrivate));
+}
+
+static void
+e_cid_request_init (ECidRequest *request)
+{
+       request->priv = G_TYPE_INSTANCE_GET_PRIVATE (request, E_TYPE_CID_REQUEST, ECidRequestPrivate);
+}
+
+EContentRequest *
+e_cid_request_new (void)
+{
+       return g_object_new (E_TYPE_CID_REQUEST, NULL);
+}
diff --git a/mail/e-cid-request.h b/mail/e-cid-request.h
new file mode 100644
index 0000000..0ed6e4b
--- /dev/null
+++ b/mail/e-cid-request.h
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2016 Red Hat, Inc. (www.redhat.com)
+ *
+ * This library 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 library 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 Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef E_CID_REQUEST_H
+#define E_CID_REQUEST_H
+
+#include <e-util/e-util.h>
+
+/* Standard GObject macros */
+#define E_TYPE_CID_REQUEST \
+       (e_cid_request_get_type ())
+#define E_CID_REQUEST(obj) \
+       (G_TYPE_CHECK_INSTANCE_CAST \
+       ((obj), E_TYPE_CID_REQUEST, ECidRequest))
+#define E_CID_REQUEST_CLASS(cls) \
+       (G_TYPE_CHECK_CLASS_CAST \
+       ((cls), E_TYPE_CID_REQUEST, ECidRequestClass))
+#define E_IS_CID_REQUEST(obj) \
+       (G_TYPE_CHECK_INSTANCE_TYPE \
+       ((obj), E_TYPE_CID_REQUEST))
+#define E_IS_CID_REQUEST_CLASS(cls) \
+       (G_TYPE_CHECK_CLASS_TYPE \
+       ((cls), E_TYPE_CID_REQUEST))
+#define E_CID_REQUEST_GET_CLASS(obj) \
+       (G_TYPE_INSTANCE_GET_CLASS \
+       ((obj), E_TYPE_CID_REQUEST, ECidRequestClass))
+
+G_BEGIN_DECLS
+
+typedef struct _ECidRequest ECidRequest;
+typedef struct _ECidRequestClass ECidRequestClass;
+typedef struct _ECidRequestPrivate ECidRequestPrivate;
+
+struct _ECidRequest {
+       GObject parent;
+       ECidRequestPrivate *priv;
+};
+
+struct _ECidRequestClass {
+       GObjectClass parent;
+};
+
+GType          e_cid_request_get_type          (void) G_GNUC_CONST;
+EContentRequest *
+               e_cid_request_new               (void);
+
+G_END_DECLS
+
+#endif /* E_CID_REQUEST_H */
diff --git a/mail/e-http-request.c b/mail/e-http-request.c
index 488732a..9e7711b 100644
--- a/mail/e-http-request.c
+++ b/mail/e-http-request.c
@@ -15,9 +15,10 @@
  *
  */
 
-#include "e-http-request.h"
-
+#ifdef HAVE_CONFIG_H
 #include <config.h>
+#endif
+
 #include <string.h>
 
 #define LIBSOUP_USE_UNSTABLE_REQUEST_API
@@ -25,7 +26,7 @@
 #include <libsoup/soup-requester.h>
 #include <libsoup/soup-request-http.h>
 #include <camel/camel.h>
-#include <webkit/webkit.h>
+#include <webkit2/webkit2.h>
 
 #include <e-util/e-util.h>
 #include <libemail-engine/libemail-engine.h>
@@ -35,19 +36,31 @@
 #include <shell/e-shell.h>
 
 #include "e-mail-ui-session.h"
+#include "e-http-request.h"
 
 #define d(x)
 
-#define E_HTTP_REQUEST_GET_PRIVATE(obj) \
-       (G_TYPE_INSTANCE_GET_PRIVATE \
-       ((obj), E_TYPE_HTTP_REQUEST, EHTTPRequestPrivate))
-
 struct _EHTTPRequestPrivate {
-       gchar *content_type;
-       gint content_length;
+       gint dummy;
 };
 
-G_DEFINE_TYPE (EHTTPRequest, e_http_request, SOUP_TYPE_REQUEST)
+static void e_http_request_content_request_init (EContentRequestInterface *iface);
+
+G_DEFINE_TYPE_WITH_CODE (EHTTPRequest, e_http_request, G_TYPE_OBJECT,
+       G_IMPLEMENT_INTERFACE (E_TYPE_CONTENT_REQUEST, e_http_request_content_request_init))
+
+static gboolean
+e_http_request_can_process_uri (EContentRequest *request,
+                               const gchar *uri)
+{
+       g_return_val_if_fail (E_IS_HTTP_REQUEST (request), FALSE);
+       g_return_val_if_fail (uri != NULL, FALSE);
+
+       return g_ascii_strncasecmp (uri, "evo-http:", 9) == 0 ||
+              g_ascii_strncasecmp (uri, "evo-https:", 10) == 0 ||
+              g_ascii_strncasecmp (uri, "http:", 5) == 0 ||
+              g_ascii_strncasecmp (uri, "https:", 6) == 0;
+}
 
 static gssize
 copy_stream_to_stream (GIOStream *file_io_stream,
@@ -162,16 +175,18 @@ http_request_cancelled_cb (GCancellable *cancellable,
        soup_session_abort (session);
 }
 
-static void
-handle_http_request (GSimpleAsyncResult *res,
-                     GObject *source_object,
-                     GCancellable *cancellable)
+static gboolean
+e_http_request_process_sync (EContentRequest *request,
+                            const gchar *uri,
+                            GObject *requester,
+                            GInputStream **out_stream,
+                            gint64 *out_stream_length,
+                            gchar **out_mime_type,
+                            GCancellable *cancellable,
+                            GError **error)
 {
-       EHTTPRequestPrivate *priv;
        SoupURI *soup_uri;
-       SoupRequest *soup_request;
-       SoupSession *soup_session;
-       gchar *evo_uri, *uri;
+       gchar *evo_uri, *use_uri;
        gchar *mail_uri = NULL;
        GInputStream *stream;
        gboolean force_load_images = FALSE;
@@ -183,15 +198,16 @@ handle_http_request (GSimpleAsyncResult *res,
        CamelDataCache *cache;
        GIOStream *cache_stream;
        gint uri_len;
+       gboolean success = FALSE;
 
-       if (g_cancellable_is_cancelled (cancellable))
-               return;
+       g_return_val_if_fail (E_IS_HTTP_REQUEST (request), FALSE);
+       g_return_val_if_fail (uri != NULL, FALSE);
 
-       priv = E_HTTP_REQUEST_GET_PRIVATE (source_object);
+       if (g_cancellable_set_error_if_cancelled (cancellable, error))
+               return FALSE;
 
-       soup_request = SOUP_REQUEST (source_object);
-       soup_session = soup_request_get_session (soup_request);
-       soup_uri = soup_request_get_uri (soup_request);
+       soup_uri = soup_uri_new (uri);
+       g_return_val_if_fail (soup_uri != NULL, FALSE);
 
        /* Remove the __evo-mail query */
        soup_query = soup_uri_get_query (soup_uri);
@@ -224,24 +240,31 @@ handle_http_request (GSimpleAsyncResult *res,
 
        /* Remove the "evo-" prefix from scheme */
        uri_len = (evo_uri != NULL) ? strlen (evo_uri) : 0;
-       uri = NULL;
+       use_uri = NULL;
        if (evo_uri != NULL && (uri_len > 5)) {
+               gint inc = 0;
+
+               if (g_str_has_prefix (evo_uri, "evo-"))
+                       inc = 4;
 
                /* Remove trailing "?" if there is no URI query */
                if (evo_uri[uri_len - 1] == '?') {
-                       uri = g_strndup (evo_uri + 4, uri_len - 5);
+                       use_uri = g_strndup (evo_uri + inc, uri_len - 1 - inc);
                } else {
-                       uri = g_strdup (evo_uri + 4);
+                       use_uri = g_strdup (evo_uri + inc);
                }
-               g_free (evo_uri);
        }
 
-       g_return_if_fail (uri && *uri);
+       g_free (evo_uri);
+
+       g_return_val_if_fail (use_uri && *use_uri, FALSE);
+
+       *out_stream_length = -1;
 
        /* Use MD5 hash of the URI as a filname of the resourec cache file.
         * We were previously using the URI as a filename but the URI is
         * sometimes too long for a filename. */
-       uri_md5 = g_compute_checksum_for_string (G_CHECKSUM_MD5, uri, -1);
+       uri_md5 = g_compute_checksum_for_string (G_CHECKSUM_MD5, use_uri, -1);
 
        /* Open Evolution's cache */
        user_cache_dir = e_get_user_cache_dir ();
@@ -251,27 +274,23 @@ handle_http_request (GSimpleAsyncResult *res,
                camel_data_cache_set_expire_access (cache, 2 * 60 * 60);
 
                cache_stream = camel_data_cache_get (cache, "http", uri_md5, NULL);
-       } else {
+       } else
                cache_stream = NULL;
-       }
 
-       /* Found item in cache! */
        if (cache_stream != NULL) {
                gssize len;
 
                stream = g_memory_input_stream_new ();
 
-               len = copy_stream_to_stream (
-                       cache_stream,
-                       G_MEMORY_INPUT_STREAM (stream), cancellable);
-               priv->content_length = len;
+               len = copy_stream_to_stream (cache_stream, G_MEMORY_INPUT_STREAM (stream), cancellable);
+               *out_stream_length = len;
 
                g_object_unref (cache_stream);
 
                /* When succesfully read some data from cache then
                 * get mimetype and return the stream to WebKit.
                 * Otherwise try to fetch the resource again from the network. */
-               if ((len != -1) && (priv->content_length > 0)) {
+               if (len != -1 && *out_stream_length > 0) {
                        GFile *file;
                        GFileInfo *info;
                        gchar *path;
@@ -284,13 +303,12 @@ handle_http_request (GSimpleAsyncResult *res,
                                0, cancellable, NULL);
 
                        if (info) {
-                               priv->content_type = g_strdup (
-                                       g_file_info_get_content_type (info));
+                               *out_mime_type = g_strdup (g_file_info_get_content_type (info));
 
                                d (
                                        printf ("'%s' found in cache (%d bytes, %s)\n",
-                                       uri, priv->content_length,
-                                       priv->content_type));
+                                       use_uri, (gint) *out_stream_length,
+                                       *out_mime_type));
                        }
 
                        g_clear_object (&info);
@@ -298,12 +316,12 @@ handle_http_request (GSimpleAsyncResult *res,
                        g_free (path);
 
                        /* Set result and quit the thread */
-                       g_simple_async_result_set_op_res_gpointer (
-                               res, stream, g_object_unref);
+                       *out_stream = stream;
+                       success = TRUE;
 
                        goto cleanup;
                } else {
-                       d (printf ("Failed to load '%s' from cache.\n", uri));
+                       d (printf ("Failed to load '%s' from cache.\n", use_uri));
                        g_object_unref (stream);
                }
        }
@@ -337,7 +355,6 @@ handle_http_request (GSimpleAsyncResult *res,
                        CamelInternetAddress *addr;
                        CamelMimeMessage *message;
                        gboolean known_address = FALSE;
-                       GError *error = NULL;
 
                        shell_backend =
                                e_shell_get_backend_by_name (shell, "mail");
@@ -347,14 +364,13 @@ handle_http_request (GSimpleAsyncResult *res,
                        message = e_mail_part_list_get_message (part_list);
                        addr = camel_mime_message_get_from (message);
 
-                       e_mail_ui_session_check_known_address_sync (
+                       if (!e_mail_ui_session_check_known_address_sync (
                                E_MAIL_UI_SESSION (session),
                                addr, FALSE, cancellable,
-                               &known_address, &error);
-
-                       if (error != NULL) {
-                               g_warning ("%s: %s", G_STRFUNC, error->message);
-                               g_error_free (error);
+                               &known_address, error)) {
+                               g_object_unref (part_list);
+                               g_free (decoded_uri);
+                               goto cleanup;
                        }
 
                        if (known_address)
@@ -368,20 +384,19 @@ handle_http_request (GSimpleAsyncResult *res,
 
        if ((image_policy == E_IMAGE_LOADING_POLICY_ALWAYS) ||
            force_load_images) {
-
+               ESource *proxy_source;
                SoupSession *temp_session;
                SoupMessage *message;
                GIOStream *cache_stream;
-               GError *error;
                GMainContext *context;
                gulong cancelled_id = 0;
 
-               if (g_cancellable_is_cancelled (cancellable))
+               if (g_cancellable_set_error_if_cancelled (cancellable, error))
                        goto cleanup;
 
-               message = soup_message_new (SOUP_METHOD_GET, uri);
+               message = soup_message_new (SOUP_METHOD_GET, use_uri);
                if (!message) {
-                       g_debug ("%s: Skipping invalid URI '%s'", G_STRFUNC, uri);
+                       g_debug ("%s: Skipping invalid URI '%s'", G_STRFUNC, use_uri);
                        goto cleanup;
                }
 
@@ -391,10 +406,15 @@ handle_http_request (GSimpleAsyncResult *res,
                temp_session = soup_session_new_with_options (
                        SOUP_SESSION_TIMEOUT, 90, NULL);
 
-               e_binding_bind_property (
-                       soup_session, "proxy-resolver",
-                       temp_session, "proxy-resolver",
-                       G_BINDING_SYNC_CREATE);
+               proxy_source = e_source_registry_ref_builtin_proxy (e_shell_get_registry (shell));
+
+               g_object_set (
+                       temp_session,
+                       SOUP_SESSION_PROXY_RESOLVER,
+                       G_PROXY_RESOLVER (proxy_source),
+                       NULL);
+
+               g_object_unref (proxy_source);
 
                soup_message_headers_append (
                        message->request_headers,
@@ -409,65 +429,68 @@ handle_http_request (GSimpleAsyncResult *res,
                        g_cancellable_disconnect (cancellable, cancelled_id);
 
                if (!SOUP_STATUS_IS_SUCCESSFUL (message->status_code)) {
-                       g_debug ("Failed to request %s (code %d)", uri, message->status_code);
+                       g_debug ("Failed to request %s (code %d)", use_uri, message->status_code);
                        g_object_unref (message);
                        g_object_unref (temp_session);
                        g_main_context_unref (context);
                        goto cleanup;
                }
 
-               /* Write the response body to cache */
-               error = NULL;
                if (cache) {
+                       GError *local_error = NULL;
+
                        cache_stream = camel_data_cache_add (
-                               cache, "http", uri_md5, &error);
-                       if (error != NULL) {
+                               cache, "http", uri_md5, &local_error);
+                       if (local_error) {
                                g_warning (
                                        "Failed to create cache file for '%s': %s",
-                                       uri, error->message);
-                               g_clear_error (&error);
+                                       uri, local_error->message);
+                               g_clear_error (&local_error);
                        } else {
                                GOutputStream *output_stream;
 
                                output_stream =
                                        g_io_stream_get_output_stream (cache_stream);
 
-                               g_output_stream_write_all (
+                               success = g_output_stream_write_all (
                                        output_stream,
                                        message->response_body->data,
                                        message->response_body->length,
-                                       NULL, cancellable, &error);
+                                       NULL, cancellable, &local_error);
 
                                g_io_stream_close (cache_stream, NULL, NULL);
                                g_object_unref (cache_stream);
 
-                               if (error != NULL) {
-                                       if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
+                               if (local_error != NULL) {
+                                       if (!g_error_matches (local_error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
                                                g_warning (
                                                        "Failed to write data to cache stream: %s",
-                                                       error->message);
-                                       g_clear_error (&error);
+                                                       local_error->message);
+                                       g_clear_error (&local_error);
                                        g_object_unref (message);
                                        g_object_unref (temp_session);
                                        g_main_context_unref (context);
                                        goto cleanup;
                                }
+
+                               if (success) {
+                                       /* Send the response body to WebKit */
+                                       stream = g_memory_input_stream_new_from_data (
+                                               g_memdup (
+                                                       message->response_body->data,
+                                                       message->response_body->length),
+                                               message->response_body->length,
+                                               (GDestroyNotify) g_free);
+
+                                       *out_stream = stream;
+                                       *out_stream_length = message->response_body->length;
+                                       *out_mime_type = g_strdup (
+                                               soup_message_headers_get_content_type (
+                                                       message->response_headers, NULL));
+                               }
                        }
                }
 
-               /* Send the response body to WebKit */
-               stream = g_memory_input_stream_new_from_data (
-                       g_memdup (
-                               message->response_body->data,
-                               message->response_body->length),
-                       message->response_body->length,
-                       (GDestroyNotify) g_free);
-
-               priv->content_length = message->response_body->length;
-               priv->content_type = g_strdup (
-                       soup_message_headers_get_content_type (
-                               message->response_headers, NULL));
-
                g_object_unref (message);
                g_object_unref (temp_session);
                g_main_context_unref (context);
@@ -476,150 +499,42 @@ handle_http_request (GSimpleAsyncResult *res,
                        "Content-Type: %s\n"
                        "Content-Length: %d bytes\n"
                        "URI MD5: %s:\n",
-                       uri, priv->content_type,
-                       priv->content_length, uri_md5));
-
-               g_simple_async_result_set_op_res_gpointer (res, stream, g_object_unref);
-
-               goto cleanup;
+                       use_uri, *out_mime_type ? *out_mime_type : "[null]",
+                       (gint) *out_stream_length, uri_md5));
        }
 
-cleanup:
+ cleanup:
        g_clear_object (&cache);
 
-       g_free (uri);
+       g_free (use_uri);
        g_free (uri_md5);
        g_free (mail_uri);
-}
-
-static void
-http_request_finalize (GObject *object)
-{
-       EHTTPRequest *request = E_HTTP_REQUEST (object);
-
-       if (request->priv->content_type) {
-               g_free (request->priv->content_type);
-               request->priv->content_type = NULL;
-       }
-
-       G_OBJECT_CLASS (e_http_request_parent_class)->finalize (object);
-}
+       soup_uri_free (soup_uri);
 
-static gboolean
-http_request_check_uri (SoupRequest *request,
-                        SoupURI *uri,
-                        GError **error)
-{
-       return ((strcmp (uri->scheme, "evo-http") == 0) ||
-               (strcmp (uri->scheme, "evo-https") == 0));
+       return success;
 }
 
 static void
-http_request_send_async (SoupRequest *request,
-                         GCancellable *cancellable,
-                         GAsyncReadyCallback callback,
-                         gpointer user_data)
-{
-       GSimpleAsyncResult *simple;
-
-       d ({
-               const gchar *soup_query;
-               SoupURI *uri;
-
-               uri = soup_request_get_uri (request);
-               soup_query = soup_uri_get_query (uri);
-
-               if (soup_query) {
-                       gchar *uri_str;
-                       GHashTable *query;
-
-                       query = soup_form_decode (soup_uri_get_query (uri));
-                       uri_str = soup_uri_to_string (uri, FALSE);
-                       printf ("received request for %s\n", uri_str);
-                       g_free (uri_str);
-                       g_hash_table_destroy (query);
-               }
-       });
-
-       simple = g_simple_async_result_new (
-               G_OBJECT (request), callback,
-               user_data, http_request_send_async);
-
-       g_simple_async_result_set_check_cancellable (simple, cancellable);
-
-       e_util_run_simple_async_result_in_thread (
-               simple, handle_http_request,
-               cancellable);
-
-       g_object_unref (simple);
-}
-
-static GInputStream *
-http_request_send_finish (SoupRequest *request,
-                          GAsyncResult *result,
-                          GError **error)
-{
-       GInputStream *stream;
-
-       stream = g_simple_async_result_get_op_res_gpointer (
-               G_SIMPLE_ASYNC_RESULT (result));
-
-       /* Reset the stream before passing it back to webkit */
-       if (stream && G_IS_SEEKABLE (stream))
-               g_seekable_seek (G_SEEKABLE (stream), 0, G_SEEK_SET, NULL, NULL);
-
-       if (!stream) /* We must always return something */
-               stream = g_memory_input_stream_new ();
-       else
-               g_object_ref (stream);
-
-       return stream;
-}
-
-static goffset
-http_request_get_content_length (SoupRequest *request)
-{
-       EHTTPRequest *efr = E_HTTP_REQUEST (request);
-
-       d (printf ("Content-Length: %d bytes\n", efr->priv->content_length));
-       return efr->priv->content_length;
-}
-
-static const gchar *
-http_request_get_content_type (SoupRequest *request)
+e_http_request_content_request_init (EContentRequestInterface *iface)
 {
-       EHTTPRequest *efr = E_HTTP_REQUEST (request);
-
-       d (printf ("Content-Type: %s\n", efr->priv->content_type));
-
-       return efr->priv->content_type;
+       iface->can_process_uri = e_http_request_can_process_uri;
+       iface->process_sync = e_http_request_process_sync;
 }
 
-static const gchar *data_schemes[] = { "evo-http", "evo-https", NULL };
-
 static void
 e_http_request_class_init (EHTTPRequestClass *class)
 {
-       GObjectClass *object_class;
-       SoupRequestClass *request_class;
-
        g_type_class_add_private (class, sizeof (EHTTPRequestPrivate));
-
-       object_class = G_OBJECT_CLASS (class);
-       object_class->finalize = http_request_finalize;
-
-       request_class = SOUP_REQUEST_CLASS (class);
-       request_class->schemes = data_schemes;
-       request_class->send_async = http_request_send_async;
-       request_class->send_finish = http_request_send_finish;
-       request_class->get_content_type = http_request_get_content_type;
-       request_class->get_content_length = http_request_get_content_length;
-       request_class->check_uri = http_request_check_uri;
 }
 
 static void
 e_http_request_init (EHTTPRequest *request)
 {
-       request->priv = E_HTTP_REQUEST_GET_PRIVATE (request);
+       request->priv = G_TYPE_INSTANCE_GET_PRIVATE (request, E_TYPE_HTTP_REQUEST, EHTTPRequestPrivate);
 }
 
+EContentRequest *
+e_http_request_new (void)
+{
+       return g_object_new (E_TYPE_HTTP_REQUEST, NULL);
+}
diff --git a/mail/e-http-request.h b/mail/e-http-request.h
index de4cb23..bdbf009 100644
--- a/mail/e-http-request.h
+++ b/mail/e-http-request.h
@@ -18,10 +18,7 @@
 #ifndef E_HTTP_REQUEST_H
 #define E_HTTP_REQUEST_H
 
-#define LIBSOUP_USE_UNSTABLE_REQUEST_API
-
-#include <libsoup/soup.h>
-#include <libsoup/soup-request.h>
+#include <e-util/e-util.h>
 
 /* Standard GObject macros */
 #define E_TYPE_HTTP_REQUEST \
@@ -49,15 +46,18 @@ typedef struct _EHTTPRequestClass EHTTPRequestClass;
 typedef struct _EHTTPRequestPrivate EHTTPRequestPrivate;
 
 struct _EHTTPRequest {
-       SoupRequest parent;
+       GObject parent;
        EHTTPRequestPrivate *priv;
 };
 
 struct _EHTTPRequestClass {
-       SoupRequestClass parent;
+       GObjectClass parent;
 };
 
 GType          e_http_request_get_type         (void) G_GNUC_CONST;
+EContentRequest *
+               e_http_request_new              (void);
+
 
 G_END_DECLS
 
diff --git a/mail/e-mail-browser.c b/mail/e-mail-browser.c
index 1130c9c..9293803 100644
--- a/mail/e-mail-browser.c
+++ b/mail/e-mail-browser.c
@@ -607,6 +607,7 @@ mail_browser_constructed (GObject *object)
        EShellBackend *shell_backend;
        EShell *shell;
        EFocusTracker *focus_tracker;
+       EAttachmentStore *attachment_store;
        GtkAccelGroup *accel_group;
        GtkActionGroup *action_group;
        GtkAction *action;
@@ -755,6 +756,18 @@ mail_browser_constructed (GObject *object)
                browser->priv->preview_pane,
                TRUE, TRUE, 0);
 
+       attachment_store = e_mail_display_get_attachment_store (E_MAIL_DISPLAY (display));
+       widget = GTK_WIDGET (e_mail_display_get_attachment_view (E_MAIL_DISPLAY (display)));
+       gtk_box_pack_start (GTK_BOX (container), widget, FALSE, FALSE, 0);
+       gtk_widget_show (widget);
+
+       e_binding_bind_property_full (
+               attachment_store, "num-attachments",
+               widget, "visible",
+               G_BINDING_SYNC_CREATE,
+               e_attachment_store_transform_num_attachments_to_visible_boolean,
+               NULL, NULL, NULL);
+
        id = "org.gnome.evolution.mail.browser";
        e_plugin_ui_register_manager (ui_manager, id, object);
        e_plugin_ui_enable_manager (ui_manager, id);
@@ -766,47 +779,9 @@ static gboolean
 mail_browser_key_press_event (GtkWidget *widget,
                               GdkEventKey *event)
 {
-       EMailDisplay *mail_display;
-
-       g_return_val_if_fail (E_IS_MAIL_BROWSER (widget), FALSE);
-
-       mail_display = e_mail_reader_get_mail_display (E_MAIL_READER (widget));
-
-       switch (event->keyval) {
-               case GDK_KEY_Escape:
-                       e_mail_browser_close (E_MAIL_BROWSER (widget));
-                       return TRUE;
-
-               case GDK_KEY_Home:
-               case GDK_KEY_Left:
-               case GDK_KEY_Up:
-               case GDK_KEY_Right:
-               case GDK_KEY_Down:
-               case GDK_KEY_Next:
-               case GDK_KEY_End:
-               case GDK_KEY_Begin:
-                       /* If Caret mode is enabled don't try to process these keys */
-                       if (e_web_view_get_caret_mode (E_WEB_VIEW (mail_display)))
-                               break;
-               case GDK_KEY_Prior:
-                       if (!e_mail_display_needs_key (mail_display, FALSE) &&
-                           webkit_web_view_get_main_frame (WEBKIT_WEB_VIEW (mail_display)) !=
-                           webkit_web_view_get_focused_frame (WEBKIT_WEB_VIEW (mail_display))) {
-                               WebKitDOMDocument *document;
-                               WebKitDOMDOMWindow *window;
-
-                               document = webkit_web_view_get_dom_document (WEBKIT_WEB_VIEW (mail_display));
-                               window = webkit_dom_document_get_default_view (document);
-
-                               /* Workaround WebKit bug for key navigation, when inner IFRAME is focused.
-                                * EMailView's inner IFRAMEs have disabled scrolling, but WebKit doesn't post
-                                * key navigation events to parent's frame, thus the view doesn't scroll.
-                                * This is a poor workaround for this issue, the main frame is focused,
-                                * which has scrolling enabled.
-                               */
-                               webkit_dom_dom_window_focus (window);
-                       }
-                       break;
+       if (event->keyval == GDK_KEY_Escape) {
+               e_mail_browser_close (E_MAIL_BROWSER (widget));
+               return TRUE;
        }
 
        /* Chain up to parent's key_press_event() method. */
diff --git a/mail/e-mail-config-identity-page.c b/mail/e-mail-config-identity-page.c
index 8b1a8fe..5baa5f5 100644
--- a/mail/e-mail-config-identity-page.c
+++ b/mail/e-mail-config-identity-page.c
@@ -80,17 +80,35 @@ mail_config_identity_page_is_email (const gchar *email_address)
 }
 
 static void
+mail_config_identity_page_signature_editor_created_cb (GObject *source_object,
+                                                      GAsyncResult *result,
+                                                      gpointer user_data)
+{
+       GtkWidget *editor;
+       GError *error = NULL;
+
+       g_return_if_fail (result != NULL);
+
+       editor = e_mail_signature_editor_new_finish (result, &error);
+       if (error) {
+               g_warning ("%s: Failed to create signature editor: %s", G_STRFUNC, error->message);
+               g_clear_error (&error);
+       } else {
+               gtk_window_set_position (GTK_WINDOW (editor), GTK_WIN_POS_CENTER);
+               gtk_widget_show (editor);
+       }
+}
+
+static void
 mail_config_identity_page_add_signature_cb (GtkButton *button,
                                             EMailConfigIdentityPage *page)
 {
        ESourceRegistry *registry;
-       GtkWidget *editor;
 
        registry = e_mail_config_identity_page_get_registry (page);
 
-       editor = e_mail_signature_editor_new (registry, NULL);
-       gtk_window_set_position (GTK_WINDOW (editor), GTK_WIN_POS_CENTER);
-       gtk_widget_show (editor);
+       e_mail_signature_editor_new (registry, NULL,
+               mail_config_identity_page_signature_editor_created_cb, NULL);
 }
 
 static void
diff --git a/mail/e-mail-display-popup-extension.c b/mail/e-mail-display-popup-extension.c
index e95dc3e..2f9e919 100644
--- a/mail/e-mail-display-popup-extension.c
+++ b/mail/e-mail-display-popup-extension.c
@@ -33,14 +33,14 @@ e_mail_display_popup_extension_default_init (EMailDisplayPopupExtensionInterface
  * e_mail_display_popup_extension_update_actions:
  *
  * @extension: An object derived from #EMailDisplayPopupExtension
- * @context: A #WebKitHitTestResult describing context of the popup menu
+ * @popup_document_uri: Document URI on top of which the popup menu had been invoked
  *
  * When #EMailDisplay is about to display a popup menu, it calls this function
  * on every extension so that they can add their items to the menu.
  */
 void
 e_mail_display_popup_extension_update_actions (EMailDisplayPopupExtension *extension,
-                                               WebKitHitTestResult *context)
+                                              const gchar *popup_document_uri)
 {
        EMailDisplayPopupExtensionInterface *iface;
 
@@ -49,5 +49,5 @@ e_mail_display_popup_extension_update_actions (EMailDisplayPopupExtension *exten
        iface = E_MAIL_DISPLAY_POPUP_EXTENSION_GET_INTERFACE (extension);
        g_return_if_fail (iface->update_actions != NULL);
 
-       iface->update_actions (extension, context);
+       iface->update_actions (extension, popup_document_uri);
 }
diff --git a/mail/e-mail-display-popup-extension.h b/mail/e-mail-display-popup-extension.h
index 67c1374..bedd23e 100644
--- a/mail/e-mail-display-popup-extension.h
+++ b/mail/e-mail-display-popup-extension.h
@@ -19,7 +19,6 @@
 #define E_MAIL_DISPLAY_POPUP_EXTENSION_H
 
 #include <glib-object.h>
-#include <webkit/webkit.h>
 
 /* Standard GObject macros */
 #define E_TYPE_MAIL_DISPLAY_POPUP_EXTENSION \
@@ -49,14 +48,14 @@ struct _EMailDisplayPopupExtensionInterface {
        GTypeInterface parent_interface;
 
        void    (*update_actions)               (EMailDisplayPopupExtension *extension,
-                                                WebKitHitTestResult *context);
+                                                const gchar *popup_document_uri);
 };
 
 GType          e_mail_display_popup_extension_get_type (void);
 
 void           e_mail_display_popup_extension_update_actions
                                                        (EMailDisplayPopupExtension *extension,
-                                                        WebKitHitTestResult *context);
+                                                        const gchar *popup_document_uri);
 
 G_END_DECLS
 
diff --git a/mail/e-mail-display.c b/mail/e-mail-display.c
index 1e475ca..eed7d74 100644
--- a/mail/e-mail-display.c
+++ b/mail/e-mail-display.c
@@ -34,20 +34,36 @@
 #include <em-format/e-mail-part-attachment.h>
 #include <em-format/e-mail-part-utils.h>
 
+#include "e-cid-request.h"
 #include "e-http-request.h"
 #include "e-mail-display-popup-extension.h"
 #include "e-mail-notes.h"
 #include "e-mail-request.h"
+#include "e-mail-ui-session.h"
 #include "em-composer-utils.h"
 #include "em-utils.h"
 
+#include <web-extensions/e-web-extension-names.h>
+
 #define d(x)
 
 #define E_MAIL_DISPLAY_GET_PRIVATE(obj) \
        (G_TYPE_INSTANCE_GET_PRIVATE \
        ((obj), E_TYPE_MAIL_DISPLAY, EMailDisplayPrivate))
 
+typedef enum {
+       E_ATTACHMENT_FLAG_VISIBLE       = (1 << 0),
+       E_ATTACHMENT_FLAG_ZOOMED_TO_100 = (1 << 1)
+} EAttachmentFlags;
+
 struct _EMailDisplayPrivate {
+       EAttachmentStore *attachment_store;
+       EAttachmentView *attachment_view;
+       GHashTable *attachment_flags; /* EAttachment * ~> guint bit-or of EAttachmentFlags */
+       guint attachment_inline_ui_id;
+
+       GtkActionGroup *attachment_inline_group;
+
        EMailPartList *part_list;
        EMailFormatterMode mode;
        EMailFormatter *formatter;
@@ -58,8 +74,6 @@ struct _EMailDisplayPrivate {
 
        GSettings *settings;
 
-       GHashTable *widgets;
-
        guint scheduled_reload;
 
        GHashTable *old_settings;
@@ -67,10 +81,16 @@ struct _EMailDisplayPrivate {
        GMutex remote_content_lock;
        EMailRemoteContent *remote_content;
        GHashTable *skipped_remote_content_sites;
+
+       guint web_extension_headers_collapsed_signal_id;
+
+       GtkAllocation attachment_popup_position;
 };
 
 enum {
        PROP_0,
+       PROP_ATTACHMENT_STORE,
+       PROP_ATTACHMENT_VIEW,
        PROP_FORMATTER,
        PROP_HEADERS_COLLAPSABLE,
        PROP_HEADERS_COLLAPSED,
@@ -142,6 +162,21 @@ G_DEFINE_TYPE (
        e_mail_display,
        E_TYPE_WEB_VIEW);
 
+static const gchar *attachment_popup_ui =
+"<ui>"
+"  <popup name='context'>"
+"    <placeholder name='inline-actions'>"
+"      <menuitem action='zoom-to-100'/>"
+"      <menuitem action='zoom-to-window'/>"
+"      <menuitem action='show'/>"
+"      <menuitem action='show-all'/>"
+"      <separator/>"
+"      <menuitem action='hide'/>"
+"      <menuitem action='hide-all'/>"
+"    </placeholder>"
+"  </popup>"
+"</ui>";
+
 static void
 e_mail_display_claim_skipped_uri (EMailDisplay *mail_display,
                                  const gchar *uri)
@@ -259,37 +294,6 @@ formatter_image_loading_policy_changed_cb (GObject *object,
                e_mail_display_reload (display);
 }
 
-static gboolean
-mail_display_image_exists_in_cache (const gchar *image_uri)
-{
-       gchar *filename;
-       gchar *hash;
-       gboolean exists = FALSE;
-
-       if (!emd_global_http_cache)
-               return FALSE;
-
-       hash = g_compute_checksum_for_string (G_CHECKSUM_MD5, image_uri, -1);
-       filename = camel_data_cache_get_filename (
-               emd_global_http_cache, "http", hash);
-
-       if (filename != NULL) {
-               struct stat st;
-
-               exists = g_file_test (filename, G_FILE_TEST_EXISTS);
-               if (exists && g_stat (filename, &st) == 0) {
-                       exists = st.st_size != 0;
-               } else {
-                       exists = FALSE;
-               }
-               g_free (filename);
-       }
-
-       g_free (hash);
-
-       return exists;
-}
-
 static void
 mail_display_update_formatter_colors (EMailDisplay *display)
 {
@@ -303,29 +307,6 @@ mail_display_update_formatter_colors (EMailDisplay *display)
                e_mail_formatter_update_style (formatter, state_flags);
 }
 
-static void
-mail_display_plugin_widget_disconnect_children (GtkWidget *widget,
-                                                gpointer mail_display)
-{
-       g_signal_handlers_disconnect_by_data (widget, mail_display);
-}
-
-static void
-mail_display_plugin_widget_disconnect (gpointer widget_uri,
-                                       gpointer widget,
-                                       gpointer mail_display)
-{
-       if (E_IS_ATTACHMENT_BAR (widget))
-               g_signal_handlers_disconnect_by_data (widget, mail_display);
-       else if (E_IS_ATTACHMENT_BUTTON (widget))
-               g_signal_handlers_disconnect_by_data (widget, mail_display);
-       else if (GTK_IS_CONTAINER (widget))
-               gtk_container_foreach (
-                       widget,
-                       mail_display_plugin_widget_disconnect_children,
-                       mail_display);
-}
-
 static gboolean
 mail_display_process_mailto (EWebView *web_view,
                              const gchar *mailto_uri,
@@ -355,14 +336,28 @@ mail_display_process_mailto (EWebView *web_view,
 }
 
 static gboolean
-mail_display_link_clicked (WebKitWebView *web_view,
-                           WebKitWebFrame *frame,
-                           WebKitNetworkRequest *request,
-                           WebKitWebNavigationAction *navigation_action,
-                           WebKitWebPolicyDecision *policy_decision,
-                           gpointer user_data)
+decide_policy_cb (WebKitWebView *web_view,
+                  WebKitPolicyDecision *decision,
+                  WebKitPolicyDecisionType type)
 {
-       const gchar *uri = webkit_network_request_get_uri (request);
+       WebKitNavigationPolicyDecision *navigation_decision;
+       WebKitNavigationAction *navigation_action;
+       WebKitURIRequest *request;
+       const gchar *uri;
+
+       if (type != WEBKIT_POLICY_DECISION_TYPE_NAVIGATION_ACTION)
+               return FALSE;
+
+       navigation_decision = WEBKIT_NAVIGATION_POLICY_DECISION (decision);
+       navigation_action = webkit_navigation_policy_decision_get_navigation_action (navigation_decision);
+       request = webkit_navigation_action_get_request (navigation_action);
+
+       uri = webkit_uri_request_get_uri (request);
+
+       if (!uri || !*uri) {
+               webkit_policy_decision_ignore (decision);
+               return TRUE;
+       }
 
        if (g_str_has_prefix (uri, "file://")) {
                gchar *filename;
@@ -370,8 +365,9 @@ mail_display_link_clicked (WebKitWebView *web_view,
                filename = g_filename_from_uri (uri, NULL, NULL);
 
                if (g_file_test (filename, G_FILE_TEST_IS_DIR)) {
-                       webkit_web_policy_decision_ignore (policy_decision);
-                       webkit_network_request_set_uri (request, "about:blank");
+                       webkit_policy_decision_ignore (decision);
+                       /* FIXME WK2 Not sure if the request will be changed there */
+                       webkit_uri_request_set_uri (request, "about:blank");
                        g_free (filename);
                        return TRUE;
                }
@@ -381,17 +377,17 @@ mail_display_link_clicked (WebKitWebView *web_view,
 
        if (mail_display_process_mailto (E_WEB_VIEW (web_view), uri, NULL)) {
                /* do nothing, function handled the "mailto:"; uri already */
-               webkit_web_policy_decision_ignore (policy_decision);
+               webkit_policy_decision_ignore (decision);
                return TRUE;
 
        } else if (g_ascii_strncasecmp (uri, "thismessage:", 12) == 0) {
                /* ignore */
-               webkit_web_policy_decision_ignore (policy_decision);
+               webkit_policy_decision_ignore (decision);
                return TRUE;
 
        } else if (g_ascii_strncasecmp (uri, "cid:", 4) == 0) {
                /* ignore */
-               webkit_web_policy_decision_ignore (policy_decision);
+               webkit_policy_decision_ignore (decision);
                return TRUE;
 
        }
@@ -401,942 +397,808 @@ mail_display_link_clicked (WebKitWebView *web_view,
 }
 
 static void
-mail_display_resource_requested (WebKitWebView *web_view,
-                                 WebKitWebFrame *frame,
-                                 WebKitWebResource *resource,
-                                 WebKitNetworkRequest *request,
-                                 WebKitNetworkResponse *response,
-                                 gpointer user_data)
+add_color_css_rule_for_web_view (EWebView *view,
+                                 const gchar *color_name,
+                                 const gchar *color_value)
 {
-       const gchar *original_uri;
-
-       original_uri = webkit_network_request_get_uri (request);
+       gchar *selector;
+       gchar *style;
 
-       if (original_uri != NULL) {
-               gchar *redirected_uri;
+       selector = g_strconcat (".-e-mail-formatter-", color_name, NULL);
 
-               redirected_uri = e_web_view_redirect_uri (
-                       E_WEB_VIEW (web_view), original_uri);
+       if (g_strstr_len (color_name, -1, "header")) {
+               style = g_strconcat (
+                       "color: ", color_value, " !important;", NULL);
+       } else if (g_strstr_len (color_name, -1, "frame")) {
+               style = g_strconcat (
+                       "border-color: ", color_value, NULL);
+       } else {
+               style = g_strconcat (
+                       "background-color: ", color_value, " !important;", NULL);
+       }
 
-               webkit_network_request_set_uri (request, redirected_uri);
+       e_web_view_add_css_rule_into_style_sheet (
+               view,
+               "-e-mail-formatter-style-sheet",
+               selector,
+               style);
 
-               g_free (redirected_uri);
-       }
+       g_free (style);
+       g_free (selector);
 }
 
-static WebKitDOMElement *
-find_element_by_id (WebKitDOMDocument *document,
-                    const gchar *id)
+static void
+initialize_web_view_colors (EMailDisplay *display)
 {
-       WebKitDOMNodeList *frames;
-       WebKitDOMElement *element = NULL;
-       gulong ii, length;
-
-       if (!WEBKIT_DOM_IS_DOCUMENT (document))
-               return NULL;
+       EMailFormatter *formatter;
+       GtkTextDirection direction;
+       const gchar *style;
+       gint ii;
 
-       /* Try to look up the element in this DOM document */
-       element = webkit_dom_document_get_element_by_id (document, id);
-       if (element != NULL)
-               return element;
+       const gchar *color_names[] = {
+               "body-color",
+               "citation-color",
+               "frame-color",
+               "header-color",
+               NULL
+       };
 
-       /* If the element is not here then recursively scan all frames */
-       frames = webkit_dom_document_get_elements_by_tag_name (
-               document, "iframe");
-       length = webkit_dom_node_list_get_length (frames);
-       for (ii = 0; ii < length; ii++) {
-               WebKitDOMHTMLIFrameElement *iframe;
-               WebKitDOMDocument *frame_doc;
+       formatter = e_mail_display_get_formatter (display);
 
-               iframe = WEBKIT_DOM_HTML_IFRAME_ELEMENT (
-                       webkit_dom_node_list_item (frames, ii));
+       for (ii = 0; color_names[ii]; ii++) {
+               GdkRGBA *color = NULL;
+               gchar *color_value;
 
-               frame_doc = webkit_dom_html_iframe_element_get_content_document (iframe);
+               g_object_get (formatter, color_names[ii], &color, NULL);
+               color_value = g_strdup_printf ("#%06x", e_rgba_to_value (color));
 
-               element = find_element_by_id (frame_doc, id);
+               add_color_css_rule_for_web_view (
+                       E_WEB_VIEW (display),
+                       color_names[ii],
+                       color_value);
 
-               g_object_unref (iframe);
-               if (element != NULL)
-                       break;
+               gdk_rgba_free (color);
+               g_free (color_value);
        }
 
-       g_object_unref (frames);
-
-       return element;
-}
+       e_web_view_add_css_rule_into_style_sheet (
+               E_WEB_VIEW (display),
+               "-e-mail-formatter-style-sheet",
+               ".-e-mail-formatter-frame-security-none",
+               "border-width: 1px; border-style: solid");
 
-static void
-mail_display_plugin_widget_resize (GtkWidget *widget,
-                                   gpointer dummy,
-                                   EMailDisplay *display)
-{
-       WebKitDOMElement *parent_element;
-       gchar *dim;
-       gint height, width;
-       gfloat scale;
-
-       parent_element = g_object_get_data (
-               G_OBJECT (widget), "parent_element");
-
-       if (!WEBKIT_DOM_IS_ELEMENT (parent_element)) {
-               d (
-                       printf ("%s: %s does not have (valid) parent element!\n",
-                       G_STRFUNC, (gchar *) g_object_get_data (G_OBJECT (widget), "uri")));
-               return;
-       }
+       /* the rgba values below were copied from e-formatter-secure-button */
+       direction = gtk_widget_get_default_direction ();
 
-       scale = webkit_web_view_get_zoom_level (WEBKIT_WEB_VIEW (display));
-       width = gtk_widget_get_allocated_width (widget);
-       gtk_widget_get_preferred_height_for_width (widget, width, &height, NULL);
+       if (direction == GTK_TEXT_DIR_RTL)
+               style = "border-width: 1px 1px 1px 4px; border-style: solid; border-color: rgba(53%, 73%, 
53%, 1.0)";
+       else
+               style = "border-width: 1px 4px 1px 1px; border-style: solid; border-color: rgba(53%, 73%, 
53%, 1.0)";
+       e_web_view_add_css_rule_into_style_sheet (
+               E_WEB_VIEW (display),
+               "-e-mail-formatter-style-sheet",
+               ".-e-mail-formatter-frame-security-good",
+               style);
 
-       /* When zooming WebKit does not change dimensions of the elements,
-        * but only scales them on the canvas.  GtkWidget can't be scaled
-        * though so we need to cope with the dimension changes to keep the
-        * the widgets the correct size.  Due to inaccuracy in rounding
-        * (float -> int) it still acts a bit funny, but at least it does
-        * not cause widgets in WebKit to go crazy when zooming. */
-       height = height * (1 / scale);
+       if (direction == GTK_TEXT_DIR_RTL)
+               style = "border-width: 1px 1px 1px 4px; border-style: solid; border-color: rgba(73%, 53%, 
53%, 1.0)";
+       else
+               style = "border-width: 1px 4px 1px 1px; border-style: solid; border-color: rgba(73%, 53%, 
53%, 1.0)";
+       e_web_view_add_css_rule_into_style_sheet (
+               E_WEB_VIEW (display),
+               "-e-mail-formatter-style-sheet",
+               ".-e-mail-formatter-frame-security-bad",
+               style);
 
-       /* Int -> Str */
-       dim = g_strdup_printf ("%d", height);
+       if (direction == GTK_TEXT_DIR_RTL)
+               style = "border-width: 1px 1px 1px 4px; border-style: solid; border-color: rgba(91%, 82%, 
13%, 1.0)";
+       else
+               style = "border-width: 1px 4px 1px 1px; border-style: solid; border-color: rgba(91%, 82%, 
13%, 1.0)";
+       e_web_view_add_css_rule_into_style_sheet (
+               E_WEB_VIEW (display),
+               "-e-mail-formatter-style-sheet",
+               ".-e-mail-formatter-frame-security-unknown",
+               style);
 
-       /* Set height of the containment <object> to match height of the
-        * GtkWidget it contains */
-       webkit_dom_html_object_element_set_height (
-               WEBKIT_DOM_HTML_OBJECT_ELEMENT (parent_element), dim);
-       g_free (dim);
+       if (direction == GTK_TEXT_DIR_RTL)
+               style = "border-width: 1px 1px 1px 4px; border-style: solid; border-color: rgba(91%, 82%, 
13%, 1.0)";
+       else
+               style = "border-width: 1px 4px 1px 1px; border-style: solid; border-color: rgba(91%, 82%, 
13%, 1.0)";
+       e_web_view_add_css_rule_into_style_sheet (
+               E_WEB_VIEW (display),
+               "-e-mail-formatter-style-sheet",
+               ".-e-mail-formatter-frame-security-need-key",
+               style);
 }
 
 static void
-plugin_widget_set_parent_element (GtkWidget *widget,
-                                  EMailDisplay *display)
+headers_collapsed_signal_cb (GDBusConnection *connection,
+                             const gchar *sender_name,
+                             const gchar *object_path,
+                             const gchar *interface_name,
+                             const gchar *signal_name,
+                             GVariant *parameters,
+                             EMailDisplay *display)
 {
-       const gchar *uri;
-       WebKitDOMDocument *document;
-       WebKitDOMElement *element;
+       gboolean expanded;
 
-       uri = g_object_get_data (G_OBJECT (widget), "uri");
-       if (uri == NULL || *uri == '\0')
+       if (g_strcmp0 (signal_name, "HeadersCollapsed") != 0)
                return;
 
-       document = webkit_web_view_get_dom_document (WEBKIT_WEB_VIEW (display));
-       element = find_element_by_id (document, uri);
+       if (parameters)
+               g_variant_get (parameters, "(b)", &expanded);
 
-       if (!WEBKIT_DOM_IS_ELEMENT (element)) {
-               g_warning ("Failed to find parent <object> for '%s' - no ID set?", uri);
-               return;
-       }
+       e_mail_display_set_headers_collapsed (display, expanded);
+}
 
-       /* Assign the WebKitDOMElement to "parent_element" data of the
-        * GtkWidget and the GtkWidget to "widget" data of the DOM Element. */
-       g_object_set_data (G_OBJECT (widget), "parent_element", element);
-       g_object_set_data (G_OBJECT (element), "widget", widget);
+static void
+setup_dom_bindings (EMailDisplay *display)
+{
+       GDBusProxy *web_extension;
+
+       web_extension = e_web_view_get_web_extension_proxy (E_WEB_VIEW (display));
+
+       if (web_extension) {
+               if (display->priv->web_extension_headers_collapsed_signal_id == 0) {
+                       display->priv->web_extension_headers_collapsed_signal_id =
+                               g_dbus_connection_signal_subscribe (
+                                       g_dbus_proxy_get_connection (web_extension),
+                                       g_dbus_proxy_get_name (web_extension),
+                                       E_WEB_EXTENSION_INTERFACE,
+                                       "HeadersCollapsed",
+                                       E_WEB_EXTENSION_OBJECT_PATH,
+                                       NULL,
+                                       G_DBUS_SIGNAL_FLAGS_NONE,
+                                       (GDBusSignalCallback) headers_collapsed_signal_cb,
+                                       display,
+                                       NULL);
+               }
 
-       e_binding_bind_property (
-               element, "hidden",
-               widget, "visible",
-               G_BINDING_SYNC_CREATE |
-               G_BINDING_INVERT_BOOLEAN);
+               g_dbus_proxy_call (
+                       web_extension,
+                       "EMailDisplayBindDOM",
+                       g_variant_new (
+                               "(t)",
+                               webkit_web_view_get_page_id (WEBKIT_WEB_VIEW (display))),
+                       G_DBUS_CALL_FLAGS_NONE,
+                       -1,
+                       NULL,
+                       NULL,
+                       NULL);
+       }
 }
 
 static void
-attachment_button_expanded (GObject *object,
-                            GParamSpec *pspec,
-                            gpointer user_data)
+mail_display_change_one_attachment_visibility (EMailDisplay *display,
+                                              EAttachment *attachment,
+                                              gboolean show,
+                                              gboolean flip)
 {
-       EAttachmentButton *button = E_ATTACHMENT_BUTTON (object);
-       EMailDisplay *display = user_data;
-       WebKitDOMDocument *document;
-       WebKitDOMElement *element, *iframe;
-       WebKitDOMCSSStyleDeclaration *css;
-       const gchar *attachment_part_id;
        gchar *element_id;
-       gboolean expanded;
-
-       d (
-               printf ("Attachment button %s has been %s!\n",
-               (gchar *) g_object_get_data (object, "uri"),
-               (e_attachment_button_get_expanded (button) ? "expanded" : "collapsed")));
-
-       expanded =
-               e_attachment_button_get_expanded (button) &&
-               gtk_widget_get_visible (GTK_WIDGET (button));
+       gchar *uri;
+       guint flags;
 
-       document = webkit_web_view_get_dom_document (WEBKIT_WEB_VIEW (display));
-       attachment_part_id = g_object_get_data (object, "attachment_id");
+       g_return_if_fail (E_IS_MAIL_DISPLAY (display));
+       g_return_if_fail (E_IS_ATTACHMENT (attachment));
+       g_return_if_fail (g_hash_table_contains (display->priv->attachment_flags, attachment));
 
-       element_id = g_strconcat (attachment_part_id, ".wrapper", NULL);
-       element = find_element_by_id (document, element_id);
-       g_free (element_id);
+       flags = GPOINTER_TO_UINT (g_hash_table_lookup (display->priv->attachment_flags, attachment));
+       if (flip)
+               show = !(flags & E_ATTACHMENT_FLAG_VISIBLE);
 
-       if (!WEBKIT_DOM_IS_ELEMENT (element)) {
-               d (
-                       printf ("%s: Content <div> of attachment %s does not exist!!\n",
-                       G_STRFUNC, (gchar *) g_object_get_data (object, "uri")));
+       if ((((flags & E_ATTACHMENT_FLAG_VISIBLE) != 0) ? 1 : 0) == (show ? 1 : 0))
                return;
-       }
-
-       if (WEBKIT_DOM_IS_HTML_ELEMENT (element) && expanded &&
-           webkit_dom_element_get_child_element_count (element) == 0) {
-               gchar *inner_html_data;
 
-               inner_html_data = webkit_dom_element_get_attribute (element, "inner-html-data");
-               if (inner_html_data && *inner_html_data) {
-                       WebKitDOMHTMLElement *html_element;
+       if (show)
+               flags = flags | E_ATTACHMENT_FLAG_VISIBLE;
+       else
+               flags = flags & (~E_ATTACHMENT_FLAG_VISIBLE);
+       g_hash_table_insert (display->priv->attachment_flags, attachment, GUINT_TO_POINTER (flags));
 
-                       html_element = WEBKIT_DOM_HTML_ELEMENT (element);
-                       webkit_dom_html_element_set_inner_html (html_element, inner_html_data, NULL);
+       element_id = g_strdup_printf ("attachment-wrapper-%p", attachment);
+       e_web_view_set_element_hidden (E_WEB_VIEW (display), element_id, !show);
+       g_free (element_id);
 
-                       webkit_dom_element_remove_attribute (element, "inner-html-data");
-               }
+       element_id = g_strdup_printf ("attachment-expander-img-%p", attachment);
+       uri = g_strdup_printf ("gtk-stock://%s?size=%d", show ? "go-down" : "go-next", GTK_ICON_SIZE_BUTTON);
 
-               g_free (inner_html_data);
-       }
+       e_web_view_set_element_attribute (E_WEB_VIEW (display), element_id, NULL, "src", uri);
 
-       /* Hide/Show all the GtkWidgets inside an attachment, otherwise they could
-        * be visible even if the wrapper is hidden. */
-       if ((iframe = webkit_dom_element_query_selector (element, "iframe", NULL))) {
-               WebKitDOMDocument *content_document;
+       g_free (element_id);
+       g_free (uri);
+}
 
-               content_document = webkit_dom_html_iframe_element_get_content_document
-                       (WEBKIT_DOM_HTML_IFRAME_ELEMENT (iframe));
+static void
+mail_display_change_attachment_visibility (EMailDisplay *display,
+                                          gboolean all,
+                                          gboolean show)
+{
+       EAttachmentView *view;
+       GList *attachments, *link;
 
-               if (content_document) {
-                       gint length, ii;
-                       WebKitDOMNodeList *list;
+       g_return_if_fail (E_IS_MAIL_DISPLAY (display));
 
-                       list = webkit_dom_document_get_elements_by_tag_name (content_document, "object");
-                       length = webkit_dom_node_list_get_length (list);
+       view = e_mail_display_get_attachment_view (display);
+       g_return_if_fail (view != NULL);
 
-                       for (ii = 0; ii < length; ii++) {
-                               WebKitDOMNode *item;
+       if (all)
+               attachments = e_attachment_store_get_attachments (display->priv->attachment_store);
+       else
+               attachments = view ? e_attachment_view_get_selected_attachments (view) : NULL;
 
-                               item = webkit_dom_node_list_item (list, ii);
+       for (link = attachments; link; link = g_list_next (link)) {
+               EAttachment *attachment = link->data;
 
-                               css = webkit_dom_element_get_style (WEBKIT_DOM_ELEMENT (item));
-                               if (expanded)
-                                       g_free (webkit_dom_css_style_declaration_remove_property (css, 
"display", NULL));
-                               else
-                                       webkit_dom_css_style_declaration_set_property (css, "display", 
"none", "", NULL);
-                               g_clear_object (&css);
-                       }
-                       g_object_unref (list);
-               }
+               if (e_attachment_get_can_show (attachment))
+                       mail_display_change_one_attachment_visibility (display, attachment, show, FALSE);
        }
 
-       /* Show or hide the DIV which contains
-        * the attachment (iframe, image...). */
-       css = webkit_dom_element_get_style (element);
-       webkit_dom_css_style_declaration_set_property (
-               css, "display", expanded ? "block" : "none", "", NULL);
-       g_object_unref (css);
+       g_list_free_full (attachments, g_object_unref);
 }
 
 static void
-attachment_button_zoom_to_window_cb (GObject *object,
-                                    GParamSpec *pspec,
-                                    gpointer user_data)
+mail_attachment_change_zoom (EMailDisplay *display,
+                            gboolean to_100_percent)
 {
-       EAttachmentButton *button = E_ATTACHMENT_BUTTON (object);
-       EMailDisplay *display = user_data;
-       WebKitDOMDocument *document;
-       WebKitDOMElement *element, *child;
-       WebKitDOMCSSStyleDeclaration *css;
-       const gchar *attachment_part_id;
-       gchar *element_id;
-       gboolean zoom_to_window;
+       EAttachmentView *view;
+       GList *attachments, *link;
 
-       d (
-               printf ("Attachment button %s has been set to %s!\n",
-               (gchar *) g_object_get_data (object, "uri"),
-               (e_attachment_botton_get_zoom_to_window (attachment) ? "zoom-to-window" : "zoom to 100%")));
-
-       if (!gtk_widget_get_visible (GTK_WIDGET (button)))
-               return;
+       g_return_if_fail (E_IS_MAIL_DISPLAY (display));
 
-       zoom_to_window = e_attachment_button_get_zoom_to_window (button);
+       view = e_mail_display_get_attachment_view (display);
+       g_return_if_fail (view != NULL);
 
-       document = webkit_web_view_get_dom_document (WEBKIT_WEB_VIEW (display));
-       attachment_part_id = g_object_get_data (object, "attachment_id");
+       attachments = view ? e_attachment_view_get_selected_attachments (view) : NULL;
 
-       element_id = g_strconcat (attachment_part_id, ".wrapper", NULL);
-       element = find_element_by_id (document, element_id);
-       g_free (element_id);
-
-       if (!WEBKIT_DOM_IS_ELEMENT (element)) {
-               d (
-                       printf ("%s: Content <div> of attachment %s does not exist!!\n",
-                       G_STRFUNC, (gchar *) g_object_get_data (object, "uri")));
-               return;
-       }
+       for (link = attachments; link; link = g_list_next (link)) {
+               EAttachment *attachment = link->data;
+               gchar *element_id;
+               const gchar *max_width;
+               guint flags;
 
-       child = webkit_dom_element_get_first_element_child (element);
-       if (!child || !WEBKIT_DOM_IS_HTML_IMAGE_ELEMENT (child)) {
-               d (
-                       printf ("%s: Content <div> of attachment %s does not contain image, but %s\n",
-                       G_STRFUNC, (gchar *) g_object_get_data (object, "uri"),
-                       child ? G_OBJECT_TYPE_NAME (child) : "[null]"));
-               g_clear_object (&child);
-               return;
-       }
+               if (!E_IS_ATTACHMENT (attachment) ||
+                   !g_hash_table_contains (display->priv->attachment_flags, attachment))
+                       continue;
 
-       css = webkit_dom_element_get_style (child);
-       if (zoom_to_window) {
-               webkit_dom_css_style_declaration_set_property (css, "max-width", "100%", "", NULL);
-       } else {
-               g_free (webkit_dom_css_style_declaration_remove_property (css, "max-width", NULL));
-       }
-       g_object_unref (css);
-       g_clear_object (&child);
-}
+               flags = GPOINTER_TO_UINT (g_hash_table_lookup (display->priv->attachment_flags, attachment));
+               if ((((flags & E_ATTACHMENT_FLAG_ZOOMED_TO_100) != 0) ? 1 : 0) == (to_100_percent ? 1 : 0))
+                       continue;
 
-static void
-mail_display_attachment_count_changed (EAttachmentStore *store,
-                                       GParamSpec *pspec,
-                                       GtkWidget *box)
-{
-       WebKitDOMHTMLElement *element;
-       GList *children;
+               if (to_100_percent)
+                       flags = flags | E_ATTACHMENT_FLAG_ZOOMED_TO_100;
+               else
+                       flags = flags & (~E_ATTACHMENT_FLAG_ZOOMED_TO_100);
+               g_hash_table_insert (display->priv->attachment_flags, attachment, GUINT_TO_POINTER (flags));
 
-       children = gtk_container_get_children (GTK_CONTAINER (box));
-       g_return_if_fail (children && children->data);
+               if (to_100_percent)
+                       max_width = NULL;
+               else
+                       max_width = "100%";
 
-       element = g_object_get_data (children->data, "parent_element");
-       g_list_free (children);
+               element_id = g_strdup_printf ("attachment-wrapper-%p::child", attachment);
 
-       g_return_if_fail (WEBKIT_DOM_IS_HTML_ELEMENT (element));
+               e_web_view_set_element_style_property (E_WEB_VIEW (display), element_id, "max-width", 
max_width, "");
 
-       if (e_attachment_store_get_num_attachments (store) == 0) {
-               gtk_widget_hide (box);
-               webkit_dom_html_element_set_hidden (element, TRUE);
-       } else {
-               gtk_widget_show (box);
-               webkit_dom_html_element_set_hidden (element, FALSE);
+               g_free (element_id);
        }
-}
 
-typedef struct _NumAttachmentsData {
-       EAttachmentStore *store;
-       gulong handler_id;
-} NumAttachmentsData;
+       g_list_free_full (attachments, g_object_unref);
+}
 
 static void
-attachment_bar_box_gone_cb (gpointer data,
-                           GObject *gone_box)
+action_attachment_show_cb (GtkAction *action,
+                          EMailDisplay *display)
 {
-       NumAttachmentsData *nad = data;
+       g_return_if_fail (E_IS_MAIL_DISPLAY (display));
 
-       if (nad) {
-               g_signal_handler_disconnect (nad->store, nad->handler_id);
-               g_object_unref (nad->store);
-               g_free (nad);
-       }
+       mail_display_change_attachment_visibility (display, FALSE, TRUE);
 }
 
-static GtkWidget *
-mail_display_plugin_widget_requested (WebKitWebView *web_view,
-                                      gchar *mime_type,
-                                      gchar *uri,
-                                      GHashTable *param,
-                                      gpointer user_data)
+static void
+action_attachment_show_all_cb (GtkAction *action,
+                              EMailDisplay *display)
 {
-       EMailDisplay *display;
-       EMailExtensionRegistry *reg;
-       EMailFormatterExtension *extension;
-       GQueue *extensions;
-       GList *head, *link;
-       EMailPart *part = NULL;
-       GtkWidget *widget = NULL;
-       GWeakRef *weakref;
-       gchar *part_id, *type, *object_uri;
+       g_return_if_fail (E_IS_MAIL_DISPLAY (display));
 
-       part_id = g_hash_table_lookup (param, "data");
-       if (part_id == NULL || !g_str_has_prefix (uri, "mail://"))
-               return NULL;
+       mail_display_change_attachment_visibility (display, TRUE, TRUE);
+}
 
-       type = g_hash_table_lookup (param, "type");
-       if (type == NULL)
-               return NULL;
+static void
+action_attachment_hide_cb (GtkAction *action,
+                          EMailDisplay *display)
+{
+       g_return_if_fail (E_IS_MAIL_DISPLAY (display));
 
-       display = E_MAIL_DISPLAY (web_view);
+       mail_display_change_attachment_visibility (display, FALSE, FALSE);
+}
 
-       weakref = g_hash_table_lookup (display->priv->widgets, part_id);
-       if (weakref)
-               widget = g_weak_ref_get (weakref);
+static void
+action_attachment_hide_all_cb (GtkAction *action,
+                              EMailDisplay *display)
+{
+       g_return_if_fail (E_IS_MAIL_DISPLAY (display));
 
-       if (widget != NULL) {
-               /* This cannot be the last reference; thread-safety is assured,
-                  because this runs in the main thread only. */
-               g_object_unref (widget);
-               d (printf ("Handled %s widget request from cache\n", part_id));
-               return widget;
-       }
+       mail_display_change_attachment_visibility (display, TRUE, FALSE);
+}
 
-       /* Find the EMailPart representing the requested widget. */
-       part = e_mail_part_list_ref_part (display->priv->part_list, part_id);
-       if (part == NULL)
-               return NULL;
+static void
+action_attachment_zoom_to_100_cb (GtkAction *action,
+                                 EMailDisplay *display)
+{
+       g_return_if_fail (E_IS_MAIL_DISPLAY (display));
 
-       reg = e_mail_formatter_get_extension_registry (display->priv->formatter);
-       extensions = e_mail_extension_registry_get_for_mime_type (reg, type);
-       if (extensions == NULL)
-               goto exit;
+       mail_attachment_change_zoom (display, TRUE);
+}
 
-       extension = NULL;
-       head = g_queue_peek_head_link (extensions);
-       for (link = head; link != NULL; link = g_list_next (link)) {
-               extension = link->data;
+static void
+action_attachment_zoom_to_window_cb (GtkAction *action,
+                                    EMailDisplay *display)
+{
+       g_return_if_fail (E_IS_MAIL_DISPLAY (display));
 
-               if (extension == NULL)
-                       continue;
+       mail_attachment_change_zoom (display, FALSE);
+}
 
-               if (e_mail_formatter_extension_has_widget (extension))
-                       break;
-       }
+static GtkActionEntry attachment_inline_entries[] = {
 
-       if (extension == NULL)
-               goto exit;
-
-       /* Get the widget from formatter */
-       widget = e_mail_formatter_extension_get_widget (
-               extension, display->priv->part_list, part, param);
-       d (
-               printf ("Created widget %s (%p) for part %s\n",
-                       G_OBJECT_TYPE_NAME (widget), widget, part_id));
-
-       /* Should not happen! WebKit will display an ugly 'Plug-in not
-        * available' placeholder instead of hiding the <object> element. */
-       if (widget == NULL)
-               goto exit;
-
-       /* Attachment button has URI different then the actual PURI because
-        * that URI identifies the attachment itself */
-       if (E_IS_ATTACHMENT_BUTTON (widget)) {
-               EMailPartAttachment *empa = (EMailPartAttachment *) part;
-               EAttachment *attachment;
-               gchar *attachment_part_id;
+       { "hide",
+         NULL,
+         N_("_Hide"),
+         NULL,
+         NULL,  /* XXX Add a tooltip! */
+         G_CALLBACK (action_attachment_hide_cb) },
 
-               if (empa->attachment_view_part_id)
-                       attachment_part_id = empa->attachment_view_part_id;
-               else
-                       attachment_part_id = part_id;
+       { "hide-all",
+         NULL,
+         N_("Hid_e All"),
+         NULL,
+         NULL,  /* XXX Add a tooltip! */
+         G_CALLBACK (action_attachment_hide_all_cb) },
 
-               object_uri = g_strconcat (
-                       attachment_part_id, ".attachment_button", NULL);
-               g_object_set_data_full (
-                       G_OBJECT (widget), "attachment_id",
-                       g_strdup (attachment_part_id),
-                       (GDestroyNotify) g_free);
+       { "show",
+         NULL,
+         N_("_View Inline"),
+         NULL,
+         NULL,  /* XXX Add a tooltip! */
+         G_CALLBACK (action_attachment_show_cb) },
 
-               attachment = e_mail_part_attachment_ref_attachment (empa);
-               if (attachment && e_attachment_is_mail_note (attachment)) {
-                       CamelFolder *folder;
-                       const gchar *message_uid;
+       { "show-all",
+         NULL,
+         N_("Vie_w All Inline"),
+         NULL,
+         NULL,  /* XXX Add a tooltip! */
+         G_CALLBACK (action_attachment_show_all_cb) },
 
-                       folder = e_mail_part_list_get_folder (display->priv->part_list);
-                       message_uid = e_mail_part_list_get_message_uid (display->priv->part_list);
+       { "zoom-to-100",
+         NULL,
+         N_("_Zoom to 100%"),
+         NULL,
+         N_("Zoom the image to its natural size"),
+         G_CALLBACK (action_attachment_zoom_to_100_cb) },
 
-                       if (folder && message_uid) {
-                               CamelMessageInfo *info;
+       { "zoom-to-window",
+         NULL,
+         N_("_Zoom to window"),
+         NULL,
+         N_("Zoom large images to not be wider than the window width"),
+         G_CALLBACK (action_attachment_zoom_to_window_cb) }
+};
 
-                               info = camel_folder_get_message_info (folder, message_uid);
-                               if (info) {
-                                       if (!camel_message_info_get_user_flag (info, E_MAIL_NOTES_USER_FLAG))
-                                               camel_message_info_set_user_flag (info, 
E_MAIL_NOTES_USER_FLAG, TRUE);
-                                       camel_message_info_unref (info);
-                               }
-                       }
-               }
+static EAttachment *
+mail_display_ref_attachment_from_element (EMailDisplay *display,
+                                         const gchar *element_value)
+{
+       EAttachment *attachment = NULL;
+       GQueue queue = G_QUEUE_INIT;
+       GList *head, *link;
 
-               g_clear_object (&attachment);
-       } else {
-               object_uri = g_strdup (part_id);
-       }
+       g_return_val_if_fail (E_IS_MAIL_DISPLAY (display), NULL);
+       g_return_val_if_fail (element_value != NULL, NULL);
 
-       /* Store the uri as data of the widget */
-       g_object_set_data_full (
-               G_OBJECT (widget), "uri",
-               object_uri, (GDestroyNotify) g_free);
+       e_mail_part_list_queue_parts (display->priv->part_list, NULL, &queue);
+       head = g_queue_peek_head_link (&queue);
 
-       /* Set pointer to the <object> element as GObject data
-        * "parent_element" and set pointer to the widget as GObject
-        * data "widget" to the <object> element. */
-       plugin_widget_set_parent_element (widget, display);
+       for (link = head; link != NULL; link = g_list_next (link)) {
+               EMailPart *part = E_MAIL_PART (link->data);
 
-       /* Resizing a GtkWidget requires changing size of parent
-        * <object> HTML element in DOM. */
-       g_signal_connect (
-               widget, "size-allocate",
-               G_CALLBACK (mail_display_plugin_widget_resize), display);
+               if (E_IS_MAIL_PART_ATTACHMENT (part)) {
+                       EAttachment *adept;
+                       gboolean can_use;
+                       gchar *tmp;
 
-       if (E_IS_ATTACHMENT_BAR (widget)) {
-               GtkWidget *box = NULL;
-               EAttachmentStore *store;
-               NumAttachmentsData *nad;
+                       adept = e_mail_part_attachment_ref_attachment (E_MAIL_PART_ATTACHMENT (part));
 
-               /* Only when packed in box (grid does not work),
-                * EAttachmentBar reports correct height */
-               box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
-               gtk_box_pack_start (GTK_BOX (box), widget, TRUE, TRUE, 0);
+                       tmp = g_strdup_printf ("%p", adept);
+                       can_use = g_strcmp0 (tmp, element_value) == 0;
+                       g_free (tmp);
 
-               /* When EAttachmentBar is expanded/collapsed it does not
-                * emit size-allocate signal despite it changes it's height. */
-               g_signal_connect (
-                       widget, "notify::expanded",
-                       G_CALLBACK (mail_display_plugin_widget_resize),
-                       display);
-               g_signal_connect (
-                       widget, "notify::active-view",
-                       G_CALLBACK (mail_display_plugin_widget_resize),
-                       display);
-
-               /* Always hide an attachment bar without attachments */
-               store = e_attachment_bar_get_store (E_ATTACHMENT_BAR (widget));
-
-               nad = g_new0 (NumAttachmentsData, 1);
-               nad->store = g_object_ref (store);
-               nad->handler_id = g_signal_connect (
-                       store, "notify::num-attachments",
-                       G_CALLBACK (mail_display_attachment_count_changed),
-                       box);
-
-               g_object_weak_ref (G_OBJECT (box), attachment_bar_box_gone_cb, nad);
-
-               gtk_widget_show (widget);
-               gtk_widget_show (box);
-
-               /* Initial sync */
-               mail_display_attachment_count_changed (store, NULL, box);
-
-               widget = box;
-
-       } else if (E_IS_ATTACHMENT_BUTTON (widget)) {
-
-               /* Bind visibility of DOM element containing related
-                * attachment with 'expanded' property of this
-                * attachment button. */
-               EMailPartAttachment *empa = (EMailPartAttachment *) part;
-
-               e_attachment_button_set_expandable (E_ATTACHMENT_BUTTON (widget),
-                       e_mail_part_attachment_get_expandable (empa));
-
-               if (e_mail_part_attachment_get_expandable (empa)) {
-                       /* Show/hide the attachment when the EAttachmentButton
-                        * is expanded/collapsed or shown/hidden. */
-                       g_signal_connect (
-                               widget, "notify::expanded",
-                               G_CALLBACK (attachment_button_expanded),
-                               display);
-                       g_signal_connect (
-                               widget, "notify::visible",
-                               G_CALLBACK (attachment_button_expanded),
-                               display);
-                       g_signal_connect (
-                               widget, "notify::zoom-to-window",
-                               G_CALLBACK (attachment_button_zoom_to_window_cb),
-                               display);
-
-                       if (e_mail_part_should_show_inline (part)) {
-                               e_attachment_button_set_expanded (
-                                       E_ATTACHMENT_BUTTON (widget), TRUE);
-                       } else {
-                               e_attachment_button_set_expanded (
-                                       E_ATTACHMENT_BUTTON (widget), FALSE);
-                               attachment_button_expanded (
-                                       G_OBJECT (widget), NULL, display);
+                       if (can_use) {
+                               attachment = adept;
+                               break;
                        }
+
+                       g_clear_object (&adept);
                }
        }
 
-       g_hash_table_insert (
-               display->priv->widgets,
-               g_strdup (object_uri), e_weak_ref_new (widget));
-
-exit:
-       if (part != NULL)
-               g_object_unref (part);
+       while (!g_queue_is_empty (&queue))
+               g_object_unref (g_queue_pop_head (&queue));
 
-       return widget;
+       return attachment;
 }
 
 static void
-toggle_headers_visibility (WebKitDOMElement *button,
-                           WebKitDOMEvent *event,
-                           WebKitWebView *web_view)
+mail_display_attachment_expander_clicked_cb (EWebView *web_view,
+                                            const gchar *element_class,
+                                            const gchar *element_value,
+                                            const GtkAllocation *element_position,
+                                            gpointer user_data)
 {
-       WebKitDOMDocument *document;
-       WebKitDOMElement *short_headers = NULL, *full_headers = NULL;
-       WebKitDOMCSSStyleDeclaration *css_short = NULL, *css_full = NULL;
-       gboolean expanded;
-       const gchar *path;
-       gchar *css_value;
+       EMailDisplay *display;
+       EAttachment *attachment;
 
-       document = webkit_web_view_get_dom_document (web_view);
+       g_return_if_fail (E_IS_MAIL_DISPLAY (web_view));
+       g_return_if_fail (element_class != NULL);
+       g_return_if_fail (element_value != NULL);
+       g_return_if_fail (element_position != NULL);
 
-       short_headers = webkit_dom_document_get_element_by_id (
-               document, "__evo-short-headers");
-       if (short_headers == NULL)
-               return;
-
-       css_short = webkit_dom_element_get_style (short_headers);
+       display = E_MAIL_DISPLAY (web_view);
+       attachment = mail_display_ref_attachment_from_element (display, element_value);
 
-       full_headers = webkit_dom_document_get_element_by_id (
-               document, "__evo-full-headers");
-       if (full_headers == NULL)
-               goto clean;
+       if (attachment) {
+               /* Flip the current 'visible' state */
+               mail_display_change_one_attachment_visibility (display, attachment, FALSE, TRUE);
+       }
 
-       css_full = webkit_dom_element_get_style (full_headers);
-       css_value = webkit_dom_css_style_declaration_get_property_value (
-               css_full, "display");
-       expanded = (g_strcmp0 (css_value, "table") == 0);
-       g_free (css_value);
+       g_clear_object (&attachment);
+}
 
-       webkit_dom_css_style_declaration_set_property (
-               css_full, "display",
-               expanded ? "none" : "table", "", NULL);
-       webkit_dom_css_style_declaration_set_property (
-               css_short, "display",
-               expanded ? "table" : "none", "", NULL);
+static void
+mail_display_attachment_inline_update_actions (EMailDisplay *display)
+{
+       GtkActionGroup *action_group;
+       GtkAction *action;
+       GList *attachments, *link;
+       EAttachmentView *view;
+       guint n_shown = 0;
+       guint n_hidden = 0;
+       guint n_selected = 0;
+       gboolean can_show = FALSE;
+       gboolean shown = FALSE;
+       gboolean is_image = FALSE;
+       gboolean zoomed_to_100 = FALSE;
+       gboolean visible;
 
-       if (expanded)
-               path = "evo-file://" EVOLUTION_IMAGESDIR "/plus.png";
-       else
-               path = "evo-file://" EVOLUTION_IMAGESDIR "/minus.png";
+       g_return_if_fail (E_IS_MAIL_DISPLAY (display));
 
-       webkit_dom_html_image_element_set_src (
-               WEBKIT_DOM_HTML_IMAGE_ELEMENT (button), path);
+       action_group = display->priv->attachment_inline_group;
+       g_return_if_fail (action_group != NULL);
 
-       e_mail_display_set_headers_collapsed (
-               E_MAIL_DISPLAY (web_view), expanded);
+       attachments = e_attachment_store_get_attachments (display->priv->attachment_store);
 
-       d (printf ("Headers %s!\n", expanded ? "collapsed" : "expanded"));
- clean:
-       g_clear_object (&short_headers);
-       g_clear_object (&css_short);
-       g_clear_object (&full_headers);
-       g_clear_object (&css_full);
-}
+       for (link = attachments; link; link = g_list_next (link)) {
+               EAttachment *attachment = link->data;
+               guint32 flags;
 
-static void
-toggle_address_visibility (WebKitDOMElement *button,
-                           WebKitDOMEvent *event)
-{
-       WebKitDOMElement *full_addr = NULL, *ellipsis = NULL;
-       WebKitDOMElement *parent = NULL, *bold = NULL;
-       WebKitDOMCSSStyleDeclaration *css_full = NULL, *css_ellipsis = NULL;
-       const gchar *path;
-       gchar *property_value;
-       gboolean expanded;
+               if (!e_attachment_get_can_show (attachment))
+                       continue;
 
-       /* <b> element */
-       bold = webkit_dom_node_get_parent_element (WEBKIT_DOM_NODE (button));
-       /* <td> element */
-       parent = webkit_dom_node_get_parent_element (WEBKIT_DOM_NODE (bold));
-       g_object_unref (bold);
+               flags = GPOINTER_TO_UINT (g_hash_table_lookup (display->priv->attachment_flags, attachment));
+               if ((flags & E_ATTACHMENT_FLAG_VISIBLE) != 0)
+                       n_shown++;
+               else
+                       n_hidden++;
+       }
 
-       full_addr = webkit_dom_element_query_selector (parent, "#__evo-moreaddr", NULL);
+       g_list_free_full (attachments, g_object_unref);
 
-       if (!full_addr)
-               goto clean;
+       view = e_mail_display_get_attachment_view (display);
+       attachments = view ? e_attachment_view_get_selected_attachments (view) : NULL;
+       n_selected = g_list_length (attachments);
 
-       css_full = webkit_dom_element_get_style (full_addr);
+       if (n_selected == 1) {
+               EAttachment *attachment;
+               gchar *mime_type;
+               guint32 flags;
 
-       ellipsis = webkit_dom_element_query_selector (parent, "#__evo-moreaddr-ellipsis", NULL);
+               attachment = attachments->data;
+               mime_type = e_attachment_dup_mime_type (attachment);
+               can_show = e_attachment_get_can_show (attachment);
+               is_image = can_show && mime_type && g_ascii_strncasecmp (mime_type, "image/", 6) == 0;
 
-       if (!ellipsis)
-               goto clean;
+               flags = GPOINTER_TO_UINT (g_hash_table_lookup (display->priv->attachment_flags, attachment));
+               shown = (flags & E_ATTACHMENT_FLAG_VISIBLE) != 0;
+               zoomed_to_100 = (flags & E_ATTACHMENT_FLAG_ZOOMED_TO_100) != 0;
 
-       css_ellipsis = webkit_dom_element_get_style (ellipsis);
+               g_free (mime_type);
+       }
+       g_list_free_full (attachments, g_object_unref);
 
-       property_value = webkit_dom_css_style_declaration_get_property_value (css_full, "display");
-       expanded = g_strcmp0 (property_value, "inline") == 0;
-       g_free (property_value);
+       action = gtk_action_group_get_action (action_group, "show");
+       gtk_action_set_visible (action, can_show && !shown);
 
-       webkit_dom_css_style_declaration_set_property (
-               css_full, "display", (expanded ? "none" : "inline"), "", NULL);
-       webkit_dom_css_style_declaration_set_property (
-               css_ellipsis, "display", (expanded ? "inline" : "none"), "", NULL);
+       /* Show this action if there are multiple viewable
+        * attachments, and at least one of them is hidden. */
+       visible = (n_shown + n_hidden > 1) && (n_hidden > 0);
+       action = gtk_action_group_get_action (action_group, "show-all");
+       gtk_action_set_visible (action, visible);
 
-       if (expanded)
-               path = "evo-file://" EVOLUTION_IMAGESDIR "/plus.png";
-       else
-               path = "evo-file://" EVOLUTION_IMAGESDIR "/minus.png";
+       action = gtk_action_group_get_action (action_group, "hide");
+       gtk_action_set_visible (action, can_show && shown);
 
-       if (!WEBKIT_DOM_IS_HTML_IMAGE_ELEMENT (button)) {
-               WebKitDOMElement *element;
+       /* Show this action if there are multiple viewable
+        * attachments, and at least one of them is shown. */
+       visible = (n_shown + n_hidden > 1) && (n_shown > 0);
+       action = gtk_action_group_get_action (action_group, "hide-all");
+       gtk_action_set_visible (action, visible);
 
-               element = webkit_dom_element_query_selector (parent, "#__evo-moreaddr-img", NULL);
-               if (!element)
-                       goto clean;
+       action = gtk_action_group_get_action (action_group, "zoom-to-100");
+       gtk_action_set_visible (action, can_show && shown && is_image && !zoomed_to_100);
 
-               webkit_dom_html_image_element_set_src (WEBKIT_DOM_HTML_IMAGE_ELEMENT (element), path);
-
-               g_object_unref (element);
-       } else
-               webkit_dom_html_image_element_set_src (WEBKIT_DOM_HTML_IMAGE_ELEMENT (button), path);
- clean:
-       g_clear_object (&css_full);
-       g_clear_object (&css_ellipsis);
-       g_clear_object (&full_addr);
-       g_clear_object (&ellipsis);
-       g_clear_object (&parent);
+       action = gtk_action_group_get_action (action_group, "zoom-to-window");
+       gtk_action_set_visible (action, can_show && shown && is_image && zoomed_to_100);
 }
 
 static void
-add_color_css_rule_for_web_view (EWebView *view,
-                                 const gchar *color_name,
-                                 const gchar *color_value)
+mail_display_attachment_menu_deactivate_cb (GtkMenuShell *menu,
+                                           gpointer user_data)
 {
-       gchar *selector;
-       gchar *style;
+       EMailDisplay *display = user_data;
 
-       selector = g_strconcat (".-e-mail-formatter-", color_name, NULL);
+       g_return_if_fail (E_IS_MAIL_DISPLAY (display));
 
-       if (g_strstr_len (color_name, -1, "header")) {
-               style = g_strconcat (
-                       "color: ", color_value, " !important;", NULL);
-       } else if (g_strstr_len (color_name, -1, "frame")) {
-               style = g_strconcat (
-                       "border-color: ", color_value, NULL);
-       } else {
-               style = g_strconcat (
-                       "background-color: ", color_value, " !important;", NULL);
-       }
+       gtk_action_group_set_visible (display->priv->attachment_inline_group, FALSE);
 
-       e_web_view_add_css_rule_into_style_sheet (
-               view,
-               "-e-mail-formatter-style-sheet",
-               selector,
-               style);
-
-       g_free (style);
-       g_free (selector);
+       g_signal_handlers_disconnect_by_func (menu,
+               G_CALLBACK (mail_display_attachment_menu_deactivate_cb), display);
 }
 
 static void
-initialize_web_view_colors (EMailDisplay *display)
+mail_display_attachment_menu_position_cb (GtkMenu *menu,
+                                         gint *x,
+                                         gint *y,
+                                         gboolean *push_in,
+                                         gpointer user_data)
 {
-       EMailFormatter *formatter;
+       GtkRequisition menu_requisition;
        GtkTextDirection direction;
-       const gchar *style;
-       gint ii;
+       GtkAllocation allocation;
+       GdkRectangle monitor;
+       GdkScreen *screen;
+       GdkWindow *window;
+       GtkWidget *widget;
+       EMailDisplay *display = user_data;
+       gint monitor_num;
 
-       const gchar *color_names[] = {
-               "body-color",
-               "citation-color",
-               "frame-color",
-               "header-color",
-               NULL
-       };
+       g_return_if_fail (E_IS_MAIL_DISPLAY (display));
 
-       formatter = e_mail_display_get_formatter (display);
+       widget = GTK_WIDGET (display);
+       gtk_widget_get_preferred_size (GTK_WIDGET (menu), &menu_requisition, NULL);
 
-       for (ii = 0; color_names[ii]; ii++) {
-               GdkRGBA *color = NULL;
-               gchar *color_value;
+       window = gtk_widget_get_parent_window (widget);
+       screen = gtk_widget_get_screen (GTK_WIDGET (menu));
+       monitor_num = gdk_screen_get_monitor_at_window (screen, window);
+       if (monitor_num < 0)
+               monitor_num = 0;
+       gdk_screen_get_monitor_geometry (screen, monitor_num, &monitor);
 
-               g_object_get (formatter, color_names[ii], &color, NULL);
-               color_value = g_strdup_printf ("#%06x", e_rgba_to_value (color));
+       allocation = display->priv->attachment_popup_position;
 
-               add_color_css_rule_for_web_view (
-                       E_WEB_VIEW (display),
-                       color_names[ii],
-                       color_value);
+       gdk_window_get_origin (window, x, y);
+       *x += allocation.x;
+       *y += allocation.y + allocation.height;
 
-               gdk_rgba_free (color);
-               g_free (color_value);
-       }
+       direction = gtk_widget_get_direction (widget);
+       if (direction == GTK_TEXT_DIR_LTR)
+               *x += MAX (allocation.width - menu_requisition.width, 0);
+       else if (menu_requisition.width > allocation.width)
+               *x -= menu_requisition.width - allocation.width;
 
-       e_web_view_add_css_rule_into_style_sheet (
-               E_WEB_VIEW (display),
-               "-e-mail-formatter-style-sheet",
-               ".-e-mail-formatter-frame-security-none",
-               "border-width: 1px; border-style: solid");
+       *push_in = FALSE;
+}
 
-       /* the rgba values below were copied from e-formatter-secure-button */
-       direction = gtk_widget_get_default_direction ();
+static void
+mail_display_attachment_select_path (EAttachmentView *view,
+                                    EAttachment *attachment)
+{
+       GtkTreePath *path;
+       GtkTreeIter iter;
+       EAttachmentStore *store;
 
-       if (direction == GTK_TEXT_DIR_RTL)
-               style = "border-width: 1px 1px 1px 4px; border-style: solid; border-color: rgba(53%, 73%, 
53%, 1.0)";
-       else
-               style = "border-width: 1px 4px 1px 1px; border-style: solid; border-color: rgba(53%, 73%, 
53%, 1.0)";
-       e_web_view_add_css_rule_into_style_sheet (
-               E_WEB_VIEW (display),
-               "-e-mail-formatter-style-sheet",
-               ".-e-mail-formatter-frame-security-good",
-               style);
+       g_return_if_fail (E_IS_ATTACHMENT_VIEW (view));
+       g_return_if_fail (E_IS_ATTACHMENT (attachment));
 
-       if (direction == GTK_TEXT_DIR_RTL)
-               style = "border-width: 1px 1px 1px 4px; border-style: solid; border-color: rgba(73%, 53%, 
53%, 1.0)";
-       else
-               style = "border-width: 1px 4px 1px 1px; border-style: solid; border-color: rgba(73%, 53%, 
53%, 1.0)";
-       e_web_view_add_css_rule_into_style_sheet (
-               E_WEB_VIEW (display),
-               "-e-mail-formatter-style-sheet",
-               ".-e-mail-formatter-frame-security-bad",
-               style);
+       store = e_attachment_view_get_store (view);
+       g_return_if_fail (e_attachment_store_find_attachment_iter (store, attachment, &iter));
 
-       if (direction == GTK_TEXT_DIR_RTL)
-               style = "border-width: 1px 1px 1px 4px; border-style: solid; border-color: rgba(91%, 82%, 
13%, 1.0)";
-       else
-               style = "border-width: 1px 4px 1px 1px; border-style: solid; border-color: rgba(91%, 82%, 
13%, 1.0)";
-       e_web_view_add_css_rule_into_style_sheet (
-               E_WEB_VIEW (display),
-               "-e-mail-formatter-style-sheet",
-               ".-e-mail-formatter-frame-security-unknown",
-               style);
+       path = gtk_tree_model_get_path (GTK_TREE_MODEL (store), &iter);
 
-       if (direction == GTK_TEXT_DIR_RTL)
-               style = "border-width: 1px 1px 1px 4px; border-style: solid; border-color: rgba(91%, 82%, 
13%, 1.0)";
-       else
-               style = "border-width: 1px 4px 1px 1px; border-style: solid; border-color: rgba(91%, 82%, 
13%, 1.0)";
-       e_web_view_add_css_rule_into_style_sheet (
-               E_WEB_VIEW (display),
-               "-e-mail-formatter-style-sheet",
-               ".-e-mail-formatter-frame-security-need-key",
-               style);
+       e_attachment_view_unselect_all (view);
+       e_attachment_view_select_path (view, path);
+
+       gtk_tree_path_free (path);
 }
 
 static void
-setup_image_click_event_listeners_on_document (WebKitDOMDocument *document,
-                                                WebKitWebView *web_view)
+mail_display_attachment_menu_clicked_cb (EWebView *web_view,
+                                        const gchar *element_class,
+                                        const gchar *element_value,
+                                        const GtkAllocation *element_position,
+                                        gpointer user_data)
 {
-       gint length, ii = 0;
-       WebKitDOMElement *button;
-       WebKitDOMNodeList *list;
+       EMailDisplay *display;
+       EAttachmentView *view;
+       EAttachment *attachment;
+
+       g_return_if_fail (E_IS_MAIL_DISPLAY (web_view));
+       g_return_if_fail (element_class != NULL);
+       g_return_if_fail (element_value != NULL);
+       g_return_if_fail (element_position != NULL);
 
-       /* Install event listeners on document */
-       button = webkit_dom_document_get_element_by_id (
-               document, "__evo-collapse-headers-img");
-       if (button != NULL)
-               webkit_dom_event_target_add_event_listener (
-                       WEBKIT_DOM_EVENT_TARGET (button), "click",
-                       G_CALLBACK (toggle_headers_visibility),
-                       FALSE, web_view);
+       display = E_MAIL_DISPLAY (web_view);
+       view = e_mail_display_get_attachment_view (display);
+       attachment = mail_display_ref_attachment_from_element (display, element_value);
 
-       list = webkit_dom_document_query_selector_all (document, "*[id^=__evo-moreaddr-]", NULL);
+       if (view && attachment) {
+               GtkWidget *popup_menu;
 
-       length = webkit_dom_node_list_get_length (list);
+               popup_menu = e_attachment_view_get_popup_menu (view);
 
-       for (ii = 0; ii < length; ii++) {
-               button = WEBKIT_DOM_ELEMENT (webkit_dom_node_list_item (list, ii));
+               g_signal_connect (
+                       popup_menu, "deactivate",
+                       G_CALLBACK (mail_display_attachment_menu_deactivate_cb), display);
 
-               webkit_dom_event_target_add_event_listener (
-                       WEBKIT_DOM_EVENT_TARGET (button), "click",
-                       G_CALLBACK (toggle_address_visibility), FALSE,
-                       NULL);
+               mail_display_attachment_select_path (view, attachment);
+               display->priv->attachment_popup_position = *element_position;
+
+               mail_display_attachment_inline_update_actions (display);
+               gtk_action_group_set_visible (display->priv->attachment_inline_group, TRUE);
+
+               e_attachment_view_show_popup_menu (view, NULL,
+                       mail_display_attachment_menu_position_cb, display);
        }
-       g_object_unref (list);
+
+       g_clear_object (&attachment);
 }
 
 static void
-setup_dom_bindings (WebKitWebView *web_view,
-                    WebKitWebFrame *frame,
-                    gpointer user_data)
+mail_display_attachment_added_cb (EAttachmentStore *store,
+                                 EAttachment *attachment,
+                                 gpointer user_data)
 {
-       WebKitDOMDocument *document;
+       EMailDisplay *display = user_data;
+       guint flags;
+
+       g_return_if_fail (E_IS_ATTACHMENT_STORE (store));
+       g_return_if_fail (E_IS_ATTACHMENT (attachment));
+       g_return_if_fail (E_IS_MAIL_DISPLAY (display));
 
-       document = webkit_web_frame_get_dom_document (frame);
+       flags = e_attachment_get_initially_shown (attachment) ? E_ATTACHMENT_FLAG_VISIBLE : 0;
 
-       setup_image_click_event_listeners_on_document (document, web_view);
+       g_hash_table_insert (display->priv->attachment_flags, attachment, GUINT_TO_POINTER (flags));
 }
 
 static void
-mail_parts_bind_dom (GObject *object,
-                     GParamSpec *pspec,
-                     gpointer user_data)
-{
-       WebKitWebFrame *frame;
-       WebKitLoadStatus load_status;
-       WebKitWebView *web_view;
-       WebKitDOMDocument *document;
-       EMailDisplay *display;
-       GQueue queue = G_QUEUE_INIT;
-       GList *head, *link;
-       const gchar *frame_name;
+mail_display_attachment_removed_cb (EAttachmentStore *store,
+                                   EAttachment *attachment,
+                                   gpointer user_data)
+{
+       EMailDisplay *display = user_data;
 
-       frame = WEBKIT_WEB_FRAME (object);
-       load_status = webkit_web_frame_get_load_status (frame);
+       g_return_if_fail (E_IS_ATTACHMENT_STORE (store));
+       g_return_if_fail (E_IS_ATTACHMENT (attachment));
+       g_return_if_fail (E_IS_MAIL_DISPLAY (display));
 
-       web_view = webkit_web_frame_get_web_view (frame);
-       display = E_MAIL_DISPLAY (web_view);
+       g_hash_table_remove (display->priv->attachment_flags, attachment);
+}
 
-       if (load_status == WEBKIT_LOAD_PROVISIONAL) {
-               if (webkit_web_view_get_main_frame (web_view) == frame)
-                       e_mail_display_cleanup_skipped_uris (display);
-               return;
+static void
+mail_element_exists_cb (GDBusProxy *web_extension,
+                        GAsyncResult *result,
+                        EMailPart *part)
+{
+       gboolean element_exists = FALSE;
+       GVariant *result_variant;
+       guint64 page_id;
+
+       result_variant = g_dbus_proxy_call_finish (web_extension, result, NULL);
+       if (result_variant) {
+               g_variant_get (result_variant, "(bt)", &element_exists, &page_id);
+               g_variant_unref (result_variant);
        }
 
-       if (load_status != WEBKIT_LOAD_FINISHED)
-               return;
+       if (element_exists)
+               e_mail_part_bind_dom_element (
+                       part,
+                       web_extension,
+                       page_id,
+                       e_mail_part_get_id (part));
+
+       g_object_unref (part);
+}
+
+static void
+mail_parts_bind_dom (EMailDisplay *display)
+{
+       EWebView *web_view;
+       GQueue queue = G_QUEUE_INIT;
+       GList *head, *link;
+       GDBusProxy *web_extension;
+       gboolean has_attachment = FALSE;
+
+       g_return_if_fail (E_IS_MAIL_DISPLAY (display));
 
        if (display->priv->part_list == NULL)
                return;
 
        initialize_web_view_colors (display);
-       frame_name = webkit_web_frame_get_name (frame);
-       if (frame_name == NULL || *frame_name == '\0')
-               frame_name = ".message.headers";
 
-       document = webkit_web_view_get_dom_document (web_view);
+       web_view = E_WEB_VIEW (display);
 
-       e_mail_part_list_queue_parts (
-               display->priv->part_list, frame_name, &queue);
+       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);
        head = g_queue_peek_head_link (&queue);
 
        for (link = head; link != NULL; link = g_list_next (link)) {
                EMailPart *part = E_MAIL_PART (link->data);
-               WebKitDOMElement *element;
                const gchar *part_id;
 
-               /* Iterate only the parts rendered in
-                * the frame and all it's subparts. */
-               if (!e_mail_part_id_has_prefix (part, frame_name))
-                       break;
-
                part_id = e_mail_part_get_id (part);
-               element = find_element_by_id (document, part_id);
 
-               if (element != NULL)
-                       e_mail_part_bind_dom_element (part, element);
+               has_attachment = has_attachment || E_IS_MAIL_PART_ATTACHMENT (part);
+
+               e_mail_part_web_view_loaded (part, web_view);
+
+               g_dbus_proxy_call (
+                       web_extension,
+                       "ElementExists",
+                       g_variant_new (
+                               "(ts)",
+                               webkit_web_view_get_page_id (
+                                       WEBKIT_WEB_VIEW (display)),
+                               part_id),
+                       G_DBUS_CALL_FLAGS_NONE,
+                       -1,
+                       NULL,
+                       (GAsyncReadyCallback)mail_element_exists_cb,
+                       g_object_ref (part));
        }
 
        while (!g_queue_is_empty (&queue))
                g_object_unref (g_queue_pop_head (&queue));
+
+       if (has_attachment) {
+               e_web_view_register_element_clicked (web_view, "attachment-expander",
+                       mail_display_attachment_expander_clicked_cb, NULL);
+               e_web_view_register_element_clicked (web_view, "attachment-menu",
+                       mail_display_attachment_menu_clicked_cb, NULL);
+       }
 }
 
 static void
-mail_display_frame_created (WebKitWebView *web_view,
-                            WebKitWebFrame *frame,
-                            gpointer user_data)
+mail_display_load_changed_cb (WebKitWebView *wk_web_view,
+                             WebKitLoadEvent load_event,
+                             gpointer user_data)
 {
-       d (printf ("Frame %s created!\n", webkit_web_frame_get_name (frame)));
+       EMailDisplay *display;
 
-       /* Call bind_func of all parts written in this frame */
-       g_signal_connect (
-               frame, "notify::load-status",
-               G_CALLBACK (mail_parts_bind_dom), NULL);
-}
+       g_return_if_fail (E_IS_MAIL_DISPLAY (wk_web_view));
 
-static void
-mail_display_uri_changed (EMailDisplay *display,
-                          GParamSpec *pspec,
-                          gpointer dummy)
-{
-       d (printf ("EMailDisplay URI changed, recreating widgets hashtable\n"));
+       display = E_MAIL_DISPLAY (wk_web_view);
 
-       if (display->priv->widgets != NULL) {
-               g_hash_table_foreach (
-                       display->priv->widgets,
-                       mail_display_plugin_widget_disconnect, display);
-               g_hash_table_destroy (display->priv->widgets);
+       if (load_event == WEBKIT_LOAD_STARTED) {
+               e_mail_display_cleanup_skipped_uris (display);
+               e_attachment_store_remove_all (display->priv->attachment_store);
+               return;
        }
 
-       display->priv->widgets = g_hash_table_new_full (
-               (GHashFunc) g_str_hash,
-               (GEqualFunc) g_str_equal,
-               (GDestroyNotify) g_free,
-               (GDestroyNotify) e_weak_ref_free);
+       if (load_event == WEBKIT_LOAD_FINISHED) {
+               setup_dom_bindings (display);
+               mail_parts_bind_dom (display);
+       }
 }
 
 static void
@@ -1387,6 +1249,20 @@ mail_display_get_property (GObject *object,
                            GParamSpec *pspec)
 {
        switch (property_id) {
+               case PROP_ATTACHMENT_STORE:
+                       g_value_set_object (
+                               value,
+                               e_mail_display_get_attachment_store (
+                               E_MAIL_DISPLAY (object)));
+                       return;
+
+               case PROP_ATTACHMENT_VIEW:
+                       g_value_set_object (
+                               value,
+                               e_mail_display_get_attachment_view (
+                               E_MAIL_DISPLAY (object)));
+                       return;
+
                case PROP_FORMATTER:
                        g_value_set_object (
                                value,
@@ -1445,22 +1321,36 @@ mail_display_dispose (GObject *object)
                priv->scheduled_reload = 0;
        }
 
-       if (priv->widgets != NULL) {
-               g_hash_table_foreach (
-                       priv->widgets,
-                       mail_display_plugin_widget_disconnect, object);
-               g_hash_table_destroy (priv->widgets);
-               priv->widgets = NULL;
-       }
-
        if (priv->settings != NULL)
                g_signal_handlers_disconnect_matched (
                        priv->settings, G_SIGNAL_MATCH_DATA,
                        0, 0, NULL, NULL, object);
 
+       if (priv->web_extension_headers_collapsed_signal_id > 0) {
+               g_dbus_connection_signal_unsubscribe (
+                       g_dbus_proxy_get_connection (
+                               e_web_view_get_web_extension_proxy (E_WEB_VIEW (object))),
+                       priv->web_extension_headers_collapsed_signal_id);
+               priv->web_extension_headers_collapsed_signal_id = 0;
+       }
+
+       if (priv->attachment_store) {
+               /* To have called the mail_display_attachment_removed_cb() before it's disconnected */
+               e_attachment_store_remove_all (priv->attachment_store);
+
+               g_signal_handlers_disconnect_by_func (priv->attachment_store,
+                       G_CALLBACK (mail_display_attachment_added_cb), object);
+
+               g_signal_handlers_disconnect_by_func (priv->attachment_store,
+                       G_CALLBACK (mail_display_attachment_removed_cb), object);
+       }
+
        g_clear_object (&priv->part_list);
        g_clear_object (&priv->formatter);
        g_clear_object (&priv->settings);
+       g_clear_object (&priv->attachment_store);
+       g_clear_object (&priv->attachment_view);
+       g_clear_object (&priv->attachment_inline_group);
 
        /* Chain up to parent's dispose() method. */
        G_OBJECT_CLASS (e_mail_display_parent_class)->dispose (object);
@@ -1484,6 +1374,7 @@ mail_display_finalize (GObject *object)
                priv->skipped_remote_content_sites = NULL;
        }
 
+       g_hash_table_destroy (priv->attachment_flags);
        g_clear_object (&priv->remote_content);
        g_mutex_unlock (&priv->remote_content_lock);
        g_mutex_clear (&priv->remote_content_lock);
@@ -1493,12 +1384,107 @@ mail_display_finalize (GObject *object)
 }
 
 static void
+mail_display_get_font_settings (GSettings *settings,
+                                PangoFontDescription **monospace,
+                                PangoFontDescription **variable)
+{
+       gboolean use_custom_font;
+       gchar *monospace_font;
+       gchar *variable_font;
+
+       use_custom_font = g_settings_get_boolean (settings, "use-custom-font");
+
+       if (!use_custom_font) {
+               if (monospace)
+                       *monospace = NULL;
+               if (variable)
+                       *variable = NULL;
+               return;
+       }
+
+       monospace_font = g_settings_get_string (settings, "monospace-font");
+       variable_font = g_settings_get_string (settings, "variable-width-font");
+
+       if (monospace)
+               *monospace = (monospace_font != NULL) ? pango_font_description_from_string (monospace_font) : 
NULL;
+       if (variable)
+               *variable = (variable_font != NULL) ? pango_font_description_from_string (variable_font) : 
NULL;
+
+       g_free (monospace_font);
+       g_free (variable_font);
+}
+
+static void
+mail_display_set_fonts (EWebView *web_view,
+                        PangoFontDescription **monospace,
+                        PangoFontDescription **variable)
+{
+       EMailDisplay *display = E_MAIL_DISPLAY (web_view);
+
+       mail_display_get_font_settings (display->priv->settings, monospace, variable);
+}
+
+static void
+mail_display_web_view_initialize (WebKitWebView *web_view)
+{
+       WebKitSettings *webkit_settings;
+
+       webkit_settings = webkit_web_view_get_settings (web_view);
+
+       g_object_set (webkit_settings,
+               "enable-frame-flattening", TRUE,
+               NULL);
+}
+
+static void
 mail_display_constructed (GObject *object)
 {
+       EContentRequest *content_request;
+       EWebView *web_view;
+       EMailDisplay *display;
+       GtkUIManager *ui_manager;
+
        e_extensible_load_extensions (E_EXTENSIBLE (object));
 
        /* Chain up to parent's constructed() method. */
        G_OBJECT_CLASS (e_mail_display_parent_class)->constructed (object);
+
+       mail_display_web_view_initialize (WEBKIT_WEB_VIEW (object));
+
+       display = E_MAIL_DISPLAY (object);
+       web_view = E_WEB_VIEW (object);
+
+       e_web_view_update_fonts (web_view);
+
+       content_request = e_http_request_new ();
+       e_web_view_register_content_request_for_scheme (web_view, "evo-http", content_request);
+       e_web_view_register_content_request_for_scheme (web_view, "evo-https", content_request);
+       g_object_unref (content_request);
+
+       content_request = e_mail_request_new ();
+       e_web_view_register_content_request_for_scheme (web_view, "mail", content_request);
+       g_object_unref (content_request);
+
+       content_request = e_cid_request_new ();
+       e_web_view_register_content_request_for_scheme (web_view, "cid", content_request);
+       g_object_unref (content_request);
+
+       display->priv->attachment_view = g_object_ref_sink (e_attachment_bar_new 
(display->priv->attachment_store));
+
+       ui_manager = e_attachment_view_get_ui_manager (display->priv->attachment_view);
+       if (ui_manager) {
+               GError *error = NULL;
+
+               gtk_ui_manager_insert_action_group (ui_manager, display->priv->attachment_inline_group, -1);
+
+               display->priv->attachment_inline_ui_id = gtk_ui_manager_add_ui_from_string (ui_manager,
+                       attachment_popup_ui, -1, &error);
+
+               if (error) {
+                       g_warning ("%s: Failed to read attachment_popup_ui: %s", G_STRFUNC, error->message);
+                       g_clear_error (&error);
+               }
+       }
 }
 
 static void
@@ -1526,98 +1512,80 @@ static gboolean
 mail_display_button_press_event (GtkWidget *widget,
                                  GdkEventButton *event)
 {
-       EWebView *web_view = E_WEB_VIEW (widget);
-       WebKitHitTestResult *hit_test;
-       GList *list, *link;
+       if (event->button == 3) {
+               EWebView *web_view = E_WEB_VIEW (widget);
+               gchar *popup_document_uri;
+               GList *list, *link;
 
-       if (event->button != 3)
-               goto chainup;
+               popup_document_uri = e_web_view_get_document_uri_from_point (web_view, event->x, event->y);
 
-       hit_test = webkit_web_view_get_hit_test_result (
-               WEBKIT_WEB_VIEW (web_view), event);
+               list = e_extensible_list_extensions (
+                       E_EXTENSIBLE (web_view), E_TYPE_EXTENSION);
+               for (link = list; link != NULL; link = g_list_next (link)) {
+                       EExtension *extension = link->data;
 
-       list = e_extensible_list_extensions (
-               E_EXTENSIBLE (web_view), E_TYPE_EXTENSION);
-       for (link = list; link != NULL; link = g_list_next (link)) {
-               EExtension *extension = link->data;
+                       if (!E_IS_MAIL_DISPLAY_POPUP_EXTENSION (extension))
+                               continue;
 
-               if (!E_IS_MAIL_DISPLAY_POPUP_EXTENSION (extension))
-                       continue;
+                       e_mail_display_popup_extension_update_actions (
+                               E_MAIL_DISPLAY_POPUP_EXTENSION (extension), popup_document_uri);
+               }
 
-               e_mail_display_popup_extension_update_actions (
-                       E_MAIL_DISPLAY_POPUP_EXTENSION (extension), hit_test);
+               g_list_free (list);
+               g_free (popup_document_uri);
        }
-       g_list_free (list);
-
-       g_object_unref (hit_test);
 
-chainup:
        /* Chain up to parent's button_press_event() method. */
        return GTK_WIDGET_CLASS (e_mail_display_parent_class)->
                button_press_event (widget, event);
 }
 
-static gchar *
-mail_display_redirect_uri (EWebView *web_view,
-                           const gchar *uri)
-{
-       EMailDisplay *display;
-       EMailPartList *part_list;
-       gboolean uri_is_http;
 
-       display = E_MAIL_DISPLAY (web_view);
-       part_list = e_mail_display_get_part_list (display);
+static gboolean
+mail_display_image_exists_in_cache (const gchar *image_uri)
+{
+       gchar *filename;
+       gchar *hash;
+       gboolean exists = FALSE;
 
-       if (part_list == NULL)
-               goto chainup;
+       if (!emd_global_http_cache)
+               return FALSE;
 
-       /* Redirect cid:part_id to mail://mail_id/cid:part_id */
-       if (g_str_has_prefix (uri, "cid:")) {
-               CamelFolder *folder;
-               const gchar *message_uid;
+       hash = g_compute_checksum_for_string (G_CHECKSUM_MD5, image_uri, -1);
+       filename = camel_data_cache_get_filename (
+               emd_global_http_cache, "http", hash);
 
-               folder = e_mail_part_list_get_folder (part_list);
-               message_uid = e_mail_part_list_get_message_uid (part_list);
+       if (filename != NULL) {
+               struct stat st;
 
-               /* Always write raw content of CID object. */
-               return e_mail_part_build_uri (
-                       folder, message_uid,
-                       "part_id", G_TYPE_STRING, uri,
-                       "mode", G_TYPE_INT, E_MAIL_FORMATTER_MODE_CID, NULL);
+               exists = g_file_test (filename, G_FILE_TEST_EXISTS);
+               if (exists && g_stat (filename, &st) == 0) {
+                       exists = st.st_size != 0;
+               } else {
+                       exists = FALSE;
+               }
+               g_free (filename);
        }
 
-       /* WebKit won't allow to load a local file when displaying
-        * "remote" mail:// protocol, so we need to handle this manually. */
-       if (g_str_has_prefix (uri, "file:")) {
-               gchar *content = NULL;
-               gchar *content_type;
-               gchar *filename;
-               gchar *encoded;
-               gchar *new_uri;
-               gsize length = 0;
-
-               filename = g_filename_from_uri (uri, NULL, NULL);
-               if (filename == NULL)
-                       goto chainup;
-
-               if (!g_file_get_contents (filename, &content, &length, NULL)) {
-                       g_free (filename);
-                       goto chainup;
-               }
+       g_free (hash);
 
-               encoded = g_base64_encode ((guchar *) content, length);
-               content_type = g_content_type_guess (filename, NULL, 0, NULL);
+       return exists;
+}
 
-               new_uri = g_strdup_printf (
-                       "data:%s;base64,%s", content_type, encoded);
+static void
+mail_display_uri_requested_cb (EWebView *web_view,
+                              const gchar *uri,
+                              gchar **redirect_to_uri)
+{
+       EMailDisplay *display;
+       EMailPartList *part_list;
+       gboolean uri_is_http;
 
-               g_free (content_type);
-               g_free (content);
-               g_free (filename);
-               g_free (encoded);
+       display = E_MAIL_DISPLAY (web_view);
+       part_list = e_mail_display_get_part_list (display);
 
-               return new_uri;
-       }
+       if (part_list == NULL)
+               return;
 
        uri_is_http =
                g_str_has_prefix (uri, "http:") ||
@@ -1639,7 +1607,8 @@ mail_display_redirect_uri (EWebView *web_view,
                can_download_uri = e_mail_display_can_download_uri (display, uri);
                if (!can_download_uri) {
                        /* Check Evolution's cache */
-                       can_download_uri = mail_display_image_exists_in_cache (uri);
+                       can_download_uri = mail_display_image_exists_in_cache (
+                               uri + (g_str_has_prefix (uri, "evo-") ? 4 : 0));
                }
 
                /* If the URI is not cached and we are not allowed to load it
@@ -1650,17 +1619,26 @@ mail_display_redirect_uri (EWebView *web_view,
                if (!can_download_uri && !display->priv->force_image_load &&
                    (image_policy == E_IMAGE_LOADING_POLICY_NEVER)) {
                        e_mail_display_claim_skipped_uri (display, uri);
-                       return g_strdup ("about:blank");
+                       g_free (*redirect_to_uri);
+                       *redirect_to_uri = g_strdup ("");
+                       return;
                }
 
                folder = e_mail_part_list_get_folder (part_list);
                message_uid = e_mail_part_list_get_message_uid (part_list);
 
-               new_uri = g_strconcat ("evo-", uri, NULL);
+               if (g_str_has_prefix (uri, "evo-")) {
+                       soup_uri = soup_uri_new (uri);
+               } else {
+                       new_uri = g_strconcat ("evo-", uri, NULL);
+                       soup_uri = soup_uri_new (new_uri);
+
+                       g_free (new_uri);
+               }
+
                mail_uri = e_mail_part_build_uri (
                        folder, message_uid, NULL, NULL);
 
-               soup_uri = soup_uri_new (new_uri);
                if (soup_uri->query)
                        query = soup_form_decode (soup_uri->query);
                else
@@ -1682,22 +1660,18 @@ mail_display_redirect_uri (EWebView *web_view,
                g_free (mail_uri);
 
                soup_uri_set_query_from_form (soup_uri, query);
-               g_free (new_uri);
 
                new_uri = soup_uri_to_string (soup_uri, FALSE);
 
                soup_uri_free (soup_uri);
                g_hash_table_unref (query);
 
-               return new_uri;
+               g_free (*redirect_to_uri);
+               *redirect_to_uri = new_uri;
        }
-
-chainup:
-       /* Chain up to parent's redirect_uri() method. */
-       return E_WEB_VIEW_CLASS (e_mail_display_parent_class)->
-               redirect_uri (web_view, uri);
 }
 
+#if 0 /* FIXME WK2 */
 static CamelMimePart *
 camel_mime_part_from_cid (EMailDisplay *display,
                           const gchar *uri)
@@ -1745,60 +1719,6 @@ mail_display_suggest_filename (EWebView *web_view,
 }
 
 static void
-mail_display_set_fonts (EWebView *web_view,
-                        PangoFontDescription **monospace,
-                        PangoFontDescription **variable)
-{
-       EMailDisplay *display = E_MAIL_DISPLAY (web_view);
-       gboolean use_custom_font;
-       gchar *monospace_font;
-       gchar *variable_font;
-
-       use_custom_font = g_settings_get_boolean (
-               display->priv->settings, "use-custom-font");
-       if (!use_custom_font) {
-               *monospace = NULL;
-               *variable = NULL;
-               return;
-       }
-
-       monospace_font = g_settings_get_string (
-               display->priv->settings, "monospace-font");
-       variable_font = g_settings_get_string (
-               display->priv->settings, "variable-width-font");
-
-       *monospace = (monospace_font != NULL) ?
-               pango_font_description_from_string (monospace_font) : NULL;
-       *variable = (variable_font != NULL) ?
-               pango_font_description_from_string (variable_font) : NULL;
-
-       g_free (monospace_font);
-       g_free (variable_font);
-}
-
-static void
-e_mail_display_test_change_and_update_fonts_cb (EMailDisplay *mail_display,
-                                               const gchar *key,
-                                               GSettings *settings)
-{
-       GVariant *new_value, *old_value;
-
-       new_value = g_settings_get_value (settings, key);
-       old_value = g_hash_table_lookup (mail_display->priv->old_settings, key);
-
-       if (!new_value || !old_value || !g_variant_equal (new_value, old_value)) {
-               if (new_value)
-                       g_hash_table_insert (mail_display->priv->old_settings, g_strdup (key), new_value);
-               else
-                       g_hash_table_remove (mail_display->priv->old_settings, key);
-
-               e_web_view_update_fonts (E_WEB_VIEW (mail_display));
-       } else if (new_value) {
-               g_variant_unref (new_value);
-       }
-}
-
-static void
 mail_display_drag_data_get (GtkWidget *widget,
                             GdkDragContext *context,
                             GtkSelectionData *data,
@@ -1859,6 +1779,29 @@ mail_display_drag_data_get (GtkWidget *widget,
  out:
        g_free (uri);
 }
+#endif
+
+static void
+e_mail_display_test_change_and_update_fonts_cb (EMailDisplay *mail_display,
+                                               const gchar *key,
+                                               GSettings *settings)
+{
+       GVariant *new_value, *old_value;
+
+       new_value = g_settings_get_value (settings, key);
+       old_value = g_hash_table_lookup (mail_display->priv->old_settings, key);
+
+       if (!new_value || !old_value || !g_variant_equal (new_value, old_value)) {
+               if (new_value)
+                       g_hash_table_insert (mail_display->priv->old_settings, g_strdup (key), new_value);
+               else
+                       g_hash_table_remove (mail_display->priv->old_settings, key);
+
+               e_web_view_update_fonts (E_WEB_VIEW (mail_display));
+       } else if (new_value) {
+               g_variant_unref (new_value);
+       }
+}
 
 static void
 e_mail_display_class_init (EMailDisplayClass *class)
@@ -1882,12 +1825,35 @@ e_mail_display_class_init (EMailDisplayClass *class)
        widget_class->button_press_event = mail_display_button_press_event;
 
        web_view_class = E_WEB_VIEW_CLASS (class);
-       web_view_class->redirect_uri = mail_display_redirect_uri;
+#if 0 /* FIXME WK2 */
        web_view_class->suggest_filename = mail_display_suggest_filename;
+#endif
        web_view_class->set_fonts = mail_display_set_fonts;
 
        g_object_class_install_property (
                object_class,
+               PROP_ATTACHMENT_STORE,
+               g_param_spec_object (
+                       "attachment-store",
+                       "Attachment Store",
+                       NULL,
+                       E_TYPE_ATTACHMENT_STORE,
+                       G_PARAM_READABLE |
+                       G_PARAM_STATIC_STRINGS));
+
+       g_object_class_install_property (
+               object_class,
+               PROP_ATTACHMENT_VIEW,
+               g_param_spec_object (
+                       "attachment-view",
+                       "Attachment View",
+                       NULL,
+                       E_TYPE_ATTACHMENT_VIEW,
+                       G_PARAM_READABLE |
+                       G_PARAM_STATIC_STRINGS));
+
+       g_object_class_install_property (
+               object_class,
                PROP_FORMATTER,
                g_param_spec_pointer (
                        "formatter",
@@ -1956,13 +1922,24 @@ static void
 e_mail_display_init (EMailDisplay *display)
 {
        GtkUIManager *ui_manager;
-       const gchar *user_cache_dir;
-       WebKitWebSettings *settings;
-       WebKitWebFrame *main_frame;
        GtkActionGroup *actions;
 
        display->priv = E_MAIL_DISPLAY_GET_PRIVATE (display);
 
+       display->priv->attachment_store = E_ATTACHMENT_STORE (e_attachment_store_new ());
+       display->priv->attachment_flags = g_hash_table_new (g_direct_hash, g_direct_equal);
+       display->priv->attachment_inline_group = gtk_action_group_new ("e-mail-display-attachment-inline");
+
+       gtk_action_group_add_actions (
+               display->priv->attachment_inline_group, attachment_inline_entries,
+               G_N_ELEMENTS (attachment_inline_entries), display);
+       gtk_action_group_set_visible (display->priv->attachment_inline_group, FALSE);
+
+       g_signal_connect (display->priv->attachment_store, "attachment-added",
+               G_CALLBACK (mail_display_attachment_added_cb), display);
+       g_signal_connect (display->priv->attachment_store, "attachment-removed",
+               G_CALLBACK (mail_display_attachment_removed_cb), display);
+
        display->priv->old_settings = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, 
(GDestroyNotify) g_variant_unref);
 
        /* Set invalid mode so that MODE property initialization is run
@@ -1972,39 +1949,18 @@ e_mail_display_init (EMailDisplay *display)
        display->priv->force_image_load = FALSE;
        display->priv->scheduled_reload = 0;
 
-       webkit_web_view_set_full_content_zoom (WEBKIT_WEB_VIEW (display), TRUE);
-
-       settings = webkit_web_view_get_settings (WEBKIT_WEB_VIEW (display));
-       g_object_set (settings, "enable-frame-flattening", TRUE, NULL);
-
        g_signal_connect (
-               display, "navigation-policy-decision-requested",
-               G_CALLBACK (mail_display_link_clicked), NULL);
-       g_signal_connect (
-               display, "resource-request-starting",
-               G_CALLBACK (mail_display_resource_requested), NULL);
+               display, "decide-policy",
+               G_CALLBACK (decide_policy_cb), NULL);
+
        g_signal_connect (
                display, "process-mailto",
                G_CALLBACK (mail_display_process_mailto), NULL);
-       g_signal_connect (
-               display, "create-plugin-widget",
-               G_CALLBACK (mail_display_plugin_widget_requested), NULL);
-       g_signal_connect (
-               display, "frame-created",
-               G_CALLBACK (mail_display_frame_created), NULL);
-       e_signal_connect_notify (
-               display, "notify::uri",
-               G_CALLBACK (mail_display_uri_changed), NULL);
-       g_signal_connect (
-               display, "document-load-finished",
-               G_CALLBACK (setup_dom_bindings), NULL);
-       g_signal_connect (
-               display, "document-load-finished",
-               G_CALLBACK (initialize_web_view_colors), NULL);
+#if 0 /* FIXME WK2 */
        g_signal_connect_after (
                display, "drag-data-get",
                G_CALLBACK (mail_display_drag_data_get), display);
-
+#endif
        display->priv->settings = e_util_ref_settings ("org.gnome.evolution.mail");
        g_signal_connect_swapped (
                display->priv->settings , "changed::monospace-font",
@@ -2016,12 +1972,9 @@ e_mail_display_init (EMailDisplay *display)
                display->priv->settings , "changed::use-custom-font",
                G_CALLBACK (e_mail_display_test_change_and_update_fonts_cb), display);
 
-       e_web_view_update_fonts (E_WEB_VIEW (display));
-
-       main_frame = webkit_web_view_get_main_frame (WEBKIT_WEB_VIEW (display));
-       e_signal_connect_notify (
-               main_frame, "notify::load-status",
-               G_CALLBACK (mail_parts_bind_dom), NULL);
+       g_signal_connect (
+               display, "load-changed",
+               G_CALLBACK (mail_display_load_changed_cb), NULL);
 
        actions = e_web_view_get_action_group (E_WEB_VIEW (display), "mailto");
        gtk_action_group_add_actions (
@@ -2030,16 +1983,14 @@ e_mail_display_init (EMailDisplay *display)
        ui_manager = e_web_view_get_ui_manager (E_WEB_VIEW (display));
        gtk_ui_manager_add_ui_from_string (ui_manager, ui, -1, NULL);
 
-       e_web_view_install_request_handler (
-               E_WEB_VIEW (display), E_TYPE_MAIL_REQUEST);
-       e_web_view_install_request_handler (
-               E_WEB_VIEW (display), E_TYPE_HTTP_REQUEST);
-       e_web_view_install_request_handler (
-               E_WEB_VIEW (display), E_TYPE_FILE_REQUEST);
-       e_web_view_install_request_handler (
-               E_WEB_VIEW (display), E_TYPE_STOCK_REQUEST);
+       g_mutex_init (&display->priv->remote_content_lock);
+       display->priv->remote_content = NULL;
+       display->priv->skipped_remote_content_sites = g_hash_table_new_full (camel_strcase_hash, 
camel_strcase_equal, g_free, NULL);
+
+       g_signal_connect (display, "uri-requested", G_CALLBACK (mail_display_uri_requested_cb), NULL);
 
        if (emd_global_http_cache == NULL) {
+               const gchar *user_cache_dir;
                GError *error = NULL;
 
                user_cache_dir = e_get_user_cache_dir ();
@@ -2058,10 +2009,6 @@ e_mail_display_init (EMailDisplay *display)
                        g_clear_error (&error);
                }
        }
-
-       g_mutex_init (&display->priv->remote_content_lock);
-       display->priv->remote_content = NULL;
-       display->priv->skipped_remote_content_sites = g_hash_table_new_full (camel_strcase_hash, 
camel_strcase_equal, g_free, NULL);
 }
 
 static void
@@ -2088,6 +2035,46 @@ e_mail_display_update_colors (EMailDisplay *display,
        g_free (color_value);
 }
 
+static void
+e_mail_display_claim_attachment (EMailFormatter *formatter,
+                                EAttachment *attachment,
+                                gpointer user_data)
+{
+       EMailDisplay *display = user_data;
+       GList *attachments;
+
+       g_return_if_fail (E_IS_MAIL_FORMATTER (formatter));
+       g_return_if_fail (E_IS_ATTACHMENT (attachment));
+       g_return_if_fail (E_IS_MAIL_DISPLAY (display));
+
+       attachments = e_attachment_store_get_attachments (display->priv->attachment_store);
+
+       if (!g_list_find (attachments, attachment)) {
+               e_attachment_store_add_attachment (display->priv->attachment_store, attachment);
+
+               if (e_attachment_is_mail_note (attachment)) {
+                       CamelFolder *folder;
+                       const gchar *message_uid;
+
+                       folder = e_mail_part_list_get_folder (display->priv->part_list);
+                       message_uid = e_mail_part_list_get_message_uid (display->priv->part_list);
+
+                       if (folder && message_uid) {
+                               CamelMessageInfo *info;
+
+                               info = camel_folder_get_message_info (folder, message_uid);
+                               if (info) {
+                                       if (!camel_message_info_get_user_flag (info, E_MAIL_NOTES_USER_FLAG))
+                                               camel_message_info_set_user_flag (info, 
E_MAIL_NOTES_USER_FLAG, TRUE);
+                                       camel_message_info_unref (info);
+                               }
+                       }
+               }
+       }
+
+       g_list_free_full (attachments, g_object_unref);
+}
+
 GtkWidget *
 e_mail_display_new (EMailRemoteContent *remote_content)
 {
@@ -2096,6 +2083,22 @@ e_mail_display_new (EMailRemoteContent *remote_content)
                NULL);
 }
 
+EAttachmentStore *
+e_mail_display_get_attachment_store (EMailDisplay *display)
+{
+       g_return_val_if_fail (E_IS_MAIL_DISPLAY (display), NULL);
+
+       return display->priv->attachment_store;
+}
+
+EAttachmentView *
+e_mail_display_get_attachment_view (EMailDisplay *display)
+{
+       g_return_val_if_fail (E_IS_MAIL_DISPLAY (display), NULL);
+
+       return display->priv->attachment_view;
+}
+
 EMailFormatterMode
 e_mail_display_get_mode (EMailDisplay *display)
 {
@@ -2178,6 +2181,8 @@ e_mail_display_set_mode (EMailDisplay *display,
                        G_CALLBACK (e_mail_display_reload), display,
                NULL);
 
+       g_signal_connect (formatter, "claim-attachment", G_CALLBACK (e_mail_display_claim_attachment), 
display);
+
        e_mail_display_reload (display);
 
        g_object_notify (G_OBJECT (display), "mode");
@@ -2281,7 +2286,7 @@ e_mail_display_load (EMailDisplay *display,
 
        g_return_if_fail (E_IS_MAIL_DISPLAY (display));
 
-       display->priv->force_image_load = FALSE;
+       e_mail_display_set_force_load_images (display, FALSE);
 
        part_list = display->priv->part_list;
        if (part_list == NULL) {
@@ -2442,85 +2447,42 @@ e_mail_display_set_status (EMailDisplay *display,
        g_free (str);
 }
 
-static gchar *
-mail_display_get_frame_selection_text (WebKitDOMElement *iframe)
-{
-       WebKitDOMDocument *document;
-       WebKitDOMDOMWindow *window;
-       WebKitDOMDOMSelection *selection;
-       WebKitDOMNodeList *frames;
-       gulong ii, length;
-
-       document = webkit_dom_html_iframe_element_get_content_document (
-               WEBKIT_DOM_HTML_IFRAME_ELEMENT (iframe));
-       window = webkit_dom_document_get_default_view (document);
-       selection = webkit_dom_dom_window_get_selection (window);
-       if (selection && (webkit_dom_dom_selection_get_range_count (selection) > 0)) {
-               WebKitDOMRange *range;
-
-               range = webkit_dom_dom_selection_get_range_at (selection, 0, NULL);
-               if (range != NULL)
-                       return webkit_dom_range_to_string (range, NULL);
-       }
-
-       frames = webkit_dom_document_get_elements_by_tag_name (
-               document, "IFRAME");
-       length = webkit_dom_node_list_get_length (frames);
-       for (ii = 0; ii < length; ii++) {
-               WebKitDOMNode *node;
-               gchar *text;
-
-               node = webkit_dom_node_list_item (frames, ii);
-
-               text = mail_display_get_frame_selection_text (
-                       WEBKIT_DOM_ELEMENT (node));
-
-               g_object_unref (node);
-               if (text != NULL) {
-                       g_object_unref (frames);
-                       return text;
-               }
-       }
-
-       g_object_unref (frames);
-
-       return NULL;
-}
-
-gchar *
-e_mail_display_get_selection_plain_text (EMailDisplay *display)
+const gchar *
+e_mail_display_get_selection_plain_text_sync (EMailDisplay *display,
+                                              GCancellable *cancellable,
+                                              GError **error)
 {
-       WebKitDOMDocument *document;
-       WebKitDOMNodeList *frames;
-       gulong ii, length;
+       GDBusProxy *web_extension;
 
        g_return_val_if_fail (E_IS_MAIL_DISPLAY (display), NULL);
-
+/* FIXME WK2
        if (!webkit_web_view_has_selection (WEBKIT_WEB_VIEW (display)))
                return NULL;
-
-       document = webkit_web_view_get_dom_document (WEBKIT_WEB_VIEW (display));
-       frames = webkit_dom_document_get_elements_by_tag_name (document, "IFRAME");
-       length = webkit_dom_node_list_get_length (frames);
-
-       for (ii = 0; ii < length; ii++) {
-               gchar *text;
-               WebKitDOMNode *node;
-
-               node = webkit_dom_node_list_item (frames, ii);
-
-               text = mail_display_get_frame_selection_text (
-                       WEBKIT_DOM_ELEMENT (node));
-
-               g_object_unref (node);
-               if (text != NULL) {
-                       g_object_unref (frames);
-                       return text;
+*/
+       web_extension = e_web_view_get_web_extension_proxy (E_WEB_VIEW (display));
+       if (web_extension) {
+               GVariant *result;
+               const gchar *text_content = NULL;
+
+               result = g_dbus_proxy_call_sync (
+                               web_extension,
+                               "GetDocumentContentText",
+                               g_variant_new (
+                                       "(t)",
+                                       webkit_web_view_get_page_id (
+                                               WEBKIT_WEB_VIEW (display))),
+                               G_DBUS_CALL_FLAGS_NONE,
+                               -1,
+                               cancellable,
+                               error);
+
+               if (result) {
+                       g_variant_get (result, "(&s)", &text_content);
+                       g_variant_unref (result);
+                       return text_content;
                }
        }
 
-       g_object_unref (frames);
-
        return NULL;
 }
 
@@ -2529,7 +2491,7 @@ e_mail_display_load_images (EMailDisplay *display)
 {
        g_return_if_fail (E_IS_MAIL_DISPLAY (display));
 
-       display->priv->force_image_load = TRUE;
+       e_mail_display_set_force_load_images (display, TRUE);
        e_web_view_reload (E_WEB_VIEW (display));
 }
 
@@ -2539,6 +2501,9 @@ e_mail_display_set_force_load_images (EMailDisplay *display,
 {
        g_return_if_fail (E_IS_MAIL_DISPLAY (display));
 
+       if ((display->priv->force_image_load ? 1 : 0) == (force_load_images ? 1 : 0))
+               return;
+
        display->priv->force_image_load = force_load_images;
 }
 
@@ -2617,37 +2582,3 @@ e_mail_display_set_remote_content (EMailDisplay *display,
 
        g_mutex_unlock (&display->priv->remote_content_lock);
 }
-
-gboolean
-e_mail_display_needs_key (EMailDisplay *mail_display,
-                         gboolean with_input)
-{
-       gboolean needs_key = FALSE;
-
-       g_return_val_if_fail (E_IS_MAIL_DISPLAY (mail_display), FALSE);
-
-       if (gtk_widget_has_focus (GTK_WIDGET (mail_display))) {
-               WebKitWebFrame *frame;
-               WebKitDOMDocument *dom;
-               WebKitDOMElement *element;
-               gchar *name = NULL;
-
-               frame = webkit_web_view_get_focused_frame (WEBKIT_WEB_VIEW (mail_display));
-               if (!frame)
-                       return FALSE;
-               dom = webkit_web_frame_get_dom_document (frame);
-               element = webkit_dom_html_document_get_active_element (WEBKIT_DOM_HTML_DOCUMENT (dom));
-
-               if (element)
-                       name = webkit_dom_node_get_node_name (WEBKIT_DOM_NODE (element));
-
-               /* if INPUT or TEXTAREA has focus, then any key press should go there */
-               if (name && ((with_input && g_ascii_strcasecmp (name, "INPUT") == 0) || g_ascii_strcasecmp 
(name, "TEXTAREA") == 0)) {
-                       needs_key = TRUE;
-               }
-
-               g_free (name);
-       }
-
-       return needs_key;
-}
diff --git a/mail/e-mail-display.h b/mail/e-mail-display.h
index 893f172..3670f91 100644
--- a/mail/e-mail-display.h
+++ b/mail/e-mail-display.h
@@ -62,6 +62,12 @@ struct _EMailDisplayClass {
 
 GType          e_mail_display_get_type         (void) G_GNUC_CONST;
 GtkWidget *    e_mail_display_new              (EMailRemoteContent *remote_content);
+EAttachmentStore *
+               e_mail_display_get_attachment_store
+                                               (EMailDisplay *display);
+EAttachmentView *
+               e_mail_display_get_attachment_view
+                                               (EMailDisplay *display);
 EMailFormatterMode
                e_mail_display_get_mode         (EMailDisplay *display);
 void           e_mail_display_set_mode         (EMailDisplay *display,
@@ -88,8 +94,10 @@ GtkAction *  e_mail_display_get_action       (EMailDisplay *display,
                                                 const gchar *action_name);
 void           e_mail_display_set_status       (EMailDisplay *display,
                                                 const gchar *status);
-gchar *                e_mail_display_get_selection_plain_text
-                                               (EMailDisplay *display);
+const gchar *  e_mail_display_get_selection_plain_text_sync
+                                               (EMailDisplay *display,
+                                                GCancellable *cancellable,
+                                                GError **error);
 void           e_mail_display_load_images      (EMailDisplay *display);
 void           e_mail_display_set_force_load_images
                                                (EMailDisplay *display,
@@ -104,8 +112,6 @@ EMailRemoteContent *
 void           e_mail_display_set_remote_content
                                                (EMailDisplay *display,
                                                 EMailRemoteContent *remote_content);
-gboolean       e_mail_display_needs_key        (EMailDisplay *mail_display,
-                                                gboolean with_input);
 
 G_END_DECLS
 
diff --git a/mail/e-mail-notes.c b/mail/e-mail-notes.c
index 7d69cb9..94a8396 100644
--- a/mail/e-mail-notes.c
+++ b/mail/e-mail-notes.c
@@ -94,12 +94,12 @@ e_mail_notes_extract_text_content (CamelMimePart *part)
 }
 
 static void
-e_mail_notes_extract_text_from_multipart_alternative (EHTMLEditorView *view,
+e_mail_notes_extract_text_from_multipart_alternative (EContentEditor *cnt_editor,
                                                      CamelMultipart *in_multipart)
 {
        guint ii, nparts;
 
-       g_return_if_fail (E_IS_HTML_EDITOR_VIEW (view));
+       g_return_if_fail (E_IS_CONTENT_EDITOR (cnt_editor));
        g_return_if_fail (CAMEL_IS_MULTIPART (in_multipart));
 
        nparts = camel_multipart_get_number (in_multipart);
@@ -122,8 +122,12 @@ e_mail_notes_extract_text_from_multipart_alternative (EHTMLEditorView *view,
 
                        text = e_mail_notes_extract_text_content (part);
                        if (text) {
-                               e_html_editor_view_set_html_mode (view, TRUE);
-                               e_html_editor_view_set_text_html (view, text);
+                               e_content_editor_set_html_mode (cnt_editor, TRUE);
+                               e_content_editor_insert_content (
+                                       cnt_editor,
+                                       text,
+                                       E_CONTENT_EDITOR_INSERT_TEXT_HTML |
+                                       E_CONTENT_EDITOR_INSERT_REPLACE_ALL);
                                g_free (text);
                                break;
                        }
@@ -132,7 +136,11 @@ e_mail_notes_extract_text_from_multipart_alternative (EHTMLEditorView *view,
 
                        text = e_mail_notes_extract_text_content (part);
                        if (text) {
-                               e_html_editor_view_set_text_plain (view, text);
+                               e_content_editor_insert_content (
+                                       cnt_editor,
+                                       text,
+                                       E_CONTENT_EDITOR_INSERT_TEXT_PLAIN |
+                                       E_CONTENT_EDITOR_INSERT_REPLACE_ALL);
                                g_free (text);
                        }
                        break;
@@ -144,13 +152,13 @@ static void
 e_mail_notes_editor_extract_text_from_multipart_related (EMailNotesEditor *notes_editor,
                                                         CamelMultipart *multipart)
 {
-       EHTMLEditorView *view;
+       EContentEditor *cnt_editor;
        guint ii, nparts;
 
        g_return_if_fail (E_IS_MAIL_NOTES_EDITOR (notes_editor));
        g_return_if_fail (CAMEL_IS_MULTIPART (multipart));
 
-       view = e_html_editor_get_view (notes_editor->editor);
+       cnt_editor = e_html_editor_get_content_editor (notes_editor->editor);
        nparts = camel_multipart_get_number (multipart);
 
        for (ii = 0; ii < nparts; ii++) {
@@ -167,12 +175,11 @@ e_mail_notes_editor_extract_text_from_multipart_related (EMailNotesEditor *notes
                        continue;
 
                if (camel_content_type_is (ct, "image", "*")) {
-                       e_html_editor_view_add_inline_image_from_mime_part (view, part);
+                       e_content_editor_insert_image_from_mime_part (cnt_editor, part);
                } else if (camel_content_type_is (ct, "multipart", "alternative")) {
                        content = camel_medium_get_content (CAMEL_MEDIUM (part));
-                       if (CAMEL_IS_MULTIPART (content)) {
-                               e_mail_notes_extract_text_from_multipart_alternative (view, CAMEL_MULTIPART 
(content));
-                       }
+                       if (CAMEL_IS_MULTIPART (content))
+                               e_mail_notes_extract_text_from_multipart_alternative (cnt_editor, 
CAMEL_MULTIPART (content));
                }
        }
 }
@@ -183,7 +190,7 @@ e_mail_notes_editor_extract_text_from_part (EMailNotesEditor *notes_editor,
 {
        CamelContentType *ct;
        CamelDataWrapper *content;
-       EHTMLEditorView *view;
+       EContentEditor *cnt_editor;
 
        g_return_if_fail (E_IS_MAIL_NOTES_EDITOR (notes_editor));
        g_return_if_fail (CAMEL_IS_MIME_PART (part));
@@ -194,7 +201,7 @@ e_mail_notes_editor_extract_text_from_part (EMailNotesEditor *notes_editor,
        g_return_if_fail (content != NULL);
        g_return_if_fail (ct != NULL);
 
-       view = e_html_editor_get_view (notes_editor->editor);
+       cnt_editor = e_html_editor_get_content_editor (notes_editor->editor);
 
        if (camel_content_type_is (ct, "multipart", "related")) {
                g_return_if_fail (CAMEL_IS_MULTIPART (content));
@@ -202,14 +209,18 @@ e_mail_notes_editor_extract_text_from_part (EMailNotesEditor *notes_editor,
                e_mail_notes_editor_extract_text_from_multipart_related (notes_editor, CAMEL_MULTIPART 
(content));
        } else if (camel_content_type_is (ct, "multipart", "alternative")) {
                if (CAMEL_IS_MULTIPART (content)) {
-                       e_mail_notes_extract_text_from_multipart_alternative (view, CAMEL_MULTIPART 
(content));
+                       e_mail_notes_extract_text_from_multipart_alternative (cnt_editor, CAMEL_MULTIPART 
(content));
                }
        } else if (camel_content_type_is (ct, "text", "plain")) {
                gchar *text;
 
                text = e_mail_notes_extract_text_content (part);
                if (text) {
-                       e_html_editor_view_set_text_plain (view, text);
+                       e_content_editor_insert_content (
+                               cnt_editor,
+                               text,
+                               E_CONTENT_EDITOR_INSERT_TEXT_PLAIN |
+                               E_CONTENT_EDITOR_INSERT_REPLACE_ALL);
                        g_free (text);
                }
        }
@@ -221,7 +232,7 @@ e_mail_notes_editor_extract_text_from_message (EMailNotesEditor *notes_editor,
 {
        CamelContentType *ct;
        CamelDataWrapper *content;
-       EHTMLEditorView *view;
+       EContentEditor *cnt_editor;
 
        g_return_if_fail (E_IS_MAIL_NOTES_EDITOR (notes_editor));
        g_return_if_fail (CAMEL_IS_MIME_MESSAGE (message));
@@ -232,7 +243,7 @@ e_mail_notes_editor_extract_text_from_message (EMailNotesEditor *notes_editor,
        g_return_if_fail (content != NULL);
        g_return_if_fail (ct != NULL);
 
-       view = e_html_editor_get_view (notes_editor->editor);
+       cnt_editor = e_html_editor_get_content_editor (notes_editor->editor);
 
        if (camel_content_type_is (ct, "multipart", "mixed")) {
                EAttachmentStore *attachment_store;
@@ -276,13 +287,13 @@ e_mail_notes_editor_extract_text_from_message (EMailNotesEditor *notes_editor,
                e_mail_notes_editor_extract_text_from_part (notes_editor, CAMEL_MIME_PART (message));
        }
 
-       e_html_editor_view_set_changed (view, FALSE);
+       e_content_editor_set_changed (cnt_editor, FALSE);
 }
 
 static CamelMimeMessage *
 e_mail_notes_editor_encode_text_to_message (EMailNotesEditor *notes_editor)
 {
-       EHTMLEditorView *view;
+       EContentEditor *cnt_editor;
        EAttachmentStore *attachment_store;
        CamelMimeMessage *message = NULL;
        gchar *message_uid;
@@ -293,8 +304,8 @@ e_mail_notes_editor_encode_text_to_message (EMailNotesEditor *notes_editor)
        g_return_val_if_fail (E_IS_MAIL_NOTES_EDITOR (notes_editor), NULL);
        g_return_val_if_fail (notes_editor->editor, NULL);
 
-       view = e_html_editor_get_view (notes_editor->editor);
-       g_return_val_if_fail (E_IS_HTML_EDITOR_VIEW (view), NULL);
+       cnt_editor = e_html_editor_get_content_editor (notes_editor->editor);
+       g_return_val_if_fail (E_IS_CONTENT_EDITOR (cnt_editor), NULL);
 
        message = camel_mime_message_new ();
        username = g_get_user_name ();
@@ -316,18 +327,23 @@ e_mail_notes_editor_encode_text_to_message (EMailNotesEditor *notes_editor)
        attachment_store = e_attachment_view_get_store (E_ATTACHMENT_VIEW (notes_editor->attachment_paned));
        has_attachments = e_attachment_store_get_num_attachments (attachment_store) > 0;
 
-       if (e_html_editor_view_get_html_mode (view)) {
+       if (e_content_editor_get_html_mode (cnt_editor)) {
                CamelMultipart *multipart_alternative;
                CamelMultipart *multipart_body;
                CamelMimePart *part;
-               GList *inline_images = NULL;
+               GSList *inline_images_parts = NULL;
                gchar *text;
 
                multipart_alternative = camel_multipart_new ();
                camel_data_wrapper_set_mime_type (CAMEL_DATA_WRAPPER (multipart_alternative), 
"multipart/alternative");
                camel_multipart_set_boundary (multipart_alternative, NULL);
 
-               text = e_html_editor_view_get_text_plain (view);
+               text = e_content_editor_get_content (
+                       cnt_editor,
+                       E_CONTENT_EDITOR_GET_TEXT_PLAIN |
+                       E_CONTENT_EDITOR_GET_PROCESSED,
+                       NULL, NULL);
+
                if (text && *text) {
                        part = camel_mime_part_new ();
                        camel_mime_part_set_content (part, text, strlen (text), "text/plain");
@@ -340,7 +356,14 @@ e_mail_notes_editor_encode_text_to_message (EMailNotesEditor *notes_editor)
 
                g_free (text);
 
-               text = e_html_editor_view_get_text_html (view, g_get_host_name (), &inline_images);
+               text = e_content_editor_get_content (
+                       cnt_editor,
+                       E_CONTENT_EDITOR_GET_PROCESSED |
+                       E_CONTENT_EDITOR_GET_TEXT_HTML |
+                       E_CONTENT_EDITOR_GET_INLINE_IMAGES,
+                       g_get_host_name (),
+                       &inline_images_parts);
+
                if (has_attachments && !has_text && (!text || !*text)) {
                        /* Text is required, thus if there are attachments,
                           but no text, then store at least a space. */
@@ -357,14 +380,14 @@ e_mail_notes_editor_encode_text_to_message (EMailNotesEditor *notes_editor)
 
                        has_text = TRUE;
                } else {
-                       g_list_free_full (inline_images, g_object_unref);
-                       inline_images = NULL;
+                       g_slist_free_full (inline_images_parts, g_object_unref);
+                       inline_images_parts = NULL;
                }
 
                g_free (text);
 
-               if (inline_images) {
-                       GList *link;
+               if (inline_images_parts) {
+                       GSList *link;
 
                        multipart_body = camel_multipart_new ();
                        camel_data_wrapper_set_mime_type (CAMEL_DATA_WRAPPER (multipart_body), 
"multipart/related");
@@ -375,7 +398,7 @@ e_mail_notes_editor_encode_text_to_message (EMailNotesEditor *notes_editor)
                        camel_multipart_add_part (multipart_body, part);
                        g_object_unref (part);
 
-                       for (link = inline_images; link; link = g_list_next (link)) {
+                       for (link = inline_images_parts; link; link = g_slist_next (link)) {
                                CamelMimePart *part = link->data;
 
                                if (!part)
@@ -408,13 +431,17 @@ e_mail_notes_editor_encode_text_to_message (EMailNotesEditor *notes_editor)
 
                camel_medium_set_content (CAMEL_MEDIUM (message), CAMEL_DATA_WRAPPER (multipart_body));
 
-               g_list_free_full (inline_images, g_object_unref);
+               g_slist_free_full (inline_images_parts, g_object_unref);
                g_clear_object (&multipart_alternative);
                g_clear_object (&multipart_body);
        } else {
                gchar *text;
 
-               text = e_html_editor_view_get_text_plain (view);
+               text = e_content_editor_get_content (
+                       cnt_editor,
+                       E_CONTENT_EDITOR_GET_TEXT_PLAIN |
+                       E_CONTENT_EDITOR_GET_PROCESSED,
+                       NULL, NULL);
 
                if (has_attachments && !has_text && (!text || !*text)) {
                        /* Text is required, thus if there are attachments,
@@ -560,17 +587,17 @@ notes_editor_activity_notify_cb (EActivityBar *activity_bar,
                                 GParamSpec *param,
                                 EMailNotesEditor *notes_editor)
 {
-       EHTMLEditorView *view;
+       EContentEditor *cnt_editor;
        GtkAction *action;
        gboolean can_edit;
 
        g_return_if_fail (E_IS_ACTIVITY_BAR (activity_bar));
        g_return_if_fail (E_IS_MAIL_NOTES_EDITOR (notes_editor));
 
-       view = e_html_editor_get_view (notes_editor->editor);
+       cnt_editor = e_html_editor_get_content_editor (notes_editor->editor);
        can_edit = notes_editor->had_message && !e_activity_bar_get_activity (activity_bar);
 
-       webkit_web_view_set_editable (WEBKIT_WEB_VIEW (view), can_edit);
+       g_object_set (cnt_editor, "editable", can_edit, NULL);
 
        action = gtk_action_group_get_action (notes_editor->action_group, "save-and-close");
        gtk_action_set_sensitive (action, can_edit);
@@ -711,12 +738,12 @@ static void
 action_close_cb (GtkAction *action,
                 EMailNotesEditor *notes_editor)
 {
-       EHTMLEditorView *view;
+       EContentEditor *cnt_editor;
        gboolean something_changed = FALSE;
 
-       view = e_html_editor_get_view (notes_editor->editor);
+       cnt_editor = e_html_editor_get_content_editor (notes_editor->editor);
 
-       something_changed = webkit_web_view_can_undo (WEBKIT_WEB_VIEW (view));
+       something_changed = e_content_editor_get_changed (cnt_editor);
 
        if (something_changed) {
                gint response;
@@ -874,9 +901,10 @@ e_mail_notes_editor_init (EMailNotesEditor *notes_editor)
 }
 
 static EMailNotesEditor *
-e_mail_notes_editor_new (GtkWindow *parent,
-                        CamelFolder *folder,
-                        const gchar *uid)
+e_mail_notes_editor_new_with_editor (EHTMLEditor *html_editor,
+                                    GtkWindow *parent,
+                                    CamelFolder *folder,
+                                    const gchar *uid)
 {
        const gchar *ui =
                "<ui>\n"
@@ -921,7 +949,7 @@ e_mail_notes_editor_new (GtkWindow *parent,
        };
 
        EMailNotesEditor *notes_editor;
-       EHTMLEditorView *view;
+       EContentEditor *cnt_editor;
        EFocusTracker *focus_tracker;
        EActivityBar *activity_bar;
        GtkUIManager *ui_manager;
@@ -948,10 +976,10 @@ e_mail_notes_editor_new (GtkWindow *parent,
 
        content = widget;
 
-       widget = e_html_editor_new ();
+       widget = GTK_WIDGET (html_editor);
 
-       notes_editor->editor = E_HTML_EDITOR (widget);
-       view = e_html_editor_get_view (notes_editor->editor);
+       notes_editor->editor = html_editor;
+       cnt_editor = e_html_editor_get_content_editor (notes_editor->editor);
        ui_manager = e_html_editor_get_ui_manager (notes_editor->editor);
 
        /* Because we are loading from a hard-coded string, there is
@@ -987,6 +1015,12 @@ e_mail_notes_editor_new (GtkWindow *parent,
        gtk_widget_show (widget);
 
        widget = GTK_WIDGET (notes_editor->editor);
+       g_object_set (G_OBJECT (widget),
+               "halign", GTK_ALIGN_FILL,
+               "hexpand", TRUE,
+               "valign", GTK_ALIGN_FILL,
+               "vexpand", TRUE,
+               NULL);
        gtk_box_pack_start (GTK_BOX (content), widget, TRUE, TRUE, 0);
        gtk_widget_show (widget);
 
@@ -996,7 +1030,7 @@ e_mail_notes_editor_new (GtkWindow *parent,
        gtk_widget_show (widget);
 
        e_binding_bind_property (
-               view, "editable",
+               cnt_editor, "editable",
                widget, "sensitive",
                G_BINDING_SYNC_CREATE);
 
@@ -1017,10 +1051,11 @@ e_mail_notes_editor_new (GtkWindow *parent,
 
        notes_editor->focus_tracker = focus_tracker;
 
-       gtk_widget_grab_focus (GTK_WIDGET (view));
+       gtk_widget_grab_focus (GTK_WIDGET (cnt_editor));
 
        settings = e_util_ref_settings ("org.gnome.evolution.mail");
-       e_html_editor_view_set_html_mode (view, g_settings_get_boolean (settings, "composer-send-html"));
+       e_content_editor_set_html_mode (
+               cnt_editor, g_settings_get_boolean (settings, "composer-send-html"));
        g_object_unref (settings);
 
        g_signal_connect (
@@ -1039,29 +1074,79 @@ e_mail_notes_editor_new (GtkWindow *parent,
        return notes_editor;
 }
 
+typedef struct _AsyncData {
+       GtkWindow *parent;
+       CamelFolder *folder;
+       gchar *uid;
+} AsyncData;
+
+static void
+async_data_free (gpointer ptr)
+{
+       AsyncData *ad = ptr;
+
+       if (ad) {
+               g_clear_object (&ad->parent);
+               g_clear_object (&ad->folder);
+               g_free (ad->uid);
+               g_free (ad);
+       }
+}
+
+static void
+e_mail_notes_editor_ready_cb (GObject *source_object,
+                             GAsyncResult *result,
+                             gpointer user_data)
+{
+       AsyncData *ad = user_data;
+       GtkWidget *html_editor;
+       GError *error = NULL;
+
+       g_return_if_fail (result != NULL);
+       g_return_if_fail (ad != NULL);
+
+       html_editor = e_html_editor_new_finish (result, &error);
+       if (error) {
+               g_warning ("%s: Failed to create HTML editor: %s", G_STRFUNC, error->message);
+               g_clear_error (&error);
+       } else {
+               EMailNotesEditor *notes_editor;
+               EActivityBar *activity_bar;
+               EActivity *activity;
+
+               notes_editor = e_mail_notes_editor_new_with_editor (E_HTML_EDITOR (html_editor),
+                       ad->parent, ad->folder, ad->uid);
+
+               activity_bar = e_html_editor_get_activity_bar (notes_editor->editor);
+               activity = e_alert_sink_submit_thread_job (E_ALERT_SINK (notes_editor->editor),
+                       _("Retrieving message..."), "mail:no-retrieve-message", NULL,
+                       e_mail_notes_retrieve_message_thread,
+                       g_object_ref (notes_editor), e_mail_notes_retrieve_message_done);
+               e_activity_bar_set_activity (activity_bar, activity);
+               g_clear_object (&activity);
+
+               gtk_widget_show (GTK_WIDGET (notes_editor));
+       }
+
+       async_data_free (ad);
+}
+
 void
 e_mail_notes_edit (GtkWindow *parent,
                   CamelFolder *folder,
                   const gchar *uid)
 {
-       EMailNotesEditor *notes_editor;
-       EActivityBar *activity_bar;
-       EActivity *activity;
+       AsyncData *ad;
 
        g_return_if_fail (CAMEL_IS_FOLDER (folder));
        g_return_if_fail (uid != NULL);
 
-       notes_editor = e_mail_notes_editor_new (parent, folder, uid);
-
-       activity_bar = e_html_editor_get_activity_bar (notes_editor->editor);
-       activity = e_alert_sink_submit_thread_job (E_ALERT_SINK (notes_editor->editor),
-               _("Retrieving message..."), "mail:no-retrieve-message", NULL,
-               e_mail_notes_retrieve_message_thread,
-               g_object_ref (notes_editor), e_mail_notes_retrieve_message_done);
-       e_activity_bar_set_activity (activity_bar, activity);
-       g_clear_object (&activity);
+       ad = g_new0 (AsyncData, 1);
+       ad->parent = parent ? g_object_ref (parent) : NULL;
+       ad->folder = g_object_ref (folder);
+       ad->uid = g_strdup (uid);
 
-       gtk_widget_show (GTK_WIDGET (notes_editor));
+       e_html_editor_new (e_mail_notes_editor_ready_cb, ad);
 }
 
 gboolean
diff --git a/mail/e-mail-paned-view.c b/mail/e-mail-paned-view.c
index 47bc059..cf5635e 100644
--- a/mail/e-mail-paned-view.c
+++ b/mail/e-mail-paned-view.c
@@ -675,7 +675,7 @@ mail_paned_view_constructed (GObject *object)
        EMailView *view;
        GtkWidget *message_list;
        GtkWidget *container;
-       GtkWidget *widget;
+       GtkWidget *widget, *vbox;
 
        priv = E_MAIL_PANED_VIEW_GET_PRIVATE (object);
 
@@ -737,8 +737,13 @@ mail_paned_view_constructed (GObject *object)
 
        container = priv->paned;
 
+       vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 2);
        widget = e_preview_pane_new (E_WEB_VIEW (priv->display));
-       gtk_paned_pack2 (GTK_PANED (container), widget, FALSE, FALSE);
+
+       gtk_box_pack_start (GTK_BOX (vbox), widget, TRUE, TRUE, 0);
+       gtk_box_pack_start (GTK_BOX (vbox), GTK_WIDGET (e_mail_display_get_attachment_view (priv->display)), 
FALSE, FALSE, 0);
+
+       gtk_paned_pack2 (GTK_PANED (container), vbox, FALSE, FALSE);
        priv->preview_pane = g_object_ref (widget);
        gtk_widget_show (GTK_WIDGET (priv->display));
        gtk_widget_show (widget);
@@ -748,6 +753,11 @@ mail_paned_view_constructed (GObject *object)
                widget, "visible",
                G_BINDING_SYNC_CREATE);
 
+       e_binding_bind_property (
+               object, "preview-visible",
+               vbox, "visible",
+               G_BINDING_SYNC_CREATE);
+
        /* Load the view instance. */
 
        e_mail_view_update_view_instance (E_MAIL_VIEW (object));
diff --git a/mail/e-mail-printer.c b/mail/e-mail-printer.c
index 2208ad6..15ab8b8 100644
--- a/mail/e-mail-printer.c
+++ b/mail/e-mail-printer.c
@@ -23,7 +23,7 @@
 #include <glib/gi18n.h>
 #include <gtk/gtk.h>
 
-#include <webkit/webkitdom.h>
+#include <camel/camel.h>
 
 #include "e-util/e-util.h"
 
@@ -49,9 +49,6 @@ struct _EMailPrinterPrivate {
 
        gchar *export_filename;
 
-       WebKitWebView *web_view; /* WebView to print from */
-
-       GtkPrintOperation *operation;
        GtkPrintOperationAction print_action;
 };
 
@@ -101,8 +98,9 @@ async_context_free (AsyncContext *async_context)
        g_slice_free (AsyncContext, async_context);
 }
 
+#if 0 /* FIXME WK2 */
 static GtkWidget *
-mail_printer_create_custom_widget_cb (GtkPrintOperation *operation,
+mail_printer_create_custom_widget_cb (WebKitPrintOperation *operation,
                                       AsyncContext *async_context)
 {
        EMailDisplay *display;
@@ -110,7 +108,7 @@ mail_printer_create_custom_widget_cb (GtkPrintOperation *operation,
        EMailPart *part;
        GtkWidget *widget;
 
-       gtk_print_operation_set_custom_tab_label (operation, _("Headers"));
+       webkit_print_operation_set_custom_tab_label (operation, _("Headers"));
 
        display = E_MAIL_DISPLAY (async_context->web_view);
        part_list = e_mail_display_get_part_list (display);
@@ -128,7 +126,7 @@ mail_printer_create_custom_widget_cb (GtkPrintOperation *operation,
 }
 
 static void
-mail_printer_custom_widget_apply_cb (GtkPrintOperation *operation,
+mail_printer_custom_widget_apply_cb (WebKitPrintOperation *operation,
                                      GtkWidget *widget,
                                      AsyncContext *async_context)
 {
@@ -171,6 +169,33 @@ mail_printer_draw_footer_cb (GtkPrintOperation *operation,
        g_object_unref (layout);
        g_free (text);
 }
+#endif
+static void
+mail_printer_print_finished_cb (WebKitPrintOperation *print_operation,
+                                GSimpleAsyncResult *simple)
+{
+       if (camel_debug ("wex"))
+               printf ("%s\n", G_STRFUNC);
+}
+
+static void
+mail_printer_print_failed_cb (WebKitPrintOperation *print_operation,
+                              GError *error,
+                              GSimpleAsyncResult *simple)
+{
+       AsyncContext *async_context;
+
+       if (camel_debug ("wex"))
+               printf ("%s\n", G_STRFUNC);
+       async_context = g_simple_async_result_get_op_res_gpointer (simple);
+
+       if (error != NULL)
+               g_simple_async_result_take_error (simple, error);
+       else
+               g_warning ("WebKit print operation returned ERROR result without setting a GError");
+
+       async_context->print_result = GTK_PRINT_OPERATION_RESULT_ERROR;
+}
 
 static gboolean
 mail_printer_print_timeout_cb (gpointer user_data)
@@ -178,34 +203,32 @@ mail_printer_print_timeout_cb (gpointer user_data)
        GSimpleAsyncResult *simple;
        AsyncContext *async_context;
        GCancellable *cancellable;
-       GtkPrintOperation *print_operation;
+       WebKitPrintOperation *print_operation;
+/* FIXME WK2   EMailPrinter *printer;
        GtkPrintOperationAction print_action;
-       EMailPrinter *printer;
-       WebKitWebFrame *web_frame;
-       gulong create_custom_widget_handler_id;
-       gulong custom_widget_apply_handler_id;
        gulong draw_page_handler_id;
+       gulong create_custom_widget_handler_id;
+       gulong custom_widget_apply_handler_id;*/
        GError *error = NULL;
 
        simple = G_SIMPLE_ASYNC_RESULT (user_data);
        async_context = g_simple_async_result_get_op_res_gpointer (simple);
 
        cancellable = async_context->cancellable;
+       /*
        print_action = async_context->print_action;
-
+*/
        /* Check for cancellation one last time before printing. */
        if (g_cancellable_set_error_if_cancelled (cancellable, &error))
                goto exit;
 
        /* This returns a new reference. */
+/*
        printer = (EMailPrinter *) g_async_result_get_source_object (
                G_ASYNC_RESULT (simple));
-
-       print_operation = e_print_operation_new ();
-
-       gtk_print_operation_set_show_progress (print_operation, TRUE);
-       gtk_print_operation_set_unit (print_operation, GTK_UNIT_PIXEL);
-
+*/
+       print_operation = webkit_print_operation_new (async_context->web_view);
+/*
        if (async_context->print_action == GTK_PRINT_OPERATION_ACTION_EXPORT) {
                const gchar *export_filename;
 
@@ -214,7 +237,8 @@ mail_printer_print_timeout_cb (gpointer user_data)
                gtk_print_operation_set_export_filename (
                        print_operation, export_filename);
        }
-
+*/
+/*
        create_custom_widget_handler_id = g_signal_connect (
                print_operation, "create-custom-widget",
                G_CALLBACK (mail_printer_create_custom_widget_cb),
@@ -224,42 +248,29 @@ mail_printer_print_timeout_cb (gpointer user_data)
                print_operation, "custom-widget-apply",
                G_CALLBACK (mail_printer_custom_widget_apply_cb),
                async_context);
+*/
+       g_signal_connect (
+               print_operation, "failed",
+               G_CALLBACK (mail_printer_print_failed_cb),
+               async_context);
+
+       g_signal_connect (
+               print_operation, "finished",
+               G_CALLBACK (mail_printer_print_finished_cb),
+               async_context);
 
+/* FIXME WK2 - this will be hard to add back to WK2 API.. There is a CSS draft
+ * that can be used to add a page numbers, but it is not in WebKit yet.
+ * http://www.w3.org/TR/css3-page/
        draw_page_handler_id = g_signal_connect (
                print_operation, "draw-page",
                G_CALLBACK (mail_printer_draw_footer_cb),
                async_context->cancellable);
-
-       web_frame = webkit_web_view_get_main_frame (async_context->web_view);
-
-       async_context->print_result = webkit_web_frame_print_full (
-               web_frame, print_operation, print_action, &error);
-
-       /* Sanity check. */
-       switch (async_context->print_result) {
-               case GTK_PRINT_OPERATION_RESULT_ERROR:
-                       if (error == NULL)
-                               g_warning (
-                                       "WebKit print operation returned "
-                                       "ERROR result without setting a "
-                                       "GError");
-                       break;
-               case GTK_PRINT_OPERATION_RESULT_APPLY:
-                       if (error != NULL)
-                               g_warning (
-                                       "WebKit print operation returned "
-                                       "APPLY result but also set a GError");
-                       break;
-               case GTK_PRINT_OPERATION_RESULT_CANCEL:
-                       if (error != NULL)
-                               g_warning (
-                                       "WebKit print operation returned "
-                                       "CANCEL result but also set a GError");
-                       break;
-               default:
-                       g_warn_if_reached ();
-       }
-
+*/
+       webkit_print_operation_run_dialog (
+               print_operation,
+               GTK_WINDOW (gtk_widget_get_toplevel (gtk_widget_get_toplevel (GTK_WIDGET 
(async_context->web_view)))));
+/* FIXME WK2
        g_signal_handler_disconnect (
                print_operation, create_custom_widget_handler_id);
 
@@ -268,10 +279,11 @@ mail_printer_print_timeout_cb (gpointer user_data)
 
        g_signal_handler_disconnect (
                print_operation, draw_page_handler_id);
-
+*/
        g_object_unref (print_operation);
 
-       g_object_unref (printer);
+/*
+       g_object_unref (printer);*/
 
 exit:
        if (error != NULL)
@@ -283,18 +295,16 @@ exit:
 }
 
 static void
-mail_printer_load_status_cb (WebKitWebView *web_view,
-                             GParamSpec *pspec,
-                             GSimpleAsyncResult *simple)
+mail_printer_load_changed_cb (WebKitWebView *web_view,
+                              WebKitLoadEvent load_event,
+                              GSimpleAsyncResult *simple)
 {
        AsyncContext *async_context;
-       WebKitLoadStatus load_status;
        GCancellable *cancellable;
        GError *error = NULL;
 
        /* Note: we disregard WEBKIT_LOAD_FAILED and print what we can. */
-       load_status = webkit_web_view_get_load_status (web_view);
-       if (load_status != WEBKIT_LOAD_FINISHED)
+       if (load_event != WEBKIT_LOAD_FINISHED)
                return;
 
        /* Signal handlers are holding the only GSimpleAsyncResult
@@ -341,7 +351,7 @@ mail_printer_new_web_view (const gchar *charset,
                            const gchar *default_charset)
 {
        WebKitWebView *web_view;
-       WebKitWebSettings *web_settings;
+       WebKitSettings *web_settings;
        EMailFormatter *formatter;
 
        web_view = g_object_new (
@@ -446,8 +456,6 @@ mail_printer_dispose (GObject *object)
        g_clear_object (&priv->formatter);
        g_clear_object (&priv->part_list);
        g_clear_object (&priv->remote_content);
-       g_clear_object (&priv->web_view);
-       g_clear_object (&priv->operation);
 
        /* Chain up to parent's dispose() method. */
        G_OBJECT_CLASS (e_mail_printer_parent_class)->dispose (object);
@@ -587,8 +595,8 @@ e_mail_printer_print (EMailPrinter *printer,
        async_context->web_view = g_object_ref_sink (web_view);
 
        handler_id = g_signal_connect_data (
-               web_view, "notify::load-status",
-               G_CALLBACK (mail_printer_load_status_cb),
+               web_view, "load-changed",
+               G_CALLBACK (mail_printer_load_changed_cb),
                g_object_ref (simple),
                (GClosureNotify) g_object_unref, 0);
        async_context->load_status_handler_id = handler_id;
diff --git a/mail/e-mail-reader-utils.c b/mail/e-mail-reader-utils.c
index fed2e85..c850bba 100644
--- a/mail/e-mail-reader-utils.c
+++ b/mail/e-mail-reader-utils.c
@@ -1729,6 +1729,76 @@ e_mail_reader_remove_duplicates (EMailReader *reader)
        g_ptr_array_unref (uids);
 }
 
+typedef struct _CreateComposerData {
+       EMailReader *reader;
+       CamelFolder *folder;
+       CamelMimeMessage *message;
+       gchar *message_uid;
+       gboolean keep_signature;
+
+       EMailPartList *part_list;
+       EMailReplyType reply_type;
+       EMailReplyStyle reply_style;
+       CamelInternetAddress *address;
+       EMailPartValidityFlags validity_pgp_sum;
+       EMailPartValidityFlags validity_smime_sum;
+
+       EMailForwardStyle forward_style;
+
+       CamelMimePart *attached_part;
+       gchar *attached_subject;
+       GPtrArray *attached_uids;
+} CreateComposerData;
+
+static void
+create_composer_data_free (CreateComposerData *ccd)
+{
+       if (ccd) {
+               if (ccd->attached_uids)
+                       g_ptr_array_unref (ccd->attached_uids);
+
+               g_clear_object (&ccd->reader);
+               g_clear_object (&ccd->folder);
+               g_clear_object (&ccd->message);
+               g_clear_object (&ccd->part_list);
+               g_clear_object (&ccd->address);
+               g_clear_object (&ccd->attached_part);
+               g_free (ccd->message_uid);
+               g_free (ccd->attached_subject);
+               g_free (ccd);
+       }
+}
+
+static void
+mail_reader_edit_messages_composer_created_cb (GObject *source_object,
+                                              GAsyncResult *result,
+                                              gpointer user_data)
+{
+       CreateComposerData *ccd = user_data;
+       EMsgComposer *composer;
+       GError *error = NULL;
+
+       g_return_if_fail (ccd != NULL);
+
+       composer = e_msg_composer_new_finish (result, &error);
+       if (error) {
+               g_warning ("%s: Failed to create msg composer: %s", G_STRFUNC, error->message);
+               g_clear_error (&error);
+       } else {
+               camel_medium_remove_header (
+                       CAMEL_MEDIUM (ccd->message), "X-Mailer");
+
+               em_utils_edit_message (
+                       composer, ccd->folder, ccd->message, ccd->message_uid,
+                       ccd->keep_signature);
+
+               e_mail_reader_composer_created (
+                       ccd->reader, composer, ccd->message);
+       }
+
+       create_composer_data_free (ccd);
+}
+
 static void
 mail_reader_edit_messages_cb (GObject *source_object,
                               GAsyncResult *result,
@@ -1780,24 +1850,17 @@ mail_reader_edit_messages_cb (GObject *source_object,
        g_hash_table_iter_init (&iter, hash_table);
 
        while (g_hash_table_iter_next (&iter, &key, &value)) {
-               EMsgComposer *composer;
-               CamelMimeMessage *message;
-               const gchar *message_uid = NULL;
-
-               if (async_context->replace)
-                       message_uid = (const gchar *) key;
-
-               message = CAMEL_MIME_MESSAGE (value);
+               CreateComposerData *ccd;
 
-               camel_medium_remove_header (
-                       CAMEL_MEDIUM (message), "X-Mailer");
+               ccd = g_new0 (CreateComposerData, 1);
+               ccd->reader = g_object_ref (async_context->reader);
+               ccd->folder = g_object_ref (folder);
+               ccd->message = g_object_ref (CAMEL_MIME_MESSAGE (value));
 
-               composer = em_utils_edit_message (
-                       shell, folder, message, message_uid,
-                       async_context->keep_signature);
+               if (async_context->replace)
+                       ccd->message_uid = g_strdup ((const gchar *) key);
 
-               e_mail_reader_composer_created (
-                       async_context->reader, composer, message);
+               e_msg_composer_new (shell, mail_reader_edit_messages_composer_created_cb, ccd);
        }
 
        g_hash_table_unref (hash_table);
@@ -1843,6 +1906,44 @@ e_mail_reader_edit_messages (EMailReader *reader,
 }
 
 static void
+mail_reader_forward_attached_composer_created_cb (GObject *source_object,
+                                                 GAsyncResult *result,
+                                                 gpointer user_data)
+{
+       CreateComposerData *ccd = user_data;
+       EMsgComposer *composer;
+       GError *error = NULL;
+
+       composer = e_msg_composer_new_finish (result, &error);
+       if (error) {
+               g_warning ("%s: Failed to create msg composer: %s", G_STRFUNC, error->message);
+               g_clear_error (&error);
+       } else {
+               CamelDataWrapper *content;
+
+               em_utils_forward_attachment (composer, ccd->attached_part, ccd->attached_subject, 
ccd->folder, ccd->attached_uids);
+
+               content = camel_medium_get_content (CAMEL_MEDIUM (ccd->attached_part));
+               if (CAMEL_IS_MIME_MESSAGE (content)) {
+                       e_mail_reader_composer_created (ccd->reader, composer, CAMEL_MIME_MESSAGE (content));
+               } else {
+                       /* XXX What to do for the multipart/digest case?
+                        *     Extract the first message from the digest, or
+                        *     change the argument type to CamelMimePart and
+                        *     just pass the whole digest through?
+                        *
+                        *     This signal is primarily serving EMailBrowser,
+                        *     which can only forward one message at a time.
+                        *     So for the moment it doesn't matter, but still
+                        *     something to consider. */
+                       e_mail_reader_composer_created (ccd->reader, composer, NULL);
+               }
+       }
+
+       create_composer_data_free (ccd);
+}
+
+static void
 mail_reader_forward_attachment_cb (GObject *source_object,
                                    GAsyncResult *result,
                                    gpointer user_data)
@@ -1851,9 +1952,9 @@ mail_reader_forward_attachment_cb (GObject *source_object,
        EMailBackend *backend;
        EActivity *activity;
        EAlertSink *alert_sink;
+       EShell *shell;
        CamelMimePart *part;
-       CamelDataWrapper *content;
-       EMsgComposer *composer;
+       CreateComposerData *ccd;
        gchar *subject = NULL;
        AsyncContext *async_context;
        GError *local_error = NULL;
@@ -1887,40 +1988,53 @@ mail_reader_forward_attachment_cb (GObject *source_object,
                goto exit;
        }
 
+       ccd = g_new0 (CreateComposerData, 1);
+       ccd->reader = g_object_ref (async_context->reader);
+       ccd->folder = g_object_ref (folder);
+       ccd->attached_part = part;
+       ccd->attached_subject = subject;
+       ccd->attached_uids = async_context->uids ? g_ptr_array_ref (async_context->uids) : NULL;
+
        backend = e_mail_reader_get_backend (async_context->reader);
+       shell = e_shell_backend_get_shell (E_SHELL_BACKEND (backend));
 
-       composer = em_utils_forward_attachment (
-               backend, part, subject, folder, async_context->uids);
-
-       content = camel_medium_get_content (CAMEL_MEDIUM (part));
-       if (CAMEL_IS_MIME_MESSAGE (content)) {
-               e_mail_reader_composer_created (
-                       async_context->reader, composer,
-                       CAMEL_MIME_MESSAGE (content));
-       } else {
-               /* XXX What to do for the multipart/digest case?
-                *     Extract the first message from the digest, or
-                *     change the argument type to CamelMimePart and
-                *     just pass the whole digest through?
-                *
-                *     This signal is primarily serving EMailBrowser,
-                *     which can only forward one message at a time.
-                *     So for the moment it doesn't matter, but still
-                *     something to consider. */
-               e_mail_reader_composer_created (
-                       async_context->reader, composer, NULL);
-       }
+       e_msg_composer_new (shell, mail_reader_forward_attached_composer_created_cb, ccd);
 
        e_activity_set_state (activity, E_ACTIVITY_COMPLETED);
 
-       g_object_unref (part);
-       g_free (subject);
-
 exit:
        async_context_free (async_context);
 }
 
 static void
+mail_reader_forward_message_composer_created_cb (GObject *source_object,
+                                                GAsyncResult *result,
+                                                gpointer user_data)
+{
+       CreateComposerData *ccd = user_data;
+       EMsgComposer *composer;
+       GError *error = NULL;
+
+       g_return_if_fail (ccd != NULL);
+
+       composer = e_msg_composer_new_finish (result, &error);
+       if (error) {
+               g_warning ("%s: Failed to create msg composer: %s", G_STRFUNC, error->message);
+               g_clear_error (&error);
+       } else {
+               em_utils_forward_message (
+                       composer, ccd->message,
+                       ccd->forward_style,
+                       ccd->folder, ccd->message_uid);
+
+               e_mail_reader_composer_created (
+                       ccd->reader, composer, ccd->message);
+       }
+
+       create_composer_data_free (ccd);
+}
+
+static void
 mail_reader_forward_messages_cb (GObject *source_object,
                                  GAsyncResult *result,
                                  gpointer user_data)
@@ -1929,6 +2043,7 @@ mail_reader_forward_messages_cb (GObject *source_object,
        EMailBackend *backend;
        EActivity *activity;
        EAlertSink *alert_sink;
+       EShell *shell;
        GHashTable *hash_table;
        GHashTableIter iter;
        gpointer key, value;
@@ -1942,6 +2057,7 @@ mail_reader_forward_messages_cb (GObject *source_object,
        alert_sink = e_activity_get_alert_sink (activity);
 
        backend = e_mail_reader_get_backend (async_context->reader);
+       shell = e_shell_backend_get_shell (E_SHELL_BACKEND (backend));
 
        hash_table = e_mail_folder_get_multiple_messages_finish (
                folder, result, &local_error);
@@ -1969,20 +2085,21 @@ mail_reader_forward_messages_cb (GObject *source_object,
        g_hash_table_iter_init (&iter, hash_table);
 
        while (g_hash_table_iter_next (&iter, &key, &value)) {
-               EMsgComposer *composer;
+               CreateComposerData *ccd;
                CamelMimeMessage *message;
                const gchar *message_uid;
 
                message_uid = (const gchar *) key;
                message = CAMEL_MIME_MESSAGE (value);
 
-               composer = em_utils_forward_message (
-                       backend, message,
-                       async_context->forward_style,
-                       folder, message_uid);
+               ccd = g_new0 (CreateComposerData, 1);
+               ccd->reader = g_object_ref (async_context->reader);
+               ccd->folder = g_object_ref (folder);
+               ccd->message = g_object_ref (message);
+               ccd->message_uid = g_strdup (message_uid);
+               ccd->forward_style = async_context->forward_style;
 
-               e_mail_reader_composer_created (
-                       async_context->reader, composer, message);
+               e_msg_composer_new (shell, mail_reader_forward_message_composer_created_cb, ccd);
        }
 
        g_hash_table_unref (hash_table);
@@ -2087,6 +2204,41 @@ html_contains_nonwhitespace (const gchar *html,
 }
 
 static void
+mail_reader_reply_composer_created_cb (GObject *object,
+                                      GAsyncResult *result,
+                                      gpointer user_data)
+{
+       AsyncContext *async_context = user_data;
+       EMsgComposer *composer;
+       GError *error = NULL;
+
+       g_return_if_fail (async_context != NULL);
+
+       composer = e_msg_composer_new_finish (result, &error);
+       if (error) {
+               g_warning ("%s: Failed to create msg composer: %s", G_STRFUNC, error->message);
+               g_clear_error (&error);
+       } else {
+               CamelMimeMessage *message;
+
+               message = e_mail_part_list_get_message (async_context->part_list);
+
+               em_utils_reply_to_message (
+                       composer, message,
+                       async_context->folder,
+                       async_context->message_uid,
+                       async_context->reply_type,
+                       async_context->reply_style,
+                       async_context->part_list,
+                       async_context->address);
+
+               e_mail_reader_composer_created (async_context->reader, composer, message);
+       }
+
+       async_context_free (async_context);
+}
+
+static void
 mail_reader_reply_message_parsed (GObject *object,
                                   GAsyncResult *result,
                                   gpointer user_data)
@@ -2094,15 +2246,12 @@ mail_reader_reply_message_parsed (GObject *object,
        EShell *shell;
        EMailBackend *backend;
        EMailReader *reader = E_MAIL_READER (object);
-       EMailPartList *part_list;
-       EMsgComposer *composer;
-       CamelMimeMessage *message;
        AsyncContext *async_context;
        GError *local_error = NULL;
 
        async_context = (AsyncContext *) user_data;
 
-       part_list = e_mail_reader_parse_message_finish (reader, result, &local_error);
+       async_context->part_list = e_mail_reader_parse_message_finish (reader, result, &local_error);
 
        if (local_error) {
                g_warn_if_fail (g_error_matches (local_error, G_IO_ERROR, G_IO_ERROR_CANCELLED));
@@ -2113,25 +2262,10 @@ mail_reader_reply_message_parsed (GObject *object,
                return;
        }
 
-       message = e_mail_part_list_get_message (part_list);
-
        backend = e_mail_reader_get_backend (async_context->reader);
        shell = e_shell_backend_get_shell (E_SHELL_BACKEND (backend));
 
-       composer = em_utils_reply_to_message (
-               shell, message,
-               async_context->folder,
-               async_context->message_uid,
-               async_context->reply_type,
-               async_context->reply_style,
-               part_list,
-               async_context->address);
-
-       e_mail_reader_composer_created (reader, composer, message);
-
-       g_object_unref (part_list);
-
-       async_context_free (async_context);
+       e_msg_composer_new (shell, mail_reader_reply_composer_created_cb, async_context);
 }
 
 static void
@@ -2186,6 +2320,60 @@ mail_reader_get_message_ready_cb (GObject *source_object,
        g_object_unref (message);
 }
 
+static void
+mail_reader_reply_to_message_composer_created_cb (GObject *source_object,
+                                                 GAsyncResult *result,
+                                                 gpointer user_data)
+{
+       CreateComposerData *ccd = user_data;
+       EMsgComposer *composer;
+       GError *error = NULL;
+
+       g_return_if_fail (ccd != NULL);
+
+       composer = e_msg_composer_new_finish (result, &error);
+       if (error) {
+               g_warning ("%s: failed to create msg composer: %s", G_STRFUNC, error->message);
+               g_clear_error (&error);
+       } else {
+               em_utils_reply_to_message (
+                       composer, ccd->message, ccd->folder, ccd->message_uid,
+                       ccd->reply_type, ccd->reply_style, ccd->part_list, ccd->address);
+
+               if (ccd->validity_pgp_sum != 0 || ccd->validity_smime_sum != 0) {
+                       GtkToggleAction *action;
+
+                       if ((ccd->validity_pgp_sum & E_MAIL_PART_VALIDITY_PGP) != 0) {
+                               if ((ccd->validity_pgp_sum & E_MAIL_PART_VALIDITY_SIGNED) != 0) {
+                                       action = GTK_TOGGLE_ACTION (E_COMPOSER_ACTION_PGP_SIGN (composer));
+                                       gtk_toggle_action_set_active (action, TRUE);
+                               }
+
+                               if ((ccd->validity_pgp_sum & E_MAIL_PART_VALIDITY_ENCRYPTED) != 0) {
+                                       action = GTK_TOGGLE_ACTION (E_COMPOSER_ACTION_PGP_ENCRYPT (composer));
+                                       gtk_toggle_action_set_active (action, TRUE);
+                               }
+                       }
+
+                       if ((ccd->validity_smime_sum & E_MAIL_PART_VALIDITY_SMIME) != 0) {
+                               if ((ccd->validity_smime_sum & E_MAIL_PART_VALIDITY_SIGNED) != 0) {
+                                       action = GTK_TOGGLE_ACTION (E_COMPOSER_ACTION_SMIME_SIGN (composer));
+                                       gtk_toggle_action_set_active (action, TRUE);
+                               }
+
+                               if ((ccd->validity_smime_sum & E_MAIL_PART_VALIDITY_ENCRYPTED) != 0) {
+                                       action = GTK_TOGGLE_ACTION (E_COMPOSER_ACTION_SMIME_ENCRYPT 
(composer));
+                                       gtk_toggle_action_set_active (action, TRUE);
+                               }
+                       }
+               }
+
+               e_mail_reader_composer_created (ccd->reader, composer, ccd->message);
+       }
+
+       create_composer_data_free (ccd);
+}
+
 void
 e_mail_reader_reply_to_message (EMailReader *reader,
                                 CamelMimeMessage *src_message,
@@ -2208,7 +2396,7 @@ e_mail_reader_reply_to_message (EMailReader *reader,
        gint length;
        gchar *mail_uri;
        CamelObjectBag *registry;
-       EMsgComposer *composer;
+       CreateComposerData *ccd;
        EMailPartValidityFlags validity_pgp_sum = 0;
        EMailPartValidityFlags validity_smime_sum = 0;
 
@@ -2305,7 +2493,7 @@ e_mail_reader_reply_to_message (EMailReader *reader,
        if (!e_web_view_is_selection_active (web_view))
                goto whole_message;
 
-       selection = e_web_view_get_selection_html (web_view);
+       selection = e_web_view_get_selection_content_html_sync (web_view, NULL, NULL);
        if (selection == NULL || *selection == '\0')
                goto whole_message;
 
@@ -2338,42 +2526,16 @@ e_mail_reader_reply_to_message (EMailReader *reader,
                CAMEL_MIME_PART (new_message),
                selection, length, "text/html; charset=utf-8");
 
-       composer = em_utils_reply_to_message (
-               shell, new_message, folder, uid,
-               reply_type, reply_style, NULL, address);
-       if (validity_pgp_sum != 0 || validity_smime_sum != 0) {
-               GtkToggleAction *action;
-
-               if ((validity_pgp_sum & E_MAIL_PART_VALIDITY_PGP) != 0) {
-                       if ((validity_pgp_sum & E_MAIL_PART_VALIDITY_SIGNED) != 0) {
-                               action = GTK_TOGGLE_ACTION (E_COMPOSER_ACTION_PGP_SIGN (composer));
-                               gtk_toggle_action_set_active (action, TRUE);
-                       }
-
-                       if ((validity_pgp_sum & E_MAIL_PART_VALIDITY_ENCRYPTED) != 0) {
-                               action = GTK_TOGGLE_ACTION (E_COMPOSER_ACTION_PGP_ENCRYPT (composer));
-                               gtk_toggle_action_set_active (action, TRUE);
-                       }
-               }
-
-               if ((validity_smime_sum & E_MAIL_PART_VALIDITY_SMIME) != 0) {
-                       if ((validity_smime_sum & E_MAIL_PART_VALIDITY_SIGNED) != 0) {
-                               action = GTK_TOGGLE_ACTION (E_COMPOSER_ACTION_SMIME_SIGN (composer));
-                               gtk_toggle_action_set_active (action, TRUE);
-                       }
-
-                       if ((validity_smime_sum & E_MAIL_PART_VALIDITY_ENCRYPTED) != 0) {
-                               action = GTK_TOGGLE_ACTION (E_COMPOSER_ACTION_SMIME_ENCRYPT (composer));
-                               gtk_toggle_action_set_active (action, TRUE);
-                       }
-               }
-       }
-
-       e_mail_reader_composer_created (reader, composer, new_message);
+       ccd = g_new0 (CreateComposerData, 1);
+       ccd->reader = g_object_ref (reader);
+       ccd->folder = g_object_ref (folder);
+       ccd->message_uid = g_strdup (uid);
+       ccd->message = new_message;
+       ccd->reply_type = reply_type;
+       ccd->reply_style = reply_style;
+       ccd->address = address ? g_object_ref (address) : NULL;
 
-       g_object_unref (new_message);
-
-       g_free (selection);
+       e_msg_composer_new (shell, mail_reader_reply_to_message_composer_created_cb, ccd);
 
        goto exit;
 
@@ -2408,14 +2570,21 @@ whole_message:
                g_object_unref (activity);
 
        } else {
-               composer = em_utils_reply_to_message (
-                       shell, src_message, folder, uid,
-                       reply_type, reply_style, part_list, address);
-
-               e_mail_reader_composer_created (reader, composer, src_message);
+               ccd = g_new0 (CreateComposerData, 1);
+               ccd->reader = g_object_ref (reader);
+               ccd->folder = g_object_ref (folder);
+               ccd->message_uid = g_strdup (uid);
+               ccd->message = g_object_ref (src_message);
+               ccd->reply_type = reply_type;
+               ccd->reply_style = reply_style;
+               ccd->part_list = part_list ? g_object_ref (part_list) : NULL;
+               ccd->address = address ? g_object_ref (address) : NULL;
+
+               e_msg_composer_new (shell, mail_reader_reply_to_message_composer_created_cb, ccd);
        }
 
 exit:
+       g_free (selection);
        g_clear_object (&address);
        g_clear_object (&folder);
 }
diff --git a/mail/e-mail-reader.c b/mail/e-mail-reader.c
index b215f79..8fe32f4 100644
--- a/mail/e-mail-reader.c
+++ b/mail/e-mail-reader.c
@@ -915,6 +915,43 @@ action_mail_message_edit_cb (GtkAction *action,
        g_ptr_array_unref (uids);
 }
 
+typedef struct _CreateComposerData {
+       EMailReader *reader;
+       CamelMimeMessage *message;
+       CamelFolder *folder;
+       gboolean is_redirect;
+} CreateComposerData;
+
+static void
+mail_reader_new_composer_created_cb (GObject *source_object,
+                                    GAsyncResult *result,
+                                    gpointer user_data)
+{
+       CreateComposerData *ccd = user_data;
+       EMsgComposer *composer;
+       GError *error = NULL;
+
+       g_return_if_fail (ccd != NULL);
+
+       composer = e_msg_composer_new_finish (result, &error);
+       if (error) {
+               g_warning ("%s: Failed to create msg composer: %s", G_STRFUNC, error->message);
+               g_clear_error (&error);
+       } else {
+               if (ccd->is_redirect)
+                       em_utils_redirect_message (composer, ccd->message);
+               else
+                       em_utils_compose_new_message (composer, ccd->folder);
+
+               e_mail_reader_composer_created (ccd->reader, composer, ccd->message);
+       }
+
+       g_clear_object (&ccd->reader);
+       g_clear_object (&ccd->message);
+       g_clear_object (&ccd->folder);
+       g_free (ccd);
+}
+
 static void
 action_mail_message_new_cb (GtkAction *action,
                             EMailReader *reader)
@@ -923,7 +960,7 @@ action_mail_message_new_cb (GtkAction *action,
        EMailBackend *backend;
        EShellBackend *shell_backend;
        CamelFolder *folder;
-       EMsgComposer *composer;
+       CreateComposerData *ccd;
 
        folder = e_mail_reader_ref_folder (reader);
        backend = e_mail_reader_get_backend (reader);
@@ -931,11 +968,12 @@ action_mail_message_new_cb (GtkAction *action,
        shell_backend = E_SHELL_BACKEND (backend);
        shell = e_shell_backend_get_shell (shell_backend);
 
-       composer = em_utils_compose_new_message (shell, folder);
-
-       e_mail_reader_composer_created (reader, composer, NULL);
+       ccd = g_new0 (CreateComposerData, 1);
+       ccd->reader = g_object_ref (reader);
+       ccd->folder = folder;
+       ccd->is_redirect = FALSE;
 
-       g_clear_object (&folder);
+       e_msg_composer_new (shell, mail_reader_new_composer_created_cb, ccd);
 }
 
 static void
@@ -1140,7 +1178,7 @@ mail_reader_redirect_cb (CamelFolder *folder,
        EMailBackend *backend;
        EAlertSink *alert_sink;
        CamelMimeMessage *message;
-       EMsgComposer *composer;
+       CreateComposerData *ccd;
        GError *error = NULL;
 
        alert_sink = e_activity_get_alert_sink (closure->activity);
@@ -1168,11 +1206,12 @@ mail_reader_redirect_cb (CamelFolder *folder,
        backend = e_mail_reader_get_backend (closure->reader);
        shell = e_shell_backend_get_shell (E_SHELL_BACKEND (backend));
 
-       composer = em_utils_redirect_message (shell, message);
+       ccd = g_new0 (CreateComposerData, 1);
+       ccd->reader = g_object_ref (closure->reader);
+       ccd->message = message;
+       ccd->is_redirect = TRUE;
 
-       e_mail_reader_composer_created (closure->reader, composer, message);
-
-       g_object_unref (message);
+       e_msg_composer_new (shell, mail_reader_new_composer_created_cb, ccd);
 
        mail_reader_closure_free (closure);
 }
@@ -2640,33 +2679,22 @@ mail_reader_key_press_event_cb (EMailReader *reader,
        const gchar *action_name;
 
        if (!gtk_widget_has_focus (GTK_WIDGET (reader))) {
-               WebKitWebFrame *frame;
-               WebKitDOMDocument *dom;
-               WebKitDOMElement *element;
                EMailDisplay *display;
-               gchar *name = NULL;
+               GDBusProxy *web_extension;
 
                display = e_mail_reader_get_mail_display (reader);
-               frame = webkit_web_view_get_focused_frame (WEBKIT_WEB_VIEW (display));
-
-               if (frame != NULL) {
-                       dom = webkit_web_frame_get_dom_document (frame);
-                       element = webkit_dom_html_document_get_active_element (WEBKIT_DOM_HTML_DOCUMENT 
(dom));
+               web_extension = e_web_view_get_web_extension_proxy (E_WEB_VIEW (display));
+               if (web_extension) {
+                       GVariant *result;
 
-                       if (element != NULL) {
-                               name = webkit_dom_node_get_node_name (WEBKIT_DOM_NODE (element));
-                               g_object_unref (element);
-                       }
+                       result = g_dbus_proxy_get_cached_property (web_extension, "NeedInput");
+                       if (result) {
+                               gboolean need_input = g_variant_get_boolean (result);
+                               g_variant_unref (result);
 
-                       /* If INPUT or TEXTAREA has focus,
-                        * then any key press should go there. */
-                       if (name != NULL &&
-                           (g_ascii_strcasecmp (name, "INPUT") == 0 ||
-                            g_ascii_strcasecmp (name, "TEXTAREA") == 0)) {
-                               g_free (name);
-                               return FALSE;
+                               if (need_input)
+                                       return FALSE;
                        }
-                       g_free (name);
                }
        }
 
@@ -2849,13 +2877,13 @@ schedule_timeout_mark_seen (EMailReader *reader)
 }
 
 static void
-mail_reader_load_status_changed_cb (EMailReader *reader,
-                                    GParamSpec *pspec,
-                                    EMailDisplay *display)
+mail_reader_load_changed_cb (EMailReader *reader,
+                             WebKitLoadEvent event,
+                             EMailDisplay *display)
 {
        EMailReaderPrivate *priv;
 
-       if (webkit_web_view_get_load_status (WEBKIT_WEB_VIEW (display)) != WEBKIT_LOAD_FINISHED)
+       if (event != WEBKIT_LOAD_FINISHED)
                return;
 
        priv = E_MAIL_READER_GET_PRIVATE (reader);
@@ -4405,9 +4433,9 @@ connect_signals:
                display, "key-press-event",
                G_CALLBACK (mail_reader_key_press_event_cb), reader);
 
-       e_signal_connect_notify_swapped (
-               display, "notify::load-status",
-               G_CALLBACK (mail_reader_load_status_changed_cb), reader);
+       g_signal_connect_swapped (
+               display, "load-changed",
+               G_CALLBACK (mail_reader_load_changed_cb), reader);
 
        g_signal_connect_swapped (
                message_list, "message-selected",
@@ -5500,37 +5528,27 @@ e_mail_reader_create_remote_content_alert_button (EMailReader *reader)
 }
 
 static void
-e_mail_reader_load_status_notify_cb (WebKitWebFrame *frame,
-                                    GParamSpec *param,
+mail_reader_display_load_changed_cb (EMailDisplay *mail_display,
+                                    WebKitLoadEvent load_event,
                                     EMailReader *reader)
 {
-       WebKitLoadStatus load_status;
-       EMailDisplay *mail_display;
        EMailReaderPrivate *priv;
 
-       g_return_if_fail (WEBKIT_IS_WEB_FRAME (frame));
+       g_return_if_fail (E_IS_MAIL_DISPLAY (mail_display));
        g_return_if_fail (E_IS_MAIL_READER (reader));
 
        priv = E_MAIL_READER_GET_PRIVATE (reader);
        g_return_if_fail (priv != NULL);
 
-       load_status = webkit_web_frame_get_load_status (frame);
-       if (load_status == WEBKIT_LOAD_PROVISIONAL) {
-               WebKitWebView *web_view;
-
-               web_view = webkit_web_frame_get_web_view (frame);
-
-               if (priv->remote_content_alert && webkit_web_view_get_main_frame (web_view) == frame)
+       if (load_event == WEBKIT_LOAD_STARTED) {
+               if (priv->remote_content_alert)
                        e_alert_response (priv->remote_content_alert, GTK_RESPONSE_CLOSE);
                return;
        }
 
-       if (load_status != WEBKIT_LOAD_FINISHED)
+       if (load_event != WEBKIT_LOAD_FINISHED)
                return;
 
-       mail_display = e_mail_reader_get_mail_display (reader);
-       g_return_if_fail (E_IS_MAIL_DISPLAY (mail_display));
-
        if (!e_mail_display_has_skipped_remote_content_sites (mail_display))
                return;
 
@@ -5552,18 +5570,6 @@ e_mail_reader_load_status_notify_cb (WebKitWebFrame *frame,
        }
 }
 
-static void
-mail_reader_display_frame_created_cb (WebKitWebView *web_view,
-                                     WebKitWebFrame *frame,
-                                     EMailReader *reader)
-{
-       g_return_if_fail (E_IS_MAIL_DISPLAY (web_view));
-       g_return_if_fail (E_IS_MAIL_READER (reader));
-
-       e_signal_connect_notify (frame, "notify::load-status",
-               G_CALLBACK (e_mail_reader_load_status_notify_cb), reader);
-}
-
 /**
  * e_mail_reader_connect_remote_content:
  * @reader: an #EMailReader
@@ -5575,18 +5581,12 @@ void
 e_mail_reader_connect_remote_content (EMailReader *reader)
 {
        EMailDisplay *mail_display;
-       WebKitWebFrame *frame;
 
        g_return_if_fail (E_IS_MAIL_READER (reader));
 
        mail_display = e_mail_reader_get_mail_display (reader);
        g_return_if_fail (E_IS_MAIL_DISPLAY (mail_display));
 
-       frame = webkit_web_view_get_main_frame (WEBKIT_WEB_VIEW (mail_display));
-
-       e_signal_connect_notify (frame, "notify::load-status",
-               G_CALLBACK (e_mail_reader_load_status_notify_cb), reader);
-
-       g_signal_connect (mail_display, "frame-created",
-               G_CALLBACK (mail_reader_display_frame_created_cb), reader);
+       g_signal_connect (mail_display, "load-changed",
+               G_CALLBACK (mail_reader_display_load_changed_cb), reader);
 }
diff --git a/mail/e-mail-request.c b/mail/e-mail-request.c
index ce673e9..a5ef505 100644
--- a/mail/e-mail-request.c
+++ b/mail/e-mail-request.c
@@ -15,19 +15,15 @@
  *
  */
 
-#define LIBSOUP_USE_UNSTABLE_REQUEST_API
-
-#include "e-mail-request.h"
-#include "em-utils.h"
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
 
 #include <libsoup/soup.h>
-#include <libsoup/soup-requester.h>
-#include <libsoup/soup-request-http.h>
-
-#include <webkit/webkit.h>
 
 #include <glib/gi18n.h>
 #include <camel/camel.h>
+#include <libedataserver/libedataserver.h>
 
 #include "shell/e-shell.h"
 
@@ -35,85 +31,127 @@
 #include "em-format/e-mail-formatter-utils.h"
 #include "em-format/e-mail-formatter-print.h"
 
+#include "em-utils.h"
+#include "e-mail-display.h"
 #include "e-mail-ui-session.h"
+#include "e-mail-request.h"
 
 #define d(x)
-#define dd(x)
-
-#define E_MAIL_REQUEST_GET_PRIVATE(obj) \
-       (G_TYPE_INSTANCE_GET_PRIVATE \
-       ((obj), E_TYPE_MAIL_REQUEST, EMailRequestPrivate))
 
 struct _EMailRequestPrivate {
-       GBytes *bytes;
-       gchar *mime_type;
+       gint dummy;
+};
 
-       GHashTable *uri_query;
-       gchar *uri_base;
-       gchar *full_uri;
+static void e_mail_request_content_request_init (EContentRequestInterface *iface);
 
-       gboolean part_converted_to_utf8;
-       gchar *ret_mime_type;
-};
+G_DEFINE_TYPE_WITH_CODE (EMailRequest, e_mail_request, G_TYPE_OBJECT,
+       G_IMPLEMENT_INTERFACE (E_TYPE_CONTENT_REQUEST, e_mail_request_content_request_init))
 
-static const gchar *data_schemes[] = { "mail", NULL };
+static gboolean
+e_mail_request_can_process_uri (EContentRequest *request,
+                               const gchar *uri)
+{
+       g_return_val_if_fail (E_IS_MAIL_REQUEST (request), FALSE);
+       g_return_val_if_fail (uri != NULL, FALSE);
 
-G_DEFINE_TYPE (EMailRequest, e_mail_request, SOUP_TYPE_REQUEST)
+       return g_ascii_strncasecmp (uri, "mail:", 5) == 0;
+}
 
 static void
-handle_mail_request (GSimpleAsyncResult *simple,
-                     GObject *object,
-                     GCancellable *cancellable)
+save_gicon_to_stream (GIcon *icon,
+                     gint size,
+                     GOutputStream *output_stream,
+                     gchar **out_mime_type)
+{
+       GtkIconInfo *icon_info;
+       GdkPixbuf *pixbuf;
+
+       if (size < 16)
+               size = 16;
+
+       icon_info = gtk_icon_theme_lookup_by_gicon (gtk_icon_theme_get_default (), icon, size, 
GTK_ICON_LOOKUP_FORCE_SIZE);
+       if (!icon_info)
+               return;
+
+       pixbuf = gtk_icon_info_load_icon (icon_info, NULL);
+       if (pixbuf) {
+               if (gdk_pixbuf_save_to_stream (
+                       pixbuf, output_stream,
+                       "png", NULL, NULL, NULL)) {
+                       *out_mime_type = g_strdup ("image/png");
+               }
+               g_object_unref (pixbuf);
+       }
+
+       g_object_unref (icon);
+}
+
+static gboolean
+mail_request_process_mail_sync (EContentRequest *request,
+                               SoupURI *suri,
+                               GHashTable *uri_query,
+                               GObject *requester,
+                               GInputStream **out_stream,
+                               gint64 *out_stream_length,
+                               gchar **out_mime_type,
+                               GCancellable *cancellable,
+                               GError **error)
 {
-       EMailRequest *request = E_MAIL_REQUEST (object);
        EMailFormatter *formatter;
        EMailPartList *part_list;
        CamelObjectBag *registry;
-       GInputStream *input_stream;
        GOutputStream *output_stream;
+       GBytes *bytes;
+       gchar *tmp, *use_mime_type = NULL;
        const gchar *val;
        const gchar *default_charset, *charset;
+       gboolean part_converted_to_utf8 = FALSE;
 
        EMailFormatterContext context = { 0 };
 
-       if (g_cancellable_is_cancelled (cancellable))
-               return;
+       if (g_cancellable_set_error_if_cancelled (cancellable, error))
+               return FALSE;
+
+       tmp = g_strdup_printf ("%s://%s%s", suri->scheme, suri->host, suri->path);
 
        registry = e_mail_part_list_get_registry ();
-       part_list = camel_object_bag_get (registry, request->priv->uri_base);
+       part_list = camel_object_bag_get (registry, tmp);
+
+       g_free (tmp);
+
+       context.uri = soup_uri_to_string (suri, FALSE);
 
        if (camel_debug_start ("emformat:requests")) {
-               printf ("%s: found part-list %p for full_uri '%s'\n", G_STRFUNC, part_list, 
request->priv->full_uri);
+               printf ("%s: found part-list %p for full_uri '%s'\n", G_STRFUNC, part_list, context.uri);
                camel_debug_end ();
        }
 
-       if (!part_list)
-               return;
+       if (!part_list) {
+               g_free (context.uri);
+               return FALSE;
+       }
 
-       val = g_hash_table_lookup (
-               request->priv->uri_query, "headers_collapsed");
+       val = g_hash_table_lookup (uri_query, "headers_collapsed");
        if (val != NULL && atoi (val) == 1)
                context.flags |= E_MAIL_FORMATTER_HEADER_FLAG_COLLAPSED;
 
-       val = g_hash_table_lookup (
-               request->priv->uri_query, "headers_collapsable");
+       val = g_hash_table_lookup (uri_query, "headers_collapsable");
        if (val != NULL && atoi (val) == 1)
                context.flags |= E_MAIL_FORMATTER_HEADER_FLAG_COLLAPSABLE;
 
-       val = g_hash_table_lookup (request->priv->uri_query, "mode");
+       val = g_hash_table_lookup (uri_query, "mode");
        if (val != NULL)
                context.mode = atoi (val);
 
-       default_charset = g_hash_table_lookup (
-               request->priv->uri_query, "formatter_default_charset");
-       charset = g_hash_table_lookup (
-               request->priv->uri_query, "formatter_charset");
+       default_charset = g_hash_table_lookup (uri_query, "formatter_default_charset");
+       charset = g_hash_table_lookup (uri_query, "formatter_charset");
 
        context.part_list = g_object_ref (part_list);
-       context.uri = request->priv->full_uri;
 
        if (context.mode == E_MAIL_FORMATTER_MODE_PRINTING)
                formatter = e_mail_formatter_print_new ();
+       else if (E_IS_MAIL_DISPLAY (requester))
+               formatter = g_object_ref (e_mail_display_get_formatter (E_MAIL_DISPLAY (requester)));
        else
                formatter = e_mail_formatter_new ();
 
@@ -124,7 +162,58 @@ handle_mail_request (GSimpleAsyncResult *simple,
 
        output_stream = g_memory_output_stream_new_resizable ();
 
-       val = g_hash_table_lookup (request->priv->uri_query, "part_id");
+       val = g_hash_table_lookup (uri_query, "attachment_icon");
+       if (val) {
+               gchar *attachment_id;
+
+               attachment_id = soup_uri_decode (val);
+               if (E_IS_MAIL_DISPLAY (requester)) {
+                       EMailDisplay *mail_display = E_MAIL_DISPLAY (requester);
+                       EAttachmentStore *attachment_store;
+                       GList *attachments, *link;
+
+                       attachment_store = e_mail_display_get_attachment_store (mail_display);
+                       attachments = e_attachment_store_get_attachments (attachment_store);
+                       for (link = attachments; link; link = g_list_next (link)) {
+                               EAttachment *attachment = link->data;
+                               gboolean can_use;
+
+                               tmp = g_strdup_printf ("%p", attachment);
+                               can_use = g_strcmp0 (tmp, attachment_id) == 0;
+                               g_free (tmp);
+
+                               if (can_use) {
+                                       GtkTreeIter iter;
+
+                                       if (e_attachment_store_find_attachment_iter (attachment_store, 
attachment, &iter)) {
+                                               GIcon *icon = NULL;
+
+                                               gtk_tree_model_get (GTK_TREE_MODEL (attachment_store), &iter,
+                                                       E_ATTACHMENT_STORE_COLUMN_ICON, &icon,
+                                                       -1);
+
+                                               if (icon) {
+                                                       const gchar *size = g_hash_table_lookup (uri_query, 
"size");
+                                                       if (!size)
+                                                               size = "16";
+
+                                                       save_gicon_to_stream (icon, atoi (size), 
output_stream, &use_mime_type);
+                                               }
+                                       }
+
+                                       break;
+                               }
+                       }
+
+                       g_list_free_full (attachments, g_object_unref);
+               }
+
+               g_free (attachment_id);
+
+               goto no_part;
+       }
+
+       val = g_hash_table_lookup (uri_query, "part_id");
        if (val != NULL) {
                EMailPart *part;
                const gchar *mime_type;
@@ -143,40 +232,20 @@ handle_mail_request (GSimpleAsyncResult *simple,
                }
                g_free (part_id);
 
-               mime_type = g_hash_table_lookup (
-                       request->priv->uri_query, "mime_type");
+               mime_type = g_hash_table_lookup (uri_query, "mime_type");
 
                if (context.mode == E_MAIL_FORMATTER_MODE_SOURCE)
                        mime_type = "application/vnd.evolution.source";
 
-               if (context.mode == E_MAIL_FORMATTER_MODE_CID) {
-                       CamelDataWrapper *dw;
-                       CamelMimePart *mime_part;
-
-                       mime_part = e_mail_part_ref_mime_part (part);
-                       dw = camel_medium_get_content (CAMEL_MEDIUM (mime_part));
-                       g_return_if_fail (dw);
-
-                       if (!mime_type) {
-                               g_free (request->priv->mime_type);
-                               request->priv->mime_type = camel_data_wrapper_get_mime_type (dw);
-                       }
-
-                       camel_data_wrapper_decode_to_output_stream_sync (
-                               dw, output_stream, cancellable, NULL);
-
-                       g_object_unref (mime_part);
-               } else {
-                       if (mime_type == NULL)
-                               mime_type = e_mail_part_get_mime_type (part);
+               if (mime_type == NULL)
+                       mime_type = e_mail_part_get_mime_type (part);
 
-                       e_mail_formatter_format_as (
-                               formatter, &context, part,
-                               output_stream, mime_type,
-                               cancellable);
+               e_mail_formatter_format_as (
+                       formatter, &context, part,
+                       output_stream, mime_type,
+                       cancellable);
 
-                       request->priv->part_converted_to_utf8 = e_mail_part_get_converted_to_utf8 (part);
-               }
+               part_converted_to_utf8 = e_mail_part_get_converted_to_utf8 (part);
 
                g_object_unref (part);
 
@@ -191,61 +260,54 @@ handle_mail_request (GSimpleAsyncResult *simple,
 
        g_output_stream_close (output_stream, NULL, NULL);
 
-       if (request->priv->bytes != NULL)
-               g_bytes_unref (request->priv->bytes);
-
-       request->priv->bytes = g_memory_output_stream_steal_as_bytes (
-               G_MEMORY_OUTPUT_STREAM (output_stream));
+       bytes = g_memory_output_stream_steal_as_bytes (G_MEMORY_OUTPUT_STREAM (output_stream));
 
-       if (g_bytes_get_size (request->priv->bytes) == 0) {
+       if (g_bytes_get_size (bytes) == 0) {
                gchar *data;
 
-               g_bytes_unref (request->priv->bytes);
+               g_bytes_unref (bytes);
 
                data = g_strdup_printf (
                        "<p align='center'>%s</p>",
                        _("The message has no text content."));
 
                /* Takes ownership of the string. */
-               request->priv->bytes = g_bytes_new_take (
-                       data, strlen (data) + 1);
+               bytes = g_bytes_new_take (data, strlen (data) + 1);
        }
 
-       input_stream =
-               g_memory_input_stream_new_from_bytes (request->priv->bytes);
+       if (!use_mime_type)
+               use_mime_type = g_strdup ("text/html");
 
-       g_simple_async_result_set_op_res_gpointer (
-               simple, g_object_ref (input_stream),
-               (GDestroyNotify) g_object_unref);
+       if (part_converted_to_utf8 && g_strcmp0 (use_mime_type, "text/html") == 0) {
+               tmp = g_strconcat (use_mime_type, "; charset=\"UTF-8\"", NULL);
+               g_free (use_mime_type);
+               use_mime_type = tmp;
+       }
 
-       g_object_unref (input_stream);
-       g_object_unref (output_stream);
+       *out_stream = g_memory_input_stream_new_from_bytes (bytes);
+       *out_stream_length = g_bytes_get_size (bytes);
+       *out_mime_type = use_mime_type;
 
+       g_object_unref (output_stream);
        g_object_unref (part_list);
        g_object_unref (formatter);
-}
-
-static GInputStream *
-get_empty_image_stream (void)
-{
-       GdkPixbuf *pixbuf;
-       gchar *buffer;
-       gsize length;
-
-       pixbuf = gdk_pixbuf_new (GDK_COLORSPACE_RGB, TRUE, 8, 1, 1);
-       gdk_pixbuf_fill (pixbuf, 0x00000000); /* transparent black */
-       gdk_pixbuf_save_to_buffer (pixbuf, &buffer, &length, "png", NULL, NULL);
-       g_object_unref (pixbuf);
+       g_bytes_unref (bytes);
+       g_free (context.uri);
 
-       return g_memory_input_stream_new_from_data (buffer, length, g_free);
+       return TRUE;
 }
 
-static void
-handle_contact_photo_request (GSimpleAsyncResult *simple,
-                              GObject *object,
-                              GCancellable *cancellable)
+static gboolean
+mail_request_process_contact_photo_sync (EContentRequest *request,
+                                        SoupURI *suri,
+                                        GHashTable *uri_query,
+                                        GObject *requester,
+                                        GInputStream **out_stream,
+                                        gint64 *out_stream_length,
+                                        gchar **out_mime_type,
+                                        GCancellable *cancellable,
+                                        GError **error)
 {
-       EMailRequest *request = E_MAIL_REQUEST (object);
        EShell *shell;
        EShellBackend *shell_backend;
        EMailBackend *mail_backend;
@@ -256,227 +318,188 @@ handle_contact_photo_request (GSimpleAsyncResult *simple,
        const gchar *email_address;
        const gchar *escaped_string;
        gchar *unescaped_string;
-       GError *error = NULL;
+       gboolean success = FALSE;
 
-       /* XXX Is this really the only way to obtain
-        *     the mail session instance from here? */
        shell = e_shell_get_default ();
        shell_backend = e_shell_get_backend_by_name (shell, "mail");
        mail_backend = E_MAIL_BACKEND (shell_backend);
        mail_session = e_mail_backend_get_session (mail_backend);
 
-       photo_cache = e_mail_ui_session_get_photo_cache (
-               E_MAIL_UI_SESSION (mail_session));
+       photo_cache = e_mail_ui_session_get_photo_cache (E_MAIL_UI_SESSION (mail_session));
 
-       request->priv->mime_type = g_strdup ("image/*");
+       escaped_string = g_hash_table_lookup (uri_query, "mailaddr");
+       if (escaped_string && *escaped_string) {
+               cia = camel_internet_address_new ();
 
-       escaped_string = g_hash_table_lookup (
-               request->priv->uri_query, "mailaddr");
-       if (escaped_string == NULL || *escaped_string == '\0')
-               goto exit;
+               unescaped_string = g_uri_unescape_string (escaped_string, NULL);
+               camel_address_decode (CAMEL_ADDRESS (cia), unescaped_string);
+               g_free (unescaped_string);
 
-       cia = camel_internet_address_new ();
+               if (camel_internet_address_get (cia, 0, NULL, &email_address))
+                       success = e_photo_cache_get_photo_sync (
+                               photo_cache, email_address,
+                               cancellable, &stream, error);
 
-       unescaped_string = g_uri_unescape_string (escaped_string, NULL);
-       camel_address_decode (CAMEL_ADDRESS (cia), unescaped_string);
-       g_free (unescaped_string);
+               g_object_unref (cia);
 
-       if (camel_internet_address_get (cia, 0, NULL, &email_address))
-               e_photo_cache_get_photo_sync (
-                       photo_cache, email_address,
-                       cancellable, &stream, &error);
+               if (success) {
+                       *out_stream = stream;
+                       *out_stream_length = -1;
+                       *out_mime_type = g_strdup ("image/*");
+               }
+       }
 
-       g_object_unref (cia);
+       if (!success) {
+               GdkPixbuf *pixbuf;
+               gchar *buffer;
+               gsize length;
 
-       /* Ignore cancellations. */
-       if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) {
-               g_clear_error (&error);
-       } else if (error != NULL) {
-               g_warning ("%s: %s", G_STRFUNC, error->message);
-               g_clear_error (&error);
-       }
+               g_clear_error (error);
 
-exit:
-       if (stream == NULL)
-               stream = get_empty_image_stream ();
+               /* Construct empty image stream, to not show "broken image" icon when no contact photo is 
found */
+               pixbuf = gdk_pixbuf_new (GDK_COLORSPACE_RGB, TRUE, 8, 1, 1);
+               gdk_pixbuf_fill (pixbuf, 0x00000000); /* transparent black */
+               gdk_pixbuf_save_to_buffer (pixbuf, &buffer, &length, "png", NULL, NULL);
+               g_object_unref (pixbuf);
 
-       g_simple_async_result_set_op_res_gpointer (
-               simple, g_object_ref (stream),
-               (GDestroyNotify) g_object_unref);
+               *out_stream = g_memory_input_stream_new_from_data (buffer, length, g_free);
+               *out_stream_length = length;
+               *out_mime_type = g_strdup ("image/png");
+       }
 
-       g_object_unref (stream);
+       return TRUE;
 }
 
-static void
-mail_request_finalize (GObject *object)
+typedef struct _MailIdleData
 {
-       EMailRequestPrivate *priv;
+       EContentRequest *request;
+       SoupURI *suri;
+       GHashTable *uri_query;
+       GObject *requester;
+       GInputStream **out_stream;
+       gint64 *out_stream_length;
+       gchar **out_mime_type;
+       GCancellable *cancellable;
+       GError **error;
 
-       priv = E_MAIL_REQUEST_GET_PRIVATE (object);
+       gboolean success;
+       EFlag *flag;
+} MailIdleData;
 
-       if (priv->bytes != NULL)
-               g_bytes_unref (priv->bytes);
+static gboolean
+process_mail_request_idle_cb (gpointer user_data)
+{
+       MailIdleData *mid = user_data;
 
-       if (priv->uri_query != NULL)
-               g_hash_table_destroy (priv->uri_query);
+       g_return_val_if_fail (mid != NULL, FALSE);
+       g_return_val_if_fail (E_IS_MAIL_REQUEST (mid->request), FALSE);
+       g_return_val_if_fail (mid->suri != NULL, FALSE);
+       g_return_val_if_fail (mid->flag != NULL, FALSE);
 
-       g_free (priv->mime_type);
-       g_free (priv->uri_base);
-       g_free (priv->full_uri);
-       g_free (priv->ret_mime_type);
+       mid->success = mail_request_process_mail_sync (mid->request,
+               mid->suri, mid->uri_query, mid->requester, mid->out_stream,
+               mid->out_stream_length, mid->out_mime_type,
+               mid->cancellable, mid->error);
 
-       /* Chain up to parent's finalize() method. */
-       G_OBJECT_CLASS (e_mail_request_parent_class)->finalize (object);
-}
+       e_flag_set (mid->flag);
 
-static gboolean
-mail_request_check_uri (SoupRequest *request,
-                       SoupURI *uri,
-                       GError **error)
-{
-       return (strcmp (uri->scheme, "mail") == 0);
+       return FALSE;
 }
 
-static void
-mail_request_send_async (SoupRequest *request,
-                         GCancellable *cancellable,
-                         GAsyncReadyCallback callback,
-                         gpointer user_data)
+static gboolean
+e_mail_request_process_sync (EContentRequest *request,
+                            const gchar *uri,
+                            GObject *requester,
+                            GInputStream **out_stream,
+                            gint64 *out_stream_length,
+                            gchar **out_mime_type,
+                            GCancellable *cancellable,
+                            GError **error)
 {
-       EMailRequestPrivate *priv;
-       GSimpleAsyncResult *simple;
-       SoupURI *uri;
+       SoupURI *suri;
+       GHashTable *uri_query;
+       gboolean success = FALSE;
 
-       priv = E_MAIL_REQUEST_GET_PRIVATE (request);
+       g_return_val_if_fail (E_IS_MAIL_REQUEST (request), FALSE);
+       g_return_val_if_fail (uri != NULL, FALSE);
 
-       uri = soup_request_get_uri (request);
+       if (g_cancellable_set_error_if_cancelled (cancellable, error))
+               return FALSE;
 
-       d (printf ("received request for %s\n", soup_uri_to_string (uri, FALSE)));
+       suri = soup_uri_new (uri);
+       g_return_val_if_fail (suri != NULL, FALSE);
 
-       if (uri->query) {
-               priv->uri_query = soup_form_decode (uri->query);
+       if (suri->query) {
+               uri_query = soup_form_decode (suri->query);
        } else {
-               priv->uri_query = NULL;
+               uri_query = NULL;
        }
 
-       priv->full_uri = soup_uri_to_string (uri, FALSE);
-       priv->uri_base = g_strdup_printf (
-               "%s://%s%s", uri->scheme, uri->host, uri->path);
-
-       simple = g_simple_async_result_new (
-               G_OBJECT (request), callback,
-               user_data, mail_request_send_async);
-
-       g_simple_async_result_set_check_cancellable (simple, cancellable);
-
-       if (g_strcmp0 (uri->host, "contact-photo") == 0) {
-               e_util_run_simple_async_result_in_thread (
-                       simple, handle_contact_photo_request,
-                       cancellable);
+       if (g_strcmp0 (suri->host, "contact-photo") == 0) {
+               success = mail_request_process_contact_photo_sync (request, suri, uri_query, requester,
+                       out_stream, out_stream_length, out_mime_type, cancellable, error);
        } else {
-               /* Process e-mail mail requests in this thread, which should be
-                * the main/UI thread, because any EMailFormatter can create
-                * GtkWidget-s, or manipulate with them, which should be always
-                * done in the main/UI thread. */
-               handle_mail_request (simple, G_OBJECT (request), cancellable);
-               g_simple_async_result_complete_in_idle (simple);
-       }
-
-       g_object_unref (simple);
-}
-
-static GInputStream *
-mail_request_send_finish (SoupRequest *request,
-                          GAsyncResult *result,
-                          GError **error)
-{
-       GSimpleAsyncResult *simple;
-       GInputStream *stream;
-
-       simple = G_SIMPLE_ASYNC_RESULT (result);
-       stream = g_simple_async_result_get_op_res_gpointer (simple);
+               MailIdleData mid;
+
+               mid.request = request;
+               mid.suri = suri;
+               mid.uri_query = uri_query;
+               mid.requester = requester;
+               mid.out_stream = out_stream;
+               mid.out_stream_length = out_stream_length;
+               mid.out_mime_type = out_mime_type;
+               mid.cancellable = cancellable;
+               mid.error = error;
+               mid.flag = e_flag_new ();
+               mid.success = FALSE;
+
+               if (e_util_is_main_thread (NULL)) {
+                       process_mail_request_idle_cb (&mid);
+               } else {
+                       /* Process e-mail mail requests in the main/UI thread, because
+                        * any EMailFormatter can create GtkWidget-s, or manipulate with
+                        * them, which should be always done in the main/UI thread. */
+                       g_idle_add_full (
+                               G_PRIORITY_HIGH_IDLE,
+                               process_mail_request_idle_cb,
+                               &mid, NULL);
+
+                       e_flag_wait (mid.flag);
+               }
 
-       /* Reset the stream before passing it back to webkit */
-       if (G_IS_SEEKABLE (stream))
-               g_seekable_seek (
-                       G_SEEKABLE (stream), 0, G_SEEK_SET, NULL, NULL);
+               e_flag_free (mid.flag);
 
-       if (stream == NULL) {
-               /* We must always return something */
-               stream = g_memory_input_stream_new ();
-       } else {
-               g_object_ref (stream);
+               success = mid.success;
        }
 
-       return stream;
-}
-
-static goffset
-mail_request_get_content_length (SoupRequest *request)
-{
-       EMailRequestPrivate *priv;
-       goffset content_length = -1;  /* -1 means unknown */
+       if (uri_query)
+               g_hash_table_destroy (uri_query);
+       soup_uri_free (suri);
 
-       priv = E_MAIL_REQUEST_GET_PRIVATE (request);
-
-       if (priv->bytes != NULL)
-               content_length = g_bytes_get_size (priv->bytes);
-
-       return content_length;
+       return success;
 }
 
-static const gchar *
-mail_request_get_content_type (SoupRequest *request)
+static void
+e_mail_request_content_request_init (EContentRequestInterface *iface)
 {
-       EMailRequestPrivate *priv;
-       gchar *mime_type;
-
-       priv = E_MAIL_REQUEST_GET_PRIVATE (request);
-
-       if (priv->mime_type != NULL) {
-               mime_type = g_strdup (priv->mime_type);
-       } else {
-               mime_type = g_strdup ("text/html");
-       }
-
-       if (g_strcmp0 (mime_type, "text/html") == 0 &&
-           priv->part_converted_to_utf8) {
-               priv->ret_mime_type = g_strconcat (
-                       mime_type, "; charset=\"UTF-8\"", NULL);
-               g_free (mime_type);
-       } else {
-               priv->ret_mime_type = mime_type;
-       }
-
-       d (printf ("Content-Type: %s\n", priv->ret_mime_type));
-
-       return priv->ret_mime_type;
+       iface->can_process_uri = e_mail_request_can_process_uri;
+       iface->process_sync = e_mail_request_process_sync;
 }
 
 static void
 e_mail_request_class_init (EMailRequestClass *class)
 {
-       GObjectClass *object_class;
-       SoupRequestClass *request_class;
-
        g_type_class_add_private (class, sizeof (EMailRequestPrivate));
-
-       object_class = G_OBJECT_CLASS (class);
-       object_class->finalize = mail_request_finalize;
-
-       request_class = SOUP_REQUEST_CLASS (class);
-       request_class->schemes = data_schemes;
-       request_class->send_async = mail_request_send_async;
-       request_class->send_finish = mail_request_send_finish;
-       request_class->get_content_type = mail_request_get_content_type;
-       request_class->get_content_length = mail_request_get_content_length;
-       request_class->check_uri = mail_request_check_uri;
 }
 
 static void
 e_mail_request_init (EMailRequest *request)
 {
-       request->priv = E_MAIL_REQUEST_GET_PRIVATE (request);
-       request->priv->part_converted_to_utf8 = FALSE;
+       request->priv = G_TYPE_INSTANCE_GET_PRIVATE (request, E_TYPE_MAIL_REQUEST, EMailRequestPrivate);
 }
 
+EContentRequest *
+e_mail_request_new (void)
+{
+       return g_object_new (E_TYPE_MAIL_REQUEST, NULL);
+}
diff --git a/mail/e-mail-request.h b/mail/e-mail-request.h
index c85266e..cd35992 100644
--- a/mail/e-mail-request.h
+++ b/mail/e-mail-request.h
@@ -18,10 +18,7 @@
 #ifndef E_MAIL_REQUEST_H
 #define E_MAIL_REQUEST_H
 
-#define LIBSOUP_USE_UNSTABLE_REQUEST_API
-
-#include <libsoup/soup.h>
-#include <libsoup/soup-request.h>
+#include <e-util/e-util.h>
 
 /* Standard GObject macros */
 #define E_TYPE_MAIL_REQUEST \
@@ -49,15 +46,17 @@ typedef struct _EMailRequestClass EMailRequestClass;
 typedef struct _EMailRequestPrivate EMailRequestPrivate;
 
 struct _EMailRequest {
-       SoupRequest parent;
+       GObject parent;
        EMailRequestPrivate *priv;
 };
 
 struct _EMailRequestClass {
-       SoupRequestClass parent;
+       GObjectClass parent;
 };
 
 GType          e_mail_request_get_type         (void) G_GNUC_CONST;
+EContentRequest *
+               e_mail_request_new              (void);
 
 G_END_DECLS
 
diff --git a/mail/em-composer-utils.c b/mail/em-composer-utils.c
index a983db5..2a68541 100644
--- a/mail/em-composer-utils.c
+++ b/mail/em-composer-utils.c
@@ -534,7 +534,7 @@ composer_presend_check_unwanted_html (EMsgComposer *composer,
 {
        EDestination **recipients;
        EHTMLEditor *editor;
-       EHTMLEditorView *view;
+       EContentEditor *cnt_editor;
        EComposerHeaderTable *table;
        GSettings *settings;
        gboolean check_passed = TRUE;
@@ -546,8 +546,8 @@ composer_presend_check_unwanted_html (EMsgComposer *composer,
        settings = e_util_ref_settings ("org.gnome.evolution.mail");
 
        editor = e_msg_composer_get_editor (composer);
-       view = e_html_editor_get_view (editor);
-       html_mode = e_html_editor_view_get_html_mode (view);
+       cnt_editor = e_html_editor_get_content_editor (editor);
+       html_mode = e_content_editor_get_html_mode (cnt_editor);
 
        table = e_msg_composer_get_header_table (composer);
        recipients = e_composer_header_table_get_destinations (table);
@@ -681,11 +681,12 @@ exit:
 
        if (set_changed) {
                EHTMLEditor *editor;
-               EHTMLEditorView *view;
+               EContentEditor *cnt_editor;
 
                editor = e_msg_composer_get_editor (async_context->composer);
-               view = e_html_editor_get_view (editor);
-               e_html_editor_view_set_changed (view, TRUE);
+               cnt_editor = e_html_editor_get_content_editor (editor);
+
+               e_content_editor_set_changed (cnt_editor, TRUE);
 
                gtk_window_present (GTK_WINDOW (async_context->composer));
        }
@@ -800,14 +801,14 @@ static void
 composer_set_no_change (EMsgComposer *composer)
 {
        EHTMLEditor *editor;
-       EHTMLEditorView *view;
+       EContentEditor *cnt_editor;
 
        g_return_if_fail (composer != NULL);
 
        editor = e_msg_composer_get_editor (composer);
-       view = e_html_editor_get_view (editor);
+       cnt_editor = e_html_editor_get_content_editor (editor);
 
-       e_html_editor_view_set_changed (view, FALSE);
+       e_content_editor_set_changed (cnt_editor, FALSE);
 }
 
 /* delete original messages from Outbox folder */
@@ -853,13 +854,13 @@ composer_save_to_drafts_complete (GObject *source_object,
        EActivity *activity;
        AsyncContext *async_context;
        EHTMLEditor *editor;
-       EHTMLEditorView *view;
+       EContentEditor *cnt_editor;
        GError *local_error = NULL;
 
        async_context = (AsyncContext *) user_data;
 
        editor = e_msg_composer_get_editor (async_context->composer);
-       view = e_html_editor_get_view (editor);
+       cnt_editor = e_html_editor_get_content_editor (editor);
 
        /* We don't really care if this failed.  If something other than
         * cancellation happened, emit a runtime warning so the error is
@@ -870,14 +871,13 @@ composer_save_to_drafts_complete (GObject *source_object,
        activity = async_context->activity;
 
        if (e_activity_handle_cancellation (activity, local_error)) {
-               e_html_editor_view_set_changed (view, TRUE);
+               e_content_editor_set_changed (cnt_editor, TRUE);
                g_error_free (local_error);
 
        } else if (local_error != NULL) {
-               e_html_editor_view_set_changed (view, TRUE);
+               e_content_editor_set_changed (cnt_editor, TRUE);
                g_warning ("%s", local_error->message);
                g_error_free (local_error);
-
        } else
                e_activity_set_state (activity, E_ACTIVITY_COMPLETED);
 
@@ -907,14 +907,14 @@ composer_save_to_drafts_cleanup (GObject *source_object,
        EAlertSink *alert_sink;
        GCancellable *cancellable;
        EHTMLEditor *editor;
-       EHTMLEditorView *view;
+       EContentEditor *cnt_editor;
        AsyncContext *async_context;
        GError *local_error = NULL;
 
        async_context = (AsyncContext *) user_data;
 
        editor = e_msg_composer_get_editor (async_context->composer);
-       view = e_html_editor_get_view (editor);
+       cnt_editor = e_html_editor_get_content_editor (editor);
 
        activity = async_context->activity;
        alert_sink = e_activity_get_alert_sink (activity);
@@ -926,7 +926,7 @@ composer_save_to_drafts_cleanup (GObject *source_object,
 
        if (e_activity_handle_cancellation (activity, local_error)) {
                g_warn_if_fail (async_context->message_uid == NULL);
-               e_html_editor_view_set_changed (view, TRUE);
+               e_content_editor_set_changed (cnt_editor, TRUE);
                async_context_free (async_context);
                g_error_free (local_error);
                return;
@@ -944,7 +944,7 @@ composer_save_to_drafts_cleanup (GObject *source_object,
                                GTK_WINDOW (async_context->composer),
                                "mail:ask-default-drafts", local_error->message, NULL);
                        if (response != GTK_RESPONSE_YES) {
-                               e_html_editor_view_set_changed (view, TRUE);
+                               e_content_editor_set_changed (cnt_editor, TRUE);
                                async_context_free (async_context);
                        } else {
                                composer_save_to_drafts_append_mail (async_context, NULL);
@@ -958,7 +958,7 @@ composer_save_to_drafts_cleanup (GObject *source_object,
                        alert_sink,
                        "mail-composer:save-to-drafts-error",
                        local_error->message, NULL);
-               e_html_editor_view_set_changed (view, TRUE);
+               e_content_editor_set_changed (cnt_editor, TRUE);
                async_context_free (async_context);
                g_error_free (local_error);
                return;
@@ -1023,7 +1023,7 @@ composer_save_to_drafts_got_folder (GObject *source_object,
        EActivity *activity;
        CamelFolder *drafts_folder;
        EHTMLEditor *editor;
-       EHTMLEditorView *view;
+       EContentEditor *cnt_editor;
        AsyncContext *async_context;
        GError *local_error = NULL;
 
@@ -1032,7 +1032,7 @@ composer_save_to_drafts_got_folder (GObject *source_object,
        activity = async_context->activity;
 
        editor = e_msg_composer_get_editor (async_context->composer);
-       view = e_html_editor_get_view (editor);
+       cnt_editor = e_html_editor_get_content_editor (editor);
 
        drafts_folder = e_mail_session_uri_to_folder_finish (
                E_MAIL_SESSION (source_object), result, &local_error);
@@ -1043,7 +1043,7 @@ composer_save_to_drafts_got_folder (GObject *source_object,
                ((drafts_folder == NULL) && (local_error != NULL)));
 
        if (e_activity_handle_cancellation (activity, local_error)) {
-               e_html_editor_view_set_changed (view, TRUE);
+               e_content_editor_set_changed (cnt_editor, TRUE);
                async_context_free (async_context);
                g_error_free (local_error);
                return;
@@ -1061,7 +1061,7 @@ composer_save_to_drafts_got_folder (GObject *source_object,
                g_error_free (local_error);
 
                if (response != GTK_RESPONSE_YES) {
-                       e_html_editor_view_set_changed (view, TRUE);
+                       e_content_editor_set_changed (cnt_editor, TRUE);
                        async_context_free (async_context);
                        return;
                }
@@ -1316,20 +1316,17 @@ em_utils_composer_print_cb (EMsgComposer *composer,
 
 /* Composing messages... */
 
-static EMsgComposer *
-create_new_composer (EShell *shell,
-                     const gchar *subject,
-                     CamelFolder *folder)
+static void
+set_up_new_composer (EMsgComposer *composer,
+                    const gchar *subject,
+                    CamelFolder *folder)
 {
-       EMsgComposer *composer;
        EClientCache *client_cache;
        ESourceRegistry *registry;
        EComposerHeaderTable *table;
        ESource *source = NULL;
        gchar *identity = NULL;
 
-       composer = e_msg_composer_new (shell);
-
        table = e_msg_composer_get_header_table (composer);
 
        client_cache = e_composer_header_table_ref_client_cache (table);
@@ -1360,50 +1357,36 @@ create_new_composer (EShell *shell,
        e_composer_header_table_set_subject (table, subject);
        e_composer_header_table_set_identity_uid (table, identity);
 
-       em_utils_apply_send_account_override_to_composer (composer, shell, folder);
+       em_utils_apply_send_account_override_to_composer (composer, folder);
 
        g_free (identity);
 
        g_object_unref (client_cache);
        g_object_unref (registry);
-
-       return composer;
 }
 
 /**
  * em_utils_compose_new_message:
- * @shell: an #EShell
- * @folder: a #CamelFolder, or %NULL
+ * @composer: an #EMsgComposer
+ * @folder: (nullable): a #CamelFolder, or %NULL
  *
- * Opens a new composer window as a child window of @parent's toplevel
- * window.
+ * Sets up a new @composer window.
  *
- * Returns: the resulting #EMsgComposer
+ * Since: 3.22
  **/
-EMsgComposer *
-em_utils_compose_new_message (EShell *shell,
+void
+em_utils_compose_new_message (EMsgComposer *composer,
                               CamelFolder *folder)
 {
-       EMsgComposer *composer;
-       EHTMLEditor *editor;
-       EHTMLEditorView *view;
-
-       g_return_val_if_fail (E_IS_SHELL (shell), NULL);
+       g_return_if_fail (E_IS_MSG_COMPOSER (composer));
 
        if (folder != NULL)
-               g_return_val_if_fail (CAMEL_IS_FOLDER (folder), NULL);
+               g_return_if_fail (CAMEL_IS_FOLDER (folder));
 
-       composer = create_new_composer (shell, "", folder);
+       set_up_new_composer (composer, "", folder);
        composer_set_no_change (composer);
 
-       editor = e_msg_composer_get_editor (composer);
-       view = e_html_editor_get_view (editor);
-
-       e_html_editor_view_set_content_is_new_message (view, TRUE);
-
        gtk_widget_show (GTK_WIDGET (composer));
-
-       return composer;
 }
 
 static CamelMimeMessage *
@@ -1483,37 +1466,50 @@ em_utils_get_composer_recipients_as_message (EMsgComposer *composer)
        return message;
 }
 
-/**
- * em_utils_compose_new_message_with_mailto:
- * @shell: an #EShell
- * @mailto: a mailto URL
- * @folder: a #CamelFolder, or %NULL
- *
- * Opens a new composer window as a child window of @parent's toplevel
- * window. If @mailto is non-NULL, the composer fields will be filled in
- * according to the values in the mailto URL.
- **/
-EMsgComposer *
-em_utils_compose_new_message_with_mailto (EShell *shell,
-                                          const gchar *mailto,
-                                          CamelFolder *folder)
+typedef struct _CreateComposerData {
+       gchar *mailto;
+       CamelFolder *folder;
+} CreateComposerData;
+
+static void
+create_composer_data_free (gpointer ptr)
+{
+       CreateComposerData *ccd = ptr;
+
+       if (ccd) {
+               g_clear_object (&ccd->folder);
+               g_free (ccd->mailto);
+               g_free (ccd);
+       }
+}
+
+static void
+msg_composer_created_with_mailto_cb (GObject *source_object,
+                                    GAsyncResult *result,
+                                    gpointer user_data)
 {
+       CreateComposerData *ccd = user_data;
        EMsgComposer *composer;
        EComposerHeaderTable *table;
        EClientCache *client_cache;
        ESourceRegistry *registry;
+       GError *error = NULL;
 
-       g_return_val_if_fail (E_IS_SHELL (shell), NULL);
+       g_return_if_fail (ccd != NULL);
 
-       if (folder != NULL)
-               g_return_val_if_fail (CAMEL_IS_FOLDER (folder), NULL);
+       composer = e_msg_composer_new_finish (result, &error);
+       if (error) {
+               g_warning ("%s: Failed to create msg composer: %s", G_STRFUNC, error->message);
+               g_clear_error (&error);
+               create_composer_data_free (ccd);
 
-       if (mailto != NULL)
-               composer = e_msg_composer_new_from_url (shell, mailto);
-       else
-               composer = e_msg_composer_new (shell);
+               return;
+       }
+
+       if (ccd->mailto)
+               e_msg_composer_setup_from_url (composer, ccd->mailto);
 
-       em_utils_apply_send_account_override_to_composer (composer, shell, folder);
+       em_utils_apply_send_account_override_to_composer (composer, ccd->folder);
 
        table = e_msg_composer_get_header_table (composer);
 
@@ -1525,11 +1521,11 @@ em_utils_compose_new_message_with_mailto (EShell *shell,
        /* If a CamelFolder was given, we need to backtrack and find
         * the corresponding ESource with a Mail Identity extension. */
 
-       if (folder != NULL) {
+       if (ccd->folder) {
                ESource *source;
                CamelStore *store;
 
-               store = camel_folder_get_parent_store (folder);
+               store = camel_folder_get_parent_store (ccd->folder);
                source = em_utils_ref_mail_identity_for_store (registry, store);
 
                if (source != NULL) {
@@ -1544,7 +1540,36 @@ em_utils_compose_new_message_with_mailto (EShell *shell,
 
        gtk_window_present (GTK_WINDOW (composer));
 
-       return composer;
+       create_composer_data_free (ccd);
+}
+
+/**
+ * em_utils_compose_new_message_with_mailto:
+ * @shell: an #EShell
+ * @mailto: a mailto URL
+ * @folder: a #CamelFolder, or %NULL
+ *
+ * Opens a new composer window as a child window of @parent's toplevel
+ * window. If @mailto is non-NULL, the composer fields will be filled in
+ * according to the values in the mailto URL.
+ **/
+void
+em_utils_compose_new_message_with_mailto (EShell *shell,
+                                          const gchar *mailto,
+                                          CamelFolder *folder)
+{
+       CreateComposerData *ccd;
+
+       g_return_if_fail (E_IS_SHELL (shell));
+
+       if (folder != NULL)
+               g_return_if_fail (CAMEL_IS_FOLDER (folder));
+
+       ccd = g_new0 (CreateComposerData, 1);
+       ccd->folder = folder ? g_object_ref (folder) : NULL;
+       ccd->mailto = g_strdup (mailto);
+
+       e_msg_composer_new (shell, msg_composer_created_with_mailto_cb, ccd);
 }
 
 static gboolean
@@ -1749,22 +1774,22 @@ quoting_text (QuotingTextEnum type)
 
 /**
  * em_utils_edit_message:
- * @shell: an #EShell
+ * @composer: an #EMsgComposer
  * @folder: a #CamelFolder
  * @message: a #CamelMimeMessage
  * @message_uid: UID of @message, or %NULL
  *
- * Opens a composer filled in with the headers/mime-parts/etc of
- * @message.
+ * Sets up the @composer with the headers/mime-parts/etc of the @message.
+ *
+ * Since: 3.22
  **/
-EMsgComposer *
-em_utils_edit_message (EShell *shell,
+void
+em_utils_edit_message (EMsgComposer *composer,
                        CamelFolder *folder,
                        CamelMimeMessage *message,
                        const gchar *message_uid,
                        gboolean keep_signature)
 {
-       EMsgComposer *composer;
        ESourceRegistry *registry;
        ESource *source;
        gboolean folder_is_sent;
@@ -1773,12 +1798,12 @@ em_utils_edit_message (EShell *shell,
        gboolean folder_is_templates;
        gchar *override_identity_uid = NULL;
 
-       g_return_val_if_fail (E_IS_SHELL (shell), NULL);
-       g_return_val_if_fail (CAMEL_IS_MIME_MESSAGE (message), NULL);
+       g_return_if_fail (E_IS_MSG_COMPOSER (composer));
+       g_return_if_fail (CAMEL_IS_MIME_MESSAGE (message));
        if (folder)
-               g_return_val_if_fail (CAMEL_IS_FOLDER (folder), NULL);
+               g_return_if_fail (CAMEL_IS_FOLDER (folder));
 
-       registry = e_shell_get_registry (shell);
+       registry = e_shell_get_registry (e_msg_composer_get_shell (composer));
 
        if (folder) {
                folder_is_sent = em_utils_folder_is_sent (registry, folder);
@@ -1830,7 +1855,7 @@ em_utils_edit_message (EShell *shell,
                        }
                }
 
-               source = em_utils_check_send_account_override (shell, message, folder);
+               source = em_utils_check_send_account_override (e_msg_composer_get_shell (composer), message, 
folder);
                if (source) {
                        g_free (override_identity_uid);
                        override_identity_uid = e_source_dup_uid (source);
@@ -1838,7 +1863,7 @@ em_utils_edit_message (EShell *shell,
                }
        }
 
-       composer = e_msg_composer_new_with_message (shell, message, keep_signature, override_identity_uid, 
NULL);
+       e_msg_composer_setup_with_message (composer, message, keep_signature, override_identity_uid, NULL);
 
        g_free (override_identity_uid);
 
@@ -1881,8 +1906,6 @@ em_utils_edit_message (EShell *shell,
        composer_set_no_change (composer);
 
        gtk_widget_show (GTK_WIDGET (composer));
-
-       return composer;
 }
 
 static void
@@ -2098,39 +2121,36 @@ setup_forward_attached_callbacks (EMsgComposer *composer,
                (GDestroyNotify) forward_data_free);
 }
 
-static EMsgComposer *
-forward_non_attached (EMailBackend *backend,
+static void
+forward_non_attached (EMsgComposer *composer,
                       CamelFolder *folder,
                       const gchar *uid,
                       CamelMimeMessage *message,
                       EMailForwardStyle style)
 {
-       EMsgComposer *composer = NULL;
-       EMailSession *session;
-       EShell *shell;
+       CamelSession *session;
        gchar *text, *forward;
        guint32 validity_found = 0;
        guint32 flags;
 
+       g_return_if_fail (E_IS_MSG_COMPOSER (composer));
+
+       session = e_msg_composer_ref_session (composer);
+
        flags = E_MAIL_FORMATTER_QUOTE_FLAG_HEADERS |
                E_MAIL_FORMATTER_QUOTE_FLAG_KEEP_SIG;
        if (style == E_MAIL_FORWARD_STYLE_QUOTED)
                flags |= E_MAIL_FORMATTER_QUOTE_FLAG_CITE;
 
-       session = e_mail_backend_get_session (backend);
-       shell = e_shell_backend_get_shell (E_SHELL_BACKEND (backend));
-
        forward = quoting_text (QUOTING_FORWARD);
-       text = em_utils_message_to_html (
-               CAMEL_SESSION (session), message,
-               forward, flags, NULL, NULL, NULL, &validity_found);
+       text = em_utils_message_to_html (session, message, forward, flags, NULL, NULL, NULL, &validity_found);
 
        if (text != NULL) {
                CamelDataWrapper *content;
                gchar *subject;
 
                subject = mail_tool_generate_forward_subject (message);
-               composer = create_new_composer (shell, subject, folder);
+               set_up_new_composer (composer, subject, folder);
                g_free (subject);
 
                content = camel_medium_get_content (CAMEL_MEDIUM (message));
@@ -2157,26 +2177,24 @@ forward_non_attached (EMailBackend *backend,
                        g_free (tmp_message_uid);
                }
 
-               emu_update_composers_security (
-                       composer, validity_found);
+               emu_update_composers_security (composer, validity_found);
                composer_set_no_change (composer);
                gtk_widget_show (GTK_WIDGET (composer));
 
                g_free (text);
        }
 
+       g_clear_object (&session);
        g_free (forward);
-
-       return composer;
 }
 
 /**
  * em_utils_forward_message:
- * @backend: an #EMailBackend
+ * @composer: an #EMsgComposer
  * @message: a #CamelMimeMessage to forward
  * @style: the forward style to use
- * @folder: a #CamelFolder, or %NULL
- * @uid: the UID of %message, or %NULL
+ * @folder: (nullable):  a #CamelFolder, or %NULL
+ * @uid: (nullable): the UID of %message, or %NULL
  *
  * Forwards @message in the given @style.
  *
@@ -2194,8 +2212,8 @@ forward_non_attached (EMailBackend *backend,
  * in its own composer window in 'quoted' form (each line starting with
  * a "> ").
  **/
-EMsgComposer *
-em_utils_forward_message (EMailBackend *backend,
+void
+em_utils_forward_message (EMsgComposer *composer,
                           CamelMimeMessage *message,
                           EMailForwardStyle style,
                           CamelFolder *folder,
@@ -2203,10 +2221,9 @@ em_utils_forward_message (EMailBackend *backend,
 {
        CamelMimePart *part;
        gchar *subject;
-       EMsgComposer *composer = NULL;
 
-       g_return_val_if_fail (E_IS_MAIL_BACKEND (backend), NULL);
-       g_return_val_if_fail (CAMEL_IS_MIME_MESSAGE (message), NULL);
+       g_return_if_fail (E_IS_MSG_COMPOSER (composer));
+       g_return_if_fail (CAMEL_IS_MIME_MESSAGE (message));
 
        switch (style) {
                case E_MAIL_FORWARD_STYLE_ATTACHED:
@@ -2214,8 +2231,7 @@ em_utils_forward_message (EMailBackend *backend,
                        part = mail_tool_make_message_attachment (message);
                        subject = mail_tool_generate_forward_subject (message);
 
-                       composer = em_utils_forward_attachment (
-                               backend, part, subject, NULL, NULL);
+                       em_utils_forward_attachment (composer, part, subject, NULL, NULL);
 
                        g_object_unref (part);
                        g_free (subject);
@@ -2223,34 +2239,27 @@ em_utils_forward_message (EMailBackend *backend,
 
                case E_MAIL_FORWARD_STYLE_INLINE:
                case E_MAIL_FORWARD_STYLE_QUOTED:
-                       composer = forward_non_attached (
-                               backend, folder, uid, message, style);
+                       forward_non_attached (composer, folder, uid, message, style);
                        break;
        }
-
-       return composer;
 }
 
-EMsgComposer *
-em_utils_forward_attachment (EMailBackend *backend,
+void
+em_utils_forward_attachment (EMsgComposer *composer,
                              CamelMimePart *part,
                              const gchar *subject,
                              CamelFolder *folder,
                              GPtrArray *uids)
 {
-       EShell *shell;
        CamelDataWrapper *content;
-       EMsgComposer *composer;
 
-       g_return_val_if_fail (E_IS_MAIL_BACKEND (backend), NULL);
-       g_return_val_if_fail (CAMEL_IS_MIME_PART (part), NULL);
+       g_return_if_fail (E_IS_MSG_COMPOSER (composer));
+       g_return_if_fail (CAMEL_IS_MIME_PART (part));
 
        if (folder != NULL)
-               g_return_val_if_fail (CAMEL_IS_FOLDER (folder), NULL);
-
-       shell = e_shell_backend_get_shell (E_SHELL_BACKEND (backend));
+               g_return_if_fail (CAMEL_IS_FOLDER (folder));
 
-       composer = create_new_composer (shell, subject, folder);
+       set_up_new_composer (composer, subject, folder);
 
        e_msg_composer_attach (composer, part);
 
@@ -2291,8 +2300,6 @@ em_utils_forward_attachment (EMailBackend *backend,
        composer_set_no_change (composer);
 
        gtk_widget_show (GTK_WIDGET (composer));
-
-       return composer;
 }
 
 static gint
@@ -2368,18 +2375,30 @@ sort_sources_by_ui (GList **psources,
        g_hash_table_destroy (uids_order);
 }
 
-/* Redirecting messages... */
-
-static EMsgComposer *
-redirect_get_composer (EShell *shell,
-                       CamelMimeMessage *message)
+/**
+ * em_utils_redirect_message:
+ * @composer: an #EMsgComposer
+ * @message: message to redirect
+ *
+ * Sets up the @composer to redirect @message (Note: only headers will be
+ * editable). Adds Resent-From/Resent-To/etc headers.
+ *
+ * Since: 3.22
+ **/
+void
+em_utils_redirect_message (EMsgComposer *composer,
+                           CamelMimeMessage *message)
 {
-       EMsgComposer *composer;
        ESourceRegistry *registry;
-       CamelMedium *medium;
        ESource *source;
+       EShell *shell;
+       CamelMedium *medium;
        gchar *identity_uid = NULL;
 
+       g_return_if_fail (E_IS_MSG_COMPOSER (composer));
+       g_return_if_fail (CAMEL_IS_MIME_MESSAGE (message));
+
+       shell = e_msg_composer_get_shell (composer);
        medium = CAMEL_MEDIUM (message);
 
        /* QMail will refuse to send a message if it finds one of
@@ -2407,40 +2426,13 @@ redirect_get_composer (EShell *shell,
                g_object_unref (source);
        }
 
-       composer = e_msg_composer_new_redirect (
-               shell, message, identity_uid, NULL);
+       e_msg_composer_setup_redirect (composer, message, identity_uid, NULL);
 
        g_free (identity_uid);
 
-       return composer;
-}
-
-/**
- * em_utils_redirect_message:
- * @shell: an #EShell
- * @message: message to redirect
- *
- * Opens a composer to redirect @message (Note: only headers will be
- * editable). Adds Resent-From/Resent-To/etc headers.
- *
- * Returns: the resulting #EMsgComposer
- **/
-EMsgComposer *
-em_utils_redirect_message (EShell *shell,
-                           CamelMimeMessage *message)
-{
-       EMsgComposer *composer;
-
-       g_return_val_if_fail (E_IS_SHELL (shell), NULL);
-       g_return_val_if_fail (CAMEL_IS_MIME_MESSAGE (message), NULL);
-
-       composer = redirect_get_composer (shell, message);
-
        gtk_widget_show (GTK_WIDGET (composer));
 
        composer_set_no_change (composer);
-
-       return composer;
 }
 
 /* Replying to messages... */
@@ -2480,33 +2472,30 @@ em_utils_camel_address_to_destination (CamelInternetAddress *iaddr)
        return destv;
 }
 
-static EMsgComposer *
-reply_get_composer (EShell *shell,
-                    CamelMimeMessage *message,
-                    const gchar *identity_uid,
-                    CamelInternetAddress *to,
-                    CamelInternetAddress *cc,
-                    CamelFolder *folder,
-                   const gchar *message_uid,
-                    CamelNNTPAddress *postto)
+static void
+reply_setup_composer (EMsgComposer *composer,
+                     CamelMimeMessage *message,
+                     const gchar *identity_uid,
+                     CamelInternetAddress *to,
+                     CamelInternetAddress *cc,
+                     CamelFolder *folder,
+                     const gchar *message_uid,
+                    CamelNNTPAddress *postto)
 {
        gchar *message_id, *references;
        EDestination **tov, **ccv;
-       EMsgComposer *composer;
        EComposerHeaderTable *table;
        CamelMedium *medium;
        gchar *subject;
 
-       g_return_val_if_fail (E_IS_SHELL (shell), NULL);
-       g_return_val_if_fail (CAMEL_IS_MIME_MESSAGE (message), NULL);
+       g_return_if_fail (E_IS_MSG_COMPOSER (composer));
+       g_return_if_fail (CAMEL_IS_MIME_MESSAGE (message));
 
        if (to != NULL)
-               g_return_val_if_fail (CAMEL_IS_INTERNET_ADDRESS (to), NULL);
+               g_return_if_fail (CAMEL_IS_INTERNET_ADDRESS (to));
 
        if (cc != NULL)
-               g_return_val_if_fail (CAMEL_IS_INTERNET_ADDRESS (cc), NULL);
-
-       composer = e_msg_composer_new (shell);
+               g_return_if_fail (CAMEL_IS_INTERNET_ADDRESS (cc));
 
        /* construct the tov/ccv */
        tov = em_utils_camel_address_to_destination (to);
@@ -2605,8 +2594,6 @@ reply_get_composer (EShell *shell,
 
        g_free (message_id);
        g_free (references);
-
-       return composer;
 }
 
 static gboolean
@@ -3327,8 +3314,8 @@ emcu_folder_is_inbox (CamelFolder *folder)
  * @folder and @message_uid may be supplied in order to update the message
  * flags once it has been replied to.
  **/
-EMsgComposer *
-em_utils_reply_to_message (EShell *shell,
+void
+em_utils_reply_to_message (EMsgComposer *composer,
                            CamelMimeMessage *message,
                            CamelFolder *folder,
                            const gchar *message_uid,
@@ -3340,19 +3327,19 @@ em_utils_reply_to_message (EShell *shell,
        ESourceRegistry *registry;
        CamelInternetAddress *to, *cc;
        CamelNNTPAddress *postto = NULL;
-       EMsgComposer *composer;
+       EShell *shell;
        ESourceMailCompositionReplyStyle prefer_reply_style = E_SOURCE_MAIL_COMPOSITION_REPLY_STYLE_DEFAULT;
        ESource *source;
        gchar *identity_uid = NULL;
-       const gchar *evo_source_header;
        guint32 flags;
 
-       g_return_val_if_fail (E_IS_SHELL (shell), NULL);
-       g_return_val_if_fail (CAMEL_IS_MIME_MESSAGE (message), NULL);
+       g_return_if_fail (E_IS_MSG_COMPOSER (composer));
+       g_return_if_fail (CAMEL_IS_MIME_MESSAGE (message));
 
        to = camel_internet_address_new ();
        cc = camel_internet_address_new ();
 
+       shell = e_msg_composer_get_shell (composer);
        registry = e_shell_get_registry (shell);
 
        /* This returns a new ESource reference. */
@@ -3411,8 +3398,7 @@ em_utils_reply_to_message (EShell *shell,
                break;
        }
 
-       composer = reply_get_composer (
-               shell, message, identity_uid, to, cc, folder, message_uid, postto);
+       reply_setup_composer (composer, message, identity_uid, to, cc, folder, message_uid, postto);
        e_msg_composer_add_message_attachments (composer, message, TRUE);
 
        if (postto)
@@ -3420,18 +3406,6 @@ em_utils_reply_to_message (EShell *shell,
        g_object_unref (to);
        g_object_unref (cc);
 
-       evo_source_header = camel_medium_get_header (
-               CAMEL_MEDIUM (message), "X-Evolution-Content-Source");
-       if (g_strcmp0 (evo_source_header, "selection") == 0) {
-               EHTMLEditor *editor;
-               EHTMLEditorView *view;
-
-               editor = e_msg_composer_get_editor (composer);
-               view = e_html_editor_get_view (editor);
-
-               e_html_editor_view_set_is_message_from_selection (view, TRUE);
-       }
-
        /* If there was no send-account override */
        if (!identity_uid) {
                EComposerHeaderTable *header_table;
@@ -3488,15 +3462,13 @@ em_utils_reply_to_message (EShell *shell,
        }
 
        /* because some reply types can change recipients after the composer is populated */
-       em_utils_apply_send_account_override_to_composer (composer, shell, folder);
+       em_utils_apply_send_account_override_to_composer (composer, folder);
 
        composer_set_no_change (composer);
 
        gtk_widget_show (GTK_WIDGET (composer));
 
        g_free (identity_uid);
-
-       return composer;
 }
 
 static void
@@ -3684,15 +3656,16 @@ em_utils_check_send_account_override (EShell *shell,
 
 void
 em_utils_apply_send_account_override_to_composer (EMsgComposer *composer,
-                                                  EShell *shell,
                                                   CamelFolder *folder)
 {
        CamelMimeMessage *message;
        EComposerHeaderTable *header_table;
+       EShell *shell;
        ESource *source;
 
        g_return_if_fail (E_IS_MSG_COMPOSER (composer));
 
+       shell = e_msg_composer_get_shell (composer);
        message = em_utils_get_composer_recipients_as_message (composer);
        source = em_utils_check_send_account_override (shell, message, folder);
        g_clear_object (&message);
diff --git a/mail/em-composer-utils.h b/mail/em-composer-utils.h
index fc5c0aa..891f68a 100644
--- a/mail/em-composer-utils.h
+++ b/mail/em-composer-utils.h
@@ -33,28 +33,28 @@
 
 G_BEGIN_DECLS
 
-EMsgComposer * em_utils_compose_new_message    (EShell *shell,
+void           em_utils_compose_new_message    (EMsgComposer *composer,
                                                 CamelFolder *folder);
-EMsgComposer * em_utils_compose_new_message_with_mailto
+void           em_utils_compose_new_message_with_mailto
                                                (EShell *shell,
                                                 const gchar *mailto,
                                                 CamelFolder *folder);
-EMsgComposer * em_utils_edit_message           (EShell *shell,
+void           em_utils_edit_message           (EMsgComposer *composer,
                                                 CamelFolder *folder,
                                                 CamelMimeMessage *message,
                                                 const gchar *message_uid,
                                                 gboolean keep_signature);
-EMsgComposer * em_utils_forward_message        (EMailBackend *backend,
+void           em_utils_forward_message        (EMsgComposer *composer,
                                                 CamelMimeMessage *message,
                                                 EMailForwardStyle style,
                                                 CamelFolder *folder,
                                                 const gchar *uid);
-EMsgComposer * em_utils_forward_attachment     (EMailBackend *backend,
+void           em_utils_forward_attachment     (EMsgComposer *composer,
                                                 CamelMimePart *part,
                                                 const gchar *subject,
                                                 CamelFolder *folder,
                                                 GPtrArray *uids);
-EMsgComposer * em_utils_redirect_message       (EShell *shell,
+void           em_utils_redirect_message       (EMsgComposer *composer,
                                                 CamelMimeMessage *message);
 gchar *                em_utils_construct_composer_text
                                                (CamelSession *session,
@@ -69,7 +69,7 @@ void          em_utils_get_reply_all          (ESourceRegistry *registry,
                                                 CamelInternetAddress *to,
                                                 CamelInternetAddress *cc,
                                                 CamelNNTPAddress *postto);
-EMsgComposer * em_utils_reply_to_message       (EShell *shell,
+void           em_utils_reply_to_message       (EMsgComposer *composer,
                                                 CamelMimeMessage *message,
                                                 CamelFolder *folder,
                                                 const gchar *message_uid,
@@ -92,7 +92,6 @@ ESource *     em_utils_check_send_account_override
                                                 CamelFolder *folder);
 void           em_utils_apply_send_account_override_to_composer
                                                (EMsgComposer *composer,
-                                                EShell *shell,
                                                 CamelFolder *folder);
 
 G_END_DECLS
diff --git a/modules/Makefile.am b/modules/Makefile.am
index 5dbe228..096146b 100644
--- a/modules/Makefile.am
+++ b/modules/Makefile.am
@@ -51,7 +51,8 @@ SUBDIRS = \
        settings \
        startup-wizard \
        vcard-inline \
-       web-inspector \
+       webkit-editor \
+       webkit-inspector \
        $(BOGOFILTER_DIR) \
        $(SPAMASSASSIN_DIR) \
        $(TNEF_ATTACHMENT_DIR) \
diff --git a/modules/addressbook/eab-composer-util.c b/modules/addressbook/eab-composer-util.c
index b2d79de..94e8aaf 100644
--- a/modules/addressbook/eab-composer-util.c
+++ b/modules/addressbook/eab-composer-util.c
@@ -29,28 +29,156 @@
 #include "addressbook/util/eab-book-util.h"
 #include "addressbook/gui/widgets/eab-gui-util.h"
 
+static const gchar *
+get_email (EContact *contact,
+           EContactField field_id,
+           gchar **to_free)
+{
+       gchar *name = NULL, *mail = NULL;
+       const gchar *value = e_contact_get_const (contact, field_id);
+
+       *to_free = NULL;
+
+       if (eab_parse_qp_email (value, &name, &mail)) {
+               *to_free = g_strdup_printf ("%s <%s>", name, mail);
+               value = *to_free;
+       }
+
+       g_free (name);
+       g_free (mail);
+
+       return value;
+}
+
+typedef struct _CreateComposerData {
+       EDestination **to_destinations;
+       EDestination **bcc_destinations;
+       GSList *attachment_destinations;
+} CreateComposerData;
+
+static void
+eab_composer_created_cb (GObject *source_object,
+                        GAsyncResult *result,
+                        gpointer user_data)
+{
+       CreateComposerData *ccd = user_data;
+       EComposerHeaderTable *table;
+       EMsgComposer *composer;
+       GError *error = NULL;
+
+       g_return_if_fail (ccd != NULL);
+
+       composer = e_msg_composer_new_finish (result, &error);
+       if (error) {
+               g_warning ("%s: Failed to create msg composer: %s", G_STRFUNC, error->message);
+               g_clear_error (&error);
+       } else {
+               table = e_msg_composer_get_header_table (composer);
+
+               if (ccd->to_destinations)
+                       e_composer_header_table_set_destinations_to (table, ccd->to_destinations);
+
+               if (ccd->bcc_destinations)
+                       e_composer_header_table_set_destinations_bcc (table, ccd->bcc_destinations);
+
+               if (ccd->attachment_destinations) {
+                       CamelMimePart *attachment;
+                       GSList *contacts, *iter;
+                       gchar *data;
+
+                       attachment = camel_mime_part_new ();
+
+                       contacts = g_slist_copy (ccd->attachment_destinations);
+                       for (iter = contacts; iter != NULL; iter = iter->next)
+                               iter->data = e_destination_get_contact (iter->data);
+                       data = eab_contact_list_to_string (contacts);
+                       g_slist_free (contacts);
+
+                       camel_mime_part_set_content (attachment, data, strlen (data), "text/x-vcard");
+
+                       if (ccd->attachment_destinations->next != NULL) {
+                               camel_mime_part_set_description (attachment, _("Multiple vCards"));
+                       } else {
+                               EContact *contact;
+                               const gchar *file_as;
+                               gchar *description;
+
+                               contact = e_destination_get_contact (ccd->attachment_destinations->data);
+                               file_as = e_contact_get_const (contact, E_CONTACT_FILE_AS);
+                               description = g_strdup_printf (_("vCard for %s"), file_as);
+                               camel_mime_part_set_description (attachment, description);
+                               g_free (description);
+                       }
+
+                       camel_mime_part_set_disposition (attachment, "attachment");
+
+                       e_msg_composer_attach (composer, attachment);
+                       g_object_unref (attachment);
+
+                       if (ccd->attachment_destinations->next != NULL) {
+                               e_composer_header_table_set_subject (table, _("Contact information"));
+                       } else {
+                               EContact *contact;
+                               gchar *tempstr;
+                               const gchar *tempstr2;
+                               gchar *tempfree = NULL;
+
+                               contact = e_destination_get_contact (ccd->attachment_destinations->data);
+                               tempstr2 = e_contact_get_const (contact, E_CONTACT_FILE_AS);
+                               if (!tempstr2 || !*tempstr2)
+                                       tempstr2 = e_contact_get_const (contact, E_CONTACT_FULL_NAME);
+                               if (!tempstr2 || !*tempstr2)
+                                       tempstr2 = e_contact_get_const (contact, E_CONTACT_ORG);
+                               if (!tempstr2 || !*tempstr2) {
+                                       g_free (tempfree);
+                                       tempstr2 = get_email (contact, E_CONTACT_EMAIL_1, &tempfree);
+                               }
+                               if (!tempstr2 || !*tempstr2) {
+                                       g_free (tempfree);
+                                       tempstr2 = get_email (contact, E_CONTACT_EMAIL_2, &tempfree);
+                               }
+                               if (!tempstr2 || !*tempstr2) {
+                                       g_free (tempfree);
+                                       tempstr2 = get_email (contact, E_CONTACT_EMAIL_3, &tempfree);
+                               }
+
+                               if (!tempstr2 || !*tempstr2)
+                                       tempstr = g_strdup_printf (_("Contact information"));
+                               else
+                                       tempstr = g_strdup_printf (_("Contact information for %s"), tempstr2);
+
+                               e_composer_header_table_set_subject (table, tempstr);
+
+                               g_free (tempstr);
+                               g_free (tempfree);
+                       }
+               }
+
+               gtk_widget_show (GTK_WIDGET (composer));
+       }
+
+       if (ccd->to_destinations)
+               e_destination_freev (ccd->to_destinations);
+       if (ccd->bcc_destinations)
+               e_destination_freev (ccd->bcc_destinations);
+       g_slist_free_full (ccd->attachment_destinations, g_object_unref);
+
+       g_free (ccd);
+}
+
 void
 eab_send_as_to (EShell *shell,
                 GSList *destinations)
 {
-       EMsgComposer *composer;
-       EComposerHeaderTable *table;
        GPtrArray *to_array;
        GPtrArray *bcc_array;
-
-       union {
-               gpointer *pdata;
-               EDestination **destinations;
-       } convert;
+       CreateComposerData *ccd;
 
        g_return_if_fail (E_IS_SHELL (shell));
 
        if (destinations == NULL)
                return;
 
-       composer = e_msg_composer_new (shell);
-       table = e_msg_composer_get_header_table (composer);
-
        to_array = g_ptr_array_new ();
        bcc_array = g_ptr_array_new ();
 
@@ -60,11 +188,11 @@ eab_send_as_to (EShell *shell,
 
                if (e_destination_is_evolution_list (destination)) {
                        if (e_destination_list_show_addresses (destination))
-                               g_ptr_array_add (to_array, destination);
+                               g_ptr_array_add (to_array, e_destination_copy (destination));
                        else
-                               g_ptr_array_add (bcc_array, destination);
+                               g_ptr_array_add (bcc_array, e_destination_copy (destination));
                } else
-                       g_ptr_array_add (to_array, destination);
+                       g_ptr_array_add (to_array, e_destination_copy (destination));
 
                destinations = g_slist_next (destinations);
        }
@@ -73,133 +201,28 @@ eab_send_as_to (EShell *shell,
        g_ptr_array_add (to_array, NULL);
        g_ptr_array_add (bcc_array, NULL);
 
-       /* XXX Acrobatics like this make me question whether NULL-terminated
-        *     arrays are really the best argument type for passing a list of
-        *     destinations to the header table. */
-
-       /* Set "To" destinations. */
-       convert.pdata = to_array->pdata;
-       e_composer_header_table_set_destinations_to (
-               table, convert.destinations);
-       g_ptr_array_free (to_array, FALSE);
-
-       /* Add "Bcc" destinations. */
-       convert.pdata = bcc_array->pdata;
-       e_composer_header_table_add_destinations_bcc (
-               table, convert.destinations);
-       g_ptr_array_free (bcc_array, FALSE);
+       ccd = g_new0 (CreateComposerData, 1);
+       ccd->to_destinations = (EDestination **) g_ptr_array_free (to_array, FALSE);
+       ccd->bcc_destinations = (EDestination **) g_ptr_array_free (bcc_array, FALSE);
+       ccd->attachment_destinations = NULL;
 
-       gtk_widget_show (GTK_WIDGET (composer));
-}
-
-static const gchar *
-get_email (EContact *contact,
-           EContactField field_id,
-           gchar **to_free)
-{
-       gchar *name = NULL, *mail = NULL;
-       const gchar *value = e_contact_get_const (contact, field_id);
-
-       *to_free = NULL;
-
-       if (eab_parse_qp_email (value, &name, &mail)) {
-               *to_free = g_strdup_printf ("%s <%s>", name, mail);
-               value = *to_free;
-       }
-
-       g_free (name);
-       g_free (mail);
-
-       return value;
+       e_msg_composer_new (shell, eab_composer_created_cb, ccd);
 }
 
 void
 eab_send_as_attachment (EShell *shell,
                         GSList *destinations)
 {
-       EMsgComposer *composer;
-       EComposerHeaderTable *table;
-       CamelMimePart *attachment;
-       GSList *contacts, *iter;
-       gchar *data;
+       CreateComposerData *ccd;
 
        g_return_if_fail (E_IS_SHELL (shell));
 
        if (destinations == NULL)
                return;
 
-       composer = e_msg_composer_new (shell);
-       table = e_msg_composer_get_header_table (composer);
-
-       attachment = camel_mime_part_new ();
-
-       contacts = g_slist_copy (destinations);
-       for (iter = contacts; iter != NULL; iter = iter->next)
-               iter->data = e_destination_get_contact (iter->data);
-       data = eab_contact_list_to_string (contacts);
-       g_slist_free (contacts);
-
-       camel_mime_part_set_content (
-               attachment, data, strlen (data), "text/x-vcard");
-
-       if (destinations->next != NULL)
-               camel_mime_part_set_description (
-                       attachment, _("Multiple vCards"));
-       else {
-               EContact *contact;
-               const gchar *file_as;
-               gchar *description;
-
-               contact = e_destination_get_contact (destinations->data);
-               file_as = e_contact_get_const (contact, E_CONTACT_FILE_AS);
-               description = g_strdup_printf (_("vCard for %s"), file_as);
-               camel_mime_part_set_description (attachment, description);
-               g_free (description);
-       }
-
-       camel_mime_part_set_disposition (attachment, "attachment");
-
-       e_msg_composer_attach (composer, attachment);
-       g_object_unref (attachment);
-
-       if (destinations->next != NULL)
-               e_composer_header_table_set_subject (
-                       table, _("Contact information"));
-       else {
-               EContact *contact;
-               gchar *tempstr;
-               const gchar *tempstr2;
-               gchar *tempfree = NULL;
-
-               contact = e_destination_get_contact (destinations->data);
-               tempstr2 = e_contact_get_const (contact, E_CONTACT_FILE_AS);
-               if (!tempstr2 || !*tempstr2)
-                       tempstr2 = e_contact_get_const (contact, E_CONTACT_FULL_NAME);
-               if (!tempstr2 || !*tempstr2)
-                       tempstr2 = e_contact_get_const (contact, E_CONTACT_ORG);
-               if (!tempstr2 || !*tempstr2) {
-                       g_free (tempfree);
-                       tempstr2 = get_email (contact, E_CONTACT_EMAIL_1, &tempfree);
-               }
-               if (!tempstr2 || !*tempstr2) {
-                       g_free (tempfree);
-                       tempstr2 = get_email (contact, E_CONTACT_EMAIL_2, &tempfree);
-               }
-               if (!tempstr2 || !*tempstr2) {
-                       g_free (tempfree);
-                       tempstr2 = get_email (contact, E_CONTACT_EMAIL_3, &tempfree);
-               }
-
-               if (!tempstr2 || !*tempstr2)
-                       tempstr = g_strdup_printf (_("Contact information"));
-               else
-                       tempstr = g_strdup_printf (_("Contact information for %s"), tempstr2);
-
-               e_composer_header_table_set_subject (table, tempstr);
-
-               g_free (tempstr);
-               g_free (tempfree);
-       }
+       ccd = g_new0 (CreateComposerData, 1);
+       ccd->attachment_destinations = g_slist_copy (destinations);
+       g_slist_foreach (ccd->attachment_destinations, (GFunc) g_object_ref, NULL);
 
-       gtk_widget_show (GTK_WIDGET (composer));
+       e_msg_composer_new (shell, eab_composer_created_cb, ccd);
 }
diff --git a/modules/composer-autosave/e-autosave-utils.c b/modules/composer-autosave/e-autosave-utils.c
index 2e83b2a..19a64b1 100644
--- a/modules/composer-autosave/e-autosave-utils.c
+++ b/modules/composer-autosave/e-autosave-utils.c
@@ -116,6 +116,43 @@ create_snapshot_file (EMsgComposer *composer,
        return snapshot_file;
 }
 
+typedef struct _CreateComposerData {
+       GSimpleAsyncResult *simple;
+       LoadContext *context;
+       CamelMimeMessage *message;
+       GFile *snapshot_file;
+} CreateComposerData;
+
+static void
+autosave_composer_created_cb (GObject *source_object,
+                             GAsyncResult *result,
+                             gpointer user_data)
+{
+       CreateComposerData *ccd = user_data;
+       EMsgComposer *composer;
+       GError *error = NULL;
+
+       composer = e_msg_composer_new_finish (result, &error);
+       if (error) {
+               g_warning ("%s: Failed to create msg composer: %s", G_STRFUNC, error->message);
+               g_simple_async_result_take_error (ccd->simple, error);
+       } else {
+               e_msg_composer_setup_with_message (composer, ccd->message, TRUE, NULL, NULL);
+               g_object_set_data_full (
+                       G_OBJECT (composer),
+                       SNAPSHOT_FILE_KEY, g_object_ref (ccd->snapshot_file),
+                       (GDestroyNotify) delete_snapshot_file);
+               ccd->context->composer = g_object_ref_sink (composer);
+       }
+
+       g_simple_async_result_complete (ccd->simple);
+
+       g_clear_object (&ccd->simple);
+       g_clear_object (&ccd->message);
+       g_clear_object (&ccd->snapshot_file);
+       g_free (ccd);
+}
+
 static void
 load_snapshot_loaded_cb (GFile *snapshot_file,
                          GAsyncResult *result,
@@ -124,11 +161,11 @@ load_snapshot_loaded_cb (GFile *snapshot_file,
        EShell *shell;
        GObject *object;
        LoadContext *context;
-       EMsgComposer *composer;
        CamelMimeMessage *message;
        CamelStream *camel_stream;
        gchar *contents = NULL;
        gsize length;
+       CreateComposerData *ccd;
        GError *local_error = NULL;
 
        context = g_simple_async_result_get_op_res_gpointer (simple);
@@ -140,6 +177,7 @@ load_snapshot_loaded_cb (GFile *snapshot_file,
                g_warn_if_fail (contents == NULL);
                g_simple_async_result_take_error (simple, local_error);
                g_simple_async_result_complete (simple);
+               g_object_unref (simple);
                return;
        }
 
@@ -157,6 +195,7 @@ load_snapshot_loaded_cb (GFile *snapshot_file,
                g_simple_async_result_take_error (simple, local_error);
                g_simple_async_result_complete (simple);
                g_object_unref (message);
+               g_object_unref (simple);
                return;
        }
 
@@ -167,19 +206,16 @@ load_snapshot_loaded_cb (GFile *snapshot_file,
         * restore its snapshot file so it continues auto-saving to
         * the same file. */
        shell = E_SHELL (object);
-       g_object_ref (snapshot_file);
-       composer = e_msg_composer_new_with_message (shell, message, TRUE, NULL, NULL);
-       g_object_set_data_full (
-               G_OBJECT (composer),
-               SNAPSHOT_FILE_KEY, snapshot_file,
-               (GDestroyNotify) delete_snapshot_file);
-       context->composer = g_object_ref_sink (composer);
-       g_object_unref (message);
 
-       g_object_unref (object);
+       ccd = g_new0 (CreateComposerData, 1);
+       ccd->simple = simple;
+       ccd->context = context;
+       ccd->message = message;
+       ccd->snapshot_file = g_object_ref (snapshot_file);
 
-       g_simple_async_result_complete (simple);
-       g_object_unref (simple);
+       e_msg_composer_new (shell, autosave_composer_created_cb, ccd);
+
+       g_object_unref (object);
 }
 
 static void
diff --git a/modules/composer-autosave/e-composer-autosave.c b/modules/composer-autosave/e-composer-autosave.c
index c794593..eabc2e1 100644
--- a/modules/composer-autosave/e-composer-autosave.c
+++ b/modules/composer-autosave/e-composer-autosave.c
@@ -120,15 +120,15 @@ static void
 composer_autosave_changed_cb (EComposerAutosave *autosave)
 {
        EHTMLEditor *editor;
-       EHTMLEditorView *view;
+       EContentEditor *cnt_editor;
        EExtensible *extensible;
 
        extensible = e_extension_get_extensible (E_EXTENSION (autosave));
 
        editor = e_msg_composer_get_editor (E_MSG_COMPOSER (extensible));
-       view = e_html_editor_get_view (editor);
+       cnt_editor = e_html_editor_get_content_editor (editor);
 
-       if (autosave->priv->timeout_id == 0 && e_html_editor_view_get_changed (view)) {
+       if (autosave->priv->timeout_id == 0 && e_content_editor_get_changed (cnt_editor)) {
                autosave->priv->timeout_id = e_named_timeout_add_seconds (
                        AUTOSAVE_INTERVAL,
                        composer_autosave_timeout_cb, autosave);
@@ -160,7 +160,7 @@ static void
 composer_autosave_constructed (GObject *object)
 {
        EHTMLEditor *editor;
-       EHTMLEditorView *view;
+       EContentEditor *cnt_editor;
        EExtensible *extensible;
 
        /* Chain up to parent's constructed() method. */
@@ -168,12 +168,12 @@ composer_autosave_constructed (GObject *object)
 
        extensible = e_extension_get_extensible (E_EXTENSION (object));
        editor = e_msg_composer_get_editor (E_MSG_COMPOSER (extensible));
-       view = e_html_editor_get_view (editor);
+       cnt_editor = e_html_editor_get_content_editor (editor);
 
        /* Do not use e_signal_connect_notify_swapped() here,
           this module relies on "false" change notifications. */
        g_signal_connect_swapped (
-               view, "notify::changed",
+               cnt_editor, "notify::changed",
                G_CALLBACK (composer_autosave_changed_cb), object);
 }
 
diff --git a/modules/itip-formatter/Makefile.am b/modules/itip-formatter/Makefile.am
index 9252ad8..f8a6241 100644
--- a/modules/itip-formatter/Makefile.am
+++ b/modules/itip-formatter/Makefile.am
@@ -1,3 +1,5 @@
+SUBDIRS = web-extension
+
 @EVO_PLUGIN_RULE@
 
 module_LTLIBRARIES = module-itip-formatter.la
@@ -21,7 +23,8 @@ module_itip_formatter_la_SOURCES =                                    \
        e-mail-part-itip.h                                              \
        itip-view.c                                                     \
        itip-view.h                                                     \
-       evolution-module-itip-formatter.c
+       evolution-module-itip-formatter.c                               \
+       itip-view-elements-defines.h
 
 module_itip_formatter_la_LIBADD =                                      \
        $(top_builddir)/e-util/libevolution-util.la                     \
diff --git a/modules/itip-formatter/e-mail-formatter-itip.c b/modules/itip-formatter/e-mail-formatter-itip.c
index 1439cf1..9ac5751 100644
--- a/modules/itip-formatter/e-mail-formatter-itip.c
+++ b/modules/itip-formatter/e-mail-formatter-itip.c
@@ -66,18 +66,25 @@ emfe_itip_format (EMailFormatterExtension *extension,
        itip_part = (EMailPartItip *) part;
 
        if (context->mode == E_MAIL_FORMATTER_MODE_PRINTING) {
-               buffer = g_string_sized_new (1024);
+               ItipView *itip_view;
 
-               itip_part->view = itip_view_new (
-                       itip_part, itip_part->client_cache);
+               buffer = g_string_sized_new (1024);
 
-               itip_view_init_view (itip_part->view);
-               itip_view_write_for_printing (itip_part->view, buffer);
+               itip_view = itip_view_new (0, e_mail_part_get_id (part),
+                       itip_part,
+                       itip_part->folder,
+                       itip_part->message_uid,
+                       itip_part->message,
+                       itip_part->itip_mime_part,
+                       itip_part->vcalendar,
+                       itip_part->cancellable);
+               itip_view_init_view (itip_view);
+               itip_view_write_for_printing (itip_view, buffer);
 
        } else if (context->mode == E_MAIL_FORMATTER_MODE_RAW) {
                buffer = g_string_sized_new (2048);
 
-               itip_view_write (formatter, buffer);
+               itip_view_write (itip_part, formatter, buffer);
 
        } else {
                CamelFolder *folder;
@@ -101,8 +108,8 @@ emfe_itip_format (EMailFormatterExtension *extension,
                }
 
                itip_part->folder = g_object_ref (folder);
-               itip_part->uid = g_strdup (message_uid);
-               itip_part->msg = g_object_ref (message);
+               itip_part->message_uid = g_strdup (message_uid);
+               itip_part->message = g_object_ref (message);
 
                default_charset = e_mail_formatter_get_default_charset (formatter);
                charset = e_mail_formatter_get_charset (formatter);
diff --git a/modules/itip-formatter/e-mail-parser-itip.c b/modules/itip-formatter/e-mail-parser-itip.c
index bd2e961..6b60153 100644
--- a/modules/itip-formatter/e-mail-parser-itip.c
+++ b/modules/itip-formatter/e-mail-parser-itip.c
@@ -38,8 +38,6 @@
 #include "e-mail-part-itip.h"
 #include "itip-view.h"
 
-#define CONF_KEY_DELETE "delete-processed"
-
 #define d(x)
 
 typedef EMailParserExtension EMailParserItip;
@@ -69,9 +67,6 @@ empe_itip_parse (EMailParserExtension *extension,
                  GCancellable *cancellable,
                  GQueue *out_mail_parts)
 {
-       EShell *shell;
-       GSettings *settings;
-       EClientCache *client_cache;
        EMailPartItip *itip_part;
        CamelDataWrapper *content;
        CamelStream *stream;
@@ -83,21 +78,8 @@ empe_itip_parse (EMailParserExtension *extension,
        len = part_id->len;
        g_string_append_printf (part_id, ".itip");
 
-       settings = e_util_ref_settings ("org.gnome.evolution.plugin.itip");
-
-       shell = e_shell_get_default ();
-       client_cache = e_shell_get_client_cache (shell);
-
        itip_part = e_mail_part_itip_new (part, part_id->str);
-       itip_part->delete_message = g_settings_get_boolean (settings, CONF_KEY_DELETE);
-       itip_part->has_organizer = FALSE;
-       itip_part->no_reply_wanted = FALSE;
-       itip_part->part = part;
-       itip_part->cancellable = g_cancellable_new ();
-       itip_part->real_comps = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_object_unref);
-       itip_part->client_cache = g_object_ref (client_cache);
-
-       g_object_unref (settings);
+       itip_part->itip_mime_part = g_object_ref (part);
 
        /* This is non-gui thread. Download the part for using in the main thread */
        content = camel_medium_get_content ((CamelMedium *) part);
@@ -153,4 +135,3 @@ e_mail_parser_itip_type_register (GTypeModule *type_module)
 {
        e_mail_parser_itip_register_type (type_module);
 }
-
diff --git a/modules/itip-formatter/e-mail-part-itip.c b/modules/itip-formatter/e-mail-part-itip.c
index 8a84ae7..fbb4ffb 100644
--- a/modules/itip-formatter/e-mail-part-itip.c
+++ b/modules/itip-formatter/e-mail-part-itip.c
@@ -15,6 +15,13 @@
  *
  */
 
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <string.h>
+#include <e-util/e-util.h>
+
 #include "e-mail-part-itip.h"
 
 #define E_MAIL_PART_ITIP_GET_PRIVATE(obj) \
@@ -22,7 +29,7 @@
        ((obj), E_TYPE_MAIL_PART_ITIP, EMailPartItipPrivate))
 
 struct _EMailPartItipPrivate {
-       gint placeholder;
+       GSList *views; /* ItipView * */
 };
 
 G_DEFINE_DYNAMIC_TYPE (
@@ -37,10 +44,16 @@ mail_part_itip_dispose (GObject *object)
 
        g_cancellable_cancel (part->cancellable);
 
+       g_free (part->message_uid);
+       part->message_uid = NULL;
+
+       g_free (part->vcalendar);
+       part->vcalendar = NULL;
+
+       g_clear_object (&part->folder);
+       g_clear_object (&part->message);
+       g_clear_object (&part->itip_mime_part);
        g_clear_object (&part->cancellable);
-       g_clear_object (&part->client_cache);
-       g_clear_object (&part->comp);
-       g_clear_object (&part->view);
 
        /* Chain up to parent's dispose() method. */
        G_OBJECT_CLASS (e_mail_part_itip_parent_class)->dispose (object);
@@ -51,66 +64,48 @@ mail_part_itip_finalize (GObject *object)
 {
        EMailPartItip *part = E_MAIL_PART_ITIP (object);
 
-       g_free (part->vcalendar);
-       g_free (part->calendar_uid);
-       g_free (part->from_address);
-       g_free (part->from_name);
-       g_free (part->to_address);
-       g_free (part->to_name);
-       g_free (part->delegator_address);
-       g_free (part->delegator_name);
-       g_free (part->my_address);
-       g_free (part->uid);
-
-       if (part->top_level != NULL)
-               icalcomponent_free (part->top_level);
-
-       if (part->main_comp != NULL)
-               icalcomponent_free (part->main_comp);
-
-       g_hash_table_destroy (part->real_comps);
+       g_slist_free_full (part->priv->views, g_object_unref);
+       part->priv->views = NULL;
 
        /* Chain up to parent's finalize() method. */
        G_OBJECT_CLASS (e_mail_part_itip_parent_class)->finalize (object);
 }
 
 static void
-mail_part_itip_bind_dom_element (EMailPart *part,
-                                 WebKitDOMElement *element)
+mail_part_itip_web_view_loaded (EMailPart *mail_part,
+                               EWebView *web_view)
 {
-       GString *buffer;
-       WebKitDOMDocument *document;
-       WebKitDOMElement *bind_element, *document_element;
-       ItipView *view;
        EMailPartItip *pitip;
-
-       pitip = E_MAIL_PART_ITIP (part);
-
-       bind_element = element;
-       if (!WEBKIT_DOM_IS_HTML_IFRAME_ELEMENT (bind_element))
-               element = webkit_dom_element_query_selector (element, "iframe", NULL);
-
-       g_return_if_fail (WEBKIT_DOM_IS_HTML_IFRAME_ELEMENT (element));
-
-       /* A view is already assigned for this element. */
-       if (g_object_get_data (G_OBJECT (element), "view"))
-               return;
-
-       buffer = g_string_new ("");
-       document = webkit_dom_html_iframe_element_get_content_document (
-               WEBKIT_DOM_HTML_IFRAME_ELEMENT (element));
-
-       view = itip_view_new (pitip, pitip->client_cache);
-       g_object_set_data_full (
-               G_OBJECT (element), "view", view,
-               (GDestroyNotify) g_object_unref);
-
-       document_element = webkit_dom_document_get_document_element (document);
-       itip_view_create_dom_bindings (view, document_element);
-       g_object_unref (document_element);
-
-       itip_view_init_view (view);
-       g_string_free (buffer, TRUE);
+       ItipView *itip_view;
+
+       g_return_if_fail (E_IS_MAIL_PART_ITIP (mail_part));
+       g_return_if_fail (E_IS_WEB_VIEW (web_view));
+
+       pitip = E_MAIL_PART_ITIP (mail_part);
+
+       /* FIXME WK2 - it can sometimes happen that the pitip members, like the folder, message_uid and 
message,
+          are not initialized yet, because the internal frame in the main EWebView is not passed
+          through the EMailFormatter, where these are set. This requires a new signal on the WebKitWebView,
+          ideally, to call this only after the iframe is truly loaded (these pitip members are only a side
+          effect of a whole issue with non-knowing that a particular iframe was fully loaded).
+
+          Also retest what happens when the same meeting is opened in multiple windows; it could crash in 
gtk+
+          when a button was clicked in one or the other, but also not always.
+       */
+       itip_view = itip_view_new (
+               webkit_web_view_get_page_id (WEBKIT_WEB_VIEW (web_view)),
+               e_mail_part_get_id (mail_part),
+               pitip,
+               pitip->folder,
+               pitip->message_uid,
+               pitip->message,
+               pitip->itip_mime_part,
+               pitip->vcalendar,
+               pitip->cancellable);
+
+       itip_view_set_web_view (itip_view, web_view);
+
+       pitip->priv->views = g_slist_prepend (pitip->priv->views, itip_view);
 }
 
 static void
@@ -126,7 +121,7 @@ e_mail_part_itip_class_init (EMailPartItipClass *class)
        object_class->finalize = mail_part_itip_finalize;
 
        mail_part_class = E_MAIL_PART_CLASS (class);
-       mail_part_class->bind_dom_element = mail_part_itip_bind_dom_element;
+       mail_part_class->web_view_loaded = mail_part_itip_web_view_loaded;
 }
 
 static void
@@ -138,6 +133,7 @@ static void
 e_mail_part_itip_init (EMailPartItip *part)
 {
        part->priv = E_MAIL_PART_ITIP_GET_PRIVATE (part);
+       part->cancellable = g_cancellable_new ();
 
        e_mail_part_set_mime_type (E_MAIL_PART (part), "text/calendar");
 
@@ -163,4 +159,3 @@ e_mail_part_itip_new (CamelMimePart *mime_part,
                E_TYPE_MAIL_PART_ITIP,
                "id", id, "mime-part", mime_part, NULL);
 }
-
diff --git a/modules/itip-formatter/e-mail-part-itip.h b/modules/itip-formatter/e-mail-part-itip.h
index 42c1828..49f5374 100644
--- a/modules/itip-formatter/e-mail-part-itip.h
+++ b/modules/itip-formatter/e-mail-part-itip.h
@@ -55,79 +55,13 @@ struct _EMailPartItip {
        EMailPartItipPrivate *priv;
 
        CamelFolder *folder;
-       CamelMimeMessage *msg;
-       CamelMimePart *part;
-
-       gchar *uid;
-
-       EClientCache *client_cache;
-
-       ECalClient *current_client;
-       ECalClientSourceType type;
+       CamelMimeMessage *message;
+       gchar *message_uid;
+       CamelMimePart *itip_mime_part;
+       gchar *vcalendar;
 
        /* cancelled when freeing the puri */
        GCancellable *cancellable;
-
-       gchar *vcalendar;
-       ECalComponent *comp;
-       icalcomponent *main_comp;
-       icalcomponent *ical_comp;
-       icalcomponent *top_level;
-       icalcompiter iter;
-       icalproperty_method method;
-       time_t start_time;
-       time_t end_time;
-
-       gint current;
-       gint total;
-
-       gchar *calendar_uid;
-
-       gchar *from_address;
-       gchar *from_name;
-       gchar *to_address;
-       gchar *to_name;
-       gchar *delegator_address;
-       gchar *delegator_name;
-       gchar *my_address;
-       gint   view_only;
-
-       guint progress_info_id;
-
-       gboolean delete_message;
-       /* a reply can only be sent if and only if there is an organizer */
-       gboolean has_organizer;
-       /*
-        * Usually replies are sent unless the user unchecks that option.
-        * There are some cases when the default is not to sent a reply
-        * (but the user can still chose to do so by checking the option):
-        * - the organizer explicitly set RSVP=FALSE for the current user
-        * - the event has no ATTENDEEs: that's the case for most non-meeting
-        *   events
-        *
-        * The last case is meant for forwarded non-meeting
-        * events. Traditionally Evolution hasn't offered to send a
-        * reply, therefore the updated implementation mimics that
-        * behavior.
-        *
-        * Unfortunately some software apparently strips all ATTENDEEs
-        * when forwarding a meeting; in that case sending a reply is
-        * also unchecked by default. So the check for ATTENDEEs is a
-        * tradeoff between sending unwanted replies in cases where
-        * that wasn't done in the past and not sending a possibly
-        * wanted reply where that wasn't possible in the past
-        * (because replies to forwarded events were not
-        * supported). Overall that should be an improvement, and the
-        * user can always override the default.
-        */
-       gboolean no_reply_wanted;
-
-       guint update_item_progress_info_id;
-       guint update_item_error_info_id;
-       ItipViewResponse update_item_response;
-       GHashTable *real_comps; /* ESource's UID -> ECalComponent stored on the server */
-
-       ItipView *view;
 };
 
 struct _EMailPartItipClass {
diff --git a/modules/itip-formatter/itip-view-elements-defines.h 
b/modules/itip-formatter/itip-view-elements-defines.h
new file mode 100644
index 0000000..35bf653
--- /dev/null
+++ b/modules/itip-formatter/itip-view-elements-defines.h
@@ -0,0 +1,65 @@
+/*
+ * itip-view-elements-defines.h
+ *
+ * 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; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+#ifndef ITIP_VIEW_ELEMENTS_DEFINES_H
+#define ITIP_VIEW_ELEMENTS_DEFINES_H
+
+#define TEXT_ROW_SENDER "text_row_sender"
+#define TABLE_ROW_SUMMARY "table_row_summary"
+#define TABLE_ROW_LOCATION "table_row_location"
+#define TABLE_ROW_START_DATE "table_row_start_time"
+#define TABLE_ROW_END_DATE "table_row_end_time"
+#define TABLE_ROW_STATUS "table_row_status"
+#define TABLE_ROW_COMMENT "table_row_comment"
+#define TABLE_ROW_DESCRIPTION "table_row_description"
+#define TABLE_ROW_RSVP_COMMENT "table_row_rsvp_comment"
+#define TABLE_ROW_ESCB "table_row_escb"
+#define TABLE_ROW_BUTTONS "table_row_buttons"
+#define TABLE_ROW_ESCB_LABEL "table_row_escb_label"
+
+#define TABLE_BUTTONS "table_buttons"
+
+#define SELECT_ESOURCE "select_esource"
+#define TEXTAREA_RSVP_COMMENT "textarea_rsvp_comment"
+
+#define CHECKBOX_RSVP "checkbox_rsvp"
+#define CHECKBOX_RECUR "checkbox_recur"
+#define CHECKBOX_UPDATE "checkbox_update"
+#define CHECKBOX_FREE_TIME "checkbox_free_time"
+#define CHECKBOX_KEEP_ALARM "checkbox_keep_alarm"
+#define CHECKBOX_INHERIT_ALARM "checkbox_inherit_alarm"
+
+#define BUTTON_OPEN_CALENDAR "button_open_calendar"
+#define BUTTON_DECLINE "button_decline"
+#define BUTTON_DECLINE_ALL "button_decline_all"
+#define BUTTON_ACCEPT "button_accept"
+#define BUTTON_ACCEPT_ALL "button_accept_all"
+#define BUTTON_TENTATIVE "button_tentative"
+#define BUTTON_TENTATIVE_ALL "button_tentative_all"
+#define BUTTON_SEND_INFORMATION "button_send_information"
+#define BUTTON_UPDATE "button_update"
+#define BUTTON_UPDATE_ATTENDEE_STATUS "button_update_attendee_status"
+#define BUTTON_SAVE "button_save"
+
+#define TABLE_UPPER_ITIP_INFO "table_upper_itip_info"
+#define TABLE_LOWER_ITIP_INFO "table_lower_itip_info"
+
+#define DIV_ITIP_CONTENT "div_itip_content"
+#define DIV_ITIP_ERROR "div_itip_error"
+
+#endif /* ITIP_VIEW_ELEMENTS_DEFINES_H */
diff --git a/modules/itip-formatter/itip-view.c b/modules/itip-formatter/itip-view.c
index 42b7669..482d2c1 100644
--- a/modules/itip-formatter/itip-view.c
+++ b/modules/itip-formatter/itip-view.c
@@ -25,7 +25,6 @@
 
 #include <string.h>
 #include <glib/gi18n.h>
-#include <webkit/webkitdom.h>
 #include <libedataserver/libedataserver.h>
 
 #include <shell/e-shell.h>
@@ -40,6 +39,10 @@
 #include "itip-view.h"
 #include "e-mail-part-itip.h"
 
+#include "itip-view-elements-defines.h"
+
+#include "web-extension/module-itip-formatter-web-extension.h"
+
 #define d(x)
 
 #define MEETING_ICON "stock_people"
@@ -105,54 +108,85 @@ struct _ItipViewPrivate {
 
        gint needs_decline : 1;
 
-        WebKitDOMDocument *dom_document;
-        EMailPartItip *itip_part;
+        gpointer itip_part_ptr; /* not referenced, only for a "reference" to which part this belongs */
+
+       GDBusProxy *web_extension;
+       guint web_extension_watch_name_id;
+       guint web_extension_source_changed_cb_signal_id;
+       guint web_extension_recur_toggled_signal_id;
+
+       guint64 page_id;
+       gchar *part_id;
 
         gchar *error;
-};
+       GWeakRef *web_view_weakref;
+
+       CamelFolder *folder;
+       CamelMimeMessage *message;
+       gchar *message_uid;
+       CamelMimePart *itip_mime_part;
+       GCancellable *cancellable;
 
-#define TEXT_ROW_SENDER "text_row_sender"
-#define TABLE_ROW_SUMMARY "table_row_summary"
-#define TABLE_ROW_LOCATION "table_row_location"
-#define TABLE_ROW_START_DATE "table_row_start_time"
-#define TABLE_ROW_END_DATE "table_row_end_time"
-#define TABLE_ROW_STATUS "table_row_status"
-#define TABLE_ROW_COMMENT "table_row_comment"
-#define TABLE_ROW_DESCRIPTION "table_row_description"
-#define TABLE_ROW_RSVP_COMMENT "table_row_rsvp_comment"
-#define TABLE_ROW_ESCB "table_row_escb"
-#define TABLE_ROW_BUTTONS "table_row_buttons"
-#define TABLE_ROW_ESCB_LABEL "table_row_escb_label"
-
-#define TABLE_BUTTONS "table_buttons"
-
-#define SELECT_ESOURCE "select_esource"
-#define TEXTAREA_RSVP_COMMENT "textarea_rsvp_comment"
-
-#define CHECKBOX_RSVP "checkbox_rsvp"
-#define CHECKBOX_RECUR "checkbox_recur"
-#define CHECKBOX_UPDATE "checkbox_update"
-#define CHECKBOX_FREE_TIME "checkbox_free_time"
-#define CHECKBOX_KEEP_ALARM "checkbox_keep_alarm"
-#define CHECKBOX_INHERIT_ALARM "checkbox_inherit_alarm"
-
-#define BUTTON_OPEN_CALENDAR "button_open_calendar"
-#define BUTTON_DECLINE "button_decline"
-#define BUTTON_DECLINE_ALL "button_decline_all"
-#define BUTTON_ACCEPT "button_accept"
-#define BUTTON_ACCEPT_ALL "button_accept_all"
-#define BUTTON_TENTATIVE "button_tentative"
-#define BUTTON_TENTATIVE_ALL "button_tentative_all"
-#define BUTTON_SEND_INFORMATION "button_send_information"
-#define BUTTON_UPDATE "button_update"
-#define BUTTON_UPDATE_ATTENDEE_STATUS "button_update_attendee_status"
-#define BUTTON_SAVE "button_save"
-
-#define TABLE_UPPER_ITIP_INFO "table_upper_itip_info"
-#define TABLE_LOWER_ITIP_INFO "table_lower_itip_info"
-
-#define DIV_ITIP_CONTENT "div_itip_content"
-#define DIV_ITIP_ERROR "div_itip_error"
+       ECalClient *current_client;
+
+       gchar *vcalendar;
+       ECalComponent *comp;
+       icalcomponent *main_comp;
+       icalcomponent *ical_comp;
+       icalcomponent *top_level;
+       icalcompiter iter;
+       icalproperty_method method;
+       time_t start_time;
+       time_t end_time;
+
+       gint current;
+       gint total;
+
+       gchar *calendar_uid;
+
+       gchar *from_address;
+       gchar *from_name;
+       gchar *to_address;
+       gchar *to_name;
+       gchar *delegator_address;
+       gchar *delegator_name;
+       gchar *my_address;
+       gint   view_only;
+
+       guint progress_info_id;
+
+       /* a reply can only be sent if and only if there is an organizer */
+       gboolean has_organizer;
+       /*
+        * Usually replies are sent unless the user unchecks that option.
+        * There are some cases when the default is not to sent a reply
+        * (but the user can still chose to do so by checking the option):
+        * - the organizer explicitly set RSVP=FALSE for the current user
+        * - the event has no ATTENDEEs: that's the case for most non-meeting
+        *   events
+        *
+        * The last case is meant for forwarded non-meeting
+        * events. Traditionally Evolution hasn't offered to send a
+        * reply, therefore the updated implementation mimics that
+        * behavior.
+        *
+        * Unfortunately some software apparently strips all ATTENDEEs
+        * when forwarding a meeting; in that case sending a reply is
+        * also unchecked by default. So the check for ATTENDEEs is a
+        * tradeoff between sending unwanted replies in cases where
+        * that wasn't done in the past and not sending a possibly
+        * wanted reply where that wasn't possible in the past
+        * (because replies to forwarded events were not
+        * supported). Overall that should be an improvement, and the
+        * user can always override the default.
+        */
+       gboolean no_reply_wanted;
+
+       guint update_item_progress_info_id;
+       guint update_item_error_info_id;
+       ItipViewResponse update_item_response;
+       GHashTable *real_comps; /* ESource's UID -> ECalComponent stored on the server */
+};
 
 enum {
        PROP_0,
@@ -612,6 +646,199 @@ set_journal_sender_text (ItipView *view)
 }
 
 static void
+enable_button (ItipView *view,
+              const gchar *button_id,
+               gboolean enable)
+{
+       if (!view->priv->web_extension)
+               return;
+
+       g_dbus_proxy_call (
+               view->priv->web_extension,
+               "EnableButton",
+               g_variant_new ("(tssb)", view->priv->page_id, view->priv->part_id, button_id, enable),
+               G_DBUS_CALL_FLAGS_NONE,
+               -1,
+               NULL,
+               NULL,
+               NULL);
+}
+
+static void
+show_button (ItipView *view,
+             const gchar *id)
+{
+       if (!view->priv->web_extension)
+               return;
+
+       g_dbus_proxy_call (
+               view->priv->web_extension,
+               "ShowButton",
+               g_variant_new ("(tss)", view->priv->page_id, view->priv->part_id, id),
+               G_DBUS_CALL_FLAGS_NONE,
+               -1,
+               NULL,
+               NULL,
+               NULL);
+}
+
+static void
+hide_element (ItipView *view,
+             const gchar *element_id,
+              gboolean hide)
+{
+       if (!view->priv->web_extension)
+               return;
+
+       g_dbus_proxy_call (
+               view->priv->web_extension,
+               "HideElement",
+               g_variant_new ("(tssb)", view->priv->page_id, view->priv->part_id, element_id, hide),
+               G_DBUS_CALL_FLAGS_NONE,
+               -1,
+               NULL,
+               NULL,
+               NULL);
+}
+
+static gboolean
+element_is_hidden (ItipView *view,
+                   const gchar *element_id)
+{
+       GVariant *result;
+       gboolean hidden;
+
+       if (!view->priv->web_extension)
+               return FALSE;
+
+       result = g_dbus_proxy_call_sync (
+                       view->priv->web_extension,
+                       "ElementIsHidden",
+                       g_variant_new ("(tss)", view->priv->page_id, view->priv->part_id, element_id),
+                       G_DBUS_CALL_FLAGS_NONE,
+                       -1,
+                       NULL,
+                       NULL);
+
+       if (result) {
+               g_variant_get (result, "(b)", &hidden);
+               g_variant_unref (result);
+               return hidden;
+       }
+
+       return FALSE;
+}
+
+static void
+set_inner_html (ItipView *view,
+               const gchar *element_id,
+                const gchar *inner_html)
+{
+       if (!view->priv->web_extension)
+               return;
+
+       g_dbus_proxy_call (
+               view->priv->web_extension,
+               "ElementSetInnerHTML",
+               g_variant_new ("(tsss)", view->priv->page_id, view->priv->part_id, element_id, inner_html),
+               G_DBUS_CALL_FLAGS_NONE,
+               -1,
+               NULL,
+               NULL,
+               NULL);
+}
+
+static void
+input_set_checked (ItipView *view,
+                   const gchar *input_id,
+                   gboolean checked)
+{
+       if (!view->priv->web_extension)
+               return;
+
+       g_dbus_proxy_call (
+               view->priv->web_extension,
+               "InputSetChecked",
+               g_variant_new ("(tssb)", view->priv->page_id, view->priv->part_id, input_id, checked),
+               G_DBUS_CALL_FLAGS_NONE,
+               -1,
+               NULL,
+               NULL,
+               NULL);
+}
+
+static gboolean
+input_is_checked (ItipView *view,
+                  const gchar *input_id)
+{
+       GVariant *result;
+       gboolean checked;
+
+       if (!view->priv->web_extension)
+               return FALSE;
+
+       result = g_dbus_proxy_call_sync (
+                       view->priv->web_extension,
+                       "InputIsChecked",
+                       g_variant_new ("(tss)", view->priv->page_id, view->priv->part_id, input_id),
+                       G_DBUS_CALL_FLAGS_NONE,
+                       -1,
+                       NULL,
+                       NULL);
+
+       if (result) {
+               g_variant_get (result, "(b)", &checked);
+               g_variant_unref (result);
+               return checked;
+       }
+
+       return FALSE;
+}
+
+static void
+show_checkbox (ItipView *view,
+               const gchar *id,
+               gboolean show,
+              gboolean update_second)
+{
+       g_return_if_fail (ITIP_IS_VIEW (view));
+
+       if (!view->priv->web_extension)
+               return;
+
+       g_dbus_proxy_call (
+               view->priv->web_extension,
+               "ShowCheckbox",
+               g_variant_new ("(tssbb)", view->priv->page_id, view->priv->part_id, id, show, update_second),
+               G_DBUS_CALL_FLAGS_NONE,
+               -1,
+               NULL,
+               NULL,
+               NULL);
+}
+
+static void
+set_area_text (ItipView *view,
+               const gchar *id,
+               const gchar *text)
+{
+       g_return_if_fail (ITIP_IS_VIEW (view));
+
+       if (!view->priv->web_extension)
+               return;
+
+       g_dbus_proxy_call (
+               view->priv->web_extension,
+               "SetAreaText",
+               g_variant_new ("(tsss)", view->priv->page_id, view->priv->part_id, id, text ? text : ""),
+               G_DBUS_CALL_FLAGS_NONE,
+               -1,
+               NULL,
+               NULL,
+               NULL);
+}
+
+static void
 set_sender_text (ItipView *view)
 {
        ItipViewPrivate *priv;
@@ -635,22 +862,14 @@ set_sender_text (ItipView *view)
                break;
        }
 
-       if (priv->sender && priv->dom_document) {
-               WebKitDOMElement *div;
-
-               div = webkit_dom_document_get_element_by_id (
-                       priv->dom_document, TEXT_ROW_SENDER);
-               webkit_dom_html_element_set_inner_html (
-                       WEBKIT_DOM_HTML_ELEMENT (div), priv->sender, NULL);
-               g_object_unref (div);
-       }
+       if (priv->sender && priv->web_extension)
+               set_inner_html (view, TEXT_ROW_SENDER, priv->sender);
 }
 
 static void
 update_start_end_times (ItipView *view)
 {
        ItipViewPrivate *priv;
-       WebKitDOMElement *row, *col;
        gchar buffer[256];
        time_t now;
        struct tm *now_tm;
@@ -695,142 +914,155 @@ update_start_end_times (ItipView *view)
        }
        #undef is_same
 
-       if (priv->dom_document) {
-               row = webkit_dom_document_get_element_by_id (
-                       priv->dom_document, TABLE_ROW_START_DATE);
-               if (priv->start_header && priv->start_label) {
-                       webkit_dom_html_element_set_hidden (
-                               WEBKIT_DOM_HTML_ELEMENT (row), FALSE);
-
-                       col = webkit_dom_element_get_first_element_child (row);
-                       webkit_dom_html_element_set_inner_html (
-                               WEBKIT_DOM_HTML_ELEMENT (col), priv->start_header, NULL);
-                       g_object_unref (col);
-
-                       col = webkit_dom_element_get_last_element_child (row);
-                       webkit_dom_html_element_set_inner_html (
-                               WEBKIT_DOM_HTML_ELEMENT (col), priv->start_label, NULL);
-                       g_object_unref (col);
-               } else {
-                       webkit_dom_html_element_set_hidden (
-                               WEBKIT_DOM_HTML_ELEMENT (row), TRUE);
-               }
-               g_object_unref (row);
-
-               row = webkit_dom_document_get_element_by_id (
-                       priv->dom_document, TABLE_ROW_END_DATE);
-               if (priv->end_header && priv->end_label) {
-                       webkit_dom_html_element_set_hidden (
-                               WEBKIT_DOM_HTML_ELEMENT (row), FALSE);
-
-                       col = webkit_dom_element_get_first_element_child (row);
-                       webkit_dom_html_element_set_inner_html (
-                               WEBKIT_DOM_HTML_ELEMENT (col), priv->end_header, NULL);
-                       g_object_unref (col);
-
-                       col = webkit_dom_element_get_last_element_child (row);
-                       webkit_dom_html_element_set_inner_html (
-                               WEBKIT_DOM_HTML_ELEMENT (col), priv->end_label, NULL);
-                       g_object_unref (col);
-               } else {
-                       webkit_dom_html_element_set_hidden (
-                               WEBKIT_DOM_HTML_ELEMENT (row), TRUE);
-               }
-               g_object_unref (row);
-       }
+       if (!priv->web_extension)
+               return;
+
+       if (priv->start_header && priv->start_label) {
+               g_dbus_proxy_call (
+                       priv->web_extension,
+                       "UpdateTimes",
+                       g_variant_new (
+                               "(tssss)",
+                               view->priv->page_id,
+                               view->priv->part_id,
+                               TABLE_ROW_START_DATE,
+                               priv->start_header,
+                               priv->start_label),
+                       G_DBUS_CALL_FLAGS_NONE,
+                       -1,
+                       NULL,
+                       NULL,
+                       NULL);
+       } else
+               hide_element (view, TABLE_ROW_START_DATE, TRUE);
+
+       if (priv->end_header && priv->end_label) {
+               g_dbus_proxy_call (
+                       priv->web_extension,
+                       "UpdateTimes",
+                       g_variant_new (
+                               "(tssss)",
+                               view->priv->page_id,
+                               view->priv->part_id,
+                               TABLE_ROW_END_DATE,
+                               priv->end_header,
+                               priv->end_label),
+                       G_DBUS_CALL_FLAGS_NONE,
+                       -1,
+                       NULL,
+                       NULL,
+                       NULL);
+       } else
+               hide_element (view, TABLE_ROW_END_DATE, TRUE);
 }
 
 static void
-button_clicked_cb (WebKitDOMElement *element,
-                   WebKitDOMEvent *event,
-                   gpointer data)
+itip_view_itip_button_clicked_cb (EWebView *web_view,
+                                 const gchar *element_class,
+                                 const gchar *element_value,
+                                 const GtkAllocation *element_position,
+                                 gpointer user_data)
 {
-       ItipViewResponse response;
-       gchar *response_str;
+       ItipView *view = user_data;
+       gboolean can_use;
+       gchar *tmp;
+
+       g_return_if_fail (E_IS_WEB_VIEW (web_view));
+       g_return_if_fail (element_class && *element_class);
+       g_return_if_fail (element_value && *element_value);
+       g_return_if_fail (ITIP_IS_VIEW (view));
 
-       response_str = webkit_dom_html_button_element_get_value (
-               WEBKIT_DOM_HTML_BUTTON_ELEMENT (element));
+       tmp = g_strdup_printf ("%p:", view->priv->itip_part_ptr);
+       can_use = g_str_has_prefix (element_value, tmp);
+       if (can_use)
+               element_value += strlen (tmp);
+       g_free (tmp);
 
-       response = atoi (response_str);
-       g_free (response_str);
+       if (can_use) {
+               gint response = atoi (element_value);
 
-       g_signal_emit (data, signals[RESPONSE], 0, response);
+               g_signal_emit (view, signals[RESPONSE], 0, response);
+       }
 }
 
 static void
-rsvp_toggled_cb (WebKitDOMHTMLInputElement *input,
-                 WebKitDOMEvent *event,
-                 gpointer data)
+itip_view_register_clicked_listener (ItipView *view)
 {
-       WebKitDOMElement *el;
+       EWebView *web_view;
 
-       ItipView *view = data;
-       gboolean rsvp;
+       g_return_if_fail (ITIP_IS_VIEW (view));
 
-       rsvp = webkit_dom_html_input_element_get_checked (input);
+       web_view = itip_view_ref_web_view (view);
+       if (web_view) {
+               e_web_view_register_element_clicked (web_view, "itip-button",
+                       itip_view_itip_button_clicked_cb, view);
+       }
 
-       el = webkit_dom_document_get_element_by_id (
-               view->priv->dom_document, TEXTAREA_RSVP_COMMENT);
-       webkit_dom_html_text_area_element_set_disabled (
-               WEBKIT_DOM_HTML_TEXT_AREA_ELEMENT (el), !rsvp);
-       g_object_unref (el);
+       g_clear_object (&web_view);
 }
 
 static void
-recur_toggled_cb (WebKitDOMHTMLInputElement *input,
-                  WebKitDOMEvent *event,
-                  gpointer data)
+recur_toggled_signal_cb (GDBusConnection *connection,
+                         const gchar *sender_name,
+                         const gchar *object_path,
+                         const gchar *interface_name,
+                         const gchar *signal_name,
+                         GVariant *parameters,
+                         ItipView *view)
 {
-       ItipView *view = data;
+       guint64 page_id = 0;
+       const gchar *part_id = NULL;
+
+       g_return_if_fail (ITIP_IS_VIEW (view));
+
+       if (g_strcmp0 (signal_name, "RecurToggled") != 0)
+               return;
+
+       g_variant_get (parameters, "(t&s)", &page_id, &part_id);
 
-       itip_view_set_mode (view, view->priv->mode);
+       if (view->priv->page_id == page_id &&
+           g_strcmp0 (view->priv->part_id, part_id) == 0)
+               itip_view_set_mode (view, view->priv->mode);
 }
 
-/*
-  alarm_check_toggled_cb
-  check1 was changed, so make the second available based on state of the first check.
-*/
 static void
-alarm_check_toggled_cb (WebKitDOMHTMLInputElement *check1,
-                        WebKitDOMEvent *event,
-                        ItipView *view)
+source_changed_cb (ItipView *view)
 {
-       WebKitDOMElement *check2;
-       gchar *id;
-
-       id = webkit_dom_element_get_id (WEBKIT_DOM_ELEMENT (check1));
+       ESource *source;
 
-       if (g_strcmp0 (id, CHECKBOX_INHERIT_ALARM)) {
-               check2 = webkit_dom_document_get_element_by_id (
-                       view->priv->dom_document, CHECKBOX_KEEP_ALARM);
-       } else {
-               check2 = webkit_dom_document_get_element_by_id (
-                       view->priv->dom_document, CHECKBOX_INHERIT_ALARM);
-       }
+       source = itip_view_ref_source (view);
 
-       g_free (id);
+       if (source) {
+               d (printf ("Source changed to '%s'\n", e_source_get_display_name (source)));
+               g_signal_emit (view, signals[SOURCE_SELECTED], 0, source);
 
-       webkit_dom_html_input_element_set_disabled (
-               WEBKIT_DOM_HTML_INPUT_ELEMENT (check2),
-               (webkit_dom_html_element_get_hidden (
-                               WEBKIT_DOM_HTML_ELEMENT (check1)) &&
-                       webkit_dom_html_input_element_get_checked (check1)));
-       g_object_unref (check2);
+               g_object_unref (source);
+       }
 }
 
 static void
-source_changed_cb (WebKitDOMElement *select,
-                   WebKitDOMEvent *event,
-                   ItipView *view)
+source_changed_cb_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)
 {
-       ESource *source;
+       ItipView *view = user_data;
+       guint64 page_id = 0;
+       const gchar *part_id = NULL;
 
-       source = itip_view_ref_source (view);
+       g_return_if_fail (ITIP_IS_VIEW (view));
+
+       if (g_strcmp0 (signal_name, "SourceChanged") != 0)
+               return;
 
-       d (printf ("Source changed to '%s'\n", e_source_get_display_name (source)));
-       g_signal_emit (view, signals[SOURCE_SELECTED], 0, source);
+       g_variant_get (parameters, "(t&s)", &page_id, &part_id);
 
-       g_object_unref (source);
+       if (view->priv->page_id == page_id &&
+           g_strcmp0 (view->priv->part_id, part_id) == 0)
+               source_changed_cb (view);
 }
 
 static void
@@ -897,19 +1129,8 @@ append_info_item_row (ItipView *view,
                       const gchar *table_id,
                       ItipViewInfoItem *item)
 {
-       WebKitDOMElement *table;
-       WebKitDOMHTMLElement *row, *cell;
        const gchar *icon_name;
-       gchar *id;
-
-       table = webkit_dom_document_get_element_by_id (
-               view->priv->dom_document, table_id);
-       row = webkit_dom_html_table_element_insert_row (
-               WEBKIT_DOM_HTML_TABLE_ELEMENT (table), -1, NULL);
-
-       id = g_strdup_printf ("%s_row_%d", table_id, item->id);
-       webkit_dom_element_set_id (WEBKIT_DOM_ELEMENT (row), id);
-       g_free (id);
+       gchar *row_id;
 
        switch (item->type) {
                case ITIP_VIEW_INFO_ITEM_TYPE_INFO:
@@ -929,42 +1150,31 @@ append_info_item_row (ItipView *view,
                        icon_name = NULL;
        }
 
-       cell = webkit_dom_html_table_row_element_insert_cell (
-               WEBKIT_DOM_HTML_TABLE_ROW_ELEMENT (row), -1, NULL);
-
-       if (icon_name) {
-               gchar *icon_uri;
-               WebKitDOMElement *image;
-               WebKitDOMNode *tmp;
+       row_id = g_strdup_printf ("%s_row_%d", table_id, item->id);
 
-               image = webkit_dom_document_create_element (
-                       view->priv->dom_document, "IMG", NULL);
-
-               icon_uri = g_strdup_printf ("gtk-stock://%s", icon_name);
-               webkit_dom_html_image_element_set_src (
-                       WEBKIT_DOM_HTML_IMAGE_ELEMENT (image), icon_uri);
-               g_free (icon_uri);
-
-               tmp = webkit_dom_node_append_child (
-                       WEBKIT_DOM_NODE (cell),
-                       WEBKIT_DOM_NODE (image),
-                       NULL);
-
-               g_object_unref (tmp);
-               g_object_unref (image);
-       }
+       if (!view->priv->web_extension)
+               return;
 
-       g_object_unref (cell);
-       cell = webkit_dom_html_table_row_element_insert_cell (
-               WEBKIT_DOM_HTML_TABLE_ROW_ELEMENT (row), -1, NULL);
+       g_dbus_proxy_call (
+               view->priv->web_extension,
+               "AppendInfoItemRow",
+               g_variant_new (
+                       "(tsssss)",
+                       view->priv->page_id,
+                       view->priv->part_id,
+                       table_id,
+                       row_id,
+                       icon_name,
+                       item->message),
+               G_DBUS_CALL_FLAGS_NONE,
+               -1,
+               NULL,
+               NULL,
+               NULL);
 
-       webkit_dom_html_element_set_inner_html (cell, item->message, NULL);
+       g_free (row_id);
 
        d (printf ("Added row %s_row_%d ('%s')\n", table_id, item->id, item->message));
-
-       g_object_unref (table);
-       g_object_unref (row);
-       g_object_unref (cell);
 }
 
 static void
@@ -972,26 +1182,31 @@ remove_info_item_row (ItipView *view,
                       const gchar *table_id,
                       guint id)
 {
-       WebKitDOMElement *row;
-       WebKitDOMNode *parent, *deleted_node;
        gchar *row_id;
 
        row_id = g_strdup_printf ("%s_row_%d", table_id, id);
-       row = webkit_dom_document_get_element_by_id (
-               view->priv->dom_document, row_id);
-       g_free (row_id);
 
-       parent = webkit_dom_node_get_parent_node (WEBKIT_DOM_NODE (row)),
-       deleted_node = webkit_dom_node_remove_child (
-               parent, WEBKIT_DOM_NODE (row), NULL);
-       g_object_unref (parent);
-       g_object_unref (deleted_node);
+       if (!view->priv->web_extension)
+               return;
+
+       g_dbus_proxy_call (
+               view->priv->web_extension,
+               "RemoveElement",
+               g_variant_new ("(tss)", view->priv->page_id, view->priv->part_id, row_id),
+               G_DBUS_CALL_FLAGS_NONE,
+               -1,
+               NULL,
+               NULL,
+               NULL);
+
+       g_free (row_id);
 
        d (printf ("Removed row %s_row_%d\n", table_id, id));
 }
 
 static void
 buttons_table_write_button (GString *buffer,
+                           gpointer itip_part_ptr,
                             const gchar *name,
                             const gchar *label,
                             const gchar *icon,
@@ -1004,18 +1219,18 @@ buttons_table_write_button (GString *buffer,
        if (icon) {
                g_string_append_printf (
                        buffer,
-                       "<td><button type=\"button\" name=\"%s\" value=\"%d\" id=\"%s\" accesskey=\"%s\" 
hidden disabled>"
+                       "<td><button class=\"itip-button\" type=\"button\" name=\"%s\" value=\"%p:%d\" 
id=\"%s\" accesskey=\"%s\" hidden disabled>"
                        "<div><img src=\"gtk-stock://%s?size=%d\"> <span>%s</span></div>"
                        "</button></td>\n",
-                       name, response, name, access_key ? access_key : "" , icon,
+                       name, itip_part_ptr, response, name, access_key ? access_key : "" , icon,
                        GTK_ICON_SIZE_BUTTON, html_label);
        } else {
                g_string_append_printf (
                        buffer,
-                       "<td><button type=\"button\" name=\"%s\" value=\"%d\" id=\"%s\" accesskey=\"%s\" 
hidden disabled>"
+                       "<td><button class=\"itip-button\" type=\"button\" name=\"%s\" value=\"%p:%d\" 
id=\"%s\" accesskey=\"%s\" hidden disabled>"
                        "<div><span>%s</span></div>"
                        "</button></td>\n",
-                       name, response, name, access_key ? access_key : "" , html_label);
+                       name, itip_part_ptr, response, name, access_key ? access_key : "" , html_label);
        }
 
        g_free (html_label);
@@ -1025,7 +1240,8 @@ buttons_table_write_button (GString *buffer,
 }
 
 static void
-append_buttons_table (GString *buffer)
+append_buttons_table (GString *buffer,
+                     gpointer itip_part_ptr)
 {
        g_string_append (
                buffer,
@@ -1036,34 +1252,34 @@ append_buttons_table (GString *buffer)
 
         /* Everything gets the open button */
        buttons_table_write_button (
-               buffer, BUTTON_OPEN_CALENDAR, _("Ope_n Calendar"),
+               buffer, itip_part_ptr, BUTTON_OPEN_CALENDAR, _("Ope_n Calendar"),
                "go-jump", ITIP_VIEW_RESPONSE_OPEN);
        buttons_table_write_button (
-               buffer, BUTTON_DECLINE_ALL, _("_Decline all"),
+               buffer, itip_part_ptr, BUTTON_DECLINE_ALL, _("_Decline all"),
                NULL, ITIP_VIEW_RESPONSE_DECLINE);
        buttons_table_write_button (
-               buffer, BUTTON_DECLINE, _("_Decline"),
+               buffer, itip_part_ptr, BUTTON_DECLINE, _("_Decline"),
                NULL, ITIP_VIEW_RESPONSE_DECLINE);
        buttons_table_write_button (
-               buffer, BUTTON_TENTATIVE_ALL, _("_Tentative all"),
+               buffer, itip_part_ptr, BUTTON_TENTATIVE_ALL, _("_Tentative all"),
                NULL, ITIP_VIEW_RESPONSE_TENTATIVE);
        buttons_table_write_button (
-               buffer, BUTTON_TENTATIVE, _("_Tentative"),
+               buffer, itip_part_ptr, BUTTON_TENTATIVE, _("_Tentative"),
                NULL, ITIP_VIEW_RESPONSE_TENTATIVE);
        buttons_table_write_button (
-               buffer, BUTTON_ACCEPT_ALL, _("Acce_pt all"),
+               buffer, itip_part_ptr, BUTTON_ACCEPT_ALL, _("Acce_pt all"),
                NULL, ITIP_VIEW_RESPONSE_ACCEPT);
        buttons_table_write_button (
-               buffer, BUTTON_ACCEPT, _("Acce_pt"),
+               buffer, itip_part_ptr, BUTTON_ACCEPT, _("Acce_pt"),
                NULL, ITIP_VIEW_RESPONSE_ACCEPT);
        buttons_table_write_button (
-               buffer, BUTTON_SEND_INFORMATION, _("Send _Information"),
+               buffer, itip_part_ptr, BUTTON_SEND_INFORMATION, _("Send _Information"),
                NULL, ITIP_VIEW_RESPONSE_REFRESH);
        buttons_table_write_button (
-               buffer, BUTTON_UPDATE_ATTENDEE_STATUS, _("_Update Attendee Status"),
+               buffer, itip_part_ptr, BUTTON_UPDATE_ATTENDEE_STATUS, _("_Update Attendee Status"),
                NULL, ITIP_VIEW_RESPONSE_UPDATE);
        buttons_table_write_button (
-               buffer, BUTTON_UPDATE,  _("_Update"),
+               buffer, itip_part_ptr, BUTTON_UPDATE,  _("_Update"),
                NULL, ITIP_VIEW_RESPONSE_CANCEL);
 
        g_string_append (buffer, "</tr></table>");
@@ -1073,14 +1289,12 @@ static void
 itip_view_rebuild_source_list (ItipView *view)
 {
        ESourceRegistry *registry;
-       WebKitDOMElement *select;
        GList *list, *link;
        const gchar *extension_name;
-       GHashTable *groups;
 
        d (printf ("Assigning a new source list!\n"));
 
-       if (!view->priv->dom_document)
+       if (!view->priv->web_extension)
                return;
 
        registry = view->priv->registry;
@@ -1089,92 +1303,49 @@ itip_view_rebuild_source_list (ItipView *view)
        if (extension_name == NULL)
                return;
 
-       select = webkit_dom_document_get_element_by_id (
-               view->priv->dom_document, SELECT_ESOURCE);
-
-       while (webkit_dom_node_has_child_nodes (WEBKIT_DOM_NODE (select))) {
-               WebKitDOMNode *removed_child, *last_child;
-
-               last_child = webkit_dom_node_get_last_child (WEBKIT_DOM_NODE (select));
-               removed_child = webkit_dom_node_remove_child (
-                       WEBKIT_DOM_NODE (select), last_child, NULL);
-               g_object_unref (last_child);
-               g_object_unref (removed_child);
-       }
+       g_dbus_proxy_call (
+               view->priv->web_extension,
+               "ElementRemoveChildNodes",
+               g_variant_new ("(tss)", view->priv->page_id, view->priv->part_id, SELECT_ESOURCE),
+               G_DBUS_CALL_FLAGS_NONE,
+               -1,
+               NULL,
+               NULL,
+               NULL);
 
        list = e_source_registry_list_enabled (registry, extension_name);
-       groups = g_hash_table_new_full (
-               g_str_hash, g_str_equal,
-               (GDestroyNotify) g_free, g_object_unref);
 
        for (link = list; link != NULL; link = g_list_next (link)) {
                ESource *source = E_SOURCE (link->data);
                ESource *parent;
-               WebKitDOMElement *option;
-               WebKitDOMNode *appended_child;
-               WebKitDOMHTMLOptGroupElement *optgroup;
 
                parent = e_source_registry_ref_source (
                        registry, e_source_get_parent (source));
 
-               optgroup = g_hash_table_lookup (groups, e_source_get_uid (parent));
-               if (!optgroup) {
-                       optgroup = WEBKIT_DOM_HTML_OPT_GROUP_ELEMENT (
-                                       webkit_dom_document_create_element (
-                                               view->priv->dom_document,
-                                               "OPTGROUP", NULL));
-                       webkit_dom_html_opt_group_element_set_label (
-                               optgroup, e_source_get_display_name (parent));
-                       g_hash_table_insert (
-                               groups, g_strdup (e_source_get_uid (parent)), optgroup);
-               }
-               g_object_unref (parent);
-
-               option = webkit_dom_document_create_element (
-                       view->priv->dom_document, "OPTION", NULL);
-               webkit_dom_html_option_element_set_value (
-                       WEBKIT_DOM_HTML_OPTION_ELEMENT (option),
-                       e_source_get_uid (source));
-               webkit_dom_html_option_element_set_label (
-                       WEBKIT_DOM_HTML_OPTION_ELEMENT (option),
-                       e_source_get_display_name (source));
-               webkit_dom_html_element_set_inner_html (
-                       WEBKIT_DOM_HTML_ELEMENT (option),
-                       e_source_get_display_name (source), NULL);
-               webkit_dom_element_set_class_name (
-                       WEBKIT_DOM_ELEMENT (option), "calendar");
-
-               if (!e_source_get_writable (source)) {
-                       webkit_dom_html_option_element_set_disabled (
-                               WEBKIT_DOM_HTML_OPTION_ELEMENT (option), TRUE);
-               }
-
-               appended_child = webkit_dom_node_append_child (
-                       WEBKIT_DOM_NODE (optgroup),
-                       WEBKIT_DOM_NODE (option),
+               g_dbus_proxy_call (
+                       view->priv->web_extension,
+                       "RebuildSourceList",
+                       g_variant_new (
+                               "(tsssssb)",
+                               view->priv->page_id,
+                               view->priv->part_id,
+                               e_source_get_uid (parent),
+                               e_source_get_display_name (parent),
+                               e_source_get_uid (source),
+                               e_source_get_display_name (source),
+                               e_source_get_writable (source)),
+                       G_DBUS_CALL_FLAGS_NONE,
+                       -1,
+                       NULL,
+                       NULL,
                        NULL);
-               g_object_unref (option);
-               g_object_unref (appended_child);
-       }
-
-       g_list_free_full (list, (GDestroyNotify) g_object_unref);
 
-       list = g_hash_table_get_values (groups);
-       for (link = list; link != NULL; link = g_list_next (link)) {
-               WebKitDOMNode *appended_child;
-               WebKitDOMNode *optgroup = link->data;
-
-               appended_child = webkit_dom_node_append_child (
-                       WEBKIT_DOM_NODE (select), optgroup, NULL);
-               g_object_unref (appended_child);
+               g_object_unref (parent);
        }
-       g_list_free (list);
-
-       g_hash_table_destroy (groups);
 
-       source_changed_cb (select, NULL, view);
+       g_list_free_full (list, (GDestroyNotify) g_object_unref);
 
-       g_object_unref (select);
+       source_changed_cb (view);
 }
 
 static void
@@ -1292,8 +1463,30 @@ itip_view_dispose (GObject *object)
                priv->source_removed_handler_id = 0;
        }
 
+       if (priv->web_extension_watch_name_id > 0) {
+               g_bus_unwatch_name (priv->web_extension_watch_name_id);
+               priv->web_extension_watch_name_id = 0;
+       }
+
+       if (priv->web_extension_recur_toggled_signal_id > 0) {
+               g_dbus_connection_signal_unsubscribe (
+                       g_dbus_proxy_get_connection (priv->web_extension),
+                       priv->web_extension_recur_toggled_signal_id);
+               priv->web_extension_recur_toggled_signal_id = 0;
+       }
+
+       if (priv->web_extension_source_changed_cb_signal_id > 0) {
+               g_dbus_connection_signal_unsubscribe (
+                       g_dbus_proxy_get_connection (priv->web_extension),
+                       priv->web_extension_source_changed_cb_signal_id);
+               priv->web_extension_source_changed_cb_signal_id = 0;
+       }
+
        g_clear_object (&priv->client_cache);
        g_clear_object (&priv->registry);
+       g_clear_object (&priv->web_extension);
+       g_clear_object (&priv->cancellable);
+       g_clear_object (&priv->comp);
 
        /* Chain up to parent's dispose() method. */
        G_OBJECT_CLASS (itip_view_parent_class)->dispose (object);
@@ -1309,7 +1502,6 @@ itip_view_finalize (GObject *object)
 
        d (printf ("Itip view finalized!\n"));
 
-       g_clear_object (&priv->dom_document);
        g_free (priv->extension_name);
        g_free (priv->sender);
        g_free (priv->organizer);
@@ -1328,6 +1520,7 @@ itip_view_finalize (GObject *object)
        g_free (priv->end_label);
        g_free (priv->description);
        g_free (priv->error);
+       g_free (priv->part_id);
 
        for (iter = priv->lower_info_items; iter; iter = iter->next) {
                ItipViewInfoItem *item = iter->data;
@@ -1345,6 +1538,31 @@ itip_view_finalize (GObject *object)
 
        g_slist_free (priv->upper_info_items);
 
+       e_weak_ref_free (priv->web_view_weakref);
+
+       g_free (priv->vcalendar);
+       g_free (priv->calendar_uid);
+       g_free (priv->from_address);
+       g_free (priv->from_name);
+       g_free (priv->to_address);
+       g_free (priv->to_name);
+       g_free (priv->delegator_address);
+       g_free (priv->delegator_name);
+       g_free (priv->my_address);
+       g_free (priv->message_uid);
+
+       g_clear_object (&priv->folder);
+       g_clear_object (&priv->message);
+       g_clear_object (&priv->itip_mime_part);
+
+       if (priv->top_level != NULL)
+               icalcomponent_free (priv->top_level);
+
+       if (priv->main_comp != NULL)
+               icalcomponent_free (priv->main_comp);
+
+       g_hash_table_destroy (priv->real_comps);
+
        /* Chain up to parent's finalize() method. */
        G_OBJECT_CLASS (itip_view_parent_class)->finalize (object);
 }
@@ -1403,8 +1621,7 @@ itip_view_class_init (ItipViewClass *class)
                        "Client Cache",
                        "Cache of shared EClient instances",
                        E_TYPE_CLIENT_CACHE,
-                       G_PARAM_READWRITE |
-                       G_PARAM_CONSTRUCT_ONLY));
+                       G_PARAM_READABLE));
 
        g_object_class_install_property (
                object_class,
@@ -1437,14 +1654,6 @@ itip_view_class_init (ItipViewClass *class)
                G_TYPE_INT);
 }
 
-EMailPartItip *
-itip_view_get_mail_part (ItipView *view)
-{
-       g_return_val_if_fail (ITIP_IS_VIEW (view), NULL);
-
-       return view->priv->itip_part;
-}
-
 EClientCache *
 itip_view_get_client_cache (ItipView *view)
 {
@@ -1480,7 +1689,8 @@ itip_view_set_extension_name (ItipView *view,
 }
 
 void
-itip_view_write (EMailFormatter *formatter,
+itip_view_write (gpointer itip_part_ptr,
+                EMailFormatter *formatter,
                  GString *buffer)
 {
        gchar *header = e_mail_formatter_get_html_header (formatter);
@@ -1577,7 +1787,7 @@ itip_view_write (EMailFormatter *formatter,
        g_string_append (buffer, "</table>\n");
 
         /* Buttons table */
-       append_buttons_table (buffer);
+       append_buttons_table (buffer, itip_part_ptr);
 
         /* <div class="itip content" > */
        g_string_append (buffer, "</div>\n");
@@ -1650,185 +1860,173 @@ itip_view_write_for_printing (ItipView *view,
        }
 }
 
-void
-itip_view_create_dom_bindings (ItipView *view,
-                               WebKitDOMElement *element)
+static void
+web_extension_proxy_created_cb (GDBusProxy *proxy,
+                                GAsyncResult *result,
+                                ItipView *view)
 {
-       WebKitDOMElement *el;
-       WebKitDOMDocument *doc;
-
-       doc = webkit_dom_node_get_owner_document (WEBKIT_DOM_NODE (element));
-       view->priv->dom_document = g_object_ref (doc);
-
-       el = webkit_dom_document_get_element_by_id (doc, CHECKBOX_RECUR);
-       if (el) {
-               webkit_dom_event_target_add_event_listener (
-                       WEBKIT_DOM_EVENT_TARGET (el), "click",
-                       G_CALLBACK (recur_toggled_cb), FALSE, view);
-       }
-
-       el = webkit_dom_document_get_element_by_id (doc, CHECKBOX_RSVP);
-       if (el) {
-               webkit_dom_event_target_add_event_listener (
-                       WEBKIT_DOM_EVENT_TARGET (el), "click",
-                       G_CALLBACK (rsvp_toggled_cb), FALSE, view);
-       }
-
-       el = webkit_dom_document_get_element_by_id (doc, CHECKBOX_INHERIT_ALARM);
-       if (el) {
-               webkit_dom_event_target_add_event_listener (
-                       WEBKIT_DOM_EVENT_TARGET (el), "click",
-                       G_CALLBACK (alarm_check_toggled_cb), FALSE, view);
-       }
-
-       el = webkit_dom_document_get_element_by_id (doc, CHECKBOX_KEEP_ALARM);
-       if (el) {
-               webkit_dom_event_target_add_event_listener (
-                       WEBKIT_DOM_EVENT_TARGET (el), "click",
-                       G_CALLBACK (alarm_check_toggled_cb), FALSE, view);
-       }
-
-       el = webkit_dom_document_get_element_by_id (doc, BUTTON_OPEN_CALENDAR);
-       if (el) {
-               webkit_dom_event_target_add_event_listener (
-                       WEBKIT_DOM_EVENT_TARGET (el), "click",
-                       G_CALLBACK (button_clicked_cb), FALSE, view);
-       }
+       GError *error = NULL;
 
-       el = webkit_dom_document_get_element_by_id (doc, BUTTON_ACCEPT);
-       if (el) {
-               webkit_dom_event_target_add_event_listener (
-                       WEBKIT_DOM_EVENT_TARGET (el), "click",
-                       G_CALLBACK (button_clicked_cb), FALSE, view);
+       view->priv->web_extension = g_dbus_proxy_new_finish (result, &error);
+       if (!view->priv->web_extension) {
+               g_warning ("Error creating web extension proxy: %s\n", error->message);
+               g_error_free (error);
        }
 
-       el = webkit_dom_document_get_element_by_id (doc, BUTTON_ACCEPT_ALL);
-       if (el) {
-               webkit_dom_event_target_add_event_listener (
-                       WEBKIT_DOM_EVENT_TARGET (el), "click",
-                       G_CALLBACK (button_clicked_cb), FALSE, view);
-       }
+       view->priv->web_extension_source_changed_cb_signal_id =
+               g_dbus_connection_signal_subscribe (
+                       g_dbus_proxy_get_connection (view->priv->web_extension),
+                       g_dbus_proxy_get_name (view->priv->web_extension),
+                       MODULE_ITIP_FORMATTER_WEB_EXTENSION_INTERFACE,
+                       "SourceChanged",
+                       MODULE_ITIP_FORMATTER_WEB_EXTENSION_OBJECT_PATH,
+                       NULL,
+                       G_DBUS_SIGNAL_FLAGS_NONE,
+                       source_changed_cb_signal_cb,
+                       view,
+                       NULL);
 
-       el = webkit_dom_document_get_element_by_id (doc, BUTTON_TENTATIVE);
-       if (el) {
-               webkit_dom_event_target_add_event_listener (
-                       WEBKIT_DOM_EVENT_TARGET (el), "click",
-                       G_CALLBACK (button_clicked_cb), FALSE, view);
-       }
+       view->priv->web_extension_recur_toggled_signal_id =
+               g_dbus_connection_signal_subscribe (
+                       g_dbus_proxy_get_connection (view->priv->web_extension),
+                       g_dbus_proxy_get_name (view->priv->web_extension),
+                       MODULE_ITIP_FORMATTER_WEB_EXTENSION_INTERFACE,
+                       "RecurToggled",
+                       MODULE_ITIP_FORMATTER_WEB_EXTENSION_OBJECT_PATH,
+                       NULL,
+                       G_DBUS_SIGNAL_FLAGS_NONE,
+                       (GDBusSignalCallback) recur_toggled_signal_cb,
+                       view,
+                       NULL);
 
-       el = webkit_dom_document_get_element_by_id (doc, BUTTON_TENTATIVE_ALL);
-       if (el) {
-               webkit_dom_event_target_add_event_listener (
-                       WEBKIT_DOM_EVENT_TARGET (el), "click",
-                       G_CALLBACK (button_clicked_cb), FALSE, view);
-       }
+       g_dbus_proxy_call (
+               view->priv->web_extension,
+               "CreateDOMBindings",
+               g_variant_new ("(ts)", view->priv->page_id, view->priv->part_id),
+               G_DBUS_CALL_FLAGS_NONE,
+               -1,
+               NULL,
+               NULL,
+               NULL);
 
-       el = webkit_dom_document_get_element_by_id (doc, BUTTON_DECLINE);
-       if (el) {
-               webkit_dom_event_target_add_event_listener (
-                       WEBKIT_DOM_EVENT_TARGET (el), "click",
-                       G_CALLBACK (button_clicked_cb), FALSE, view);
-       }
+       itip_view_init_view (view);
+}
 
-       el = webkit_dom_document_get_element_by_id (doc, BUTTON_DECLINE_ALL);
-       if (el) {
-               webkit_dom_event_target_add_event_listener (
-                       WEBKIT_DOM_EVENT_TARGET (el), "click",
-                       G_CALLBACK (button_clicked_cb), FALSE, view);
-       }
+static void
+web_extension_appeared_cb (GDBusConnection *connection,
+                           const gchar *name,
+                           const gchar *name_owner,
+                           ItipView *view)
+{
+       g_dbus_proxy_new (
+               connection,
+               G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START |
+               G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES |
+               G_DBUS_PROXY_FLAGS_DO_NOT_CONNECT_SIGNALS,
+               NULL,
+               name,
+               MODULE_ITIP_FORMATTER_WEB_EXTENSION_OBJECT_PATH,
+               MODULE_ITIP_FORMATTER_WEB_EXTENSION_INTERFACE,
+               NULL,
+               (GAsyncReadyCallback)web_extension_proxy_created_cb,
+               view);
+}
 
-       el = webkit_dom_document_get_element_by_id (doc, BUTTON_UPDATE);
-       if (el) {
-               webkit_dom_event_target_add_event_listener (
-                       WEBKIT_DOM_EVENT_TARGET (el), "click",
-                       G_CALLBACK (button_clicked_cb), FALSE, view);
-       }
+static void
+web_extension_vanished_cb (GDBusConnection *connection,
+                           const gchar *name,
+                           ItipView *view)
+{
+       g_clear_object (&view->priv->web_extension);
+}
 
-       el = webkit_dom_document_get_element_by_id (doc, BUTTON_UPDATE_ATTENDEE_STATUS);
-       if (el) {
-               webkit_dom_event_target_add_event_listener (
-                       WEBKIT_DOM_EVENT_TARGET (el), "click",
-                       G_CALLBACK (button_clicked_cb), FALSE, view);
-       }
+static void
+itip_view_watch_web_extension (ItipView *view)
+{
+       view->priv->web_extension_watch_name_id =
+               g_bus_watch_name (
+                       G_BUS_TYPE_SESSION,
+                       MODULE_ITIP_FORMATTER_WEB_EXTENSION_SERVICE_NAME,
+                       G_BUS_NAME_WATCHER_FLAGS_NONE,
+                       (GBusNameAppearedCallback) web_extension_appeared_cb,
+                       (GBusNameVanishedCallback) web_extension_vanished_cb,
+                       view, NULL);
+}
 
-       el = webkit_dom_document_get_element_by_id (doc, BUTTON_SEND_INFORMATION);
-       if (el) {
-               webkit_dom_event_target_add_event_listener (
-                       WEBKIT_DOM_EVENT_TARGET (el), "click",
-                       G_CALLBACK (button_clicked_cb), FALSE, view);
-       }
+GDBusProxy *
+itip_view_get_web_extension_proxy (ItipView *view)
+{
+       g_return_val_if_fail (ITIP_IS_VIEW (view), NULL);
 
-       el = webkit_dom_document_get_element_by_id (doc, SELECT_ESOURCE);
-       if (el) {
-               webkit_dom_event_target_add_event_listener (
-                       WEBKIT_DOM_EVENT_TARGET (el), "change",
-                       G_CALLBACK (source_changed_cb), FALSE, view);
-       }
+       return view->priv->web_extension;
 }
 
 static void
 itip_view_init (ItipView *view)
 {
+       EShell *shell;
+       EClientCache *client_cache;
+
+       shell = e_shell_get_default ();
+       client_cache = e_shell_get_client_cache (shell);
+
        view->priv = ITIP_VIEW_GET_PRIVATE (view);
+       view->priv->web_view_weakref = e_weak_ref_new (NULL);
+       view->priv->real_comps = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_object_unref);
+       view->priv->client_cache = g_object_ref (client_cache);
 }
 
 ItipView *
-itip_view_new (EMailPartItip *puri,
-               EClientCache *client_cache)
+itip_view_new (guint64 page_id,
+               const gchar *part_id,
+              gpointer itip_part_ptr,
+              CamelFolder *folder,
+              const gchar *message_uid,
+              CamelMimeMessage *message,
+              CamelMimePart *itip_mime_part,
+              const gchar *vcalendar,
+              GCancellable *cancellable)
 {
        ItipView *view;
 
-       g_return_val_if_fail (E_IS_CLIENT_CACHE (client_cache), NULL);
+       view = ITIP_VIEW (g_object_new (ITIP_TYPE_VIEW, NULL));
+       view->priv->page_id = page_id;
+       view->priv->part_id = g_strdup (part_id);
+       view->priv->itip_part_ptr = itip_part_ptr;
+       view->priv->folder = g_object_ref (folder);
+       view->priv->message_uid = g_strdup (message_uid);
+       view->priv->message = g_object_ref (message);
+       view->priv->itip_mime_part = g_object_ref (itip_mime_part);
+       view->priv->vcalendar = g_strdup (vcalendar);
+       view->priv->cancellable = g_object_ref (cancellable);
 
-       view = ITIP_VIEW (g_object_new (
-               ITIP_TYPE_VIEW,
-               "client-cache", client_cache,
-               NULL));
-       view->priv->itip_part = puri;
+       itip_view_watch_web_extension (view);
 
        return view;
 }
 
-static void
-show_button (ItipView *view,
-             const gchar *id)
-{
-       WebKitDOMElement *button;
-
-       button = webkit_dom_document_get_element_by_id (
-               view->priv->dom_document, id);
-       webkit_dom_html_element_set_hidden (
-               WEBKIT_DOM_HTML_ELEMENT (button), FALSE);
-       g_object_unref (button);
-}
-
 void
 itip_view_set_mode (ItipView *view,
                     ItipViewMode mode)
 {
-       WebKitDOMElement *row, *cell;
-       WebKitDOMElement *button;
-
        g_return_if_fail (ITIP_IS_VIEW (view));
 
        view->priv->mode = mode;
 
        set_sender_text (view);
 
-       if (!view->priv->dom_document)
+       if (!view->priv->web_extension)
                return;
 
-       row = webkit_dom_document_get_element_by_id (
-               view->priv->dom_document, TABLE_ROW_BUTTONS);
-       cell = webkit_dom_element_get_first_element_child (row);
-       do {
-               button = webkit_dom_element_get_first_element_child (cell);
-               webkit_dom_html_element_set_hidden (
-                       WEBKIT_DOM_HTML_ELEMENT (button), TRUE);
-               g_object_unref (button);
-       } while ((cell = webkit_dom_element_get_next_element_sibling (cell)) != NULL);
-
-       g_object_unref (row);
+       g_dbus_proxy_call (
+               view->priv->web_extension,
+               "ElementHideChildNodes",
+               g_variant_new ("(tss)", view->priv->page_id, view->priv->part_id, TABLE_ROW_BUTTONS),
+               G_DBUS_CALL_FLAGS_NONE,
+               -1,
+               NULL,
+               NULL,
+               NULL);
 
        view->priv->is_recur_set = itip_view_get_recur_check_state (view);
 
@@ -1886,7 +2084,6 @@ void
 itip_view_set_item_type (ItipView *view,
                          ECalClientSourceType type)
 {
-       WebKitDOMElement *label;
        const gchar *header;
        gchar *access_key, *html_label;
 
@@ -1894,12 +2091,9 @@ itip_view_set_item_type (ItipView *view,
 
        view->priv->type = type;
 
-       if (!view->priv->dom_document)
+       if (!view->priv->web_extension)
                return;
 
-       label = webkit_dom_document_get_element_by_id (
-               view->priv->dom_document, TABLE_ROW_ESCB_LABEL);
-
        switch (view->priv->type) {
                case E_CAL_CLIENT_SOURCE_TYPE_EVENTS:
                        header = _("_Calendar:");
@@ -1922,12 +2116,18 @@ itip_view_set_item_type (ItipView *view,
 
        html_label = e_mail_formatter_parse_html_mnemonics (header, &access_key);
 
-       webkit_dom_html_element_set_access_key (
-               WEBKIT_DOM_HTML_ELEMENT (label), access_key);
-       webkit_dom_html_element_set_inner_html (
-               WEBKIT_DOM_HTML_ELEMENT (label), html_label, NULL);
+       g_dbus_proxy_call (
+               view->priv->web_extension,
+               "ElementSetAccessKey",
+               g_variant_new ("(tsss)", view->priv->page_id, view->priv->part_id, TABLE_ROW_ESCB_LABEL, 
access_key),
+               G_DBUS_CALL_FLAGS_NONE,
+               -1,
+               NULL,
+               NULL,
+               NULL);
+
+       set_inner_html (view, TABLE_ROW_ESCB_LABEL, html_label);
 
-       g_object_unref (label);
        g_free (html_label);
 
        if (access_key)
@@ -2080,8 +2280,6 @@ void
 itip_view_set_summary (ItipView *view,
                        const gchar *summary)
 {
-       WebKitDOMElement *row, *col;
-
        g_return_if_fail (ITIP_IS_VIEW (view));
 
        if (view->priv->summary)
@@ -2089,21 +2287,7 @@ itip_view_set_summary (ItipView *view,
 
        view->priv->summary = summary ? g_strstrip (e_utf8_ensure_valid (summary)) : NULL;
 
-       if (!view->priv->dom_document)
-               return;
-
-       row = webkit_dom_document_get_element_by_id (
-               view->priv->dom_document, TABLE_ROW_SUMMARY);
-       webkit_dom_html_element_set_hidden (
-               WEBKIT_DOM_HTML_ELEMENT (row), (view->priv->summary == NULL));
-
-       col = webkit_dom_element_get_last_element_child (row);
-       webkit_dom_html_element_set_inner_html (
-               WEBKIT_DOM_HTML_ELEMENT (col),
-               view->priv->summary ? view->priv->summary : "",
-               NULL);
-       g_object_unref (row);
-       g_object_unref (col);
+       set_area_text (view, TABLE_ROW_SUMMARY, view->priv->summary);
 }
 
 const gchar *
@@ -2118,8 +2302,6 @@ void
 itip_view_set_location (ItipView *view,
                         const gchar *location)
 {
-       WebKitDOMElement *row, *col;
-
        g_return_if_fail (ITIP_IS_VIEW (view));
 
        if (view->priv->location)
@@ -2127,21 +2309,7 @@ itip_view_set_location (ItipView *view,
 
        view->priv->location = location ? g_strstrip (e_utf8_ensure_valid (location)) : NULL;
 
-       if (!view->priv->dom_document)
-               return;
-
-       row = webkit_dom_document_get_element_by_id (
-               view->priv->dom_document, TABLE_ROW_LOCATION);
-       webkit_dom_html_element_set_hidden (
-               WEBKIT_DOM_HTML_ELEMENT (row), (view->priv->location == NULL));
-
-       col = webkit_dom_element_get_last_element_child (row);
-       webkit_dom_html_element_set_inner_html (
-               WEBKIT_DOM_HTML_ELEMENT (col),
-               view->priv->location ? view->priv->location : "",
-               NULL);
-       g_object_unref (row);
-       g_object_unref (col);
+       set_area_text (view, TABLE_ROW_LOCATION, view->priv->location);
 }
 
 const gchar *
@@ -2156,8 +2324,6 @@ void
 itip_view_set_status (ItipView *view,
                       const gchar *status)
 {
-       WebKitDOMElement *row, *col;
-
        g_return_if_fail (ITIP_IS_VIEW (view));
 
        if (view->priv->status)
@@ -2165,21 +2331,7 @@ itip_view_set_status (ItipView *view,
 
        view->priv->status = status ? g_strstrip (e_utf8_ensure_valid (status)) : NULL;
 
-       if (!view->priv->dom_document)
-               return;
-
-       row = webkit_dom_document_get_element_by_id (
-               view->priv->dom_document, TABLE_ROW_STATUS);
-       webkit_dom_html_element_set_hidden (
-               WEBKIT_DOM_HTML_ELEMENT (row), (view->priv->status == NULL));
-
-       col = webkit_dom_element_get_last_element_child (row);
-       webkit_dom_html_element_set_inner_html (
-               WEBKIT_DOM_HTML_ELEMENT (col),
-               view->priv->status ? view->priv->status : "",
-               NULL);
-       g_object_unref (row);
-       g_object_unref (col);
+       set_area_text (view, TABLE_ROW_STATUS, view->priv->status);
 }
 
 const gchar *
@@ -2194,8 +2346,6 @@ void
 itip_view_set_comment (ItipView *view,
                        const gchar *comment)
 {
-       WebKitDOMElement *row, *col;
-
        g_return_if_fail (ITIP_IS_VIEW (view));
 
        if (view->priv->comment)
@@ -2203,21 +2353,7 @@ itip_view_set_comment (ItipView *view,
 
        view->priv->comment = comment ? g_strstrip (e_utf8_ensure_valid (comment)) : NULL;
 
-       if (!view->priv->dom_document)
-               return;
-
-       row = webkit_dom_document_get_element_by_id (
-               view->priv->dom_document, TABLE_ROW_COMMENT);
-       webkit_dom_html_element_set_hidden (
-               WEBKIT_DOM_HTML_ELEMENT (row), (view->priv->comment == NULL));
-
-       col = webkit_dom_element_get_last_element_child (row);
-       webkit_dom_html_element_set_inner_html (
-               WEBKIT_DOM_HTML_ELEMENT (col),
-               view->priv->comment ? view->priv->comment : "",
-               NULL);
-       g_object_unref (row);
-       g_object_unref (col);
+       set_area_text (view, TABLE_ROW_COMMENT, view->priv->comment);
 }
 
 const gchar *
@@ -2232,8 +2368,6 @@ void
 itip_view_set_description (ItipView *view,
                            const gchar *description)
 {
-       WebKitDOMElement *div;
-
        g_return_if_fail (ITIP_IS_VIEW (view));
 
        if (view->priv->description)
@@ -2241,19 +2375,11 @@ itip_view_set_description (ItipView *view,
 
        view->priv->description = description ? g_strstrip (e_utf8_ensure_valid (description)) : NULL;
 
-       if (!view->priv->dom_document)
-               return;
-
-       div = webkit_dom_document_get_element_by_id (
-               view->priv->dom_document, TABLE_ROW_DESCRIPTION);
-       webkit_dom_html_element_set_hidden (
-               WEBKIT_DOM_HTML_ELEMENT (div), (view->priv->description == NULL));
-
-       webkit_dom_html_element_set_inner_html (
-               WEBKIT_DOM_HTML_ELEMENT (div),
-               view->priv->description ? view->priv->description : "",
-               NULL);
-       g_object_unref (div);
+       hide_element (view, TABLE_ROW_DESCRIPTION, (view->priv->description == NULL));
+       set_inner_html (
+               view,
+               TABLE_ROW_DESCRIPTION,
+               view->priv->description ? view->priv->description : "");
 }
 
 const gchar *
@@ -2360,7 +2486,7 @@ itip_view_add_upper_info_item (ItipView *view,
 
        priv->upper_info_items = g_slist_append (priv->upper_info_items, item);
 
-       if (!view->priv->dom_document)
+       if (!view->priv->web_extension)
                return item->id;
 
        append_info_item_row (view, TABLE_UPPER_ITIP_INFO, item);
@@ -2410,8 +2536,7 @@ itip_view_remove_upper_info_item (ItipView *view,
                        g_free (item->message);
                        g_free (item);
 
-                       if (!view->priv->dom_document)
-                               remove_info_item_row (view, TABLE_UPPER_ITIP_INFO, id);
+                       remove_info_item_row (view, TABLE_UPPER_ITIP_INFO, id);
 
                        return;
                }
@@ -2431,8 +2556,7 @@ itip_view_clear_upper_info_items (ItipView *view)
        for (l = priv->upper_info_items; l; l = l->next) {
                ItipViewInfoItem *item = l->data;
 
-               if (view->priv->dom_document)
-                       remove_info_item_row (view, TABLE_UPPER_ITIP_INFO, item->id);
+               remove_info_item_row (view, TABLE_UPPER_ITIP_INFO, item->id);
 
                g_free (item->message);
                g_free (item);
@@ -2462,7 +2586,7 @@ itip_view_add_lower_info_item (ItipView *view,
 
        priv->lower_info_items = g_slist_append (priv->lower_info_items, item);
 
-       if (!view->priv->dom_document)
+       if (!view->priv->web_extension)
                return item->id;
 
        append_info_item_row (view, TABLE_LOWER_ITIP_INFO, item);
@@ -2512,8 +2636,7 @@ itip_view_remove_lower_info_item (ItipView *view,
                        g_free (item->message);
                        g_free (item);
 
-                       if (view->priv->dom_document)
-                               remove_info_item_row (view, TABLE_LOWER_ITIP_INFO, id);
+                       remove_info_item_row (view, TABLE_LOWER_ITIP_INFO, id);
 
                        return;
                }
@@ -2533,8 +2656,7 @@ itip_view_clear_lower_info_items (ItipView *view)
        for (l = priv->lower_info_items; l; l = l->next) {
                ItipViewInfoItem *item = l->data;
 
-               if (view->priv->dom_document)
-                       remove_info_item_row (view, TABLE_LOWER_ITIP_INFO, item->id);
+               remove_info_item_row (view, TABLE_LOWER_ITIP_INFO, item->id);
 
                g_free (item->message);
                g_free (item);
@@ -2548,112 +2670,124 @@ void
 itip_view_set_source (ItipView *view,
                       ESource *source)
 {
-       WebKitDOMElement *select;
-       WebKitDOMElement *row;
        ESource *selected_source;
-       gulong i, len;
 
        g_return_if_fail (ITIP_IS_VIEW (view));
 
        d (printf ("Settings default source '%s'\n", e_source_get_display_name (source)));
 
-       if (!view->priv->dom_document)
-               return;
+       hide_element (view, TABLE_ROW_ESCB, (source == NULL));
 
-       row = webkit_dom_document_get_element_by_id (
-               view->priv->dom_document, TABLE_ROW_ESCB);
-       webkit_dom_html_element_set_hidden (
-               WEBKIT_DOM_HTML_ELEMENT (row), (source == NULL));
-       g_object_unref (row);
-       if (source == NULL)
+       if (!source)
                return;
 
-       select = webkit_dom_document_get_element_by_id (
-               view->priv->dom_document, SELECT_ESOURCE);
-
         /* <select> does not emit 'change' event when already selected
         * <option> is re-selected, but we need to notify itip formatter,
         * so that it would make all the buttons sensitive */
        selected_source = itip_view_ref_source (view);
        if (source == selected_source) {
-               source_changed_cb (select, NULL, view);
+               source_changed_cb (view);
                return;
        }
 
        if (selected_source != NULL)
                g_object_unref (selected_source);
 
-       if (webkit_dom_html_select_element_get_disabled (
-                       WEBKIT_DOM_HTML_SELECT_ELEMENT (select))) {
-               webkit_dom_html_select_element_set_disabled (
-                       WEBKIT_DOM_HTML_SELECT_ELEMENT (select), FALSE);
-       }
-
-       len = webkit_dom_html_select_element_get_length (
-               WEBKIT_DOM_HTML_SELECT_ELEMENT (select));
-       for (i = 0; i < len; i++) {
-
-               WebKitDOMNode *node;
-               WebKitDOMHTMLOptionElement *option;
-               gchar *value;
-
-               node = webkit_dom_html_select_element_item (
-                       WEBKIT_DOM_HTML_SELECT_ELEMENT (select), i);
-               option = WEBKIT_DOM_HTML_OPTION_ELEMENT (node);
-
-               value = webkit_dom_html_option_element_get_value (option);
-               if (g_strcmp0 (value, e_source_get_uid (source)) == 0) {
-                       webkit_dom_html_option_element_set_selected (
-                               option, TRUE);
-
-                       g_free (value);
-                       break;
-               }
+       if (!view->priv->web_extension)
+               return;
 
-               g_object_unref (node);
-               g_free (value);
-       }
+       g_dbus_proxy_call (
+               view->priv->web_extension,
+               "EnableSelect",
+               g_variant_new ("(tssb)", view->priv->page_id, view->priv->part_id, SELECT_ESOURCE, TRUE),
+               G_DBUS_CALL_FLAGS_NONE,
+               -1,
+               NULL,
+               NULL,
+               NULL);
 
-       source_changed_cb (select, NULL, view);
+       g_dbus_proxy_call (
+               view->priv->web_extension,
+               "SelectSetSelected",
+               g_variant_new ("(tsss)", view->priv->page_id, view->priv->part_id, SELECT_ESOURCE, 
e_source_get_uid (source)),
+               G_DBUS_CALL_FLAGS_NONE,
+               -1,
+               NULL,
+               NULL,
+               NULL);
 
-       g_object_unref (select);
+       source_changed_cb (view);
 }
 
 ESource *
 itip_view_ref_source (ItipView *view)
 {
-       WebKitDOMElement *select;
-       gchar *uid;
-       ESource *source;
-       gboolean disable = FALSE;
+       ESource *source = NULL;
+       gboolean disable = FALSE, enabled = FALSE;
+       GVariant *result;
 
        g_return_val_if_fail (ITIP_IS_VIEW (view), NULL);
 
-       if (!view->priv->dom_document)
+       if (!view->priv->web_extension)
                return NULL;
 
-       select = webkit_dom_document_get_element_by_id (
-               view->priv->dom_document, SELECT_ESOURCE);
-       if (webkit_dom_html_select_element_get_disabled (
-                       WEBKIT_DOM_HTML_SELECT_ELEMENT (select))) {
-               webkit_dom_html_select_element_set_disabled (
-                       WEBKIT_DOM_HTML_SELECT_ELEMENT (select), FALSE);
+       result = g_dbus_proxy_call_sync (
+                       view->priv->web_extension,
+                       "SelectIsEnabled",
+                       g_variant_new ("(tss)", view->priv->page_id, view->priv->part_id, SELECT_ESOURCE),
+                       G_DBUS_CALL_FLAGS_NONE,
+                       -1,
+                       NULL,
+                       NULL);
+
+       if (result) {
+               g_variant_get (result, "(b)", &enabled);
+               g_variant_unref (result);
+       }
+
+       if (enabled) {
+               g_dbus_proxy_call (
+                       view->priv->web_extension,
+                       "EnableSelect",
+                       g_variant_new ("(tssb)", view->priv->page_id, view->priv->part_id, SELECT_ESOURCE, 
TRUE),
+                       G_DBUS_CALL_FLAGS_NONE,
+                       -1,
+                       NULL,
+                       NULL,
+                       NULL);
+
                disable = TRUE;
        }
 
-       uid = webkit_dom_html_select_element_get_value (
-               WEBKIT_DOM_HTML_SELECT_ELEMENT (select));
+       result = g_dbus_proxy_call_sync (
+               view->priv->web_extension,
+               "SelectGetValue",
+               g_variant_new ("(tss)", view->priv->page_id, view->priv->part_id, SELECT_ESOURCE),
+               G_DBUS_CALL_FLAGS_NONE,
+               -1,
+               NULL,
+               NULL);
 
-       source = e_source_registry_ref_source (view->priv->registry, uid);
+       if (result) {
+               const gchar *uid;
 
-       g_free (uid);
+               g_variant_get (result, "(&s)", &uid);
+               source = e_source_registry_ref_source (view->priv->registry, uid);
+               g_variant_unref (result);
+       }
 
        if (disable) {
-               webkit_dom_html_select_element_set_disabled (
-                       WEBKIT_DOM_HTML_SELECT_ELEMENT (select), TRUE);
+               g_dbus_proxy_call (
+                       view->priv->web_extension,
+                       "EnableSelect",
+                       g_variant_new ("(tssb)", view->priv->page_id, view->priv->part_id, SELECT_ESOURCE, 
FALSE),
+                       G_DBUS_CALL_FLAGS_NONE,
+                       -1,
+                       NULL,
+                       NULL,
+                       NULL);
        }
 
-       g_object_unref (select);
        return source;
 }
 
@@ -2661,227 +2795,137 @@ void
 itip_view_set_rsvp (ItipView *view,
                     gboolean rsvp)
 {
-       WebKitDOMElement *el;
-
        g_return_if_fail (ITIP_IS_VIEW (view));
 
-       if (!view->priv->dom_document)
+       if (!view->priv->web_extension)
                return;
 
-       el = webkit_dom_document_get_element_by_id (
-               view->priv->dom_document, CHECKBOX_RSVP);
-       webkit_dom_html_input_element_set_checked (
-               WEBKIT_DOM_HTML_INPUT_ELEMENT (el), rsvp);
-       g_object_unref (el);
+       input_set_checked (view, CHECKBOX_RSVP, rsvp);
 
-       el = webkit_dom_document_get_element_by_id (
-               view->priv->dom_document, TEXTAREA_RSVP_COMMENT);
-       webkit_dom_html_text_area_element_set_disabled (
-               WEBKIT_DOM_HTML_TEXT_AREA_ELEMENT (el), !rsvp);
-       g_object_unref (el);
+       g_dbus_proxy_call (
+               view->priv->web_extension,
+               "EnableTextArea",
+               g_variant_new ("(tssb)", view->priv->page_id, view->priv->part_id, TEXTAREA_RSVP_COMMENT, 
!rsvp),
+               G_DBUS_CALL_FLAGS_NONE,
+               -1,
+               NULL,
+               NULL,
+               NULL);
 }
 
 gboolean
 itip_view_get_rsvp (ItipView *view)
 {
-       gboolean value;
-       WebKitDOMElement *el;
-
        g_return_val_if_fail (ITIP_IS_VIEW (view), FALSE);
 
-       if (!view->priv->dom_document)
-               return FALSE;
-
-       el = webkit_dom_document_get_element_by_id (
-               view->priv->dom_document, CHECKBOX_RSVP);
-       value = webkit_dom_html_input_element_get_checked (WEBKIT_DOM_HTML_INPUT_ELEMENT (el));
-       g_object_unref (el);
-       return value;
+       return input_is_checked (view, CHECKBOX_RSVP);
 }
 
 void
 itip_view_set_show_rsvp_check (ItipView *view,
                                gboolean show)
 {
-       WebKitDOMElement *label;
-       WebKitDOMElement *el;
-
        g_return_if_fail (ITIP_IS_VIEW (view));
 
-       if (!view->priv->dom_document)
-               return;
-
-       el = webkit_dom_document_get_element_by_id (
-               view->priv->dom_document, "table_row_" CHECKBOX_RSVP);
-       webkit_dom_html_element_set_hidden (WEBKIT_DOM_HTML_ELEMENT (el), !show);
-       g_object_unref (el);
-
-       el = webkit_dom_document_get_element_by_id (
-               view->priv->dom_document, CHECKBOX_RSVP);
-       label = webkit_dom_element_get_next_element_sibling (el);
-       webkit_dom_html_element_set_hidden (WEBKIT_DOM_HTML_ELEMENT (label), !show);
-       g_object_unref (label);
-
-       if (!show) {
-               webkit_dom_html_input_element_set_checked (
-                       WEBKIT_DOM_HTML_INPUT_ELEMENT (el), FALSE);
-       }
-       g_object_unref (el);
-
-       el = webkit_dom_document_get_element_by_id (
-               view->priv->dom_document, TABLE_ROW_RSVP_COMMENT);
-       webkit_dom_html_element_set_hidden (WEBKIT_DOM_HTML_ELEMENT (el), !show);
-       g_object_unref (el);
+       show_checkbox (view, CHECKBOX_RSVP, show, FALSE);
+       hide_element (view, TABLE_ROW_RSVP_COMMENT, !show);
 }
 
 gboolean
 itip_view_get_show_rsvp_check (ItipView *view)
 {
-       gboolean value;
-       WebKitDOMElement *el;
-
        g_return_val_if_fail (ITIP_IS_VIEW (view), FALSE);
 
-       if (!view->priv->dom_document)
-               return FALSE;
-
-       el = webkit_dom_document_get_element_by_id (
-               view->priv->dom_document, CHECKBOX_RSVP);
-       value = webkit_dom_html_element_get_hidden (WEBKIT_DOM_HTML_ELEMENT (el));
-       g_object_unref (el);
-       return !value;
+       return !element_is_hidden (view, CHECKBOX_RSVP);
 }
 
 void
 itip_view_set_update (ItipView *view,
                       gboolean update)
 {
-       WebKitDOMElement *el;
-
        g_return_if_fail (ITIP_IS_VIEW (view));
 
-       if (!view->priv->dom_document)
-               return;
-
-       el = webkit_dom_document_get_element_by_id (
-               view->priv->dom_document, CHECKBOX_UPDATE);
-
-       webkit_dom_html_input_element_set_checked (
-               WEBKIT_DOM_HTML_INPUT_ELEMENT (el), update);
-       g_object_unref (el);
+       input_set_checked (view, CHECKBOX_UPDATE, update);
 }
 
 gboolean
 itip_view_get_update (ItipView *view)
 {
-       gboolean value;
-       WebKitDOMElement *el;
-
        g_return_val_if_fail (ITIP_IS_VIEW (view), FALSE);
 
-       if (!view->priv->dom_document)
-               return FALSE;
-
-       el = webkit_dom_document_get_element_by_id (
-               view->priv->dom_document, CHECKBOX_UPDATE);
-       value = webkit_dom_html_input_element_get_checked (WEBKIT_DOM_HTML_INPUT_ELEMENT (el));
-       g_object_unref (el);
-       return value;
+       return input_is_checked (view, CHECKBOX_UPDATE);
 }
 
 void
 itip_view_set_show_update_check (ItipView *view,
                                  gboolean show)
 {
-       WebKitDOMElement *label;
-       WebKitDOMElement *el;
-
        g_return_if_fail (ITIP_IS_VIEW (view));
 
-       if (!view->priv->dom_document)
-               return;
-
-       el = webkit_dom_document_get_element_by_id (
-               view->priv->dom_document, "table_row_" CHECKBOX_UPDATE);
-       webkit_dom_html_element_set_hidden (WEBKIT_DOM_HTML_ELEMENT (el), !show);
-       g_object_unref (el);
-
-       el = webkit_dom_document_get_element_by_id (
-               view->priv->dom_document, CHECKBOX_UPDATE);
-       label = webkit_dom_element_get_next_element_sibling (el);
-       webkit_dom_html_element_set_hidden (WEBKIT_DOM_HTML_ELEMENT (label), !show);
-       g_object_unref (label);
-
-       if (!show) {
-               webkit_dom_html_input_element_set_checked (
-                       WEBKIT_DOM_HTML_INPUT_ELEMENT (el), FALSE);
-       }
-       g_object_unref (el);
+       show_checkbox (view, CHECKBOX_UPDATE, show, FALSE);
 }
 
 gboolean
 itip_view_get_show_update_check (ItipView *view)
 {
-       gboolean value;
-       WebKitDOMElement *el;
-
        g_return_val_if_fail (ITIP_IS_VIEW (view), FALSE);
 
-       if (!view->priv->dom_document)
-               return FALSE;
-
-       el = webkit_dom_document_get_element_by_id (
-               view->priv->dom_document, CHECKBOX_UPDATE);
-       value = webkit_dom_html_element_get_hidden (WEBKIT_DOM_HTML_ELEMENT (el));
-       g_object_unref (el);
-       return !value;
+       return !element_is_hidden (view, CHECKBOX_UPDATE);
 }
 
 void
 itip_view_set_rsvp_comment (ItipView *view,
                             const gchar *comment)
 {
-       WebKitDOMElement *el;
-
        g_return_if_fail (ITIP_IS_VIEW (view));
 
-       if (!view->priv->dom_document)
+       if (!view->priv->web_extension)
                return;
 
-       el = webkit_dom_document_get_element_by_id (
-               view->priv->dom_document, TEXTAREA_RSVP_COMMENT);
-       webkit_dom_html_element_set_hidden (
-               WEBKIT_DOM_HTML_ELEMENT (el), (comment == NULL));
-
        if (comment) {
-               webkit_dom_html_text_area_element_set_value (
-                       WEBKIT_DOM_HTML_TEXT_AREA_ELEMENT (el), comment);
+               g_dbus_proxy_call (
+                       view->priv->web_extension,
+                       "TextAreaSetValue",
+                       g_variant_new ("(tsss)", view->priv->page_id, view->priv->part_id, 
TEXTAREA_RSVP_COMMENT, comment),
+                       G_DBUS_CALL_FLAGS_NONE,
+                       -1,
+                       NULL,
+                       NULL,
+                       NULL);
        }
-       g_object_unref (el);
 }
 
 gchar *
 itip_view_get_rsvp_comment (ItipView *view)
 {
-       gchar *value;
-       WebKitDOMElement *el;
+       GVariant *result;
 
        g_return_val_if_fail (ITIP_IS_VIEW (view), NULL);
 
-       if (!view->priv->dom_document)
+       if (!view->priv->web_extension)
                return NULL;
 
-       el = webkit_dom_document_get_element_by_id (
-               view->priv->dom_document, TEXTAREA_RSVP_COMMENT);
-
-       if (webkit_dom_html_element_get_hidden (WEBKIT_DOM_HTML_ELEMENT (el))) {
+       if (element_is_hidden (view, TEXTAREA_RSVP_COMMENT))
                return NULL;
+
+       result = g_dbus_proxy_call_sync (
+               view->priv->web_extension,
+               "TextAreaGetValue",
+               g_variant_new ("(tss)", view->priv->page_id, view->priv->part_id, TEXTAREA_RSVP_COMMENT),
+               G_DBUS_CALL_FLAGS_NONE,
+               -1,
+               NULL,
+               NULL);
+
+       if (result) {
+               gchar *value;
+
+               g_variant_get (result, "(s)", &value);
+               g_variant_unref (result);
+               return value;
        }
 
-       value = webkit_dom_html_text_area_element_get_value (
-               WEBKIT_DOM_HTML_TEXT_AREA_ELEMENT (el));
-       g_object_unref (el);
-       return value;
+       return NULL;
 }
 
 void
@@ -2897,77 +2941,24 @@ void
 itip_view_set_buttons_sensitive (ItipView *view,
                                  gboolean sensitive)
 {
-       WebKitDOMElement *el, *cell;
-
        g_return_if_fail (ITIP_IS_VIEW (view));
 
        d (printf ("Settings buttons %s\n", sensitive ? "sensitive" : "insensitive"));
 
        view->priv->buttons_sensitive = sensitive;
 
-       if (!view->priv->dom_document)
+       if (!view->priv->web_extension)
                return;
 
-       el = webkit_dom_document_get_element_by_id (
-               view->priv->dom_document, CHECKBOX_UPDATE);
-       webkit_dom_html_input_element_set_disabled (
-               WEBKIT_DOM_HTML_INPUT_ELEMENT (el), !sensitive);
-       g_object_unref (el);
-
-       el = webkit_dom_document_get_element_by_id (
-               view->priv->dom_document, CHECKBOX_RECUR);
-       webkit_dom_html_input_element_set_disabled (
-               WEBKIT_DOM_HTML_INPUT_ELEMENT (el), !sensitive);
-       g_object_unref (el);
-
-       el = webkit_dom_document_get_element_by_id (
-               view->priv->dom_document, CHECKBOX_FREE_TIME);
-       webkit_dom_html_input_element_set_disabled (
-               WEBKIT_DOM_HTML_INPUT_ELEMENT (el), !sensitive);
-       g_object_unref (el);
-
-       el = webkit_dom_document_get_element_by_id (
-               view->priv->dom_document, CHECKBOX_KEEP_ALARM);
-       webkit_dom_html_input_element_set_disabled (
-               WEBKIT_DOM_HTML_INPUT_ELEMENT (el), !sensitive);
-       g_object_unref (el);
-
-       el = webkit_dom_document_get_element_by_id (
-               view->priv->dom_document, CHECKBOX_INHERIT_ALARM);
-       webkit_dom_html_input_element_set_disabled (
-               WEBKIT_DOM_HTML_INPUT_ELEMENT (el), !sensitive);
-       g_object_unref (el);
-
-       el = webkit_dom_document_get_element_by_id (
-               view->priv->dom_document, CHECKBOX_RSVP);
-       webkit_dom_html_input_element_set_disabled (
-               WEBKIT_DOM_HTML_INPUT_ELEMENT (el), !sensitive);
-       g_object_unref (el);
-
-       el = webkit_dom_document_get_element_by_id (
-               view->priv->dom_document, TEXTAREA_RSVP_COMMENT);
-       webkit_dom_html_text_area_element_set_disabled (
-               WEBKIT_DOM_HTML_TEXT_AREA_ELEMENT (el), !sensitive);
-       g_object_unref (el);
-
-       el = webkit_dom_document_get_element_by_id (
-               view->priv->dom_document, TABLE_ROW_BUTTONS);
-       cell = webkit_dom_element_get_first_element_child (el);
-       do {
-               WebKitDOMElement *btn, *next_cell;
-
-               next_cell = webkit_dom_element_get_next_element_sibling (cell);
-               btn = webkit_dom_element_get_first_element_child (cell);
-               if (!webkit_dom_html_element_get_hidden (
-                       WEBKIT_DOM_HTML_ELEMENT (btn))) {
-                       webkit_dom_html_button_element_set_disabled (
-                               WEBKIT_DOM_HTML_BUTTON_ELEMENT (btn), !sensitive);
-               }
-               g_object_unref (btn);
-               g_object_unref (cell);
-               cell = next_cell;
-       } while (cell);
-       g_object_unref (el);
+       g_dbus_proxy_call (
+               view->priv->web_extension,
+               "SetButtonsSensitive",
+               g_variant_new ("(tsb)", view->priv->page_id, view->priv->part_id, sensitive),
+               G_DBUS_CALL_FLAGS_NONE,
+               -1,
+               NULL,
+               NULL,
+               NULL);
 }
 
 gboolean
@@ -2981,217 +2972,69 @@ itip_view_get_buttons_sensitive (ItipView *view)
 gboolean
 itip_view_get_recur_check_state (ItipView *view)
 {
-       gboolean value;
-       WebKitDOMElement *el;
-
        g_return_val_if_fail (ITIP_IS_VIEW (view), FALSE);
 
-       if (!view->priv->dom_document)
-               return FALSE;
-
-       el = webkit_dom_document_get_element_by_id (
-               view->priv->dom_document, CHECKBOX_RECUR);
-       value = webkit_dom_html_input_element_get_checked (
-               WEBKIT_DOM_HTML_INPUT_ELEMENT (el));
-       g_object_unref (el);
-       return value;
+       return input_is_checked (view, CHECKBOX_RECUR);
 }
 
 void
 itip_view_set_show_recur_check (ItipView *view,
                                 gboolean show)
 {
-       WebKitDOMElement *label;
-       WebKitDOMElement *el;
-
        g_return_if_fail (ITIP_IS_VIEW (view));
 
-       if (!view->priv->dom_document)
-               return;
-
-       el = webkit_dom_document_get_element_by_id (
-               view->priv->dom_document, "table_row_" CHECKBOX_RECUR);
-       webkit_dom_html_element_set_hidden (WEBKIT_DOM_HTML_ELEMENT (el), !show);
-       g_object_unref (el);
-
-       el = webkit_dom_document_get_element_by_id (
-               view->priv->dom_document, CHECKBOX_RECUR);
-       label = webkit_dom_element_get_next_element_sibling (el);
-       webkit_dom_html_element_set_hidden (WEBKIT_DOM_HTML_ELEMENT (label), !show);
-       g_object_unref (label);
-
-       if (!show) {
-               webkit_dom_html_input_element_set_checked (
-                       WEBKIT_DOM_HTML_INPUT_ELEMENT (el), FALSE);
-       }
-
-        /* and update state of the second check */
-       alarm_check_toggled_cb (
-               WEBKIT_DOM_HTML_INPUT_ELEMENT (el),
-               NULL, view);
-       g_object_unref (el);
+       show_checkbox (view, CHECKBOX_RECUR, show, TRUE);
 }
 
 void
 itip_view_set_show_free_time_check (ItipView *view,
                                     gboolean show)
 {
-       WebKitDOMElement *label;
-       WebKitDOMElement *el;
-
        g_return_if_fail (ITIP_IS_VIEW (view));
 
-       if (!view->priv->dom_document)
-               return;
-
-       el = webkit_dom_document_get_element_by_id (
-               view->priv->dom_document, "table_row_" CHECKBOX_FREE_TIME);
-       webkit_dom_html_element_set_hidden (WEBKIT_DOM_HTML_ELEMENT (el), !show);
-       g_object_unref (el);
-
-       el = webkit_dom_document_get_element_by_id (
-               view->priv->dom_document, CHECKBOX_FREE_TIME);
-       label = webkit_dom_element_get_next_element_sibling (el);
-       webkit_dom_html_element_set_hidden (WEBKIT_DOM_HTML_ELEMENT (label), !show);
-       g_object_unref (label);
-
-       if (!show) {
-               webkit_dom_html_input_element_set_checked (
-                       WEBKIT_DOM_HTML_INPUT_ELEMENT (el), FALSE);
-       }
-
-        /* and update state of the second check */
-       alarm_check_toggled_cb (
-               WEBKIT_DOM_HTML_INPUT_ELEMENT (el),
-               NULL, view);
-       g_object_unref (el);
+       show_checkbox (view, CHECKBOX_FREE_TIME, show, TRUE);
 }
 
 gboolean
 itip_view_get_free_time_check_state (ItipView *view)
 {
-       gboolean value;
-       WebKitDOMElement *el;
-
        g_return_val_if_fail (ITIP_IS_VIEW (view), FALSE);
 
-       if (!view->priv->dom_document)
-               return FALSE;
-
-       el = webkit_dom_document_get_element_by_id (
-               view->priv->dom_document, CHECKBOX_FREE_TIME);
-       value = webkit_dom_html_input_element_get_checked (
-               WEBKIT_DOM_HTML_INPUT_ELEMENT (el));
-       g_object_unref (el);
-       return value;
+       return input_is_checked (view, CHECKBOX_FREE_TIME);
 }
 
 void
 itip_view_set_show_keep_alarm_check (ItipView *view,
                                      gboolean show)
 {
-       WebKitDOMElement *label;
-       WebKitDOMElement *el;
-
        g_return_if_fail (ITIP_IS_VIEW (view));
 
-       if (!view->priv->dom_document)
-               return;
-
-       el = webkit_dom_document_get_element_by_id (
-               view->priv->dom_document, "table_row_" CHECKBOX_KEEP_ALARM);
-       webkit_dom_html_element_set_hidden (WEBKIT_DOM_HTML_ELEMENT (el), !show);
-       g_object_unref (el);
-
-       el = webkit_dom_document_get_element_by_id (
-               view->priv->dom_document, CHECKBOX_KEEP_ALARM);
-       label = webkit_dom_element_get_next_element_sibling (el);
-       webkit_dom_html_element_set_hidden (WEBKIT_DOM_HTML_ELEMENT (label), !show);
-       g_object_unref (label);
-
-       if (!show) {
-               webkit_dom_html_input_element_set_checked (
-                       WEBKIT_DOM_HTML_INPUT_ELEMENT (el), FALSE);
-       }
-
-        /* and update state of the second check */
-       alarm_check_toggled_cb (
-               WEBKIT_DOM_HTML_INPUT_ELEMENT (el),
-               NULL, view);
-       g_object_unref (el);
+       show_checkbox (view, CHECKBOX_KEEP_ALARM, show, TRUE);
 }
 
 gboolean
 itip_view_get_keep_alarm_check_state (ItipView *view)
 {
-       gboolean value;
-       WebKitDOMElement *el;
-
        g_return_val_if_fail (ITIP_IS_VIEW (view), FALSE);
 
-       if (!view->priv->dom_document)
-               return FALSE;
-
-       el = webkit_dom_document_get_element_by_id (
-               view->priv->dom_document, CHECKBOX_KEEP_ALARM);
-       value = webkit_dom_html_input_element_get_checked (
-               WEBKIT_DOM_HTML_INPUT_ELEMENT (el));
-       g_object_unref (el);
-       return value;
+       return input_is_checked (view, CHECKBOX_KEEP_ALARM);
 }
 
 void
 itip_view_set_show_inherit_alarm_check (ItipView *view,
                                         gboolean show)
 {
-       WebKitDOMElement *label;
-       WebKitDOMElement *el;
-
        g_return_if_fail (ITIP_IS_VIEW (view));
 
-       if (!view->priv->dom_document)
-               return;
-
-       el = webkit_dom_document_get_element_by_id (
-               view->priv->dom_document, "table_row_" CHECKBOX_INHERIT_ALARM);
-       webkit_dom_html_element_set_hidden (WEBKIT_DOM_HTML_ELEMENT (el), !show);
-       g_object_unref (el);
-
-       el = webkit_dom_document_get_element_by_id (
-               view->priv->dom_document, CHECKBOX_INHERIT_ALARM);
-       label = webkit_dom_element_get_next_element_sibling (el);
-       webkit_dom_html_element_set_hidden (WEBKIT_DOM_HTML_ELEMENT (label), !show);
-       g_object_unref (label);
-
-       if (!show) {
-               webkit_dom_html_input_element_set_checked (
-                       WEBKIT_DOM_HTML_INPUT_ELEMENT (el), FALSE);
-       }
-
-       /* and update state of the second check */
-       alarm_check_toggled_cb (
-               WEBKIT_DOM_HTML_INPUT_ELEMENT (el),
-               NULL, view);
-       g_object_unref (el);
+       show_checkbox (view, CHECKBOX_INHERIT_ALARM, show, TRUE);
 }
 
 gboolean
 itip_view_get_inherit_alarm_check_state (ItipView *view)
 {
-       gboolean value;
-       WebKitDOMElement *el;
-
        g_return_val_if_fail (ITIP_IS_VIEW (view), FALSE);
 
-       if (!view->priv->dom_document)
-               return FALSE;
-
-       el = webkit_dom_document_get_element_by_id (
-               view->priv->dom_document, CHECKBOX_INHERIT_ALARM);
-       value = webkit_dom_html_input_element_get_checked (
-               WEBKIT_DOM_HTML_INPUT_ELEMENT (el));
-       g_object_unref (el);
-       return value;
+       return input_is_checked (view, CHECKBOX_INHERIT_ALARM);
 }
 
 void
@@ -3199,7 +3042,6 @@ itip_view_set_error (ItipView *view,
                      const gchar *error_html,
                      gboolean show_save_btn)
 {
-       WebKitDOMElement *content, *error;
        GString *str;
 
        g_return_if_fail (ITIP_IS_VIEW (view));
@@ -3214,7 +3056,7 @@ itip_view_set_error (ItipView *view,
                        "<tr width=\"100%\" id=\"" TABLE_ROW_BUTTONS "\">");
 
                buttons_table_write_button (
-                       str, BUTTON_SAVE, _("Sa_ve"),
+                       str, view->priv->itip_part_ptr, BUTTON_SAVE, _("Sa_ve"),
                        "document-save", ITIP_VIEW_RESPONSE_SAVE);
 
                g_string_append (str, "</tr></table>");
@@ -3223,43 +3065,24 @@ itip_view_set_error (ItipView *view,
        view->priv->error = str->str;
        g_string_free (str, FALSE);
 
-       if (!view->priv->dom_document)
+       if (!view->priv->web_extension)
                return;
 
-       content = webkit_dom_document_get_element_by_id (
-               view->priv->dom_document, DIV_ITIP_CONTENT);
-       webkit_dom_html_element_set_hidden (
-               WEBKIT_DOM_HTML_ELEMENT (content), TRUE);
-       g_object_unref (content);
-
-       error = webkit_dom_document_get_element_by_id (
-               view->priv->dom_document, DIV_ITIP_ERROR);
-       webkit_dom_html_element_set_hidden (
-               WEBKIT_DOM_HTML_ELEMENT (error), FALSE);
-
-       webkit_dom_html_element_set_inner_html (
-               WEBKIT_DOM_HTML_ELEMENT (error), view->priv->error, NULL);
-       g_object_unref (error);
+       hide_element (view, DIV_ITIP_CONTENT, TRUE);
+       hide_element (view, DIV_ITIP_ERROR, FALSE);
+       set_inner_html (view, DIV_ITIP_ERROR, view->priv->error);
 
        if (show_save_btn) {
-               WebKitDOMElement *el;
-
                show_button (view, BUTTON_SAVE);
+               enable_button (view, BUTTON_SAVE, TRUE);
 
-               el = webkit_dom_document_get_element_by_id (
-                       view->priv->dom_document, BUTTON_SAVE);
-               webkit_dom_html_button_element_set_disabled (
-                       WEBKIT_DOM_HTML_BUTTON_ELEMENT (el), FALSE);
-               webkit_dom_event_target_add_event_listener (
-                       WEBKIT_DOM_EVENT_TARGET (el), "click",
-                       G_CALLBACK (button_clicked_cb), FALSE, view);
+               itip_view_register_clicked_listener (view);
        }
 }
 
 /******************************************************************************/
 
 typedef struct {
-       EMailPartItip *puri;
         ItipView *view;
        GCancellable *itip_cancellable;
        GCancellable *cancellable;
@@ -3350,7 +3173,6 @@ find_attendee_if_sentby (icalcomponent *ical_comp,
 
 static void
 find_to_address (ItipView *view,
-                 EMailPartItip *itip_part,
                  icalcomponent *ical_comp,
                  icalparameter_partstat *status)
 {
@@ -3362,26 +3184,26 @@ find_to_address (ItipView *view,
        registry = view->priv->registry;
        extension_name = E_SOURCE_EXTENSION_MAIL_IDENTITY;
 
-       if (itip_part->to_address != NULL)
+       if (view->priv->to_address != NULL)
                return;
 
-       if (itip_part->msg != NULL && itip_part->folder != NULL) {
+       if (view->priv->message != NULL && view->priv->folder != NULL) {
                ESource *source;
 
                source = em_utils_guess_mail_identity (
-                       registry, itip_part->msg,
-                       itip_part->folder, itip_part->uid);
+                       registry, view->priv->message,
+                       view->priv->folder, view->priv->message_uid);
 
                if (source != NULL) {
                        extension = e_source_get_extension (source, extension_name);
 
-                       itip_part->to_address = e_source_mail_identity_dup_address (extension);
+                       view->priv->to_address = e_source_mail_identity_dup_address (extension);
 
                        g_object_unref (source);
                }
        }
 
-       if (itip_part->to_address != NULL)
+       if (view->priv->to_address != NULL)
                return;
 
        /* Look through the list of attendees to find the user's address */
@@ -3403,20 +3225,20 @@ find_to_address (ItipView *view,
 
                param = icalproperty_get_first_parameter (prop, ICAL_CN_PARAMETER);
                if (param != NULL)
-                       itip_part->to_name = g_strdup (icalparameter_get_cn (param));
+                       view->priv->to_name = g_strdup (icalparameter_get_cn (param));
 
                text = icalproperty_get_value_as_string_r (prop);
 
-               itip_part->to_address = g_strdup (itip_strip_mailto (text));
+               view->priv->to_address = g_strdup (itip_strip_mailto (text));
                g_free (text);
-               g_strstrip (itip_part->to_address);
+               g_strstrip (view->priv->to_address);
 
-               itip_part->my_address = g_strdup (address);
+               view->priv->my_address = g_strdup (address);
 
                param = icalproperty_get_first_parameter (prop, ICAL_RSVP_PARAMETER);
                if (param != NULL &&
                    icalparameter_get_rsvp (param) == ICAL_RSVP_FALSE)
-                       itip_part->no_reply_wanted = TRUE;
+                       view->priv->no_reply_wanted = TRUE;
 
                if (status) {
                        param = icalproperty_get_first_parameter (prop, ICAL_PARTSTAT_PARAMETER);
@@ -3428,7 +3250,7 @@ find_to_address (ItipView *view,
 
        g_list_free_full (list, (GDestroyNotify) g_object_unref);
 
-       if (itip_part->to_address != NULL)
+       if (view->priv->to_address != NULL)
                return;
 
        /* If the user's address was not found in the attendee's list,
@@ -3461,20 +3283,20 @@ find_to_address (ItipView *view,
 
                param = icalproperty_get_first_parameter (prop, ICAL_CN_PARAMETER);
                if (param != NULL)
-                       itip_part->to_name = g_strdup (icalparameter_get_cn (param));
+                       view->priv->to_name = g_strdup (icalparameter_get_cn (param));
 
                text = icalproperty_get_value_as_string_r (prop);
 
-               itip_part->to_address = g_strdup (itip_strip_mailto (text));
+               view->priv->to_address = g_strdup (itip_strip_mailto (text));
                g_free (text);
-               g_strstrip (itip_part->to_address);
+               g_strstrip (view->priv->to_address);
 
-               itip_part->my_address = g_strdup (address);
+               view->priv->my_address = g_strdup (address);
 
                param = icalproperty_get_first_parameter (prop, ICAL_RSVP_PARAMETER);
                if (param != NULL &&
                    ICAL_RSVP_FALSE == icalparameter_get_rsvp (param))
-                       itip_part->no_reply_wanted = TRUE;
+                       view->priv->no_reply_wanted = TRUE;
 
                if (status) {
                        param = icalproperty_get_first_parameter (prop, ICAL_PARTSTAT_PARAMETER);
@@ -3489,7 +3311,6 @@ find_to_address (ItipView *view,
 
 static void
 find_from_address (ItipView *view,
-                   EMailPartItip *pitip,
                    icalcomponent *ical_comp)
 {
        ESourceRegistry *registry;
@@ -3528,11 +3349,11 @@ find_from_address (ItipView *view,
        if (!(organizer_sentby_clean || organizer_clean))
                return;
 
-       pitip->from_address = g_strdup (organizer_clean);
+       view->priv->from_address = g_strdup (organizer_clean);
 
        param = icalproperty_get_first_parameter (prop, ICAL_CN_PARAMETER);
        if (param)
-               pitip->from_name = g_strdup (icalparameter_get_cn (param));
+               view->priv->from_name = g_strdup (icalparameter_get_cn (param));
 
        extension_name = E_SOURCE_EXTENSION_MAIL_IDENTITY;
        list = e_source_registry_list_enabled (registry, extension_name);
@@ -3550,7 +3371,7 @@ find_from_address (ItipView *view,
 
                if ((organizer_clean && !g_ascii_strcasecmp (organizer_clean, address))
                    || (organizer_sentby_clean && !g_ascii_strcasecmp (organizer_sentby_clean, address))) {
-                       pitip->my_address = g_strdup (address);
+                       view->priv->my_address = g_strdup (address);
 
                        break;
                }
@@ -3563,14 +3384,14 @@ find_from_address (ItipView *view,
 }
 
 static ECalComponent *
-get_real_item (EMailPartItip *pitip)
+get_real_item (ItipView *view)
 {
        ECalComponent *comp = NULL;
        ESource *source;
 
-       source = e_client_get_source (E_CLIENT (pitip->current_client));
+       source = e_client_get_source (E_CLIENT (view->priv->current_client));
        if (source)
-               comp = g_hash_table_lookup (pitip->real_comps, e_source_get_uid (source));
+               comp = g_hash_table_lookup (view->priv->real_comps, e_source_get_uid (source));
 
        if (!comp) {
                return NULL;
@@ -3580,12 +3401,12 @@ get_real_item (EMailPartItip *pitip)
 }
 
 static void
-adjust_item (EMailPartItip *pitip,
+adjust_item (ItipView *view,
              ECalComponent *comp)
 {
        ECalComponent *real_comp;
 
-       real_comp = get_real_item (pitip);
+       real_comp = get_real_item (view);
        if (real_comp != NULL) {
                ECalComponentText text;
                const gchar *string;
@@ -3608,16 +3429,16 @@ adjust_item (EMailPartItip *pitip,
 }
 
 static gboolean
-same_attendee_status (EMailPartItip *pitip,
+same_attendee_status (ItipView *view,
                       ECalComponent *received_comp)
 {
        ECalComponent *saved_comp;
        GSList *received_attendees = NULL, *saved_attendees = NULL, *riter, *siter;
        gboolean same = FALSE;
 
-       g_return_val_if_fail (pitip != NULL, FALSE);
+       g_return_val_if_fail (ITIP_IS_VIEW (view), FALSE);
 
-       saved_comp = get_real_item (pitip);
+       saved_comp = get_real_item (view);
        if (!saved_comp)
                return FALSE;
 
@@ -3662,31 +3483,22 @@ same_attendee_status (EMailPartItip *pitip,
 }
 
 static void
-set_buttons_sensitive (EMailPartItip *pitip,
-                       ItipView *view)
+set_buttons_sensitive (ItipView *view)
 {
-       gboolean enabled = pitip->current_client != NULL;
+       gboolean enabled = view->priv->current_client != NULL;
 
-       if (enabled && pitip->current_client)
-               enabled = !e_client_is_readonly (E_CLIENT (pitip->current_client));
+       if (enabled && view->priv->current_client)
+               enabled = !e_client_is_readonly (E_CLIENT (view->priv->current_client));
 
        itip_view_set_buttons_sensitive (view, enabled);
 
        if (enabled && itip_view_get_mode (view) == ITIP_VIEW_MODE_REPLY &&
-           pitip->comp && same_attendee_status (pitip, pitip->comp)) {
+           view->priv->comp && same_attendee_status (view, view->priv->comp)) {
                itip_view_add_lower_info_item (
                        view, ITIP_VIEW_INFO_ITEM_TYPE_INFO,
                        _("Attendee status updated"));
 
-               if (view->priv->dom_document) {
-                       WebKitDOMElement *el;
-
-                       el = webkit_dom_document_get_element_by_id (
-                               view->priv->dom_document, BUTTON_UPDATE_ATTENDEE_STATUS);
-                       webkit_dom_html_button_element_set_disabled (
-                               WEBKIT_DOM_HTML_BUTTON_ELEMENT (el), TRUE);
-                       g_object_unref (el);
-               }
+               enable_button (view, BUTTON_UPDATE_ATTENDEE_STATUS, FALSE);
        }
 }
 
@@ -3707,12 +3519,10 @@ itip_view_cal_opened_cb (GObject *source_object,
                          gpointer user_data)
 {
        ItipView *view;
-       EMailPartItip *pitip;
        EClient *client;
        GError *error = NULL;
 
        view = ITIP_VIEW (user_data);
-       pitip = itip_view_get_mail_part (view);
 
        client = e_client_cache_get_client_finish (
                E_CLIENT_CACHE (source_object), result, &error);
@@ -3737,13 +3547,13 @@ itip_view_cal_opened_cb (GObject *source_object,
                icalcomponent *icalcomp;
                gboolean show_recur_check;
 
-               icalcomp = e_cal_component_get_icalcomponent (pitip->comp);
+               icalcomp = e_cal_component_get_icalcomponent (view->priv->comp);
 
                show_recur_check = check_is_instance (icalcomp);
                itip_view_set_show_recur_check (view, show_recur_check);
        }
 
-       if (pitip->type == E_CAL_CLIENT_SOURCE_TYPE_MEMOS) {
+       if (view->priv->type == E_CAL_CLIENT_SOURCE_TYPE_MEMOS) {
                gboolean needs_decline;
 
                needs_decline = e_client_check_capability (
@@ -3753,9 +3563,9 @@ itip_view_cal_opened_cb (GObject *source_object,
                itip_view_set_mode (view, ITIP_VIEW_MODE_PUBLISH);
        }
 
-       pitip->current_client = g_object_ref (client);
+       view->priv->current_client = g_object_ref (client);
 
-       set_buttons_sensitive (pitip, view);
+       set_buttons_sensitive (view);
 
 exit:
        g_clear_object (&client);
@@ -3763,8 +3573,7 @@ exit:
 }
 
 static void
-start_calendar_server (EMailPartItip *pitip,
-                       ItipView *view,
+start_calendar_server (ItipView *view,
                        ESource *source,
                        ECalClientSourceType type,
                        GAsyncReadyCallback func,
@@ -3793,12 +3602,11 @@ start_calendar_server (EMailPartItip *pitip,
 
        e_client_cache_get_client (
                client_cache, source, extension_name, 30,
-               pitip->cancellable, func, data);
+               view->priv->cancellable, func, data);
 }
 
 static void
-start_calendar_server_by_uid (EMailPartItip *pitip,
-                              ItipView *view,
+start_calendar_server_by_uid (ItipView *view,
                               const gchar *uid,
                               ECalClientSourceType type)
 {
@@ -3810,7 +3618,7 @@ start_calendar_server_by_uid (EMailPartItip *pitip,
 
        if (source != NULL) {
                start_calendar_server (
-                       pitip, view, source, type,
+                       view, source, type,
                        itip_view_cal_opened_cb,
                        g_object_ref (view));
                g_object_unref (source);
@@ -3820,16 +3628,15 @@ start_calendar_server_by_uid (EMailPartItip *pitip,
 static void
 source_selected_cb (ItipView *view,
                     ESource *source,
-                    gpointer data)
+                    gpointer user_data)
 {
-       EMailPartItip *pitip = data;
+       g_return_if_fail (ITIP_IS_VIEW (view));
+       g_return_if_fail (E_IS_SOURCE (source));
 
        itip_view_set_buttons_sensitive (view, FALSE);
 
-       g_return_if_fail (source != NULL);
-
        start_calendar_server (
-               pitip, view, source, pitip->type,
+               view, source, view->priv->type,
                itip_view_cal_opened_cb,
                g_object_ref (view));
 }
@@ -3838,13 +3645,11 @@ static void
 find_cal_update_ui (FormatItipFindData *fd,
                     ECalClient *cal_client)
 {
-       EMailPartItip *pitip;
        ItipView *view;
        ESource *source;
 
        g_return_if_fail (fd != NULL);
 
-       pitip = fd->puri;
        view = fd->view;
 
        /* UI part gone */
@@ -3862,26 +3667,26 @@ find_cal_update_ui (FormatItipFindData *fd,
        }
 
        /* search for a master object if the detached object doesn't exist in the calendar */
-       if (pitip->current_client && pitip->current_client == cal_client) {
+       if (view->priv->current_client && view->priv->current_client == cal_client) {
                const gchar *extension_name;
                gboolean rsvp_enabled = FALSE;
 
                itip_view_set_show_keep_alarm_check (view, fd->keep_alarm_check);
 
-               pitip->current_client = cal_client;
+               view->priv->current_client = cal_client;
 
                /* Provide extra info, since its not in the component */
                /* FIXME Check sequence number of meeting? */
                /* FIXME Do we need to adjust elsewhere for the delegated calendar item? */
                /* FIXME Need to update the fields in the view now */
-               if (pitip->method == ICAL_METHOD_REPLY || pitip->method == ICAL_METHOD_REFRESH)
-                       adjust_item (pitip, pitip->comp);
+               if (view->priv->method == ICAL_METHOD_REPLY || view->priv->method == ICAL_METHOD_REFRESH)
+                       adjust_item (view, view->priv->comp);
 
                /* We clear everything because we don't really care
                 * about any other info/warnings now we found an
                 * existing versions */
                itip_view_clear_lower_info_items (view);
-               pitip->progress_info_id = 0;
+               view->priv->progress_info_id = 0;
 
                /* FIXME Check read only state of calendar? */
                itip_view_add_lower_info_item_printf (
@@ -3894,21 +3699,21 @@ find_cal_update_ui (FormatItipFindData *fd,
                 * invitiations (REQUEST), but not replies (REPLY).
                 * Replies only make sense for events with an organizer.
                 */
-               if ((!pitip->current_client || !e_cal_client_check_save_schedules (pitip->current_client)) &&
-                   (pitip->method == ICAL_METHOD_PUBLISH || pitip->method == ICAL_METHOD_REQUEST) &&
-                   pitip->has_organizer) {
+               if ((!view->priv->current_client || !e_cal_client_check_save_schedules 
(view->priv->current_client)) &&
+                   (view->priv->method == ICAL_METHOD_PUBLISH || view->priv->method == ICAL_METHOD_REQUEST) 
&&
+                   view->priv->has_organizer) {
                        rsvp_enabled = TRUE;
                }
                itip_view_set_show_rsvp_check (view, rsvp_enabled);
 
                /* default is chosen in extract_itip_data() based on content of the VEVENT */
-               itip_view_set_rsvp (view, !pitip->no_reply_wanted);
+               itip_view_set_rsvp (view, !view->priv->no_reply_wanted);
 
-               set_buttons_sensitive (pitip, view);
+               set_buttons_sensitive (view);
 
                g_cancellable_cancel (fd->cancellable);
 
-               switch (pitip->type) {
+               switch (view->priv->type) {
                        case E_CAL_CLIENT_SOURCE_TYPE_EVENTS:
                                extension_name = E_SOURCE_EXTENSION_CALENDAR;
                                break;
@@ -3926,15 +3731,15 @@ find_cal_update_ui (FormatItipFindData *fd,
 
                g_signal_connect (
                        view, "source_selected",
-                       G_CALLBACK (source_selected_cb), pitip);
+                       G_CALLBACK (source_selected_cb), NULL);
 
                itip_view_set_source (view, source);
-       } else if (!pitip->current_client)
+       } else if (!view->priv->current_client)
                itip_view_set_show_keep_alarm_check (view, FALSE);
 
-       if (pitip->current_client && pitip->current_client == cal_client) {
-               if (e_cal_client_check_recurrences_no_master (pitip->current_client)) {
-                       icalcomponent *icalcomp = e_cal_component_get_icalcomponent (pitip->comp);
+       if (view->priv->current_client && view->priv->current_client == cal_client) {
+               if (e_cal_client_check_recurrences_no_master (view->priv->current_client)) {
+                       icalcomponent *icalcomp = e_cal_component_get_icalcomponent (view->priv->comp);
 
                        if (check_is_instance (icalcomp))
                                itip_view_set_show_recur_check (view, TRUE);
@@ -3942,9 +3747,9 @@ find_cal_update_ui (FormatItipFindData *fd,
                                itip_view_set_show_recur_check (view, FALSE);
                }
 
-               if (pitip->type == E_CAL_CLIENT_SOURCE_TYPE_MEMOS) {
+               if (view->priv->type == E_CAL_CLIENT_SOURCE_TYPE_MEMOS) {
                        /* TODO The static capability should be made generic to convey that the calendar 
contains unaccepted items */
-                       if (e_client_check_capability (E_CLIENT (pitip->current_client), 
CAL_STATIC_CAPABILITY_HAS_UNACCEPTED_MEETING))
+                       if (e_client_check_capability (E_CLIENT (view->priv->current_client), 
CAL_STATIC_CAPABILITY_HAS_UNACCEPTED_MEETING))
                                itip_view_set_needs_decline (view, TRUE);
                        else
                                itip_view_set_needs_decline (view, FALSE);
@@ -3964,11 +3769,10 @@ decrease_find_data (FormatItipFindData *fd)
 
        if (fd->count == 0 && !g_cancellable_is_cancelled (fd->cancellable)) {
                gboolean rsvp_enabled = FALSE;
-               EMailPartItip *pitip = fd->puri;
                ItipView *view = fd->view;
 
-               itip_view_remove_lower_info_item (view, pitip->progress_info_id);
-               pitip->progress_info_id = 0;
+               itip_view_remove_lower_info_item (view, view->priv->progress_info_id);
+               view->priv->progress_info_id = 0;
 
                /*
                 * Only allow replies if backend doesn't do that automatically.
@@ -3976,23 +3780,23 @@ decrease_find_data (FormatItipFindData *fd)
                 * invitiations (REQUEST), but not replies (REPLY).
                 * Replies only make sense for events with an organizer.
                 */
-               if ((!pitip->current_client || !e_cal_client_check_save_schedules (pitip->current_client)) &&
-                   (pitip->method == ICAL_METHOD_PUBLISH || pitip->method == ICAL_METHOD_REQUEST) &&
-                   pitip->has_organizer) {
+               if ((!view->priv->current_client || !e_cal_client_check_save_schedules 
(view->priv->current_client)) &&
+                   (view->priv->method == ICAL_METHOD_PUBLISH || view->priv->method == ICAL_METHOD_REQUEST) 
&&
+                   view->priv->has_organizer) {
                        rsvp_enabled = TRUE;
                }
                itip_view_set_show_rsvp_check (view, rsvp_enabled);
 
                /* default is chosen in extract_itip_data() based on content of the VEVENT */
-               itip_view_set_rsvp (view, !pitip->no_reply_wanted);
+               itip_view_set_rsvp (view, !view->priv->no_reply_wanted);
 
-               if ((pitip->method == ICAL_METHOD_PUBLISH || pitip->method == ICAL_METHOD_REQUEST)
-                   && !pitip->current_client) {
+               if ((view->priv->method == ICAL_METHOD_PUBLISH || view->priv->method == ICAL_METHOD_REQUEST)
+                   && !view->priv->current_client) {
                        /* Reuse already declared one or rename? */
                        ESource *source = NULL;
                        const gchar *extension_name;
 
-                       switch (pitip->type) {
+                       switch (view->priv->type) {
                                case E_CAL_CLIENT_SOURCE_TYPE_EVENTS:
                                        extension_name = E_SOURCE_EXTENSION_CALENDAR;
                                        break;
@@ -4013,7 +3817,7 @@ decrease_find_data (FormatItipFindData *fd)
 
                        g_signal_connect (
                                view, "source_selected",
-                               G_CALLBACK (source_selected_cb), pitip);
+                               G_CALLBACK (source_selected_cb), NULL);
 
                        if (source != NULL) {
                                itip_view_set_source (view, source);
@@ -4024,8 +3828,8 @@ decrease_find_data (FormatItipFindData *fd)
                                itip_view_add_lower_info_item (view, ITIP_VIEW_INFO_ITEM_TYPE_ERROR, 
_("Unable to find any calendars"));
                                itip_view_set_buttons_sensitive (view, FALSE);
                        }
-               } else if (!pitip->current_client) {
-                       switch (pitip->type) {
+               } else if (!view->priv->current_client) {
+                       switch (view->priv->type) {
                        case E_CAL_CLIENT_SOURCE_TYPE_EVENTS:
                                itip_view_add_lower_info_item_printf (
                                        view, ITIP_VIEW_INFO_ITEM_TYPE_WARNING,
@@ -4087,8 +3891,8 @@ get_object_without_rid_ready_cb (GObject *source_object,
        if (icalcomp) {
                ECalComponent *comp;
 
-               fd->puri->current_client = cal_client;
-               fd->keep_alarm_check = (fd->puri->method == ICAL_METHOD_PUBLISH || fd->puri->method == 
ICAL_METHOD_REQUEST) &&
+               fd->view->priv->current_client = cal_client;
+               fd->keep_alarm_check = (fd->view->priv->method == ICAL_METHOD_PUBLISH || 
fd->view->priv->method == ICAL_METHOD_REQUEST) &&
                        (icalcomponent_get_first_component (icalcomp, ICAL_VALARM_COMPONENT) ||
                        icalcomponent_get_first_component (icalcomp, ICAL_XAUDIOALARM_COMPONENT) ||
                        icalcomponent_get_first_component (icalcomp, ICAL_XDISPLAYALARM_COMPONENT) ||
@@ -4099,7 +3903,7 @@ get_object_without_rid_ready_cb (GObject *source_object,
                if (comp) {
                        ESource *source = e_client_get_source (E_CLIENT (cal_client));
 
-                       g_hash_table_insert (fd->puri->real_comps, g_strdup (e_source_get_uid (source)), 
comp);
+                       g_hash_table_insert (fd->view->priv->real_comps, g_strdup (e_source_get_uid 
(source)), comp);
                }
 
                find_cal_update_ui (fd, cal_client);
@@ -4136,8 +3940,8 @@ get_object_with_rid_ready_cb (GObject *source_object,
        if (icalcomp) {
                ECalComponent *comp;
 
-               fd->puri->current_client = cal_client;
-               fd->keep_alarm_check = (fd->puri->method == ICAL_METHOD_PUBLISH || fd->puri->method == 
ICAL_METHOD_REQUEST) &&
+               fd->view->priv->current_client = cal_client;
+               fd->keep_alarm_check = (fd->view->priv->method == ICAL_METHOD_PUBLISH || 
fd->view->priv->method == ICAL_METHOD_REQUEST) &&
                        (icalcomponent_get_first_component (icalcomp, ICAL_VALARM_COMPONENT) ||
                        icalcomponent_get_first_component (icalcomp, ICAL_XAUDIOALARM_COMPONENT) ||
                        icalcomponent_get_first_component (icalcomp, ICAL_XDISPLAYALARM_COMPONENT) ||
@@ -4148,7 +3952,7 @@ get_object_with_rid_ready_cb (GObject *source_object,
                if (comp) {
                        ESource *source = e_client_get_source (E_CLIENT (cal_client));
 
-                       g_hash_table_insert (fd->puri->real_comps, g_strdup (e_source_get_uid (source)), 
comp);
+                       g_hash_table_insert (fd->view->priv->real_comps, g_strdup (e_source_get_uid 
(source)), comp);
                }
 
                find_cal_update_ui (fd, cal_client);
@@ -4210,7 +4014,6 @@ find_cal_opened_cb (GObject *source_object,
                     gpointer user_data)
 {
        FormatItipFindData *fd = user_data;
-       EMailPartItip *pitip = fd->puri;
        ItipView *view = fd->view;
        EClient *client;
        ESource *source;
@@ -4260,7 +4063,7 @@ find_cal_opened_cb (GObject *source_object,
 
                extension = e_source_get_extension (source, extension_name);
                search_for_conflicts =
-                       (pitip->type == E_CAL_CLIENT_SOURCE_TYPE_EVENTS) &&
+                       (view->priv->type == E_CAL_CLIENT_SOURCE_TYPE_EVENTS) &&
                        e_source_conflict_search_get_include_me (extension);
        }
 
@@ -4282,7 +4085,7 @@ find_cal_opened_cb (GObject *source_object,
                return;
        }
 
-       if (!pitip->current_client) {
+       if (!view->priv->current_client) {
                e_cal_client_get_object (
                        cal_client, fd->uid, fd->rid,
                        fd->cancellable,
@@ -4302,8 +4105,7 @@ itip_cancellable_cancelled (GCancellable *itip_cancellable,
 }
 
 static void
-find_server (EMailPartItip *pitip,
-             ItipView *view,
+find_server (ItipView *view,
              ECalComponent *comp)
 {
        FormatItipFindData *fd = NULL;
@@ -4316,9 +4118,10 @@ find_server (EMailPartItip *pitip,
        const gchar *extension_name;
        const gchar *store_uid;
 
-       g_return_if_fail (pitip->folder != NULL);
+       g_return_if_fail (ITIP_IS_VIEW (view));
+       g_return_if_fail (view->priv->folder != NULL);
 
-       switch (pitip->type) {
+       switch (view->priv->type) {
                case E_CAL_CLIENT_SOURCE_TYPE_EVENTS:
                        extension_name = E_SOURCE_EXTENSION_CALENDAR;
                        break;
@@ -4341,7 +4144,7 @@ find_server (EMailPartItip *pitip,
        /* XXX Not sure what this was trying to do,
         *     but it propbably doesn't work anymore.
         *     Some comments would have been helpful. */
-       parent_store = camel_folder_get_parent_store (pitip->folder);
+       parent_store = camel_folder_get_parent_store (view->priv->folder);
 
        store_uid = camel_service_get_uid (CAMEL_SERVICE (parent_store));
 
@@ -4382,12 +4185,12 @@ find_server (EMailPartItip *pitip,
        if (current_source) {
                link = conflict_list;
 
-               pitip->progress_info_id = itip_view_add_lower_info_item (
+               view->priv->progress_info_id = itip_view_add_lower_info_item (
                        view, ITIP_VIEW_INFO_ITEM_TYPE_PROGRESS,
                        _("Opening the calendar. Please wait..."));
        } else {
                link = list;
-               pitip->progress_info_id = itip_view_add_lower_info_item (
+               view->priv->progress_info_id = itip_view_add_lower_info_item (
                        view, ITIP_VIEW_INFO_ITEM_TYPE_PROGRESS,
                        _("Searching for an existing version of this appointment"));
        }
@@ -4399,9 +4202,8 @@ find_server (EMailPartItip *pitip,
                        gchar *start = NULL, *end = NULL;
 
                        fd = g_new0 (FormatItipFindData, 1);
-                       fd->puri = pitip;
                        fd->view = g_object_ref (view);
-                       fd->itip_cancellable = g_object_ref (pitip->cancellable);
+                       fd->itip_cancellable = g_object_ref (view->priv->cancellable);
                        fd->cancellable = g_cancellable_new ();
                        fd->cancelled_id = g_cancellable_connect (
                                fd->itip_cancellable,
@@ -4412,9 +4214,9 @@ find_server (EMailPartItip *pitip,
                        /* avoid free this at the end */
                        rid = NULL;
 
-                       if (pitip->start_time && pitip->end_time) {
-                               start = isodate_from_time_t (pitip->start_time);
-                               end = isodate_from_time_t (pitip->end_time);
+                       if (view->priv->start_time && view->priv->end_time) {
+                               start = isodate_from_time_t (view->priv->start_time);
+                               end = isodate_from_time_t (view->priv->end_time);
 
                                fd->sexp = g_strdup_printf (
                                        "(and (occur-in-time-range? "
@@ -4422,7 +4224,7 @@ find_server (EMailPartItip *pitip,
                                        "(make-time \"%s\")) "
                                        "(not (uid? \"%s\")))",
                                        start, end,
-                                       icalcomponent_get_uid (pitip->ical_comp));
+                                       icalcomponent_get_uid (view->priv->ical_comp));
                        }
 
                        g_free (start);
@@ -4432,7 +4234,7 @@ find_server (EMailPartItip *pitip,
                d (printf ("Increasing itip formatter search count to %d\n", fd->count));
 
                start_calendar_server (
-                       pitip, view, source, pitip->type,
+                       view, source, view->priv->type,
                        find_cal_opened_cb, fd);
        }
 
@@ -4633,26 +4435,25 @@ get_uri_for_part (CamelMimePart *mime_part)
 }
 
 static void
-update_item_progress_info (EMailPartItip *pitip,
-                           ItipView *view,
+update_item_progress_info (ItipView *view,
                            const gchar *message)
 {
-       if (pitip->update_item_progress_info_id) {
-               itip_view_remove_lower_info_item (view, pitip->update_item_progress_info_id);
-               pitip->update_item_progress_info_id = 0;
+       if (view->priv->update_item_progress_info_id) {
+               itip_view_remove_lower_info_item (view, view->priv->update_item_progress_info_id);
+               view->priv->update_item_progress_info_id = 0;
 
                if (!message)
                        itip_view_set_buttons_sensitive (view, TRUE);
        }
 
-       if (pitip->update_item_error_info_id) {
-               itip_view_remove_lower_info_item (view, pitip->update_item_error_info_id);
-               pitip->update_item_error_info_id = 0;
+       if (view->priv->update_item_error_info_id) {
+               itip_view_remove_lower_info_item (view, view->priv->update_item_error_info_id);
+               view->priv->update_item_error_info_id = 0;
        }
 
        if (message) {
                itip_view_set_buttons_sensitive (view, FALSE);
-               pitip->update_item_progress_info_id =
+               view->priv->update_item_progress_info_id =
                        itip_view_add_lower_info_item (
                                view,
                                ITIP_VIEW_INFO_ITEM_TYPE_PROGRESS,
@@ -4660,13 +4461,25 @@ update_item_progress_info (EMailPartItip *pitip,
        }
 }
 
+static gboolean
+itip_view_get_delete_message (void)
+{
+       GSettings *settings;
+       gboolean delete_message;
+
+       settings = e_util_ref_settings ("org.gnome.evolution.plugin.itip");
+       delete_message = g_settings_get_boolean (settings, "delete-processed");
+       g_clear_object (&settings);
+
+       return delete_message;
+}
+
 static void
-finish_message_delete_with_rsvp (EMailPartItip *pitip,
-                                 ItipView *view,
+finish_message_delete_with_rsvp (ItipView *view,
                                  ECalClient *client)
 {
-       if (pitip->delete_message && pitip->folder)
-               camel_folder_delete_message (pitip->folder, pitip->uid);
+       if (itip_view_get_delete_message () && view->priv->folder)
+               camel_folder_delete_message (view->priv->folder, view->priv->message_uid);
 
        if (itip_view_get_rsvp (view)) {
                ECalComponent *comp = NULL;
@@ -4678,13 +4491,13 @@ finish_message_delete_with_rsvp (EMailPartItip *pitip,
                GSList *l, *list = NULL;
                gboolean found;
 
-               comp = e_cal_component_clone (pitip->comp);
+               comp = e_cal_component_clone (view->priv->comp);
                if (comp == NULL)
                        return;
 
-               if (pitip->to_address == NULL)
-                       find_to_address (view, pitip, pitip->ical_comp, NULL);
-               g_return_if_fail (pitip->to_address != NULL);
+               if (view->priv->to_address == NULL)
+                       find_to_address (view, view->priv->ical_comp, NULL);
+               g_return_if_fail (view->priv->to_address != NULL);
 
                ical_comp = e_cal_component_get_icalcomponent (comp);
 
@@ -4706,9 +4519,9 @@ finish_message_delete_with_rsvp (EMailPartItip *pitip,
 
                        /* We do this to ensure there is at most one
                         * attendee in the response */
-                       if (found || g_ascii_strcasecmp (pitip->to_address, text))
+                       if (found || g_ascii_strcasecmp (view->priv->to_address, text))
                                list = g_slist_prepend (list, prop);
-                       else if (!g_ascii_strcasecmp (pitip->to_address, text))
+                       else if (!g_ascii_strcasecmp (view->priv->to_address, text))
                                found = TRUE;
                        g_free (text);
                }
@@ -4742,11 +4555,11 @@ finish_message_delete_with_rsvp (EMailPartItip *pitip,
                if (itip_send_comp_sync (
                                view->priv->registry,
                                E_CAL_COMPONENT_METHOD_REPLY,
-                               comp, pitip->current_client,
-                               pitip->top_level, NULL, NULL, TRUE, FALSE, NULL, NULL) &&
-                               pitip->folder) {
+                               comp, view->priv->current_client,
+                               view->priv->top_level, NULL, NULL, TRUE, FALSE, NULL, NULL) &&
+                               view->priv->folder) {
                        camel_folder_set_message_flags (
-                               pitip->folder, pitip->uid,
+                               view->priv->folder, view->priv->message_uid,
                                CAMEL_MESSAGE_ANSWERED,
                                CAMEL_MESSAGE_ANSWERED);
                }
@@ -4754,7 +4567,7 @@ finish_message_delete_with_rsvp (EMailPartItip *pitip,
                g_object_unref (comp);
        }
 
-       update_item_progress_info (pitip, view, NULL);
+       update_item_progress_info (view, NULL);
 }
 
 static void
@@ -4765,7 +4578,6 @@ receive_objects_ready_cb (GObject *ecalclient,
        ECalClient *client = E_CAL_CLIENT (ecalclient);
        ESource *source = e_client_get_source (E_CLIENT (client));
        ItipView *view = user_data;
-       EMailPartItip *pitip = itip_view_get_mail_part (view);
        GError *error = NULL;
 
        e_cal_client_receive_objects_finish (client, result, &error);
@@ -4775,8 +4587,8 @@ receive_objects_ready_cb (GObject *ecalclient,
                return;
 
        } else if (error != NULL) {
-               update_item_progress_info (pitip, view, NULL);
-               pitip->update_item_error_info_id =
+               update_item_progress_info (view, NULL);
+               view->priv->update_item_error_info_id =
                        itip_view_add_lower_info_item_printf (
                                view, ITIP_VIEW_INFO_ITEM_TYPE_INFO,
                                _("Unable to send item to calendar '%s'.  %s"),
@@ -4790,7 +4602,7 @@ receive_objects_ready_cb (GObject *ecalclient,
 
        itip_view_clear_lower_info_items (view);
 
-       switch (pitip->update_item_response) {
+       switch (view->priv->update_item_response) {
        case ITIP_VIEW_RESPONSE_ACCEPT:
                itip_view_add_lower_info_item_printf (
                        view, ITIP_VIEW_INFO_ITEM_TYPE_INFO,
@@ -4818,12 +4630,11 @@ receive_objects_ready_cb (GObject *ecalclient,
                break;
        }
 
-       finish_message_delete_with_rsvp (pitip, view, client);
+       finish_message_delete_with_rsvp (view, client);
 }
 
 static void
-update_item (EMailPartItip *pitip,
-             ItipView *view,
+update_item (ItipView *view,
              ItipViewResponse response)
 {
        struct icaltimetype stamp;
@@ -4832,7 +4643,7 @@ update_item (EMailPartItip *pitip,
        ECalComponent *clone_comp;
        gchar *str;
 
-       update_item_progress_info (pitip, view, _("Saving changes to the calendar. Please wait..."));
+       update_item_progress_info (view, _("Saving changes to the calendar. Please wait..."));
 
        /* Set X-MICROSOFT-CDO-REPLYTIME to record the time at which
         * the user accepted/declined the request. (Outlook ignores
@@ -4848,11 +4659,11 @@ update_item (EMailPartItip *pitip,
        prop = icalproperty_new_x (str);
        g_free (str);
        icalproperty_set_x_name (prop, "X-MICROSOFT-CDO-REPLYTIME");
-       icalcomponent_add_property (pitip->ical_comp, prop);
+       icalcomponent_add_property (view->priv->ical_comp, prop);
 
-       clone = icalcomponent_new_clone (pitip->ical_comp);
-       icalcomponent_add_component (pitip->top_level, clone);
-       icalcomponent_set_method (pitip->top_level, pitip->method);
+       clone = icalcomponent_new_clone (view->priv->ical_comp);
+       icalcomponent_add_component (view->priv->top_level, clone);
+       icalcomponent_set_method (view->priv->top_level, view->priv->method);
 
        if (!itip_view_get_inherit_alarm_check_state (view)) {
                icalcomponent *alarm_comp;
@@ -4869,8 +4680,8 @@ update_item (EMailPartItip *pitip,
 
        clone_comp = e_cal_component_new ();
        if (!e_cal_component_set_icalcomponent (clone_comp, clone)) {
-               update_item_progress_info (pitip, view, NULL);
-               pitip->update_item_error_info_id =
+               update_item_progress_info (view, NULL);
+               view->priv->update_item_error_info_id =
                        itip_view_add_lower_info_item (
                                view, ITIP_VIEW_INFO_ITEM_TYPE_ERROR,
                                _("Unable to parse item"));
@@ -4882,7 +4693,7 @@ update_item (EMailPartItip *pitip,
                GList *alarms, *l;
                ECalComponentAlarm *alarm;
 
-               real_comp = get_real_item (pitip);
+               real_comp = get_real_item (view);
                if (real_comp != NULL) {
                        alarms = e_cal_component_get_alarm_uids (real_comp);
 
@@ -4910,7 +4721,7 @@ update_item (EMailPartItip *pitip,
        if ((response != ITIP_VIEW_RESPONSE_CANCEL)
                && (response != ITIP_VIEW_RESPONSE_DECLINE)) {
                GSList *attachments = NULL, *new_attachments = NULL, *l;
-               CamelMimeMessage *msg = pitip->msg;
+               CamelMimeMessage *msg = view->priv->message;
 
                e_cal_component_get_attachment_list (clone_comp, &attachments);
 
@@ -4929,7 +4740,7 @@ update_item (EMailPartItip *pitip,
 
                                        /* Skip the actual message and the text/calendar part */
                                        /* FIXME Do we need to skip anything else? */
-                                       if (part == (CamelMimePart *) msg || part == pitip->part)
+                                       if (part == (CamelMimePart *) msg || part == 
view->priv->itip_mime_part)
                                                continue;
 
                                        new_uri = get_uri_for_part (part);
@@ -4959,17 +4770,17 @@ update_item (EMailPartItip *pitip,
                e_cal_component_set_attachment_list (clone_comp, new_attachments);
        }
 
-       pitip->update_item_response = response;
+       view->priv->update_item_response = response;
 
        e_cal_client_receive_objects (
-               pitip->current_client,
-               pitip->top_level,
-               pitip->cancellable,
+               view->priv->current_client,
+               view->priv->top_level,
+               view->priv->cancellable,
                receive_objects_ready_cb,
                view);
 
  cleanup:
-       icalcomponent_remove_component (pitip->top_level, clone);
+       icalcomponent_remove_component (view->priv->top_level, clone);
        g_object_unref (clone_comp);
 }
 
@@ -5056,8 +4867,7 @@ send_comp_to_attendee (ESourceRegistry *registry,
 }
 
 static void
-remove_delegate (EMailPartItip *pitip,
-                 ItipView *view,
+remove_delegate (ItipView *view,
                  const gchar *delegate,
                  const gchar *delegator,
                  ECalComponent *comp)
@@ -5072,13 +4882,13 @@ remove_delegate (EMailPartItip *pitip,
        /* send cancellation notice to delegate */
        status = send_comp_to_attendee (
                view->priv->registry,
-               E_CAL_COMPONENT_METHOD_CANCEL, pitip->comp,
-               delegate, pitip->current_client, comment);
+               E_CAL_COMPONENT_METHOD_CANCEL, view->priv->comp,
+               delegate, view->priv->current_client, comment);
        if (status != 0) {
                send_comp_to_attendee (
                        view->priv->registry,
-                       E_CAL_COMPONENT_METHOD_REQUEST, pitip->comp,
-                       delegator, pitip->current_client, comment);
+                       E_CAL_COMPONENT_METHOD_REQUEST, view->priv->comp,
+                       delegator, view->priv->current_client, comment);
        }
        if (status != 0) {
                itip_view_add_lower_info_item (
@@ -5095,10 +4905,10 @@ remove_delegate (EMailPartItip *pitip,
 }
 
 static void
-update_x (ECalComponent *pitip_comp,
+update_x (ECalComponent *view_comp,
           ECalComponent *comp)
 {
-       icalcomponent *itip_icalcomp = e_cal_component_get_icalcomponent (pitip_comp);
+       icalcomponent *itip_icalcomp = e_cal_component_get_icalcomponent (view_comp);
        icalcomponent *icalcomp = e_cal_component_get_icalcomponent (comp);
 
        icalproperty *prop = icalcomponent_get_first_property (itip_icalcomp, ICAL_X_PROPERTY);
@@ -5122,7 +4932,6 @@ modify_object_cb (GObject *ecalclient,
 {
        ECalClient *client = E_CAL_CLIENT (ecalclient);
        ItipView *view = user_data;
-       EMailPartItip *pitip = itip_view_get_mail_part (view);
        GError *error = NULL;
 
        e_cal_client_modify_object_finish (client, result, &error);
@@ -5131,8 +4940,8 @@ modify_object_cb (GObject *ecalclient,
                g_error_free (error);
 
        } else if (error != NULL) {
-               update_item_progress_info (pitip, view, NULL);
-               pitip->update_item_error_info_id =
+               update_item_progress_info (view, NULL);
+               view->priv->update_item_error_info_id =
                        itip_view_add_lower_info_item_printf (
                                view, ITIP_VIEW_INFO_ITEM_TYPE_ERROR,
                                _("Unable to update attendee. %s"),
@@ -5140,29 +4949,20 @@ modify_object_cb (GObject *ecalclient,
                g_error_free (error);
 
        } else {
-               update_item_progress_info (pitip, view, NULL);
+               update_item_progress_info (view, NULL);
                itip_view_add_lower_info_item (
                        view, ITIP_VIEW_INFO_ITEM_TYPE_INFO,
                        _("Attendee status updated"));
 
-               if (view->priv->dom_document) {
-                       WebKitDOMElement *el;
-
-                       el = webkit_dom_document_get_element_by_id (
-                               view->priv->dom_document, BUTTON_UPDATE_ATTENDEE_STATUS);
-                       webkit_dom_html_button_element_set_disabled (
-                               WEBKIT_DOM_HTML_BUTTON_ELEMENT (el), TRUE);
-                       g_object_unref (el);
-               }
+               enable_button (view, BUTTON_UPDATE_ATTENDEE_STATUS, FALSE);
 
-               if (pitip->delete_message && pitip->folder)
-                       camel_folder_delete_message (pitip->folder, pitip->uid);
+               if (itip_view_get_delete_message () && view->priv->folder)
+                       camel_folder_delete_message (view->priv->folder, view->priv->message_uid);
        }
 }
 
 static void
-update_attendee_status_icalcomp (EMailPartItip *pitip,
-                                 ItipView *view,
+update_attendee_status_icalcomp (ItipView *view,
                                  icalcomponent *icalcomp)
 {
        ECalComponent *comp;
@@ -5170,8 +4970,8 @@ update_attendee_status_icalcomp (EMailPartItip *pitip,
        gchar *rid;
        GSList *attendees;
 
-       e_cal_component_get_uid (pitip->comp, &uid);
-       rid = e_cal_component_get_recurid_as_string (pitip->comp);
+       e_cal_component_get_uid (view->priv->comp, &uid);
+       rid = e_cal_component_get_recurid_as_string (view->priv->comp);
 
        comp = e_cal_component_new ();
        if (!e_cal_component_set_icalcomponent (comp, icalcomp)) {
@@ -5184,9 +4984,9 @@ update_attendee_status_icalcomp (EMailPartItip *pitip,
                icalcomponent *org_icalcomp;
                const gchar *delegate;
 
-               org_icalcomp = e_cal_component_get_icalcomponent (pitip->comp);
+               org_icalcomp = e_cal_component_get_icalcomponent (view->priv->comp);
 
-               e_cal_component_get_attendee_list (pitip->comp, &attendees);
+               e_cal_component_get_attendee_list (view->priv->comp, &attendees);
                if (attendees != NULL) {
                        ECalComponentAttendee *a = attendees->data;
                        icalproperty *prop, *del_prop;
@@ -5205,7 +5005,7 @@ update_attendee_status_icalcomp (EMailPartItip *pitip,
                                        icalcomponent_add_property (icalcomp, icalproperty_new_clone 
(del_prop));
                                        e_cal_component_rescan (comp);
                                } else if (response == GTK_RESPONSE_NO) {
-                                       remove_delegate (pitip, view, delegate, itip_strip_mailto (a->value), 
comp);
+                                       remove_delegate (view, delegate, itip_strip_mailto (a->value), comp);
                                        goto cleanup;
                                } else {
                                        goto cleanup;
@@ -5228,7 +5028,6 @@ update_attendee_status_icalcomp (EMailPartItip *pitip,
                                                e_cal_component_rescan (comp);
                                        } else if (response == GTK_RESPONSE_NO) {
                                                remove_delegate (
-                                                       pitip,
                                                        view,
                                                        itip_strip_mailto (a->value),
                                                        itip_strip_mailto (a->delfrom),
@@ -5279,23 +5078,23 @@ update_attendee_status_icalcomp (EMailPartItip *pitip,
                }
        }
 
-       update_x (pitip->comp, comp);
+       update_x (view->priv->comp, comp);
 
        if (itip_view_get_update (view)) {
                e_cal_component_commit_sequence (comp);
                itip_send_comp_sync (
                        view->priv->registry,
                        E_CAL_COMPONENT_METHOD_REQUEST,
-                       comp, pitip->current_client,
+                       comp, view->priv->current_client,
                        NULL, NULL, NULL, TRUE, FALSE, NULL, NULL);
        }
 
-       update_item_progress_info (pitip, view, _("Saving changes to the calendar. Please wait..."));
+       update_item_progress_info (view, _("Saving changes to the calendar. Please wait..."));
 
        e_cal_client_modify_object (
-               pitip->current_client,
+               view->priv->current_client,
                icalcomp, rid ? E_CAL_OBJ_MOD_THIS : E_CAL_OBJ_MOD_ALL,
-               pitip->cancellable,
+               view->priv->cancellable,
                modify_object_cb,
                view);
 
@@ -5310,7 +5109,6 @@ update_attendee_status_get_object_without_rid_cb (GObject *ecalclient,
 {
        ECalClient *client = E_CAL_CLIENT (ecalclient);
        ItipView *view = user_data;
-       EMailPartItip *pitip = itip_view_get_mail_part (view);
        icalcomponent *icalcomp = NULL;
        GError *error = NULL;
 
@@ -5322,8 +5120,8 @@ update_attendee_status_get_object_without_rid_cb (GObject *ecalclient,
        } else if (error != NULL) {
                g_error_free (error);
 
-               update_item_progress_info (pitip, view, NULL);
-               pitip->update_item_error_info_id =
+               update_item_progress_info (view, NULL);
+               view->priv->update_item_error_info_id =
                        itip_view_add_lower_info_item (
                                view,
                                ITIP_VIEW_INFO_ITEM_TYPE_WARNING,
@@ -5331,7 +5129,7 @@ update_attendee_status_get_object_without_rid_cb (GObject *ecalclient,
                                "because the item no longer exists"));
 
        } else {
-               update_attendee_status_icalcomp (pitip, view, icalcomp);
+               update_attendee_status_icalcomp (view, icalcomp);
        }
 }
 
@@ -5342,7 +5140,6 @@ update_attendee_status_get_object_with_rid_cb (GObject *ecalclient,
 {
        ECalClient *client = E_CAL_CLIENT (ecalclient);
        ItipView *view = user_data;
-       EMailPartItip *pitip = itip_view_get_mail_part (view);
        icalcomponent *icalcomp = NULL;
        GError *error = NULL;
 
@@ -5357,12 +5154,12 @@ update_attendee_status_get_object_with_rid_cb (GObject *ecalclient,
 
                g_error_free (error);
 
-               e_cal_component_get_uid (pitip->comp, &uid);
-               rid = e_cal_component_get_recurid_as_string (pitip->comp);
+               e_cal_component_get_uid (view->priv->comp, &uid);
+               rid = e_cal_component_get_recurid_as_string (view->priv->comp);
 
                if (rid == NULL || *rid == '\0') {
-                       update_item_progress_info (pitip, view, NULL);
-                       pitip->update_item_error_info_id =
+                       update_item_progress_info (view, NULL);
+                       view->priv->update_item_error_info_id =
                                itip_view_add_lower_info_item (
                                        view,
                                        ITIP_VIEW_INFO_ITEM_TYPE_WARNING,
@@ -5370,10 +5167,10 @@ update_attendee_status_get_object_with_rid_cb (GObject *ecalclient,
                                        "because the item no longer exists"));
                } else {
                        e_cal_client_get_object (
-                               pitip->current_client,
+                               view->priv->current_client,
                                uid,
                                NULL,
-                               pitip->cancellable,
+                               view->priv->cancellable,
                                update_attendee_status_get_object_without_rid_cb,
                                view);
                }
@@ -5381,28 +5178,27 @@ update_attendee_status_get_object_with_rid_cb (GObject *ecalclient,
                g_free (rid);
 
        } else {
-               update_attendee_status_icalcomp (pitip, view, icalcomp);
+               update_attendee_status_icalcomp (view, icalcomp);
        }
 }
 
 static void
-update_attendee_status (EMailPartItip *pitip,
-                        ItipView *view)
+update_attendee_status (ItipView *view)
 {
        const gchar *uid = NULL;
        gchar *rid;
 
        /* Obtain our version */
-       e_cal_component_get_uid (pitip->comp, &uid);
-       rid = e_cal_component_get_recurid_as_string (pitip->comp);
+       e_cal_component_get_uid (view->priv->comp, &uid);
+       rid = e_cal_component_get_recurid_as_string (view->priv->comp);
 
-       update_item_progress_info (pitip, view, _("Saving changes to the calendar. Please wait..."));
+       update_item_progress_info (view, _("Saving changes to the calendar. Please wait..."));
 
        /* search for a master object if the detached object doesn't exist in the calendar */
        e_cal_client_get_object (
-               pitip->current_client,
+               view->priv->current_client,
                uid, rid,
-               pitip->cancellable,
+               view->priv->cancellable,
                update_attendee_status_get_object_with_rid_cb,
                view);
 
@@ -5410,22 +5206,21 @@ update_attendee_status (EMailPartItip *pitip,
 }
 
 static void
-send_item (EMailPartItip *pitip,
-           ItipView *view)
+send_item (ItipView *view)
 {
        ECalComponent *comp;
 
-       comp = get_real_item (pitip);
+       comp = get_real_item (view);
 
        if (comp != NULL) {
                itip_send_comp_sync (
                        view->priv->registry,
                        E_CAL_COMPONENT_METHOD_REQUEST,
-                       comp, pitip->current_client,
+                       comp, view->priv->current_client,
                        NULL, NULL, NULL, TRUE, FALSE, NULL, NULL);
                g_object_unref (comp);
 
-               switch (pitip->type) {
+               switch (view->priv->type) {
                case E_CAL_CLIENT_SOURCE_TYPE_EVENTS:
                        itip_view_add_lower_info_item (
                                view, ITIP_VIEW_INFO_ITEM_TYPE_INFO,
@@ -5446,7 +5241,7 @@ send_item (EMailPartItip *pitip,
                        break;
                }
        } else {
-               switch (pitip->type) {
+               switch (view->priv->type) {
                case E_CAL_CLIENT_SOURCE_TYPE_EVENTS:
                        itip_view_add_lower_info_item (
                                view, ITIP_VIEW_INFO_ITEM_TYPE_ERROR,
@@ -5511,18 +5306,18 @@ attachment_load_finish (EAttachment *attachment,
 }
 
 static void
-save_vcalendar_cb (EMailPartItip *pitip)
+save_vcalendar_cb (ItipView *view)
 {
        EAttachment *attachment;
        EShell *shell;
        GFile *file;
        const gchar *suggestion;
 
-       g_return_if_fail (pitip != NULL);
-       g_return_if_fail (pitip->vcalendar != NULL);
-       g_return_if_fail (pitip->part != NULL);
+       g_return_if_fail (ITIP_IS_VIEW (view));
+       g_return_if_fail (view->priv->vcalendar != NULL);
+       g_return_if_fail (view->priv->itip_mime_part != NULL);
 
-       suggestion = camel_mime_part_get_filename (pitip->part);
+       suggestion = camel_mime_part_get_filename (view->priv->itip_mime_part);
        if (suggestion == NULL) {
                /* Translators: This is a default filename for a calendar. */
                suggestion = _("calendar.ics");
@@ -5535,7 +5330,7 @@ save_vcalendar_cb (EMailPartItip *pitip)
                return;
 
        attachment = e_attachment_new ();
-       e_attachment_set_mime_part (attachment, pitip->part);
+       e_attachment_set_mime_part (attachment, view->priv->itip_mime_part);
 
        e_attachment_load_async (
                attachment, (GAsyncReadyCallback)
@@ -5562,8 +5357,7 @@ set_itip_error (ItipView *view,
 }
 
 static gboolean
-extract_itip_data (EMailPartItip *pitip,
-                   ItipView *view,
+extract_itip_data (ItipView *view,
                    gboolean *have_alarms)
 {
        GSettings *settings;
@@ -5576,7 +5370,7 @@ extract_itip_data (EMailPartItip *pitip,
        ECalComponent *comp;
        gboolean use_default_reminder;
 
-       if (!pitip->vcalendar) {
+       if (!view->priv->vcalendar) {
                set_itip_error (
                        view,
                        _("The calendar attached is not valid"),
@@ -5586,53 +5380,53 @@ extract_itip_data (EMailPartItip *pitip,
                return FALSE;
        }
 
-       pitip->top_level = e_cal_util_new_top_level ();
+       view->priv->top_level = e_cal_util_new_top_level ();
 
-       pitip->main_comp = icalparser_parse_string (pitip->vcalendar);
-       if (pitip->main_comp == NULL || !is_icalcomp_valid (pitip->main_comp)) {
+       view->priv->main_comp = icalparser_parse_string (view->priv->vcalendar);
+       if (view->priv->main_comp == NULL || !is_icalcomp_valid (view->priv->main_comp)) {
                set_itip_error (
                        view,
                        _("The calendar attached is not valid"),
                        _("The message claims to contain a calendar, but the calendar is not a valid 
iCalendar."),
                        FALSE);
 
-               if (pitip->main_comp) {
-                       icalcomponent_free (pitip->main_comp);
-                       pitip->main_comp = NULL;
+               if (view->priv->main_comp) {
+                       icalcomponent_free (view->priv->main_comp);
+                       view->priv->main_comp = NULL;
                }
 
                return FALSE;
        }
 
-       prop = icalcomponent_get_first_property (pitip->main_comp, ICAL_METHOD_PROPERTY);
+       prop = icalcomponent_get_first_property (view->priv->main_comp, ICAL_METHOD_PROPERTY);
        if (prop == NULL) {
-               pitip->method = ICAL_METHOD_PUBLISH;
+               view->priv->method = ICAL_METHOD_PUBLISH;
        } else {
-               pitip->method = icalproperty_get_method (prop);
+               view->priv->method = icalproperty_get_method (prop);
        }
 
-       tz_iter = icalcomponent_begin_component (pitip->main_comp, ICAL_VTIMEZONE_COMPONENT);
+       tz_iter = icalcomponent_begin_component (view->priv->main_comp, ICAL_VTIMEZONE_COMPONENT);
        while ((tz_comp = icalcompiter_deref (&tz_iter)) != NULL) {
                icalcomponent *clone;
 
                clone = icalcomponent_new_clone (tz_comp);
-               icalcomponent_add_component (pitip->top_level, clone);
+               icalcomponent_add_component (view->priv->top_level, clone);
 
                icalcompiter_next (&tz_iter);
        }
 
-       pitip->iter = icalcomponent_begin_component (pitip->main_comp, ICAL_ANY_COMPONENT);
-       pitip->ical_comp = icalcompiter_deref (&pitip->iter);
-       if (pitip->ical_comp != NULL) {
-               kind = icalcomponent_isa (pitip->ical_comp);
+       view->priv->iter = icalcomponent_begin_component (view->priv->main_comp, ICAL_ANY_COMPONENT);
+       view->priv->ical_comp = icalcompiter_deref (&view->priv->iter);
+       if (view->priv->ical_comp != NULL) {
+               kind = icalcomponent_isa (view->priv->ical_comp);
                if (kind != ICAL_VEVENT_COMPONENT
                    && kind != ICAL_VTODO_COMPONENT
                    && kind != ICAL_VFREEBUSY_COMPONENT
                    && kind != ICAL_VJOURNAL_COMPONENT)
-                       pitip->ical_comp = get_next (&pitip->iter);
+                       view->priv->ical_comp = get_next (&view->priv->iter);
        }
 
-       if (pitip->ical_comp == NULL) {
+       if (view->priv->ical_comp == NULL) {
                set_itip_error (
                        view,
                        _("The item in the calendar is not valid"),
@@ -5642,13 +5436,13 @@ extract_itip_data (EMailPartItip *pitip,
                return FALSE;
        }
 
-       switch (icalcomponent_isa (pitip->ical_comp)) {
+       switch (icalcomponent_isa (view->priv->ical_comp)) {
        case ICAL_VEVENT_COMPONENT:
-               pitip->type = E_CAL_CLIENT_SOURCE_TYPE_EVENTS;
-               pitip->has_organizer = icalcomponent_get_first_property (pitip->ical_comp, 
ICAL_ORGANIZER_PROPERTY) != NULL;
-               if (icalcomponent_get_first_property (pitip->ical_comp, ICAL_ATTENDEE_PROPERTY) == NULL) {
+               view->priv->type = E_CAL_CLIENT_SOURCE_TYPE_EVENTS;
+               view->priv->has_organizer = icalcomponent_get_first_property (view->priv->ical_comp, 
ICAL_ORGANIZER_PROPERTY) != NULL;
+               if (icalcomponent_get_first_property (view->priv->ical_comp, ICAL_ATTENDEE_PROPERTY) == NULL) 
{
                        /* no attendees: assume that that this is not a meeting and organizer doesn't want a 
reply */
-                       pitip->no_reply_wanted = TRUE;
+                       view->priv->no_reply_wanted = TRUE;
                } else {
                        /*
                         * if we have attendees, then find_to_address() will check for our RSVP
@@ -5657,10 +5451,10 @@ extract_itip_data (EMailPartItip *pitip,
                }
                break;
        case ICAL_VTODO_COMPONENT:
-               pitip->type = E_CAL_CLIENT_SOURCE_TYPE_TASKS;
+               view->priv->type = E_CAL_CLIENT_SOURCE_TYPE_TASKS;
                break;
        case ICAL_VJOURNAL_COMPONENT:
-               pitip->type = E_CAL_CLIENT_SOURCE_TYPE_MEMOS;
+               view->priv->type = E_CAL_CLIENT_SOURCE_TYPE_MEMOS;
                break;
        default:
                set_itip_error (
@@ -5672,12 +5466,12 @@ extract_itip_data (EMailPartItip *pitip,
                return FALSE;
        }
 
-       pitip->total = icalcomponent_count_components (pitip->main_comp, ICAL_VEVENT_COMPONENT);
-       pitip->total += icalcomponent_count_components (pitip->main_comp, ICAL_VTODO_COMPONENT);
-       pitip->total += icalcomponent_count_components (pitip->main_comp, ICAL_VFREEBUSY_COMPONENT);
-       pitip->total += icalcomponent_count_components (pitip->main_comp, ICAL_VJOURNAL_COMPONENT);
+       view->priv->total = icalcomponent_count_components (view->priv->main_comp, ICAL_VEVENT_COMPONENT);
+       view->priv->total += icalcomponent_count_components (view->priv->main_comp, ICAL_VTODO_COMPONENT);
+       view->priv->total += icalcomponent_count_components (view->priv->main_comp, ICAL_VFREEBUSY_COMPONENT);
+       view->priv->total += icalcomponent_count_components (view->priv->main_comp, ICAL_VJOURNAL_COMPONENT);
 
-       if (pitip->total > 1) {
+       if (view->priv->total > 1) {
 
                set_itip_error (
                        view,
@@ -5685,27 +5479,29 @@ extract_itip_data (EMailPartItip *pitip,
                        _("To process all of these items, the file should be saved and the calendar 
imported"),
                        TRUE);
 
-       } if (pitip->total > 0) {
-               pitip->current = 1;
+       }
+
+       if (view->priv->total > 0) {
+               view->priv->current = 1;
        } else {
-               pitip->current = 0;
+               view->priv->current = 0;
        }
 
-       if (icalcomponent_isa (pitip->ical_comp) != ICAL_VJOURNAL_COMPONENT) {
+       if (icalcomponent_isa (view->priv->ical_comp) != ICAL_VJOURNAL_COMPONENT) {
                gchar *my_address;
 
                prop = NULL;
                comp = e_cal_component_new ();
-               e_cal_component_set_icalcomponent (comp, icalcomponent_new_clone (pitip->ical_comp));
+               e_cal_component_set_icalcomponent (comp, icalcomponent_new_clone (view->priv->ical_comp));
                my_address = itip_get_comp_attendee (
                        view->priv->registry, comp, NULL);
                g_object_unref (comp);
                comp = NULL;
 
                if (!prop)
-                       prop = find_attendee (pitip->ical_comp, my_address);
+                       prop = find_attendee (view->priv->ical_comp, my_address);
                if (!prop)
-                       prop = find_attendee_if_sentby (pitip->ical_comp, my_address);
+                       prop = find_attendee_if_sentby (view->priv->ical_comp, my_address);
                if (prop) {
                        icalparameter *param;
                        const gchar * delfrom;
@@ -5713,14 +5509,14 @@ extract_itip_data (EMailPartItip *pitip,
                        if ((param = icalproperty_get_first_parameter (prop, ICAL_DELEGATEDFROM_PARAMETER))) {
                                delfrom = icalparameter_get_delegatedfrom (param);
 
-                               pitip->delegator_address = g_strdup (itip_strip_mailto (delfrom));
+                               view->priv->delegator_address = g_strdup (itip_strip_mailto (delfrom));
                        }
                }
                g_free (my_address);
                prop = NULL;
 
                /* Determine any delegate sections */
-               prop = icalcomponent_get_first_property (pitip->ical_comp, ICAL_X_PROPERTY);
+               prop = icalcomponent_get_first_property (view->priv->ical_comp, ICAL_X_PROPERTY);
                while (prop) {
                        const gchar *x_name, *x_val;
 
@@ -5728,19 +5524,19 @@ extract_itip_data (EMailPartItip *pitip,
                        x_val = icalproperty_get_x (prop);
 
                        if (!strcmp (x_name, "X-EVOLUTION-DELEGATOR-CALENDAR-UID"))
-                               pitip->calendar_uid = g_strdup (x_val);
+                               view->priv->calendar_uid = g_strdup (x_val);
                        else if (!strcmp (x_name, "X-EVOLUTION-DELEGATOR-CALENDAR-URI"))
                                g_warning (G_STRLOC ": X-EVOLUTION-DELEGATOR-CALENDAR-URI used");
                        else if (!strcmp (x_name, "X-EVOLUTION-DELEGATOR-ADDRESS"))
-                               pitip->delegator_address = g_strdup (x_val);
+                               view->priv->delegator_address = g_strdup (x_val);
                        else if (!strcmp (x_name, "X-EVOLUTION-DELEGATOR-NAME"))
-                               pitip->delegator_name = g_strdup (x_val);
+                               view->priv->delegator_name = g_strdup (x_val);
 
-                       prop = icalcomponent_get_next_property (pitip->ical_comp, ICAL_X_PROPERTY);
+                       prop = icalcomponent_get_next_property (view->priv->ical_comp, ICAL_X_PROPERTY);
                }
 
                /* Strip out procedural alarms for security purposes */
-               alarm_iter = icalcomponent_begin_component (pitip->ical_comp, ICAL_VALARM_COMPONENT);
+               alarm_iter = icalcomponent_begin_component (view->priv->ical_comp, ICAL_VALARM_COMPONENT);
                while ((alarm_comp = icalcompiter_deref (&alarm_iter)) != NULL) {
                        icalproperty *p;
 
@@ -5748,21 +5544,21 @@ extract_itip_data (EMailPartItip *pitip,
 
                        p = icalcomponent_get_first_property (alarm_comp, ICAL_ACTION_PROPERTY);
                        if (!p || icalproperty_get_action (p) == ICAL_ACTION_PROCEDURE)
-                               icalcomponent_remove_component (pitip->ical_comp, alarm_comp);
+                               icalcomponent_remove_component (view->priv->ical_comp, alarm_comp);
 
                        icalcomponent_free (alarm_comp);
                }
 
                if (have_alarms) {
-                       alarm_iter = icalcomponent_begin_component (pitip->ical_comp, ICAL_VALARM_COMPONENT);
+                       alarm_iter = icalcomponent_begin_component (view->priv->ical_comp, 
ICAL_VALARM_COMPONENT);
                        *have_alarms = icalcompiter_deref (&alarm_iter) != NULL;
                }
        }
 
-       pitip->comp = e_cal_component_new ();
-       if (!e_cal_component_set_icalcomponent (pitip->comp, pitip->ical_comp)) {
-               g_object_unref (pitip->comp);
-               pitip->comp = NULL;
+       view->priv->comp = e_cal_component_new ();
+       if (!e_cal_component_set_icalcomponent (view->priv->comp, view->priv->ical_comp)) {
+               g_object_unref (view->priv->comp);
+               view->priv->comp = NULL;
 
                set_itip_error (
                        view,
@@ -5816,29 +5612,29 @@ extract_itip_data (EMailPartItip *pitip,
                }
 
                e_cal_component_alarm_set_trigger (acomp, trigger);
-               e_cal_component_add_alarm (pitip->comp, acomp);
+               e_cal_component_add_alarm (view->priv->comp, acomp);
 
                e_cal_component_alarm_free (acomp);
        }
 
        g_object_unref (settings);
 
-       find_from_address (view, pitip, pitip->ical_comp);
-       find_to_address (view, pitip, pitip->ical_comp, NULL);
+       find_from_address (view, view->priv->ical_comp);
+       find_to_address (view, view->priv->ical_comp, NULL);
 
        return TRUE;
 }
 
 static gboolean
-idle_open_cb (gpointer data)
+idle_open_cb (gpointer user_data)
 {
-       EMailPartItip *pitip = data;
+       ItipView *view = user_data;
        EShell *shell;
        const gchar *uris[2];
        gchar *start, *end, *shell_uri;
 
-       start = isodate_from_time_t (pitip->start_time ? pitip->start_time : time (NULL));
-       end = isodate_from_time_t (pitip->end_time ? pitip->end_time : time (NULL));
+       start = isodate_from_time_t (view->priv->start_time ? view->priv->start_time : time (NULL));
+       end = isodate_from_time_t (view->priv->end_time ? view->priv->end_time : time (NULL));
        shell_uri = g_strdup_printf ("calendar:///?startdate=%s&enddate=%s", start, end);
 
        uris[0] = shell_uri;
@@ -5857,100 +5653,99 @@ idle_open_cb (gpointer data)
 static void
 view_response_cb (ItipView *view,
                   ItipViewResponse response,
-                  gpointer data)
+                  gpointer user_data)
 {
-       EMailPartItip *pitip = data;
        gboolean status = FALSE;
        icalproperty *prop;
        ECalComponentTransparency trans;
 
        if (response == ITIP_VIEW_RESPONSE_SAVE) {
-               save_vcalendar_cb (pitip);
+               save_vcalendar_cb (view);
                return;
        }
 
-       if (pitip->method == ICAL_METHOD_PUBLISH || pitip->method == ICAL_METHOD_REQUEST) {
+       if (view->priv->method == ICAL_METHOD_PUBLISH || view->priv->method == ICAL_METHOD_REQUEST) {
                if (itip_view_get_free_time_check_state (view))
-                       e_cal_component_set_transparency (pitip->comp, E_CAL_COMPONENT_TRANSP_TRANSPARENT);
+                       e_cal_component_set_transparency (view->priv->comp, 
E_CAL_COMPONENT_TRANSP_TRANSPARENT);
                else
-                       e_cal_component_set_transparency (pitip->comp, E_CAL_COMPONENT_TRANSP_OPAQUE);
+                       e_cal_component_set_transparency (view->priv->comp, E_CAL_COMPONENT_TRANSP_OPAQUE);
        } else {
-               e_cal_component_get_transparency (pitip->comp, &trans);
+               e_cal_component_get_transparency (view->priv->comp, &trans);
 
                if (trans == E_CAL_COMPONENT_TRANSP_NONE)
-                       e_cal_component_set_transparency (pitip->comp, E_CAL_COMPONENT_TRANSP_OPAQUE);
+                       e_cal_component_set_transparency (view->priv->comp, E_CAL_COMPONENT_TRANSP_OPAQUE);
        }
 
-       if (!pitip->to_address && pitip->current_client != NULL)
-               e_client_get_backend_property_sync (E_CLIENT (pitip->current_client), 
CAL_BACKEND_PROPERTY_CAL_EMAIL_ADDRESS, &pitip->to_address, NULL, NULL);
+       if (!view->priv->to_address && view->priv->current_client != NULL)
+               e_client_get_backend_property_sync (E_CLIENT (view->priv->current_client), 
CAL_BACKEND_PROPERTY_CAL_EMAIL_ADDRESS, &view->priv->to_address, NULL, NULL);
 
        /* check if it is a  recur instance (no master object) and
         * add a property */
        if (itip_view_get_recur_check_state (view)) {
                prop = icalproperty_new_x ("All");
                icalproperty_set_x_name (prop, "X-GW-RECUR-INSTANCES-MOD-TYPE");
-               icalcomponent_add_property (pitip->ical_comp, prop);
+               icalcomponent_add_property (view->priv->ical_comp, prop);
        }
 
        switch (response) {
                case ITIP_VIEW_RESPONSE_ACCEPT:
-                       if (pitip->type != E_CAL_CLIENT_SOURCE_TYPE_MEMOS)
+                       if (view->priv->type != E_CAL_CLIENT_SOURCE_TYPE_MEMOS)
                                status = change_status (
                                        view->priv->registry,
-                                       pitip->ical_comp,
-                                       pitip->to_address,
+                                       view->priv->ical_comp,
+                                       view->priv->to_address,
                                        ICAL_PARTSTAT_ACCEPTED);
                        else
                                status = TRUE;
                        if (status) {
-                               e_cal_component_rescan (pitip->comp);
-                               update_item (pitip, view, response);
+                               e_cal_component_rescan (view->priv->comp);
+                               update_item (view, response);
                        }
                        break;
                case ITIP_VIEW_RESPONSE_TENTATIVE:
                        status = change_status (
                                        view->priv->registry,
-                                       pitip->ical_comp,
-                                       pitip->to_address,
+                                       view->priv->ical_comp,
+                                       view->priv->to_address,
                                        ICAL_PARTSTAT_TENTATIVE);
                        if (status) {
-                               e_cal_component_rescan (pitip->comp);
-                               update_item (pitip, view, response);
+                               e_cal_component_rescan (view->priv->comp);
+                               update_item (view, response);
                        }
                        break;
                case ITIP_VIEW_RESPONSE_DECLINE:
-                       if (pitip->type != E_CAL_CLIENT_SOURCE_TYPE_MEMOS)
+                       if (view->priv->type != E_CAL_CLIENT_SOURCE_TYPE_MEMOS)
                                status = change_status (
                                        view->priv->registry,
-                                       pitip->ical_comp,
-                                       pitip->to_address,
+                                       view->priv->ical_comp,
+                                       view->priv->to_address,
                                        ICAL_PARTSTAT_DECLINED);
                        else {
                                prop = icalproperty_new_x ("1");
                                icalproperty_set_x_name (prop, "X-GW-DECLINED");
-                               icalcomponent_add_property (pitip->ical_comp, prop);
+                               icalcomponent_add_property (view->priv->ical_comp, prop);
                                status = TRUE;
                        }
 
                        if (status) {
-                               e_cal_component_rescan (pitip->comp);
-                               update_item (pitip, view, response);
+                               e_cal_component_rescan (view->priv->comp);
+                               update_item (view, response);
                        }
                        break;
                case ITIP_VIEW_RESPONSE_UPDATE:
-                       update_attendee_status (pitip, view);
+                       update_attendee_status (view);
                        break;
                case ITIP_VIEW_RESPONSE_CANCEL:
-                       update_item (pitip, view, response);
+                       update_item (view, response);
                        break;
                case ITIP_VIEW_RESPONSE_REFRESH:
-                       send_item (pitip, view);
+                       send_item (view);
                        break;
                case ITIP_VIEW_RESPONSE_OPEN:
                        /* Prioritize ahead of GTK+ redraws. */
                        g_idle_add_full (
                                G_PRIORITY_HIGH_IDLE,
-                               idle_open_cb, pitip, NULL);
+                               idle_open_cb, g_object_ref (view), g_object_unref);
                        return;
                default:
                        break;
@@ -6035,8 +5830,6 @@ in_proper_folder (CamelFolder *folder)
 void
 itip_view_init_view (ItipView *view)
 {
-       EShell *shell;
-       EClientCache *client_cache;
        ECalComponentText text;
        ECalComponentOrganizer organizer;
        ECalComponentDateTime datetime;
@@ -6049,33 +5842,26 @@ itip_view_init_view (ItipView *view)
        const gchar *string, *org;
        gboolean response_enabled;
        gboolean have_alarms = FALSE;
-       EMailPartItip *info;
-
-       info = view->priv->itip_part;
-       g_return_if_fail (info != NULL);
-
-       shell = e_shell_get_default ();
-       client_cache = e_shell_get_client_cache (shell);
 
-       info->client_cache = g_object_ref (client_cache);
+       g_return_if_fail (ITIP_IS_VIEW (view));
 
         /* Reset current client before initializing view */
-       info->current_client = NULL;
+       view->priv->current_client = NULL;
 
         /* FIXME Handle multiple VEVENTS with the same UID, ie detached instances */
-       if (!extract_itip_data (info, view, &have_alarms))
+       if (!extract_itip_data (view, &have_alarms))
                return;
 
-       response_enabled = in_proper_folder (info->folder);
+       response_enabled = in_proper_folder (view->priv->folder);
 
        if (!response_enabled) {
                itip_view_set_mode (view, ITIP_VIEW_MODE_HIDE_ALL);
        } else {
                itip_view_set_show_inherit_alarm_check (
                        view,
-                       have_alarms && (info->method == ICAL_METHOD_PUBLISH || info->method == 
ICAL_METHOD_REQUEST));
+                       have_alarms && (view->priv->method == ICAL_METHOD_PUBLISH || view->priv->method == 
ICAL_METHOD_REQUEST));
 
-               switch (info->method) {
+               switch (view->priv->method) {
                        case ICAL_METHOD_PUBLISH:
                        case ICAL_METHOD_REQUEST:
                                 /*
@@ -6087,7 +5873,7 @@ itip_view_init_view (ItipView *view)
                                  */
                                itip_view_set_mode (
                                        view,
-                                       info->has_organizer ?
+                                       view->priv->has_organizer ?
                                        ITIP_VIEW_MODE_REQUEST :
                                        ITIP_VIEW_MODE_PUBLISH);
                                break;
@@ -6113,7 +5899,7 @@ itip_view_init_view (ItipView *view)
                                 /* Handle appointment requests from Microsoft Live. This is
                                  * a best-at-hand-now handling. Must be revisited when we have
                                  * better access to the source of such meetings */
-                               info->method = ICAL_METHOD_REQUEST;
+                               view->priv->method = ICAL_METHOD_REQUEST;
                                itip_view_set_mode (view, ITIP_VIEW_MODE_REQUEST);
                                break;
                        default:
@@ -6121,13 +5907,13 @@ itip_view_init_view (ItipView *view)
                }
        }
 
-       itip_view_set_item_type (view, info->type);
+       itip_view_set_item_type (view, view->priv->type);
 
        if (response_enabled) {
-               switch (info->method) {
+               switch (view->priv->method) {
                        case ICAL_METHOD_REQUEST:
                                 /* FIXME What about the name? */
-                               itip_view_set_delegator (view, info->delegator_name ? info->delegator_name : 
info->delegator_address);
+                               itip_view_set_delegator (view, view->priv->delegator_name ? 
view->priv->delegator_name : view->priv->delegator_address);
                                /* coverity[fallthrough] */
                        case ICAL_METHOD_PUBLISH:
                        case ICAL_METHOD_ADD:
@@ -6136,7 +5922,7 @@ itip_view_init_view (ItipView *view)
                                itip_view_set_show_update_check (view, FALSE);
 
                                 /* An organizer sent this */
-                               e_cal_component_get_organizer (info->comp, &organizer);
+                               e_cal_component_get_organizer (view->priv->comp, &organizer);
                                org = organizer.cn ? organizer.cn : itip_strip_mailto (organizer.value);
 
                                itip_view_set_organizer (view, org);
@@ -6144,11 +5930,11 @@ itip_view_init_view (ItipView *view)
                                        itip_view_set_organizer_sentby (
                                                view, itip_strip_mailto (organizer.sentby));
 
-                               if (info->my_address) {
-                                       if (!(organizer.value && !g_ascii_strcasecmp (itip_strip_mailto 
(organizer.value), info->my_address))
-                                               && !(organizer.sentby && !g_ascii_strcasecmp 
(itip_strip_mailto (organizer.sentby), info->my_address))
-                                               && (info->to_address && g_ascii_strcasecmp (info->to_address, 
info->my_address)))
-                                               itip_view_set_proxy (view, info->to_name ? info->to_name : 
info->to_address);
+                               if (view->priv->my_address) {
+                                       if (!(organizer.value && !g_ascii_strcasecmp (itip_strip_mailto 
(organizer.value), view->priv->my_address))
+                                               && !(organizer.sentby && !g_ascii_strcasecmp 
(itip_strip_mailto (organizer.sentby), view->priv->my_address))
+                                               && (view->priv->to_address && g_ascii_strcasecmp 
(view->priv->to_address, view->priv->my_address)))
+                                               itip_view_set_proxy (view, view->priv->to_name ? 
view->priv->to_name : view->priv->to_address);
                                }
                                break;
                        case ICAL_METHOD_REPLY:
@@ -6157,7 +5943,7 @@ itip_view_init_view (ItipView *view)
                                itip_view_set_show_update_check (view, TRUE);
 
                                 /* An attendee sent this */
-                               e_cal_component_get_attendee_list (info->comp, &list);
+                               e_cal_component_get_attendee_list (view->priv->comp, &list);
                                if (list != NULL) {
                                        ECalComponentAttendee *attendee;
 
@@ -6168,11 +5954,11 @@ itip_view_init_view (ItipView *view)
                                        if (attendee->sentby)
                                                itip_view_set_attendee_sentby (view, itip_strip_mailto 
(attendee->sentby));
 
-                                       if (info->my_address) {
-                                               if (!(attendee->value && !g_ascii_strcasecmp 
(itip_strip_mailto (attendee->value), info->my_address))
-                                                       && !(attendee->sentby && !g_ascii_strcasecmp 
(itip_strip_mailto (attendee->sentby), info->my_address))
-                                                       && (info->from_address && g_ascii_strcasecmp 
(info->from_address, info->my_address)))
-                                                       itip_view_set_proxy (view, info->from_name ? 
info->from_name : info->from_address);
+                                       if (view->priv->my_address) {
+                                               if (!(attendee->value && !g_ascii_strcasecmp 
(itip_strip_mailto (attendee->value), view->priv->my_address))
+                                                       && !(attendee->sentby && !g_ascii_strcasecmp 
(itip_strip_mailto (attendee->sentby), view->priv->my_address))
+                                                       && (view->priv->from_address && g_ascii_strcasecmp 
(view->priv->from_address, view->priv->my_address)))
+                                                       itip_view_set_proxy (view, view->priv->from_name ? 
view->priv->from_name : view->priv->from_address);
                                        }
 
                                        e_cal_component_free_attendee_list (list);
@@ -6184,15 +5970,15 @@ itip_view_init_view (ItipView *view)
                }
        }
 
-       e_cal_component_get_summary (info->comp, &text);
+       e_cal_component_get_summary (view->priv->comp, &text);
        itip_view_set_summary (view, text.value ? text.value : C_("cal-itip", "None"));
 
-       e_cal_component_get_location (info->comp, &string);
+       e_cal_component_get_location (view->priv->comp, &string);
        itip_view_set_location (view, string);
 
         /* Status really only applies for REPLY */
-       if (response_enabled && info->method == ICAL_METHOD_REPLY) {
-               e_cal_component_get_attendee_list (info->comp, &list);
+       if (response_enabled && view->priv->method == ICAL_METHOD_REPLY) {
+               e_cal_component_get_attendee_list (view->priv->comp, &list);
                if (list != NULL) {
                        ECalComponentAttendee *a = list->data;
 
@@ -6216,12 +6002,12 @@ itip_view_init_view (ItipView *view)
                e_cal_component_free_attendee_list (list);
        }
 
-       if (info->method == ICAL_METHOD_REPLY
-               || info->method == ICAL_METHOD_COUNTER
-               || info->method == ICAL_METHOD_DECLINECOUNTER) {
+       if (view->priv->method == ICAL_METHOD_REPLY
+               || view->priv->method == ICAL_METHOD_COUNTER
+               || view->priv->method == ICAL_METHOD_DECLINECOUNTER) {
                 /* FIXME Check spec to see if multiple comments are actually valid */
                 /* Comments for iTIP are limited to one per object */
-               e_cal_component_get_comment_list (info->comp, &list);
+               e_cal_component_get_comment_list (view->priv->comp, &list);
                if (list) {
                        ECalComponentText *text = list->data;
 
@@ -6243,7 +6029,7 @@ itip_view_init_view (ItipView *view)
                e_cal_component_free_text_list (list);
        }
 
-       e_cal_component_get_description_list (info->comp, &list);
+       e_cal_component_get_description_list (view->priv->comp, &list);
        for (l = list; l; l = l->next) {
                ECalComponentText *text = l->data;
 
@@ -6291,8 +6077,8 @@ itip_view_init_view (ItipView *view)
 
        g_object_unref (settings);
 
-       e_cal_component_get_dtstart (info->comp, &datetime);
-       info->start_time = 0;
+       e_cal_component_get_dtstart (view->priv->comp, &datetime);
+       view->priv->start_time = 0;
        if (datetime.value) {
                struct tm start_tm;
 
@@ -6301,17 +6087,17 @@ itip_view_init_view (ItipView *view)
                if (datetime.value->is_utc)
                        from_zone = icaltimezone_get_utc_timezone ();
                else if (!datetime.value->is_utc && datetime.tzid)
-                       from_zone = icalcomponent_get_timezone (info->top_level, datetime.tzid);
+                       from_zone = icalcomponent_get_timezone (view->priv->top_level, datetime.tzid);
                else
                        from_zone = NULL;
 
                start_tm = icaltimetype_to_tm_with_zone (datetime.value, from_zone, to_zone);
 
                itip_view_set_start (view, &start_tm, datetime.value->is_date);
-               info->start_time = icaltime_as_timet_with_zone (*datetime.value, from_zone);
+               view->priv->start_time = icaltime_as_timet_with_zone (*datetime.value, from_zone);
        }
 
-       icalcomp = e_cal_component_get_icalcomponent (info->comp);
+       icalcomp = e_cal_component_get_icalcomponent (view->priv->comp);
 
         /* Set the recurrence id */
        if (check_is_instance (icalcomp) && datetime.value) {
@@ -6322,13 +6108,13 @@ itip_view_init_view (ItipView *view)
                recur_id->type = E_CAL_COMPONENT_RANGE_SINGLE;
                recur_id->datetime.value = &icaltime;
                recur_id->datetime.tzid = icaltimezone_get_tzid (to_zone);
-               e_cal_component_set_recurid (info->comp, recur_id);
+               e_cal_component_set_recurid (view->priv->comp, recur_id);
                g_free (recur_id); /* it's ok to call g_free here */
        }
        e_cal_component_free_datetime (&datetime);
 
-       e_cal_component_get_dtend (info->comp, &datetime);
-       info->end_time = 0;
+       e_cal_component_get_dtend (view->priv->comp, &datetime);
+       view->priv->end_time = 0;
        if (datetime.value) {
                struct tm end_tm;
 
@@ -6337,7 +6123,7 @@ itip_view_init_view (ItipView *view)
                if (datetime.value->is_utc)
                        from_zone = icaltimezone_get_utc_timezone ();
                else if (!datetime.value->is_utc && datetime.tzid)
-                       from_zone = icalcomponent_get_timezone (info->top_level, datetime.tzid);
+                       from_zone = icalcomponent_get_timezone (view->priv->top_level, datetime.tzid);
                else
                        from_zone = NULL;
 
@@ -6351,15 +6137,15 @@ itip_view_init_view (ItipView *view)
                end_tm = icaltimetype_to_tm_with_zone (datetime.value, from_zone, to_zone);
 
                itip_view_set_end (view, &end_tm, datetime.value->is_date);
-               info->end_time = icaltime_as_timet_with_zone (*datetime.value, from_zone);
+               view->priv->end_time = icaltime_as_timet_with_zone (*datetime.value, from_zone);
        }
        e_cal_component_free_datetime (&datetime);
 
         /* Recurrence info */
         /* FIXME Better recurring description */
-       if (e_cal_component_has_recurrences (info->comp)) {
+       if (e_cal_component_has_recurrences (view->priv->comp)) {
                 /* FIXME Tell the user we don't support recurring tasks */
-               switch (info->type) {
+               switch (view->priv->type) {
                        case E_CAL_CLIENT_SOURCE_TYPE_EVENTS:
                                itip_view_add_upper_info_item (view, ITIP_VIEW_INFO_ITEM_TYPE_INFO, _("This 
meeting recurs"));
                                break;
@@ -6377,25 +6163,40 @@ itip_view_init_view (ItipView *view)
 
        g_signal_connect (
                view, "response",
-               G_CALLBACK (view_response_cb), info);
+               G_CALLBACK (view_response_cb), NULL);
 
        if (response_enabled) {
-               itip_view_set_show_free_time_check (view, info->type == E_CAL_CLIENT_SOURCE_TYPE_EVENTS && 
(info->method == ICAL_METHOD_PUBLISH || info->method == ICAL_METHOD_REQUEST));
+               itip_view_set_show_free_time_check (view, view->priv->type == E_CAL_CLIENT_SOURCE_TYPE_EVENTS 
&& (view->priv->method == ICAL_METHOD_PUBLISH || view->priv->method == ICAL_METHOD_REQUEST));
 
-               if (info->calendar_uid) {
-                       start_calendar_server_by_uid (info, view, info->calendar_uid, info->type);
+               if (view->priv->calendar_uid) {
+                       start_calendar_server_by_uid (view, view->priv->calendar_uid, view->priv->type);
                } else {
-                       find_server (info, view, info->comp);
-                       set_buttons_sensitive (info, view);
+                       find_server (view, view->priv->comp);
+                       set_buttons_sensitive (view);
                }
-       } else if (view->priv->dom_document) {
+       } else if (view->priv->web_extension) {
                /* The Open Calendar button can be shown, thus enable it */
-               WebKitDOMElement *el;
-
-               el = webkit_dom_document_get_element_by_id (
-                       view->priv->dom_document, BUTTON_OPEN_CALENDAR);
-               webkit_dom_html_button_element_set_disabled (
-                       WEBKIT_DOM_HTML_BUTTON_ELEMENT (el), FALSE);
-               g_object_unref (el);
+               enable_button (view, BUTTON_OPEN_CALENDAR, TRUE);
        }
 }
+
+void
+itip_view_set_web_view (ItipView *view,
+                       EWebView *web_view)
+{
+       g_return_if_fail (ITIP_IS_VIEW (view));
+       if (web_view)
+               g_return_if_fail (E_IS_WEB_VIEW (web_view));
+
+       g_weak_ref_set (view->priv->web_view_weakref, web_view);
+
+       itip_view_register_clicked_listener (view);
+}
+
+EWebView *
+itip_view_ref_web_view (ItipView *view)
+{
+       g_return_val_if_fail (ITIP_IS_VIEW (view), NULL);
+
+       return g_weak_ref_get (view->priv->web_view_weakref);
+}
diff --git a/modules/itip-formatter/itip-view.h b/modules/itip-formatter/itip-view.h
index 0ffd96f..7b220f8 100644
--- a/modules/itip-formatter/itip-view.h
+++ b/modules/itip-formatter/itip-view.h
@@ -27,7 +27,6 @@
 #include <unistd.h>
 
 #include <gtk/gtk.h>
-#include <webkit/webkitdom.h>
 
 #include <libecal/libecal.h>
 
@@ -59,8 +58,6 @@ typedef struct _ItipView ItipView;
 typedef struct _ItipViewClass ItipViewClass;
 typedef struct _ItipViewPrivate ItipViewPrivate;
 
-struct _EMailPartItip;
-
 typedef enum {
        ITIP_VIEW_MODE_NONE,
        ITIP_VIEW_MODE_PUBLISH,
@@ -109,17 +106,24 @@ struct _ItipViewClass {
 };
 
 GType          itip_view_get_type              (void);
-ItipView *     itip_view_new                   (struct _EMailPartItip *puri,
-                                                EClientCache *client_cache);
+ItipView *     itip_view_new                   (guint64 page_id,
+                                                const gchar *part_id,
+                                                gpointer itip_part_ptr,
+                                                CamelFolder *folder,
+                                                const gchar *message_uid,
+                                                CamelMimeMessage *message,
+                                                CamelMimePart *itip_mime_part,
+                                                const gchar *vcalendar,
+                                                GCancellable *cancellable);
 void           itip_view_init_view             (ItipView *view);
-void           itip_view_write                 (EMailFormatter *formatter,
+void           itip_view_set_web_view          (ItipView *view,
+                                                EWebView *web_view);
+EWebView *     itip_view_ref_web_view          (ItipView *view);
+void           itip_view_write                 (gpointer itip_part,
+                                                EMailFormatter *formatter,
                                                 GString *buffer);
 void           itip_view_write_for_printing    (ItipView *view,
                                                 GString *buffer);
-void           itip_view_create_dom_bindings   (ItipView *view,
-                                                WebKitDOMElement *element);
-struct _EMailPartItip *
-               itip_view_get_mail_part         (ItipView *view);
 EClientCache * itip_view_get_client_cache      (ItipView *view);
 const gchar *  itip_view_get_extension_name    (ItipView *view);
 void           itip_view_set_extension_name    (ItipView *view,
@@ -246,6 +250,8 @@ void                itip_view_set_show_inherit_alarm_check
 void           itip_view_set_error             (ItipView *view,
                                                 const gchar *error_html,
                                                 gboolean show_save_btn);
+GDBusProxy *   itip_view_get_web_extension_proxy
+                                               (ItipView *view);
 
 G_END_DECLS
 
diff --git a/modules/itip-formatter/web-extension/Makefile.am 
b/modules/itip-formatter/web-extension/Makefile.am
new file mode 100644
index 0000000..a519744
--- /dev/null
+++ b/modules/itip-formatter/web-extension/Makefile.am
@@ -0,0 +1,26 @@
+webextensions_LTLIBRARIES = libmoduleitipformatterwebextension.la
+
+libmoduleitipformatterwebextension_la_SOURCES =                \
+       module-itip-formatter-web-extension.c           \
+       module-itip-formatter-web-extension.h           \
+       module-itip-formatter-dom-utils.c               \
+       module-itip-formatter-dom-utils.h
+
+libmoduleitipformatterwebextension_la_CPPFLAGS =       \
+       $(AM_CPPFLAGS)                                  \
+       -I$(top_srcdir)                                 \
+       $(EVOLUTION_DATA_SERVER_CFLAGS)                 \
+       $(GNOME_PLATFORM_CFLAGS)                        \
+       $(WEB_EXTENSIONS_CFLAGS)
+
+libmoduleitipformatterwebextension_la_LIBADD =         \
+       $(top_builddir)/e-util/libevolution-util.la     \
+       $(top_builddir)/web-extensions/libedomutils.la  \
+       $(EVOLUTION_DATA_SERVER_LIBS)                   \
+       $(GNOME_PLATFORM_LIBS)                          \
+       $(WEB_EXTENSIONS_LIBS)
+
+libmoduleitipformatterwebextension_la_LDFLAGS =                \
+       -module -avoid-version -no-undefined
+
+-include $(top_srcdir)/git.mk
diff --git a/modules/itip-formatter/web-extension/module-itip-formatter-dom-utils.c 
b/modules/itip-formatter/web-extension/module-itip-formatter-dom-utils.c
new file mode 100644
index 0000000..72809ee
--- /dev/null
+++ b/modules/itip-formatter/web-extension/module-itip-formatter-dom-utils.c
@@ -0,0 +1,642 @@
+/*
+ * module-itip-formatter-dom-utils.c
+ *
+ * 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; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+#include "module-itip-formatter-dom-utils.h"
+
+#define WEBKIT_DOM_USE_UNSTABLE_API
+#include <webkitdom/WebKitDOMHTMLElementUnstable.h>
+
+#include "module-itip-formatter-web-extension.h"
+#include "../itip-view-elements-defines.h"
+
+#include <e-util/e-util.h>
+
+#define ITIP_WEB_EXTENSION_PAGE_ID_KEY "itip-web-extension-page-id"
+#define ITIP_WEB_EXTENSION_PART_ID_KEY "itip-web-extension-part-id"
+
+static void
+recur_toggled_cb (WebKitDOMHTMLInputElement *input,
+                  WebKitDOMEvent *event,
+                  GDBusConnection *connection)
+{
+       guint64 *ppage_id;
+       const gchar *part_id;
+       GError *error = NULL;
+
+       ppage_id = g_object_get_data (G_OBJECT (input), ITIP_WEB_EXTENSION_PAGE_ID_KEY);
+       part_id = g_object_get_data (G_OBJECT (input), ITIP_WEB_EXTENSION_PART_ID_KEY);
+       if (!ppage_id || !part_id) {
+               g_warning ("%s: page_id/part_id not set on %p", G_STRFUNC, input);
+               return;
+       }
+
+       g_dbus_connection_emit_signal (
+               connection,
+               NULL,
+               MODULE_ITIP_FORMATTER_WEB_EXTENSION_OBJECT_PATH,
+               MODULE_ITIP_FORMATTER_WEB_EXTENSION_INTERFACE,
+               "RecurToggled",
+               g_variant_new ("(ts)", *ppage_id, part_id),
+               &error);
+
+       if (error) {
+               g_warning ("Error emitting signal RecurToggled: %s\n", error->message);
+               g_error_free (error);
+       }
+}
+
+static void
+source_changed_cb (WebKitDOMElement *element,
+                   WebKitDOMEvent *event,
+                   GDBusConnection *connection)
+{
+       guint64 *ppage_id;
+       const gchar *part_id;
+       GError *error = NULL;
+
+       ppage_id = g_object_get_data (G_OBJECT (element), ITIP_WEB_EXTENSION_PAGE_ID_KEY);
+       part_id = g_object_get_data (G_OBJECT (element), ITIP_WEB_EXTENSION_PART_ID_KEY);
+       if (!ppage_id || !part_id) {
+               g_warning ("%s: page_id/part_id not set on %p", G_STRFUNC, element);
+               return;
+       }
+
+       g_dbus_connection_emit_signal (
+               connection,
+               NULL,
+               MODULE_ITIP_FORMATTER_WEB_EXTENSION_OBJECT_PATH,
+               MODULE_ITIP_FORMATTER_WEB_EXTENSION_INTERFACE,
+               "SourceChanged",
+               g_variant_new ("(ts)", *ppage_id, part_id),
+               &error);
+
+       if (error) {
+               g_warning ("Error emitting signal SourceChanged: %s\n", error->message);
+               g_error_free (error);
+       }
+}
+
+static void
+rsvp_toggled_cb (WebKitDOMHTMLInputElement *input,
+                 WebKitDOMEvent *event,
+                 GDBusConnection *connection)
+{
+       WebKitDOMElement *el;
+       WebKitDOMDocument *document;
+       gboolean rsvp;
+
+       document = webkit_dom_node_get_owner_document (WEBKIT_DOM_NODE (input));
+       rsvp = webkit_dom_html_input_element_get_checked (input);
+       el = webkit_dom_document_get_element_by_id (
+               document, TEXTAREA_RSVP_COMMENT);
+       webkit_dom_html_text_area_element_set_disabled (
+               WEBKIT_DOM_HTML_TEXT_AREA_ELEMENT (el), !rsvp);
+}
+
+/**
+  alarm_check_toggled_cb
+  check1 was changed, so make the second available based on state of the first check.
+*/
+static void
+alarm_check_toggled_cb (WebKitDOMHTMLInputElement *check1,
+                        WebKitDOMEvent *event,
+                        GDBusConnection *connection)
+{
+       WebKitDOMDocument *document;
+       WebKitDOMElement *check2;
+       gchar *id;
+
+       document = webkit_dom_node_get_owner_document (WEBKIT_DOM_NODE (check1));
+#if WEBKIT_CHECK_VERSION(2,2,0) /* XXX should really be (2,1,something) */
+       id = webkit_dom_element_get_id (WEBKIT_DOM_ELEMENT (check1));
+#else
+       id = webkit_dom_html_element_get_id (WEBKIT_DOM_HTML_ELEMENT (check1));
+#endif
+
+       if (g_strcmp0 (id, CHECKBOX_INHERIT_ALARM)) {
+               check2 = webkit_dom_document_get_element_by_id (
+                       document, CHECKBOX_KEEP_ALARM);
+       } else {
+               check2 = webkit_dom_document_get_element_by_id (
+                       document, CHECKBOX_INHERIT_ALARM);
+       }
+
+       g_free (id);
+
+       webkit_dom_html_input_element_set_disabled (
+               WEBKIT_DOM_HTML_INPUT_ELEMENT (check2),
+               (webkit_dom_html_element_get_hidden (
+                               WEBKIT_DOM_HTML_ELEMENT (check1)) &&
+                       webkit_dom_html_input_element_get_checked (check1)));
+}
+
+void
+module_itip_formatter_dom_utils_create_dom_bindings (WebKitDOMDocument *document,
+                                                    guint64 page_id,
+                                                    const gchar *part_id,
+                                                     GDBusConnection *connection)
+{
+       WebKitDOMElement *el;
+
+       g_return_if_fail (part_id && *part_id);
+
+       el = webkit_dom_document_get_element_by_id (document, CHECKBOX_RECUR);
+       if (el) {
+               guint64 *ppage_id;
+
+               ppage_id = g_new0 (guint64, 1);
+               *ppage_id = page_id;
+
+               g_object_set_data_full (G_OBJECT (el), ITIP_WEB_EXTENSION_PAGE_ID_KEY, ppage_id, g_free);
+               g_object_set_data_full (G_OBJECT (el), ITIP_WEB_EXTENSION_PART_ID_KEY, g_strdup (part_id), 
g_free);
+
+               webkit_dom_event_target_add_event_listener (
+                       WEBKIT_DOM_EVENT_TARGET (el), "click",
+                       G_CALLBACK (recur_toggled_cb), FALSE, connection);
+       }
+
+       el = webkit_dom_document_get_element_by_id (document, SELECT_ESOURCE);
+       if (el) {
+               guint64 *ppage_id;
+
+               ppage_id = g_new0 (guint64, 1);
+               *ppage_id = page_id;
+
+               g_object_set_data_full (G_OBJECT (el), ITIP_WEB_EXTENSION_PAGE_ID_KEY, ppage_id, g_free);
+               g_object_set_data_full (G_OBJECT (el), ITIP_WEB_EXTENSION_PART_ID_KEY, g_strdup (part_id), 
g_free);
+
+               webkit_dom_event_target_add_event_listener (
+                       WEBKIT_DOM_EVENT_TARGET (el), "change",
+                       G_CALLBACK (source_changed_cb), FALSE, connection);
+       }
+
+       el = webkit_dom_document_get_element_by_id (document, CHECKBOX_RSVP);
+       if (el) {
+               webkit_dom_event_target_add_event_listener (
+                       WEBKIT_DOM_EVENT_TARGET (el), "click",
+                       G_CALLBACK (rsvp_toggled_cb), FALSE, connection);
+       }
+
+       el = webkit_dom_document_get_element_by_id (document, CHECKBOX_INHERIT_ALARM);
+       if (el) {
+               webkit_dom_event_target_add_event_listener (
+                       WEBKIT_DOM_EVENT_TARGET (el), "click",
+                       G_CALLBACK (alarm_check_toggled_cb), FALSE, connection);
+       }
+
+       el = webkit_dom_document_get_element_by_id (document, CHECKBOX_KEEP_ALARM);
+       if (el) {
+               webkit_dom_event_target_add_event_listener (
+                       WEBKIT_DOM_EVENT_TARGET (el), "click",
+                       G_CALLBACK (alarm_check_toggled_cb), FALSE, connection);
+       }
+}
+
+void
+module_itip_formatter_dom_utils_show_button (WebKitDOMDocument *document,
+                                             const gchar *button_id)
+{
+       WebKitDOMElement *button;
+
+       button = webkit_dom_document_get_element_by_id (document, button_id);
+       webkit_dom_html_element_set_hidden (WEBKIT_DOM_HTML_ELEMENT (button), FALSE);
+}
+
+void
+module_itip_formatter_dom_utils_enable_button (WebKitDOMDocument *document,
+                                               const gchar *button_id,
+                                               gboolean enable)
+{
+       WebKitDOMElement *el;
+
+       el = webkit_dom_document_get_element_by_id (document, button_id);
+       webkit_dom_html_button_element_set_disabled (
+               WEBKIT_DOM_HTML_BUTTON_ELEMENT (el), !enable);
+}
+
+gboolean
+module_itip_formatter_dom_utils_input_is_checked (WebKitDOMDocument *document,
+                                                  const gchar *input_id)
+{
+       WebKitDOMElement *element;
+
+       element = webkit_dom_document_get_element_by_id (document, input_id);
+
+       if (!element)
+               return FALSE;
+
+       return webkit_dom_html_input_element_get_checked (
+               WEBKIT_DOM_HTML_INPUT_ELEMENT (element));
+}
+
+void
+module_itip_formatter_dom_utils_input_set_checked (WebKitDOMDocument *document,
+                                                   const gchar *input_id,
+                                                   gboolean checked)
+{
+       WebKitDOMElement *element;
+
+       element = webkit_dom_document_get_element_by_id (document, input_id);
+
+       if (!element)
+               return;
+
+       webkit_dom_html_input_element_set_checked (
+               WEBKIT_DOM_HTML_INPUT_ELEMENT (element), checked);
+}
+
+void
+module_itip_formatter_dom_utils_show_checkbox (WebKitDOMDocument *document,
+                                               const gchar *id,
+                                               gboolean show,
+                                              gboolean update_second)
+{
+       WebKitDOMElement *label;
+       WebKitDOMElement *el;
+       gchar *row_id;
+
+       el = webkit_dom_document_get_element_by_id (document, id);
+       webkit_dom_html_element_set_hidden (WEBKIT_DOM_HTML_ELEMENT (el), !show);
+
+       label = webkit_dom_element_get_next_element_sibling (el);
+       webkit_dom_html_element_set_hidden (WEBKIT_DOM_HTML_ELEMENT (label), !show);
+
+       if (!show) {
+               webkit_dom_html_input_element_set_checked (
+                       WEBKIT_DOM_HTML_INPUT_ELEMENT (el), FALSE);
+       }
+
+       if (update_second) {
+               /* and update state of the second check */
+               alarm_check_toggled_cb (
+                       WEBKIT_DOM_HTML_INPUT_ELEMENT (el),
+                       NULL, NULL);
+       }
+
+       row_id = g_strconcat ("table_row_", id, NULL);
+       el = webkit_dom_document_get_element_by_id (document, row_id);
+       webkit_dom_html_element_set_hidden (WEBKIT_DOM_HTML_ELEMENT (el), !show);
+       g_free (row_id);
+}
+
+void
+module_itip_formatter_dom_utils_set_buttons_sensitive (WebKitDOMDocument *document,
+                                                       gboolean sensitive)
+{
+       WebKitDOMElement *el, *cell;
+
+       el = webkit_dom_document_get_element_by_id (
+               document, CHECKBOX_UPDATE);
+       webkit_dom_html_input_element_set_disabled (
+               WEBKIT_DOM_HTML_INPUT_ELEMENT (el), !sensitive);
+
+       el = webkit_dom_document_get_element_by_id (
+               document, CHECKBOX_RECUR);
+       webkit_dom_html_input_element_set_disabled (
+               WEBKIT_DOM_HTML_INPUT_ELEMENT (el), !sensitive);
+
+       el = webkit_dom_document_get_element_by_id (
+               document, CHECKBOX_FREE_TIME);
+       webkit_dom_html_input_element_set_disabled (
+               WEBKIT_DOM_HTML_INPUT_ELEMENT (el), !sensitive);
+
+       el = webkit_dom_document_get_element_by_id (
+               document, CHECKBOX_KEEP_ALARM);
+       webkit_dom_html_input_element_set_disabled (
+               WEBKIT_DOM_HTML_INPUT_ELEMENT (el), !sensitive);
+
+       el = webkit_dom_document_get_element_by_id (
+               document, CHECKBOX_INHERIT_ALARM);
+       webkit_dom_html_input_element_set_disabled (
+               WEBKIT_DOM_HTML_INPUT_ELEMENT (el), !sensitive);
+
+       el = webkit_dom_document_get_element_by_id (
+               document, CHECKBOX_RSVP);
+       webkit_dom_html_input_element_set_disabled (
+               WEBKIT_DOM_HTML_INPUT_ELEMENT (el), !sensitive);
+
+       el = webkit_dom_document_get_element_by_id (
+               document, TEXTAREA_RSVP_COMMENT);
+       webkit_dom_html_text_area_element_set_disabled (
+               WEBKIT_DOM_HTML_TEXT_AREA_ELEMENT (el), !sensitive);
+
+       el = webkit_dom_document_get_element_by_id (
+               document, TABLE_ROW_BUTTONS);
+       cell = webkit_dom_element_get_first_element_child (el);
+       do {
+               WebKitDOMElement *btn;
+               btn = webkit_dom_element_get_first_element_child (cell);
+               if (!webkit_dom_html_element_get_hidden (
+                       WEBKIT_DOM_HTML_ELEMENT (btn))) {
+                       webkit_dom_html_button_element_set_disabled (
+                               WEBKIT_DOM_HTML_BUTTON_ELEMENT (btn), !sensitive);
+               }
+       } while ((cell = webkit_dom_element_get_next_element_sibling (cell)) != NULL);
+}
+
+void
+module_itip_formatter_dom_utils_set_area_text (WebKitDOMDocument *document,
+                                               const gchar *area_id,
+                                               const gchar *text)
+{
+       WebKitDOMElement *row, *col;
+
+       row = webkit_dom_document_get_element_by_id (document, area_id);
+       webkit_dom_html_element_set_hidden (
+               WEBKIT_DOM_HTML_ELEMENT (row), (g_strcmp0 (text, "") == 0));
+
+       col = webkit_dom_element_get_last_element_child (row);
+       webkit_dom_element_set_inner_html (col, text, NULL);
+}
+
+void
+module_itip_formatter_dom_utils_element_set_access_key (WebKitDOMDocument *document,
+                                                        const gchar *element_id,
+                                                        const gchar *access_key)
+{
+       WebKitDOMElement *element;
+
+       element = webkit_dom_document_get_element_by_id (document, element_id);
+
+       if (!element)
+               return;
+
+       webkit_dom_html_element_set_access_key (
+               WEBKIT_DOM_HTML_ELEMENT (element), access_key);
+}
+
+void
+module_itip_formatter_dom_utils_element_hide_child_nodes (WebKitDOMDocument *document,
+                                                          const gchar *element_id)
+{
+       WebKitDOMElement *element, *cell, *button;
+
+       element = webkit_dom_document_get_element_by_id (document, element_id);
+
+       if (!element)
+               return;
+
+       element = webkit_dom_document_get_element_by_id (document, element_id);
+       cell = webkit_dom_element_get_first_element_child (element);
+       do {
+               button = webkit_dom_element_get_first_element_child (cell);
+               webkit_dom_html_element_set_hidden (
+                       WEBKIT_DOM_HTML_ELEMENT (button), TRUE);
+       } while ((cell = webkit_dom_element_get_next_element_sibling (cell)) != NULL);
+}
+
+void
+module_itip_formatter_dom_utils_enable_select (WebKitDOMDocument *document,
+                                               const gchar *select_id,
+                                               gboolean enabled)
+{
+       WebKitDOMElement *element;
+
+       element = webkit_dom_document_get_element_by_id (document, select_id);
+
+       if (!element)
+               return;
+
+       webkit_dom_html_select_element_set_disabled (
+               WEBKIT_DOM_HTML_SELECT_ELEMENT (element), !enabled);
+}
+
+gboolean
+module_itip_formatter_dom_utils_select_is_enabled (WebKitDOMDocument *document,
+                                                   const gchar *select_id)
+{
+       WebKitDOMElement *element;
+
+       element = webkit_dom_document_get_element_by_id (document, select_id);
+
+       if (!element)
+               return FALSE;
+
+       return !webkit_dom_html_select_element_get_disabled (
+               WEBKIT_DOM_HTML_SELECT_ELEMENT (element));
+}
+
+gchar *
+module_itip_formatter_dom_utils_select_get_value (WebKitDOMDocument *document,
+                                                  const gchar *select_id)
+{
+       WebKitDOMElement *element;
+
+       element = webkit_dom_document_get_element_by_id (document, select_id);
+
+       if (!element)
+               return NULL;
+
+       return webkit_dom_html_select_element_get_value (
+               WEBKIT_DOM_HTML_SELECT_ELEMENT (element));
+}
+
+void
+module_itip_formatter_dom_utils_select_set_selected (WebKitDOMDocument *document,
+                                                     const gchar *select_id,
+                                                     const gchar *option)
+{
+       WebKitDOMElement *element;
+       gint length, ii;
+
+       element = webkit_dom_document_get_element_by_id (document, select_id);
+
+       if (!element)
+               return;
+
+       length = webkit_dom_html_select_element_get_length (
+               WEBKIT_DOM_HTML_SELECT_ELEMENT (element));
+       for (ii = 0; ii < length; ii++) {
+               WebKitDOMNode *node;
+               WebKitDOMHTMLOptionElement *option_element;
+               gchar *value;
+
+               node = webkit_dom_html_select_element_item (
+                       WEBKIT_DOM_HTML_SELECT_ELEMENT (element), ii);
+               option_element = WEBKIT_DOM_HTML_OPTION_ELEMENT (node);
+
+               value = webkit_dom_html_option_element_get_value (option_element);
+               if (g_strcmp0 (value, option) == 0) {
+                       webkit_dom_html_option_element_set_selected (
+                               option_element, TRUE);
+
+                       g_free (value);
+                       break;
+               }
+
+               g_free (value);
+       }
+}
+
+void
+module_itip_formatter_dom_utils_update_times (WebKitDOMDocument *document,
+                                              const gchar *element_id,
+                                              const gchar *header,
+                                              const gchar *label)
+{
+       WebKitDOMElement *element, *col;
+
+       element = webkit_dom_document_get_element_by_id (document, element_id);
+
+       if (!element)
+               return;
+
+       webkit_dom_html_element_set_hidden (
+               WEBKIT_DOM_HTML_ELEMENT (element), FALSE);
+
+       col = webkit_dom_element_get_first_element_child (element);
+       webkit_dom_element_set_inner_html (col, header, NULL);
+
+       col = webkit_dom_element_get_last_element_child (element);
+       webkit_dom_element_set_inner_html (col, label, NULL);
+}
+
+void
+module_itip_formatter_dom_utils_append_info_item_row (WebKitDOMDocument *document,
+                                                      const gchar *table_id,
+                                                      const gchar *row_id,
+                                                      const gchar *icon_name,
+                                                      const gchar *message)
+{
+       WebKitDOMElement *table;
+        WebKitDOMHTMLElement *cell, *row;
+
+       table = webkit_dom_document_get_element_by_id (document, table_id);
+
+       if (!table)
+               return;
+
+       table = webkit_dom_document_get_element_by_id (document, table_id);
+       row = webkit_dom_html_table_element_insert_row (
+               WEBKIT_DOM_HTML_TABLE_ELEMENT (table), -1, NULL);
+
+       webkit_dom_element_set_id (WEBKIT_DOM_ELEMENT (row), row_id);
+
+       cell = webkit_dom_html_table_row_element_insert_cell (
+               WEBKIT_DOM_HTML_TABLE_ROW_ELEMENT (row), -1, NULL);
+
+       if (icon_name) {
+               WebKitDOMElement *image;
+               gchar *icon_uri;
+
+               image = webkit_dom_document_create_element (
+                       document, "IMG", NULL);
+
+               icon_uri = g_strdup_printf ("gtk-stock://%s", icon_name);
+               webkit_dom_html_image_element_set_src (
+                       WEBKIT_DOM_HTML_IMAGE_ELEMENT (image), icon_uri);
+               g_free (icon_uri);
+
+               webkit_dom_node_append_child (
+                       WEBKIT_DOM_NODE (cell),
+                       WEBKIT_DOM_NODE (image),
+                       NULL);
+       }
+
+       cell = webkit_dom_html_table_row_element_insert_cell (
+               WEBKIT_DOM_HTML_TABLE_ROW_ELEMENT (row), -1, NULL);
+
+       webkit_dom_element_set_inner_html (WEBKIT_DOM_ELEMENT (cell), message, NULL);
+}
+
+void
+module_itip_formatter_dom_utils_enable_text_area (WebKitDOMDocument *document,
+                                                  const gchar *area_id,
+                                                  gboolean enable)
+{
+       WebKitDOMElement *el;
+
+       el = webkit_dom_document_get_element_by_id (document, area_id);
+       webkit_dom_html_text_area_element_set_disabled (
+               WEBKIT_DOM_HTML_TEXT_AREA_ELEMENT (el), !enable);
+}
+
+void
+module_itip_formatter_dom_utils_text_area_set_value (WebKitDOMDocument *document,
+                                                     const gchar *area_id,
+                                                     const gchar *value)
+{
+       WebKitDOMElement *el;
+
+       el = webkit_dom_document_get_element_by_id (document, area_id);
+       webkit_dom_html_text_area_element_set_value (
+               WEBKIT_DOM_HTML_TEXT_AREA_ELEMENT (el), value);
+}
+
+gchar *
+module_itip_formatter_dom_utils_text_area_get_value (WebKitDOMDocument *document,
+                                                     const gchar *area_id)
+{
+       WebKitDOMElement *el;
+
+       el = webkit_dom_document_get_element_by_id (document, area_id);
+       return webkit_dom_html_text_area_element_get_value (
+               WEBKIT_DOM_HTML_TEXT_AREA_ELEMENT (el));
+}
+
+void
+module_itip_formatter_dom_utils_rebuild_source_list (WebKitDOMDocument *document,
+                                                     const gchar *optgroup_id,
+                                                     const gchar *optgroup_label,
+                                                     const gchar *option_id,
+                                                     const gchar *option_label,
+                                                    gboolean writable)
+{
+       WebKitDOMElement *option;
+       WebKitDOMElement *select;
+       WebKitDOMHTMLOptGroupElement *optgroup;
+
+       select = webkit_dom_document_get_element_by_id (document, SELECT_ESOURCE);
+
+       if (!select)
+               return;
+
+       optgroup = WEBKIT_DOM_HTML_OPT_GROUP_ELEMENT (
+                       webkit_dom_document_get_element_by_id (
+                               document, optgroup_id));
+
+       if (!optgroup) {
+               optgroup = WEBKIT_DOM_HTML_OPT_GROUP_ELEMENT (
+                               webkit_dom_document_create_element (
+                                       document, "OPTGROUP", NULL));
+               webkit_dom_html_opt_group_element_set_label (
+                       optgroup, optgroup_label);
+               webkit_dom_node_append_child (
+                       WEBKIT_DOM_NODE (select), WEBKIT_DOM_NODE (optgroup), NULL);
+       }
+
+       option = webkit_dom_document_create_element (document, "OPTION", NULL);
+       webkit_dom_html_option_element_set_value (
+               WEBKIT_DOM_HTML_OPTION_ELEMENT (option), option_id);
+       webkit_dom_html_option_element_set_label (
+               WEBKIT_DOM_HTML_OPTION_ELEMENT (option), option_label);
+       webkit_dom_element_set_inner_html (option, option_label, NULL);
+
+       webkit_dom_element_set_class_name (
+               WEBKIT_DOM_ELEMENT (option), "calendar");
+
+       if (!writable) {
+               webkit_dom_html_option_element_set_disabled (
+                       WEBKIT_DOM_HTML_OPTION_ELEMENT (option), TRUE);
+       }
+
+       webkit_dom_node_append_child (
+               WEBKIT_DOM_NODE (optgroup),
+               WEBKIT_DOM_NODE (option),
+               NULL);
+}
diff --git a/modules/itip-formatter/web-extension/module-itip-formatter-dom-utils.h 
b/modules/itip-formatter/web-extension/module-itip-formatter-dom-utils.h
new file mode 100644
index 0000000..8fab80f
--- /dev/null
+++ b/modules/itip-formatter/web-extension/module-itip-formatter-dom-utils.h
@@ -0,0 +1,111 @@
+/*
+ * module-itip-formatter-dom-utils.h
+ *
+ * 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; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+#ifndef MODULE_ITIP_FORMATTER_DOM_UTILS_H
+#define MODULE_ITIP_FORMATTER_DOM_UTILS_H
+
+#include <webkitdom/webkitdom.h>
+
+#include <gtk/gtk.h>
+
+G_BEGIN_DECLS
+
+void           module_itip_formatter_dom_utils_create_dom_bindings
+                                               (WebKitDOMDocument *document,
+                                                guint64 page_id,
+                                                const gchar *part_id,
+                                                GDBusConnection *connection);
+void           module_itip_formatter_dom_utils_show_button
+                                               (WebKitDOMDocument *document,
+                                                const gchar *button_id);
+void           module_itip_formatter_dom_utils_enable_button
+                                               (WebKitDOMDocument *document,
+                                                const gchar *button_id,
+                                                gboolean enable);
+void           module_itip_formatter_dom_utils_input_set_checked
+                                               (WebKitDOMDocument *document,
+                                                const gchar *input_id,
+                                                gboolean checked);
+gboolean       module_itip_formatter_dom_utils_input_is_checked
+                                               (WebKitDOMDocument *document,
+                                                const gchar *input_id);
+void           module_itip_formatter_dom_utils_show_checkbox
+                                               (WebKitDOMDocument *document,
+                                                const gchar *id,
+                                                gboolean show,
+                                                gboolean update_second);
+void           module_itip_formatter_dom_utils_set_buttons_sensitive
+                                               (WebKitDOMDocument *document,
+                                                gboolean sensitive);
+void           module_itip_formatter_dom_utils_set_area_text
+                                               (WebKitDOMDocument *document,
+                                                const gchar *area_id,
+                                                const gchar *text);
+void           module_itip_formatter_dom_utils_element_set_access_key
+                                               (WebKitDOMDocument *document,
+                                                const gchar *element_id,
+                                                const gchar *access_key);
+void           module_itip_formatter_dom_utils_element_hide_child_nodes
+                                               (WebKitDOMDocument *document,
+                                                const gchar *element_id);
+void           module_itip_formatter_dom_utils_enable_select
+                                               (WebKitDOMDocument *document,
+                                                const gchar *select_id,
+                                                gboolean enabled);
+gboolean       module_itip_formatter_dom_utils_select_is_enabled
+                                               (WebKitDOMDocument *document,
+                                                const gchar *select_id);
+gchar *                module_itip_formatter_dom_utils_select_get_value
+                                               (WebKitDOMDocument *document,
+                                                const gchar *select_id);
+void           module_itip_formatter_dom_utils_select_set_selected
+                                               (WebKitDOMDocument *document,
+                                                const gchar *select_id,
+                                                const gchar *option);
+void           module_itip_formatter_dom_utils_update_times
+                                               (WebKitDOMDocument *document,
+                                                const gchar *element_id,
+                                                const gchar *header,
+                                                const gchar *label);
+void           module_itip_formatter_dom_utils_append_info_item_row
+                                               (WebKitDOMDocument *document,
+                                                const gchar *table_id,
+                                                const gchar *row_id,
+                                                const gchar *icon_name,
+                                                const gchar *message);
+void           module_itip_formatter_dom_utils_enable_text_area
+                                               (WebKitDOMDocument *document,
+                                                const gchar *area_id,
+                                                gboolean enable);
+void           module_itip_formatter_dom_utils_text_area_set_value
+                                               (WebKitDOMDocument *document,
+                                                const gchar *area_id,
+                                                const gchar *value);
+gchar *                module_itip_formatter_dom_utils_text_area_get_value
+                                               (WebKitDOMDocument *document,
+                                                const gchar *area_id);
+void           module_itip_formatter_dom_utils_rebuild_source_list
+                                               (WebKitDOMDocument *document,
+                                                const gchar *optgroup_id,
+                                                const gchar *optgroup_label,
+                                                const gchar *option_id,
+                                                const gchar *option_label,
+                                                gboolean writable);
+G_END_DECLS
+
+#endif /* MODULE_ITIP_FORMATTER_DOM_UTILS_H */
diff --git a/modules/itip-formatter/web-extension/module-itip-formatter-web-extension.c 
b/modules/itip-formatter/web-extension/module-itip-formatter-web-extension.c
new file mode 100644
index 0000000..b83e590
--- /dev/null
+++ b/modules/itip-formatter/web-extension/module-itip-formatter-web-extension.c
@@ -0,0 +1,646 @@
+/*
+ * module-itip-formatter-web-extension.c
+ *
+ * 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; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+#include "module-itip-formatter-web-extension.h"
+
+#include <gio/gio.h>
+#include <gtk/gtk.h>
+#include <webkit2/webkit-web-extension.h>
+
+#include <web-extensions/e-dom-utils.h>
+
+#include "module-itip-formatter-dom-utils.h"
+
+/* FIXME Clean it */
+static GDBusConnection *dbus_connection;
+
+static const char introspection_xml[] =
+"<node>"
+"  <interface name='" MODULE_ITIP_FORMATTER_WEB_EXTENSION_INTERFACE "'>"
+"    <signal name='RecurToggled'>"
+"      <arg type='t' name='page_id' direction='out'/>"
+"      <arg type='s' name='part_id' direction='out'/>"
+"    </signal>"
+"    <signal name='SourceChanged'>"
+"      <arg type='t' name='page_id' direction='out'/>"
+"      <arg type='s' name='part_id' direction='out'/>"
+"    </signal>"
+"    <method name='CreateDOMBindings'>"
+"      <arg type='t' name='page_id' direction='in'/>"
+"      <arg type='s' name='part_id' direction='in'/>"
+"    </method>"
+"    <method name='ShowButton'>"
+"      <arg type='t' name='page_id' direction='in'/>"
+"      <arg type='s' name='part_id' direction='in'/>"
+"      <arg type='s' name='button_id' direction='in'/>"
+"    </method>"
+"    <method name='ElementSetInnerHTML'>"
+"      <arg type='t' name='page_id' direction='in'/>"
+"      <arg type='s' name='part_id' direction='in'/>"
+"      <arg type='s' name='element_id' direction='in'/>"
+"      <arg type='s' name='inner_html' direction='in'/>"
+"    </method>"
+"    <method name='RemoveElement'>"
+"      <arg type='t' name='page_id' direction='in'/>"
+"      <arg type='s' name='part_id' direction='in'/>"
+"      <arg type='s' name='element_id' direction='in'/>"
+"    </method>"
+"    <method name='ElementRemoveChildNodes'>"
+"      <arg type='t' name='page_id' direction='in'/>"
+"      <arg type='s' name='part_id' direction='in'/>"
+"      <arg type='s' name='element_id' direction='in'/>"
+"    </method>"
+"    <method name='EnableButton'>"
+"      <arg type='t' name='page_id' direction='in'/>"
+"      <arg type='s' name='part_id' direction='in'/>"
+"      <arg type='s' name='button_id' direction='in'/>"
+"      <arg type='b' name='enable' direction='in'/>"
+"    </method>"
+"    <method name='ElementIsHidden'>"
+"      <arg type='t' name='page_id' direction='in'/>"
+"      <arg type='s' name='part_id' direction='in'/>"
+"      <arg type='s' name='element_id' direction='in'/>"
+"      <arg type='b' name='is_hidden' direction='out'/>"
+"    </method>"
+"    <method name='HideElement'>"
+"      <arg type='t' name='page_id' direction='in'/>"
+"      <arg type='s' name='part_id' direction='in'/>"
+"      <arg type='s' name='element_id' direction='in'/>"
+"      <arg type='b' name='hide' direction='in'/>"
+"    </method>"
+"    <method name='InputSetChecked'>"
+"      <arg type='t' name='page_id' direction='in'/>"
+"      <arg type='s' name='part_id' direction='in'/>"
+"      <arg type='s' name='input_id' direction='in'/>"
+"      <arg type='b' name='checked' direction='in'/>"
+"    </method>"
+"    <method name='InputIsChecked'>"
+"      <arg type='t' name='page_id' direction='in'/>"
+"      <arg type='s' name='part_id' direction='in'/>"
+"      <arg type='s' name='input_id' direction='in'/>"
+"      <arg type='b' name='checked' direction='out'/>"
+"    </method>"
+"    <method name='ShowCheckbox'>"
+"      <arg type='t' name='page_id' direction='in'/>"
+"      <arg type='s' name='part_id' direction='in'/>"
+"      <arg type='s' name='id' direction='in'/>"
+"      <arg type='b' name='show' direction='in'/>"
+"      <arg type='b' name='update_second' direction='in'/>"
+"    </method>"
+"    <method name='SetButtonsSensitive'>"
+"      <arg type='t' name='page_id' direction='in'/>"
+"      <arg type='s' name='part_id' direction='in'/>"
+"      <arg type='b' name='sensitive' direction='in'/>"
+"    </method>"
+"    <method name='SetAreaText'>"
+"      <arg type='t' name='page_id' direction='in'/>"
+"      <arg type='s' name='part_id' direction='in'/>"
+"      <arg type='s' name='id' direction='in'/>"
+"      <arg type='s' name='text' direction='in'/>"
+"    </method>"
+"    <method name='ElementSetAccessKey'>"
+"      <arg type='t' name='page_id' direction='in'/>"
+"      <arg type='s' name='part_id' direction='in'/>"
+"      <arg type='s' name='element_id' direction='in'/>"
+"      <arg type='s' name='access_key' direction='in'/>"
+"    </method>"
+"    <method name='ElementHideChildNodes'>"
+"      <arg type='t' name='page_id' direction='in'/>"
+"      <arg type='s' name='part_id' direction='in'/>"
+"      <arg type='s' name='element_id' direction='in'/>"
+"    </method>"
+"    <method name='EnableSelect'>"
+"      <arg type='t' name='page_id' direction='in'/>"
+"      <arg type='s' name='part_id' direction='in'/>"
+"      <arg type='s' name='select_id' direction='in'/>"
+"      <arg type='b' name='enable' direction='in'/>"
+"    </method>"
+"    <method name='SelectIsEnabled'>"
+"      <arg type='t' name='page_id' direction='in'/>"
+"      <arg type='s' name='part_id' direction='in'/>"
+"      <arg type='s' name='select_id' direction='in'/>"
+"      <arg type='b' name='enable' direction='out'/>"
+"    </method>"
+"    <method name='SelectGetValue'>"
+"      <arg type='t' name='page_id' direction='in'/>"
+"      <arg type='s' name='part_id' direction='in'/>"
+"      <arg type='s' name='select_id' direction='in'/>"
+"      <arg type='s' name='value' direction='out'/>"
+"    </method>"
+"    <method name='SelectSetSelected'>"
+"      <arg type='t' name='page_id' direction='in'/>"
+"      <arg type='s' name='part_id' direction='in'/>"
+"      <arg type='s' name='select_id' direction='in'/>"
+"      <arg type='s' name='option' direction='in'/>"
+"    </method>"
+"    <method name='UpdateTimes'>"
+"      <arg type='t' name='page_id' direction='in'/>"
+"      <arg type='s' name='part_id' direction='in'/>"
+"      <arg type='s' name='element_id' direction='in'/>"
+"      <arg type='s' name='header' direction='in'/>"
+"      <arg type='s' name='label' direction='in'/>"
+"    </method>"
+"    <method name='AppendInfoItemRow'>"
+"      <arg type='t' name='page_id' direction='in'/>"
+"      <arg type='s' name='part_id' direction='in'/>"
+"      <arg type='s' name='table_id' direction='in'/>"
+"      <arg type='s' name='row_id' direction='in'/>"
+"      <arg type='s' name='icon_name' direction='in'/>"
+"      <arg type='s' name='message' direction='in'/>"
+"    </method>"
+"    <method name='EnableTextArea'>"
+"      <arg type='t' name='page_id' direction='in'/>"
+"      <arg type='s' name='part_id' direction='in'/>"
+"      <arg type='s' name='area_id' direction='in'/>"
+"      <arg type='b' name='enable' direction='in'/>"
+"    </method>"
+"    <method name='TextAreaSetValue'>"
+"      <arg type='t' name='page_id' direction='in'/>"
+"      <arg type='s' name='part_id' direction='in'/>"
+"      <arg type='s' name='area_id' direction='in'/>"
+"      <arg type='s' name='value' direction='in'/>"
+"    </method>"
+"    <method name='TextAreaGetValue'>"
+"      <arg type='t' name='page_id' direction='in'/>"
+"      <arg type='s' name='part_id' direction='in'/>"
+"      <arg type='s' name='area_id' direction='in'/>"
+"      <arg type='s' name='value' direction='out'/>"
+"    </method>"
+"    <method name='RebuildSourceList'>"
+"      <arg type='t' name='page_id' direction='in'/>"
+"      <arg type='s' name='part_id' direction='in'/>"
+"      <arg type='s' name='optgroup_id' direction='in'/>"
+"      <arg type='s' name='optgroup_label' direction='in'/>"
+"      <arg type='s' name='option_id' direction='in'/>"
+"      <arg type='s' name='option_label' direction='in'/>"
+"      <arg type='b' name='writable' direction='in'/>"
+"    </method>"
+"  </interface>"
+"</node>";
+
+static WebKitDOMDocument *
+get_webkit_document_or_return_dbus_error (GDBusMethodInvocation *invocation,
+                                          WebKitWebExtension *web_extension,
+                                          guint64 page_id)
+{
+       WebKitDOMDocument *document;
+       WebKitWebPage *web_page;
+
+       web_page = webkit_web_extension_get_page (web_extension, page_id);
+       if (!web_page) {
+               g_dbus_method_invocation_return_error (
+                       invocation, G_DBUS_ERROR, G_DBUS_ERROR_INVALID_ARGS,
+                       "Invalid page ID: %" G_GUINT64_FORMAT, page_id);
+               return NULL;
+       }
+
+       document = webkit_web_page_get_dom_document (web_page);
+       if (!document) {
+               g_dbus_method_invocation_return_error (
+                       invocation, G_DBUS_ERROR, G_DBUS_ERROR_INVALID_ARGS,
+                       "No document for page ID: %" G_GUINT64_FORMAT, page_id);
+               return NULL;
+       }
+
+       return document;
+}
+
+static WebKitDOMDocument *
+find_webkit_document_for_partid_or_return_dbus_error (GDBusMethodInvocation *invocation,
+                                                     WebKitDOMDocument *owner,
+                                                     const gchar *part_id)
+{
+       WebKitDOMElement *element;
+
+       g_return_val_if_fail (G_IS_DBUS_METHOD_INVOCATION (invocation), NULL);
+       g_return_val_if_fail (WEBKIT_DOM_IS_DOCUMENT (owner), NULL);
+       g_return_val_if_fail (part_id && *part_id, NULL);
+
+       element = e_dom_utils_find_element_by_id (owner, part_id);
+       if (element && WEBKIT_DOM_IS_HTML_IFRAME_ELEMENT (element)) {
+               WebKitDOMDocument *document = webkit_dom_html_iframe_element_get_content_document 
(WEBKIT_DOM_HTML_IFRAME_ELEMENT (element));
+               return document;
+       }
+
+       if (element)
+               g_dbus_method_invocation_return_error (
+                       invocation, G_DBUS_ERROR, G_DBUS_ERROR_INVALID_ARGS,
+                       "Part ID '%s' is not IFRAME, but %s", part_id, G_OBJECT_TYPE_NAME (element));
+       else
+               g_dbus_method_invocation_return_error (
+                       invocation, G_DBUS_ERROR, G_DBUS_ERROR_INVALID_ARGS,
+                       "Part ID '%s' not found", part_id);
+       return NULL;
+}
+
+static void
+handle_method_call (GDBusConnection *connection,
+                    const char *sender,
+                    const char *object_path,
+                    const char *interface_name,
+                    const char *method_name,
+                    GVariant *parameters,
+                    GDBusMethodInvocation *invocation,
+                    gpointer user_data)
+{
+       WebKitWebExtension *web_extension = WEBKIT_WEB_EXTENSION (user_data);
+       WebKitDOMDocument *document;
+       const gchar *part_id = NULL;
+       guint64 page_id;
+
+       if (g_strcmp0 (interface_name, MODULE_ITIP_FORMATTER_WEB_EXTENSION_INTERFACE) != 0)
+               return;
+
+       if (g_strcmp0 (method_name, "CreateDOMBindings") == 0) {
+               g_variant_get (parameters, "(t&s)", &page_id, &part_id);
+
+               document = get_webkit_document_or_return_dbus_error (invocation, web_extension, page_id);
+               if (document)
+                       document = find_webkit_document_for_partid_or_return_dbus_error (invocation, 
document, part_id);
+               if (document) {
+                       module_itip_formatter_dom_utils_create_dom_bindings (document, page_id, part_id, 
connection);
+                       g_dbus_method_invocation_return_value (invocation, NULL);
+               }
+       } else if (g_strcmp0 (method_name, "ShowButton") == 0) {
+               const gchar *button_id;
+
+               g_variant_get (parameters, "(t&s&s)", &page_id, &part_id, &button_id);
+
+               document = get_webkit_document_or_return_dbus_error (invocation, web_extension, page_id);
+               if (document)
+                       document = find_webkit_document_for_partid_or_return_dbus_error (invocation, 
document, part_id);
+               if (document) {
+                       module_itip_formatter_dom_utils_show_button (document, button_id);
+                       g_dbus_method_invocation_return_value (invocation, NULL);
+               }
+       } else if (g_strcmp0 (method_name, "EnableButton") == 0) {
+               const gchar *button_id;
+               gboolean enable;
+
+               g_variant_get (parameters, "(t&s&sb)", &page_id, &part_id, &button_id, &enable);
+
+               document = get_webkit_document_or_return_dbus_error (invocation, web_extension, page_id);
+               if (document)
+                       document = find_webkit_document_for_partid_or_return_dbus_error (invocation, 
document, part_id);
+               if (document) {
+                       module_itip_formatter_dom_utils_enable_button (document, button_id, enable);
+                       g_dbus_method_invocation_return_value (invocation, NULL);
+               }
+       } else if (g_strcmp0 (method_name, "ElementSetInnerHTML") == 0) {
+               const gchar *element_id, *inner_html;
+
+               g_variant_get (parameters, "(t&s&s&s)", &page_id, &part_id, &element_id, &inner_html);
+
+               document = get_webkit_document_or_return_dbus_error (invocation, web_extension, page_id);
+               if (document)
+                       document = find_webkit_document_for_partid_or_return_dbus_error (invocation, 
document, part_id);
+               if (document) {
+                       e_dom_utils_element_set_inner_html (document, element_id, inner_html);
+                       g_dbus_method_invocation_return_value (invocation, NULL);
+               }
+       } else if (g_strcmp0 (method_name, "RemoveElement") == 0) {
+               const gchar *element_id;
+
+               g_variant_get (parameters, "(t&s&s)", &page_id, &part_id, &element_id);
+
+               document = get_webkit_document_or_return_dbus_error (invocation, web_extension, page_id);
+               if (document)
+                       document = find_webkit_document_for_partid_or_return_dbus_error (invocation, 
document, part_id);
+               if (document) {
+                       e_dom_utils_remove_element (document, element_id);
+                       g_dbus_method_invocation_return_value (invocation, NULL);
+               }
+       } else if (g_strcmp0 (method_name, "ElementRemoveChildNodes") == 0) {
+               const gchar *element_id;
+
+               g_variant_get (parameters, "(t&s&s)", &page_id, &part_id, &element_id);
+
+               document = get_webkit_document_or_return_dbus_error (invocation, web_extension, page_id);
+               if (document)
+                       document = find_webkit_document_for_partid_or_return_dbus_error (invocation, 
document, part_id);
+               if (document) {
+                       e_dom_utils_element_remove_child_nodes (document, element_id);
+                       g_dbus_method_invocation_return_value (invocation, NULL);
+               }
+       } else if (g_strcmp0 (method_name, "HideElement") == 0) {
+               const gchar *element_id;
+               gboolean hide;
+
+               g_variant_get (parameters, "(t&s&sb)", &page_id, &part_id, &element_id, &hide);
+
+               document = get_webkit_document_or_return_dbus_error (invocation, web_extension, page_id);
+               if (document)
+                       document = find_webkit_document_for_partid_or_return_dbus_error (invocation, 
document, part_id);
+               if (document) {
+                       e_dom_utils_hide_element (document, element_id, hide);
+                       g_dbus_method_invocation_return_value (invocation, NULL);
+               }
+       } else if (g_strcmp0 (method_name, "ElementIsHidden") == 0) {
+               const gchar *element_id;
+               gboolean hidden;
+
+               g_variant_get (parameters, "(t&s&s)", &page_id, &part_id, &element_id);
+
+               document = get_webkit_document_or_return_dbus_error (invocation, web_extension, page_id);
+               if (document)
+                       document = find_webkit_document_for_partid_or_return_dbus_error (invocation, 
document, part_id);
+               if (document) {
+                       hidden = e_dom_utils_element_is_hidden (document, element_id);
+                       g_dbus_method_invocation_return_value (invocation, g_variant_new ("(b)", hidden));
+               }
+       } else if (g_strcmp0 (method_name, "InputSetChecked") == 0) {
+               const gchar *input_id;
+               gboolean checked;
+
+               g_variant_get (parameters, "(t&s&sb)", &page_id, &part_id, &input_id, &checked);
+
+               document = get_webkit_document_or_return_dbus_error (invocation, web_extension, page_id);
+               if (document)
+                       document = find_webkit_document_for_partid_or_return_dbus_error (invocation, 
document, part_id);
+               if (document) {
+                       module_itip_formatter_dom_utils_input_set_checked (document, input_id, checked);
+                       g_dbus_method_invocation_return_value (invocation, NULL);
+               }
+       } else if (g_strcmp0 (method_name, "InputIsChecked") == 0) {
+               const gchar *input_id;
+               gboolean checked;
+
+               g_variant_get (parameters, "(t&s&s)", &page_id, &part_id, &input_id);
+
+               document = get_webkit_document_or_return_dbus_error (invocation, web_extension, page_id);
+               if (document)
+                       document = find_webkit_document_for_partid_or_return_dbus_error (invocation, 
document, part_id);
+               if (document) {
+                       checked = module_itip_formatter_dom_utils_input_is_checked (document, input_id);
+                       g_dbus_method_invocation_return_value (invocation, g_variant_new ("(b)", checked));
+               }
+       } else if (g_strcmp0 (method_name, "ShowCheckbox") == 0) {
+               const gchar *id;
+               gboolean show, update_second;
+
+               g_variant_get (parameters, "(t&s&sbb)", &page_id, &part_id, &id, &show, &update_second);
+
+               document = get_webkit_document_or_return_dbus_error (invocation, web_extension, page_id);
+               if (document)
+                       document = find_webkit_document_for_partid_or_return_dbus_error (invocation, 
document, part_id);
+               if (document) {
+                       module_itip_formatter_dom_utils_show_checkbox (document, id, show, update_second);
+                       g_dbus_method_invocation_return_value (invocation, NULL);
+               }
+       } else if (g_strcmp0 (method_name, "SetButtonsSensitive") == 0) {
+               gboolean sensitive;
+
+               g_variant_get (parameters, "(t&sb)", &page_id, &part_id, &sensitive);
+
+               document = get_webkit_document_or_return_dbus_error (invocation, web_extension, page_id);
+               if (document)
+                       document = find_webkit_document_for_partid_or_return_dbus_error (invocation, 
document, part_id);
+               if (document) {
+                       module_itip_formatter_dom_utils_set_buttons_sensitive (document, sensitive);
+                       g_dbus_method_invocation_return_value (invocation, NULL);
+               }
+       } else if (g_strcmp0 (method_name, "SetAreaText") == 0) {
+               const gchar *id, *text;
+
+               g_variant_get (parameters, "(t&s&s&s)", &page_id, &part_id, &id, &text);
+
+               document = get_webkit_document_or_return_dbus_error (invocation, web_extension, page_id);
+               if (document)
+                       document = find_webkit_document_for_partid_or_return_dbus_error (invocation, 
document, part_id);
+               if (document) {
+                       module_itip_formatter_dom_utils_set_area_text (document, id, text);
+                       g_dbus_method_invocation_return_value (invocation, NULL);
+               }
+       } else if (g_strcmp0 (method_name, "ElementSetAccessKey") == 0) {
+               const gchar *element_id, *access_key;
+
+               g_variant_get (parameters, "(t&s&s&s)", &page_id, &part_id, &element_id, &access_key);
+
+               document = get_webkit_document_or_return_dbus_error (invocation, web_extension, page_id);
+               if (document)
+                       document = find_webkit_document_for_partid_or_return_dbus_error (invocation, 
document, part_id);
+               if (document) {
+                       module_itip_formatter_dom_utils_element_set_access_key (document, element_id, 
access_key);
+                       g_dbus_method_invocation_return_value (invocation, NULL);
+               }
+       } else if (g_strcmp0 (method_name, "ElementHideChildNodes") == 0) {
+               const gchar *element_id;
+
+               g_variant_get (parameters, "(t&s&s)", &page_id, &part_id, &element_id);
+
+               document = get_webkit_document_or_return_dbus_error (invocation, web_extension, page_id);
+               if (document)
+                       document = find_webkit_document_for_partid_or_return_dbus_error (invocation, 
document, part_id);
+               if (document) {
+                       module_itip_formatter_dom_utils_element_hide_child_nodes (document, element_id);
+                       g_dbus_method_invocation_return_value (invocation, NULL);
+               }
+       } else if (g_strcmp0 (method_name, "EnableSelect") == 0) {
+               const gchar *select_id;
+               gboolean enable;
+
+               g_variant_get (parameters, "(t&s&sb)", &page_id, &part_id, &select_id, &enable);
+
+               document = get_webkit_document_or_return_dbus_error (invocation, web_extension, page_id);
+               if (document)
+                       document = find_webkit_document_for_partid_or_return_dbus_error (invocation, 
document, part_id);
+               if (document) {
+                       module_itip_formatter_dom_utils_enable_select (document, select_id, enable);
+                       g_dbus_method_invocation_return_value (invocation, NULL);
+               }
+       } else if (g_strcmp0 (method_name, "SelectIsEnabled") == 0) {
+               const gchar *select_id;
+               gboolean enabled;
+
+               g_variant_get (parameters, "(t&s&s)", &page_id, &part_id, &select_id);
+
+               document = get_webkit_document_or_return_dbus_error (invocation, web_extension, page_id);
+               if (document)
+                       document = find_webkit_document_for_partid_or_return_dbus_error (invocation, 
document, part_id);
+               if (document) {
+                       enabled = module_itip_formatter_dom_utils_select_is_enabled (document, select_id);
+                       g_dbus_method_invocation_return_value (invocation, g_variant_new ("(b)", enabled));
+               }
+       } else if (g_strcmp0 (method_name, "SelectGetValue") == 0) {
+               const gchar *select_id;
+               gchar *value;
+
+               g_variant_get (parameters, "(t&s&s)", &page_id, &part_id, &select_id);
+
+               document = get_webkit_document_or_return_dbus_error (invocation, web_extension, page_id);
+               if (document)
+                       document = find_webkit_document_for_partid_or_return_dbus_error (invocation, 
document, part_id);
+               if (document) {
+                       value = module_itip_formatter_dom_utils_select_get_value (document, select_id);
+                       g_dbus_method_invocation_return_value (invocation,
+                               g_variant_new (
+                                       "(@s)",
+                                       g_variant_new_take_string (value ? value : g_strdup (""))));
+               }
+       } else if (g_strcmp0 (method_name, "SelectSetSelected") == 0) {
+               const gchar *select_id, *option;
+
+               g_variant_get (parameters, "(t&s&s&s)", &page_id, &part_id, &select_id, &option);
+
+               document = get_webkit_document_or_return_dbus_error (invocation, web_extension, page_id);
+               if (document)
+                       document = find_webkit_document_for_partid_or_return_dbus_error (invocation, 
document, part_id);
+               if (document) {
+                       module_itip_formatter_dom_utils_select_set_selected (document, select_id, option);
+                       g_dbus_method_invocation_return_value (invocation, NULL);
+               }
+       } else if (g_strcmp0 (method_name, "UpdateTimes") == 0) {
+               const gchar *element_id, *header, *label;
+
+               g_variant_get (parameters, "(t&s&s&s&s)", &page_id, &part_id, &element_id, &header, &label);
+
+               document = get_webkit_document_or_return_dbus_error (invocation, web_extension, page_id);
+               if (document)
+                       document = find_webkit_document_for_partid_or_return_dbus_error (invocation, 
document, part_id);
+               if (document) {
+                       module_itip_formatter_dom_utils_update_times (document, element_id, header, label);
+                       g_dbus_method_invocation_return_value (invocation, NULL);
+               }
+       } else if (g_strcmp0 (method_name, "AppendInfoItemRow") == 0) {
+               const gchar *table_id, *row_id, *icon_name, *message;
+
+               g_variant_get (parameters, "(t&s&s&s&s&s)", &page_id, &part_id, &table_id, &row_id, 
&icon_name, &message);
+
+               document = get_webkit_document_or_return_dbus_error (invocation, web_extension, page_id);
+               if (document)
+                       document = find_webkit_document_for_partid_or_return_dbus_error (invocation, 
document, part_id);
+               if (document) {
+                       module_itip_formatter_dom_utils_append_info_item_row (document, table_id, row_id, 
icon_name, message);
+                       g_dbus_method_invocation_return_value (invocation, NULL);
+               }
+       } else if (g_strcmp0 (method_name, "EnableTextArea") == 0) {
+               const gchar *area_id;
+               gboolean enable;
+
+               g_variant_get (parameters, "(t&s&sb)", &page_id, &part_id, &area_id, &enable);
+
+               document = get_webkit_document_or_return_dbus_error (invocation, web_extension, page_id);
+               if (document)
+                       document = find_webkit_document_for_partid_or_return_dbus_error (invocation, 
document, part_id);
+               if (document) {
+                       module_itip_formatter_dom_utils_enable_text_area (document, area_id, enable);
+                       g_dbus_method_invocation_return_value (invocation, NULL);
+               }
+       } else if (g_strcmp0 (method_name, "TextAreaSetValue") == 0) {
+               const gchar *area_id, *value;
+
+               g_variant_get (parameters, "(t&s&s&s)", &page_id, &part_id, &area_id, &value);
+
+               document = get_webkit_document_or_return_dbus_error (invocation, web_extension, page_id);
+               if (document)
+                       document = find_webkit_document_for_partid_or_return_dbus_error (invocation, 
document, part_id);
+               if (document) {
+                       module_itip_formatter_dom_utils_text_area_set_value (document, area_id, value);
+                       g_dbus_method_invocation_return_value (invocation, NULL);
+               }
+       } else if (g_strcmp0 (method_name, "TextAreaGetValue") == 0) {
+               const gchar *area_id;
+               gchar *value;
+
+               g_variant_get (parameters, "(t&s&s)", &page_id, &part_id, &area_id);
+
+               document = get_webkit_document_or_return_dbus_error (invocation, web_extension, page_id);
+               if (document)
+                       document = find_webkit_document_for_partid_or_return_dbus_error (invocation, 
document, part_id);
+               if (document) {
+                       value = module_itip_formatter_dom_utils_text_area_get_value (document, area_id);
+                       g_dbus_method_invocation_return_value (invocation,
+                               g_variant_new (
+                                       "(@s)",
+                                       g_variant_new_take_string (value ? value : g_strdup (""))));
+               }
+       } else if (g_strcmp0 (method_name, "RebuildSourceList") == 0) {
+               const gchar *optgroup_id, *optgroup_label, *option_id, *option_label;
+               gboolean writable;
+
+               g_variant_get (parameters,"(t&s&s&s&s&sb)", &page_id, &part_id, &optgroup_id, 
&optgroup_label, &option_id, &option_label, &writable);
+
+               document = get_webkit_document_or_return_dbus_error (invocation, web_extension, page_id);
+               if (document)
+                       document = find_webkit_document_for_partid_or_return_dbus_error (invocation, 
document, part_id);
+               if (document) {
+                       module_itip_formatter_dom_utils_rebuild_source_list (
+                               document,
+                               optgroup_id,
+                               optgroup_label,
+                               option_id,
+                               option_label,
+                               writable);
+
+                       g_dbus_method_invocation_return_value (invocation, NULL);
+               }
+       }
+}
+
+static const GDBusInterfaceVTable interface_vtable = {
+       handle_method_call,
+       NULL,
+       NULL
+};
+
+static void
+bus_acquired_cb (GDBusConnection *connection,
+                 const char *name,
+                 gpointer user_data)
+{
+       guint registration_id;
+       GError *error = NULL;
+       static GDBusNodeInfo *introspection_data = NULL;
+
+       if (!introspection_data)
+               introspection_data = g_dbus_node_info_new_for_xml (introspection_xml, NULL);
+
+       registration_id =
+               g_dbus_connection_register_object (
+                       connection,
+                       MODULE_ITIP_FORMATTER_WEB_EXTENSION_OBJECT_PATH,
+                       introspection_data->interfaces[0],
+                       &interface_vtable,
+                       g_object_ref (user_data),
+                       g_object_unref,
+                       &error);
+
+       if (!registration_id) {
+               g_warning ("Failed to register object: %s\n", error->message);
+               g_error_free (error);
+       } else {
+               dbus_connection = connection;
+               g_object_add_weak_pointer (G_OBJECT (connection), (gpointer *)&dbus_connection);
+       }
+}
+
+/* Forward declaration */
+G_MODULE_EXPORT void webkit_web_extension_initialize (WebKitWebExtension *extension);
+
+G_MODULE_EXPORT void
+webkit_web_extension_initialize (WebKitWebExtension *extension)
+{
+       g_bus_own_name (
+               G_BUS_TYPE_SESSION,
+               MODULE_ITIP_FORMATTER_WEB_EXTENSION_SERVICE_NAME,
+               G_BUS_NAME_OWNER_FLAGS_NONE,
+               bus_acquired_cb,
+               NULL, NULL,
+               g_object_ref (extension),
+               g_object_unref);
+}
diff --git a/modules/itip-formatter/web-extension/module-itip-formatter-web-extension.h 
b/modules/itip-formatter/web-extension/module-itip-formatter-web-extension.h
new file mode 100644
index 0000000..b9ffae0
--- /dev/null
+++ b/modules/itip-formatter/web-extension/module-itip-formatter-web-extension.h
@@ -0,0 +1,26 @@
+/*
+ * module-itip-formatter-web-extension.h
+ *
+ * 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; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+#ifndef MODULE_ITIP_FORMATTER_WEB_EXTENSION_H
+#define MODULE_ITIP_FORMATTER_WEB_EXTENSION_H
+
+#define MODULE_ITIP_FORMATTER_WEB_EXTENSION_SERVICE_NAME 
"org.gnome.Evolution.Module.ItipFormatter.WebExtension"
+#define MODULE_ITIP_FORMATTER_WEB_EXTENSION_OBJECT_PATH  
"/org/gnome/Evolution/Module/ItipFormatter/WebExtension"
+#define MODULE_ITIP_FORMATTER_WEB_EXTENSION_INTERFACE    
"org.gnome.Evolution.Module.ItipFormatter.WebExtension"
+
+#endif /* MODULE_ITIP_FORMATTER_WEB_EXTENSION_H */
diff --git a/modules/mail/e-mail-attachment-handler.c b/modules/mail/e-mail-attachment-handler.c
index 50d0c74..a5252b8 100644
--- a/modules/mail/e-mail-attachment-handler.c
+++ b/modules/mail/e-mail-attachment-handler.c
@@ -167,6 +167,65 @@ exit:
        return message;
 }
 
+typedef struct _CreateComposerData {
+       CamelMimeMessage *message;
+       CamelFolder *folder;
+       gboolean is_redirect;
+
+       gboolean is_reply;
+       EMailReplyType reply_type;
+
+       gboolean is_forward;
+       EMailForwardStyle forward_style;
+} CreateComposerData;
+
+static void
+create_composer_data_free (CreateComposerData *ccd)
+{
+       if (ccd) {
+               g_clear_object (&ccd->message);
+               g_clear_object (&ccd->folder);
+               g_free (ccd);
+       }
+}
+
+static void
+mail_attachment_handler_composer_created_cb (GObject *source_object,
+                                            GAsyncResult *result,
+                                            gpointer user_data)
+{
+       CreateComposerData *ccd = user_data;
+       EMsgComposer *composer;
+       GError *error = NULL;
+
+       g_return_if_fail (ccd != NULL);
+
+       composer = e_msg_composer_new_finish (result, &error);
+       if (error) {
+               g_warning ("%s: Failed to create msg composer: %s", G_STRFUNC, error->message);
+               g_clear_error (&error);
+       } else {
+               if (ccd->is_redirect) {
+                       em_utils_redirect_message (composer, ccd->message);
+               } else if (ccd->is_reply) {
+                       GSettings *settings;
+                       EMailReplyStyle style;
+
+                       settings = e_util_ref_settings ("org.gnome.evolution.mail");
+                       style = g_settings_get_enum (settings, "reply-style-name");
+                       g_object_unref (settings);
+
+                       em_utils_reply_to_message (composer, ccd->message, NULL, NULL, ccd->reply_type, 
style, NULL, NULL);
+               } else if (ccd->is_forward) {
+                       em_utils_forward_message (composer, ccd->message, ccd->forward_style, ccd->folder, 
NULL);
+               } else {
+                       em_utils_edit_message (composer, ccd->folder, ccd->message, NULL, TRUE);
+               }
+       }
+
+       create_composer_data_free (ccd);
+}
+
 static void
 mail_attachment_handler_forward_with_style (EAttachmentHandler *handler,
                                            EMailForwardStyle style)
@@ -174,6 +233,8 @@ mail_attachment_handler_forward_with_style (EAttachmentHandler *handler,
        EMailAttachmentHandlerPrivate *priv;
        CamelMimeMessage *message;
        CamelFolder *folder;
+       CreateComposerData *ccd;
+       EShell *shell;
 
        priv = E_MAIL_ATTACHMENT_HANDLER_GET_PRIVATE (handler);
 
@@ -181,11 +242,15 @@ mail_attachment_handler_forward_with_style (EAttachmentHandler *handler,
        g_return_if_fail (message != NULL);
 
        folder = mail_attachment_handler_guess_folder_ref (handler);
+       shell = e_shell_backend_get_shell (E_SHELL_BACKEND (priv->backend));
 
-       em_utils_forward_message (priv->backend, message, style, folder, NULL);
+       ccd = g_new0 (CreateComposerData, 1);
+       ccd->message = message;
+       ccd->folder = folder;
+       ccd->is_forward = TRUE;
+       ccd->forward_style = style;
 
-       g_clear_object (&folder);
-       g_object_unref (message);
+       e_msg_composer_new (shell, mail_attachment_handler_composer_created_cb, ccd);
 }
 
 static void
@@ -207,9 +272,8 @@ mail_attachment_handler_reply (EAttachmentHandler *handler,
                                EMailReplyType reply_type)
 {
        EMailAttachmentHandlerPrivate *priv;
-       GSettings *settings;
-       EMailReplyStyle style;
        CamelMimeMessage *message;
+       CreateComposerData *ccd;
        EShellBackend *shell_backend;
        EShell *shell;
 
@@ -218,17 +282,15 @@ mail_attachment_handler_reply (EAttachmentHandler *handler,
        message = mail_attachment_handler_get_selected_message (handler);
        g_return_if_fail (message != NULL);
 
-       settings = e_util_ref_settings ("org.gnome.evolution.mail");
-       style = g_settings_get_enum (settings, "reply-style-name");
-       g_object_unref (settings);
-
        shell_backend = E_SHELL_BACKEND (priv->backend);
        shell = e_shell_backend_get_shell (shell_backend);
 
-       em_utils_reply_to_message (
-               shell, message, NULL, NULL, reply_type, style, NULL, NULL);
+       ccd = g_new0 (CreateComposerData, 1);
+       ccd->message = message;
+       ccd->reply_type = reply_type;
+       ccd->is_reply = TRUE;
 
-       g_object_unref (message);
+       e_msg_composer_new (shell, mail_attachment_handler_composer_created_cb, ccd);
 }
 
 static void
@@ -259,6 +321,7 @@ mail_attachment_handler_message_edit (GtkAction *action,
        EMailAttachmentHandlerPrivate *priv;
        CamelMimeMessage *message;
        CamelFolder *folder;
+       CreateComposerData *ccd;
        EShell *shell;
 
        priv = E_MAIL_ATTACHMENT_HANDLER_GET_PRIVATE (handler);
@@ -269,10 +332,11 @@ mail_attachment_handler_message_edit (GtkAction *action,
        shell = e_shell_backend_get_shell (E_SHELL_BACKEND (priv->backend));
        folder = mail_attachment_handler_guess_folder_ref (handler);
 
-       em_utils_edit_message (shell, folder, message, NULL, TRUE);
+       ccd = g_new0 (CreateComposerData, 1);
+       ccd->message = message;
+       ccd->folder = folder;
 
-       g_clear_object (&folder);
-       g_object_unref (message);
+       e_msg_composer_new (shell, mail_attachment_handler_composer_created_cb, ccd);
 }
 
 static void
@@ -301,6 +365,7 @@ mail_attachment_handler_redirect (GtkAction *action,
 {
        EMailAttachmentHandlerPrivate *priv;
        CamelMimeMessage *message;
+       CreateComposerData *ccd;
        EShell *shell;
 
        priv = E_MAIL_ATTACHMENT_HANDLER_GET_PRIVATE (handler);
@@ -310,9 +375,12 @@ mail_attachment_handler_redirect (GtkAction *action,
 
        shell = e_shell_backend_get_shell (E_SHELL_BACKEND (priv->backend));
 
-       em_utils_redirect_message (shell, message);
+       ccd = g_new0 (CreateComposerData, 1);
+       ccd->message = message;
+       ccd->folder = NULL;
+       ccd->is_redirect = TRUE;
 
-       g_object_unref (message);
+       e_msg_composer_new (shell, mail_attachment_handler_composer_created_cb, ccd);
 }
 
 static GtkActionEntry standard_entries[] = {
diff --git a/modules/mail/e-mail-shell-backend.c b/modules/mail/e-mail-shell-backend.c
index d748f31..506b0d7 100644
--- a/modules/mail/e-mail-shell-backend.c
+++ b/modules/mail/e-mail-shell-backend.c
@@ -293,6 +293,29 @@ action_mail_account_new_cb (GtkAction *action,
 }
 
 static void
+action_mail_message_new_composer_created_cb (GObject *source_object,
+                                            GAsyncResult *result,
+                                            gpointer user_data)
+{
+       CamelFolder *folder = user_data;
+       EMsgComposer *composer;
+       GError *error = NULL;
+
+       if (folder)
+               g_return_if_fail (CAMEL_IS_FOLDER (folder));
+
+       composer = e_msg_composer_new_finish (result, &error);
+       if (error) {
+               g_warning ("%s: Failed to create msg composer: %s", G_STRFUNC, error->message);
+               g_clear_error (&error);
+       } else {
+               em_utils_compose_new_message (composer, folder);
+       }
+
+       g_clear_object (&folder);
+}
+
+static void
 action_mail_message_new_cb (GtkAction *action,
                             EShellWindow *shell_window)
 {
@@ -342,8 +365,9 @@ action_mail_message_new_cb (GtkAction *action,
                g_free (folder_name);
        }
 
-exit:
-       em_utils_compose_new_message (shell, folder);
+ exit:
+       e_msg_composer_new (shell, action_mail_message_new_composer_created_cb,
+               folder ? g_object_ref (folder) : NULL);
 }
 
 static GtkActionEntry item_entries[] = {
@@ -495,11 +519,11 @@ mail_shell_backend_window_added_cb (GtkApplication *application,
 
        /* This applies to both the composer and signature editor. */
        if (editor != NULL) {
-               EHTMLEditorView *view;
+               EContentEditor *cnt_editor;
                GSettings *settings;
                gboolean active = TRUE;
 
-               view = e_html_editor_get_view (editor);
+               cnt_editor = e_html_editor_get_content_editor (editor);
 
                settings = e_util_ref_settings ("org.gnome.evolution.mail");
 
@@ -508,7 +532,7 @@ mail_shell_backend_window_added_cb (GtkApplication *application,
 
                g_object_unref (settings);
 
-               e_html_editor_view_set_html_mode (view, active);
+               e_content_editor_set_html_mode (cnt_editor, active);
        }
 
        if (E_IS_MSG_COMPOSER (window)) {
diff --git a/modules/mail/e-mail-shell-content.c b/modules/mail/e-mail-shell-content.c
index 66a79c1..917c6cf 100644
--- a/modules/mail/e-mail-shell-content.c
+++ b/modules/mail/e-mail-shell-content.c
@@ -67,6 +67,27 @@ G_DEFINE_DYNAMIC_TYPE_EXTENDED (
                E_TYPE_MAIL_READER,
                e_mail_shell_content_reader_init))
 
+static gboolean
+mail_shell_content_transform_num_attachments_to_visible_boolean_with_settings (GBinding *binding,
+                                                                              const GValue *from_value,
+                                                                              GValue *to_value,
+                                                                              gpointer user_data)
+{
+       GSettings *settings;
+       gboolean res = TRUE;
+
+       settings = e_util_ref_settings ("org.gnome.evolution.mail");
+
+       if (g_settings_get_boolean (settings, "show-attachment-bar"))
+               res = e_attachment_store_transform_num_attachments_to_visible_boolean (binding, from_value, 
to_value, user_data);
+       else
+               g_value_set_boolean (to_value, FALSE);
+
+       g_clear_object (&settings);
+
+       return res;
+}
+
 static void
 reconnect_changed_event (EMailReader *child,
                          EMailReader *parent)
@@ -179,9 +200,11 @@ mail_shell_content_constructed (GObject *object)
        EMailShellContentPrivate *priv;
        EShellContent *shell_content;
        EShellView *shell_view;
+       EAttachmentStore *attachment_store;
+       EMailDisplay *display;
        GtkWindow *window;
-       GtkWidget *container;
        GtkWidget *widget;
+       GtkBox *vbox;
 
        priv = E_MAIL_SHELL_CONTENT_GET_PRIVATE (object);
 
@@ -193,11 +216,15 @@ mail_shell_content_constructed (GObject *object)
 
        /* Build content widgets. */
 
-       container = GTK_WIDGET (object);
+       widget = gtk_box_new (GTK_ORIENTATION_VERTICAL, 4);
+       gtk_container_add (GTK_CONTAINER (shell_content), widget);
+       gtk_widget_show (widget);
+
+       vbox = GTK_BOX (widget);
 
        widget = e_mail_paned_view_new (shell_view);
+       gtk_box_pack_start (vbox, widget, TRUE, TRUE, 0);
 
-       gtk_container_add (GTK_CONTAINER (container), widget);
        priv->mail_view = g_object_ref (widget);
        gtk_widget_show (widget);
 
@@ -208,6 +235,17 @@ mail_shell_content_constructed (GObject *object)
                widget, "folder-loaded",
                G_CALLBACK (reconnect_folder_loaded_event), object);
 
+       display = e_mail_reader_get_mail_display (E_MAIL_READER (object));
+       attachment_store = e_mail_display_get_attachment_store (display);
+       widget = GTK_WIDGET (e_mail_display_get_attachment_view (display));
+
+       e_binding_bind_property_full (
+               attachment_store, "num-attachments",
+               widget, "visible",
+               G_BINDING_SYNC_CREATE,
+               mail_shell_content_transform_num_attachments_to_visible_boolean_with_settings,
+               NULL, NULL, NULL);
+
        window = e_mail_reader_get_window (E_MAIL_READER (object));
        widget = e_mail_reader_get_message_list (E_MAIL_READER (object));
 
diff --git a/modules/mail/e-mail-shell-view-actions.c b/modules/mail/e-mail-shell-view-actions.c
index fc6cebf..d843671 100644
--- a/modules/mail/e-mail-shell-view-actions.c
+++ b/modules/mail/e-mail-shell-view-actions.c
@@ -256,6 +256,29 @@ action_mail_create_search_folder_cb (GtkAction *action,
 }
 
 static void
+action_mail_attachment_bar_cb (GtkAction *action,
+                              EMailShellView *mail_shell_view)
+{
+       EMailDisplay *mail_display;
+       EAttachmentView *attachment_view;
+
+       g_return_if_fail (E_IS_MAIL_SHELL_VIEW (mail_shell_view));
+
+       mail_display = e_mail_reader_get_mail_display (E_MAIL_READER 
(mail_shell_view->priv->mail_shell_content));
+       attachment_view = e_mail_display_get_attachment_view (mail_display);
+       if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action))) {
+               EAttachmentStore *store;
+               guint num_attachments;
+
+               store = e_attachment_bar_get_store (E_ATTACHMENT_BAR (attachment_view));
+               num_attachments = e_attachment_store_get_num_attachments (store);
+               gtk_widget_set_visible (GTK_WIDGET (attachment_view), num_attachments > 0);
+       } else {
+               gtk_widget_hide (GTK_WIDGET (attachment_view));
+       }
+}
+
+static void
 action_mail_download_finished_cb (CamelStore *store,
                                   GAsyncResult *result,
                                   EActivity *activity)
@@ -2025,6 +2048,14 @@ static GtkToggleActionEntry mail_toggle_entries[] = {
          NULL,  /* Handled by property bindings */
          TRUE },
 
+       { "mail-attachment-bar",
+         NULL,
+         N_("Show _Attachment Bar"),
+         NULL,
+         N_("Show Attachment Bar below the message preview pane when the message has attachments"),
+         G_CALLBACK (action_mail_attachment_bar_cb),
+         TRUE },
+
        { "mail-show-deleted",
          NULL,
          N_("Show _Deleted Messages"),
@@ -2334,6 +2365,11 @@ e_mail_shell_view_actions_init (EMailShellView *mail_shell_view)
                ACTION (MAIL_VFOLDER_UNMATCHED_ENABLE), "active",
                G_SETTINGS_BIND_DEFAULT);
 
+       g_settings_bind (
+               settings, "show-attachment-bar",
+               ACTION (MAIL_ATTACHMENT_BAR), "active",
+               G_SETTINGS_BIND_DEFAULT);
+
        g_object_unref (settings);
 
        /* Fine tuning. */
diff --git a/modules/mail/e-mail-shell-view-actions.h b/modules/mail/e-mail-shell-view-actions.h
index 866cc98..5459f73 100644
--- a/modules/mail/e-mail-shell-view-actions.h
+++ b/modules/mail/e-mail-shell-view-actions.h
@@ -34,6 +34,8 @@
        E_SHELL_WINDOW_ACTION ((window), "mail-account-refresh")
 #define E_SHELL_WINDOW_ACTION_MAIL_ADD_SENDER(window) \
        E_SHELL_WINDOW_ACTION ((window), "mail-add-sender")
+#define E_SHELL_WINDOW_ACTION_MAIL_ATTACHMENT_BAR(window) \
+       E_SHELL_WINDOW_ACTION ((window), "mail-attachment-bar")
 #define E_SHELL_WINDOW_ACTION_MAIL_CARET_MODE(window) \
        E_SHELL_WINDOW_ACTION ((window), "mail-caret-mode")
 #define E_SHELL_WINDOW_ACTION_MAIL_CHECK_FOR_JUNK(window) \
diff --git a/modules/mail/e-mail-shell-view-private.c b/modules/mail/e-mail-shell-view-private.c
index caf2392..e3c5ec1 100644
--- a/modules/mail/e-mail-shell-view-private.c
+++ b/modules/mail/e-mail-shell-view-private.c
@@ -250,6 +250,33 @@ mail_shell_view_folder_tree_popup_event_cb (EShellView *shell_view,
 }
 
 static gboolean
+mail_shell_view_mail_display_needs_key (EMailShellView *mail_shell_view,
+                                        EMailDisplay *mail_display)
+{
+       if (gtk_widget_has_focus (GTK_WIDGET (mail_display))) {
+               GDBusProxy *web_extension;
+
+               /* Intentionally use Evolution Web Extension */
+               web_extension = e_web_view_get_web_extension_proxy (E_WEB_VIEW (mail_display));
+               if (web_extension) {
+                       GVariant *result;
+
+                       result = g_dbus_proxy_get_cached_property (web_extension, "NeedInput");
+                       if (result) {
+                               gboolean need_input;
+
+                               need_input = g_variant_get_boolean (result);
+                               g_variant_unref (result);
+
+                               return need_input;
+                       }
+               }
+       }
+
+       return FALSE;
+}
+
+static gboolean
 mail_shell_view_key_press_event_cb (EMailShellView *mail_shell_view,
                                     GdkEventKey *event)
 {
@@ -281,42 +308,11 @@ mail_shell_view_key_press_event_cb (EMailShellView *mail_shell_view,
                        action = ACTION (MAIL_SMART_BACKWARD);
                        break;
 
-               case GDK_KEY_Home:
-               case GDK_KEY_Left:
-               case GDK_KEY_Up:
-               case GDK_KEY_Right:
-               case GDK_KEY_Down:
-               case GDK_KEY_Next:
-               case GDK_KEY_End:
-               case GDK_KEY_Begin:
-                       /* If Caret mode is enabled don't try to process these keys */
-                       if (e_web_view_get_caret_mode (E_WEB_VIEW (mail_display)))
-                               return FALSE;
-               case GDK_KEY_Prior:
-                       if (!e_mail_display_needs_key (mail_display, FALSE) &&
-                           webkit_web_view_get_main_frame (WEBKIT_WEB_VIEW (mail_display)) !=
-                           webkit_web_view_get_focused_frame (WEBKIT_WEB_VIEW (mail_display))) {
-                               WebKitDOMDocument *document;
-                               WebKitDOMDOMWindow *window;
-
-                               document = webkit_web_view_get_dom_document (WEBKIT_WEB_VIEW (mail_display));
-                               window = webkit_dom_document_get_default_view (document);
-
-                               /* Workaround WebKit bug for key navigation, when inner IFRAME is focused.
-                                * EMailView's inner IFRAMEs have disabled scrolling, but WebKit doesn't post
-                                * key navigation events to parent's frame, thus the view doesn't scroll.
-                                * This is a poor workaround for this issue, the main frame is focused,
-                                * which has scrolling enabled.
-                               */
-                               webkit_dom_dom_window_focus (window);
-                       }
-
-                       return FALSE;
                default:
                        return FALSE;
        }
 
-       if (e_mail_display_needs_key (mail_display, TRUE))
+       if (mail_shell_view_mail_display_needs_key (mail_shell_view, mail_display))
                return FALSE;
 
        gtk_action_activate (action);
diff --git a/modules/mail/e-mail-shell-view-private.h b/modules/mail/e-mail-shell-view-private.h
index 4300742..c2eb113 100644
--- a/modules/mail/e-mail-shell-view-private.h
+++ b/modules/mail/e-mail-shell-view-private.h
@@ -167,6 +167,8 @@ void                e_mail_shell_view_update_sidebar
                                        (EMailShellView *mail_shell_view);
 void           e_mail_shell_view_update_send_receive_menus
                                        (EMailShellView *mail_shell_view);
+GDBusProxy *   e_mail_shell_view_get_web_extension_proxy
+                                       (EMailShellView *mail_shell_view);
 
 G_END_DECLS
 
diff --git a/modules/mail/em-composer-prefs.c b/modules/mail/em-composer-prefs.c
index 11d9172..ecdcf40 100644
--- a/modules/mail/em-composer-prefs.c
+++ b/modules/mail/em-composer-prefs.c
@@ -141,11 +141,10 @@ spell_language_save (EMComposerPrefs *prefs)
 static void
 spell_setup (EMComposerPrefs *prefs)
 {
-       GList *list, *link;
+       GList *list = NULL, *link;
        GtkListStore *store;
 
        store = GTK_LIST_STORE (prefs->language_model);
-
        list = e_spell_checker_list_available_dicts (prefs->spell_checker);
 
        /* Populate the GtkListStore. */
diff --git a/modules/prefer-plain/e-mail-display-popup-prefer-plain.c 
b/modules/prefer-plain/e-mail-display-popup-prefer-plain.c
index c23e5d2..21fc1ce 100644
--- a/modules/prefer-plain/e-mail-display-popup-prefer-plain.c
+++ b/modules/prefer-plain/e-mail-display-popup-prefer-plain.c
@@ -35,12 +35,11 @@ typedef struct _EMailDisplayPopupPreferPlainClass EMailDisplayPopupPreferPlainCl
 struct _EMailDisplayPopupPreferPlain {
        EExtension parent;
 
-       WebKitDOMDocument *document;
        gchar *text_plain_id;
        gchar *text_html_id;
+       gchar *document_uri;
 
        GtkActionGroup *action_group;
-
 };
 
 struct _EMailDisplayPopupPreferPlainClass {
@@ -94,16 +93,20 @@ toggle_part (GtkAction *action,
              EMailDisplayPopupExtension *extension)
 {
        EMailDisplayPopupPreferPlain *pp_extension = (EMailDisplayPopupPreferPlain *) extension;
-       WebKitDOMDocument *doc = pp_extension->document;
-       WebKitDOMDOMWindow *window;
-       WebKitDOMElement *frame_element;
        SoupURI *soup_uri;
        GHashTable *query;
        gchar *uri;
 
-       uri = webkit_dom_document_get_document_uri (doc);
-       soup_uri = soup_uri_new (uri);
-       g_free (uri);
+       if (!pp_extension->document_uri)
+               return;
+
+       soup_uri = soup_uri_new (pp_extension->document_uri);
+
+       if (!soup_uri || !soup_uri->query) {
+               if (soup_uri)
+                       soup_uri_free (soup_uri);
+               return;
+       }
 
        query = soup_form_decode (soup_uri->query);
        g_hash_table_replace (
@@ -123,11 +126,8 @@ toggle_part (GtkAction *action,
        uri = soup_uri_to_string (soup_uri, FALSE);
        soup_uri_free (soup_uri);
 
-       /* Get frame's window and from the window the actual <iframe> element */
-       window = webkit_dom_document_get_default_view (doc);
-       frame_element = webkit_dom_dom_window_get_frame_element (window);
-       webkit_dom_html_iframe_element_set_src (
-               WEBKIT_DOM_HTML_IFRAME_ELEMENT (frame_element), uri);
+       e_web_view_set_document_iframe_src (E_WEB_VIEW (e_extension_get_extensible (E_EXTENSION (extension))),
+               pp_extension->document_uri, uri);
 
        g_free (uri);
 }
@@ -169,6 +169,17 @@ set_text_html_id (EMailDisplayPopupPreferPlain *extension,
        extension->text_html_id = g_strdup (id);
 }
 
+static void
+set_document_uri (EMailDisplayPopupPreferPlain *extension,
+                  const gchar *document_uri)
+{
+       if (extension->document_uri == document_uri)
+               return;
+
+       g_free (extension->document_uri);
+       extension->document_uri = g_strdup (document_uri);
+}
+
 static GtkActionGroup *
 create_group (EMailDisplayPopupExtension *extension)
 {
@@ -218,18 +229,17 @@ create_group (EMailDisplayPopupExtension *extension)
 
 static void
 mail_display_popup_prefer_plain_update_actions (EMailDisplayPopupExtension *extension,
-                                                WebKitHitTestResult *context)
+                                               const gchar *popup_document_uri)
 {
        EMailDisplay *display;
+       EMailDisplayPopupPreferPlain *pp_extension;
        GtkAction *action;
-       WebKitDOMNode *node;
-       gchar *uri, *part_id, *pos, *prefix;
+       gchar *part_id, *pos, *prefix;
        SoupURI *soup_uri;
        GHashTable *query;
        EMailPartList *part_list;
        gboolean is_text_plain;
        const gchar *action_name;
-       EMailDisplayPopupPreferPlain *pp_extension;
        GQueue queue = G_QUEUE_INIT;
        GList *head, *link;
 
@@ -241,22 +251,17 @@ mail_display_popup_prefer_plain_update_actions (EMailDisplayPopupExtension *exte
        if (!pp_extension->action_group)
                pp_extension->action_group = create_group (extension);
 
-       g_object_get (context, "inner-node", &node, NULL);
+       set_document_uri (pp_extension, popup_document_uri);
 
-       if (!node) {
-               gtk_action_group_set_visible (pp_extension->action_group, FALSE);
-               return;
-       }
+       if (pp_extension->document_uri)
+               soup_uri = soup_uri_new (pp_extension->document_uri);
+       else
+               soup_uri = NULL;
 
-       pp_extension->document = webkit_dom_node_get_owner_document (node);
-       uri = webkit_dom_document_get_document_uri (pp_extension->document);
-
-       soup_uri = soup_uri_new (uri);
        if (!soup_uri || !soup_uri->query) {
                gtk_action_group_set_visible (pp_extension->action_group, FALSE);
                if (soup_uri)
                        soup_uri_free (soup_uri);
-               g_free (uri);
                return;
        }
 
@@ -264,28 +269,19 @@ mail_display_popup_prefer_plain_update_actions (EMailDisplayPopupExtension *exte
        part_id = g_hash_table_lookup (query, "part_id");
        if (part_id == NULL) {
                gtk_action_group_set_visible (pp_extension->action_group, FALSE);
-               g_hash_table_destroy (query);
-               soup_uri_free (soup_uri);
-               g_free (uri);
-               return;
+               goto out;
        }
 
        pos = strstr (part_id, ".alternative-prefer-plain.");
        if (!pos) {
                gtk_action_group_set_visible (pp_extension->action_group, FALSE);
-               g_hash_table_destroy (query);
-               soup_uri_free (soup_uri);
-               g_free (uri);
-               return;
+               goto out;
        }
 
        /* Don't display the actions on any other than text/plain or text/html parts */
        if (!strstr (pos, "plain_text") && !strstr (pos, "text_html")) {
                gtk_action_group_set_visible (pp_extension->action_group, FALSE);
-               g_hash_table_destroy (query);
-               soup_uri_free (soup_uri);
-               g_free (uri);
-               return;
+               goto out;
        }
 
        /* Check whether the displayed part is text_plain */
@@ -353,9 +349,9 @@ mail_display_popup_prefer_plain_update_actions (EMailDisplayPopupExtension *exte
        }
 
        g_free (prefix);
+ out:
        g_hash_table_destroy (query);
        soup_uri_free (soup_uri);
-       g_free (uri);
 }
 
 void
@@ -377,8 +373,7 @@ e_mail_display_popup_prefer_plain_dispose (GObject *object)
        }
 
        /* Chain up to parent's dispose() method. */
-       G_OBJECT_CLASS (e_mail_display_popup_prefer_plain_parent_class)->
-               dispose (object);
+       G_OBJECT_CLASS (e_mail_display_popup_prefer_plain_parent_class)->dispose (object);
 }
 
 static void
@@ -390,10 +385,10 @@ e_mail_display_popup_prefer_plain_finalize (GObject *object)
 
        g_free (extension->text_html_id);
        g_free (extension->text_plain_id);
+       g_free (extension->document_uri);
 
        /* Chain up to parent's finalize() method. */
-       G_OBJECT_CLASS (e_mail_display_popup_prefer_plain_parent_class)->
-               finalize (object);
+       G_OBJECT_CLASS (e_mail_display_popup_prefer_plain_parent_class)->finalize (object);
 }
 
 static void
@@ -419,7 +414,6 @@ e_mail_display_popup_extension_interface_init (EMailDisplayPopupExtensionInterfa
 void
 e_mail_display_popup_prefer_plain_class_finalize (EMailDisplayPopupPreferPlainClass *class)
 {
-
 }
 
 static void
@@ -428,5 +422,5 @@ e_mail_display_popup_prefer_plain_init (EMailDisplayPopupPreferPlain *extension)
        extension->action_group = NULL;
        extension->text_html_id = NULL;
        extension->text_plain_id = NULL;
-       extension->document = NULL;
+       extension->document_uri = NULL;
 }
diff --git a/modules/settings/Makefile.am b/modules/settings/Makefile.am
index e137bea..45db4e8 100644
--- a/modules/settings/Makefile.am
+++ b/modules/settings/Makefile.am
@@ -25,8 +25,8 @@ module_settings_la_SOURCES = \
        e-settings-date-edit.h \
        e-settings-deprecated.c \
        e-settings-deprecated.h \
-       e-settings-html-editor-view.c \
-       e-settings-html-editor-view.h \
+       e-settings-content-editor.c \
+       e-settings-content-editor.h \
        e-settings-mail-browser.c \
        e-settings-mail-browser.h \
        e-settings-mail-formatter.c \
diff --git a/modules/settings/e-settings-content-editor.c b/modules/settings/e-settings-content-editor.c
new file mode 100644
index 0000000..3bc438c
--- /dev/null
+++ b/modules/settings/e-settings-content-editor.c
@@ -0,0 +1,224 @@
+/*
+ * e-settings-content-editor.c
+ *
+ * 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 <stdio.h>
+#include <string.h>
+
+#include "e-settings-content-editor.h"
+
+#include <e-util/e-util.h>
+
+#define E_SETTINGS_CONTENT_EDITOR_GET_PRIVATE(obj) \
+       (G_TYPE_INSTANCE_GET_PRIVATE \
+       ((obj), E_TYPE_SETTINGS_CONTENT_EDITOR, ESettingsContentEditorPrivate))
+
+struct _ESettingsContentEditorPrivate {
+       GSettings *settings;
+
+       GHashTable *old_settings;
+};
+
+G_DEFINE_DYNAMIC_TYPE (
+       ESettingsContentEditor,
+       e_settings_content_editor,
+       E_TYPE_EXTENSION)
+
+static void
+settings_content_editor_inline_spelling_changed (ESettingsContentEditor *extension,
+                                                gboolean spell_check_enabled)
+{
+       EExtensible *extensible;
+       EContentEditor *cnt_editor;
+
+       extensible = e_extension_get_extensible (E_EXTENSION (extension));
+       cnt_editor = e_html_editor_get_content_editor (E_HTML_EDITOR (extensible));
+
+       e_content_editor_set_spell_check_enabled (cnt_editor, spell_check_enabled);
+}
+
+static void
+settings_content_editor_load_style (ESettingsContentEditor *extension)
+{
+       EExtensible *extensible;
+       EContentEditor *cnt_editor;
+
+       extensible = e_extension_get_extensible (E_EXTENSION (extension));
+       cnt_editor = e_html_editor_get_content_editor (E_HTML_EDITOR (extensible));
+       e_content_editor_update_styles (cnt_editor);
+}
+
+static void
+settings_content_editor_changed_cb (GSettings *settings,
+                                    const gchar *key,
+                                    ESettingsContentEditor *extension)
+{
+       GVariant *new_value, *old_value;
+
+       new_value = g_settings_get_value (settings, key);
+       old_value = g_hash_table_lookup (extension->priv->old_settings, key);
+
+       if (!new_value || !old_value || !g_variant_equal (new_value, old_value)) {
+               if (new_value)
+                       g_hash_table_insert (extension->priv->old_settings, g_strdup (key), new_value);
+               else
+                       g_hash_table_remove (extension->priv->old_settings, key);
+
+               if (g_strcmp0 (key, "composer-inline-spelling") == 0)
+                       settings_content_editor_inline_spelling_changed (extension, g_settings_get_boolean 
(settings, key));
+               else
+                       settings_content_editor_load_style (extension);
+       } else if (new_value) {
+               g_variant_unref (new_value);
+       }
+}
+
+static void
+settings_content_editor_html_editor_realize_cb (GtkWidget *html_editor,
+                                               ESettingsContentEditor *extension)
+{
+       GSettings *settings;
+
+       settings = extension->priv->settings;
+
+       settings_content_editor_inline_spelling_changed (extension, g_settings_get_boolean (settings, 
"composer-inline-spelling"));
+       settings_content_editor_load_style (extension);
+
+       /* Reload the web view when certain settings change. */
+
+       g_signal_connect (
+               settings, "changed::use-custom-font",
+               G_CALLBACK (settings_content_editor_changed_cb), extension);
+
+       g_signal_connect (
+               settings, "changed::monospace-font",
+               G_CALLBACK (settings_content_editor_changed_cb), extension);
+
+       g_signal_connect (
+               settings, "changed::variable-width-font",
+               G_CALLBACK (settings_content_editor_changed_cb), extension);
+
+       g_signal_connect (
+               settings, "changed::mark-citations",
+               G_CALLBACK (settings_content_editor_changed_cb), extension);
+
+       g_signal_connect (
+               settings, "changed::citation-color",
+               G_CALLBACK (settings_content_editor_changed_cb), extension);
+
+       g_signal_connect (
+               settings, "changed::composer-inline-spelling",
+               G_CALLBACK (settings_content_editor_changed_cb), extension);
+}
+
+static void
+settings_content_editor_dispose (GObject *object)
+{
+       ESettingsContentEditorPrivate *priv;
+
+       priv = E_SETTINGS_CONTENT_EDITOR_GET_PRIVATE (object);
+
+       if (priv->settings != NULL) {
+               g_signal_handlers_disconnect_by_func (
+                       priv->settings,
+                       settings_content_editor_changed_cb, object);
+       }
+
+       g_clear_object (&priv->settings);
+
+       /* Chain up to parent's dispose() method. */
+       G_OBJECT_CLASS (e_settings_content_editor_parent_class)->dispose (object);
+}
+
+static void
+settings_content_editor_finalize (GObject *object)
+{
+       ESettingsContentEditorPrivate *priv;
+
+       priv = E_SETTINGS_CONTENT_EDITOR_GET_PRIVATE (object);
+
+       if (priv->old_settings) {
+               g_hash_table_destroy (priv->old_settings);
+               priv->old_settings = NULL;
+       }
+
+       /* Chain up to parent's finalize() method. */
+       G_OBJECT_CLASS (e_settings_content_editor_parent_class)->finalize (object);
+}
+
+static void
+settings_content_editor_constructed (GObject *object)
+{
+       EExtensible *extensible;
+
+       /* Chain up to parent's method. */
+       G_OBJECT_CLASS (e_settings_content_editor_parent_class)->constructed (object);
+
+       extensible = e_extension_get_extensible (E_EXTENSION (object));
+
+       g_signal_connect (
+               extensible, "realize",
+               G_CALLBACK (settings_content_editor_html_editor_realize_cb), object);
+}
+
+static void
+e_settings_content_editor_class_init (ESettingsContentEditorClass *class)
+{
+       GObjectClass *object_class;
+       EExtensionClass *extension_class;
+
+       g_type_class_add_private (class, sizeof (ESettingsContentEditorPrivate));
+
+       object_class = G_OBJECT_CLASS (class);
+       object_class->dispose = settings_content_editor_dispose;
+       object_class->finalize = settings_content_editor_finalize;
+       object_class->constructed = settings_content_editor_constructed;
+
+       extension_class = E_EXTENSION_CLASS (class);
+       extension_class->extensible_type = E_TYPE_HTML_EDITOR;
+}
+
+static void
+e_settings_content_editor_class_finalize (ESettingsContentEditorClass *class)
+{
+}
+
+static void
+e_settings_content_editor_init (ESettingsContentEditor *extension)
+{
+       GSettings *settings;
+
+       extension->priv = E_SETTINGS_CONTENT_EDITOR_GET_PRIVATE (extension);
+
+       settings = e_util_ref_settings ("org.gnome.evolution.mail");
+       extension->priv->settings = settings;
+
+       extension->priv->old_settings = g_hash_table_new_full (
+               g_str_hash, g_str_equal, g_free, (GDestroyNotify) g_variant_unref);
+}
+
+void
+e_settings_content_editor_type_register (GTypeModule *type_module)
+{
+       /* XXX G_DEFINE_DYNAMIC_TYPE declares a static type registration
+        *     function, so we have to wrap it with a public function in
+        *     order to register types from a separate compilation unit. */
+       e_settings_content_editor_register_type (type_module);
+}
diff --git a/modules/settings/e-settings-content-editor.h b/modules/settings/e-settings-content-editor.h
new file mode 100644
index 0000000..2b5dc90
--- /dev/null
+++ b/modules/settings/e-settings-content-editor.h
@@ -0,0 +1,64 @@
+/*
+ * e-settings-content-editor.h
+ *
+ * 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_SETTINGS_CONTENT_EDITOR_H
+#define E_SETTINGS_CONTENT_EDITOR_H
+
+#include <libebackend/libebackend.h>
+
+/* Standard GObject macros */
+#define E_TYPE_SETTINGS_CONTENT_EDITOR \
+       (e_settings_content_editor_get_type ())
+#define E_SETTINGS_CONTENT_EDITOR(obj) \
+       (G_TYPE_CHECK_INSTANCE_CAST \
+       ((obj), E_TYPE_SETTINGS_CONTENT_EDITOR, ESettingsContentEditor))
+#define E_SETTINGS_CONTENT_EDITOR_CLASS(cls) \
+       (G_TYPE_CHECK_CLASS_CAST \
+       ((cls), E_TYPE_SETTINGS_CONTENT_EDITOR, ESettingsContentEditorClass))
+#define E_IS_SETTINGS_CONTENT_EDITOR(obj) \
+       (G_TYPE_CHECK_INSTANCE_TYPE \
+       ((obj), E_TYPE_SETTINGS_CONTENT_EDITOR))
+#define E_IS_SETTINGS_CONTENT_EDITOR_CLASS(cls) \
+       (G_TYPE_CHECK_CLASS_TYPE \
+       ((cls), E_TYPE_SETTINGS_CONTENT_EDITOR))
+#define E_SETTINGS_CONTENT_EDITOR_GET_CLASS(obj) \
+       (G_TYPE_INSTANCE_GET_CLASS \
+       ((obj), E_TYPE_SETTINGS_CONTENT_EDITOR, ESettingsContentEditorClass))
+
+G_BEGIN_DECLS
+
+typedef struct _ESettingsContentEditor ESettingsContentEditor;
+typedef struct _ESettingsContentEditorClass ESettingsContentEditorClass;
+typedef struct _ESettingsContentEditorPrivate ESettingsContentEditorPrivate;
+
+struct _ESettingsContentEditor {
+       EExtension parent;
+       ESettingsContentEditorPrivate *priv;
+};
+
+struct _ESettingsContentEditorClass {
+       EExtensionClass parent_class;
+};
+
+GType          e_settings_content_editor_get_type
+                                               (void) G_GNUC_CONST;
+void           e_settings_content_editor_type_register
+                                               (GTypeModule *type_module);
+
+G_END_DECLS
+
+#endif /* E_SETTINGS_CONTENT_EDITOR_H */
diff --git a/modules/settings/e-settings-spell-checker.c b/modules/settings/e-settings-spell-checker.c
index 8192766..812e3b0 100644
--- a/modules/settings/e-settings-spell-checker.c
+++ b/modules/settings/e-settings-spell-checker.c
@@ -62,8 +62,7 @@ settings_spell_checker_constructed (GObject *object)
                E_SETTINGS_SPELL_CHECKER (object));
 
        /* Make sure there are no active languages at this point. */
-       g_warn_if_fail (
-               e_spell_checker_count_active_languages (spell_checker) == 0);
+       g_warn_if_fail (e_spell_checker_count_active_languages (spell_checker) == 0);
 
        settings = e_util_ref_settings ("org.gnome.evolution.mail");
        strv = g_settings_get_strv (settings, "composer-spell-languages");
diff --git a/modules/settings/evolution-module-settings.c b/modules/settings/evolution-module-settings.c
index 5b81a94..8ade039 100644
--- a/modules/settings/evolution-module-settings.c
+++ b/modules/settings/evolution-module-settings.c
@@ -19,9 +19,10 @@
 #include "e-settings-calendar-item.h"
 #include "e-settings-calendar-view.h"
 #include "e-settings-client-cache.h"
+#include "e-settings-content-editor.h"
 #include "e-settings-date-edit.h"
 #include "e-settings-deprecated.h"
-#include "e-settings-html-editor-view.h"
+#include "e-settings-content-editor.h"
 #include "e-settings-mail-browser.h"
 #include "e-settings-mail-formatter.h"
 #include "e-settings-mail-part-headers.h"
@@ -46,9 +47,10 @@ e_module_load (GTypeModule *type_module)
        e_settings_calendar_item_type_register (type_module);
        e_settings_calendar_view_type_register (type_module);
        e_settings_client_cache_type_register (type_module);
+       e_settings_content_editor_type_register (type_module);
        e_settings_date_edit_type_register (type_module);
        e_settings_deprecated_type_register (type_module);
-       e_settings_html_editor_view_type_register (type_module);
+       e_settings_content_editor_type_register (type_module);
        e_settings_mail_browser_type_register (type_module);
        e_settings_mail_formatter_type_register (type_module);
        e_settings_mail_part_headers_type_register (type_module);
diff --git a/modules/text-highlight/e-mail-display-popup-text-highlight.c 
b/modules/text-highlight/e-mail-display-popup-text-highlight.c
index eb072a0..a28932e 100644
--- a/modules/text-highlight/e-mail-display-popup-text-highlight.c
+++ b/modules/text-highlight/e-mail-display-popup-text-highlight.c
@@ -36,7 +36,8 @@ typedef struct _EMailDisplayPopupTextHighlight {
 
        GtkActionGroup *action_group;
 
-       WebKitDOMDocument *document;
+       volatile gint updating;
+       gchar *document_uri;
 } EMailDisplayPopupTextHighlight;
 
 typedef struct _EMailDisplayPopupTextHighlightClass {
@@ -107,33 +108,42 @@ static GtkActionEntry entries[] = {
 };
 
 static void
+set_document_uri (EMailDisplayPopupTextHighlight *extension,
+                  const gchar *document_uri)
+{
+       if (extension->document_uri == document_uri)
+               return;
+
+       g_free (extension->document_uri);
+       extension->document_uri = g_strdup (document_uri);
+}
+
+static void
 reformat (GtkAction *old,
           GtkAction *action,
           gpointer user_data)
 {
        EMailDisplayPopupTextHighlight *th_extension;
-       WebKitDOMDocument *doc;
-       WebKitDOMDOMWindow *window;
-       WebKitDOMElement *frame_element;
        SoupURI *soup_uri;
        GHashTable *query;
        gchar *uri;
 
        th_extension = E_MAIL_DISPLAY_POPUP_TEXT_HIGHLIGHT (user_data);
-       doc = th_extension->document;
-       if (!doc)
+
+       if (g_atomic_int_get (&th_extension->updating))
                return;
 
-       uri = webkit_dom_document_get_document_uri (doc);
-       soup_uri = soup_uri_new (uri);
-       g_free (uri);
+       if (th_extension->document_uri)
+               soup_uri = soup_uri_new (th_extension->document_uri);
+       else
+               soup_uri = NULL;
 
        if (!soup_uri)
-               goto exit;
+               return;
 
        if (!soup_uri->query) {
                soup_uri_free (soup_uri);
-               goto exit;
+               return;
        }
 
        query = soup_form_decode (soup_uri->query);
@@ -148,17 +158,10 @@ reformat (GtkAction *old,
        uri = soup_uri_to_string (soup_uri, FALSE);
        soup_uri_free (soup_uri);
 
-       /* Get frame's window and from the window the actual <iframe> element */
-       window = webkit_dom_document_get_default_view (doc);
-       frame_element = webkit_dom_dom_window_get_frame_element (window);
-       webkit_dom_html_iframe_element_set_src (
-               WEBKIT_DOM_HTML_IFRAME_ELEMENT (frame_element), uri);
+       e_web_view_set_document_iframe_src (E_WEB_VIEW (e_extension_get_extensible (E_EXTENSION 
(th_extension))),
+               th_extension->document_uri, uri);
 
        g_free (uri);
-
-       /* The frame has been reloaded, the document pointer is invalid now */
-exit:
-       th_extension->document = NULL;
 }
 
 static GtkActionGroup *
@@ -215,10 +218,12 @@ create_group (EMailDisplayPopupExtension *extension)
                                NULL, NULL, action_index);
                action_index++;
                gtk_action_group_add_action (group, GTK_ACTION (action));
-               g_signal_connect (
-                       action, "changed",
-                       G_CALLBACK (reformat), extension);
-               gtk_radio_action_set_group (action, radio_group);
+               if (radio_group)
+                       gtk_radio_action_set_group (action, radio_group);
+               else
+                       g_signal_connect (
+                               action, "changed",
+                               G_CALLBACK (reformat), extension);
                radio_group = gtk_radio_action_get_group (action);
 
                g_object_unref (action);
@@ -246,11 +251,13 @@ create_group (EMailDisplayPopupExtension *extension)
                                NULL, NULL, action_index);
                action_index++;
                gtk_action_group_add_action (group, GTK_ACTION (action));
-               g_signal_connect (
-                       action, "changed",
-                       G_CALLBACK (reformat), extension);
 
-               gtk_radio_action_set_group (action, radio_group);
+               if (radio_group)
+                       gtk_radio_action_set_group (action, radio_group);
+               else
+                       g_signal_connect (
+                               action, "changed",
+                               G_CALLBACK (reformat), extension);
                radio_group = gtk_radio_action_get_group (action);
 
                g_object_unref (action);
@@ -273,33 +280,26 @@ create_group (EMailDisplayPopupExtension *extension)
 
 static void
 update_actions (EMailDisplayPopupExtension *extension,
-                WebKitHitTestResult *context)
+               const gchar *popup_document_uri)
 {
        EMailDisplayPopupTextHighlight *th_extension;
-       WebKitDOMNode *node;
-       WebKitDOMDocument *document;
-       gchar *uri;
 
        th_extension = E_MAIL_DISPLAY_POPUP_TEXT_HIGHLIGHT (extension);
 
-       if (th_extension->action_group == NULL) {
+       if (!th_extension->action_group)
                th_extension->action_group = create_group (extension);
-       }
 
-       th_extension->document = NULL;
-       g_object_get (G_OBJECT (context), "inner-node", &node, NULL);
-       document = webkit_dom_node_get_owner_document (node);
-       uri = webkit_dom_document_get_document_uri (document);
+       set_document_uri (th_extension, popup_document_uri);
 
        /* If the part below context menu was made by text-highlight formatter,
         * then try to check what formatter it's using at the moment and set
         * it as active in the popup menu */
-       if (uri && strstr (uri, ".text-highlight") != NULL) {
+       if (th_extension->document_uri && strstr (th_extension->document_uri, ".text-highlight") != NULL) {
                SoupURI *soup_uri;
                gtk_action_group_set_visible (
                        th_extension->action_group, TRUE);
 
-               soup_uri = soup_uri_new (uri);
+               soup_uri = soup_uri_new (th_extension->document_uri);
                if (soup_uri && soup_uri->query) {
                        GHashTable *query = soup_form_decode (soup_uri->query);
                        gchar *highlighter;
@@ -310,31 +310,23 @@ update_actions (EMailDisplayPopupExtension *extension,
                                        th_extension->action_group, highlighter);
                                if (action) {
                                        gint value;
+                                       g_atomic_int_add (&th_extension->updating, 1);
                                        g_object_get (
                                                G_OBJECT (action), "value",
                                                &value, NULL);
                                        gtk_radio_action_set_current_value (
                                                GTK_RADIO_ACTION (action), value);
+                                       g_atomic_int_add (&th_extension->updating, -1);
                                }
                        }
                        g_hash_table_destroy (query);
                }
 
-               if (soup_uri) {
+               if (soup_uri)
                        soup_uri_free (soup_uri);
-               }
-
        } else {
-               gtk_action_group_set_visible (
-                       th_extension->action_group, FALSE);
+               gtk_action_group_set_visible (th_extension->action_group, FALSE);
        }
-
-       /* Set the th_extension->document AFTER changing the active action to
-        * prevent the reformat() from doing some crazy reformatting
-        * (reformat() returns immediatelly when th_extension->document is NULL) */
-       th_extension->document = document;
-
-       g_free (uri);
 }
 
 void
@@ -363,11 +355,11 @@ e_mail_display_popup_extension_interface_init (EMailDisplayPopupExtensionInterfa
 void
 e_mail_display_popup_text_highlight_class_finalize (EMailDisplayPopupTextHighlightClass *klass)
 {
-
 }
 
 static void
 e_mail_display_popup_text_highlight_init (EMailDisplayPopupTextHighlight *extension)
 {
        extension->action_group = NULL;
+       extension->document_uri = NULL;
 }
diff --git a/modules/vcard-inline/e-mail-formatter-vcard.c b/modules/vcard-inline/e-mail-formatter-vcard.c
index f13d677..40e573c 100644
--- a/modules/vcard-inline/e-mail-formatter-vcard.c
+++ b/modules/vcard-inline/e-mail-formatter-vcard.c
@@ -146,9 +146,12 @@ mail_formatter_vcard_format (EMailFormatterExtension *extension,
                str = g_strdup_printf (
                        "<button type=\"button\" "
                                "name=\"set-display-mode\" "
+                               "id=\"%s\" "
                                "class=\"org-gnome-vcard-display-mode-button\" "
                                "value=\"%d\" "
+                               "style=\"margin-left: 0px\""
                                "accesskey=\"%s\">%s</button>",
+                       e_mail_part_get_id (part),
                        mode, access_key, html_label);
                g_output_stream_write_all (
                        stream, str, strlen (str), NULL, cancellable, NULL);
@@ -166,8 +169,10 @@ mail_formatter_vcard_format (EMailFormatterExtension *extension,
                                "class=\"org-gnome-vcard-save-button\" "
                                "value=\"%s\" "
                                "accesskey=\"%s\">%s</button><br>"
-                       "<iframe width=\"100%%\" height=\"auto\" frameborder=\"0\""
-                               "src=\"%s\" name=\"%s\"></iframe>"
+                               "<iframe width=\"100%%\" height=\"auto\" "
+                               " class=\"-e-mail-formatter-frame-color -e-web-view-background-color\" "
+                               " style=\"border: 1px solid;\""
+                               " src=\"%s\" name=\"%s\"></iframe>"
                        "</div>",
                        e_mail_part_get_id (part),
                        access_key, html_label, uri,
diff --git a/modules/vcard-inline/e-mail-part-vcard.c b/modules/vcard-inline/e-mail-part-vcard.c
index f164ab6..f5729a6 100644
--- a/modules/vcard-inline/e-mail-part-vcard.c
+++ b/modules/vcard-inline/e-mail-part-vcard.c
@@ -32,6 +32,12 @@
 
 struct _EMailPartVCardPrivate {
        gint placeholder;
+
+       guint display_mode_toggled_signal_id;
+       guint save_vcard_button_pressed_signal_id;
+
+       GDBusProxy *web_extension;
+       guint64 page_id;
 };
 
 G_DEFINE_DYNAMIC_TYPE (
@@ -85,8 +91,12 @@ client_connect_cb (GObject *source_object,
 }
 
 static void
-save_vcard_cb (WebKitDOMEventTarget *button,
-               WebKitDOMEvent *event,
+save_vcard_cb (GDBusConnection *connection,
+               const gchar *sender_name,
+               const gchar *object_path,
+               const gchar *interface_name,
+               const gchar *signal_name,
+               GVariant *parameters,
                EMailPartVCard *vcard_part)
 {
        EShell *shell;
@@ -94,9 +104,19 @@ save_vcard_cb (WebKitDOMEventTarget *button,
        ESourceRegistry *registry;
        ESourceSelector *selector;
        GSList *contact_list;
-       const gchar *extension_name;
+       const gchar *extension_name, *button_value, *part_id;
        GtkWidget *dialog;
 
+       if (g_strcmp0 (signal_name, "VCardInlineSaveButtonPressed") != 0)
+               return;
+
+       g_variant_get (parameters, "(&s)", &button_value);
+
+       part_id = e_mail_part_get_id (E_MAIL_PART (vcard_part));
+
+       if (!strstr (part_id, button_value))
+               return;
+
        shell = e_shell_get_default ();
        registry = e_shell_get_registry (shell);
        extension_name = E_SOURCE_EXTENSION_ADDRESS_BOOK;
@@ -131,8 +151,12 @@ save_vcard_cb (WebKitDOMEventTarget *button,
 }
 
 static void
-display_mode_toggle_cb (WebKitDOMEventTarget *button,
-                        WebKitDOMEvent *event,
+display_mode_toggle_cb (GDBusConnection *connection,
+                        const gchar *sender_name,
+                        const gchar *object_path,
+                        const gchar *interface_name,
+                        const gchar *signal_name,
+                        GVariant *parameters,
                         EMailPartVCard *vcard_part)
 {
        EABContactDisplayMode mode;
@@ -140,47 +164,54 @@ display_mode_toggle_cb (WebKitDOMEventTarget *button,
        gchar *html_label;
        gchar *access_key;
        const gchar *part_id;
+       const gchar *button_id;
+
+       if (g_strcmp0 (signal_name, "VCardInlineDisplayModeToggled") != 0)
+               return;
+
+       if (!vcard_part->priv->web_extension)
+               return;
+
+       g_variant_get (parameters, "(&s)", &button_id);
 
        part_id = e_mail_part_get_id (E_MAIL_PART (vcard_part));
 
+       if (!strstr (part_id, button_id))
+               return;
+
        mode = eab_contact_formatter_get_display_mode (vcard_part->formatter);
        if (mode == EAB_CONTACT_DISPLAY_RENDER_NORMAL) {
                mode = EAB_CONTACT_DISPLAY_RENDER_COMPACT;
 
                html_label = e_mail_formatter_parse_html_mnemonics (
                                _("Show F_ull vCard"), &access_key);
-
-               webkit_dom_html_element_set_inner_html (
-                       WEBKIT_DOM_HTML_ELEMENT (button),
-                       html_label, NULL);
-               if (access_key) {
-                       webkit_dom_html_element_set_access_key (
-                               WEBKIT_DOM_HTML_ELEMENT (button),
-                               access_key);
-                       g_free (access_key);
-               }
-
-               g_free (html_label);
-
        } else {
                mode = EAB_CONTACT_DISPLAY_RENDER_NORMAL;
 
                html_label = e_mail_formatter_parse_html_mnemonics (
                                _("Show Com_pact vCard"), &access_key);
-
-               webkit_dom_html_element_set_inner_html (
-                       WEBKIT_DOM_HTML_ELEMENT (button),
-                       html_label, NULL);
-               if (access_key) {
-                       webkit_dom_html_element_set_access_key (
-                               WEBKIT_DOM_HTML_ELEMENT (button),
-                               access_key);
-                       g_free (access_key);
-               }
-
-               g_free (html_label);
        }
 
+       g_dbus_proxy_call (
+               vcard_part->priv->web_extension,
+               "VCardInlineUpdateButton",
+               g_variant_new (
+                       "(tsss)",
+                       vcard_part->priv->page_id,
+                       button_id,
+                       html_label,
+                       access_key),
+               G_DBUS_CALL_FLAGS_NONE,
+               -1,
+               NULL,
+               NULL,
+               NULL);
+
+       if (access_key)
+               g_free (access_key);
+
+       g_free (html_label);
+
        eab_contact_formatter_set_display_mode (vcard_part->formatter, mode);
 
        uri = e_mail_part_build_uri (
@@ -188,8 +219,19 @@ display_mode_toggle_cb (WebKitDOMEventTarget *button,
                "part_id", G_TYPE_STRING, part_id,
                "mode", G_TYPE_INT, E_MAIL_FORMATTER_MODE_RAW, NULL);
 
-       webkit_dom_html_iframe_element_set_src (
-               WEBKIT_DOM_HTML_IFRAME_ELEMENT (vcard_part->iframe), uri);
+       g_dbus_proxy_call (
+               vcard_part->priv->web_extension,
+               "VCardInlineSetIFrameSrc",
+               g_variant_new (
+                       "(tss)",
+                       vcard_part->priv->page_id,
+                       button_id,
+                       uri),
+               G_DBUS_CALL_FLAGS_NONE,
+               -1,
+               NULL,
+               NULL,
+               NULL);
 
        g_free (uri);
 }
@@ -202,11 +244,22 @@ mail_part_vcard_dispose (GObject *object)
        g_clear_object (&part->contact_display);
        g_clear_object (&part->message_label);
        g_clear_object (&part->formatter);
-       g_clear_object (&part->iframe);
-       g_clear_object (&part->save_button);
-       g_clear_object (&part->toggle_button);
        g_clear_object (&part->folder);
 
+       if (part->priv->display_mode_toggled_signal_id > 0) {
+               g_dbus_connection_signal_unsubscribe (
+                       g_dbus_proxy_get_connection (part->priv->web_extension),
+                       part->priv->display_mode_toggled_signal_id);
+               part->priv->display_mode_toggled_signal_id = 0;
+       }
+
+       if (part->priv->save_vcard_button_pressed_signal_id > 0) {
+               g_dbus_connection_signal_unsubscribe (
+                       g_dbus_proxy_get_connection (part->priv->web_extension),
+                       part->priv->save_vcard_button_pressed_signal_id);
+               part->priv->save_vcard_button_pressed_signal_id = 0;
+       }
+
        /* Chain up to parent's dispose() method. */
        G_OBJECT_CLASS (e_mail_part_vcard_parent_class)->dispose (object);
 }
@@ -249,66 +302,55 @@ mail_part_vcard_constructed (GObject *object)
 
 static void
 mail_part_vcard_bind_dom_element (EMailPart *part,
-                                  WebKitDOMElement *element)
+                                  GDBusProxy *evolution_web_extension,
+                                  guint64 page_id,
+                                  const gchar *element_id)
 {
        EMailPartVCard *vcard_part;
-       WebKitDOMNodeList *list;
-       WebKitDOMElement *iframe;
-       WebKitDOMElement *toggle_button;
-       WebKitDOMElement *save_button;
 
        vcard_part = E_MAIL_PART_VCARD (part);
 
-       /* IFRAME */
-       list = webkit_dom_element_get_elements_by_tag_name (
-               element, "iframe");
-       if (webkit_dom_node_list_get_length (list) != 1) {
-               g_object_unref (list);
-               return;
-       }
-       iframe = WEBKIT_DOM_ELEMENT (webkit_dom_node_list_item (list, 0));
-       g_clear_object (&vcard_part->iframe);
-       vcard_part->iframe = iframe;
-       g_object_unref (list);
-
-       /* TOGGLE DISPLAY MODE BUTTON */
-       list = webkit_dom_element_get_elements_by_class_name (
-               element, "org-gnome-vcard-display-mode-button");
-       if (webkit_dom_node_list_get_length (list) != 1) {
-               g_object_unref (list);
-               return;
-       }
-       toggle_button = WEBKIT_DOM_ELEMENT (webkit_dom_node_list_item (list, 0));
-       g_clear_object (&vcard_part->toggle_button);
-       vcard_part->toggle_button = toggle_button;
-       g_object_unref (list);
-
-       /* SAVE TO ADDRESSBOOK BUTTON */
-       list = webkit_dom_element_get_elements_by_class_name (
-               element, "org-gnome-vcard-save-button");
-       if (webkit_dom_node_list_get_length (list) != 1) {
-               g_object_unref (list);
-               return;
-       }
-       save_button = WEBKIT_DOM_ELEMENT (webkit_dom_node_list_item (list, 0));
-       g_clear_object (&vcard_part->save_button);
-       vcard_part->save_button = save_button;
-       g_object_unref (list);
-
-       webkit_dom_event_target_add_event_listener (
-               WEBKIT_DOM_EVENT_TARGET (toggle_button),
-               "click", G_CALLBACK (display_mode_toggle_cb),
-               FALSE, vcard_part);
-
-       webkit_dom_event_target_add_event_listener (
-               WEBKIT_DOM_EVENT_TARGET (save_button),
-               "click", G_CALLBACK (save_vcard_cb),
-               FALSE, vcard_part);
-
-       /* Bind collapse buttons for contact lists. */
-       eab_contact_formatter_bind_dom (
-               webkit_dom_html_iframe_element_get_content_document (
-                       WEBKIT_DOM_HTML_IFRAME_ELEMENT (iframe)));
+       vcard_part->priv->web_extension = evolution_web_extension;
+       vcard_part->priv->page_id = page_id;
+
+       vcard_part->priv->display_mode_toggled_signal_id =
+               g_dbus_connection_signal_subscribe (
+                       g_dbus_proxy_get_connection (evolution_web_extension),
+                       g_dbus_proxy_get_name (evolution_web_extension),
+                       g_dbus_proxy_get_interface_name (evolution_web_extension),
+                       "VCardInlineDisplayModeToggled",
+                       g_dbus_proxy_get_object_path (evolution_web_extension),
+                       NULL,
+                       G_DBUS_SIGNAL_FLAGS_NONE,
+                       (GDBusSignalCallback) display_mode_toggle_cb,
+                       vcard_part,
+                       NULL);
+
+       vcard_part->priv->save_vcard_button_pressed_signal_id =
+               g_dbus_connection_signal_subscribe (
+                       g_dbus_proxy_get_connection (evolution_web_extension),
+                       g_dbus_proxy_get_name (evolution_web_extension),
+                       g_dbus_proxy_get_interface_name (evolution_web_extension),
+                       "VCardInlineSaveButtonPressed",
+                       g_dbus_proxy_get_object_path (evolution_web_extension),
+                       NULL,
+                       G_DBUS_SIGNAL_FLAGS_NONE,
+                       (GDBusSignalCallback) save_vcard_cb,
+                       vcard_part,
+                       NULL);
+
+       g_dbus_proxy_call (
+               vcard_part->priv->web_extension,
+               "VCardInlineBindDOM",
+               g_variant_new (
+                       "(ts)",
+                       vcard_part->priv->page_id,
+                       element_id),
+               G_DBUS_CALL_FLAGS_NONE,
+               -1,
+               NULL,
+               NULL,
+               NULL);
 }
 
 static void
diff --git a/modules/vcard-inline/e-mail-part-vcard.h b/modules/vcard-inline/e-mail-part-vcard.h
index d9409c5..9aeb595 100644
--- a/modules/vcard-inline/e-mail-part-vcard.h
+++ b/modules/vcard-inline/e-mail-part-vcard.h
@@ -21,7 +21,6 @@
 #include <em-format/e-mail-part.h>
 
 #include <addressbook/gui/widgets/eab-contact-formatter.h>
-#include <webkit/webkitdom.h>
 
 /* Standard GObject macros */
 #define E_TYPE_MAIL_PART_VCARD \
@@ -57,9 +56,6 @@ struct _EMailPartVCard {
        GtkWidget *message_label;
 
        EABContactFormatter *formatter;
-       WebKitDOMElement *iframe;
-       WebKitDOMElement *toggle_button;
-       WebKitDOMElement *save_button;
 
        CamelFolder *folder;
        gchar *message_uid;
diff --git a/modules/webkit-editor/Makefile.am b/modules/webkit-editor/Makefile.am
new file mode 100644
index 0000000..1844797
--- /dev/null
+++ b/modules/webkit-editor/Makefile.am
@@ -0,0 +1,31 @@
+SUBDIRS = . web-extension
+
+module_LTLIBRARIES = module-webkit-editor.la
+
+module_webkit_editor_la_CPPFLAGS =             \
+       $(AM_CPPFLAGS)                                  \
+       -I$(top_srcdir)                                 \
+       -DEVOLUTION_WEB_EXTENSIONS_WEBKIT_EDITOR_DIR=\""$(webextensionswebkiteditordir)"\" \
+       -DG_LOG_DOMAIN=\"webkit-editor\"        \
+       $(EVOLUTION_DATA_SERVER_CFLAGS)                 \
+       $(GNOME_PLATFORM_CFLAGS)                        \
+       $(CODE_COVERAGE_CFLAGS)                         \
+       $(NULL)
+
+module_webkit_editor_la_SOURCES = \
+       evolution-module-webkit-editor.c                \
+       e-webkit-editor-extension.c                     \
+       e-webkit-editor-extension.h                     \
+       e-webkit-editor.c                               \
+       e-webkit-editor.h
+
+module_webkit_editor_la_LIBADD = \
+       $(top_builddir)/e-util/libevolution-util.la     \
+       $(EVOLUTION_DATA_SERVER_LIBS)                   \
+       $(GNOME_PLATFORM_LIBS)                          \
+       $(NULL)
+
+module_webkit_editor_la_LDFLAGS = \
+       -module -avoid-version $(NO_UNDEFINED) $(CODE_COVERAGE_LDFLAGS)
+
+-include $(top_srcdir)/git.mk
diff --git a/modules/webkit-editor/e-webkit-editor-extension.c 
b/modules/webkit-editor/e-webkit-editor-extension.c
new file mode 100644
index 0000000..e05ee20
--- /dev/null
+++ b/modules/webkit-editor/e-webkit-editor-extension.c
@@ -0,0 +1,101 @@
+/*
+ * Copyright (C) 2016 Red Hat, Inc. (www.redhat.com)
+ *
+ * This library 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 library 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 Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "e-webkit-editor-extension.h"
+#include "e-webkit-editor.h"
+
+#include <e-util/e-util.h>
+
+#define E_WEBKIT_EDITOR_EXTENSION_GET_PRIVATE(obj) \
+       (G_TYPE_INSTANCE_GET_PRIVATE \
+       ((obj), E_TYPE_WEBKIT_EDITOR_EXTENSION, EWebKitEditorExtensionPrivate))
+
+struct _EWebKitEditorExtensionPrivate {
+       EWebKitEditor *wk_editor;
+};
+
+G_DEFINE_DYNAMIC_TYPE (
+       EWebKitEditorExtension,
+       e_webkit_editor_extension,
+       E_TYPE_EXTENSION)
+
+static void
+e_webkit_editor_extension_init (EWebKitEditorExtension *editor_extension)
+{
+       editor_extension->priv = E_WEBKIT_EDITOR_EXTENSION_GET_PRIVATE (editor_extension);
+
+       editor_extension->priv->wk_editor = g_object_ref_sink (e_webkit_editor_new ());
+}
+
+static void
+webkit_editor_extension_constructed (GObject *object)
+{
+       EWebKitEditorExtensionPrivate *priv;
+       EExtensible *extensible;
+
+       /* Chain up to parent's constructed() method. */
+       G_OBJECT_CLASS (e_webkit_editor_extension_parent_class)->constructed (object);
+
+       priv = E_WEBKIT_EDITOR_EXTENSION_GET_PRIVATE (object);
+       extensible = e_extension_get_extensible (E_EXTENSION (object));
+
+       e_html_editor_register_content_editor (E_HTML_EDITOR (extensible),
+               DEFAULT_CONTENT_EDITOR_NAME, E_CONTENT_EDITOR (priv->wk_editor));
+}
+
+static void
+webkit_editor_extension_dispose (GObject *object)
+{
+       EWebKitEditorExtensionPrivate *priv;
+
+       priv = E_WEBKIT_EDITOR_EXTENSION_GET_PRIVATE (object);
+
+       g_clear_object (&priv->wk_editor);
+
+       /* Chain up to parent's dispose() method. */
+       G_OBJECT_CLASS (e_webkit_editor_extension_parent_class)->dispose (object);
+}
+
+static void
+e_webkit_editor_extension_class_init (EWebKitEditorExtensionClass *class)
+{
+       EExtensionClass *extension_class;
+       GObjectClass *object_class;
+
+       g_type_class_add_private (class, sizeof (EWebKitEditorExtensionPrivate));
+
+       extension_class = E_EXTENSION_CLASS (class);
+       extension_class->extensible_type = E_TYPE_HTML_EDITOR;
+
+       object_class = G_OBJECT_CLASS (class);
+       object_class->dispose = webkit_editor_extension_dispose;
+       object_class->constructed = webkit_editor_extension_constructed;
+}
+
+static void
+e_webkit_editor_extension_class_finalize (EWebKitEditorExtensionClass *class)
+{
+}
+
+void
+e_webkit_editor_extension_type_register (GTypeModule *type_module)
+{
+       e_webkit_editor_extension_register_type (type_module);
+}
diff --git a/modules/webkit-editor/e-webkit-editor-extension.h 
b/modules/webkit-editor/e-webkit-editor-extension.h
new file mode 100644
index 0000000..ff5d781
--- /dev/null
+++ b/modules/webkit-editor/e-webkit-editor-extension.h
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2016 Red Hat, Inc. (www.redhat.com)
+ *
+ * This library 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 library 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 Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef E_WEBKIT_EDITOR_EXTENSION_H
+#define E_WEBKIT_EDITOR_EXTENSION_H
+
+#include <libebackend/libebackend.h>
+
+/* Standard GObject macros */
+#define E_TYPE_WEBKIT_EDITOR_EXTENSION \
+       (e_webkit_editor_extension_get_type ())
+#define E_WEBKIT_EDITOR_EXTENSION(obj) \
+       (G_TYPE_CHECK_INSTANCE_CAST \
+       ((obj), E_TYPE_WEBKIT_EDITOR_EXTENSION, EWebKitEditorExtension))
+#define E_WEBKIT_EDITOR_EXTENSION_CLASS(cls) \
+       (G_TYPE_CHECK_CLASS_CAST \
+       ((cls), E_TYPE_WEBKIT_EDITOR_EXTENSION, EWebKitEditorExtensionClass))
+#define E_IS_WEBKIT_EDITOR_EXTENSION(obj) \
+       (G_TYPE_CHECK_INSTANCE_TYPE \
+       ((obj), E_TYPE_WEBKIT_EDITOR_EXTENSION))
+#define E_IS_WEBKIT_EDITOR_EXTENSION_CLASS(cls) \
+       (G_TYPE_CHECK_CLASS_TYPE \
+       ((cls), E_TYPE_WEBKIT_EDITOR_EXTENSION))
+#define E_WEBKIT_EDITOR_EXTENSION_GET_CLASS(obj) \
+       (G_TYPE_INSTANCE_GET_CLASS \
+       ((obj), E_TYPE_WEBKIT_EDITOR_EXTENSION, EWebKitEditorExtensionClass))
+
+G_BEGIN_DECLS
+
+typedef struct _EWebKitEditorExtension EWebKitEditorExtension;
+typedef struct _EWebKitEditorExtensionClass EWebKitEditorExtensionClass;
+typedef struct _EWebKitEditorExtensionPrivate EWebKitEditorExtensionPrivate;
+
+struct _EWebKitEditorExtension {
+       EExtension parent;
+
+       EWebKitEditorExtensionPrivate *priv;
+};
+
+struct _EWebKitEditorExtensionClass {
+       EExtensionClass parent_class;
+};
+
+GType          e_webkit_editor_extension_get_type      (void) G_GNUC_CONST;
+void           e_webkit_editor_extension_type_register (GTypeModule *type_module);
+
+G_END_DECLS
+
+#endif /* E_WEBKIT_EDITOR_EXTENSION_H */
diff --git a/modules/webkit-editor/e-webkit-editor.c b/modules/webkit-editor/e-webkit-editor.c
new file mode 100644
index 0000000..c984a57
--- /dev/null
+++ b/modules/webkit-editor/e-webkit-editor.c
@@ -0,0 +1,6291 @@
+/*
+ * Copyright (C) 2016 Red Hat, Inc. (www.redhat.com)
+ *
+ * This library 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 library 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 Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "e-webkit-editor.h"
+
+#include "web-extension/e-editor-web-extension-names.h"
+
+#include <e-util/e-util.h>
+#include <string.h>
+
+#define E_WEBKIT_EDITOR_GET_PRIVATE(obj) \
+       (G_TYPE_INSTANCE_GET_PRIVATE \
+       ((obj), E_TYPE_WEBKIT_EDITOR, EWebKitEditorPrivate))
+
+/* FIXME WK2 Move to e-content-editor? */
+#define UNICODE_NBSP "\xc2\xa0"
+#define SPACES_PER_LIST_LEVEL 3
+#define SPACES_ORDERED_LIST_FIRST_LEVEL 6
+
+enum {
+       PROP_0,
+       PROP_WEB_EXTENSION, /* for test purposes */
+       PROP_CAN_COPY,
+       PROP_CAN_CUT,
+       PROP_CAN_PASTE,
+       PROP_CAN_REDO,
+       PROP_CAN_UNDO,
+       PROP_CHANGED,
+       PROP_EDITABLE,
+       PROP_HTML_MODE,
+       PROP_SPELL_CHECK_ENABLED,
+       PROP_SPELL_CHECKER,
+
+       PROP_ALIGNMENT,
+       PROP_BACKGROUND_COLOR,
+       PROP_BLOCK_FORMAT,
+       PROP_BOLD,
+       PROP_FONT_COLOR,
+       PROP_FONT_NAME,
+       PROP_FONT_SIZE,
+       PROP_INDENTED,
+       PROP_ITALIC,
+       PROP_MONOSPACED,
+       PROP_STRIKETHROUGH,
+       PROP_SUBSCRIPT,
+       PROP_SUPERSCRIPT,
+       PROP_UNDERLINE
+};
+
+struct _EWebKitEditorPrivate {
+       EContentEditorInitializedCallback initialized_callback;
+       gpointer initialized_user_data;
+
+       GDBusProxy *web_extension;
+       guint web_extension_watch_name_id;
+       guint web_extension_selection_changed_cb_id;
+       guint web_extension_content_changed_cb_id;
+       guint web_extension_undo_redo_state_changed_cb_id;
+
+       gboolean html_mode;
+       gboolean changed;
+       gboolean can_copy;
+       gboolean can_cut;
+       gboolean can_paste;
+       gboolean can_undo;
+       gboolean can_redo;
+
+       gboolean emit_load_finished_when_extension_is_ready;
+       gboolean reload_in_progress;
+       gboolean copy_paste_clipboard_in_view;
+       gboolean copy_paste_primary_in_view;
+       gboolean copy_cut_actions_triggered;
+       gboolean pasting_primary_clipboard;
+       gboolean pasting_from_itself_extension_value;
+
+       guint32 style_flags;
+       gboolean is_indented;
+
+       GdkRGBA *background_color;
+       GdkRGBA *font_color;
+
+       gchar *font_name;
+
+       guint font_size;
+
+       EContentEditorBlockFormat block_format;
+       EContentEditorAlignment alignment;
+
+       gchar *current_user_stylesheet;
+
+       WebKitLoadEvent webkit_load_event;
+
+       GQueue *post_reload_operations;
+
+       GSettings *mail_settings;
+       GSettings *font_settings;
+       GSettings *aliasing_settings;
+
+       GHashTable *old_settings;
+
+       ESpellChecker *spell_checker;
+       gboolean spell_check_enabled;
+
+       gulong owner_change_primary_clipboard_cb_id;
+       gulong owner_change_clipboard_cb_id;
+
+       WebKitFindController *find_controller; /* not referenced; set to non-NULL only if the search is in 
progress */
+       gboolean performing_replace_all;
+       guint replaced_count;
+       gchar *replace_with;
+       gulong found_text_handler_id;
+       gulong failed_to_find_text_handler_id;
+};
+
+static const GdkRGBA black = { 0, 0, 0, 1 };
+static const GdkRGBA white = { 1, 1, 1, 1 };
+static const GdkRGBA transparent = { 0, 0, 0, 0 };
+
+typedef void (*PostReloadOperationFunc) (EWebKitEditor *wk_editor, gpointer data, 
EContentEditorInsertContentFlags flags);
+
+typedef struct {
+       PostReloadOperationFunc func;
+       EContentEditorInsertContentFlags flags;
+       gpointer data;
+       GDestroyNotify data_free_func;
+} PostReloadOperation;
+
+static void e_webkit_editor_content_editor_init (EContentEditorInterface *iface);
+
+G_DEFINE_TYPE_WITH_CODE (
+       EWebKitEditor,
+       e_webkit_editor,
+       WEBKIT_TYPE_WEB_VIEW,
+       G_IMPLEMENT_INTERFACE (
+               E_TYPE_CONTENT_EDITOR,
+               e_webkit_editor_content_editor_init));
+
+EWebKitEditor *
+e_webkit_editor_new (void)
+{
+       return g_object_new (E_TYPE_WEBKIT_EDITOR, NULL);
+}
+
+static void
+webkit_editor_can_paste_cb (WebKitWebView *view,
+                            GAsyncResult *result,
+                            EWebKitEditor *wk_editor)
+{
+       gboolean value;
+
+       value = webkit_web_view_can_execute_editing_command_finish (view, result, NULL);
+
+       if (wk_editor->priv->can_paste != value) {
+               wk_editor->priv->can_paste = value;
+               g_object_notify (G_OBJECT (wk_editor), "can-paste");
+       }
+}
+
+static gboolean
+webkit_editor_can_paste (EWebKitEditor *wk_editor)
+{
+       g_return_val_if_fail (E_IS_WEBKIT_EDITOR (wk_editor), FALSE);
+
+       return wk_editor->priv->can_paste;
+}
+
+static void
+webkit_editor_can_cut_cb (WebKitWebView *view,
+                                  GAsyncResult *result,
+                                  EWebKitEditor *wk_editor)
+{
+       gboolean value;
+
+       value = webkit_web_view_can_execute_editing_command_finish (view, result, NULL);
+
+       if (wk_editor->priv->can_cut != value) {
+               wk_editor->priv->can_cut = value;
+               g_object_notify (G_OBJECT (wk_editor), "can-cut");
+       }
+}
+
+static gboolean
+webkit_editor_can_cut (EWebKitEditor *wk_editor)
+{
+       g_return_val_if_fail (E_IS_WEBKIT_EDITOR (wk_editor), FALSE);
+
+       return wk_editor->priv->can_cut;
+}
+
+static void
+webkit_editor_can_copy_cb (WebKitWebView *view,
+                           GAsyncResult *result,
+                           EWebKitEditor *wk_editor)
+{
+       gboolean value;
+
+       value = webkit_web_view_can_execute_editing_command_finish (view, result, NULL);
+
+       if (wk_editor->priv->can_copy != value) {
+               wk_editor->priv->can_copy = value;
+               /* This means that we have an active selection thus the primary
+                * clipboard content is from composer. */
+               if (value)
+                       wk_editor->priv->copy_paste_primary_in_view = TRUE;
+               /* FIXME notify web extension about pasting content from itself */
+               g_object_notify (G_OBJECT (wk_editor), "can-copy");
+       }
+}
+
+static gboolean
+webkit_editor_can_copy (EWebKitEditor *wk_editor)
+{
+       g_return_val_if_fail (E_IS_WEBKIT_EDITOR (wk_editor), FALSE);
+
+       return wk_editor->priv->can_copy;
+}
+
+static gboolean
+webkit_editor_get_changed (EWebKitEditor *wk_editor)
+{
+       g_return_val_if_fail (E_IS_WEBKIT_EDITOR (wk_editor), FALSE);
+
+       return wk_editor->priv->changed;
+}
+
+static void
+webkit_editor_set_changed (EWebKitEditor *wk_editor,
+                           gboolean changed)
+{
+       g_return_if_fail (E_IS_WEBKIT_EDITOR (wk_editor));
+
+       if (wk_editor->priv->changed == changed)
+               return;
+
+       wk_editor->priv->changed = changed;
+
+       g_object_notify (G_OBJECT (wk_editor), "changed");
+}
+
+static void
+webkit_editor_set_can_undo (EWebKitEditor *wk_editor,
+                           gboolean can_undo)
+{
+       g_return_if_fail (E_IS_WEBKIT_EDITOR (wk_editor));
+
+       if ((wk_editor->priv->can_undo ? 1 : 0) == (can_undo ? 1 : 0))
+               return;
+
+       wk_editor->priv->can_undo = can_undo;
+
+       g_object_notify (G_OBJECT (wk_editor), "can-undo");
+}
+
+static void
+webkit_editor_set_can_redo (EWebKitEditor *wk_editor,
+                           gboolean can_redo)
+{
+       g_return_if_fail (E_IS_WEBKIT_EDITOR (wk_editor));
+
+       if ((wk_editor->priv->can_redo ? 1 : 0) == (can_redo ? 1 : 0))
+               return;
+
+       wk_editor->priv->can_redo = can_redo;
+
+       g_object_notify (G_OBJECT (wk_editor), "can-redo");
+}
+
+static void
+web_extension_content_changed_cb (GDBusConnection *connection,
+                                  const gchar *sender_name,
+                                  const gchar *object_path,
+                                  const gchar *interface_name,
+                                  const gchar *signal_name,
+                                  GVariant *parameters,
+                                  EWebKitEditor *wk_editor)
+{
+       if (g_strcmp0 (signal_name, "ContentChanged") != 0)
+               return;
+
+       if (parameters) {
+               guint64 page_id = 0;
+
+               g_variant_get (parameters, "(t)", &page_id);
+
+               if (page_id == webkit_web_view_get_page_id (WEBKIT_WEB_VIEW (wk_editor)))
+                       webkit_editor_set_changed (wk_editor, TRUE);
+       }
+}
+
+static void
+web_extension_selection_changed_cb (GDBusConnection *connection,
+                                    const gchar *sender_name,
+                                    const gchar *object_path,
+                                    const gchar *interface_name,
+                                    const gchar *signal_name,
+                                    GVariant *parameters,
+                                    EWebKitEditor *wk_editor)
+{
+       guint64 page_id = 0;
+       gchar *font_color = NULL;
+       guint32 alignment, block_format, style_flags, font_size;
+       gboolean is_indented;
+
+       if (g_strcmp0 (signal_name, "SelectionChanged") != 0)
+               return;
+
+       if (!parameters)
+               return;
+
+       g_variant_get (
+               parameters,
+               "(tiibiis)",
+               &page_id,
+               &alignment,
+               &block_format,
+               &is_indented,
+               &style_flags,
+               &font_size,
+               &font_color);
+
+       if (page_id != webkit_web_view_get_page_id (WEBKIT_WEB_VIEW (wk_editor))) {
+               g_free (font_color);
+               return;
+       }
+
+       webkit_web_view_can_execute_editing_command (
+               WEBKIT_WEB_VIEW (wk_editor),
+               WEBKIT_EDITING_COMMAND_COPY,
+               NULL, /* cancellable */
+               (GAsyncReadyCallback) webkit_editor_can_copy_cb,
+               wk_editor);
+
+       webkit_web_view_can_execute_editing_command (
+               WEBKIT_WEB_VIEW (wk_editor),
+               WEBKIT_EDITING_COMMAND_CUT,
+               NULL, /* cancellable */
+               (GAsyncReadyCallback) webkit_editor_can_cut_cb,
+               wk_editor);
+
+       webkit_web_view_can_execute_editing_command (
+               WEBKIT_WEB_VIEW (wk_editor),
+               WEBKIT_EDITING_COMMAND_PASTE,
+               NULL, /* cancellable */
+               (GAsyncReadyCallback) webkit_editor_can_paste_cb,
+               wk_editor);
+
+       g_object_freeze_notify (G_OBJECT (wk_editor));
+
+       wk_editor->priv->alignment = alignment;
+       wk_editor->priv->block_format = block_format;
+       wk_editor->priv->is_indented = is_indented;
+       wk_editor->priv->style_flags = style_flags;
+       wk_editor->priv->font_size = font_size;
+
+       if (wk_editor->priv->html_mode) {
+               GdkRGBA color;
+
+               if (font_color && *font_color && gdk_rgba_parse (&color, font_color)) {
+                       if (wk_editor->priv->font_color)
+                               gdk_rgba_free (wk_editor->priv->font_color);
+                       wk_editor->priv->font_color = gdk_rgba_copy (&color);
+               }
+       }
+       g_free (font_color);
+
+       g_object_notify (G_OBJECT (wk_editor), "alignment");
+       g_object_notify (G_OBJECT (wk_editor), "block-format");
+       g_object_notify (G_OBJECT (wk_editor), "indented");
+
+       if (wk_editor->priv->html_mode) {
+               /* g_object_notify (G_OBJECT (wk_editor), "background-color"); */
+               g_object_notify (G_OBJECT (wk_editor), "bold");
+               /* g_object_notify (G_OBJECT (wk_editor), "font-name"); */
+               g_object_notify (G_OBJECT (wk_editor), "font-size");
+               g_object_notify (G_OBJECT (wk_editor), "font-color");
+               g_object_notify (G_OBJECT (wk_editor), "italic");
+               g_object_notify (G_OBJECT (wk_editor), "monospaced");
+               g_object_notify (G_OBJECT (wk_editor), "strikethrough");
+               g_object_notify (G_OBJECT (wk_editor), "subscript");
+               g_object_notify (G_OBJECT (wk_editor), "superscript");
+               g_object_notify (G_OBJECT (wk_editor), "underline");
+       }
+
+       g_object_thaw_notify (G_OBJECT (wk_editor));
+}
+
+static void
+web_extension_undo_redo_state_changed_cb (GDBusConnection *connection,
+                                         const gchar *sender_name,
+                                         const gchar *object_path,
+                                         const gchar *interface_name,
+                                         const gchar *signal_name,
+                                         GVariant *parameters,
+                                         EWebKitEditor *wk_editor)
+{
+       guint64 page_id = 0;
+       gboolean can_undo = FALSE, can_redo = FALSE;
+
+       if (g_strcmp0 (signal_name, "UndoRedoStateChanged") != 0)
+               return;
+
+       g_variant_get (parameters, "(tbb)", &page_id, &can_undo, &can_redo);
+
+       if (page_id == webkit_web_view_get_page_id (WEBKIT_WEB_VIEW (wk_editor))) {
+               webkit_editor_set_can_undo (wk_editor, can_undo);
+               webkit_editor_set_can_redo (wk_editor, can_redo);
+       }
+}
+
+static void
+dispatch_pending_operations (EWebKitEditor *wk_editor)
+{
+       if (wk_editor->priv->webkit_load_event != WEBKIT_LOAD_FINISHED ||
+           !wk_editor->priv->web_extension)
+               return;
+
+       /* Dispatch queued operations - as we are using this just for load
+        * operations load just the latest request and throw away the rest. */
+       if (wk_editor->priv->post_reload_operations &&
+           !g_queue_is_empty (wk_editor->priv->post_reload_operations)) {
+
+               PostReloadOperation *op;
+
+               op = g_queue_pop_head (wk_editor->priv->post_reload_operations);
+
+               op->func (wk_editor, op->data, op->flags);
+
+               if (op->data_free_func)
+                       op->data_free_func (op->data);
+               g_free (op);
+
+               while ((op = g_queue_pop_head (wk_editor->priv->post_reload_operations))) {
+                       if (op->data_free_func)
+                               op->data_free_func (op->data);
+                       g_free (op);
+               }
+
+               g_queue_clear (wk_editor->priv->post_reload_operations);
+       }
+}
+
+static void
+web_extension_proxy_created_cb (GDBusProxy *proxy,
+                                GAsyncResult *result,
+                                EWebKitEditor *wk_editor)
+{
+       GError *error = NULL;
+
+       wk_editor->priv->web_extension = g_dbus_proxy_new_finish (result, &error);
+       if (!wk_editor->priv->web_extension) {
+               g_warning ("Error creating web extension proxy: %s\n", error->message);
+               g_error_free (error);
+
+               if (wk_editor->priv->initialized_callback) {
+                       wk_editor->priv->initialized_callback (E_CONTENT_EDITOR (wk_editor), 
wk_editor->priv->initialized_user_data);
+
+                       wk_editor->priv->initialized_callback = NULL;
+                       wk_editor->priv->initialized_user_data = NULL;
+               }
+
+               return;
+       }
+
+       if (wk_editor->priv->web_extension_selection_changed_cb_id == 0) {
+               wk_editor->priv->web_extension_selection_changed_cb_id =
+                       g_dbus_connection_signal_subscribe (
+                               g_dbus_proxy_get_connection (wk_editor->priv->web_extension),
+                               g_dbus_proxy_get_name (wk_editor->priv->web_extension),
+                               E_WEBKIT_EDITOR_WEB_EXTENSION_INTERFACE,
+                               "SelectionChanged",
+                               E_WEBKIT_EDITOR_WEB_EXTENSION_OBJECT_PATH,
+                               NULL,
+                               G_DBUS_SIGNAL_FLAGS_NONE,
+                               (GDBusSignalCallback) web_extension_selection_changed_cb,
+                               wk_editor,
+                               NULL);
+       }
+
+       if (wk_editor->priv->web_extension_content_changed_cb_id == 0) {
+               wk_editor->priv->web_extension_content_changed_cb_id =
+                       g_dbus_connection_signal_subscribe (
+                               g_dbus_proxy_get_connection (wk_editor->priv->web_extension),
+                               g_dbus_proxy_get_name (wk_editor->priv->web_extension),
+                               E_WEBKIT_EDITOR_WEB_EXTENSION_INTERFACE,
+                               "ContentChanged",
+                               E_WEBKIT_EDITOR_WEB_EXTENSION_OBJECT_PATH,
+                               NULL,
+                               G_DBUS_SIGNAL_FLAGS_NONE,
+                               (GDBusSignalCallback) web_extension_content_changed_cb,
+                               wk_editor,
+                               NULL);
+       }
+
+       if (wk_editor->priv->web_extension_undo_redo_state_changed_cb_id == 0) {
+               wk_editor->priv->web_extension_undo_redo_state_changed_cb_id =
+                       g_dbus_connection_signal_subscribe (
+                               g_dbus_proxy_get_connection (wk_editor->priv->web_extension),
+                               g_dbus_proxy_get_name (wk_editor->priv->web_extension),
+                               E_WEBKIT_EDITOR_WEB_EXTENSION_INTERFACE,
+                               "UndoRedoStateChanged",
+                               E_WEBKIT_EDITOR_WEB_EXTENSION_OBJECT_PATH,
+                               NULL,
+                               G_DBUS_SIGNAL_FLAGS_NONE,
+                               (GDBusSignalCallback) web_extension_undo_redo_state_changed_cb,
+                               wk_editor,
+                               NULL);
+       }
+
+       dispatch_pending_operations (wk_editor);
+
+       if (wk_editor->priv->emit_load_finished_when_extension_is_ready) {
+               e_content_editor_emit_load_finished (E_CONTENT_EDITOR (wk_editor));
+
+               wk_editor->priv->emit_load_finished_when_extension_is_ready = FALSE;
+       }
+
+       g_object_notify (G_OBJECT (wk_editor), "web-extension");
+
+       if (wk_editor->priv->initialized_callback) {
+               wk_editor->priv->initialized_callback (E_CONTENT_EDITOR (wk_editor), 
wk_editor->priv->initialized_user_data);
+
+               wk_editor->priv->initialized_callback = NULL;
+               wk_editor->priv->initialized_user_data = NULL;
+       }
+}
+
+static void
+web_extension_appeared_cb (GDBusConnection *connection,
+                           const gchar *name,
+                           const gchar *name_owner,
+                           EWebKitEditor *wk_editor)
+{
+       g_dbus_proxy_new (
+               connection,
+               G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START |
+               G_DBUS_PROXY_FLAGS_DO_NOT_CONNECT_SIGNALS,
+               NULL,
+               name,
+               E_WEBKIT_EDITOR_WEB_EXTENSION_OBJECT_PATH,
+               E_WEBKIT_EDITOR_WEB_EXTENSION_INTERFACE,
+               NULL,
+               (GAsyncReadyCallback) web_extension_proxy_created_cb,
+               wk_editor);
+}
+
+static void
+web_extension_vanished_cb (GDBusConnection *connection,
+                           const gchar *name,
+                           EWebKitEditor *wk_editor)
+{
+       g_clear_object (&wk_editor->priv->web_extension);
+}
+
+static void
+webkit_editor_watch_web_extension (EWebKitEditor *wk_editor)
+{
+       wk_editor->priv->web_extension_watch_name_id =
+               g_bus_watch_name (
+                       G_BUS_TYPE_SESSION,
+                       E_WEBKIT_EDITOR_WEB_EXTENSION_SERVICE_NAME,
+                       G_BUS_NAME_WATCHER_FLAGS_NONE,
+                       (GBusNameAppearedCallback) web_extension_appeared_cb,
+                       (GBusNameVanishedCallback) web_extension_vanished_cb,
+                       wk_editor,
+                       NULL);
+}
+
+static guint64
+current_page_id (EWebKitEditor *wk_editor)
+{
+       return webkit_web_view_get_page_id (WEBKIT_WEB_VIEW (wk_editor));
+}
+
+static void
+sync_wrapper_result_callback (GObject *source_object,
+                             GAsyncResult *result,
+                             gpointer user_data)
+{
+       GAsyncResult **out_async_result = user_data;
+
+       g_return_if_fail (out_async_result != NULL);
+       g_return_if_fail (*out_async_result == NULL);
+
+       *out_async_result = g_object_ref (result);
+}
+
+/* Wraps GDBusProxy synchronous call into an asynchronous without blocking
+   the main context, thus there is no freeze when this is called in the UI
+   process and the WebProcess also does its own IPC call. */
+static GVariant *
+g_dbus_proxy_call_sync_wrapper (GDBusProxy *proxy,
+                               const gchar *method_name,
+                               GVariant *parameters,
+                               GDBusCallFlags flags,
+                               gint timeout_msec,
+                               GCancellable *cancellable,
+                               GError **error)
+{
+       GAsyncResult *async_result = NULL;
+       GVariant *var_result;
+
+       g_return_val_if_fail (G_IS_DBUS_PROXY (proxy), NULL);
+       g_return_val_if_fail (method_name != NULL, NULL);
+
+       g_dbus_proxy_call (
+               proxy, method_name, parameters, flags, timeout_msec, cancellable,
+               sync_wrapper_result_callback, &async_result);
+
+       while (!async_result) {
+               g_main_context_iteration (NULL, TRUE);
+       }
+
+       var_result = g_dbus_proxy_call_finish (proxy, async_result, error);
+
+       g_clear_object (&async_result);
+
+       return var_result;
+}
+
+static void
+webkit_editor_call_simple_extension_function_sync (EWebKitEditor *wk_editor,
+                                                   const gchar *function)
+{
+       if (!wk_editor->priv->web_extension) {
+               g_warning ("EHTMLEditorWebExtension not ready at %s!", G_STRFUNC);
+               return;
+       }
+
+       g_dbus_proxy_call_sync_wrapper (
+               wk_editor->priv->web_extension,
+               function,
+               g_variant_new ("(t)", current_page_id (wk_editor)),
+               G_DBUS_CALL_FLAGS_NONE,
+               -1,
+               NULL,
+               NULL);
+}
+
+static void
+webkit_editor_call_simple_extension_function (EWebKitEditor *wk_editor,
+                                              const gchar *function)
+{
+       if (!wk_editor->priv->web_extension) {
+               g_warning ("EHTMLEditorWebExtension not ready at %s!", G_STRFUNC);
+               return;
+       }
+
+       g_dbus_proxy_call (
+               wk_editor->priv->web_extension,
+               function,
+               g_variant_new ("(t)", current_page_id (wk_editor)),
+               G_DBUS_CALL_FLAGS_NONE,
+               -1,
+               NULL,
+               NULL,
+               NULL);
+}
+
+static GVariant *
+webkit_editor_get_element_attribute (EWebKitEditor *wk_editor,
+                                     const gchar *selector,
+                                     const gchar *attribute)
+{
+       if (!wk_editor->priv->web_extension) {
+               g_warning ("EHTMLEditorWebExtension not ready at %s!", G_STRFUNC);
+               return NULL;
+       }
+
+       return g_dbus_proxy_call_sync_wrapper (
+               wk_editor->priv->web_extension,
+               "ElementGetAttributeBySelector",
+               g_variant_new ("(tss)", current_page_id (wk_editor), selector, attribute),
+               G_DBUS_CALL_FLAGS_NONE,
+               -1,
+               NULL,
+               NULL);
+}
+
+static void
+webkit_editor_set_element_attribute (EWebKitEditor *wk_editor,
+                                     const gchar *selector,
+                                     const gchar *attribute,
+                                     const gchar *value)
+{
+       if (!wk_editor->priv->web_extension) {
+               g_warning ("EHTMLEditorWebExtension not ready at %s!", G_STRFUNC);
+               return;
+       }
+
+       g_dbus_proxy_call (
+               wk_editor->priv->web_extension,
+               "ElementSetAttributeBySelector",
+               g_variant_new (
+                       "(tsss)", current_page_id (wk_editor), selector, attribute, value),
+               G_DBUS_CALL_FLAGS_NONE,
+               -1,
+               NULL,
+               NULL,
+               NULL);
+}
+
+static void
+webkit_editor_remove_element_attribute (EWebKitEditor *wk_editor,
+                                        const gchar *selector,
+                                        const gchar *attribute)
+{
+       if (!wk_editor->priv->web_extension) {
+               g_warning ("EHTMLEditorWebExtension not ready at %s!", G_STRFUNC);
+               return;
+       }
+
+       g_dbus_proxy_call (
+               wk_editor->priv->web_extension,
+               "ElementRemoveAttributeBySelector",
+               g_variant_new ("(tss)", current_page_id (wk_editor), selector, attribute),
+               G_DBUS_CALL_FLAGS_NONE,
+               -1,
+               NULL,
+               NULL,
+               NULL);
+}
+
+static void
+webkit_editor_set_format_boolean (EWebKitEditor *wk_editor,
+                                  const gchar *format_dom_function,
+                                  gboolean format_value)
+{
+       if (!wk_editor->priv->web_extension) {
+               g_warning ("EHTMLEditorWebExtension not ready at %s!", G_STRFUNC);
+               return;
+       }
+
+       g_dbus_proxy_call (
+               wk_editor->priv->web_extension,
+               format_dom_function,
+               g_variant_new ("(tb)", current_page_id (wk_editor), format_value),
+               G_DBUS_CALL_FLAGS_NONE,
+               -1,
+               NULL,
+               NULL,
+               NULL);
+}
+
+static void
+webkit_editor_set_format_int (EWebKitEditor *wk_editor,
+                              const gchar *format_dom_function,
+                              gint32 format_value)
+{
+       if (!wk_editor->priv->web_extension) {
+               g_warning ("EHTMLEditorWebExtension not ready at %s!", G_STRFUNC);
+               return;
+       }
+
+       g_dbus_proxy_call (
+               wk_editor->priv->web_extension,
+               format_dom_function,
+               g_variant_new ("(ti)", current_page_id (wk_editor), format_value),
+               G_DBUS_CALL_FLAGS_NONE,
+               -1,
+               NULL,
+               NULL,
+               NULL);
+}
+
+static void
+webkit_editor_set_format_string (EWebKitEditor *wk_editor,
+                                 const gchar *format_name,
+                                 const gchar *format_dom_function,
+                                 const gchar *format_value)
+{
+       if (!wk_editor->priv->web_extension) {
+               g_warning ("EHTMLEditorWebExtension not ready at %s!", G_STRFUNC);
+               return;
+       }
+
+       if (!wk_editor->priv->html_mode)
+               return;
+
+       webkit_editor_set_changed (wk_editor, TRUE);
+
+       g_dbus_proxy_call (
+               wk_editor->priv->web_extension,
+               format_dom_function,
+               g_variant_new ("(ts)", current_page_id (wk_editor), format_value),
+               G_DBUS_CALL_FLAGS_NONE,
+               -1,
+               NULL,
+               NULL,
+               NULL);
+
+       g_object_notify (G_OBJECT (wk_editor), format_name);
+}
+
+static void
+webkit_editor_queue_post_reload_operation (EWebKitEditor *wk_editor,
+                                           PostReloadOperationFunc func,
+                                           gpointer data,
+                                           GDestroyNotify data_free_func,
+                                           EContentEditorInsertContentFlags flags)
+{
+       PostReloadOperation *op;
+
+       g_return_if_fail (func != NULL);
+
+       if (wk_editor->priv->post_reload_operations == NULL)
+               wk_editor->priv->post_reload_operations = g_queue_new ();
+
+       op = g_new0 (PostReloadOperation, 1);
+       op->func = func;
+       op->flags = flags;
+       op->data = data;
+       op->data_free_func = data_free_func;
+
+       g_queue_push_head (wk_editor->priv->post_reload_operations, op);
+}
+
+static void
+webkit_editor_show_inspector (EWebKitEditor *wk_editor)
+{
+       WebKitWebInspector *inspector;
+
+       g_return_if_fail (E_IS_WEBKIT_EDITOR (wk_editor));
+
+       inspector = webkit_web_view_get_inspector (WEBKIT_WEB_VIEW (wk_editor));
+
+       webkit_web_inspector_show (inspector);
+}
+
+static void
+webkit_editor_initialize (EContentEditor *content_editor,
+                         EContentEditorInitializedCallback callback,
+                         gpointer user_data)
+{
+       EWebKitEditor *wk_editor;
+
+       g_return_if_fail (E_IS_WEBKIT_EDITOR (content_editor));
+       g_return_if_fail (callback != NULL);
+
+       wk_editor = E_WEBKIT_EDITOR (content_editor);
+
+       if (wk_editor->priv->web_extension) {
+               callback (content_editor, user_data);
+       } else {
+               g_return_if_fail (wk_editor->priv->initialized_callback == NULL);
+
+               wk_editor->priv->initialized_callback = callback;
+               wk_editor->priv->initialized_user_data = user_data;
+       }
+}
+
+static void
+webkit_editor_update_styles (EContentEditor *editor)
+{
+       EWebKitEditor *wk_editor;
+       gboolean mark_citations, use_custom_font;
+       gchar *font, *aa = NULL, *citation_color;
+       const gchar *styles[] = { "normal", "oblique", "italic" };
+       const gchar *smoothing = NULL;
+       GString *stylesheet;
+       PangoFontDescription *min_size, *ms, *vw;
+       WebKitSettings *settings;
+       WebKitUserContentManager *manager;
+       WebKitUserStyleSheet *style_sheet;
+
+       wk_editor = E_WEBKIT_EDITOR (editor);
+
+       use_custom_font = g_settings_get_boolean (
+               wk_editor->priv->mail_settings, "use-custom-font");
+
+       if (use_custom_font) {
+               font = g_settings_get_string (
+                       wk_editor->priv->mail_settings, "monospace-font");
+               ms = pango_font_description_from_string (font ? font : "monospace 10");
+               g_free (font);
+       } else {
+               font = g_settings_get_string (
+                       wk_editor->priv->font_settings, "monospace-font-name");
+               ms = pango_font_description_from_string (font ? font : "monospace 10");
+               g_free (font);
+       }
+
+       if (wk_editor->priv->html_mode) {
+               if (use_custom_font) {
+                       font = g_settings_get_string (
+                               wk_editor->priv->mail_settings, "variable-width-font");
+                       vw = pango_font_description_from_string (font ? font : "serif 10");
+                       g_free (font);
+               } else {
+                       font = g_settings_get_string (
+                               wk_editor->priv->font_settings, "font-name");
+                       vw = pango_font_description_from_string (font ? font : "serif 10");
+                       g_free (font);
+               }
+       } else {
+               /* When in plain text mode, force monospace font */
+               vw = pango_font_description_copy (ms);
+       }
+
+       stylesheet = g_string_new ("");
+       g_string_append_printf (
+               stylesheet,
+               "body {\n"
+               "  font-family: '%s';\n"
+               "  font-size: %dpt;\n"
+               "  font-weight: %d;\n"
+               "  font-style: %s;\n"
+               " -webkit-line-break: after-white-space;\n",
+               pango_font_description_get_family (vw),
+               pango_font_description_get_size (vw) / PANGO_SCALE,
+               pango_font_description_get_weight (vw),
+               styles[pango_font_description_get_style (vw)]);
+
+       if (wk_editor->priv->aliasing_settings != NULL)
+               aa = g_settings_get_string (
+                       wk_editor->priv->aliasing_settings, "antialiasing");
+
+       if (g_strcmp0 (aa, "none") == 0)
+               smoothing = "none";
+       else if (g_strcmp0 (aa, "grayscale") == 0)
+               smoothing = "antialiased";
+       else if (g_strcmp0 (aa, "rgba") == 0)
+               smoothing = "subpixel-antialiased";
+
+       if (smoothing != NULL)
+               g_string_append_printf (
+                       stylesheet,
+                       " -webkit-font-smoothing: %s;\n",
+                       smoothing);
+
+       g_free (aa);
+
+       g_string_append (stylesheet, "}\n");
+
+       g_string_append_printf (
+               stylesheet,
+               "pre,code,.pre {\n"
+               "  font-family: '%s';\n"
+               "  font-size: %dpt;\n"
+               "  font-weight: %d;\n"
+               "  font-style: %s;\n"
+               "}",
+               pango_font_description_get_family (ms),
+               pango_font_description_get_size (ms) / PANGO_SCALE,
+               pango_font_description_get_weight (ms),
+               styles[pango_font_description_get_style (ms)]);
+
+       /* See bug #689777 for details */
+       g_string_append (
+               stylesheet,
+               "p,pre,code,address {\n"
+               "  margin: 0;\n"
+               "}\n"
+               "h1,h2,h3,h4,h5,h6 {\n"
+               "  margin-top: 0.2em;\n"
+               "  margin-bottom: 0.2em;\n"
+               "}\n");
+
+       /* When inserting a table into contenteditable element the width of the
+        * cells is nearly zero and the td { min-height } doesn't work so put
+        * unicode zero width space before each cell. */
+       g_string_append (
+               stylesheet,
+               "td:before {\n"
+               " content: \"\xe2\x80\x8b\";"
+               "}\n");
+
+       g_string_append (
+               stylesheet,
+               "img "
+               "{\n"
+               "  height: inherit; \n"
+               "  width: inherit; \n"
+               "}\n");
+
+       g_string_append (
+               stylesheet,
+               "span.-x-evo-resizable-wrapper:hover "
+               "{\n"
+               "  outline: 1px dashed red; \n"
+               "  resize: both; \n"
+               "  overflow: hidden; \n"
+               "  display: inline-block; \n"
+               "}\n");
+
+       g_string_append (
+               stylesheet,
+               "td:hover "
+               "{\n"
+               "  outline: 1px dotted red;\n"
+               "}\n");
+
+       g_string_append (
+               stylesheet,
+               "body[data-evo-plain-text] img.-x-evo-smiley-img, "
+               "body:not([data-evo-plain-text]) span.-x-evo-smiley-text "
+               "{\n"
+               "  display: none \n"
+               "}\n");
+
+       g_string_append (
+               stylesheet,
+               "body[data-evo-plain-text] [data-evo-paragraph] "
+               "{\n"
+               "  word-wrap: break-word; \n"
+               "  word-break: break-word; \n"
+               "}\n");
+
+       g_string_append_printf (
+               stylesheet,
+               ".-x-evo-plaintext-table "
+               "{\n"
+               "  border-collapse: collapse;\n"
+               "  width: %dch;\n"
+               "}\n",
+               g_settings_get_int (wk_editor->priv->mail_settings, "composer-word-wrap-length"));
+
+       g_string_append (
+               stylesheet,
+               ".-x-evo-plaintext-table td "
+               "{\n"
+               "  vertical-align: top;\n"
+               "}\n");
+
+       g_string_append (
+               stylesheet,
+               "td > * "
+               "{\n"
+               "  display : inline-block;\n"
+               "}\n");
+
+       g_string_append_printf (
+               stylesheet,
+               "body[data-evo-plain-text] ul "
+               "{\n"
+               "  list-style: outside none;\n"
+               "  -webkit-padding-start: %dch; \n"
+               "}\n", SPACES_PER_LIST_LEVEL);
+
+       g_string_append_printf (
+               stylesheet,
+               "body[data-evo-plain-text] ul > li "
+               "{\n"
+               "  list-style-position: outside;\n"
+               "  text-indent: -%dch;\n"
+               "}\n", SPACES_PER_LIST_LEVEL - 1);
+
+       g_string_append (
+               stylesheet,
+               "body[data-evo-plain-text] ul > li::before "
+               "{\n"
+               "  content: \"*"UNICODE_NBSP"\";\n"
+               "}\n");
+
+       g_string_append_printf (
+               stylesheet,
+               "body[data-evo-plain-text] ul.-x-evo-indented "
+               "{\n"
+               "  -webkit-padding-start: %dch; \n"
+               "}\n", SPACES_PER_LIST_LEVEL);
+
+       g_string_append (
+               stylesheet,
+               "body:not([data-evo-plain-text]) ul > li.-x-evo-align-center,ol > li.-x-evo-align-center "
+               "{\n"
+               "  list-style-position: inside;\n"
+               "}\n");
+
+       g_string_append (
+               stylesheet,
+               "body:not([data-evo-plain-text]) ul > li.-x-evo-align-right, ol > li.-x-evo-align-right "
+               "{\n"
+               "  list-style-position: inside;\n"
+               "}\n");
+
+       g_string_append_printf (
+               stylesheet,
+               "ol "
+               "{\n"
+               "  -webkit-padding-start: %dch; \n"
+               "}\n", SPACES_ORDERED_LIST_FIRST_LEVEL);
+
+       g_string_append_printf (
+               stylesheet,
+               "ol.-x-evo-indented "
+               "{\n"
+               "  -webkit-padding-start: %dch; \n"
+               "}\n", SPACES_PER_LIST_LEVEL);
+
+       g_string_append (
+               stylesheet,
+               ".-x-evo-align-left "
+               "{\n"
+               "  text-align: left; \n"
+               "}\n");
+
+       g_string_append (
+               stylesheet,
+               ".-x-evo-align-center "
+               "{\n"
+               "  text-align: center; \n"
+               "}\n");
+
+       g_string_append (
+               stylesheet,
+               ".-x-evo-align-right "
+               "{\n"
+               "  text-align: right; \n"
+               "}\n");
+
+       g_string_append (
+               stylesheet,
+               "ol,ul "
+               "{\n"
+               "  -webkit-margin-before: 0em; \n"
+               "  -webkit-margin-after: 0em; \n"
+               "}\n");
+
+       g_string_append (
+               stylesheet,
+               "blockquote "
+               "{\n"
+               "  -webkit-margin-before: 0em; \n"
+               "  -webkit-margin-after: 0em; \n"
+               "}\n");
+
+       g_string_append (
+               stylesheet,
+               "a "
+               "{\n"
+               "  word-wrap: break-word; \n"
+               "  word-break: break-all; \n"
+               "}\n");
+
+       citation_color = g_settings_get_string (
+               wk_editor->priv->mail_settings, "citation-color");
+       mark_citations = g_settings_get_boolean (
+               wk_editor->priv->mail_settings, "mark-citations");
+
+       g_string_append (
+               stylesheet,
+               "blockquote[type=cite] "
+               "{\n"
+               "  padding: 0.0ex 0ex;\n"
+               "  margin: 0ex;\n"
+               "  -webkit-margin-start: 0em; \n"
+               "  -webkit-margin-end : 0em; \n");
+
+       if (mark_citations && citation_color)
+               g_string_append_printf (
+                       stylesheet,
+                       "  color: %s !important; \n",
+                       citation_color);
+
+       g_free (citation_color);
+       citation_color = NULL;
+
+       g_string_append (stylesheet, "}\n");
+
+       g_string_append_printf (
+               stylesheet,
+               ".-x-evo-quote-character "
+               "{\n"
+               "  color: %s;\n"
+               "}\n",
+               e_web_view_get_citation_color_for_level (1));
+
+       g_string_append_printf (
+               stylesheet,
+               ".-x-evo-quote-character+"
+               ".-x-evo-quote-character"
+               "{\n"
+               "  color: %s;\n"
+               "}\n",
+               e_web_view_get_citation_color_for_level (2));
+
+       g_string_append_printf (
+               stylesheet,
+               ".-x-evo-quote-character+"
+               ".-x-evo-quote-character+"
+               ".-x-evo-quote-character"
+               "{\n"
+               "  color: %s;\n"
+               "}\n",
+               e_web_view_get_citation_color_for_level (3));
+
+       g_string_append_printf (
+               stylesheet,
+               ".-x-evo-quote-character+"
+               ".-x-evo-quote-character+"
+               ".-x-evo-quote-character+"
+               ".-x-evo-quote-character"
+               "{\n"
+               "  color: %s;\n"
+               "}\n",
+               e_web_view_get_citation_color_for_level (4));
+
+       g_string_append_printf (
+               stylesheet,
+               ".-x-evo-quote-character+"
+               ".-x-evo-quote-character+"
+               ".-x-evo-quote-character+"
+               ".-x-evo-quote-character+"
+               ".-x-evo-quote-character"
+               "{\n"
+               "  color: %s;\n"
+               "}\n",
+               e_web_view_get_citation_color_for_level (5));
+
+       g_string_append (
+               stylesheet,
+               "body:not([data-evo-plain-text]) "
+               "blockquote[type=cite] "
+               "{\n"
+               "  padding: 0ch 1ch 0ch 1ch;\n"
+               "  margin: 0ch;\n"
+               "  border-width: 0px 2px 0px 2px;\n"
+               "  border-style: none solid none solid;\n"
+               "  border-radius: 2px;\n"
+               "}\n");
+
+       g_string_append_printf (
+               stylesheet,
+               "body:not([data-evo-plain-text]) "
+               "blockquote[type=cite] "
+               "{\n"
+               "  border-color: %s;\n"
+               "}\n",
+               e_web_view_get_citation_color_for_level (1));
+
+       g_string_append_printf (
+               stylesheet,
+               "body:not([data-evo-plain-text]) "
+               "blockquote[type=cite] "
+               "blockquote[type=cite] "
+               "{\n"
+               "  border-color: %s;\n"
+               "}\n",
+               e_web_view_get_citation_color_for_level (2));
+
+       g_string_append_printf (
+               stylesheet,
+               "body:not([data-evo-plain-text]) "
+               "blockquote[type=cite] "
+               "blockquote[type=cite] "
+               "blockquote[type=cite] "
+               "{\n"
+               "  border-color: %s;\n"
+               "}\n",
+               e_web_view_get_citation_color_for_level (3));
+
+       g_string_append_printf (
+               stylesheet,
+               "body:not([data-evo-plain-text]) "
+               "blockquote[type=cite] "
+               "blockquote[type=cite] "
+               "blockquote[type=cite] "
+               "blockquote[type=cite] "
+               "{\n"
+               "  border-color: %s;\n"
+               "}\n",
+               e_web_view_get_citation_color_for_level (4));
+
+       g_string_append_printf (
+               stylesheet,
+               "body:not([data-evo-plain-text]) "
+               "blockquote[type=cite] "
+               "blockquote[type=cite] "
+               "blockquote[type=cite] "
+               "blockquote[type=cite] "
+               "blockquote[type=cite] "
+               "{\n"
+               "  border-color: %s;\n"
+               "}\n",
+               e_web_view_get_citation_color_for_level (5));
+
+       if (pango_font_description_get_size (ms) < pango_font_description_get_size (vw) || 
!wk_editor->priv->html_mode)
+               min_size = ms;
+       else
+               min_size = vw;
+
+       settings = webkit_web_view_get_settings (WEBKIT_WEB_VIEW (wk_editor));
+       g_object_set (
+               G_OBJECT (settings),
+               "default-font-size",
+               e_util_normalize_font_size (
+                       GTK_WIDGET (wk_editor), pango_font_description_get_size (vw) / PANGO_SCALE),
+               "default-font-family",
+               pango_font_description_get_family (vw),
+               "monospace-font-family",
+               pango_font_description_get_family (ms),
+               "default-monospace-font-size", pango_font_description_get_size (ms) / PANGO_SCALE,
+               "minimum-font-size", pango_font_description_get_size (min_size) / PANGO_SCALE,
+               NULL);
+
+       manager = webkit_web_view_get_user_content_manager (WEBKIT_WEB_VIEW (wk_editor));
+       webkit_user_content_manager_remove_all_style_sheets (manager);
+
+       style_sheet = webkit_user_style_sheet_new (
+               stylesheet->str,
+               WEBKIT_USER_CONTENT_INJECT_ALL_FRAMES,
+               WEBKIT_USER_STYLE_LEVEL_USER,
+               NULL,
+               NULL);
+
+       webkit_user_content_manager_add_style_sheet (manager, style_sheet);
+
+       g_free (wk_editor->priv->current_user_stylesheet);
+       wk_editor->priv->current_user_stylesheet = g_string_free (stylesheet, FALSE);
+
+       webkit_user_style_sheet_unref (style_sheet);
+
+       pango_font_description_free (ms);
+       pango_font_description_free (vw);
+}
+
+static gboolean
+webkit_editor_get_html_mode (EWebKitEditor *wk_editor)
+{
+       return wk_editor->priv->html_mode;
+}
+
+static gboolean
+show_lose_formatting_dialog (EWebKitEditor *wk_editor)
+{
+       gboolean lose;
+       GtkWidget *toplevel;
+       GtkWindow *parent = NULL;
+
+       toplevel = gtk_widget_get_toplevel (GTK_WIDGET (wk_editor));
+
+       if (GTK_IS_WINDOW (toplevel))
+               parent = GTK_WINDOW (toplevel);
+
+       lose = e_util_prompt_user (
+               parent, "org.gnome.evolution.mail", "prompt-on-composer-mode-switch",
+               "mail-composer:prompt-composer-mode-switch", NULL);
+
+       if (!lose) {
+               /* Nothing has changed, but notify anyway */
+               g_object_notify (G_OBJECT (wk_editor), "html-mode");
+               return FALSE;
+       }
+
+       return TRUE;
+}
+
+static void
+webkit_editor_set_html_mode (EWebKitEditor *wk_editor,
+                             gboolean html_mode)
+{
+       gboolean convert = FALSE;
+       GVariant *result;
+
+       g_return_if_fail (E_IS_WEBKIT_EDITOR (wk_editor));
+
+       if (!wk_editor->priv->web_extension) {
+               g_warning ("EHTMLEditorWebExtension not ready at %s!", G_STRFUNC);
+               return;
+       }
+
+       if (html_mode == wk_editor->priv->html_mode)
+               return;
+
+       result = g_dbus_proxy_call_sync_wrapper (
+               wk_editor->priv->web_extension,
+               "DOMCheckIfConversionNeeded",
+               g_variant_new ("(t)", current_page_id (wk_editor)),
+               G_DBUS_CALL_FLAGS_NONE,
+               -1,
+               NULL,
+               NULL);
+
+       if (result) {
+               g_variant_get (result, "(b)", &convert);
+               g_variant_unref (result);
+       }
+
+       /* If toggling from HTML to the plain text mode, ask the user first if
+        * he wants to convert the content. */
+       if (convert) {
+               if (!show_lose_formatting_dialog (wk_editor))
+                       return;
+
+               webkit_editor_set_changed (wk_editor, TRUE);
+       }
+
+       wk_editor->priv->html_mode = html_mode;
+
+       g_dbus_proxy_call (
+               wk_editor->priv->web_extension,
+               "SetEditorHTMLMode",
+               g_variant_new ("(tbb)", current_page_id (wk_editor), html_mode, convert),
+               G_DBUS_CALL_FLAGS_NONE,
+               -1,
+               NULL,
+               NULL,
+               NULL);
+
+       /* Update fonts - in plain text we only want monospaced */
+       webkit_editor_update_styles (E_CONTENT_EDITOR (wk_editor));
+
+       g_object_notify (G_OBJECT (wk_editor), "html-mode");
+}
+
+static void
+set_convert_in_situ (EWebKitEditor *wk_editor,
+                     gboolean value)
+{
+       g_dbus_proxy_call (
+               wk_editor->priv->web_extension,
+               "SetConvertInSitu",
+               g_variant_new ("(tb)", current_page_id (wk_editor), value),
+               G_DBUS_CALL_FLAGS_NONE,
+               -1,
+               NULL,
+               NULL,
+               NULL);
+
+}
+
+static void
+webkit_editor_insert_content (EContentEditor *editor,
+                              const gchar *content,
+                              EContentEditorInsertContentFlags flags)
+{
+       EWebKitEditor *wk_editor;
+
+       wk_editor = E_WEBKIT_EDITOR (editor);
+
+       /* It can happen that the view is not ready yet (it is in the middle of
+        * another load operation) so we have to queue the current operation and
+        * redo it again when the view is ready. This was happening when loading
+        * the stuff in EMailSignatureEditor. */
+       if (wk_editor->priv->webkit_load_event != WEBKIT_LOAD_FINISHED ||
+           wk_editor->priv->reload_in_progress) {
+               webkit_editor_queue_post_reload_operation (
+                       wk_editor,
+                       (PostReloadOperationFunc) webkit_editor_insert_content,
+                       g_strdup (content),
+                       g_free,
+                       flags);
+               return;
+       }
+
+       if (!wk_editor->priv->web_extension) {
+               /* If the operation needs a web extension and it is not ready yet
+                * we need to schedule the current operation again a dispatch it
+                * when the extension is ready */
+               if (!((flags & E_CONTENT_EDITOR_INSERT_REPLACE_ALL) &&
+                     (flags & E_CONTENT_EDITOR_INSERT_TEXT_HTML) &&
+                     (strstr (content, "data-evo-draft") ||
+                      strstr (content, "data-evo-signature-plain-text-mode")))) {
+                       webkit_editor_queue_post_reload_operation (
+                               wk_editor,
+                               (PostReloadOperationFunc) webkit_editor_insert_content,
+                               g_strdup (content),
+                               g_free,
+                               flags);
+                       return;
+               }
+       }
+
+       if ((flags & E_CONTENT_EDITOR_INSERT_CONVERT) &&
+           !(flags & E_CONTENT_EDITOR_INSERT_REPLACE_ALL)) {
+               /* e_html_editor_view_convert_and_insert_plain_text
+                  e_html_editor_view_convert_and_insert_html_to_plain_text
+                  e_html_editor_view_insert_text */
+               g_dbus_proxy_call (
+                       wk_editor->priv->web_extension,
+                       "DOMConvertAndInsertHTMLIntoSelection",
+                       g_variant_new (
+                               "(tsb)",
+                               current_page_id (wk_editor),
+                               content,
+                               (flags & E_CONTENT_EDITOR_INSERT_TEXT_HTML)),
+                       G_DBUS_CALL_FLAGS_NONE,
+                       -1,
+                       NULL,
+                       NULL,
+                       NULL);
+       } else if ((flags & E_CONTENT_EDITOR_INSERT_REPLACE_ALL) &&
+                  (flags & E_CONTENT_EDITOR_INSERT_TEXT_HTML)) {
+               if ((strstr (content, "data-evo-draft") ||
+                    strstr (content, "data-evo-signature-plain-text-mode"))) {
+                       wk_editor->priv->reload_in_progress = TRUE;
+                       webkit_web_view_load_html (WEBKIT_WEB_VIEW (wk_editor), content, "file://");
+                       return;
+               }
+
+               if (strstr (content, "data-evo-draft") && !(wk_editor->priv->html_mode)) {
+                       if (content && *content)
+                               set_convert_in_situ (wk_editor, TRUE);
+                       wk_editor->priv->reload_in_progress = TRUE;
+                       webkit_web_view_load_html (WEBKIT_WEB_VIEW (wk_editor), content, "file://");
+                       return;
+               }
+
+               /* Only convert messages that are in HTML */
+               if (!(wk_editor->priv->html_mode)) {
+                       if (strstr (content, "<!-- text/html -->")) {
+                               if (!show_lose_formatting_dialog (wk_editor)) {
+                                       wk_editor->priv->reload_in_progress = TRUE;
+                                       webkit_editor_set_html_mode (wk_editor, TRUE);
+                                       webkit_web_view_load_html (
+                                               WEBKIT_WEB_VIEW (wk_editor), content, "file://");
+                                       return;
+                               }
+                       }
+                       if (content && *content)
+                               set_convert_in_situ (wk_editor, TRUE);
+               }
+
+               wk_editor->priv->reload_in_progress = TRUE;
+               webkit_web_view_load_html (WEBKIT_WEB_VIEW (wk_editor), content, "file://");
+       } else if ((flags & E_CONTENT_EDITOR_INSERT_REPLACE_ALL) &&
+                  (flags & E_CONTENT_EDITOR_INSERT_TEXT_PLAIN)) {
+               g_dbus_proxy_call (
+                       wk_editor->priv->web_extension,
+                       "DOMConvertContent",
+                       g_variant_new ("(ts)", current_page_id (wk_editor), content),
+                       G_DBUS_CALL_FLAGS_NONE,
+                       -1,
+                       NULL,
+                       NULL,
+                       NULL);
+       } else if ((flags & E_CONTENT_EDITOR_INSERT_CONVERT) &&
+                   !(flags & E_CONTENT_EDITOR_INSERT_REPLACE_ALL) &&
+                   !(flags & E_CONTENT_EDITOR_INSERT_QUOTE_CONTENT)) {
+               /* e_html_editor_view_paste_as_text */
+               g_dbus_proxy_call (
+                       wk_editor->priv->web_extension,
+                       "DOMConvertAndInsertHTMLIntoSelection",
+                       g_variant_new (
+                               "(tsb)", current_page_id (wk_editor), content, TRUE),
+                       G_DBUS_CALL_FLAGS_NONE,
+                       -1,
+                       NULL,
+                       NULL,
+                       NULL);
+       } else if ((flags & E_CONTENT_EDITOR_INSERT_QUOTE_CONTENT) &&
+                  !(flags & E_CONTENT_EDITOR_INSERT_REPLACE_ALL)) {
+               /* e_html_editor_view_paste_clipboard_quoted */
+               g_dbus_proxy_call (
+                       wk_editor->priv->web_extension,
+                       "DOMQuoteAndInsertTextIntoSelection",
+                       g_variant_new (
+                               "(tsb)", current_page_id (wk_editor), content, (flags & 
E_CONTENT_EDITOR_INSERT_TEXT_HTML) != 0),
+                       G_DBUS_CALL_FLAGS_NONE,
+                       -1,
+                       NULL,
+                       NULL,
+                       NULL);
+       } else if (!(flags & E_CONTENT_EDITOR_INSERT_CONVERT) &&
+                  !(flags & E_CONTENT_EDITOR_INSERT_REPLACE_ALL)) {
+               /* e_html_editor_view_insert_html */
+               g_dbus_proxy_call (
+                       wk_editor->priv->web_extension,
+                       "DOMInsertHTML",
+                       g_variant_new (
+                               "(ts)", current_page_id (wk_editor), content),
+                       G_DBUS_CALL_FLAGS_NONE,
+                       -1,
+                       NULL,
+                       NULL,
+                       NULL);
+       } else
+               g_warning ("Unsupported flags combination (%d) in (%s)", flags, G_STRFUNC);
+}
+
+static CamelMimePart *
+create_part_for_inline_image_from_element_data (const gchar *element_src,
+                                                const gchar *name,
+                                                const gchar *id)
+{
+       CamelStream *stream;
+       CamelDataWrapper *wrapper;
+       CamelMimePart *part = NULL;
+       gsize decoded_size;
+       gssize size;
+       gchar *mime_type = NULL;
+       const gchar *base64_encoded_data;
+       guchar *base64_decoded_data = NULL;
+
+       base64_encoded_data = strstr (element_src, ";base64,");
+       if (!base64_encoded_data)
+               goto out;
+
+       mime_type = g_strndup (
+               element_src + 5,
+               base64_encoded_data - (strstr (element_src, "data:") + 5));
+
+       /* Move to actual data */
+       base64_encoded_data += 8;
+
+       base64_decoded_data = g_base64_decode (base64_encoded_data, &decoded_size);
+
+       stream = camel_stream_mem_new ();
+       size = camel_stream_write (
+               stream, (gchar *) base64_decoded_data, decoded_size, NULL, NULL);
+
+       if (size == -1)
+               goto out;
+
+       wrapper = camel_data_wrapper_new ();
+       camel_data_wrapper_construct_from_stream_sync (
+               wrapper, stream, NULL, NULL);
+       g_object_unref (stream);
+
+       camel_data_wrapper_set_mime_type (wrapper, mime_type);
+
+       part = camel_mime_part_new ();
+       camel_medium_set_content (CAMEL_MEDIUM (part), wrapper);
+       g_object_unref (wrapper);
+
+       camel_mime_part_set_content_id (part, id);
+       camel_mime_part_set_filename (part, name);
+       camel_mime_part_set_disposition (part, "inline");
+       camel_mime_part_set_encoding (part, CAMEL_TRANSFER_ENCODING_BASE64);
+out:
+       g_free (mime_type);
+       g_free (base64_decoded_data);
+
+       return part;
+}
+
+static GSList *
+webkit_editor_get_parts_for_inline_images (GVariant *images)
+{
+       const gchar *element_src, *name, *id;
+       GVariantIter *iter;
+       GSList *parts = NULL;
+
+       if (g_variant_check_format_string (images, "a(sss)", FALSE)) {
+               g_variant_get (images, "a(sss)", &iter);
+               while (g_variant_iter_loop (iter, "(&s&s&s)", &element_src, &name, &id)) {
+                       CamelMimePart *part;
+
+                       part = create_part_for_inline_image_from_element_data (
+                               element_src, name, id);
+                       parts = g_slist_prepend (parts, part);
+               }
+               g_variant_iter_free (iter);
+       }
+
+       return parts ? g_slist_reverse (parts) : NULL;
+}
+
+static gchar *
+webkit_editor_get_content (EContentEditor *editor,
+                           EContentEditorGetContentFlags flags,
+                           const gchar *inline_images_from_domain,
+                          GSList **inline_images_parts)
+{
+       EWebKitEditor *wk_editor;
+       GVariant *result;
+
+       wk_editor = E_WEBKIT_EDITOR (editor);
+       if (!wk_editor->priv->web_extension)
+               return g_strdup ("");
+
+       if ((flags & E_CONTENT_EDITOR_GET_TEXT_HTML) &&
+           !(flags & E_CONTENT_EDITOR_GET_PROCESSED) &&
+            !(flags & E_CONTENT_EDITOR_GET_BODY))
+               g_dbus_proxy_call (
+                       wk_editor->priv->web_extension,
+                       "DOMEmbedStyleSheet",
+                       g_variant_new (
+                               "(ts)",
+                               current_page_id (wk_editor),
+                               wk_editor->priv->current_user_stylesheet),
+                       G_DBUS_CALL_FLAGS_NONE,
+                       -1,
+                       NULL,
+                       NULL,
+                       NULL);
+
+       result = g_dbus_proxy_call_sync_wrapper (
+               wk_editor->priv->web_extension,
+               "DOMGetContent",
+               g_variant_new (
+                       "(tsi)",
+                       current_page_id (wk_editor),
+                       inline_images_from_domain ? inline_images_from_domain : "",
+                       (gint32) flags),
+               G_DBUS_CALL_FLAGS_NONE,
+               -1,
+               NULL,
+               NULL);
+
+       if ((flags & E_CONTENT_EDITOR_GET_TEXT_HTML) &&
+           !(flags & E_CONTENT_EDITOR_GET_PROCESSED) &&
+            !(flags & E_CONTENT_EDITOR_GET_BODY))
+               webkit_editor_call_simple_extension_function (
+                       wk_editor, "DOMRemoveEmbeddedStyleSheet");
+
+       if (result) {
+               GVariant *images = NULL;
+               gchar *value = NULL;
+
+               g_variant_get (result, "(sv)", &value, &images);
+               if (inline_images_parts)
+                       *inline_images_parts = webkit_editor_get_parts_for_inline_images (images);
+
+               if (images)
+                       g_variant_unref (images);
+
+               g_variant_unref (result);
+
+               return value;
+       }
+
+       return g_strdup ("");
+}
+
+static gboolean
+webkit_editor_can_undo (EWebKitEditor *wk_editor)
+{
+       g_return_val_if_fail (E_IS_WEBKIT_EDITOR (wk_editor), FALSE);
+
+       return wk_editor->priv->can_undo;
+}
+
+static void
+webkit_editor_undo (EContentEditor *editor)
+{
+       EWebKitEditor *wk_editor;
+
+       wk_editor = E_WEBKIT_EDITOR (editor);
+
+       webkit_editor_call_simple_extension_function (wk_editor, "DOMUndo");
+}
+
+static gboolean
+webkit_editor_can_redo (EWebKitEditor *wk_editor)
+{
+       g_return_val_if_fail (E_IS_WEBKIT_EDITOR (wk_editor), FALSE);
+
+       return wk_editor->priv->can_redo;
+}
+
+static void
+webkit_editor_redo (EContentEditor *editor)
+{
+       EWebKitEditor *wk_editor;
+
+       g_return_if_fail (E_IS_WEBKIT_EDITOR (editor));
+
+       wk_editor = E_WEBKIT_EDITOR (editor);
+
+       webkit_editor_call_simple_extension_function (wk_editor, "DOMRedo");
+}
+
+static void
+webkit_editor_move_caret_on_coordinates (EContentEditor *editor,
+                                         gint x,
+                                         gint y,
+                                         gboolean cancel_if_not_collapsed)
+{
+       EWebKitEditor *wk_editor;
+
+       wk_editor = E_WEBKIT_EDITOR (editor);
+       if (!wk_editor->priv->web_extension) {
+               g_warning ("EHTMLEditorWebExtension not ready at %s!", G_STRFUNC);
+               return;
+       }
+
+       g_dbus_proxy_call_sync_wrapper (
+               wk_editor->priv->web_extension,
+               "DOMMoveSelectionOnPoint",
+               g_variant_new (
+                       "(tiib)", current_page_id (wk_editor), x, y, cancel_if_not_collapsed),
+               G_DBUS_CALL_FLAGS_NONE,
+               -1,
+               NULL,
+               NULL);
+}
+
+static void
+webkit_editor_insert_emoticon (EContentEditor *editor,
+                               EEmoticon *emoticon)
+{
+       EWebKitEditor *wk_editor;
+
+       wk_editor = E_WEBKIT_EDITOR (editor);
+       if (!wk_editor->priv->web_extension) {
+               g_warning ("EHTMLEditorWebExtension not ready at %s!", G_STRFUNC);
+               return;
+       }
+
+       g_dbus_proxy_call (
+               wk_editor->priv->web_extension,
+               "DOMInsertSmiley",
+               g_variant_new (
+                       "(ts)", current_page_id (wk_editor), e_emoticon_get_name (emoticon)),
+               G_DBUS_CALL_FLAGS_NONE,
+               -1,
+               NULL,
+               NULL,
+               NULL);
+}
+
+static void
+webkit_editor_insert_image_from_mime_part (EContentEditor *editor,
+                                           CamelMimePart *part)
+{
+       CamelDataWrapper *dw;
+       CamelStream *stream;
+       EWebKitEditor *wk_editor;
+       GByteArray *byte_array;
+       gchar *src, *base64_encoded, *mime_type, *cid_uri;
+       const gchar *cid, *name;
+
+       wk_editor = E_WEBKIT_EDITOR (editor);
+       if (!wk_editor->priv->web_extension) {
+               g_warning ("EHTMLEditorWebExtension not ready at %s!", G_STRFUNC);
+               return;
+       }
+
+       stream = camel_stream_mem_new ();
+       dw = camel_medium_get_content (CAMEL_MEDIUM (part));
+       g_return_if_fail (dw);
+
+       mime_type = camel_data_wrapper_get_mime_type (dw);
+       camel_data_wrapper_decode_to_stream_sync (dw, stream, NULL, NULL);
+       camel_stream_close (stream, NULL, NULL);
+
+       byte_array = camel_stream_mem_get_byte_array (CAMEL_STREAM_MEM (stream));
+
+       if (!byte_array->data)
+               return;
+
+       base64_encoded = g_base64_encode ((const guchar *) byte_array->data, byte_array->len);
+
+       name = camel_mime_part_get_filename (part);
+       /* Insert file name before new src */
+       src = g_strconcat (name, ";data:", mime_type, ";base64,", base64_encoded, NULL);
+
+       cid = camel_mime_part_get_content_id (part);
+       if (!cid) {
+               camel_mime_part_set_content_id (part, NULL);
+               cid = camel_mime_part_get_content_id (part);
+       }
+       cid_uri = g_strdup_printf ("cid:%s", cid);
+
+       g_dbus_proxy_call (
+               wk_editor->priv->web_extension,
+               "DOMAddNewInlineImageIntoList",
+               g_variant_new ("(tsss)", current_page_id (wk_editor), name, cid_uri, src),
+               G_DBUS_CALL_FLAGS_NONE,
+               -1,
+               NULL,
+               NULL,
+               NULL);
+
+       g_free (base64_encoded);
+       g_free (mime_type);
+       g_object_unref (stream);
+}
+
+static void
+webkit_editor_select_all (EContentEditor *editor)
+{
+       EWebKitEditor *wk_editor;
+
+       wk_editor = E_WEBKIT_EDITOR (editor);
+
+       webkit_web_view_execute_editing_command (
+               WEBKIT_WEB_VIEW (wk_editor), WEBKIT_EDITING_COMMAND_SELECT_ALL);
+}
+
+static void
+webkit_editor_selection_wrap (EContentEditor *editor)
+{
+       EWebKitEditor *wk_editor;
+
+       wk_editor = E_WEBKIT_EDITOR (editor);
+
+       webkit_editor_call_simple_extension_function (wk_editor, "DOMSelectionWrap");
+}
+
+static gboolean
+webkit_editor_selection_is_indented (EWebKitEditor *wk_editor)
+{
+       g_return_val_if_fail (E_IS_WEBKIT_EDITOR (wk_editor), FALSE);
+
+       return wk_editor->priv->is_indented;
+}
+
+static void
+webkit_editor_selection_indent (EContentEditor *editor)
+{
+       EWebKitEditor *wk_editor;
+
+       wk_editor = E_WEBKIT_EDITOR (editor);
+
+       webkit_editor_call_simple_extension_function (
+               wk_editor, "DOMSelectionIndent");
+}
+
+static void
+webkit_editor_selection_unindent (EContentEditor *editor)
+{
+       EWebKitEditor *wk_editor;
+
+       wk_editor = E_WEBKIT_EDITOR (editor);
+
+       webkit_editor_call_simple_extension_function (
+               wk_editor, "DOMSelectionUnindent");
+}
+
+static void
+webkit_editor_cut (EContentEditor *editor)
+{
+       EWebKitEditor *wk_editor;
+
+       wk_editor = E_WEBKIT_EDITOR (editor);
+
+       wk_editor->priv->copy_cut_actions_triggered = TRUE;
+
+       webkit_editor_call_simple_extension_function_sync (
+               wk_editor, "EEditorActionsSaveHistoryForCut");
+
+       webkit_web_view_execute_editing_command (
+               WEBKIT_WEB_VIEW (wk_editor), WEBKIT_EDITING_COMMAND_CUT);
+}
+
+static void
+webkit_editor_copy (EContentEditor *editor)
+{
+       EWebKitEditor *wk_editor;
+
+       wk_editor = E_WEBKIT_EDITOR (editor);
+
+       wk_editor->priv->copy_cut_actions_triggered = TRUE;
+
+       webkit_web_view_execute_editing_command (
+               WEBKIT_WEB_VIEW (wk_editor), WEBKIT_EDITING_COMMAND_COPY);
+}
+
+static ESpellChecker *
+webkit_editor_get_spell_checker (EWebKitEditor *wk_editor)
+{
+       g_return_val_if_fail (E_IS_WEBKIT_EDITOR (wk_editor), NULL);
+
+       return wk_editor->priv->spell_checker;
+}
+
+static gchar *
+webkit_editor_get_caret_word (EContentEditor *editor)
+{
+       EWebKitEditor *wk_editor;
+       gchar *ret_val = NULL;
+       GVariant *result;
+
+       wk_editor = E_WEBKIT_EDITOR (editor);
+       if (!wk_editor->priv->web_extension) {
+               g_warning ("EHTMLEditorWebExtension not ready at %s!", G_STRFUNC);
+               return NULL;
+       }
+
+       result = g_dbus_proxy_call_sync_wrapper (
+               wk_editor->priv->web_extension,
+               "DOMGetCaretWord",
+               g_variant_new ("(t)", current_page_id (wk_editor)),
+               G_DBUS_CALL_FLAGS_NONE,
+               -1,
+               NULL,
+               NULL);
+
+       if (result) {
+               g_variant_get (result, "(s)", &ret_val);
+               g_variant_unref (result);
+       }
+
+       return ret_val;
+}
+
+static void
+webkit_editor_set_spell_checking_languages (EContentEditor *editor,
+                                            const gchar **languages)
+{
+       EWebKitEditor *wk_editor;
+       WebKitWebContext *web_context;
+
+       wk_editor = E_WEBKIT_EDITOR (editor);
+       web_context = webkit_web_view_get_context (WEBKIT_WEB_VIEW (wk_editor));
+       webkit_web_context_set_spell_checking_languages (web_context, (const gchar * const *) languages);
+}
+
+static void
+webkit_editor_set_spell_check_enabled (EWebKitEditor *wk_editor,
+                                      gboolean enable)
+{
+       g_return_if_fail (E_IS_WEBKIT_EDITOR (wk_editor));
+
+       if ((wk_editor->priv->spell_check_enabled ? 1 : 0) == (enable ? 1 : 0))
+               return;
+
+       wk_editor->priv->spell_check_enabled = enable;
+
+       webkit_editor_call_simple_extension_function (
+               wk_editor, enable ? "DOMForceSpellCheck" : "DOMTurnSpellCheckOff");
+
+       g_object_notify (G_OBJECT (wk_editor), "spell-check-enabled");
+}
+
+static gboolean
+webkit_editor_get_spell_check_enabled (EWebKitEditor *wk_editor)
+{
+       g_return_val_if_fail (E_IS_WEBKIT_EDITOR (wk_editor), FALSE);
+
+       return wk_editor->priv->spell_check_enabled;
+}
+
+static gboolean
+webkit_editor_is_editable (EWebKitEditor *wk_editor)
+{
+       g_return_val_if_fail (E_IS_WEBKIT_EDITOR (wk_editor), FALSE);
+
+       return webkit_web_view_is_editable (WEBKIT_WEB_VIEW (wk_editor));
+}
+
+static void
+webkit_editor_set_editable (EWebKitEditor *wk_editor,
+                                    gboolean editable)
+{
+       g_return_if_fail (E_IS_WEBKIT_EDITOR (wk_editor));
+
+       return webkit_web_view_set_editable (WEBKIT_WEB_VIEW (wk_editor), editable);
+}
+
+static gchar *
+webkit_editor_get_current_signature_uid (EContentEditor *editor)
+{
+       EWebKitEditor *wk_editor;
+       gchar *ret_val= NULL;
+       GVariant *result;
+
+       wk_editor = E_WEBKIT_EDITOR (editor);
+
+       if (!wk_editor->priv->web_extension) {
+               g_warning ("EHTMLEditorWebExtension not ready at %s!", G_STRFUNC);
+               return NULL;
+       }
+
+       result = g_dbus_proxy_call_sync_wrapper (
+               wk_editor->priv->web_extension,
+               "DOMGetActiveSignatureUid",
+               g_variant_new ("(t)", current_page_id (wk_editor)),
+               G_DBUS_CALL_FLAGS_NONE,
+               -1,
+               NULL,
+               NULL);
+
+       if (result) {
+               g_variant_get (result, "(s)", &ret_val);
+               g_variant_unref (result);
+       }
+
+       return ret_val;
+}
+
+static gboolean
+webkit_editor_is_ready (EContentEditor *editor)
+{
+       EWebKitEditor *wk_editor;
+
+       wk_editor = E_WEBKIT_EDITOR (editor);
+
+       return !webkit_web_view_is_loading (WEBKIT_WEB_VIEW (wk_editor)) && wk_editor->priv->web_extension;
+}
+
+static char *
+webkit_editor_insert_signature (EContentEditor *editor,
+                                const gchar *content,
+                                gboolean is_html,
+                                const gchar *signature_id,
+                                gboolean *set_signature_from_message,
+                                gboolean *check_if_signature_is_changed,
+                                gboolean *ignore_next_signature_change)
+{
+       EWebKitEditor *wk_editor;
+       gchar *ret_val = NULL;
+       GVariant *result;
+
+       wk_editor = E_WEBKIT_EDITOR (editor);
+
+       if (!wk_editor->priv->web_extension) {
+               g_warning ("EHTMLEditorWebExtension not ready at %s!", G_STRFUNC);
+               return NULL;
+       }
+
+       result = g_dbus_proxy_call_sync_wrapper (
+               wk_editor->priv->web_extension,
+               "DOMInsertSignature",
+               g_variant_new (
+                       "(tsbsbbb)",
+                       current_page_id (wk_editor),
+                       content ? content : "",
+                       is_html,
+                       signature_id,
+                       *set_signature_from_message,
+                       *check_if_signature_is_changed,
+                       *ignore_next_signature_change),
+               G_DBUS_CALL_FLAGS_NONE,
+               -1,
+               NULL,
+               NULL);
+
+       if (result) {
+               g_variant_get (
+                       result,
+                       "(sbbb)",
+                       &ret_val,
+                       set_signature_from_message,
+                       check_if_signature_is_changed,
+                       ignore_next_signature_change);
+               g_variant_unref (result);
+       }
+
+       return ret_val;
+}
+
+static guint
+webkit_editor_get_caret_position (EContentEditor *editor)
+{
+       EWebKitEditor *wk_editor;
+       GVariant *result;
+       guint ret_val = 0;
+
+       wk_editor = E_WEBKIT_EDITOR (editor);
+
+       if (!wk_editor->priv->web_extension) {
+               g_warning ("EHTMLEditorWebExtension not ready at %s!", G_STRFUNC);
+               return 0;
+       }
+
+       result = g_dbus_proxy_call_sync_wrapper (
+               wk_editor->priv->web_extension,
+               "DOMGetCaretPosition",
+               g_variant_new ("(t)", current_page_id (wk_editor)),
+               G_DBUS_CALL_FLAGS_NONE,
+               -1,
+               NULL,
+               NULL);
+
+       if (result) {
+               ret_val = g_variant_get_uint32 (result);
+               g_variant_unref (result);
+       }
+
+       return ret_val;
+}
+
+static guint
+webkit_editor_get_caret_offset (EContentEditor *editor)
+{
+       EWebKitEditor *wk_editor;
+       GVariant *result;
+       guint ret_val = 0;
+
+       wk_editor = E_WEBKIT_EDITOR (editor);
+
+       if (!wk_editor->priv->web_extension) {
+               g_warning ("EHTMLEditorWebExtension not ready at %s!", G_STRFUNC);
+               return 0;
+       }
+
+       result = g_dbus_proxy_call_sync_wrapper (
+               wk_editor->priv->web_extension,
+               "DOMGetCaretOffset",
+               g_variant_new ("(t)", current_page_id (wk_editor)),
+               G_DBUS_CALL_FLAGS_NONE,
+               -1,
+               NULL,
+               NULL);
+
+       if (result) {
+               ret_val = g_variant_get_uint32 (result);
+               g_variant_unref (result);
+       }
+
+       return ret_val;
+}
+
+static void
+webkit_editor_clear_undo_redo_history (EContentEditor *editor)
+{
+       EWebKitEditor *wk_editor;
+
+       wk_editor = E_WEBKIT_EDITOR (editor);
+
+       if (!wk_editor->priv->web_extension) {
+               g_warning ("EHTMLEditorWebExtension not ready at %s!", G_STRFUNC);
+               return;
+       }
+
+       g_dbus_proxy_call (
+               wk_editor->priv->web_extension,
+               "DOMClearUndoRedoHistory",
+               g_variant_new ("(t)", current_page_id (wk_editor)),
+               G_DBUS_CALL_FLAGS_NONE,
+               -1,
+               NULL,
+               NULL,
+               NULL);
+}
+
+static void
+webkit_editor_replace_caret_word (EContentEditor *editor,
+                                  const gchar *replacement)
+{
+       EWebKitEditor *wk_editor;
+
+       wk_editor = E_WEBKIT_EDITOR (editor);
+       if (!wk_editor->priv->web_extension) {
+               g_warning ("EHTMLEditorWebExtension not ready at %s!", G_STRFUNC);
+               return;
+       }
+
+       g_dbus_proxy_call (
+               wk_editor->priv->web_extension,
+               "DOMReplaceCaretWord",
+               g_variant_new ("(ts)", current_page_id (wk_editor), replacement),
+               G_DBUS_CALL_FLAGS_NONE,
+               -1,
+               NULL,
+               NULL,
+               NULL);
+}
+
+static void
+webkit_editor_finish_search (EWebKitEditor *wk_editor)
+{
+       g_return_if_fail (E_IS_WEBKIT_EDITOR (wk_editor));
+
+       if (!wk_editor->priv->find_controller)
+               return;
+
+       webkit_find_controller_search_finish (wk_editor->priv->find_controller);
+
+       wk_editor->priv->performing_replace_all = FALSE;
+       wk_editor->priv->replaced_count = 0;
+       g_free (wk_editor->priv->replace_with);
+       wk_editor->priv->replace_with = NULL;
+
+       if (wk_editor->priv->found_text_handler_id) {
+               g_signal_handler_disconnect (wk_editor->priv->find_controller, 
wk_editor->priv->found_text_handler_id);
+               wk_editor->priv->found_text_handler_id = 0;
+       }
+
+       if (wk_editor->priv->failed_to_find_text_handler_id) {
+               g_signal_handler_disconnect (wk_editor->priv->find_controller, 
wk_editor->priv->failed_to_find_text_handler_id);
+               wk_editor->priv->failed_to_find_text_handler_id = 0;
+       }
+
+       wk_editor->priv->find_controller = NULL;
+}
+
+static guint32 /* WebKitFindOptions */
+find_flags_to_webkit_find_options (guint32 flags /* EContentEditorFindFlags */)
+{
+       guint32 options = 0;
+
+       if (flags & E_CONTENT_EDITOR_FIND_CASE_INSENSITIVE)
+               options |= WEBKIT_FIND_OPTIONS_CASE_INSENSITIVE;
+
+       if (flags & E_CONTENT_EDITOR_FIND_WRAP_AROUND)
+               options |= WEBKIT_FIND_OPTIONS_WRAP_AROUND;
+
+       if (flags & E_CONTENT_EDITOR_FIND_MODE_BACKWARDS)
+               options |= WEBKIT_FIND_OPTIONS_BACKWARDS;
+
+       return options;
+}
+
+static void
+webkit_find_controller_found_text_cb (WebKitFindController *find_controller,
+                                      guint match_count,
+                                      EWebKitEditor *wk_editor)
+{
+       if (wk_editor->priv->performing_replace_all) {
+               if (!wk_editor->priv->replaced_count)
+                       wk_editor->priv->replaced_count = match_count;
+
+               /* Repeatedly search for 'word', then replace selection by
+                * 'replacement'. Repeat until there's at least one occurrence of
+                * 'word' in the document */
+               e_content_editor_insert_content (
+                       E_CONTENT_EDITOR (wk_editor),
+                       wk_editor->priv->replace_with,
+                       E_CONTENT_EDITOR_INSERT_TEXT_PLAIN);
+
+               webkit_find_controller_search_next (find_controller);
+       } else {
+               e_content_editor_emit_find_done (E_CONTENT_EDITOR (wk_editor), match_count);
+       }
+}
+
+static void
+webkit_find_controller_failed_to_find_text_cb (WebKitFindController *find_controller,
+                                               EWebKitEditor *wk_editor)
+{
+       if (wk_editor->priv->performing_replace_all) {
+               guint replaced_count = wk_editor->priv->replaced_count;
+
+               webkit_editor_finish_search (wk_editor);
+               e_content_editor_emit_replace_all_done (E_CONTENT_EDITOR (wk_editor), replaced_count);
+       } else {
+               e_content_editor_emit_find_done (E_CONTENT_EDITOR (wk_editor), 0);
+       }
+}
+
+static void
+webkit_editor_prepare_find_controller (EWebKitEditor *wk_editor)
+{
+       g_return_if_fail (E_IS_WEBKIT_EDITOR (wk_editor));
+       g_return_if_fail (wk_editor->priv->find_controller == NULL);
+
+       wk_editor->priv->find_controller = webkit_web_view_get_find_controller (WEBKIT_WEB_VIEW (wk_editor));
+
+       wk_editor->priv->found_text_handler_id = g_signal_connect (
+               wk_editor->priv->find_controller, "found-text",
+               G_CALLBACK (webkit_find_controller_found_text_cb), wk_editor);
+
+       wk_editor->priv->failed_to_find_text_handler_id = g_signal_connect (
+               wk_editor->priv->find_controller, "failed-to-find-text",
+               G_CALLBACK (webkit_find_controller_failed_to_find_text_cb), wk_editor);
+
+       wk_editor->priv->performing_replace_all = FALSE;
+       wk_editor->priv->replaced_count = 0;
+       g_free (wk_editor->priv->replace_with);
+       wk_editor->priv->replace_with = NULL;
+}
+
+static void
+webkit_editor_find (EContentEditor *editor,
+                    guint32 flags,
+                    const gchar *text)
+{
+       EWebKitEditor *wk_editor;
+       guint32 wk_options;
+       gboolean needs_init;
+
+       g_return_if_fail (E_IS_WEBKIT_EDITOR (editor));
+       g_return_if_fail (text != NULL);
+
+       wk_editor = E_WEBKIT_EDITOR (editor);
+
+       wk_options = find_flags_to_webkit_find_options (flags);
+
+       needs_init = !wk_editor->priv->find_controller;
+       if (needs_init) {
+               webkit_editor_prepare_find_controller (wk_editor);
+       } else {
+               needs_init = wk_options != webkit_find_controller_get_options 
(wk_editor->priv->find_controller) ||
+                       g_strcmp0 (text, webkit_find_controller_get_search_text 
(wk_editor->priv->find_controller)) != 0;
+       }
+
+       if (needs_init) {
+               webkit_find_controller_search (wk_editor->priv->find_controller, text, wk_options, G_MAXUINT);
+       } else if ((flags & E_CONTENT_EDITOR_FIND_PREVIOUS) != 0) {
+               webkit_find_controller_search_previous (wk_editor->priv->find_controller);
+       } else {
+               webkit_find_controller_search_next (wk_editor->priv->find_controller);
+       }
+}
+
+static void
+webkit_editor_replace (EContentEditor *editor,
+                       const gchar *replacement)
+{
+       EWebKitEditor *wk_editor;
+
+       wk_editor = E_WEBKIT_EDITOR (editor);
+       if (!wk_editor->priv->web_extension) {
+               g_warning ("EHTMLEditorWebExtension not ready at %s!", G_STRFUNC);
+               return;
+       }
+
+       g_dbus_proxy_call (
+               wk_editor->priv->web_extension,
+               "DOMSelectionReplace",
+               g_variant_new ("(ts)", current_page_id (wk_editor), replacement),
+               G_DBUS_CALL_FLAGS_NONE,
+               -1,
+               NULL,
+               NULL,
+               NULL);
+}
+
+static void
+webkit_editor_replace_all (EContentEditor *editor,
+                           guint32 flags,
+                           const gchar *find_text,
+                           const gchar *replace_with)
+{
+       EWebKitEditor *wk_editor;
+       guint32 wk_options;
+
+       g_return_if_fail (E_IS_WEBKIT_EDITOR (editor));
+       g_return_if_fail (find_text != NULL);
+       g_return_if_fail (replace_with != NULL);
+
+       wk_editor = E_WEBKIT_EDITOR (editor);
+       wk_options = find_flags_to_webkit_find_options (flags);
+
+       if (!wk_editor->priv->find_controller)
+               webkit_editor_prepare_find_controller (wk_editor);
+
+       g_free (wk_editor->priv->replace_with);
+       wk_editor->priv->replace_with = g_strdup (replace_with);
+
+       wk_editor->priv->performing_replace_all = TRUE;
+       wk_editor->priv->replaced_count = 0;
+
+       webkit_find_controller_search (wk_editor->priv->find_controller, find_text, wk_options, G_MAXUINT);
+}
+
+static void
+webkit_editor_selection_save (EContentEditor *editor)
+{
+       EWebKitEditor *wk_editor;
+
+       wk_editor = E_WEBKIT_EDITOR (editor);
+
+       webkit_editor_call_simple_extension_function (
+               wk_editor, "DOMSaveSelection");
+}
+
+static void
+webkit_editor_selection_restore (EContentEditor *editor)
+{
+       EWebKitEditor *wk_editor;
+
+       wk_editor = E_WEBKIT_EDITOR (editor);
+
+       webkit_editor_call_simple_extension_function (
+               wk_editor, "DOMRestoreSelection");
+}
+
+static void
+webkit_editor_delete_cell_contents (EContentEditor *editor)
+{
+       EWebKitEditor *wk_editor;
+
+       wk_editor = E_WEBKIT_EDITOR (editor);
+
+       webkit_editor_call_simple_extension_function (
+               wk_editor, "EEditorDialogDeleteCellContents");
+}
+
+static void
+webkit_editor_delete_column (EContentEditor *editor)
+{
+       EWebKitEditor *wk_editor;
+
+       wk_editor = E_WEBKIT_EDITOR (editor);
+
+       webkit_editor_call_simple_extension_function (
+               wk_editor, "EEditorDialogDeleteColumn");
+}
+
+static void
+webkit_editor_delete_row (EContentEditor *editor)
+{
+       EWebKitEditor *wk_editor;
+
+       wk_editor = E_WEBKIT_EDITOR (editor);
+
+       webkit_editor_call_simple_extension_function (
+               wk_editor, "EEditorDialogDeleteRow");
+}
+
+static void
+webkit_editor_delete_table (EContentEditor *editor)
+{
+       EWebKitEditor *wk_editor;
+
+       wk_editor = E_WEBKIT_EDITOR (editor);
+
+       webkit_editor_call_simple_extension_function (
+               wk_editor, "EEditorDialogDeleteTable");
+}
+
+static void
+webkit_editor_insert_column_after (EContentEditor *editor)
+{
+       EWebKitEditor *wk_editor;
+
+       wk_editor = E_WEBKIT_EDITOR (editor);
+
+       webkit_editor_call_simple_extension_function (
+               wk_editor, "EEditorDialogInsertColumnAfter");
+}
+
+static void
+webkit_editor_insert_column_before (EContentEditor *editor)
+{
+       EWebKitEditor *wk_editor;
+
+       wk_editor = E_WEBKIT_EDITOR (editor);
+
+       webkit_editor_call_simple_extension_function (
+               wk_editor, "EEditorDialogInsertColumnBefore");
+}
+
+
+static void
+webkit_editor_insert_row_above (EContentEditor *editor)
+{
+       EWebKitEditor *wk_editor;
+
+       wk_editor = E_WEBKIT_EDITOR (editor);
+
+       webkit_editor_call_simple_extension_function (
+               wk_editor, "EEditorDialogInsertRowAbove");
+}
+
+static void
+webkit_editor_insert_row_below (EContentEditor *editor)
+{
+       EWebKitEditor *wk_editor;
+
+       wk_editor = E_WEBKIT_EDITOR (editor);
+
+       webkit_editor_call_simple_extension_function (
+               wk_editor, "EEditorDialogInsertRowBelow");
+}
+
+static gboolean
+webkit_editor_on_h_rule_dialog_open (EContentEditor *editor)
+{
+       EWebKitEditor *wk_editor;
+       gboolean value = FALSE;
+       GVariant *result;
+
+       wk_editor = E_WEBKIT_EDITOR (editor);
+       if (!wk_editor->priv->web_extension) {
+               g_warning ("EHTMLEditorWebExtension not ready at %s!", G_STRFUNC);
+               return FALSE;
+       }
+
+       result = g_dbus_proxy_call_sync_wrapper (
+               wk_editor->priv->web_extension,
+               "EEditorHRuleDialogFindHRule",
+               g_variant_new ("(t)", current_page_id (wk_editor)),
+               G_DBUS_CALL_FLAGS_NONE,
+               -1,
+               NULL,
+               NULL);
+
+       if (result) {
+               g_variant_get (result, "(b)", &value);
+               g_variant_unref (result);
+       }
+
+       return value;
+}
+
+static void
+webkit_editor_on_h_rule_dialog_close (EContentEditor *editor)
+{
+       EWebKitEditor *wk_editor;
+
+       wk_editor = E_WEBKIT_EDITOR (editor);
+
+       webkit_editor_call_simple_extension_function (
+               wk_editor, "EEditorHRuleDialogOnClose");
+}
+
+static void
+webkit_editor_h_rule_set_align (EContentEditor *editor,
+                                const gchar *value)
+{
+       EWebKitEditor *wk_editor;
+
+       wk_editor = E_WEBKIT_EDITOR (editor);
+
+       webkit_editor_set_element_attribute (
+               wk_editor, "#-x-evo-current-hr", "align", value);
+}
+
+static gchar *
+webkit_editor_h_rule_get_align (EContentEditor *editor)
+{
+       EWebKitEditor *wk_editor;
+       gchar *value = NULL;
+       GVariant *result;
+
+       wk_editor = E_WEBKIT_EDITOR (editor);
+
+       result = webkit_editor_get_element_attribute (
+               wk_editor, "#-x-evo-current-hr", "align");
+       if (result) {
+               g_variant_get (result, "(s)", &value);
+               g_variant_unref (result);
+       }
+
+       return value;
+}
+
+static void
+webkit_editor_h_rule_set_size (EContentEditor *editor,
+                               gint value)
+{
+       EWebKitEditor *wk_editor;
+       gchar *size;
+
+       wk_editor = E_WEBKIT_EDITOR (editor);
+
+       size = g_strdup_printf ("%d", value);
+
+       webkit_editor_set_element_attribute (
+               wk_editor, "#-x-evo-current-hr", "size", size);
+
+       g_free (size);
+}
+
+static gint
+webkit_editor_h_rule_get_size (EContentEditor *editor)
+{
+       EWebKitEditor *wk_editor;
+       gint size = 0;
+       GVariant *result;
+
+       wk_editor = E_WEBKIT_EDITOR (editor);
+
+       result = webkit_editor_get_element_attribute (
+               wk_editor, "#-x-evo-current-hr", "size");
+       if (result) {
+               const gchar *value;
+
+               g_variant_get (result, "(&s)", &value);
+               if (value && *value)
+                       size = atoi (value);
+
+               if (size == 0)
+                       size = 2;
+
+               g_variant_unref (result);
+       }
+
+       return size;
+}
+
+static void
+webkit_editor_h_rule_set_width (EContentEditor *editor,
+                                gint value,
+                                EContentEditorUnit unit)
+{
+       EWebKitEditor *wk_editor;
+       gchar *width;
+
+       wk_editor = E_WEBKIT_EDITOR (editor);
+
+       width = g_strdup_printf (
+               "%d%s",
+               value,
+               (unit == E_CONTENT_EDITOR_UNIT_PIXEL) ? "px" : "%");
+
+       webkit_editor_set_element_attribute (
+               wk_editor, "#-x-evo-current-hr", "width", width);
+
+       g_free (width);
+}
+
+static gint
+webkit_editor_h_rule_get_width (EContentEditor *editor,
+                                EContentEditorUnit *unit)
+{
+       EWebKitEditor *wk_editor;
+       gint value = 0;
+       GVariant *result;
+
+       wk_editor = E_WEBKIT_EDITOR (editor);
+
+       *unit = E_CONTENT_EDITOR_UNIT_PIXEL;
+
+       result = webkit_editor_get_element_attribute (
+               wk_editor, "#-x-evo-current-hr", "width");
+       if (result) {
+               const gchar *width;
+               g_variant_get (result, "(&s)", &width);
+               if (width && *width) {
+                       value = atoi (width);
+                       if (strstr (width, "%"))
+                               *unit = E_CONTENT_EDITOR_UNIT_PERCENTAGE;
+               }
+               g_variant_unref (result);
+       }
+
+       return value;
+}
+
+static void
+webkit_editor_h_rule_set_no_shade (EContentEditor *editor,
+                                   gboolean value)
+{
+       EWebKitEditor *wk_editor;
+
+       wk_editor = E_WEBKIT_EDITOR (editor);
+       if (!wk_editor->priv->web_extension) {
+               g_warning ("EHTMLEditorWebExtension not ready at %s!", G_STRFUNC);
+               return;
+       }
+
+       if (value)
+               webkit_editor_set_element_attribute (
+                       wk_editor, "#-x-evo-current-hr", "noshade", "");
+       else
+               webkit_editor_remove_element_attribute (
+                       wk_editor, "#-x-evo-current-hr", "noshade");
+}
+
+static gboolean
+webkit_editor_h_rule_get_no_shade (EContentEditor *editor)
+{
+       EWebKitEditor *wk_editor;
+       GVariant *result;
+       gboolean no_shade = FALSE;
+
+       wk_editor = E_WEBKIT_EDITOR (editor);
+       if (!wk_editor->priv->web_extension) {
+               g_warning ("EHTMLEditorWebExtension not ready at %s!", G_STRFUNC);
+               return FALSE;
+       }
+
+       result = g_dbus_proxy_call_sync_wrapper (
+               wk_editor->priv->web_extension,
+               "ElementHasAttribute",
+               g_variant_new ("(tss)", current_page_id (wk_editor), "-x-evo-current-hr", "noshade"),
+               G_DBUS_CALL_FLAGS_NONE,
+               -1,
+               NULL,
+               NULL);
+
+       if (result) {
+               g_variant_get (result, "(b)", &no_shade);
+               g_variant_unref (result);
+       }
+
+       return no_shade;
+}
+
+static void
+webkit_editor_on_image_dialog_open (EContentEditor *editor)
+{
+       EWebKitEditor *wk_editor;
+
+       wk_editor = E_WEBKIT_EDITOR (editor);
+
+       webkit_editor_call_simple_extension_function (
+               wk_editor, "EEditorImageDialogMarkImage");
+}
+
+static void
+webkit_editor_on_image_dialog_close (EContentEditor *editor)
+{
+       EWebKitEditor *wk_editor;
+
+       wk_editor = E_WEBKIT_EDITOR (editor);
+
+       webkit_editor_call_simple_extension_function (
+               wk_editor, "EEditorImageDialogSaveHistoryOnExit");
+}
+
+static void
+webkit_editor_insert_image (EContentEditor *editor,
+                            const gchar *image_uri)
+{
+       EWebKitEditor *wk_editor;
+
+       wk_editor = E_WEBKIT_EDITOR (editor);
+
+       if (!wk_editor->priv->web_extension) {
+               g_warning ("EHTMLEditorWebExtension not ready at %s!", G_STRFUNC);
+               return;
+       }
+
+       g_dbus_proxy_call (
+               wk_editor->priv->web_extension,
+               "DOMSelectionInsertImage",
+               g_variant_new ("(ts)", current_page_id (wk_editor), image_uri),
+               G_DBUS_CALL_FLAGS_NONE,
+               -1,
+               NULL,
+               NULL,
+               NULL);
+}
+
+static void
+webkit_editor_replace_image_src (EWebKitEditor *wk_editor,
+                                 const gchar *selector,
+                                 const gchar *image_uri)
+{
+
+       if (!wk_editor->priv->web_extension) {
+               g_warning ("EHTMLEditorWebExtension not ready at %s!", G_STRFUNC);
+               return;
+       }
+
+       g_dbus_proxy_call (
+               wk_editor->priv->web_extension,
+               "DOMReplaceImageSrc",
+               g_variant_new ("(tss)", current_page_id (wk_editor), selector, image_uri),
+               G_DBUS_CALL_FLAGS_NONE,
+               -1,
+               NULL,
+               NULL,
+               NULL);
+}
+
+static void
+webkit_editor_image_set_src (EContentEditor *editor,
+                             const gchar *value)
+{
+       EWebKitEditor *wk_editor;
+
+       wk_editor = E_WEBKIT_EDITOR (editor);
+
+       webkit_editor_replace_image_src (
+               wk_editor, "img#-x-evo-current-img", value);
+}
+
+static gchar *
+webkit_editor_image_get_src (EContentEditor *editor)
+{
+       EWebKitEditor *wk_editor;
+       gchar *value = NULL;
+       GVariant *result;
+
+       wk_editor = E_WEBKIT_EDITOR (editor);
+
+       result = webkit_editor_get_element_attribute (
+               wk_editor, "#-x-evo-current-img", "data-uri");
+
+       if (result) {
+               g_variant_get (result, "(s)", &value);
+               g_variant_unref (result);
+       }
+
+       return value;
+}
+
+static void
+webkit_editor_image_set_alt (EContentEditor *editor,
+                             const gchar *value)
+{
+       EWebKitEditor *wk_editor;
+
+       wk_editor = E_WEBKIT_EDITOR (editor);
+
+       webkit_editor_set_element_attribute (
+               wk_editor, "#-x-evo-current-img", "alt", value);
+}
+
+static gchar *
+webkit_editor_image_get_alt (EContentEditor *editor)
+{
+       EWebKitEditor *wk_editor;
+       gchar *value = NULL;
+       GVariant *result;
+
+       wk_editor = E_WEBKIT_EDITOR (editor);
+
+       result = webkit_editor_get_element_attribute (
+               wk_editor, "#-x-evo-current-img", "alt");
+
+       if (result) {
+               g_variant_get (result, "(s)", &value);
+               g_variant_unref (result);
+       }
+
+       return value;
+}
+
+static void
+webkit_editor_image_set_url (EContentEditor *editor,
+                             const gchar *value)
+{
+       EWebKitEditor *wk_editor;
+
+       wk_editor = E_WEBKIT_EDITOR (editor);
+
+       if (!wk_editor->priv->web_extension) {
+               g_warning ("EHTMLEditorWebExtension not ready at %s!", G_STRFUNC);
+               return;
+       }
+
+       g_dbus_proxy_call (
+               wk_editor->priv->web_extension,
+               "EEditorImageDialogSetElementUrl",
+               g_variant_new ("(ts)", current_page_id (wk_editor), value),
+               G_DBUS_CALL_FLAGS_NONE,
+               -1,
+               NULL,
+               NULL,
+               NULL);
+}
+
+static gchar *
+webkit_editor_image_get_url (EContentEditor *editor)
+{
+       EWebKitEditor *wk_editor;
+       GVariant *result;
+       gchar *value = NULL;
+
+       wk_editor = E_WEBKIT_EDITOR (editor);
+
+       if (!wk_editor->priv->web_extension) {
+               g_warning ("EHTMLEditorWebExtension not ready at %s!", G_STRFUNC);
+               return NULL;
+       }
+
+       result = g_dbus_proxy_call_sync_wrapper (
+               wk_editor->priv->web_extension,
+               "EEditorImageDialogGetElementUrl",
+               g_variant_new ("(t)", current_page_id (wk_editor)),
+               G_DBUS_CALL_FLAGS_NONE,
+               -1,
+               NULL,
+               NULL);
+
+       if (result) {
+               g_variant_get (result, "(s)", &value);
+               g_variant_unref (result);
+       }
+
+       return value;
+}
+
+static void
+webkit_editor_image_set_vspace (EContentEditor *editor,
+                                gint value)
+{
+       EWebKitEditor *wk_editor;
+
+       wk_editor = E_WEBKIT_EDITOR (editor);
+
+       if (!wk_editor->priv->web_extension) {
+               g_warning ("EHTMLEditorWebExtension not ready at %s!", G_STRFUNC);
+               return;
+       }
+
+       g_dbus_proxy_call (
+               wk_editor->priv->web_extension,
+               "ImageElementSetVSpace",
+               g_variant_new (
+                       "(tsi)", current_page_id (wk_editor), "-x-evo-current-img", value),
+               G_DBUS_CALL_FLAGS_NONE,
+               -1,
+               NULL,
+               NULL,
+               NULL);
+}
+
+static gint
+webkit_editor_image_get_vspace (EContentEditor *editor)
+{
+       EWebKitEditor *wk_editor;
+       GVariant *result;
+       gint value = 0;
+
+       wk_editor = E_WEBKIT_EDITOR (editor);
+
+       if (!wk_editor->priv->web_extension) {
+               g_warning ("EHTMLEditorWebExtension not ready at %s!", G_STRFUNC);
+               return 0;
+       }
+
+       result = g_dbus_proxy_call_sync_wrapper (
+               wk_editor->priv->web_extension,
+               "ImageElementGetVSpace",
+               g_variant_new ("(ts)", current_page_id (wk_editor), "-x-evo-current-img"),
+               G_DBUS_CALL_FLAGS_NONE,
+               -1,
+               NULL,
+               NULL);
+
+       if (result) {
+               g_variant_get (result, "(i)", &value);
+               g_variant_unref (result);
+       }
+
+       return value;
+}
+
+static void
+webkit_editor_image_set_hspace (EContentEditor *editor,
+                                        gint value)
+{
+       EWebKitEditor *wk_editor;
+
+       wk_editor = E_WEBKIT_EDITOR (editor);
+
+       if (!wk_editor->priv->web_extension) {
+               g_warning ("EHTMLEditorWebExtension not ready at %s!", G_STRFUNC);
+               return;
+       }
+
+       g_dbus_proxy_call (
+               wk_editor->priv->web_extension,
+               "ImageElementSetHSpace",
+               g_variant_new (
+                       "(tsi)", current_page_id (wk_editor), "-x-evo-current-img", value),
+               G_DBUS_CALL_FLAGS_NONE,
+               -1,
+               NULL,
+               NULL,
+               NULL);
+}
+
+static gint
+webkit_editor_image_get_hspace (EContentEditor *editor)
+{
+       EWebKitEditor *wk_editor;
+       GVariant *result;
+       gint value = 0;
+
+       wk_editor = E_WEBKIT_EDITOR (editor);
+
+       if (!wk_editor->priv->web_extension) {
+               g_warning ("EHTMLEditorWebExtension not ready at %s!", G_STRFUNC);
+               return 0;
+       }
+
+       result = g_dbus_proxy_call_sync_wrapper (
+               wk_editor->priv->web_extension,
+               "ImageElementGetHSpace",
+               g_variant_new ("(ts)", current_page_id (wk_editor), "-x-evo-current-img"),
+               G_DBUS_CALL_FLAGS_NONE,
+               -1,
+               NULL,
+               NULL);
+
+       if (result) {
+               g_variant_get (result, "(i)", &value);
+               g_variant_unref (result);
+       }
+
+       return value;
+}
+
+static void
+webkit_editor_image_set_border (EContentEditor *editor,
+                                gint value)
+{
+       EWebKitEditor *wk_editor;
+       gchar *border;
+
+       wk_editor = E_WEBKIT_EDITOR (editor);
+
+       border = g_strdup_printf ("%d", value);
+
+       webkit_editor_set_element_attribute (
+               wk_editor, "#-x-evo-current-img", "border", border);
+
+       g_free (border);
+}
+
+static gint
+webkit_editor_image_get_border (EContentEditor *editor)
+{
+       EWebKitEditor *wk_editor;
+       gint value = 0;
+       GVariant *result;
+
+       wk_editor = E_WEBKIT_EDITOR (editor);
+
+       result = webkit_editor_get_element_attribute (
+               wk_editor, "#-x-evo-current-img", "border");
+
+       if (result) {
+               const gchar *border;
+               g_variant_get (result, "(&s)", &border);
+               if (border && *border)
+                       value = atoi (border);
+               g_variant_unref (result);
+       }
+
+       return value;
+}
+
+static void
+webkit_editor_image_set_align (EContentEditor *editor,
+                               const gchar *value)
+{
+       EWebKitEditor *wk_editor;
+
+       wk_editor = E_WEBKIT_EDITOR (editor);
+
+       webkit_editor_set_element_attribute (
+               wk_editor, "#-x-evo-current-img", "align", value);
+}
+
+static gchar *
+webkit_editor_image_get_align (EContentEditor *editor)
+{
+       EWebKitEditor *wk_editor;
+       gchar *value = NULL;
+       GVariant *result;
+
+       wk_editor = E_WEBKIT_EDITOR (editor);
+
+       result = webkit_editor_get_element_attribute (
+               wk_editor, "#-x-evo-current-img", "align");
+
+       if (result) {
+               g_variant_get (result, "(s)", &value);
+               g_variant_unref (result);
+       }
+
+       return value;
+}
+
+static gint32
+webkit_editor_image_get_natural_width (EContentEditor *editor)
+{
+       EWebKitEditor *wk_editor;
+       GVariant *result;
+       gint32 value = 0;
+
+       wk_editor = E_WEBKIT_EDITOR (editor);
+
+       if (!wk_editor->priv->web_extension) {
+               g_warning ("EHTMLEditorWebExtension not ready at %s!", G_STRFUNC);
+               return 0;
+       }
+
+       result = g_dbus_proxy_call_sync_wrapper (
+               wk_editor->priv->web_extension,
+               "ImageElementGetNaturalWidth",
+               g_variant_new ("(ts)", current_page_id (wk_editor), "-x-evo-current-img"),
+               G_DBUS_CALL_FLAGS_NONE,
+               -1,
+               NULL,
+               NULL);
+
+       if (result) {
+               g_variant_get (result, "(i)", &value);
+               g_variant_unref (result);
+       }
+
+       return value;
+}
+
+static gint32
+webkit_editor_image_get_natural_height (EContentEditor *editor)
+{
+       EWebKitEditor *wk_editor;
+       GVariant *result;
+       gint32 value = 0;
+
+       wk_editor = E_WEBKIT_EDITOR (editor);
+
+       if (!wk_editor->priv->web_extension) {
+               g_warning ("EHTMLEditorWebExtension not ready at %s!", G_STRFUNC);
+               return 0;
+       }
+
+       result = g_dbus_proxy_call_sync_wrapper (
+               wk_editor->priv->web_extension,
+               "ImageElementGetNaturalHeight",
+               g_variant_new ("(ts)", current_page_id (wk_editor), "-x-evo-current-img"),
+               G_DBUS_CALL_FLAGS_NONE,
+               -1,
+               NULL,
+               NULL);
+
+       if (result) {
+               g_variant_get (result, "(i)", &value);
+               g_variant_unref (result);
+       }
+
+       return value;
+}
+
+static void
+webkit_editor_image_set_height (EContentEditor *editor,
+                                gint value)
+{
+       EWebKitEditor *wk_editor;
+
+       wk_editor = E_WEBKIT_EDITOR (editor);
+
+       if (!wk_editor->priv->web_extension) {
+               g_warning ("EHTMLEditorWebExtension not ready at %s!", G_STRFUNC);
+               return;
+       }
+
+       g_dbus_proxy_call (
+               wk_editor->priv->web_extension,
+               "ImageElementSetHeight",
+               g_variant_new (
+                       "(tsi)", current_page_id (wk_editor), "-x-evo-current-img", value),
+               G_DBUS_CALL_FLAGS_NONE,
+               -1,
+               NULL,
+               NULL,
+               NULL);
+}
+
+static void
+webkit_editor_image_set_width (EContentEditor *editor,
+                               gint value)
+{
+       EWebKitEditor *wk_editor;
+
+       wk_editor = E_WEBKIT_EDITOR (editor);
+
+       if (!wk_editor->priv->web_extension) {
+               g_warning ("EHTMLEditorWebExtension not ready at %s!", G_STRFUNC);
+               return;
+       }
+
+       g_dbus_proxy_call (
+               wk_editor->priv->web_extension,
+               "ImageElementSetWidth",
+               g_variant_new (
+                       "(tsi)", current_page_id (wk_editor), "-x-evo-current-img", value),
+               G_DBUS_CALL_FLAGS_NONE,
+               -1,
+               NULL,
+               NULL,
+               NULL);
+}
+
+static void
+webkit_editor_image_set_height_follow (EContentEditor *editor,
+                                      gboolean value)
+{
+       EWebKitEditor *wk_editor;
+
+       wk_editor = E_WEBKIT_EDITOR (editor);
+
+       if (value)
+               webkit_editor_set_element_attribute (
+                       wk_editor, "#-x-evo-current-img", "style", "height: auto;");
+       else
+               webkit_editor_remove_element_attribute (
+                       wk_editor, "#-x-evo-current-img", "style");
+}
+
+static void
+webkit_editor_image_set_width_follow (EContentEditor *editor,
+                                     gboolean value)
+{
+       EWebKitEditor *wk_editor;
+
+       wk_editor = E_WEBKIT_EDITOR (editor);
+
+       if (value)
+               webkit_editor_set_element_attribute (
+                       wk_editor, "#-x-evo-current-img", "style", "width: auto;");
+       else
+               webkit_editor_remove_element_attribute (
+                       wk_editor, "#-x-evo-current-img", "style");
+}
+
+static gint32
+webkit_editor_image_get_width (EContentEditor *editor)
+{
+       EWebKitEditor *wk_editor;
+       GVariant *result;
+       gint32 value = 0;
+
+       wk_editor = E_WEBKIT_EDITOR (editor);
+
+       if (!wk_editor->priv->web_extension) {
+               g_warning ("EHTMLEditorWebExtension not ready at %s!", G_STRFUNC);
+               return 0;
+       }
+
+       result = g_dbus_proxy_call_sync_wrapper (
+               wk_editor->priv->web_extension,
+               "ImageElementGetWidth",
+               g_variant_new ("(ts)", current_page_id (wk_editor), "-x-evo-current-img"),
+               G_DBUS_CALL_FLAGS_NONE,
+               -1,
+               NULL,
+               NULL);
+
+       if (result) {
+               g_variant_get (result, "(i)", &value);
+               g_variant_unref (result);
+       }
+
+       return value;
+}
+
+static gint32
+webkit_editor_image_get_height (EContentEditor *editor)
+{
+       EWebKitEditor *wk_editor;
+       GVariant *result;
+       gint32 value = 0;
+
+       wk_editor = E_WEBKIT_EDITOR (editor);
+
+       if (!wk_editor->priv->web_extension) {
+               g_warning ("EHTMLEditorWebExtension not ready at %s!", G_STRFUNC);
+               return 0;
+       }
+
+       result = g_dbus_proxy_call_sync_wrapper (
+               wk_editor->priv->web_extension,
+               "ImageElementGetHeight",
+               g_variant_new ("(ts)", current_page_id (wk_editor), "-x-evo-current-img"),
+               G_DBUS_CALL_FLAGS_NONE,
+               -1,
+               NULL,
+               NULL);
+
+       if (result) {
+               g_variant_get (result, "(i)", &value);
+               g_variant_unref (result);
+       }
+
+       return value;
+}
+
+static void
+webkit_editor_selection_unlink (EContentEditor *editor)
+{
+       EWebKitEditor *wk_editor;
+
+       wk_editor = E_WEBKIT_EDITOR (editor);
+
+       webkit_editor_call_simple_extension_function (
+               wk_editor, "EEditorLinkDialogUnlink");
+}
+
+static void
+webkit_editor_on_link_dialog_open (EContentEditor *editor)
+{
+       EWebKitEditor *wk_editor;
+
+       wk_editor = E_WEBKIT_EDITOR (editor);
+
+       webkit_editor_call_simple_extension_function (
+               wk_editor, "EEditorLinkDialogOnOpen");
+}
+
+static void
+webkit_editor_on_link_dialog_close (EContentEditor *editor)
+{
+       EWebKitEditor *wk_editor;
+
+       wk_editor = E_WEBKIT_EDITOR (editor);
+
+       webkit_editor_call_simple_extension_function (
+               wk_editor, "EEditorLinkDialogOnClose");
+}
+
+static void
+webkit_editor_link_set_values (EContentEditor *editor,
+                               const gchar *href,
+                               const gchar *text)
+{
+       EWebKitEditor *wk_editor;
+
+       wk_editor = E_WEBKIT_EDITOR (editor);
+
+       if (!wk_editor->priv->web_extension) {
+               g_warning ("EHTMLEditorWebExtension not ready at %s!", G_STRFUNC);
+               return;
+       }
+
+       g_dbus_proxy_call (
+               wk_editor->priv->web_extension,
+               "EEditorLinkDialogOk",
+               g_variant_new ("(tss)", current_page_id (wk_editor), href, text),
+               G_DBUS_CALL_FLAGS_NONE,
+               -1,
+               NULL,
+               NULL,
+               NULL);
+}
+
+static void
+webkit_editor_link_get_values (EContentEditor *editor,
+                               gchar **href,
+                               gchar **text)
+{
+       EWebKitEditor *wk_editor;
+       GVariant *result;
+
+       wk_editor = E_WEBKIT_EDITOR (editor);
+
+       if (!wk_editor->priv->web_extension) {
+               g_warning ("EHTMLEditorWebExtension not ready at %s!", G_STRFUNC);
+               return;
+       }
+
+       result = g_dbus_proxy_call_sync_wrapper (
+               wk_editor->priv->web_extension,
+               "EEditorLinkDialogShow",
+               g_variant_new ("(t)", current_page_id (wk_editor)),
+               G_DBUS_CALL_FLAGS_NONE,
+               -1,
+               NULL,
+               NULL);
+
+       if (result) {
+               g_variant_get (result, "(ss)", href, text);
+               g_variant_unref (result);
+       } else {
+               *href = NULL;
+               *text = NULL;
+       }
+}
+
+static void
+webkit_editor_set_alignment (EWebKitEditor *wk_editor,
+                             EContentEditorAlignment value)
+{
+       g_return_if_fail (E_IS_WEBKIT_EDITOR (wk_editor));
+
+       webkit_editor_set_format_int (
+               wk_editor, "DOMSelectionSetAlignment", (gint32) value);
+}
+
+static EContentEditorAlignment
+webkit_editor_get_alignment (EWebKitEditor *wk_editor)
+{
+       g_return_val_if_fail (E_IS_WEBKIT_EDITOR (wk_editor), E_CONTENT_EDITOR_ALIGNMENT_LEFT);
+
+       return wk_editor->priv->alignment;
+}
+
+static void
+webkit_editor_set_block_format (EWebKitEditor *wk_editor,
+                                EContentEditorBlockFormat value)
+{
+       g_return_if_fail (E_IS_WEBKIT_EDITOR (wk_editor));
+
+       webkit_editor_set_format_int (
+               wk_editor, "DOMSelectionSetBlockFormat", (gint32) value);
+}
+
+static EContentEditorBlockFormat
+webkit_editor_get_block_format (EWebKitEditor *wk_editor)
+{
+       g_return_val_if_fail (E_IS_WEBKIT_EDITOR (wk_editor), E_CONTENT_EDITOR_BLOCK_FORMAT_NONE);
+
+       return wk_editor->priv->block_format;
+}
+
+static void
+webkit_editor_set_background_color (EWebKitEditor *wk_editor,
+                                    const GdkRGBA *value)
+{
+       gchar *color;
+
+       g_return_if_fail (E_IS_WEBKIT_EDITOR (wk_editor));
+
+       if (gdk_rgba_equal (value, wk_editor->priv->background_color))
+               return;
+
+       color = g_strdup_printf ("#%06x", e_rgba_to_value (value));
+
+       if (wk_editor->priv->background_color)
+               gdk_rgba_free (wk_editor->priv->background_color);
+
+       wk_editor->priv->background_color = gdk_rgba_copy (value);
+
+       webkit_editor_set_format_string (
+               wk_editor,
+               "background-color",
+               "DOMSelectionSetBackgroundColor",
+               color);
+
+       g_free (color);
+}
+
+static const GdkRGBA *
+webkit_editor_get_background_color (EWebKitEditor *wk_editor)
+{
+       g_return_val_if_fail (E_IS_WEBKIT_EDITOR (wk_editor), NULL);
+
+       if (!wk_editor->priv->web_extension) {
+               g_warning ("EHTMLEditorWebExtension not ready at %s!", G_STRFUNC);
+               return NULL;
+       }
+
+       if (!wk_editor->priv->html_mode || !wk_editor->priv->background_color)
+               return &white;
+
+       return wk_editor->priv->background_color;
+}
+
+static void
+webkit_editor_set_font_name (EWebKitEditor *wk_editor,
+                             const gchar *value)
+{
+       g_return_if_fail (E_IS_WEBKIT_EDITOR (wk_editor));
+
+       wk_editor->priv->font_name = g_strdup (value);
+
+       webkit_editor_set_format_string (
+               wk_editor, "font-name", "DOMSelectionSetFontName", value);
+}
+
+static const gchar *
+webkit_editor_get_font_name (EWebKitEditor *wk_editor)
+{
+       g_return_val_if_fail (E_IS_WEBKIT_EDITOR (wk_editor), NULL);
+
+       return wk_editor->priv->font_name;
+}
+
+static void
+webkit_editor_set_font_color (EWebKitEditor *wk_editor,
+                              const GdkRGBA *value)
+{
+       gchar *color;
+
+       g_return_if_fail (E_IS_WEBKIT_EDITOR (wk_editor));
+
+       if (gdk_rgba_equal (value, wk_editor->priv->font_color))
+               return;
+
+       color = g_strdup_printf ("#%06x", e_rgba_to_value (value));
+
+       if (wk_editor->priv->font_color)
+               gdk_rgba_free (wk_editor->priv->font_color);
+
+       wk_editor->priv->font_color = gdk_rgba_copy (value);
+
+       webkit_editor_set_format_string (
+               wk_editor,
+               "font-color",
+               "DOMSelectionSetFontColor",
+               color);
+
+       g_free (color);
+}
+
+static const GdkRGBA *
+webkit_editor_get_font_color (EWebKitEditor *wk_editor)
+{
+       g_return_val_if_fail (E_IS_WEBKIT_EDITOR (wk_editor), NULL);
+
+       if (!wk_editor->priv->web_extension) {
+               g_warning ("EHTMLEditorWebExtension not ready at %s!", G_STRFUNC);
+               return NULL;
+       }
+
+       if (!wk_editor->priv->html_mode || !wk_editor->priv->font_color)
+               return &black;
+
+       return wk_editor->priv->font_color;
+}
+
+static void
+webkit_editor_set_font_size (EWebKitEditor *wk_editor,
+                             gint value)
+{
+       g_return_if_fail (E_IS_WEBKIT_EDITOR (wk_editor));
+
+       if (wk_editor->priv->font_size == value)
+               return;
+
+       wk_editor->priv->font_size = value;
+
+       webkit_editor_set_format_int (
+               wk_editor, "DOMSelectionSetFontSize", value);
+}
+
+static gint
+webkit_editor_get_font_size (EWebKitEditor *wk_editor)
+{
+       g_return_val_if_fail (E_IS_WEBKIT_EDITOR (wk_editor), -1);
+
+       return wk_editor->priv->font_size;
+}
+
+static void
+webkit_editor_set_style_flag (EWebKitEditor *wk_editor,
+                             EContentEditorStyleFlags flag,
+                             gboolean do_set,
+                             const gchar *dom_function_name)
+{
+       g_return_if_fail (E_IS_WEBKIT_EDITOR (wk_editor));
+
+       if (((wk_editor->priv->style_flags & flag) != 0 ? 1 : 0) == (do_set ? 1 : 0))
+               return;
+
+       wk_editor->priv->style_flags = (wk_editor->priv->style_flags & ~flag) | (do_set ? flag : 0);
+
+       webkit_editor_set_format_boolean (wk_editor, dom_function_name, do_set);
+}
+
+static gboolean
+webkit_editor_get_style_flag (EWebKitEditor *wk_editor,
+                             EContentEditorStyleFlags flag)
+{
+       g_return_val_if_fail (E_IS_WEBKIT_EDITOR (wk_editor), FALSE);
+
+       return (wk_editor->priv->style_flags & flag) != 0;
+}
+
+static void
+webkit_editor_page_set_text_color (EContentEditor *editor,
+                                   const GdkRGBA *value)
+{
+       EWebKitEditor *wk_editor;
+       gchar *color;
+
+       wk_editor = E_WEBKIT_EDITOR (editor);
+
+       color = g_strdup_printf ("#%06x", e_rgba_to_value (value));
+
+       webkit_editor_set_element_attribute (wk_editor, "body", "text", color);
+
+       g_free (color);
+}
+
+static void
+webkit_editor_page_get_text_color (EContentEditor *editor,
+                                   GdkRGBA *color)
+{
+       EWebKitEditor *wk_editor;
+       GVariant *result;
+
+       wk_editor = E_WEBKIT_EDITOR (editor);
+
+       if (!wk_editor->priv->html_mode)
+               goto theme;
+
+       result = webkit_editor_get_element_attribute (wk_editor, "body", "text");
+       if (result) {
+               const gchar *value;
+
+               g_variant_get (result, "(&s)", &value);
+               if (!value || !*value || !gdk_rgba_parse (color, value)) {
+                       g_variant_unref (result);
+                       goto theme;
+               }
+               g_variant_unref (result);
+               return;
+       }
+
+ theme:
+       e_utils_get_theme_color (
+               GTK_WIDGET (wk_editor),
+               "theme_text_color",
+               E_UTILS_DEFAULT_THEME_TEXT_COLOR,
+               color);
+}
+
+static void
+webkit_editor_page_set_background_color (EContentEditor *editor,
+                                         const GdkRGBA *value)
+{
+       EWebKitEditor *wk_editor;
+       gchar *color;
+
+       wk_editor = E_WEBKIT_EDITOR (editor);
+
+       if (value->alpha != 0.0)
+               color = g_strdup_printf ("#%06x", e_rgba_to_value (value));
+       else
+               color = g_strdup ("");
+
+       webkit_editor_set_element_attribute (wk_editor, "body", "bgcolor", color);
+
+       g_free (color);
+}
+
+static void
+webkit_editor_page_get_background_color (EContentEditor *editor,
+                                         GdkRGBA *color)
+{
+       EWebKitEditor *wk_editor;
+       GVariant *result;
+
+       wk_editor = E_WEBKIT_EDITOR (editor);
+
+       if (!wk_editor->priv->html_mode)
+               goto theme;
+
+       result = webkit_editor_get_element_attribute (wk_editor, "body", "bgcolor");
+       if (result) {
+               const gchar *value;
+
+               g_variant_get (result, "(&s)", &value);
+               if (!value || !*value || !gdk_rgba_parse (color, value)) {
+                       g_variant_unref (result);
+                       goto theme;
+               }
+               g_variant_unref (result);
+               return;
+       }
+
+ theme:
+       e_utils_get_theme_color (
+               GTK_WIDGET (wk_editor),
+               "theme_base_color",
+               E_UTILS_DEFAULT_THEME_BASE_COLOR,
+               color);
+}
+
+static void
+webkit_editor_page_set_link_color (EContentEditor *editor,
+                                   const GdkRGBA *value)
+{
+       EWebKitEditor *wk_editor;
+       gchar *color;
+
+       wk_editor = E_WEBKIT_EDITOR (editor);
+
+       color = g_strdup_printf ("#%06x", e_rgba_to_value (value));
+
+       webkit_editor_set_element_attribute (wk_editor, "body", "link", color);
+
+       g_free (color);
+}
+
+static void
+webkit_editor_page_get_link_color (EContentEditor *editor,
+                                   GdkRGBA *color)
+{
+       EWebKitEditor *wk_editor;
+       GVariant *result;
+
+       wk_editor = E_WEBKIT_EDITOR (editor);
+
+       if (!wk_editor->priv->html_mode)
+               goto theme;
+
+       result = webkit_editor_get_element_attribute (wk_editor, "body", "link");
+       if (result) {
+               const gchar *value;
+
+               g_variant_get (result, "(&s)", &value);
+               if (!value || !*value || !gdk_rgba_parse (color, value)) {
+                       g_variant_unref (result);
+                       goto theme;
+               }
+               g_variant_unref (result);
+               return;
+       }
+
+ theme:
+       color->alpha = 1;
+       color->red = 0;
+       color->green = 0;
+       color->blue = 1;
+}
+
+static void
+webkit_editor_page_set_visited_link_color (EContentEditor *editor,
+                                           const GdkRGBA *value)
+{
+       EWebKitEditor *wk_editor;
+       gchar *color;
+
+       wk_editor = E_WEBKIT_EDITOR (editor);
+
+       color = g_strdup_printf ("#%06x", e_rgba_to_value (value));
+
+       webkit_editor_set_element_attribute (wk_editor, "body", "vlink", color);
+
+       g_free (color);
+}
+
+static void
+webkit_editor_page_get_visited_link_color (EContentEditor *editor,
+                                           GdkRGBA *color)
+{
+       EWebKitEditor *wk_editor;
+       GVariant *result;
+
+       wk_editor = E_WEBKIT_EDITOR (editor);
+
+       if (!wk_editor->priv->html_mode)
+               goto theme;
+
+       result = webkit_editor_get_element_attribute (wk_editor, "body", "vlink");
+       if (result) {
+               const gchar *value;
+
+               g_variant_get (result, "(&s)", &value);
+               if (!value || !*value || !gdk_rgba_parse (color, value)) {
+                       g_variant_unref (result);
+                       goto theme;
+               }
+               g_variant_unref (result);
+               return;
+       }
+
+ theme:
+       color->alpha = 1;
+       color->red = 1;
+       color->green = 0;
+       color->blue = 0;
+}
+
+static void
+webkit_editor_on_page_dialog_open (EContentEditor *editor)
+{
+       EWebKitEditor *wk_editor;
+
+       wk_editor = E_WEBKIT_EDITOR (editor);
+
+       webkit_editor_call_simple_extension_function (
+               wk_editor, "EEditorPageDialogSaveHistory");
+}
+
+static void
+webkit_editor_on_page_dialog_close (EContentEditor *editor)
+{
+       EWebKitEditor *wk_editor;
+
+       wk_editor = E_WEBKIT_EDITOR (editor);
+
+       webkit_editor_call_simple_extension_function (
+               wk_editor, "EEditorPageDialogSaveHistoryOnExit");
+}
+
+static gchar *
+webkit_editor_page_get_background_image_uri (EContentEditor *editor)
+{
+       EWebKitEditor *wk_editor;
+       GVariant *result;
+
+       wk_editor = E_WEBKIT_EDITOR (editor);
+
+       if (!wk_editor->priv->html_mode)
+               return NULL;
+
+       result = webkit_editor_get_element_attribute (wk_editor, "body", "data-uri");
+       if (result) {
+               gchar *value;
+
+               g_variant_get (result, "(s)", &value);
+               g_variant_unref (result);
+       }
+
+       return NULL;
+}
+
+static void
+webkit_editor_page_set_background_image_uri (EContentEditor *editor,
+                                             const gchar *uri)
+{
+       EWebKitEditor *wk_editor;
+
+       wk_editor = E_WEBKIT_EDITOR (editor);
+
+       if (!wk_editor->priv->html_mode)
+               return;
+
+       if (uri && *uri)
+               webkit_editor_replace_image_src (wk_editor, "body", uri);
+       else {
+               g_dbus_proxy_call (
+                       wk_editor->priv->web_extension,
+                       "RemoveImageAttributesFromElementBySelector",
+                       g_variant_new ("(ts)", current_page_id (wk_editor), "body"),
+                       G_DBUS_CALL_FLAGS_NONE,
+                       -1,
+                       NULL,
+                       NULL,
+                       NULL);
+       }
+}
+
+static void
+webkit_editor_on_cell_dialog_open (EContentEditor *editor)
+{
+       EWebKitEditor *wk_editor;
+
+       wk_editor = E_WEBKIT_EDITOR (editor);
+
+       if (!wk_editor->priv->web_extension) {
+               g_warning ("EHTMLEditorWebExtension not ready at %s!", G_STRFUNC);
+               return;
+       }
+
+       g_dbus_proxy_call (
+               wk_editor->priv->web_extension,
+               "EEditorCellDialogMarkCurrentCellElement",
+               g_variant_new ("(ts)", current_page_id (wk_editor), "-x-evo-current-cell"),
+               G_DBUS_CALL_FLAGS_NONE,
+               -1,
+               NULL,
+               NULL,
+               NULL);
+}
+
+static void
+webkit_editor_on_cell_dialog_close (EContentEditor *editor)
+{
+       EWebKitEditor *wk_editor;
+
+       wk_editor = E_WEBKIT_EDITOR (editor);
+
+       webkit_editor_call_simple_extension_function (
+               wk_editor, "EEditorCellDialogSaveHistoryOnExit");
+}
+
+static void
+webkit_editor_cell_set_v_align (EContentEditor *editor,
+                                const gchar *value,
+                                EContentEditorScope scope)
+{
+       EWebKitEditor *wk_editor;
+
+       wk_editor = E_WEBKIT_EDITOR (editor);
+
+       if (!wk_editor->priv->html_mode)
+               return;
+
+       if (!wk_editor->priv->web_extension) {
+               g_warning ("EHTMLEditorWebExtension not ready at %s!", G_STRFUNC);
+               return;
+       }
+
+       g_dbus_proxy_call (
+               wk_editor->priv->web_extension,
+               "EEditorCellDialogSetElementVAlign",
+               g_variant_new ("(tsi)", current_page_id (wk_editor), value, (gint32) scope),
+               G_DBUS_CALL_FLAGS_NONE,
+               -1,
+               NULL,
+               NULL,
+               NULL);
+}
+
+static gchar *
+webkit_editor_cell_get_v_align (EContentEditor *editor)
+{
+       EWebKitEditor *wk_editor;
+       gchar *value = NULL;
+       GVariant *result;
+
+       wk_editor = E_WEBKIT_EDITOR (editor);
+
+       if (!wk_editor->priv->html_mode)
+               return NULL;
+
+       result = webkit_editor_get_element_attribute (
+               wk_editor, "#-x-evo-current-cell", "valign");
+       if (result) {
+               g_variant_get (result, "(s)", &value);
+               g_variant_unref (result);
+       }
+
+       return value;
+}
+
+static void
+webkit_editor_cell_set_align (EContentEditor *editor,
+                              const gchar *value,
+                              EContentEditorScope scope)
+{
+       EWebKitEditor *wk_editor;
+
+       wk_editor = E_WEBKIT_EDITOR (editor);
+
+       if (!wk_editor->priv->html_mode)
+               return;
+
+       if (!wk_editor->priv->web_extension) {
+               g_warning ("EHTMLEditorWebExtension not ready at %s!", G_STRFUNC);
+               return;
+       }
+
+       g_dbus_proxy_call (
+               wk_editor->priv->web_extension,
+               "EEditorCellDialogSetElementAlign",
+               g_variant_new ("(tsi)", current_page_id (wk_editor), value, (gint32) scope),
+               G_DBUS_CALL_FLAGS_NONE,
+               -1,
+               NULL,
+               NULL,
+               NULL);
+}
+
+static gchar *
+webkit_editor_cell_get_align (EContentEditor *editor)
+{
+       EWebKitEditor *wk_editor;
+       gchar *value = NULL;
+       GVariant *result;
+
+       wk_editor = E_WEBKIT_EDITOR (editor);
+
+       if (!wk_editor->priv->html_mode)
+               return NULL;
+
+       result = webkit_editor_get_element_attribute (
+               wk_editor, "#-x-evo-current-cell", "align");
+       if (result) {
+               g_variant_get (result, "(s)", &value);
+               g_variant_unref (result);
+       }
+
+       return value;
+}
+
+static void
+webkit_editor_cell_set_wrap (EContentEditor *editor,
+                             gboolean value,
+                             EContentEditorScope scope)
+{
+       EWebKitEditor *wk_editor;
+
+       wk_editor = E_WEBKIT_EDITOR (editor);
+
+       if (!wk_editor->priv->html_mode)
+               return;
+
+       if (!wk_editor->priv->web_extension) {
+               g_warning ("EHTMLEditorWebExtension not ready at %s!", G_STRFUNC);
+               return;
+       }
+
+       g_dbus_proxy_call (
+               wk_editor->priv->web_extension,
+               "EEditorCellDialogSetElementNoWrap",
+               g_variant_new ("(tbi)", current_page_id (wk_editor), !value, (gint32) scope),
+               G_DBUS_CALL_FLAGS_NONE,
+               -1,
+               NULL,
+               NULL,
+               NULL);
+}
+
+static gboolean
+webkit_editor_cell_get_wrap (EContentEditor *editor)
+{
+       EWebKitEditor *wk_editor;
+       gboolean value = FALSE;
+       GVariant *result;
+
+       wk_editor = E_WEBKIT_EDITOR (editor);
+
+       if (!wk_editor->priv->html_mode)
+               return FALSE;
+
+       if (!wk_editor->priv->web_extension) {
+               g_warning ("EHTMLEditorWebExtension not ready at %s!", G_STRFUNC);
+               return FALSE;
+       }
+
+       result = g_dbus_proxy_call_sync_wrapper (
+               wk_editor->priv->web_extension,
+               "TableCellElementGetNoWrap",
+               g_variant_new ("(ts)", current_page_id (wk_editor), "-x-evo-current-cell"),
+               G_DBUS_CALL_FLAGS_NONE,
+               -1,
+               NULL,
+               NULL);
+
+       if (result) {
+               g_variant_get (result, "(b)", &value);
+               value = !value;
+               g_variant_unref (result);
+       }
+
+       return value;
+}
+
+static void
+webkit_editor_cell_set_header_style (EContentEditor *editor,
+                                     gboolean value,
+                                     EContentEditorScope scope)
+{
+       EWebKitEditor *wk_editor;
+
+       wk_editor = E_WEBKIT_EDITOR (editor);
+
+       if (!wk_editor->priv->web_extension) {
+               g_warning ("EHTMLEditorWebExtension not ready at %s!", G_STRFUNC);
+               return;
+       }
+
+       if (!wk_editor->priv->html_mode)
+               return;
+
+       g_dbus_proxy_call (
+               wk_editor->priv->web_extension,
+               "EEditorCellDialogSetElementHeaderStyle",
+               g_variant_new ("(tbi)", current_page_id (wk_editor), value, (gint32) scope),
+               G_DBUS_CALL_FLAGS_NONE,
+               -1,
+               NULL,
+               NULL,
+               NULL);
+}
+
+static gboolean
+webkit_editor_cell_is_header (EContentEditor *editor)
+{
+       EWebKitEditor *wk_editor;
+       gboolean value = FALSE;
+       GVariant *result;
+
+       wk_editor = E_WEBKIT_EDITOR (editor);
+
+       if (!wk_editor->priv->html_mode)
+               return FALSE;
+
+       if (!wk_editor->priv->web_extension) {
+               g_warning ("EHTMLEditorWebExtension not ready at %s!", G_STRFUNC);
+               return FALSE;
+       }
+
+       result = g_dbus_proxy_call_sync_wrapper (
+               wk_editor->priv->web_extension,
+               "ElementGetTagName",
+               g_variant_new ("(ts)", current_page_id (wk_editor), "-x-evo-current-cell"),
+               G_DBUS_CALL_FLAGS_NONE,
+               -1,
+               NULL,
+               NULL);
+
+       if (result) {
+               const gchar *tag_name;
+
+               g_variant_get (result, "(&s)", &tag_name);
+               value = g_ascii_strncasecmp (tag_name, "TH", 2) == 0;
+               g_variant_unref (result);
+       }
+
+       return value;
+}
+
+static gint
+webkit_editor_cell_get_width (EContentEditor *editor,
+                              EContentEditorUnit *unit)
+{
+       EWebKitEditor *wk_editor;
+       gint value = 0;
+       GVariant *result;
+
+       wk_editor = E_WEBKIT_EDITOR (editor);
+
+       *unit = E_CONTENT_EDITOR_UNIT_AUTO;
+
+       if (!wk_editor->priv->html_mode)
+               return 0;
+
+       result = webkit_editor_get_element_attribute (
+               wk_editor, "#-x-evo-current-cell", "width");
+
+       if (result) {
+               const gchar *width;
+
+               g_variant_get (result, "(&s)", &width);
+               if (width && *width) {
+                       value = atoi (width);
+                       if (strstr (width, "%"))
+                               *unit = E_CONTENT_EDITOR_UNIT_PERCENTAGE;
+                       else if (g_ascii_strncasecmp (width, "auto", 4) != 0)
+                               *unit = E_CONTENT_EDITOR_UNIT_PIXEL;
+               }
+               g_variant_unref (result);
+       }
+
+       return value;
+}
+
+static gint
+webkit_editor_cell_get_row_span (EContentEditor *editor)
+{
+       EWebKitEditor *wk_editor;
+       gint value = 0;
+       GVariant *result;
+
+       wk_editor = E_WEBKIT_EDITOR (editor);
+
+       if (!wk_editor->priv->html_mode)
+               return 0;
+
+       if (!wk_editor->priv->web_extension) {
+               g_warning ("EHTMLEditorWebExtension not ready at %s!", G_STRFUNC);
+               return 0;
+       }
+
+       result = g_dbus_proxy_call_sync_wrapper (
+               wk_editor->priv->web_extension,
+               "TableCellElementGetRowSpan",
+               g_variant_new ("(ts)", current_page_id (wk_editor), "-x-evo-current-cell"),
+               G_DBUS_CALL_FLAGS_NONE,
+               -1,
+               NULL,
+               NULL);
+
+       if (result) {
+               g_variant_get (result, "(i)", &value);
+               g_variant_unref (result);
+       }
+
+       return value;
+}
+
+static gint
+webkit_editor_cell_get_col_span (EContentEditor *editor)
+{
+       EWebKitEditor *wk_editor;
+       gint value = 0;
+       GVariant *result;
+
+       wk_editor = E_WEBKIT_EDITOR (editor);
+
+       if (!wk_editor->priv->html_mode)
+               return 0;
+
+       if (!wk_editor->priv->web_extension) {
+               g_warning ("EHTMLEditorWebExtension not ready at %s!", G_STRFUNC);
+               return 0;
+       }
+
+       result = g_dbus_proxy_call_sync_wrapper (
+               wk_editor->priv->web_extension,
+               "TableCellElementGetColSpan",
+               g_variant_new ("(ts)", current_page_id (wk_editor), "-x-evo-current-cell"),
+               G_DBUS_CALL_FLAGS_NONE,
+               -1,
+               NULL,
+               NULL);
+
+       if (result) {
+               g_variant_get (result, "(i)", &value);
+               g_variant_unref (result);
+       }
+
+       return value;
+}
+
+static gchar *
+webkit_editor_cell_get_background_image_uri (EContentEditor *editor)
+{
+       EWebKitEditor *wk_editor;
+       GVariant *result;
+
+       wk_editor = E_WEBKIT_EDITOR (editor);
+
+       if (!wk_editor->priv->html_mode)
+               return NULL;
+
+       result = webkit_editor_get_element_attribute (
+               wk_editor, "#-x-evo-current-cell", "data-uri");
+       if (result) {
+               gchar *value;
+
+               g_variant_get (result, "(s)", &value);
+               g_variant_unref (result);
+       }
+
+       return NULL;
+}
+
+static void
+webkit_editor_cell_get_background_color (EContentEditor *editor,
+                                         GdkRGBA *color)
+{
+       EWebKitEditor *wk_editor;
+       GVariant *result;
+
+       wk_editor = E_WEBKIT_EDITOR (editor);
+
+       if (!wk_editor->priv->html_mode)
+               goto exit;
+
+       result = webkit_editor_get_element_attribute (
+               wk_editor, "#-x-evo-current-cell", "bgcolor");
+       if (result) {
+               const gchar *value;
+
+               g_variant_get (result, "(&s)", &value);
+               if (!value || !*value || !gdk_rgba_parse (color, value)) {
+                       g_variant_unref (result);
+                       goto exit;
+               }
+               g_variant_unref (result);
+               return;
+       }
+
+ exit:
+       *color = transparent;
+}
+
+static void
+webkit_editor_cell_set_row_span (EContentEditor *editor,
+                                 gint value,
+                                 EContentEditorScope scope)
+{
+       EWebKitEditor *wk_editor;
+
+       wk_editor = E_WEBKIT_EDITOR (editor);
+
+       if (!wk_editor->priv->html_mode)
+               return;
+
+       if (!wk_editor->priv->web_extension) {
+               g_warning ("EHTMLEditorWebExtension not ready at %s!", G_STRFUNC);
+               return;
+       }
+
+       g_dbus_proxy_call (
+               wk_editor->priv->web_extension,
+               "EEditorCellDialogSetElementRowSpan",
+               g_variant_new ("(tii)", current_page_id (wk_editor), value, (gint32) scope),
+               G_DBUS_CALL_FLAGS_NONE,
+               -1,
+               NULL,
+               NULL,
+               NULL);
+}
+
+static void
+webkit_editor_cell_set_col_span (EContentEditor *editor,
+                                 gint value,
+                                 EContentEditorScope scope)
+{
+       EWebKitEditor *wk_editor;
+
+       wk_editor = E_WEBKIT_EDITOR (editor);
+
+       if (!wk_editor->priv->html_mode)
+               return;
+
+       if (!wk_editor->priv->web_extension) {
+               g_warning ("EHTMLEditorWebExtension not ready at %s!", G_STRFUNC);
+               return;
+       }
+
+       g_dbus_proxy_call (
+               wk_editor->priv->web_extension,
+               "EEditorCellDialogSetElementColSpan",
+               g_variant_new ("(tii)", current_page_id (wk_editor), value, (gint32) scope),
+               G_DBUS_CALL_FLAGS_NONE,
+               -1,
+               NULL,
+               NULL,
+               NULL);
+}
+
+static void
+webkit_editor_cell_set_width (EContentEditor *editor,
+                              gint value,
+                              EContentEditorUnit unit,
+                              EContentEditorScope scope)
+{
+       EWebKitEditor *wk_editor;
+       gchar *width;
+
+       wk_editor = E_WEBKIT_EDITOR (editor);
+
+       if (!wk_editor->priv->html_mode)
+               return;
+
+       if (!wk_editor->priv->web_extension) {
+               g_warning ("EHTMLEditorWebExtension not ready at %s!", G_STRFUNC);
+               return;
+       }
+
+       if (unit == E_CONTENT_EDITOR_UNIT_AUTO)
+               width = g_strdup ("auto");
+       else
+               width = g_strdup_printf (
+                       "%d%s",
+                       value,
+                       (unit == E_CONTENT_EDITOR_UNIT_PIXEL) ? "px" : "%");
+
+       g_dbus_proxy_call (
+               wk_editor->priv->web_extension,
+               "EEditorCellDialogSetElementWidth",
+               g_variant_new ("(tsi)", current_page_id (wk_editor), width, (gint32) scope),
+               G_DBUS_CALL_FLAGS_NONE,
+               -1,
+               NULL,
+               NULL,
+               NULL);
+
+       g_free (width);
+}
+
+static void
+webkit_editor_cell_set_background_color (EContentEditor *editor,
+                                         const GdkRGBA *value,
+                                         EContentEditorScope scope)
+{
+       EWebKitEditor *wk_editor;
+       gchar *color;
+
+       wk_editor = E_WEBKIT_EDITOR (editor);
+
+       if (!wk_editor->priv->web_extension) {
+               g_warning ("EHTMLEditorWebExtension not ready at %s!", G_STRFUNC);
+               return;
+       }
+
+       if (value->alpha != 0.0)
+               color = g_strdup_printf ("#%06x", e_rgba_to_value (value));
+       else
+               color = g_strdup ("");
+
+       g_dbus_proxy_call (
+               wk_editor->priv->web_extension,
+               "EEditorCellDialogSetElementBgColor",
+               g_variant_new ("(tsi)", current_page_id (wk_editor), color, (gint32) scope),
+               G_DBUS_CALL_FLAGS_NONE,
+               -1,
+               NULL,
+               NULL,
+               NULL);
+
+       g_free (color);
+}
+
+static void
+webkit_editor_cell_set_background_image_uri (EContentEditor *editor,
+                                             const gchar *uri)
+{
+       EWebKitEditor *wk_editor;
+
+       wk_editor = E_WEBKIT_EDITOR (editor);
+
+       if (!wk_editor->priv->web_extension) {
+               g_warning ("EHTMLEditorWebExtension not ready at %s!", G_STRFUNC);
+               return;
+       }
+
+       if (!wk_editor->priv->html_mode)
+               return;
+
+       if (uri && *uri)
+               webkit_editor_replace_image_src (wk_editor, "#-x-evo-current-cell", uri);
+       else {
+               g_dbus_proxy_call (
+                       wk_editor->priv->web_extension,
+                       "RemoveImageAttributesFromElementBySelector",
+                       g_variant_new ("(ts)", current_page_id (wk_editor), "#-x-evo-current-cell"),
+                       G_DBUS_CALL_FLAGS_NONE,
+                       -1,
+                       NULL,
+                       NULL,
+                       NULL);
+       }
+}
+
+static void
+webkit_editor_table_set_row_count (EContentEditor *editor,
+                                   guint value)
+{
+       EWebKitEditor *wk_editor;
+
+       wk_editor = E_WEBKIT_EDITOR (editor);
+
+       if (!wk_editor->priv->html_mode)
+               return;
+
+       if (!wk_editor->priv->web_extension) {
+               g_warning ("EHTMLEditorWebExtension not ready at %s!", G_STRFUNC);
+               return;
+       }
+
+       g_dbus_proxy_call (
+               wk_editor->priv->web_extension,
+               "EEditorTableDialogSetRowCount",
+               g_variant_new ("(tu)", current_page_id (wk_editor), value),
+               G_DBUS_CALL_FLAGS_NONE,
+               -1,
+               NULL,
+               NULL,
+               NULL);
+}
+
+static guint
+webkit_editor_table_get_row_count (EContentEditor *editor)
+{
+       EWebKitEditor *wk_editor;
+       GVariant *result;
+       guint value = 0;
+
+       wk_editor = E_WEBKIT_EDITOR (editor);
+
+       if (!wk_editor->priv->html_mode)
+               return 0;
+
+       if (!wk_editor->priv->web_extension) {
+               g_warning ("EHTMLEditorWebExtension not ready at %s!", G_STRFUNC);
+               return 0;
+       }
+
+       result = g_dbus_proxy_call_sync_wrapper (
+               wk_editor->priv->web_extension,
+               "EEditorTableDialogGetRowCount",
+               g_variant_new ("(t)", current_page_id (wk_editor)),
+               G_DBUS_CALL_FLAGS_NONE,
+               -1,
+               NULL,
+               NULL);
+
+       if (result) {
+               g_variant_get (result, "(u)", &value);
+               g_variant_unref (result);
+       }
+
+       return value;
+}
+
+static void
+webkit_editor_table_set_column_count (EContentEditor *editor,
+                                      guint value)
+{
+       EWebKitEditor *wk_editor;
+
+       wk_editor = E_WEBKIT_EDITOR (editor);
+
+       if (!wk_editor->priv->html_mode)
+               return;
+
+       if (!wk_editor->priv->web_extension) {
+               g_warning ("EHTMLEditorWebExtension not ready at %s!", G_STRFUNC);
+               return;
+       }
+
+       g_dbus_proxy_call (
+               wk_editor->priv->web_extension,
+               "EEditorTableDialogSetColumnCount",
+               g_variant_new ("(tu)", current_page_id (wk_editor), value),
+               G_DBUS_CALL_FLAGS_NONE,
+               -1,
+               NULL,
+               NULL,
+               NULL);
+}
+
+static guint
+webkit_editor_table_get_column_count (EContentEditor *editor)
+{
+       EWebKitEditor *wk_editor;
+       GVariant *result;
+       guint value = 0;
+
+       wk_editor = E_WEBKIT_EDITOR (editor);
+
+       if (!wk_editor->priv->html_mode)
+               return 0;
+
+       if (!wk_editor->priv->web_extension) {
+               g_warning ("EHTMLEditorWebExtension not ready at %s!", G_STRFUNC);
+               return 0;
+       }
+
+       result = g_dbus_proxy_call_sync_wrapper (
+               wk_editor->priv->web_extension,
+               "EEditorTableDialogGetColumnCount",
+               g_variant_new ("(t)", current_page_id (wk_editor)),
+               G_DBUS_CALL_FLAGS_NONE,
+               -1,
+               NULL,
+               NULL);
+
+       if (result) {
+               g_variant_get (result, "(u)", &value);
+               g_variant_unref (result);
+       }
+
+       return value;
+}
+
+static void
+webkit_editor_table_set_width (EContentEditor *editor,
+                               gint value,
+                               EContentEditorUnit unit)
+{
+       EWebKitEditor *wk_editor;
+       gchar *width;
+
+       wk_editor = E_WEBKIT_EDITOR (editor);
+
+       if (!wk_editor->priv->html_mode)
+               return;
+
+       if (!wk_editor->priv->web_extension) {
+               g_warning ("EHTMLEditorWebExtension not ready at %s!", G_STRFUNC);
+               return;
+       }
+
+       if (unit == E_CONTENT_EDITOR_UNIT_AUTO)
+               width = g_strdup ("auto");
+       else
+               width = g_strdup_printf (
+                       "%d%s",
+                       value,
+                       (unit == E_CONTENT_EDITOR_UNIT_PIXEL) ? "px" : "%");
+
+       webkit_editor_set_element_attribute (
+               wk_editor, "#-x-evo-current-table", "width", width);
+
+       g_free (width);
+}
+
+static guint
+webkit_editor_table_get_width (EContentEditor *editor,
+                               EContentEditorUnit *unit)
+{
+       EWebKitEditor *wk_editor;
+       guint value = 0;
+       GVariant *result;
+
+       wk_editor = E_WEBKIT_EDITOR (editor);
+
+       *unit = E_CONTENT_EDITOR_UNIT_PIXEL;
+
+       if (!wk_editor->priv->html_mode)
+               return 0;
+
+       result = webkit_editor_get_element_attribute (
+               wk_editor, "#-x-evo-current-table", "width");
+
+       if (result) {
+               const gchar *width;
+
+               g_variant_get (result, "(&s)", &width);
+               if (width && *width) {
+                       value = atoi (width);
+                       if (strstr (width, "%"))
+                               *unit = E_CONTENT_EDITOR_UNIT_PERCENTAGE;
+                       else if (g_ascii_strncasecmp (width, "auto", 4) != 0)
+                               *unit = E_CONTENT_EDITOR_UNIT_PIXEL;
+               }
+               g_variant_unref (result);
+       }
+
+       return value;
+}
+
+static void
+webkit_editor_table_set_align (EContentEditor *editor,
+                               const gchar *value)
+{
+       EWebKitEditor *wk_editor;
+
+       wk_editor = E_WEBKIT_EDITOR (editor);
+
+       if (!wk_editor->priv->html_mode)
+               return;
+
+       webkit_editor_set_element_attribute (
+               wk_editor, "#-x-evo-current-table", "align", value);
+}
+
+static gchar *
+webkit_editor_table_get_align (EContentEditor *editor)
+{
+       EWebKitEditor *wk_editor;
+       gchar *value = NULL;
+       GVariant *result;
+
+       wk_editor = E_WEBKIT_EDITOR (editor);
+
+       if (!wk_editor->priv->html_mode)
+               return NULL;
+
+       result = webkit_editor_get_element_attribute (
+               wk_editor, "#-x-evo-current-table", "align");
+       if (result) {
+               g_variant_get (result, "(s)", &value);
+               g_variant_unref (result);
+       }
+
+       return value;
+}
+
+static void
+webkit_editor_table_set_padding (EContentEditor *editor,
+                                 gint value)
+{
+       EWebKitEditor *wk_editor;
+       gchar *padding;
+
+       wk_editor = E_WEBKIT_EDITOR (editor);
+
+       padding = g_strdup_printf ("%d", value);
+
+       webkit_editor_set_element_attribute (
+               wk_editor, "#-x-evo-current-table", "cellpadding", padding);
+
+       g_free (padding);
+}
+
+static gint
+webkit_editor_table_get_padding (EContentEditor *editor)
+{
+       EWebKitEditor *wk_editor;
+       GVariant *result;
+       gint value = 0;
+
+       wk_editor = E_WEBKIT_EDITOR (editor);
+
+       result = webkit_editor_get_element_attribute (
+               wk_editor, "#-x-evo-current-table", "cellpadding");
+
+       if (result) {
+               const gchar *padding;
+
+               g_variant_get (result, "(&s)", &padding);
+               if (padding && *padding)
+                       value = atoi (padding);
+               g_variant_unref (result);
+       }
+
+       return value;
+}
+
+static void
+webkit_editor_table_set_spacing (EContentEditor *editor,
+                                 gint value)
+{
+       EWebKitEditor *wk_editor;
+       gchar *spacing;
+
+       wk_editor = E_WEBKIT_EDITOR (editor);
+
+       spacing = g_strdup_printf ("%d", value);
+
+       webkit_editor_set_element_attribute (
+               wk_editor, "#-x-evo-current-table", "cellspacing", spacing);
+
+       g_free (spacing);
+}
+
+static gint
+webkit_editor_table_get_spacing (EContentEditor *editor)
+{
+       EWebKitEditor *wk_editor;
+       GVariant *result;
+       gint value = 0;
+
+       wk_editor = E_WEBKIT_EDITOR (editor);
+
+       result = webkit_editor_get_element_attribute (
+               wk_editor, "#-x-evo-current-table", "cellspacing");
+
+       if (result) {
+               const gchar *spacing;
+
+               g_variant_get (result, "(&s)", &spacing);
+               if (spacing && *spacing)
+                       value = atoi (spacing);
+               g_variant_unref (result);
+       }
+
+       return value;
+}
+
+static void
+webkit_editor_table_set_border (EContentEditor *editor,
+                                gint value)
+{
+       EWebKitEditor *wk_editor;
+       gchar *border;
+
+       wk_editor = E_WEBKIT_EDITOR (editor);
+
+       border = g_strdup_printf ("%d", value);
+
+       webkit_editor_set_element_attribute (
+               wk_editor, "#-x-evo-current-table", "border", border);
+
+       g_free (border);
+}
+
+static gint
+webkit_editor_table_get_border (EContentEditor *editor)
+{
+       EWebKitEditor *wk_editor;
+       GVariant *result;
+       gint value = 0;
+
+       wk_editor = E_WEBKIT_EDITOR (editor);
+
+       result = webkit_editor_get_element_attribute (
+               wk_editor, "#-x-evo-current-table", "border");
+
+       if (result) {
+               const gchar *border;
+
+               g_variant_get (result, "(&s)", &border);
+               if (border && *border)
+                       value = atoi (border);
+               g_variant_unref (result);
+       }
+
+       return value;
+}
+
+static void
+webkit_editor_table_get_background_color (EContentEditor *editor,
+                                          GdkRGBA *color)
+{
+       EWebKitEditor *wk_editor;
+       GVariant *result;
+
+       wk_editor = E_WEBKIT_EDITOR (editor);
+
+       if (!wk_editor->priv->html_mode)
+               goto exit;
+
+       result = webkit_editor_get_element_attribute (
+               wk_editor, "#-x-evo-current-table", "bgcolor");
+       if (result) {
+               const gchar *value;
+
+               g_variant_get (result, "(&s)", &value);
+               if (!value || !*value || !gdk_rgba_parse (color, value)) {
+                       g_variant_unref (result);
+                       goto exit;
+               }
+               g_variant_unref (result);
+               return;
+       }
+
+ exit:
+       *color = transparent;
+}
+
+static void
+webkit_editor_table_set_background_color (EContentEditor *editor,
+                                          const GdkRGBA *value)
+{
+       EWebKitEditor *wk_editor;
+       gchar *color;
+
+       wk_editor = E_WEBKIT_EDITOR (editor);
+
+       if (!wk_editor->priv->web_extension) {
+               g_warning ("EHTMLEditorWebExtension not ready at %s!", G_STRFUNC);
+               return;
+       }
+
+       if (value->alpha != 0.0)
+               color = g_strdup_printf ("#%06x", e_rgba_to_value (value));
+       else
+               color = g_strdup ("");
+
+       webkit_editor_set_element_attribute (
+               wk_editor, "#-x-evo-current-table", "bgcolor", color);
+
+       g_free (color);
+}
+
+static gchar *
+webkit_editor_table_get_background_image_uri (EContentEditor *editor)
+{
+       EWebKitEditor *wk_editor;
+       GVariant *result;
+
+       wk_editor = E_WEBKIT_EDITOR (editor);
+
+       if (!wk_editor->priv->html_mode)
+               return NULL;
+
+       result = webkit_editor_get_element_attribute (wk_editor, "#-x-evo-current-table", "data-uri");
+       if (result) {
+               gchar *value;
+
+               g_variant_get (result, "(s)", &value);
+               g_variant_unref (result);
+       }
+
+       return NULL;
+}
+
+static void
+webkit_editor_table_set_background_image_uri (EContentEditor *editor,
+                                              const gchar *uri)
+{
+       EWebKitEditor *wk_editor;
+
+       wk_editor = E_WEBKIT_EDITOR (editor);
+
+       if (!wk_editor->priv->web_extension) {
+               g_warning ("EHTMLEditorWebExtension not ready at %s!", G_STRFUNC);
+               return;
+       }
+
+       if (!wk_editor->priv->html_mode)
+               return;
+
+       if (uri && *uri)
+               webkit_editor_replace_image_src (wk_editor, "#-x-evo-current-table", uri);
+       else {
+               g_dbus_proxy_call (
+                       wk_editor->priv->web_extension,
+                       "RemoveImageAttributesFromElementBySelector",
+                       g_variant_new ("(ts)", current_page_id (wk_editor), "#-x-evo-current-table"),
+                       G_DBUS_CALL_FLAGS_NONE,
+                       -1,
+                       NULL,
+                       NULL,
+                       NULL);
+       }
+}
+
+static gboolean
+webkit_editor_on_table_dialog_open (EContentEditor *editor)
+{
+       EWebKitEditor *wk_editor;
+       GVariant *result;
+       gboolean value = FALSE;
+
+       wk_editor = E_WEBKIT_EDITOR (editor);
+
+       if (!wk_editor->priv->web_extension) {
+               g_warning ("EHTMLEditorWebExtension not ready at %s!", G_STRFUNC);
+               return FALSE;
+       }
+
+       result = g_dbus_proxy_call_sync_wrapper (
+               wk_editor->priv->web_extension,
+               "EEditorTableDialogShow",
+               g_variant_new ("(t)", current_page_id (wk_editor)),
+               G_DBUS_CALL_FLAGS_NONE,
+               -1,
+               NULL,
+               NULL);
+
+       if (result) {
+               g_variant_get (result, "(b)", &value);
+               g_variant_unref (result);
+       }
+
+       return value;
+}
+
+static void
+webkit_editor_on_table_dialog_close (EContentEditor *editor)
+{
+       EWebKitEditor *wk_editor;
+
+       wk_editor = E_WEBKIT_EDITOR (editor);
+
+       webkit_editor_call_simple_extension_function (
+               wk_editor, "EEditorTableDialogSaveHistoryOnExit");
+
+       webkit_editor_finish_search (E_WEBKIT_EDITOR (editor));
+}
+
+static void
+webkit_editor_on_spell_check_dialog_open (EContentEditor *editor)
+{
+}
+
+static void
+webkit_editor_on_spell_check_dialog_close (EContentEditor *editor)
+{
+       webkit_editor_finish_search (E_WEBKIT_EDITOR (editor));
+}
+
+static gchar *
+move_to_another_word (EContentEditor *editor,
+                      const gchar *word,
+                      const gchar *dom_function)
+{
+       EWebKitEditor *wk_editor;
+       gchar **active_languages;
+       gchar *another_word = NULL;
+       GVariant *result;
+
+       wk_editor = E_WEBKIT_EDITOR (editor);
+
+       if (!wk_editor->priv->web_extension) {
+               g_warning ("EHTMLEditorWebExtension not ready at %s!", G_STRFUNC);
+               return NULL;
+       }
+
+       active_languages = e_spell_checker_list_active_languages (
+               wk_editor->priv->spell_checker, NULL);
+       if (!active_languages)
+               return NULL;
+
+       result = g_dbus_proxy_call_sync_wrapper (
+               wk_editor->priv->web_extension,
+               dom_function,
+               g_variant_new (
+                       "(ts^as)", current_page_id (wk_editor), word ? word : "", active_languages),
+               G_DBUS_CALL_FLAGS_NONE,
+               -1,
+               NULL,
+               NULL);
+
+       g_strfreev (active_languages);
+
+       if (result) {
+               g_variant_get (result, "(s)", &another_word);
+               g_variant_unref (result);
+       }
+
+       return another_word;
+}
+
+static gchar *
+webkit_editor_spell_check_next_word (EContentEditor *editor,
+                                     const gchar *word)
+{
+       return move_to_another_word (editor, word, "EEditorSpellCheckDialogNext");
+}
+
+static gchar *
+webkit_editor_spell_check_prev_word (EContentEditor *editor,
+                                     const gchar *word)
+{
+       return move_to_another_word (editor, word, "EEditorSpellCheckDialogPrev");
+}
+
+static void
+webkit_editor_on_replace_dialog_open (EContentEditor *editor)
+{
+}
+
+static void
+webkit_editor_on_replace_dialog_close (EContentEditor *editor)
+{
+       webkit_editor_finish_search (E_WEBKIT_EDITOR (editor));
+}
+
+static void
+webkit_editor_on_find_dialog_open (EContentEditor *editor)
+{
+}
+
+static void
+webkit_editor_on_find_dialog_close (EContentEditor *editor)
+{
+       webkit_editor_finish_search (E_WEBKIT_EDITOR (editor));
+}
+
+static GDBusProxy *
+webkit_editor_get_web_extension (EWebKitEditor *wk_editor)
+{
+       g_return_val_if_fail (E_IS_WEBKIT_EDITOR (wk_editor), NULL);
+
+       return wk_editor->priv->web_extension;
+}
+
+static void
+webkit_editor_constructed (GObject *object)
+{
+       EWebKitEditor *wk_editor;
+       gchar **languages;
+       WebKitWebContext *web_context;
+       WebKitSettings *web_settings;
+       WebKitWebView *web_view;
+
+       G_OBJECT_CLASS (e_webkit_editor_parent_class)->constructed (object);
+
+       wk_editor = E_WEBKIT_EDITOR (object);
+       web_view = WEBKIT_WEB_VIEW (wk_editor);
+
+       /* Give spell check languages to WebKit */
+       languages = e_spell_checker_list_active_languages (wk_editor->priv->spell_checker, NULL);
+
+       web_context = webkit_web_view_get_context (web_view);
+       webkit_web_context_set_spell_checking_enabled (web_context, TRUE);
+       webkit_web_context_set_spell_checking_languages (web_context, (const gchar * const *) languages);
+       g_strfreev (languages);
+
+       webkit_web_view_set_editable (web_view, TRUE);
+
+       web_settings = webkit_web_view_get_settings (web_view);
+       webkit_settings_set_allow_file_access_from_file_urls (web_settings, TRUE);
+       webkit_settings_set_enable_developer_extras (web_settings, TRUE);
+
+       /* Make WebKit think we are displaying a local file, so that it
+        * does not block loading resources from file:// protocol */
+       webkit_web_view_load_html (WEBKIT_WEB_VIEW (object), "", "file://");
+}
+
+static GObjectConstructParam*
+find_property (guint n_properties,
+               GObjectConstructParam* properties,
+               GParamSpec* param_spec)
+{
+       while (n_properties--) {
+               if (properties->pspec == param_spec)
+                       return properties;
+               properties++;
+       }
+
+       return NULL;
+}
+
+static GObject *
+webkit_editor_constructor (GType type,
+                           guint n_construct_properties,
+                           GObjectConstructParam *construct_properties)
+{
+       GObjectClass* object_class;
+       GParamSpec* param_spec;
+       GObjectConstructParam *param = NULL;
+
+       object_class = G_OBJECT_CLASS (g_type_class_ref (type));
+       g_return_val_if_fail (object_class != NULL, NULL);
+
+       if (construct_properties && n_construct_properties != 0) {
+               param_spec = g_object_class_find_property (object_class, "settings");
+               if ((param = find_property (n_construct_properties, construct_properties, param_spec)))
+                       g_value_take_object (param->value, e_web_view_get_default_webkit_settings ());
+               param_spec = g_object_class_find_property (object_class, "user-content-manager");
+               if ((param = find_property (n_construct_properties, construct_properties, param_spec)))
+                       g_value_take_object (param->value, webkit_user_content_manager_new ());
+               param_spec = g_object_class_find_property (object_class, "web-context");
+               if ((param = find_property (n_construct_properties, construct_properties, param_spec))) {
+                       /* Share one web_context between all editors, thus there is one WebProcess
+                          for all the editors (and one for the preview). */
+                       static gpointer web_context = NULL;
+
+                       if (!web_context) {
+                               web_context = webkit_web_context_new ();
+
+                               webkit_web_context_set_cache_model (web_context, 
WEBKIT_CACHE_MODEL_DOCUMENT_VIEWER);
+                               webkit_web_context_set_web_extensions_directory (web_context, 
EVOLUTION_WEB_EXTENSIONS_WEBKIT_EDITOR_DIR);
+
+                               g_object_add_weak_pointer (G_OBJECT (web_context), &web_context);
+                       } else {
+                               g_object_ref (web_context);
+                       }
+
+                       g_value_take_object (param->value, web_context);
+               }
+       }
+
+       g_type_class_unref (object_class);
+
+       return G_OBJECT_CLASS (e_webkit_editor_parent_class)->constructor (type, n_construct_properties, 
construct_properties);
+}
+
+static void
+webkit_editor_dispose (GObject *object)
+{
+       EWebKitEditorPrivate *priv;
+
+       priv = E_WEBKIT_EDITOR_GET_PRIVATE (object);
+
+       if (priv->aliasing_settings != NULL) {
+               g_signal_handlers_disconnect_by_data (priv->aliasing_settings, object);
+               g_object_unref (priv->aliasing_settings);
+               priv->aliasing_settings = NULL;
+       }
+
+       if (priv->current_user_stylesheet != NULL) {
+               g_free (priv->current_user_stylesheet);
+               priv->current_user_stylesheet = NULL;
+       }
+
+       if (priv->font_settings != NULL) {
+               g_signal_handlers_disconnect_by_data (priv->font_settings, object);
+               g_object_unref (priv->font_settings);
+               priv->font_settings = NULL;
+       }
+
+       if (priv->mail_settings != NULL) {
+               g_signal_handlers_disconnect_by_data (priv->mail_settings, object);
+               g_object_unref (priv->mail_settings);
+               priv->mail_settings = NULL;
+       }
+
+       if (priv->web_extension_content_changed_cb_id > 0) {
+               g_dbus_connection_signal_unsubscribe (
+                       g_dbus_proxy_get_connection (priv->web_extension),
+                       priv->web_extension_content_changed_cb_id);
+               priv->web_extension_content_changed_cb_id = 0;
+       }
+
+       if (priv->web_extension_selection_changed_cb_id > 0) {
+               g_dbus_connection_signal_unsubscribe (
+                       g_dbus_proxy_get_connection (priv->web_extension),
+                       priv->web_extension_selection_changed_cb_id);
+               priv->web_extension_selection_changed_cb_id = 0;
+       }
+
+       if (priv->web_extension_undo_redo_state_changed_cb_id > 0) {
+               g_dbus_connection_signal_unsubscribe (
+                       g_dbus_proxy_get_connection (priv->web_extension),
+                       priv->web_extension_undo_redo_state_changed_cb_id);
+               priv->web_extension_undo_redo_state_changed_cb_id = 0;
+       }
+
+       if (priv->web_extension_watch_name_id > 0) {
+               g_bus_unwatch_name (priv->web_extension_watch_name_id);
+               priv->web_extension_watch_name_id = 0;
+       }
+
+       if (priv->owner_change_clipboard_cb_id > 0) {
+               g_signal_handler_disconnect (
+                       gtk_clipboard_get (GDK_SELECTION_CLIPBOARD),
+                       priv->owner_change_clipboard_cb_id);
+               priv->owner_change_clipboard_cb_id = 0;
+       }
+
+       if (priv->owner_change_primary_clipboard_cb_id > 0) {
+               g_signal_handler_disconnect (
+                       gtk_clipboard_get (GDK_SELECTION_PRIMARY),
+                       priv->owner_change_primary_clipboard_cb_id);
+               priv->owner_change_primary_clipboard_cb_id = 0;
+       }
+
+       webkit_editor_finish_search (E_WEBKIT_EDITOR (object));
+
+       g_clear_object (&priv->web_extension);
+
+       /* Chain up to parent's dispose() method. */
+       G_OBJECT_CLASS (e_webkit_editor_parent_class)->dispose (object);
+}
+
+static void
+webkit_editor_finalize (GObject *object)
+{
+       EWebKitEditorPrivate *priv;
+
+       priv = E_WEBKIT_EDITOR_GET_PRIVATE (object);
+
+       if (priv->old_settings) {
+               g_hash_table_destroy (priv->old_settings);
+               priv->old_settings = NULL;
+       }
+
+       if (priv->post_reload_operations) {
+               g_warn_if_fail (g_queue_is_empty (priv->post_reload_operations));
+
+               g_queue_free (priv->post_reload_operations);
+               priv->post_reload_operations = NULL;
+       }
+
+       if (priv->background_color != NULL) {
+               gdk_rgba_free (priv->background_color);
+               priv->background_color = NULL;
+       }
+
+       if (priv->font_color != NULL) {
+               gdk_rgba_free (priv->font_color);
+               priv->font_color = NULL;
+       }
+
+       g_clear_object (&priv->spell_checker);
+
+       g_free (priv->font_name);
+
+       /* Chain up to parent's finalize() method. */
+       G_OBJECT_CLASS (e_webkit_editor_parent_class)->finalize (object);
+}
+
+static void
+webkit_editor_set_property (GObject *object,
+                            guint property_id,
+                            const GValue *value,
+                            GParamSpec *pspec)
+{
+       switch (property_id) {
+               case PROP_CHANGED:
+                       webkit_editor_set_changed (
+                               E_WEBKIT_EDITOR (object),
+                               g_value_get_boolean (value));
+                       return;
+
+               case PROP_EDITABLE:
+                       webkit_editor_set_editable (
+                               E_WEBKIT_EDITOR (object),
+                               g_value_get_boolean (value));
+                       return;
+
+               case PROP_HTML_MODE:
+                       webkit_editor_set_html_mode (
+                               E_WEBKIT_EDITOR (object),
+                               g_value_get_boolean (value));
+                       return;
+
+               case PROP_ALIGNMENT:
+                       webkit_editor_set_alignment (
+                               E_WEBKIT_EDITOR (object),
+                               g_value_get_enum (value));
+                       return;
+
+               case PROP_BACKGROUND_COLOR:
+                       webkit_editor_set_background_color (
+                               E_WEBKIT_EDITOR (object),
+                               g_value_get_boxed (value));
+                       return;
+
+               case PROP_BOLD:
+                       webkit_editor_set_style_flag (
+                               E_WEBKIT_EDITOR (object),
+                               E_CONTENT_EDITOR_STYLE_IS_BOLD,
+                               g_value_get_boolean (value),
+                               "DOMSelectionSetBold");
+                       return;
+
+               case PROP_FONT_COLOR:
+                       webkit_editor_set_font_color (
+                               E_WEBKIT_EDITOR (object),
+                               g_value_get_boxed (value));
+                       return;
+
+               case PROP_BLOCK_FORMAT:
+                       webkit_editor_set_block_format (
+                               E_WEBKIT_EDITOR (object),
+                               g_value_get_enum (value));
+                       return;
+
+               case PROP_FONT_NAME:
+                       webkit_editor_set_font_name (
+                               E_WEBKIT_EDITOR (object),
+                               g_value_get_string (value));
+                       return;
+
+               case PROP_FONT_SIZE:
+                       webkit_editor_set_font_size (
+                               E_WEBKIT_EDITOR (object),
+                               g_value_get_int (value));
+                       return;
+
+               case PROP_ITALIC:
+                       webkit_editor_set_style_flag (
+                               E_WEBKIT_EDITOR (object),
+                               E_CONTENT_EDITOR_STYLE_IS_ITALIC,
+                               g_value_get_boolean (value),
+                               "DOMSelectionSetItalic");
+                       return;
+
+               case PROP_MONOSPACED:
+                       webkit_editor_set_style_flag (
+                               E_WEBKIT_EDITOR (object),
+                               E_CONTENT_EDITOR_STYLE_IS_MONOSPACE,
+                               g_value_get_boolean (value),
+                               "DOMSelectionSetMonospaced");
+                       return;
+
+               case PROP_STRIKETHROUGH:
+                       webkit_editor_set_style_flag (
+                               E_WEBKIT_EDITOR (object),
+                               E_CONTENT_EDITOR_STYLE_IS_STRIKETHROUGH,
+                               g_value_get_boolean (value),
+                               "DOMSelectionSetStrikethrough");
+                       return;
+
+               case PROP_SUBSCRIPT:
+                       webkit_editor_set_style_flag (
+                               E_WEBKIT_EDITOR (object),
+                               E_CONTENT_EDITOR_STYLE_IS_SUBSCRIPT,
+                               g_value_get_boolean (value),
+                               "DOMSelectionSetSubscript");
+                       return;
+
+               case PROP_SUPERSCRIPT:
+                       webkit_editor_set_style_flag (
+                               E_WEBKIT_EDITOR (object),
+                               E_CONTENT_EDITOR_STYLE_IS_SUPERSCRIPT,
+                               g_value_get_boolean (value),
+                               "DOMSelectionSetSuperscript");
+                       return;
+
+               case PROP_UNDERLINE:
+                       webkit_editor_set_style_flag (
+                               E_WEBKIT_EDITOR (object),
+                               E_CONTENT_EDITOR_STYLE_IS_UNDERLINE,
+                               g_value_get_boolean (value),
+                               "DOMSelectionSetUnderline");
+                       return;
+
+               case PROP_SPELL_CHECK_ENABLED:
+                       webkit_editor_set_spell_check_enabled (
+                               E_WEBKIT_EDITOR (object),
+                               g_value_get_boolean (value));
+                       return;
+       }
+
+       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+}
+
+static void
+webkit_editor_get_property (GObject *object,
+                            guint property_id,
+                            GValue *value,
+                            GParamSpec *pspec)
+{
+       switch (property_id) {
+               case PROP_WEB_EXTENSION:
+                       g_value_set_object (
+                               value, webkit_editor_get_web_extension (
+                               E_WEBKIT_EDITOR (object)));
+                       return;
+
+               case PROP_CAN_COPY:
+                       g_value_set_boolean (
+                               value, webkit_editor_can_copy (
+                               E_WEBKIT_EDITOR (object)));
+                       return;
+
+               case PROP_CAN_CUT:
+                       g_value_set_boolean (
+                               value, webkit_editor_can_cut (
+                               E_WEBKIT_EDITOR (object)));
+                       return;
+
+               case PROP_CAN_PASTE:
+                       g_value_set_boolean (
+                               value, webkit_editor_can_paste (
+                               E_WEBKIT_EDITOR (object)));
+                       return;
+
+               case PROP_CAN_REDO:
+                       g_value_set_boolean (
+                               value, webkit_editor_can_redo (
+                               E_WEBKIT_EDITOR (object)));
+                       return;
+
+               case PROP_CAN_UNDO:
+                       g_value_set_boolean (
+                               value, webkit_editor_can_undo (
+                               E_WEBKIT_EDITOR (object)));
+                       return;
+
+               case PROP_CHANGED:
+                       g_value_set_boolean (
+                               value, webkit_editor_get_changed (
+                               E_WEBKIT_EDITOR (object)));
+                       return;
+
+               case PROP_HTML_MODE:
+                       g_value_set_boolean (
+                               value, webkit_editor_get_html_mode (
+                               E_WEBKIT_EDITOR (object)));
+                       return;
+
+               case PROP_EDITABLE:
+                       g_value_set_boolean (
+                               value, webkit_editor_is_editable (
+                               E_WEBKIT_EDITOR (object)));
+                       return;
+
+               case PROP_ALIGNMENT:
+                       g_value_set_enum (
+                               value,
+                               webkit_editor_get_alignment (
+                                       E_WEBKIT_EDITOR (object)));
+                       return;
+
+               case PROP_BACKGROUND_COLOR:
+                       g_value_set_boxed (
+                               value,
+                               webkit_editor_get_background_color (
+                                       E_WEBKIT_EDITOR (object)));
+                       return;
+
+               case PROP_BLOCK_FORMAT:
+                       g_value_set_enum (
+                               value,
+                               webkit_editor_get_block_format (
+                                       E_WEBKIT_EDITOR (object)));
+                       return;
+
+               case PROP_BOLD:
+                       g_value_set_boolean (
+                               value,
+                               webkit_editor_get_style_flag (
+                                       E_WEBKIT_EDITOR (object),
+                                       E_CONTENT_EDITOR_STYLE_IS_BOLD));
+                       return;
+
+               case PROP_FONT_COLOR:
+                       g_value_set_boxed (
+                               value,
+                               webkit_editor_get_font_color (
+                                       E_WEBKIT_EDITOR (object)));
+                       return;
+
+               case PROP_FONT_NAME:
+                       g_value_set_string (
+                               value,
+                               webkit_editor_get_font_name (
+                                       E_WEBKIT_EDITOR (object)));
+                       return;
+
+               case PROP_FONT_SIZE:
+                       g_value_set_int (
+                               value,
+                               webkit_editor_get_font_size (
+                                       E_WEBKIT_EDITOR (object)));
+                       return;
+
+               case PROP_INDENTED:
+                       g_value_set_boolean (
+                               value,
+                               webkit_editor_selection_is_indented (
+                                       E_WEBKIT_EDITOR (object)));
+                       return;
+
+               case PROP_ITALIC:
+                       g_value_set_boolean (
+                               value,
+                               webkit_editor_get_style_flag (
+                                       E_WEBKIT_EDITOR (object),
+                                       E_CONTENT_EDITOR_STYLE_IS_ITALIC));
+                       return;
+
+               case PROP_MONOSPACED:
+                       g_value_set_boolean (
+                               value,
+                               webkit_editor_get_style_flag (
+                                       E_WEBKIT_EDITOR (object),
+                                       E_CONTENT_EDITOR_STYLE_IS_MONOSPACE));
+                       return;
+
+               case PROP_STRIKETHROUGH:
+                       g_value_set_boolean (
+                               value,
+                               webkit_editor_get_style_flag (
+                                       E_WEBKIT_EDITOR (object),
+                                       E_CONTENT_EDITOR_STYLE_IS_STRIKETHROUGH));
+                       return;
+
+               case PROP_SUBSCRIPT:
+                       g_value_set_boolean (
+                               value,
+                               webkit_editor_get_style_flag (
+                                       E_WEBKIT_EDITOR (object),
+                                       E_CONTENT_EDITOR_STYLE_IS_SUBSCRIPT));
+                       return;
+
+               case PROP_SUPERSCRIPT:
+                       g_value_set_boolean (
+                               value,
+                               webkit_editor_get_style_flag (
+                                       E_WEBKIT_EDITOR (object),
+                                       E_CONTENT_EDITOR_STYLE_IS_SUPERSCRIPT));
+                       return;
+
+               case PROP_UNDERLINE:
+                       g_value_set_boolean (
+                               value,
+                               webkit_editor_get_style_flag (
+                                       E_WEBKIT_EDITOR (object),
+                                       E_CONTENT_EDITOR_STYLE_IS_UNDERLINE));
+                       return;
+
+               case PROP_SPELL_CHECK_ENABLED:
+                       g_value_set_boolean (
+                               value,
+                               webkit_editor_get_spell_check_enabled (
+                                       E_WEBKIT_EDITOR (object)));
+                       return;
+
+               case PROP_SPELL_CHECKER:
+                       g_value_set_object (
+                               value,
+                               webkit_editor_get_spell_checker (
+                                       E_WEBKIT_EDITOR (object)));
+                       return;
+       }
+
+       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+}
+
+static void
+webkit_editor_move_caret_on_current_coordinates (GtkWidget *widget)
+{
+       gint x, y;
+       GdkDeviceManager *device_manager;
+       GdkDevice *pointer;
+
+       device_manager = gdk_display_get_device_manager (gtk_widget_get_display (widget));
+       pointer = gdk_device_manager_get_client_pointer (device_manager);
+       gdk_window_get_device_position (
+               gtk_widget_get_window (widget), pointer, &x, &y, NULL);
+       webkit_editor_move_caret_on_coordinates
+               (E_CONTENT_EDITOR (widget), x, y, TRUE);
+}
+
+static void
+webkit_editor_settings_changed_cb (GSettings *settings,
+                                   const gchar *key,
+                                   EWebKitEditor *wk_editor)
+{
+       GVariant *new_value, *old_value;
+
+       new_value = g_settings_get_value (settings, key);
+       old_value = g_hash_table_lookup (wk_editor->priv->old_settings, key);
+
+       if (!new_value || !old_value || !g_variant_equal (new_value, old_value)) {
+               if (new_value)
+                       g_hash_table_insert (wk_editor->priv->old_settings, g_strdup (key), new_value);
+               else
+                       g_hash_table_remove (wk_editor->priv->old_settings, key);
+
+               webkit_editor_update_styles (E_CONTENT_EDITOR (wk_editor));
+       } else if (new_value) {
+               g_variant_unref (new_value);
+       }
+}
+
+static void
+webkit_editor_load_changed_cb (EWebKitEditor *wk_editor,
+                                       WebKitLoadEvent load_event)
+{
+       wk_editor->priv->webkit_load_event = load_event;
+
+       if (load_event != WEBKIT_LOAD_FINISHED)
+               return;
+
+       if (wk_editor->priv->web_extension)
+               e_content_editor_emit_load_finished (E_CONTENT_EDITOR (wk_editor));
+       else
+               wk_editor->priv->emit_load_finished_when_extension_is_ready = TRUE;
+
+       wk_editor->priv->reload_in_progress = FALSE;
+
+       dispatch_pending_operations (wk_editor);
+}
+
+static void
+webkit_editor_clipboard_owner_change_cb (GtkClipboard *clipboard,
+                                         GdkEventOwnerChange *event,
+                                         EWebKitEditor *wk_editor)
+{
+       if (!E_IS_WEBKIT_EDITOR (wk_editor))
+               return;
+
+       if (!wk_editor->priv->web_extension)
+               return;
+
+       if (wk_editor->priv->copy_cut_actions_triggered && event->owner)
+               wk_editor->priv->copy_paste_clipboard_in_view = TRUE;
+       else
+               wk_editor->priv->copy_paste_clipboard_in_view = FALSE;
+
+       if (wk_editor->priv->copy_paste_clipboard_in_view == 
wk_editor->priv->pasting_from_itself_extension_value)
+               return;
+
+       g_dbus_proxy_call (
+               wk_editor->priv->web_extension,
+               "SetPastingContentFromItself",
+               g_variant_new (
+                       "(tb)",
+                       current_page_id (wk_editor),
+                       wk_editor->priv->copy_paste_clipboard_in_view),
+               G_DBUS_CALL_FLAGS_NONE,
+               -1,
+               NULL,
+               NULL,
+               NULL);
+
+       wk_editor->priv->copy_cut_actions_triggered = FALSE;
+
+       wk_editor->priv->pasting_from_itself_extension_value = wk_editor->priv->copy_paste_clipboard_in_view;
+}
+
+static void
+webkit_editor_primary_clipboard_owner_change_cb (GtkClipboard *clipboard,
+                                                 GdkEventOwnerChange *event,
+                                                 EWebKitEditor *wk_editor)
+{
+       if (!E_IS_WEBKIT_EDITOR (wk_editor) ||
+           !wk_editor->priv->web_extension)
+               return;
+
+       if (!event->owner || !wk_editor->priv->can_copy)
+               wk_editor->priv->copy_paste_clipboard_in_view = FALSE;
+
+       if (wk_editor->priv->copy_paste_clipboard_in_view == 
wk_editor->priv->pasting_from_itself_extension_value)
+               return;
+
+       g_dbus_proxy_call (
+               wk_editor->priv->web_extension,
+               "SetPastingContentFromItself",
+               g_variant_new (
+                       "(tb)",
+                       current_page_id (wk_editor),
+                       wk_editor->priv->copy_paste_clipboard_in_view),
+               G_DBUS_CALL_FLAGS_NONE,
+               -1,
+               NULL,
+               NULL,
+               NULL);
+
+       wk_editor->priv->pasting_from_itself_extension_value = wk_editor->priv->copy_paste_clipboard_in_view;
+}
+
+static gboolean
+webkit_editor_paste_prefer_text_html (EWebKitEditor *wk_editor)
+{
+       if (wk_editor->priv->pasting_primary_clipboard)
+               return wk_editor->priv->copy_paste_primary_in_view;
+       else
+               return wk_editor->priv->copy_paste_clipboard_in_view;
+}
+
+static void
+webkit_editor_paste_clipboard_targets_cb (GtkClipboard *clipboard,
+                                          GdkAtom *targets,
+                                          gint n_targets,
+                                          EWebKitEditor *wk_editor)
+{
+       if (targets == NULL || n_targets < 0)
+               return;
+
+       /* If view doesn't have focus, focus it */
+       if (!gtk_widget_has_focus (GTK_WIDGET (wk_editor)))
+               gtk_widget_grab_focus (GTK_WIDGET (wk_editor));
+
+       /* Order is important here to ensure common use cases are
+        * handled correctly.  See GNOME bug #603715 for details. */
+       /* Prefer plain text over HTML when in the plain text mode, but only
+        * when pasting content from outside the editor view. */
+       if (wk_editor->priv->html_mode ||
+           webkit_editor_paste_prefer_text_html (wk_editor)) {
+               gchar *content = NULL;
+
+               if (e_targets_include_html (targets, n_targets)) {
+                       if (!(content = e_clipboard_wait_for_html (clipboard)))
+                               return;
+
+                       webkit_editor_insert_content (
+                               E_CONTENT_EDITOR (wk_editor),
+                               content,
+                               E_CONTENT_EDITOR_INSERT_TEXT_HTML);
+
+                       g_free (content);
+                       return;
+               }
+
+               if (gtk_targets_include_text (targets, n_targets)) {
+                       if (!(content = gtk_clipboard_wait_for_text (clipboard)))
+                               return;
+
+                       webkit_editor_insert_content (
+                               E_CONTENT_EDITOR (wk_editor),
+                               content,
+                               E_CONTENT_EDITOR_INSERT_TEXT_PLAIN |
+                               E_CONTENT_EDITOR_INSERT_CONVERT);
+
+                       g_free (content);
+                       return;
+               }
+       } else {
+               gchar *content = NULL;
+
+               if (gtk_targets_include_text (targets, n_targets)) {
+                       if (!(content = gtk_clipboard_wait_for_text (clipboard)))
+                               return;
+
+                       webkit_editor_insert_content (
+                               E_CONTENT_EDITOR (wk_editor),
+                               content,
+                               E_CONTENT_EDITOR_INSERT_TEXT_PLAIN |
+                               E_CONTENT_EDITOR_INSERT_CONVERT);
+
+                       g_free (content);
+                       return;
+               }
+
+               if (e_targets_include_html (targets, n_targets)) {
+                       if (!(content = e_clipboard_wait_for_html (clipboard)))
+                               return;
+
+                       webkit_editor_insert_content (
+                               E_CONTENT_EDITOR (wk_editor),
+                               content,
+                               E_CONTENT_EDITOR_INSERT_TEXT_HTML);
+
+                       g_free (content);
+                       return;
+               }
+       }
+
+       if (gtk_targets_include_image (targets, n_targets, TRUE)) {
+               gchar *uri;
+
+               if (!(uri = e_util_save_image_from_clipboard (clipboard)))
+                       return;
+
+               webkit_editor_insert_image (E_CONTENT_EDITOR (wk_editor), uri);
+
+               g_free (uri);
+
+               return;
+       }
+}
+
+static void
+webkit_editor_paste_primary (EContentEditor *editor)
+{
+
+       GtkClipboard *clipboard;
+       EWebKitEditor *wk_editor;
+
+       wk_editor = E_WEBKIT_EDITOR (editor);
+
+       /* Remember, that we are pasting primary clipboard to return
+        * correct value in e_html_editor_view_is_pasting_content_from_itself. */
+       wk_editor->priv->pasting_primary_clipboard = TRUE;
+
+       webkit_editor_move_caret_on_current_coordinates (GTK_WIDGET (wk_editor));
+
+       clipboard = gtk_clipboard_get (GDK_SELECTION_PRIMARY);
+
+       gtk_clipboard_request_targets (
+               clipboard, (GtkClipboardTargetsReceivedFunc)
+               webkit_editor_paste_clipboard_targets_cb, wk_editor);
+}
+
+static void
+webkit_editor_paste (EContentEditor *editor)
+{
+       GtkClipboard *clipboard;
+       EWebKitEditor *wk_editor;
+
+       wk_editor = E_WEBKIT_EDITOR (editor);
+
+       clipboard = gtk_clipboard_get (GDK_SELECTION_CLIPBOARD);
+
+       gtk_clipboard_request_targets (
+               clipboard, (GtkClipboardTargetsReceivedFunc)
+               webkit_editor_paste_clipboard_targets_cb, wk_editor);
+}
+
+static void
+webkit_editor_mouse_target_changed_cb (EWebKitEditor *wk_editor,
+                                       WebKitHitTestResult *hit_test_result,
+                                       guint modifiers,
+                                       gpointer user_data)
+{
+       /* Ctrl + Left Click on link opens it. */
+       if (webkit_hit_test_result_context_is_link (hit_test_result) &&
+           (modifiers & GDK_CONTROL_MASK)) {
+               GdkScreen *screen;
+               const gchar *uri;
+               GtkWidget *toplevel;
+
+               toplevel = gtk_widget_get_toplevel (GTK_WIDGET (wk_editor));
+               screen = gtk_window_get_screen (GTK_WINDOW (toplevel));
+
+               uri = webkit_hit_test_result_get_link_uri (hit_test_result);
+
+               gtk_show_uri (screen, uri, GDK_CURRENT_TIME, NULL);
+       }
+}
+
+static gboolean
+webkit_editor_context_menu_cb (EWebKitEditor *wk_editor,
+                               WebKitContextMenu *context_menu,
+                               GdkEvent *event,
+                               WebKitHitTestResult *hit_test_result)
+{
+       GVariant *result;
+       EContentEditorNodeFlags flags = 0;
+       gboolean handled;
+
+       webkit_context_menu_remove_all (context_menu);
+
+       if ((result = webkit_context_menu_get_user_data (context_menu)))
+               flags = g_variant_get_int32 (result);
+
+       handled = e_content_editor_emit_context_menu_requested (E_CONTENT_EDITOR (wk_editor), flags, event);
+
+       return handled;
+}
+
+static void
+webkit_editor_drag_end_cb (EWebKitEditor *wk_editor,
+                           GdkDragContext *context)
+{
+       webkit_editor_call_simple_extension_function (wk_editor, "DOMDragAndDropEnd");
+}
+
+static void
+webkit_editor_web_process_crashed_cb (EWebKitEditor *wk_editor)
+{
+       g_warning (
+               "WebKitWebProcess (page id %ld) for EWebKitEditor crashed",
+               webkit_web_view_get_page_id (WEBKIT_WEB_VIEW (wk_editor)));
+
+       wk_editor->priv->web_extension_selection_changed_cb_id = 0;
+       wk_editor->priv->web_extension_content_changed_cb_id = 0;
+       wk_editor->priv->web_extension_undo_redo_state_changed_cb_id = 0;
+}
+
+static gboolean
+webkit_editor_button_press_event (GtkWidget *widget,
+                                  GdkEventButton *event)
+{
+       if (event->button == 2) {
+               if (!e_content_editor_emit_paste_primary_clipboard (E_CONTENT_EDITOR (widget)))
+                       webkit_editor_paste_primary (E_CONTENT_EDITOR( (widget)));
+
+               return TRUE;
+       }
+
+       /* Chain up to parent's button_press_event() method. */
+       return GTK_WIDGET_CLASS (e_webkit_editor_parent_class)->button_press_event (widget, event);
+}
+
+static gboolean
+webkit_editor_key_press_event (GtkWidget *widget,
+                               GdkEventKey *event)
+{
+       EWebKitEditor *wk_editor;
+
+       wk_editor = E_WEBKIT_EDITOR (widget);
+
+       if ((((event)->state & GDK_SHIFT_MASK) &&
+           ((event)->keyval == GDK_KEY_Insert)) ||
+           (((event)->state & GDK_CONTROL_MASK) &&
+           ((event)->keyval == GDK_KEY_v))) {
+               if (!e_content_editor_emit_paste_clipboard (E_CONTENT_EDITOR (widget)))
+                       webkit_editor_paste (E_CONTENT_EDITOR (widget));
+
+               return TRUE;
+       }
+
+       if (((event)->state & GDK_CONTROL_MASK) &&
+           ((event)->keyval == GDK_KEY_Insert)) {
+               webkit_editor_copy (E_CONTENT_EDITOR (wk_editor));
+               return TRUE;
+       }
+
+       if (((event)->state & GDK_CONTROL_MASK) &&
+           ((event)->keyval == GDK_KEY_z)) {
+               webkit_editor_undo (E_CONTENT_EDITOR (wk_editor));
+               return TRUE;
+       }
+
+       if (((event)->state & (GDK_CONTROL_MASK)) &&
+           ((event)->keyval == GDK_KEY_Z)) {
+               webkit_editor_redo (E_CONTENT_EDITOR (wk_editor));
+               return TRUE;
+       }
+
+       if (((event)->state & GDK_SHIFT_MASK) &&
+           ((event)->keyval == GDK_KEY_Delete)) {
+               webkit_editor_cut (E_CONTENT_EDITOR (wk_editor));
+               return TRUE;
+       }
+
+       if (((event)->state & GDK_CONTROL_MASK) &&
+           ((event)->state & GDK_SHIFT_MASK) &&
+           ((event)->keyval == GDK_KEY_I)) {
+               webkit_editor_show_inspector (wk_editor);
+               return TRUE;
+       }
+
+       /* Chain up to parent's key_press_event() method. */
+       return GTK_WIDGET_CLASS (e_webkit_editor_parent_class)->key_press_event (widget, event);
+}
+
+static void
+e_webkit_editor_class_init (EWebKitEditorClass *class)
+{
+       GObjectClass *object_class;
+       GtkWidgetClass *widget_class;
+
+       g_type_class_add_private (class, sizeof (EWebKitEditorPrivate));
+
+       object_class = G_OBJECT_CLASS (class);
+       object_class->constructed = webkit_editor_constructed;
+       object_class->constructor = webkit_editor_constructor;
+       object_class->get_property = webkit_editor_get_property;
+       object_class->set_property = webkit_editor_set_property;
+       object_class->dispose = webkit_editor_dispose;
+       object_class->finalize = webkit_editor_finalize;
+
+       widget_class = GTK_WIDGET_CLASS (class);
+       widget_class->button_press_event = webkit_editor_button_press_event;
+       widget_class->key_press_event = webkit_editor_key_press_event;
+
+       g_object_class_install_property (
+               object_class,
+               PROP_WEB_EXTENSION,
+               g_param_spec_object (
+                       "web-extension",
+                       "Web Extension",
+                       "The Web Extension to use to talk to the WebProcess",
+                       G_TYPE_DBUS_PROXY,
+                       G_PARAM_READABLE |
+                       G_PARAM_STATIC_STRINGS));
+
+       g_object_class_override_property (
+               object_class, PROP_CAN_COPY, "can-copy");
+       g_object_class_override_property (
+               object_class, PROP_CAN_CUT, "can-cut");
+       g_object_class_override_property (
+               object_class, PROP_CAN_PASTE, "can-paste");
+       g_object_class_override_property (
+               object_class, PROP_CAN_REDO, "can-redo");
+       g_object_class_override_property (
+               object_class, PROP_CAN_UNDO, "can-undo");
+       g_object_class_override_property (
+               object_class, PROP_CHANGED, "changed");
+       g_object_class_override_property (
+               object_class, PROP_HTML_MODE, "html-mode");
+       g_object_class_override_property (
+               object_class, PROP_EDITABLE, "editable");
+       g_object_class_override_property (
+               object_class, PROP_ALIGNMENT, "alignment");
+       g_object_class_override_property (
+               object_class, PROP_BACKGROUND_COLOR, "background-color");
+       g_object_class_override_property (
+               object_class, PROP_BLOCK_FORMAT, "block-format");
+       g_object_class_override_property (
+               object_class, PROP_BOLD, "bold");
+       g_object_class_override_property (
+               object_class, PROP_FONT_COLOR, "font-color");
+       g_object_class_override_property (
+               object_class, PROP_FONT_NAME, "font-name");
+       g_object_class_override_property (
+               object_class, PROP_FONT_SIZE, "font-size");
+       g_object_class_override_property (
+               object_class, PROP_INDENTED, "indented");
+       g_object_class_override_property (
+               object_class, PROP_ITALIC, "italic");
+       g_object_class_override_property (
+               object_class, PROP_MONOSPACED, "monospaced");
+       g_object_class_override_property (
+               object_class, PROP_STRIKETHROUGH, "strikethrough");
+       g_object_class_override_property (
+               object_class, PROP_SUBSCRIPT, "subscript");
+       g_object_class_override_property (
+               object_class, PROP_SUPERSCRIPT, "superscript");
+       g_object_class_override_property (
+               object_class, PROP_UNDERLINE, "underline");
+       g_object_class_override_property (
+               object_class, PROP_SPELL_CHECK_ENABLED, "spell-check-enabled");
+       g_object_class_override_property (
+               object_class, PROP_SPELL_CHECKER, "spell-checker");
+}
+
+static void
+e_webkit_editor_init (EWebKitEditor *wk_editor)
+{
+       GSettings *g_settings;
+       GSettingsSchema *settings_schema;
+
+       wk_editor->priv = E_WEBKIT_EDITOR_GET_PRIVATE (wk_editor);
+
+       wk_editor->priv->spell_check_enabled = TRUE;
+       wk_editor->priv->spell_checker = e_spell_checker_new ();
+       wk_editor->priv->old_settings = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, 
(GDestroyNotify) g_variant_unref);
+
+       webkit_editor_watch_web_extension (wk_editor);
+
+       g_signal_connect (
+               wk_editor, "load-changed",
+               G_CALLBACK (webkit_editor_load_changed_cb), NULL);
+
+       g_signal_connect (
+               wk_editor, "context-menu",
+               G_CALLBACK (webkit_editor_context_menu_cb), NULL);
+
+       g_signal_connect (
+               wk_editor, "mouse-target-changed",
+               G_CALLBACK (webkit_editor_mouse_target_changed_cb), NULL);
+
+       g_signal_connect (
+               wk_editor, "drag-end",
+               G_CALLBACK (webkit_editor_drag_end_cb), NULL);
+
+       g_signal_connect (
+               wk_editor, "web-process-crashed",
+               G_CALLBACK (webkit_editor_web_process_crashed_cb), NULL);
+
+       wk_editor->priv->owner_change_primary_clipboard_cb_id = g_signal_connect (
+               gtk_clipboard_get (GDK_SELECTION_PRIMARY), "owner-change",
+               G_CALLBACK (webkit_editor_primary_clipboard_owner_change_cb), wk_editor);
+
+       wk_editor->priv->owner_change_clipboard_cb_id = g_signal_connect (
+               gtk_clipboard_get (GDK_SELECTION_CLIPBOARD), "owner-change",
+               G_CALLBACK (webkit_editor_clipboard_owner_change_cb), wk_editor);
+
+       g_settings = e_util_ref_settings ("org.gnome.desktop.interface");
+       g_signal_connect (
+               g_settings, "changed::font-name",
+               G_CALLBACK (webkit_editor_settings_changed_cb), wk_editor);
+       g_signal_connect (
+               g_settings, "changed::monospace-font-name",
+               G_CALLBACK (webkit_editor_settings_changed_cb), wk_editor);
+       wk_editor->priv->font_settings = g_settings;
+
+       g_settings = e_util_ref_settings ("org.gnome.evolution.mail");
+       wk_editor->priv->mail_settings = g_settings;
+
+       /* This schema is optional.  Use if available. */
+       settings_schema = g_settings_schema_source_lookup (
+               g_settings_schema_source_get_default (),
+               "org.gnome.settings-daemon.plugins.xsettings", FALSE);
+       if (settings_schema != NULL) {
+               g_settings = e_util_ref_settings ("org.gnome.settings-daemon.plugins.xsettings");
+               g_signal_connect (
+                       g_settings, "changed::antialiasing",
+                       G_CALLBACK (webkit_editor_settings_changed_cb), wk_editor);
+               wk_editor->priv->aliasing_settings = g_settings;
+       }
+
+       wk_editor->priv->html_mode = TRUE;
+       wk_editor->priv->changed = FALSE;
+       wk_editor->priv->can_copy = FALSE;
+       wk_editor->priv->can_cut = FALSE;
+       wk_editor->priv->can_paste = FALSE;
+       wk_editor->priv->can_undo = FALSE;
+       wk_editor->priv->can_redo = FALSE;
+       wk_editor->priv->copy_paste_clipboard_in_view = FALSE;
+       wk_editor->priv->copy_paste_primary_in_view = FALSE;
+       wk_editor->priv->copy_cut_actions_triggered = FALSE;
+       wk_editor->priv->pasting_primary_clipboard = FALSE;
+       wk_editor->priv->pasting_from_itself_extension_value = FALSE;
+       wk_editor->priv->current_user_stylesheet = NULL;
+       wk_editor->priv->emit_load_finished_when_extension_is_ready = FALSE;
+
+       wk_editor->priv->font_color = gdk_rgba_copy (&black);
+       wk_editor->priv->background_color = gdk_rgba_copy (&white);
+       wk_editor->priv->font_name = NULL;
+       wk_editor->priv->font_size = E_CONTENT_EDITOR_FONT_SIZE_NORMAL;
+       wk_editor->priv->block_format = E_CONTENT_EDITOR_BLOCK_FORMAT_PARAGRAPH;
+       wk_editor->priv->alignment = E_CONTENT_EDITOR_ALIGNMENT_LEFT;
+
+       wk_editor->priv->web_extension_selection_changed_cb_id = 0;
+       wk_editor->priv->web_extension_content_changed_cb_id = 0;
+       wk_editor->priv->web_extension_undo_redo_state_changed_cb_id = 0;
+}
+
+static void
+e_webkit_editor_content_editor_init (EContentEditorInterface *iface)
+{
+       iface->initialize = webkit_editor_initialize;
+       iface->update_styles = webkit_editor_update_styles;
+       iface->insert_content = webkit_editor_insert_content;
+       iface->get_content = webkit_editor_get_content;
+       iface->insert_image = webkit_editor_insert_image;
+       iface->insert_image_from_mime_part = webkit_editor_insert_image_from_mime_part;
+       iface->insert_emoticon = webkit_editor_insert_emoticon;
+       iface->move_caret_on_coordinates = webkit_editor_move_caret_on_coordinates;
+       iface->cut = webkit_editor_cut;
+       iface->copy = webkit_editor_copy;
+       iface->paste = webkit_editor_paste;
+       iface->paste_primary = webkit_editor_paste_primary;
+       iface->undo = webkit_editor_undo;
+       iface->redo = webkit_editor_redo;
+       iface->clear_undo_redo_history = webkit_editor_clear_undo_redo_history;
+       iface->set_spell_checking_languages = webkit_editor_set_spell_checking_languages;
+       /* FIXME WK2 iface->get_selected_text = webkit_editor_get_selected_text; */
+       iface->get_caret_word = webkit_editor_get_caret_word;
+       iface->replace_caret_word = webkit_editor_replace_caret_word;
+       iface->select_all = webkit_editor_select_all;
+       iface->selection_indent = webkit_editor_selection_indent;
+       iface->selection_unindent = webkit_editor_selection_unindent;
+       /* FIXME WK2 iface->create_link = webkit_editor_create_link; */
+       iface->selection_unlink = webkit_editor_selection_unlink;
+       iface->find = webkit_editor_find;
+       iface->replace = webkit_editor_replace;
+       iface->replace_all = webkit_editor_replace_all;
+       iface->selection_save = webkit_editor_selection_save;
+       iface->selection_restore = webkit_editor_selection_restore;
+       iface->selection_wrap = webkit_editor_selection_wrap;
+       iface->get_caret_position = webkit_editor_get_caret_position;
+       iface->get_caret_offset = webkit_editor_get_caret_offset;
+       iface->get_current_signature_uid =  webkit_editor_get_current_signature_uid;
+       iface->is_ready = webkit_editor_is_ready;
+       iface->insert_signature = webkit_editor_insert_signature;
+       iface->delete_cell_contents = webkit_editor_delete_cell_contents;
+       iface->delete_column = webkit_editor_delete_column;
+       iface->delete_row = webkit_editor_delete_row;
+       iface->delete_table = webkit_editor_delete_table;
+       iface->insert_column_after = webkit_editor_insert_column_after;
+       iface->insert_column_before = webkit_editor_insert_column_before;
+       iface->insert_row_above = webkit_editor_insert_row_above;
+       iface->insert_row_below = webkit_editor_insert_row_below;
+       iface->on_h_rule_dialog_open = webkit_editor_on_h_rule_dialog_open;
+       iface->on_h_rule_dialog_close = webkit_editor_on_h_rule_dialog_close;
+       iface->h_rule_set_align = webkit_editor_h_rule_set_align;
+       iface->h_rule_get_align = webkit_editor_h_rule_get_align;
+       iface->h_rule_set_size = webkit_editor_h_rule_set_size;
+       iface->h_rule_get_size = webkit_editor_h_rule_get_size;
+       iface->h_rule_set_width = webkit_editor_h_rule_set_width;
+       iface->h_rule_get_width = webkit_editor_h_rule_get_width;
+       iface->h_rule_set_no_shade = webkit_editor_h_rule_set_no_shade;
+       iface->h_rule_get_no_shade = webkit_editor_h_rule_get_no_shade;
+       iface->on_image_dialog_open = webkit_editor_on_image_dialog_open;
+       iface->on_image_dialog_close = webkit_editor_on_image_dialog_close;
+       iface->image_set_src = webkit_editor_image_set_src;
+       iface->image_get_src = webkit_editor_image_get_src;
+       iface->image_set_alt = webkit_editor_image_set_alt;
+       iface->image_get_alt = webkit_editor_image_get_alt;
+       iface->image_set_url = webkit_editor_image_set_url;
+       iface->image_get_url = webkit_editor_image_get_url;
+       iface->image_set_vspace = webkit_editor_image_set_vspace;
+       iface->image_get_vspace = webkit_editor_image_get_vspace;
+       iface->image_set_hspace = webkit_editor_image_set_hspace;
+       iface->image_get_hspace = webkit_editor_image_get_hspace;
+       iface->image_set_border = webkit_editor_image_set_border;
+       iface->image_get_border = webkit_editor_image_get_border;
+       iface->image_set_align = webkit_editor_image_set_align;
+       iface->image_get_align = webkit_editor_image_get_align;
+       iface->image_get_natural_width = webkit_editor_image_get_natural_width;
+       iface->image_get_natural_height = webkit_editor_image_get_natural_height;
+       iface->image_set_height = webkit_editor_image_set_height;
+       iface->image_set_width = webkit_editor_image_set_width;
+       iface->image_set_height_follow = webkit_editor_image_set_height_follow;
+       iface->image_set_width_follow = webkit_editor_image_set_width_follow;
+       iface->image_get_width = webkit_editor_image_get_width;
+       iface->image_get_height = webkit_editor_image_get_height;
+       iface->on_link_dialog_open = webkit_editor_on_link_dialog_open;
+       iface->on_link_dialog_close = webkit_editor_on_link_dialog_close;
+       iface->link_set_values = webkit_editor_link_set_values;
+       iface->link_get_values = webkit_editor_link_get_values;
+       iface->page_set_text_color = webkit_editor_page_set_text_color;
+       iface->page_get_text_color = webkit_editor_page_get_text_color;
+       iface->page_set_background_color = webkit_editor_page_set_background_color;
+       iface->page_get_background_color = webkit_editor_page_get_background_color;
+       iface->page_set_link_color = webkit_editor_page_set_link_color;
+       iface->page_get_link_color = webkit_editor_page_get_link_color;
+       iface->page_set_visited_link_color = webkit_editor_page_set_visited_link_color;
+       iface->page_get_visited_link_color = webkit_editor_page_get_visited_link_color;
+       iface->page_set_background_image_uri = webkit_editor_page_set_background_image_uri;
+       iface->page_get_background_image_uri = webkit_editor_page_get_background_image_uri;
+       iface->on_page_dialog_open = webkit_editor_on_page_dialog_open;
+       iface->on_page_dialog_close = webkit_editor_on_page_dialog_close;
+       iface->on_cell_dialog_open = webkit_editor_on_cell_dialog_open;
+       iface->on_cell_dialog_close = webkit_editor_on_cell_dialog_close;
+       iface->cell_set_v_align = webkit_editor_cell_set_v_align;
+       iface->cell_get_v_align = webkit_editor_cell_get_v_align;
+       iface->cell_set_align = webkit_editor_cell_set_align;
+       iface->cell_get_align = webkit_editor_cell_get_align;
+       iface->cell_set_wrap = webkit_editor_cell_set_wrap;
+       iface->cell_get_wrap = webkit_editor_cell_get_wrap;
+       iface->cell_set_header_style = webkit_editor_cell_set_header_style;
+       iface->cell_is_header = webkit_editor_cell_is_header;
+       iface->cell_get_width = webkit_editor_cell_get_width;
+       iface->cell_set_width = webkit_editor_cell_set_width;
+       iface->cell_get_row_span = webkit_editor_cell_get_row_span;
+       iface->cell_set_row_span = webkit_editor_cell_set_row_span;
+       iface->cell_get_col_span = webkit_editor_cell_get_col_span;
+       iface->cell_set_col_span = webkit_editor_cell_set_col_span;
+       iface->cell_get_background_image_uri = webkit_editor_cell_get_background_image_uri;
+       iface->cell_set_background_image_uri = webkit_editor_cell_set_background_image_uri;
+       iface->cell_get_background_color = webkit_editor_cell_get_background_color;
+       iface->cell_set_background_color = webkit_editor_cell_set_background_color;
+       iface->table_set_row_count = webkit_editor_table_set_row_count;
+       iface->table_get_row_count = webkit_editor_table_get_row_count;
+       iface->table_set_column_count = webkit_editor_table_set_column_count;
+       iface->table_get_column_count = webkit_editor_table_get_column_count;
+       iface->table_set_width = webkit_editor_table_set_width;
+       iface->table_get_width = webkit_editor_table_get_width;
+       iface->table_set_align = webkit_editor_table_set_align;
+       iface->table_get_align = webkit_editor_table_get_align;
+       iface->table_set_padding = webkit_editor_table_set_padding;
+       iface->table_get_padding = webkit_editor_table_get_padding;
+       iface->table_set_spacing = webkit_editor_table_set_spacing;
+       iface->table_get_spacing = webkit_editor_table_get_spacing;
+       iface->table_set_border = webkit_editor_table_set_border;
+       iface->table_get_border = webkit_editor_table_get_border;
+       iface->table_get_background_image_uri = webkit_editor_table_get_background_image_uri;
+       iface->table_set_background_image_uri = webkit_editor_table_set_background_image_uri;
+       iface->table_get_background_color = webkit_editor_table_get_background_color;
+       iface->table_set_background_color = webkit_editor_table_set_background_color;
+       iface->on_table_dialog_open = webkit_editor_on_table_dialog_open;
+       iface->on_table_dialog_close = webkit_editor_on_table_dialog_close;
+       iface->on_spell_check_dialog_open = webkit_editor_on_spell_check_dialog_open;
+       iface->on_spell_check_dialog_close = webkit_editor_on_spell_check_dialog_close;
+       iface->spell_check_next_word = webkit_editor_spell_check_next_word;
+       iface->spell_check_prev_word = webkit_editor_spell_check_prev_word;
+       iface->on_replace_dialog_open = webkit_editor_on_replace_dialog_open;
+       iface->on_replace_dialog_close = webkit_editor_on_replace_dialog_close;
+       iface->on_find_dialog_open = webkit_editor_on_find_dialog_open;
+       iface->on_find_dialog_close = webkit_editor_on_find_dialog_close;
+}
diff --git a/modules/webkit-editor/e-webkit-editor.h b/modules/webkit-editor/e-webkit-editor.h
new file mode 100644
index 0000000..3b21679
--- /dev/null
+++ b/modules/webkit-editor/e-webkit-editor.h
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2016 Red Hat, Inc. (www.redhat.com)
+ *
+ * This library 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 library 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 Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef E_WEBKIT_EDITOR_H
+#define E_WEBKIT_EDITOR_H
+
+#include <webkit2/webkit2.h>
+
+/* Standard GObject macros */
+#define E_TYPE_WEBKIT_EDITOR \
+       (e_webkit_editor_get_type ())
+#define E_WEBKIT_EDITOR(obj) \
+       (G_TYPE_CHECK_INSTANCE_CAST \
+       ((obj), E_TYPE_WEBKIT_EDITOR, EWebKitEditor))
+#define E_WEBKIT_EDITOR_CLASS(cls) \
+       (G_TYPE_CHECK_CLASS_CAST \
+       ((cls), E_TYPE_WEBKIT_EDITOR, EWebKitEditorClass))
+#define E_IS_WEBKIT_EDITOR(obj) \
+       (G_TYPE_CHECK_INSTANCE_TYPE \
+       ((obj), E_TYPE_WEBKIT_EDITOR))
+#define E_IS_WEBKIT_EDITOR_CLASS(cls) \
+       (G_TYPE_CHECK_CLASS_TYPE \
+       ((cls), E_TYPE_WEBKIT_EDITOR))
+#define E_WEBKIT_EDITOR_GET_CLASS(obj) \
+       (G_TYPE_INSTANCE_GET_CLASS \
+       ((obj), E_TYPE_WEBKIT_EDITOR, EWebKitEditorClass))
+
+G_BEGIN_DECLS
+
+typedef struct _EWebKitEditor EWebKitEditor;
+typedef struct _EWebKitEditorClass EWebKitEditorClass;
+typedef struct _EWebKitEditorPrivate EWebKitEditorPrivate;
+
+struct _EWebKitEditor {
+       WebKitWebView parent;
+       EWebKitEditorPrivate *priv;
+};
+
+struct _EWebKitEditorClass {
+       WebKitWebViewClass parent_class;
+
+       gboolean        (*popup_event)          (EWebKitEditor *wk_editor,
+                                                GdkEventButton *event);
+       void            (*paste_primary_clipboard)
+                                               (EWebKitEditor *wk_editor);
+};
+
+GType          e_webkit_editor_get_type                (void) G_GNUC_CONST;
+
+EWebKitEditor *
+               e_webkit_editor_new                     (void);
+
+G_END_DECLS
+
+#endif /* E_WEBKIT_EDITOR_H */
diff --git a/modules/webkit-editor/evolution-module-webkit-editor.c 
b/modules/webkit-editor/evolution-module-webkit-editor.c
new file mode 100644
index 0000000..116903f
--- /dev/null
+++ b/modules/webkit-editor/evolution-module-webkit-editor.c
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2016 Red Hat, Inc. (www.redhat.com)
+ *
+ * This library 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 library 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 Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "e-webkit-editor-extension.h"
+
+/* Module Entry Points */
+void e_module_load (GTypeModule *type_module);
+void e_module_unload (GTypeModule *type_module);
+
+G_MODULE_EXPORT void
+e_module_load (GTypeModule *type_module)
+{
+       e_webkit_editor_extension_type_register (type_module);
+}
+
+G_MODULE_EXPORT void
+e_module_unload (GTypeModule *type_module)
+{
+}
diff --git a/modules/webkit-editor/web-extension/Makefile.am b/modules/webkit-editor/web-extension/Makefile.am
new file mode 100644
index 0000000..f2a7b02
--- /dev/null
+++ b/modules/webkit-editor/web-extension/Makefile.am
@@ -0,0 +1,38 @@
+webextensionswebkiteditor_LTLIBRARIES = libewebkiteditorwebextension.la
+
+libewebkiteditorwebextension_la_SOURCES = \
+       e-composer-dom-functions.c      \
+       e-composer-dom-functions.h      \
+       e-dialogs-dom-functions.c       \
+       e-dialogs-dom-functions.h       \
+       e-editor-dom-functions.c        \
+       e-editor-dom-functions.h        \
+       e-editor-page.c                 \
+       e-editor-page.h                 \
+       e-editor-undo-redo-manager.c    \
+       e-editor-undo-redo-manager.h    \
+       e-editor-web-extension.c        \
+       e-editor-web-extension.h        \
+       e-editor-web-extension-main.c   \
+       e-editor-web-extension-names.h  \
+       $(NULL)
+
+libewebkiteditorwebextension_la_CPPFLAGS = \
+       $(AM_CPPFLAGS)                                  \
+       -I$(top_srcdir)                                 \
+       -DEVOLUTION_IMAGESDIR=\""$(imagesdir)"\"        \
+       $(EVOLUTION_DATA_SERVER_CFLAGS)                 \
+       $(GNOME_PLATFORM_CFLAGS)                        \
+       $(WEB_EXTENSIONS_CFLAGS)
+
+libewebkiteditorwebextension_la_LIBADD =               \
+       $(top_builddir)/e-util/libevolution-util.la     \
+       $(top_builddir)/web-extensions/libedomutils.la  \
+       $(EVOLUTION_DATA_SERVER_LIBS)                   \
+       $(GNOME_PLATFORM_LIBS)                          \
+       $(WEB_EXTENSIONS_LIBS)
+
+libewebkiteditorwebextension_la_LDFLAGS = \
+       -module -avoid-version -no-undefined
+
+-include $(top_srcdir)/git.mk
diff --git a/modules/webkit-editor/web-extension/e-composer-dom-functions.c 
b/modules/webkit-editor/web-extension/e-composer-dom-functions.c
new file mode 100644
index 0000000..9c8a4fa
--- /dev/null
+++ b/modules/webkit-editor/web-extension/e-composer-dom-functions.c
@@ -0,0 +1,832 @@
+/*
+ * e-composer-private-dom-functions.c
+ *
+ * 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; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <string.h>
+
+#define WEBKIT_DOM_USE_UNSTABLE_API
+#include <webkitdom/WebKitDOMDOMSelection.h>
+#include <webkitdom/WebKitDOMDOMWindowUnstable.h>
+#include <webkitdom/WebKitDOMHTMLElementUnstable.h>
+#undef WEBKIT_DOM_USE_UNSTABLE_API
+
+#include <camel/camel.h>
+
+#include "web-extensions/e-dom-utils.h"
+
+#include "e-editor-page.h"
+#include "e-editor-dom-functions.h"
+#include "e-editor-undo-redo-manager.h"
+
+#include "e-composer-dom-functions.h"
+
+static WebKitDOMElement *
+prepare_top_signature_spacer (EEditorPage *editor_page)
+{
+       WebKitDOMElement *element;
+
+       element = e_editor_dom_prepare_paragraph (editor_page, FALSE);
+       webkit_dom_element_remove_attribute (element, "id");
+       element_add_class (element, "-x-evo-top-signature-spacer");
+
+       return element;
+}
+
+static gboolean
+add_signature_delimiter (void)
+{
+       gboolean ret_val;
+       GSettings *settings;
+
+       settings = e_util_ref_settings ("org.gnome.evolution.mail");
+       ret_val = !g_settings_get_boolean (settings, "composer-no-signature-delim");
+       g_object_unref (settings);
+
+       return ret_val;
+}
+
+static gboolean
+use_top_signature (void)
+{
+       gboolean ret_val;
+       GSettings *settings;
+
+       settings = e_util_ref_settings ("org.gnome.evolution.mail");
+       ret_val = g_settings_get_boolean (settings, "composer-top-signature");
+       g_object_unref (settings);
+
+       return ret_val;
+}
+
+static gboolean
+start_typing_at_bottom (void)
+{
+       gboolean ret_val;
+       GSettings *settings;
+
+       settings = e_util_ref_settings ("org.gnome.evolution.mail");
+       ret_val = g_settings_get_boolean (settings, "composer-reply-start-bottom");
+       g_object_unref (settings);
+
+       return ret_val;
+}
+
+static void
+move_caret_after_signature_inserted (EEditorPage *editor_page)
+{
+       WebKitDOMDocument *document;
+       WebKitDOMElement *element, *signature;
+       WebKitDOMHTMLElement *body;
+       WebKitDOMNodeList *paragraphs = NULL;
+       gboolean top_signature;
+       gboolean start_bottom;
+       gboolean has_paragraphs_in_body = TRUE;
+
+       g_return_if_fail (E_IS_EDITOR_PAGE (editor_page));
+
+       document = e_editor_page_get_document (editor_page);
+
+       top_signature = use_top_signature ();
+       start_bottom = start_typing_at_bottom ();
+
+       body = webkit_dom_document_get_body (document);
+       e_editor_page_block_selection_changed (editor_page);
+
+       paragraphs = webkit_dom_document_query_selector_all (document, "[data-evo-paragraph]", NULL);
+       signature = webkit_dom_document_query_selector (document, ".-x-evo-signature-wrapper", NULL);
+       /* Situation when wrapped paragraph is just in signature and not in message body */
+       if (webkit_dom_node_list_get_length (paragraphs) == 1)
+               if (signature && webkit_dom_element_query_selector (signature, "[data-evo-paragraph]", NULL))
+                       has_paragraphs_in_body = FALSE;
+
+       /*
+        *
+        * Keeping Signatures in the beginning of composer
+        * ------------------------------------------------
+        *
+        * Purists are gonna blast me for this.
+        * But there are so many people (read Outlook users) who want this.
+        * And Evo is an exchange-client, Outlook-replacement etc.
+        * So Here it goes :(
+        *
+        * -- Sankar
+        *
+        */
+       if (signature && top_signature) {
+               WebKitDOMElement *spacer;
+
+               spacer = prepare_top_signature_spacer (editor_page);
+               webkit_dom_node_insert_before (
+                       WEBKIT_DOM_NODE (body),
+                       WEBKIT_DOM_NODE (spacer),
+                       webkit_dom_node_get_next_sibling (WEBKIT_DOM_NODE (signature)),
+                       NULL);
+       }
+
+       if (webkit_dom_node_list_get_length (paragraphs) == 0)
+               has_paragraphs_in_body = FALSE;
+
+       element = webkit_dom_document_get_element_by_id (document, "-x-evo-input-start");
+       if (!signature) {
+               if (start_bottom) {
+                       if (!element) {
+                               element = e_editor_dom_prepare_paragraph (editor_page, FALSE);
+                               webkit_dom_element_set_id (element, "-x-evo-input-start");
+                               webkit_dom_node_append_child (
+                                       WEBKIT_DOM_NODE (body),
+                                       WEBKIT_DOM_NODE (element),
+                                       NULL);
+                       }
+               } else
+                       element = WEBKIT_DOM_ELEMENT (body);
+
+               goto move_caret;
+       }
+
+       /* When there is an option composer-reply-start-bottom set we have
+        * to move the caret between reply and signature. */
+       if (!has_paragraphs_in_body) {
+               element = e_editor_dom_prepare_paragraph (editor_page, FALSE);
+               webkit_dom_element_set_id (element, "-x-evo-input-start");
+               if (top_signature) {
+                       if (start_bottom) {
+                               webkit_dom_node_append_child (
+                                       WEBKIT_DOM_NODE (body),
+                                       WEBKIT_DOM_NODE (element),
+                                       NULL);
+                       } else {
+                               webkit_dom_node_insert_before (
+                                       WEBKIT_DOM_NODE (body),
+                                       WEBKIT_DOM_NODE (element),
+                                       WEBKIT_DOM_NODE (signature),
+                                       NULL);
+                       }
+               } else {
+                       if (start_bottom)
+                               webkit_dom_node_insert_before (
+                                       WEBKIT_DOM_NODE (body),
+                                       WEBKIT_DOM_NODE (element),
+                                       WEBKIT_DOM_NODE (signature),
+                                       NULL);
+                       else
+                               element = WEBKIT_DOM_ELEMENT (body);
+               }
+       } else {
+               if (!element && top_signature) {
+                       element = e_editor_dom_prepare_paragraph (editor_page, FALSE);
+                       webkit_dom_element_set_id (element, "-x-evo-input-start");
+                       if (start_bottom) {
+                                       webkit_dom_node_append_child (
+                                       WEBKIT_DOM_NODE (body),
+                                       WEBKIT_DOM_NODE (element),
+                                       NULL);
+                       } else {
+                               webkit_dom_node_insert_before (
+                                       WEBKIT_DOM_NODE (body),
+                                       WEBKIT_DOM_NODE (element),
+                                       WEBKIT_DOM_NODE (signature),
+                                       NULL);
+                       }
+               } else if (element && top_signature && !start_bottom) {
+                       webkit_dom_node_insert_before (
+                               WEBKIT_DOM_NODE (body),
+                               WEBKIT_DOM_NODE (element),
+                               WEBKIT_DOM_NODE (signature),
+                               NULL);
+               } else if (element && start_bottom) {
+                       /* Leave it how it is */
+               } else
+                       element = WEBKIT_DOM_ELEMENT (body);
+       }
+
+ move_caret:
+       if (element) {
+               WebKitDOMDOMSelection *dom_selection = NULL;
+               WebKitDOMDOMWindow *dom_window = NULL;
+               WebKitDOMRange *range = NULL;
+
+               dom_window = webkit_dom_document_get_default_view (document);
+               dom_selection = webkit_dom_dom_window_get_selection (dom_window);
+               range = webkit_dom_document_create_range (document);
+
+               webkit_dom_range_select_node_contents (
+                       range, WEBKIT_DOM_NODE (element), NULL);
+               webkit_dom_range_collapse (range, TRUE, NULL);
+               webkit_dom_dom_selection_remove_all_ranges (dom_selection);
+               webkit_dom_dom_selection_add_range (dom_selection, range);
+
+               g_clear_object (&dom_selection);
+               g_clear_object (&dom_window);
+               g_clear_object (&range);
+       }
+
+       if (start_bottom)
+               e_editor_dom_scroll_to_caret (editor_page);
+
+       g_clear_object (&paragraphs);
+
+       e_editor_dom_force_spell_check_in_viewport (editor_page);
+       e_editor_page_unblock_selection_changed (editor_page);
+}
+
+gchar *
+e_composer_dom_insert_signature (EEditorPage *editor_page,
+                                const gchar *content,
+                                gboolean is_html,
+                                const gchar *id,
+                                gboolean *set_signature_from_message,
+                                gboolean *check_if_signature_is_changed,
+                                gboolean *ignore_next_signature_change)
+{
+       WebKitDOMDocument *document;
+       WebKitDOMElement *signature_to_insert;
+       WebKitDOMElement *insert_signature_in = NULL;
+       WebKitDOMElement *signature_wrapper = NULL;
+       WebKitDOMElement *element, *converted_signature = NULL;
+       WebKitDOMHTMLElement *body;
+       WebKitDOMHTMLCollection *signatures = NULL;
+       gchar *new_signature_id = NULL;
+       gchar *signature_text = NULL;
+       gboolean top_signature, html_mode;
+       gulong list_length, ii;
+
+       g_return_val_if_fail (E_IS_EDITOR_PAGE (editor_page), NULL);
+       g_return_val_if_fail (set_signature_from_message != NULL, NULL);
+       g_return_val_if_fail (check_if_signature_is_changed != NULL, NULL);
+       g_return_val_if_fail (ignore_next_signature_change != NULL, NULL);
+
+       document = e_editor_page_get_document (editor_page);
+       body = webkit_dom_document_get_body (document);
+
+       /* "Edit as New Message" sets is_message_from_edit_as_new.
+        * Always put the signature at the bottom for that case. */
+       top_signature = use_top_signature ();
+
+       html_mode = e_editor_page_get_html_mode (editor_page);
+
+       /* Create the DOM signature that is the same across all types of signatures. */
+       signature_to_insert = webkit_dom_document_create_element (document, "span", NULL);
+       webkit_dom_element_set_class_name (signature_to_insert, "-x-evo-signature");
+       /* The combo box active ID is the signature's ESource UID. */
+       webkit_dom_element_set_id (signature_to_insert, id);
+       insert_signature_in = signature_to_insert;
+
+       /* The signature has no content usually it means it is set to None. */
+       if (!(content && *content))
+               goto insert;
+
+       if (!is_html) {
+               gchar *html;
+
+               html = camel_text_to_html (content, 0, 0);
+               if (html) {
+                       signature_text = html;
+               } else
+                       signature_text = g_strdup (content);
+
+               insert_signature_in = webkit_dom_document_create_element (document, "pre", NULL);
+               webkit_dom_node_append_child (
+                       WEBKIT_DOM_NODE (signature_to_insert),
+                       WEBKIT_DOM_NODE (insert_signature_in),
+                       NULL);
+       } else
+               signature_text = g_strdup (content);
+
+       /* If inserting HTML signature in the plain text composer we have to convert it. */
+       if (is_html && !html_mode && !strstr (signature_text, "data-evo-signature-plain-text-mode")) {
+               gchar *inner_text;
+
+               /* Save the converted signature to avoid parsing it later again
+                * while inserting it into the view. */
+               converted_signature = webkit_dom_document_create_element (document, "pre", NULL);
+               webkit_dom_element_set_inner_html (converted_signature, signature_text, NULL);
+               e_editor_dom_convert_element_from_html_to_plain_text (editor_page, converted_signature);
+               inner_text = webkit_dom_html_element_get_inner_text (WEBKIT_DOM_HTML_ELEMENT 
(converted_signature));
+
+               g_free (signature_text);
+               signature_text = inner_text ? g_strstrip (inner_text) : g_strdup ("");
+               /* because of the -- \n check */
+               is_html = FALSE;
+       }
+
+       /* The signature dash convention ("-- \n") is specified
+        * in the "Son of RFC 1036", section 4.3.2.
+        * http://www.chemie.fu-berlin.de/outerspace/netnews/son-of-1036.html
+        */
+       if (add_signature_delimiter ()) {
+               const gchar *delim;
+               const gchar *delim_nl;
+
+               if (is_html) {
+                       delim = "-- <BR>";
+                       delim_nl = "\n-- <BR>";
+               } else {
+                       delim = "-- \n";
+                       delim_nl = "\n-- \n";
+               }
+
+               /* Skip the delimiter if the signature already has one. */
+               if (g_ascii_strncasecmp (signature_text, delim, strlen (delim)) == 0)
+                       ;  /* skip */
+               else if (e_util_strstrcase (signature_text, delim_nl) != NULL)
+                       ;  /* skip */
+               else {
+                       WebKitDOMElement *pre_delimiter;
+
+                       pre_delimiter = webkit_dom_document_create_element (document, "pre", NULL);
+                       /* Always use the HTML delimiter as we are never in anything
+                        * like a strict plain text mode. */
+                       webkit_dom_element_set_inner_html (pre_delimiter, "-- <br>", NULL);
+                       webkit_dom_node_append_child (
+                               WEBKIT_DOM_NODE (insert_signature_in),
+                               WEBKIT_DOM_NODE (pre_delimiter),
+                               NULL);
+               }
+       }
+
+       if (converted_signature) {
+               WebKitDOMNode *node;
+
+               while ((node = webkit_dom_node_get_first_child (WEBKIT_DOM_NODE (converted_signature))))
+                       webkit_dom_node_append_child (
+                               WEBKIT_DOM_NODE (insert_signature_in), node, NULL);
+               remove_node (WEBKIT_DOM_NODE (converted_signature));
+       } else
+               webkit_dom_html_element_insert_adjacent_html (
+                       WEBKIT_DOM_HTML_ELEMENT (insert_signature_in),
+                       "beforeend",
+                       signature_text,
+                       NULL);
+
+       element = webkit_dom_element_query_selector (
+               insert_signature_in, "[data-evo-signature-plain-text-mode]", NULL);
+       if (element)
+               webkit_dom_element_remove_attribute (
+                       element, "data-evo-signature-plain-text-mode");
+       g_free (signature_text);
+
+insert:
+       /* Remove the old signature and insert the new one. */
+       signatures = webkit_dom_document_get_elements_by_class_name_as_html_collection (
+               document, "-x-evo-signature-wrapper");
+       list_length = webkit_dom_html_collection_get_length (signatures);
+       for (ii = 0; ii < list_length; ii++) {
+               WebKitDOMNode *wrapper, *signature;
+
+               wrapper = webkit_dom_html_collection_item (signatures, ii);
+               signature = webkit_dom_node_get_first_child (wrapper);
+
+               /* Old messages will have the signature id in the name attribute, correct it. */
+               element_rename_attribute (WEBKIT_DOM_ELEMENT (signature), "name", "id");
+
+               /* When we are editing a message with signature, we need to unset the
+                * active signature id as if the signature in the message was edited
+                * by the user we would discard these changes. */
+               if (*set_signature_from_message && content) {
+                       if (*check_if_signature_is_changed) {
+                               /* Normalize the signature that we want to insert as the one in the
+                                * message already is normalized. */
+                               webkit_dom_node_normalize (WEBKIT_DOM_NODE (signature_to_insert));
+                               if (!webkit_dom_node_is_equal_node (WEBKIT_DOM_NODE (signature_to_insert), 
signature)) {
+                                       /* Signature in the body is different than the one with the
+                                        * same id, so set the active signature to None and leave
+                                        * the signature that is in the body. */
+                                       new_signature_id = g_strdup ("none");
+                                       *ignore_next_signature_change = TRUE;
+                               }
+
+                               *check_if_signature_is_changed = FALSE;
+                               *set_signature_from_message = FALSE;
+                       } else {
+                               /* Load the signature and check if is it the same
+                                * as the signature in body or the user previously
+                                * changed it. */
+                               new_signature_id = webkit_dom_element_get_id (WEBKIT_DOM_ELEMENT (signature));
+                               *check_if_signature_is_changed = TRUE;
+                       }
+                       g_object_unref (wrapper);
+                       g_clear_object (&signatures);
+
+                       return new_signature_id;
+               }
+
+               /* If the top signature was set we have to remove the newline
+                * that was inserted after it */
+               if (top_signature) {
+                       WebKitDOMElement *spacer;
+
+                       spacer = webkit_dom_document_query_selector (
+                               document, ".-x-evo-top-signature-spacer", NULL);
+                       if (spacer)
+                               remove_node_if_empty (WEBKIT_DOM_NODE (spacer));
+               }
+
+               /* Leave just one signature wrapper there as it will be reused. */
+               if (ii != list_length - 1) {
+                       g_object_unref (wrapper);
+                       remove_node (wrapper);
+               } else {
+                       remove_node (signature);
+                       signature_wrapper = WEBKIT_DOM_ELEMENT (wrapper);
+               }
+       }
+
+       if (signature_wrapper) {
+               webkit_dom_node_append_child (
+                       WEBKIT_DOM_NODE (signature_wrapper),
+                       WEBKIT_DOM_NODE (signature_to_insert),
+                       NULL);
+
+               /* Insert a spacer below the top signature */
+               if (top_signature && content) {
+                       WebKitDOMElement *spacer;
+
+                       spacer = prepare_top_signature_spacer (editor_page);
+                       webkit_dom_node_insert_before (
+                               WEBKIT_DOM_NODE (body),
+                               WEBKIT_DOM_NODE (spacer),
+                               webkit_dom_node_get_next_sibling (WEBKIT_DOM_NODE (signature_wrapper)),
+                               NULL);
+               }
+
+               g_object_unref (signature_wrapper);
+       } else {
+               signature_wrapper = webkit_dom_document_create_element (document, "div", NULL);
+               webkit_dom_element_set_class_name (signature_wrapper, "-x-evo-signature-wrapper");
+
+               webkit_dom_node_append_child (
+                       WEBKIT_DOM_NODE (signature_wrapper),
+                       WEBKIT_DOM_NODE (signature_to_insert),
+                       NULL);
+
+               if (top_signature) {
+                       WebKitDOMNode *child;
+
+                       child = webkit_dom_node_get_first_child (WEBKIT_DOM_NODE (body));
+
+                       if (start_typing_at_bottom ()) {
+                               webkit_dom_node_insert_before (
+                                       WEBKIT_DOM_NODE (body),
+                                       WEBKIT_DOM_NODE (signature_wrapper),
+                                       child,
+                                       NULL);
+                       } else {
+                               /* When we are using signature on top the caret
+                                * should be before the signature */
+                               webkit_dom_node_insert_before (
+                                       WEBKIT_DOM_NODE (body),
+                                       WEBKIT_DOM_NODE (signature_wrapper),
+                                       child,
+                                       NULL);
+                       }
+               } else {
+                       webkit_dom_node_append_child (
+                               WEBKIT_DOM_NODE (body),
+                               WEBKIT_DOM_NODE (signature_wrapper),
+                               NULL);
+               }
+
+               move_caret_after_signature_inserted (editor_page);
+       }
+       g_clear_object (&signatures);
+
+       if (is_html && html_mode)
+               e_editor_dom_fix_file_uri_images (editor_page);
+
+       /* Make sure the flag will be unset and won't influence user's choice */
+       *set_signature_from_message = FALSE;
+
+       return NULL;
+}
+
+gchar *
+e_composer_dom_get_active_signature_uid (EEditorPage *editor_page)
+{
+       WebKitDOMDocument *document;
+       WebKitDOMElement *element;
+       gchar *uid = NULL;
+
+       g_return_val_if_fail (E_IS_EDITOR_PAGE (editor_page), NULL);
+
+       document = e_editor_page_get_document (editor_page);
+
+       if ((element = webkit_dom_document_query_selector (document, ".-x-evo-signature[id]", NULL)))
+               uid = webkit_dom_element_get_id (element);
+
+       return uid;
+}
+
+gchar *
+e_composer_dom_get_raw_body_content_without_signature (EEditorPage *editor_page)
+{
+       WebKitDOMDocument *document;
+       WebKitDOMNodeList *list = NULL;
+       GString* content;
+       gulong ii, length;
+
+       g_return_val_if_fail (E_IS_EDITOR_PAGE (editor_page), NULL);
+
+       document = e_editor_page_get_document (editor_page);
+
+       content = g_string_new (NULL);
+
+       list = webkit_dom_document_query_selector_all (
+               document, "body > *:not(.-x-evo-signature-wrapper)", NULL);
+       length = webkit_dom_node_list_get_length (list);
+       for (ii = 0; ii < length; ii++) {
+               WebKitDOMNode *node = webkit_dom_node_list_item (list, ii);
+
+               if (!WEBKIT_DOM_IS_HTML_QUOTE_ELEMENT (node)) {
+                       gchar *text;
+
+                       text = webkit_dom_html_element_get_inner_text (WEBKIT_DOM_HTML_ELEMENT (node));
+                       g_string_append (content, text);
+                       g_free (text);
+
+                       if (WEBKIT_DOM_IS_HTML_DIV_ELEMENT (node))
+                               g_string_append (content, "\n");
+                       else
+                               g_string_append (content, " ");
+               }
+       }
+       g_clear_object (&list);
+
+       return g_string_free (content, FALSE);
+}
+
+gchar *
+e_composer_dom_get_raw_body_content (EEditorPage *editor_page)
+{
+       WebKitDOMDocument *document;
+       WebKitDOMHTMLElement *body;
+
+       g_return_val_if_fail (E_IS_EDITOR_PAGE (editor_page), NULL);
+
+       document = e_editor_page_get_document (editor_page);
+
+       body = webkit_dom_document_get_body (document);
+
+       return  webkit_dom_html_element_get_inner_text (body);
+}
+
+static void
+insert_nbsp_history_event (WebKitDOMDocument *document,
+                          EEditorUndoRedoManager *manager,
+                           gboolean delete,
+                           guint x,
+                           guint y)
+{
+       EEditorHistoryEvent *event;
+       WebKitDOMDocumentFragment *fragment;
+
+       event = g_new0 (EEditorHistoryEvent, 1);
+       event->type = HISTORY_AND;
+       e_editor_undo_redo_manager_insert_history_event (manager, event);
+
+       fragment = webkit_dom_document_create_document_fragment (document);
+       webkit_dom_node_append_child (
+               WEBKIT_DOM_NODE (fragment),
+               WEBKIT_DOM_NODE (
+                       webkit_dom_document_create_text_node (document, UNICODE_NBSP)),
+               NULL);
+
+       event = g_new0 (EEditorHistoryEvent, 1);
+       event->type = HISTORY_DELETE;
+
+       if (delete)
+               g_object_set_data (G_OBJECT (fragment), "history-delete-key", GINT_TO_POINTER (1));
+
+       event->data.fragment = fragment;
+
+       event->before.start.x = x;
+       event->before.start.y = y;
+       event->before.end.x = x;
+       event->before.end.y = y;
+
+       event->after.start.x = x;
+       event->after.start.y = y;
+       event->after.end.x = x;
+       event->after.end.y = y;
+
+       e_editor_undo_redo_manager_insert_history_event (manager, event);
+}
+
+void
+e_composer_dom_save_drag_and_drop_history (EEditorPage *editor_page)
+{
+       WebKitDOMDocument *document;
+       WebKitDOMDocumentFragment *fragment;
+       WebKitDOMDOMSelection *dom_selection = NULL;
+       WebKitDOMDOMWindow *dom_window = NULL;
+       WebKitDOMRange *beginning_of_line = NULL;
+       WebKitDOMRange *range = NULL, *range_clone = NULL;
+       EEditorHistoryEvent *event;
+       EEditorUndoRedoManager *manager;
+       gboolean start_to_start, end_to_end;
+       gchar *range_text;
+       guint x, y;
+
+       g_return_if_fail (E_IS_EDITOR_PAGE (editor_page));
+
+       document = e_editor_page_get_document (editor_page);
+       manager = e_editor_page_get_undo_redo_manager (editor_page);
+
+       if (!(dom_window = webkit_dom_document_get_default_view (document)))
+               return;
+
+       if (!(dom_selection = webkit_dom_dom_window_get_selection (dom_window))) {
+               g_clear_object (&dom_window);
+               return;
+       }
+
+       g_clear_object (&dom_window);
+
+       if (webkit_dom_dom_selection_get_range_count (dom_selection) < 1) {
+               g_clear_object (&dom_selection);
+               return;
+       }
+
+       /* Obtain the dragged content. */
+       range = webkit_dom_dom_selection_get_range_at (dom_selection, 0, NULL);
+       range_clone = webkit_dom_range_clone_range (range, NULL);
+
+       /* Create the history event for the content that will
+        * be removed by DnD. */
+       event = g_new0 (EEditorHistoryEvent, 1);
+       event->type = HISTORY_DELETE;
+
+       e_editor_dom_selection_get_coordinates (editor_page,
+               &event->before.start.x,
+               &event->before.start.y,
+               &event->before.end.x,
+               &event->before.end.y);
+
+       x = event->before.start.x;
+       y = event->before.start.y;
+
+       event->after.start.x = x;
+       event->after.start.y = y;
+       event->after.end.x = x;
+       event->after.end.y = y;
+
+       /* Save the content that will be removed. */
+       fragment = webkit_dom_range_clone_contents (range_clone, NULL);
+
+       /* Extend the cloned range to point one character after
+        * the selection ends to later check if there is a whitespace
+        * after it. */
+       webkit_dom_range_set_end (
+               range_clone,
+               webkit_dom_range_get_end_container (range_clone, NULL),
+               webkit_dom_range_get_end_offset (range_clone, NULL) + 1,
+               NULL);
+       range_text = webkit_dom_range_get_text (range_clone);
+
+       /* Check if the current selection starts on the beginning
+        * of line. */
+       webkit_dom_dom_selection_modify (
+               dom_selection, "extend", "left", "lineboundary");
+       beginning_of_line = webkit_dom_dom_selection_get_range_at (dom_selection, 0, NULL);
+       start_to_start = webkit_dom_range_compare_boundary_points (
+               beginning_of_line, 0 /* START_TO_START */, range, NULL) == 0;
+
+       /* Restore the selection to state before the check. */
+       webkit_dom_dom_selection_remove_all_ranges (dom_selection);
+       webkit_dom_dom_selection_add_range (dom_selection, range);
+       g_clear_object (&beginning_of_line);
+
+       /* Check if the current selection end on the end of the line. */
+       webkit_dom_dom_selection_modify (
+               dom_selection, "extend", "right", "lineboundary");
+       beginning_of_line = webkit_dom_dom_selection_get_range_at (dom_selection, 0, NULL);
+       end_to_end = webkit_dom_range_compare_boundary_points (
+               beginning_of_line, 2 /* END_TO_END */, range, NULL) == 0;
+
+       /* Dragging the whole line. */
+       if (start_to_start && end_to_end) {
+               WebKitDOMNode *container, *actual_block, *tmp_block;
+
+               /* Select the whole line (to the beginning of the next
+                * one so we can reuse the undo code while undoing this.
+                * Because of this we need to special mark the event
+                * with history-drag-and-drop to correct the selection
+                * after undoing it (otherwise the beginning of the next
+                * line will be selected as well. */
+               webkit_dom_dom_selection_modify (
+                       dom_selection, "extend", "right", "character");
+               g_clear_object (&beginning_of_line);
+               beginning_of_line = webkit_dom_dom_selection_get_range_at (dom_selection, 0, NULL);
+
+               container = webkit_dom_range_get_end_container (range, NULL);
+               actual_block = e_editor_dom_get_parent_block_node_from_child (container);
+
+               tmp_block = webkit_dom_range_get_end_container (beginning_of_line, NULL);
+               if ((tmp_block = e_editor_dom_get_parent_block_node_from_child (tmp_block))) {
+                       e_editor_dom_selection_get_coordinates (editor_page,
+                               &event->before.start.x,
+                               &event->before.start.y,
+                               &event->before.end.x,
+                               &event->before.end.y);
+
+                       /* Create the right content for the history event. */
+                       fragment = webkit_dom_document_create_document_fragment (document);
+                       /* The removed line. */
+                       webkit_dom_node_append_child (
+                               WEBKIT_DOM_NODE (fragment),
+                               webkit_dom_node_clone_node_with_error (actual_block, TRUE, NULL),
+                               NULL);
+                       /* The following block, but empty. */
+                       webkit_dom_node_append_child (
+                               WEBKIT_DOM_NODE (fragment),
+                               webkit_dom_node_clone_node_with_error (tmp_block, FALSE, NULL),
+                               NULL);
+                       g_object_set_data (
+                               G_OBJECT (fragment),
+                               "history-drag-and-drop",
+                               GINT_TO_POINTER (1));
+                       /* It should act as a Delete key press. */
+                       g_object_set_data (
+                               G_OBJECT (fragment),
+                               "history-delete-key",
+                               GINT_TO_POINTER (1));
+               }
+       }
+
+       event->data.fragment = fragment;
+       e_editor_undo_redo_manager_insert_history_event (manager, event);
+
+       /* Selection is ending on the end of the line, check if
+        * there is a space before the selection start. If so, it
+        * will be removed and we need create the history event
+        * for it. */
+       if (end_to_end) {
+               gchar *range_text_start;
+               glong start_offset;
+
+               start_offset = webkit_dom_range_get_start_offset (range_clone, NULL);
+               webkit_dom_range_set_start (
+                       range_clone,
+                       webkit_dom_range_get_start_container (range_clone, NULL),
+                       start_offset > 0 ? start_offset - 1 : 0,
+                       NULL);
+
+               range_text_start = webkit_dom_range_get_text (range_clone);
+               if (g_str_has_prefix (range_text_start, " ") ||
+                   g_str_has_prefix (range_text_start, UNICODE_NBSP))
+                       insert_nbsp_history_event (document, manager, FALSE, x, y);
+
+               g_free (range_text_start);
+       }
+
+       /* WebKit removes the space (if presented) after selection and
+        * we need to create a new history event for it. */
+       if (g_str_has_suffix (range_text, " ") ||
+           g_str_has_suffix (range_text, UNICODE_NBSP))
+               insert_nbsp_history_event (document, manager, TRUE, x, y);
+
+       g_free (range_text);
+
+       /* Restore the selection to original state. */
+       webkit_dom_dom_selection_remove_all_ranges (dom_selection);
+       webkit_dom_dom_selection_add_range (dom_selection, range);
+       g_clear_object (&beginning_of_line);
+
+       /* All the things above were about removing the content,
+        * create an AND event to continue later with inserting
+        * the dropped content. */
+       event = g_new0 (EEditorHistoryEvent, 1);
+       event->type = HISTORY_AND;
+       e_editor_undo_redo_manager_insert_history_event (manager, event);
+
+       g_clear_object (&dom_selection);
+
+       g_clear_object (&range);
+       g_clear_object (&range_clone);
+}
+
+void
+e_composer_dom_clean_after_drag_and_drop (EEditorPage *editor_page)
+{
+       g_return_if_fail (E_IS_EDITOR_PAGE (editor_page));
+
+       e_editor_dom_save_history_for_drop (editor_page);
+       e_editor_dom_check_magic_links (editor_page, FALSE);
+}
diff --git a/modules/webkit-editor/web-extension/e-composer-dom-functions.h 
b/modules/webkit-editor/web-extension/e-composer-dom-functions.h
new file mode 100644
index 0000000..8e0f934
--- /dev/null
+++ b/modules/webkit-editor/web-extension/e-composer-dom-functions.h
@@ -0,0 +1,48 @@
+/*
+ * e-composer-dom-functions.h
+ *
+ * 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; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+#ifndef E_COMPOSER_DOM_FUNCTIONS_H
+#define E_COMPOSER_DOM_FUNCTIONS_H
+
+#include <webkitdom/webkitdom.h>
+
+#include "e-editor-page.h"
+
+G_BEGIN_DECLS
+
+gchar *                e_composer_dom_insert_signature (EEditorPage *editor_page,
+                                                const gchar *content,
+                                                gboolean is_html,
+                                                const gchar *id,
+                                                gboolean *set_signature_from_message,
+                                                gboolean *check_if_signature_is_changed,
+                                                gboolean *ignore_next_signature_change);
+gchar *                e_composer_dom_get_active_signature_uid
+                                               (EEditorPage *editor_page);
+gchar *                e_composer_dom_get_raw_body_content_without_signature
+                                               (EEditorPage *editor_page);
+gchar *                e_composer_dom_get_raw_body_content
+                                               (EEditorPage *editor_page);
+void           e_composer_dom_save_drag_and_drop_history
+                                               (EEditorPage *editor_page);
+void           e_composer_dom_clean_after_drag_and_drop
+                                               (EEditorPage *editor_page);
+
+G_END_DECLS
+
+#endif /* E_COMPOSER_DOM_FUNCTIONS_H */
diff --git a/modules/webkit-editor/web-extension/e-dialogs-dom-functions.c 
b/modules/webkit-editor/web-extension/e-dialogs-dom-functions.c
new file mode 100644
index 0000000..84d17ff
--- /dev/null
+++ b/modules/webkit-editor/web-extension/e-dialogs-dom-functions.c
@@ -0,0 +1,1455 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2016 Red Hat, Inc. (www.redhat.com)
+ *
+ * This library 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 library 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 Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#define WEBKIT_DOM_USE_UNSTABLE_API
+#include <webkitdom/WebKitDOMDOMSelection.h>
+#include <webkitdom/WebKitDOMDOMWindowUnstable.h>
+#undef WEBKIT_DOM_USE_UNSTABLE_API
+
+#include "web-extensions/e-dom-utils.h"
+
+#include "e-editor-dom-functions.h"
+#include "e-editor-undo-redo-manager.h"
+
+#include "e-dialogs-dom-functions.h"
+
+/* ******************** Cell Dialog ***************** */
+
+typedef void (*DOMStrFunc) (WebKitDOMHTMLTableCellElement *cell, const gchar *val, gpointer user_data);
+typedef void (*DOMLongFunc) (WebKitDOMHTMLTableCellElement *cell, glong val, gpointer user_data);
+typedef void (*DOMBoolFunc) (WebKitDOMHTMLTableCellElement *cell, gboolean val, gpointer user_data);
+
+static WebKitDOMElement *
+get_current_cell_element (WebKitDOMDocument *document)
+{
+       return webkit_dom_document_get_element_by_id (document, "-x-evo-current-cell");
+}
+
+static void
+call_cell_dom_func (WebKitDOMHTMLTableCellElement *cell,
+                    gpointer func,
+                    GValue *value,
+                    gpointer user_data)
+{
+       if (G_VALUE_HOLDS_STRING (value)) {
+               DOMStrFunc f = func;
+               f (cell, g_value_get_string (value), user_data);
+       } else if (G_VALUE_HOLDS_LONG (value)) {
+               DOMLongFunc f = func;
+               f (cell, g_value_get_long (value), user_data);
+       } else if (G_VALUE_HOLDS_BOOLEAN (value)) {
+               DOMBoolFunc f = func;
+               f (cell, g_value_get_boolean (value), user_data);
+       }
+}
+
+static void
+for_each_cell_do (WebKitDOMElement *row,
+                  gpointer func,
+                  GValue *value,
+                  gpointer user_data)
+{
+       WebKitDOMHTMLCollection *cells = NULL;
+       gulong ii, length;
+
+       cells = webkit_dom_html_table_row_element_get_cells (
+                       WEBKIT_DOM_HTML_TABLE_ROW_ELEMENT (row));
+       length = webkit_dom_html_collection_get_length (cells);
+       for (ii = 0; ii < length; ii++) {
+               WebKitDOMNode *cell;
+               cell = webkit_dom_html_collection_item (cells, ii);
+               if (!cell) {
+                       continue;
+               }
+
+               call_cell_dom_func (
+                       WEBKIT_DOM_HTML_TABLE_CELL_ELEMENT (cell), func, value, user_data);
+               g_object_unref (cell);
+       }
+       g_clear_object (&cells);
+}
+
+static void
+cell_dialog_set_attribute (WebKitDOMDocument *document,
+                          EContentEditorScope scope,
+                          gpointer func,
+                          GValue *value,
+                          gpointer user_data)
+{
+       WebKitDOMElement *cell = get_current_cell_element (document);
+
+       if (scope == E_CONTENT_EDITOR_SCOPE_CELL) {
+
+               call_cell_dom_func (
+                       WEBKIT_DOM_HTML_TABLE_CELL_ELEMENT (cell),
+                       func, value, user_data);
+
+       } else if (scope == E_CONTENT_EDITOR_SCOPE_COLUMN) {
+               gulong index, ii, length;
+               WebKitDOMElement *table;
+               WebKitDOMHTMLCollection *rows = NULL;
+
+               index = webkit_dom_html_table_cell_element_get_cell_index (
+                               WEBKIT_DOM_HTML_TABLE_CELL_ELEMENT (cell));
+               table = dom_node_find_parent_element (WEBKIT_DOM_NODE (cell), "TABLE");
+               if (!table) {
+                       return;
+               }
+
+               rows = webkit_dom_html_table_element_get_rows (
+                               WEBKIT_DOM_HTML_TABLE_ELEMENT (table));
+               length = webkit_dom_html_collection_get_length (rows);
+               for (ii = 0; ii < length; ii++) {
+                       WebKitDOMNode *row, *cell;
+                       WebKitDOMHTMLCollection *cells = NULL;
+
+                       row = webkit_dom_html_collection_item (rows, ii);
+                       cells = webkit_dom_html_table_row_element_get_cells (
+                                       WEBKIT_DOM_HTML_TABLE_ROW_ELEMENT (row));
+                       cell = webkit_dom_html_collection_item (cells, index);
+                       if (!cell) {
+                               g_object_unref (row);
+                               g_clear_object (&cells);
+                               continue;
+                       }
+
+                       call_cell_dom_func (
+                               WEBKIT_DOM_HTML_TABLE_CELL_ELEMENT (cell),
+                               func, value, user_data);
+                       g_object_unref (row);
+                       g_clear_object (&cells);
+                       g_object_unref (cell);
+               }
+               g_clear_object (&rows);
+
+       } else if (scope == E_CONTENT_EDITOR_SCOPE_ROW) {
+               WebKitDOMElement *row;
+
+               row = dom_node_find_parent_element (WEBKIT_DOM_NODE (cell), "TR");
+               if (!row) {
+                       return;
+               }
+
+               for_each_cell_do (row, func, value, user_data);
+
+       } else if (scope == E_CONTENT_EDITOR_SCOPE_TABLE) {
+               gulong ii, length;
+               WebKitDOMElement *table;
+               WebKitDOMHTMLCollection *rows = NULL;
+
+               table = dom_node_find_parent_element (WEBKIT_DOM_NODE (cell), "TABLE");
+               if (!table) {
+                       return;
+               }
+
+               rows = webkit_dom_html_table_element_get_rows (
+                               WEBKIT_DOM_HTML_TABLE_ELEMENT (table));
+               length = webkit_dom_html_collection_get_length (rows);
+               for (ii = 0; ii < length; ii++) {
+                       WebKitDOMNode *row;
+
+                       row = webkit_dom_html_collection_item (rows, ii);
+                       if (!row) {
+                               g_object_unref (row);
+                               continue;
+                       }
+
+                       for_each_cell_do (
+                               WEBKIT_DOM_ELEMENT (row), func, value, user_data);
+                       g_object_unref (row);
+               }
+               g_clear_object (&rows);
+       }
+}
+
+static void
+cell_set_header_style (WebKitDOMHTMLTableCellElement *cell,
+                       gboolean header_style,
+                      gpointer user_data)
+{
+       WebKitDOMDocument *document;
+       WebKitDOMNodeList *nodes = NULL;
+       WebKitDOMElement *new_cell;
+       gulong length, ii;
+       gchar *tagname;
+
+       document = webkit_dom_node_get_owner_document (WEBKIT_DOM_NODE (cell));
+       tagname = webkit_dom_element_get_tag_name (WEBKIT_DOM_ELEMENT (cell));
+
+       if (header_style && (g_ascii_strncasecmp (tagname, "TD", 2) == 0)) {
+
+               new_cell = webkit_dom_document_create_element (document, "TH", NULL);
+
+       } else if (!header_style && (g_ascii_strncasecmp (tagname, "TH", 2) == 0)) {
+
+               new_cell = webkit_dom_document_create_element (document, "TD", NULL);
+
+       } else {
+               g_free (tagname);
+               return;
+       }
+
+       webkit_dom_element_set_id (new_cell, "-x-evo-current-cell");
+
+       /* Move all child nodes from cell to new_cell */
+       nodes = webkit_dom_node_get_child_nodes (WEBKIT_DOM_NODE (cell));
+       length = webkit_dom_node_list_get_length (nodes);
+       for (ii = 0; ii < length; ii++) {
+               WebKitDOMNode *node;
+
+               node = webkit_dom_node_list_item (nodes, ii);
+               webkit_dom_node_append_child (
+                       WEBKIT_DOM_NODE (new_cell), node, NULL);
+               g_object_unref (node);
+       }
+       g_clear_object (&nodes);
+
+       /* Insert new_cell before cell and remove cell */
+       webkit_dom_node_insert_before (
+               webkit_dom_node_get_parent_node (
+                       WEBKIT_DOM_NODE (cell)),
+               WEBKIT_DOM_NODE (new_cell),
+               WEBKIT_DOM_NODE (cell), NULL);
+
+       webkit_dom_node_remove_child (
+               webkit_dom_node_get_parent_node (WEBKIT_DOM_NODE (cell)),
+               WEBKIT_DOM_NODE (cell), NULL);
+
+       g_free (tagname);
+}
+
+void
+e_dialogs_dom_cell_mark_current_cell_element (EEditorPage *editor_page,
+                                             const gchar *id)
+{
+       EEditorUndoRedoManager *manager;
+       WebKitDOMElement *cell;
+       WebKitDOMDocument *document;
+       WebKitDOMNode *node_under_mouse_click;
+
+       g_return_if_fail (E_IS_EDITOR_PAGE (editor_page));
+       g_return_if_fail (id != NULL);
+
+       document = e_editor_page_get_document (editor_page);
+
+       node_under_mouse_click = e_editor_page_get_node_under_mouse_click (editor_page);
+
+       if (node_under_mouse_click && WEBKIT_DOM_IS_HTML_TABLE_CELL_ELEMENT (node_under_mouse_click)) {
+               cell = WEBKIT_DOM_ELEMENT (node_under_mouse_click);
+       } else {
+               WebKitDOMElement *selection_start;
+
+               e_editor_dom_selection_save (editor_page);
+
+               selection_start = webkit_dom_document_get_element_by_id (
+                       document, "-x-evo-selection-start-marker");
+
+               cell = dom_node_find_parent_element (WEBKIT_DOM_NODE (selection_start), "TD");
+               if (!cell)
+                       cell = dom_node_find_parent_element (WEBKIT_DOM_NODE (selection_start), "TH");
+
+               e_editor_dom_selection_restore (editor_page);
+       }
+
+       if (cell)
+               webkit_dom_element_set_id (cell, "-x-evo-current-cell");
+       else
+               return;
+
+       manager = e_editor_page_get_undo_redo_manager (editor_page);
+       if (!e_editor_undo_redo_manager_is_operation_in_progress (manager)) {
+               EEditorHistoryEvent *ev;
+               WebKitDOMElement *table;
+
+               ev = g_new0 (EEditorHistoryEvent, 1);
+               ev->type = HISTORY_TABLE_DIALOG;
+
+               e_editor_dom_selection_get_coordinates (editor_page,
+                       &ev->before.start.x,
+                       &ev->before.start.y,
+                       &ev->before.end.x,
+                       &ev->before.end.y);
+
+               table = dom_node_find_parent_element (
+                       WEBKIT_DOM_NODE (cell), "TABLE");
+               if (table)
+                       ev->data.dom.from = webkit_dom_node_clone_node_with_error (
+                               WEBKIT_DOM_NODE (table), TRUE, NULL);
+
+               e_editor_undo_redo_manager_insert_history_event (manager, ev);
+       }
+}
+
+void
+e_dialogs_dom_cell_save_history_on_exit (EEditorPage *editor_page)
+{
+       EEditorUndoRedoManager *manager;
+       EEditorHistoryEvent *ev = NULL;
+       WebKitDOMElement *cell, *table;
+       WebKitDOMDocument *document;
+
+       g_return_if_fail (E_IS_EDITOR_PAGE (editor_page));
+
+       document = e_editor_page_get_document (editor_page);
+
+       cell = get_current_cell_element (document);
+
+       table = dom_node_find_parent_element (WEBKIT_DOM_NODE (cell), "TABLE");
+       g_return_if_fail (table != NULL);
+
+       webkit_dom_element_remove_attribute (cell, "id");
+
+       manager = e_editor_page_get_undo_redo_manager (editor_page);
+       ev = e_editor_undo_redo_manager_get_current_history_event (manager);
+       ev->data.dom.to = webkit_dom_node_clone_node_with_error (
+               WEBKIT_DOM_NODE (table), TRUE, NULL);
+
+       if (ev->data.dom.from && webkit_dom_node_is_equal_node (ev->data.dom.from, ev->data.dom.to))
+               e_editor_undo_redo_manager_remove_current_history_event (manager);
+       else
+               e_editor_dom_selection_get_coordinates (editor_page, &ev->after.start.x, &ev->after.start.y, 
&ev->after.end.x, &ev->after.end.y);
+}
+
+void
+e_dialogs_dom_cell_set_element_v_align (EEditorPage *editor_page,
+                                       const gchar *v_align,
+                                       EContentEditorScope scope)
+{
+       GValue val = { 0 };
+
+       g_return_if_fail (E_IS_EDITOR_PAGE (editor_page));
+
+       g_value_init (&val, G_TYPE_STRING);
+       g_value_set_string (&val, v_align);
+
+       cell_dialog_set_attribute (e_editor_page_get_document (editor_page),
+               scope, webkit_dom_html_table_cell_element_set_v_align, &val, NULL);
+
+       g_value_unset (&val);
+}
+
+void
+e_dialogs_dom_cell_set_element_align (EEditorPage *editor_page,
+                                     const gchar *align,
+                                     EContentEditorScope scope)
+{
+       GValue val = { 0 };
+
+       g_return_if_fail (E_IS_EDITOR_PAGE (editor_page));
+
+       g_value_init (&val, G_TYPE_STRING);
+       g_value_set_string (&val, align);
+
+       cell_dialog_set_attribute (e_editor_page_get_document (editor_page),
+               scope, webkit_dom_html_table_cell_element_set_align, &val, NULL);
+
+       g_value_unset (&val);
+}
+
+void
+e_dialogs_dom_cell_set_element_no_wrap (EEditorPage *editor_page,
+                                       gboolean wrap_text,
+                                       EContentEditorScope scope)
+{
+       GValue val = { 0 };
+
+       g_return_if_fail (E_IS_EDITOR_PAGE (editor_page));
+
+       g_value_init (&val, G_TYPE_BOOLEAN);
+       g_value_set_boolean (&val, wrap_text);
+
+       cell_dialog_set_attribute (e_editor_page_get_document (editor_page),
+               scope, webkit_dom_html_table_cell_element_set_no_wrap, &val, NULL);
+}
+
+void
+e_dialogs_dom_cell_set_element_header_style (EEditorPage *editor_page,
+                                            gboolean header_style,
+                                            EContentEditorScope scope)
+{
+       GValue val = { 0 };
+
+       g_return_if_fail (E_IS_EDITOR_PAGE (editor_page));
+
+       g_value_init (&val, G_TYPE_BOOLEAN);
+       g_value_set_boolean (&val, header_style);
+
+       cell_dialog_set_attribute (e_editor_page_get_document (editor_page),
+               scope, cell_set_header_style, &val, NULL);
+}
+
+void
+e_dialogs_dom_cell_set_element_width (EEditorPage *editor_page,
+                                     const gchar *width,
+                                     EContentEditorScope scope)
+{
+       GValue val = { 0 };
+
+       g_return_if_fail (E_IS_EDITOR_PAGE (editor_page));
+
+       g_value_init (&val, G_TYPE_STRING);
+       g_value_set_string (&val, width);
+
+       cell_dialog_set_attribute (e_editor_page_get_document (editor_page),
+               scope, webkit_dom_html_table_cell_element_set_width, &val, NULL);
+
+       g_value_unset (&val);
+}
+
+void
+e_dialogs_dom_cell_set_element_col_span (EEditorPage *editor_page,
+                                        glong span,
+                                        EContentEditorScope scope)
+{
+       GValue val = { 0 };
+
+       g_return_if_fail (E_IS_EDITOR_PAGE (editor_page));
+
+       g_value_init (&val, G_TYPE_LONG);
+       g_value_set_long (&val, span);
+
+       cell_dialog_set_attribute (e_editor_page_get_document (editor_page),
+               scope, webkit_dom_html_table_cell_element_set_col_span, &val, NULL);
+}
+
+void
+e_dialogs_dom_cell_set_element_row_span (EEditorPage *editor_page,
+                                        glong span,
+                                        EContentEditorScope scope)
+{
+       GValue val = { 0 };
+
+       g_return_if_fail (E_IS_EDITOR_PAGE (editor_page));
+
+       g_value_init (&val, G_TYPE_LONG);
+       g_value_set_long (&val, span);
+
+       cell_dialog_set_attribute (e_editor_page_get_document (editor_page),
+               scope, webkit_dom_html_table_cell_element_set_row_span, &val, NULL);
+}
+
+void
+e_dialogs_dom_cell_set_element_bg_color (EEditorPage *editor_page,
+                                        const gchar *color,
+                                        EContentEditorScope scope)
+{
+       GValue val = { 0 };
+
+       g_return_if_fail (E_IS_EDITOR_PAGE (editor_page));
+
+       g_value_init (&val, G_TYPE_STRING);
+       g_value_set_string (&val, color);
+
+       cell_dialog_set_attribute (e_editor_page_get_document (editor_page),
+               scope, webkit_dom_html_table_cell_element_set_bg_color, &val, NULL);
+}
+
+/* ******************** HRule Dialog ***************** */
+
+static WebKitDOMElement *
+get_current_hrule_element (WebKitDOMDocument *document)
+{
+       return webkit_dom_document_get_element_by_id (document, "-x-evo-current-hr");
+}
+
+gboolean
+e_dialogs_dom_h_rule_find_hrule (EEditorPage *editor_page)
+{
+       EEditorUndoRedoManager *manager;
+       gboolean created = FALSE;
+       WebKitDOMDocument *document;
+       WebKitDOMElement *rule;
+       WebKitDOMNode *node_under_mouse_click;
+
+       g_return_val_if_fail (E_IS_EDITOR_PAGE (editor_page), FALSE);
+
+       document = e_editor_page_get_document (editor_page);
+
+       node_under_mouse_click = e_editor_page_get_node_under_mouse_click (editor_page);
+
+       if (node_under_mouse_click && WEBKIT_DOM_IS_HTML_HR_ELEMENT (node_under_mouse_click)) {
+               rule = WEBKIT_DOM_ELEMENT (node_under_mouse_click);
+               webkit_dom_element_set_id (rule, "-x-evo-current-hr");
+       } else {
+               WebKitDOMElement *selection_start, *parent;
+
+               e_editor_dom_selection_save (editor_page);
+
+               selection_start = webkit_dom_document_get_element_by_id (
+                       document, "-x-evo-selection-start-marker");
+               parent = get_parent_block_element (WEBKIT_DOM_NODE (selection_start));
+
+               rule = webkit_dom_document_create_element (document, "HR", NULL);
+               webkit_dom_element_set_id (rule, "-x-evo-current-hr");
+
+               /* Insert horizontal rule into body below the caret */
+               webkit_dom_node_insert_before (
+                       webkit_dom_node_get_parent_node (WEBKIT_DOM_NODE (parent)),
+                       WEBKIT_DOM_NODE (rule),
+                       webkit_dom_node_get_next_sibling (WEBKIT_DOM_NODE (parent)),
+                       NULL);
+
+               e_editor_dom_selection_restore (editor_page);
+
+               e_editor_page_emit_content_changed (editor_page);
+
+               created = TRUE;
+       }
+
+       manager = e_editor_page_get_undo_redo_manager (editor_page);
+       if (!e_editor_undo_redo_manager_is_operation_in_progress (manager)) {
+               EEditorHistoryEvent *ev;
+
+               ev = g_new0 (EEditorHistoryEvent, 1);
+               ev->type = HISTORY_HRULE_DIALOG;
+
+               e_editor_dom_selection_get_coordinates (editor_page, &ev->before.start.x, 
&ev->before.start.y, &ev->before.end.x, &ev->before.end.y);
+               if (!created)
+                       ev->data.dom.from = webkit_dom_node_clone_node_with_error (
+                               WEBKIT_DOM_NODE (rule), FALSE, NULL);
+               else
+                       ev->data.dom.from = NULL;
+
+               e_editor_undo_redo_manager_insert_history_event (manager, ev);
+       }
+
+       return created;
+}
+
+void
+e_dialogs_dom_h_rule_dialog_on_close (EEditorPage *editor_page)
+{
+       EEditorUndoRedoManager *manager;
+       EEditorHistoryEvent *ev = NULL;
+       WebKitDOMDocument *document;
+       WebKitDOMElement *element;
+
+       g_return_if_fail (E_IS_EDITOR_PAGE (editor_page));
+
+       document = e_editor_page_get_document (editor_page);
+       element = get_current_hrule_element (document);
+       g_return_if_fail (element != NULL);
+
+       webkit_dom_element_remove_attribute (element, "id");
+
+       manager = e_editor_page_get_undo_redo_manager (editor_page);
+       ev = e_editor_undo_redo_manager_get_current_history_event (manager);
+
+       ev->data.dom.to = webkit_dom_node_clone_node_with_error (
+               WEBKIT_DOM_NODE (element), TRUE, NULL);
+
+       if (ev->data.dom.from && webkit_dom_node_is_equal_node (ev->data.dom.from, ev->data.dom.to))
+               e_editor_undo_redo_manager_remove_current_history_event (manager);
+       else
+               e_editor_dom_selection_get_coordinates (editor_page, &ev->after.start.x, &ev->after.start.y, 
&ev->after.end.x, &ev->after.end.y);
+}
+
+/* ******************** Image Dialog ***************** */
+
+static WebKitDOMElement *
+get_current_image_element (WebKitDOMDocument *document)
+{
+       return webkit_dom_document_get_element_by_id (document, "-x-evo-current-img");
+}
+
+void
+e_dialogs_dom_image_mark_image (EEditorPage *editor_page)
+{
+       EEditorUndoRedoManager *manager;
+       WebKitDOMNode *node_under_mouse_click;
+
+       g_return_if_fail (E_IS_EDITOR_PAGE (editor_page));
+
+       node_under_mouse_click = e_editor_page_get_node_under_mouse_click (editor_page);
+
+       g_return_if_fail (node_under_mouse_click && WEBKIT_DOM_IS_HTML_IMAGE_ELEMENT 
(node_under_mouse_click));
+
+       webkit_dom_element_set_id (WEBKIT_DOM_ELEMENT (node_under_mouse_click), "-x-evo-current-img");
+
+       manager = e_editor_page_get_undo_redo_manager (editor_page);
+       if (!e_editor_undo_redo_manager_is_operation_in_progress (manager)) {
+               EEditorHistoryEvent *ev;
+
+               ev = g_new0 (EEditorHistoryEvent, 1);
+               ev->type = HISTORY_IMAGE_DIALOG;
+
+               e_editor_dom_selection_get_coordinates (editor_page, &ev->before.start.x, 
&ev->before.start.y, &ev->before.end.x, &ev->before.end.y);
+               ev->data.dom.from = webkit_dom_node_clone_node_with_error (node_under_mouse_click, FALSE, 
NULL);
+
+               e_editor_undo_redo_manager_insert_history_event (manager, ev);
+       }
+}
+
+void
+e_dialogs_dom_image_save_history_on_exit (EEditorPage *editor_page)
+{
+       EEditorUndoRedoManager *manager;
+       EEditorHistoryEvent *ev = NULL;
+       WebKitDOMDocument *document;
+       WebKitDOMElement *element;
+
+       g_return_if_fail (E_IS_EDITOR_PAGE (editor_page));
+
+       document = e_editor_page_get_document (editor_page);
+       element = get_current_image_element (document);
+       g_return_if_fail (element != NULL);
+
+       webkit_dom_element_remove_attribute (element, "id");
+
+       manager = e_editor_page_get_undo_redo_manager (editor_page);
+       ev = e_editor_undo_redo_manager_get_current_history_event (manager);
+       ev->data.dom.to = webkit_dom_node_clone_node_with_error (
+               WEBKIT_DOM_NODE (element), TRUE, NULL);
+
+       if (ev->data.dom.from && webkit_dom_node_is_equal_node (ev->data.dom.from, ev->data.dom.to))
+               e_editor_undo_redo_manager_remove_current_history_event (manager);
+       else
+               e_editor_dom_selection_get_coordinates (editor_page, &ev->after.start.x, &ev->after.start.y, 
&ev->after.end.x, &ev->after.end.y);
+}
+
+void
+e_dialogs_dom_image_set_element_url (EEditorPage *editor_page,
+                                    const gchar *url)
+{
+       WebKitDOMElement *image, *link;
+
+       g_return_if_fail (E_IS_EDITOR_PAGE (editor_page));
+
+       image = get_current_image_element (e_editor_page_get_document (editor_page));
+       link = dom_node_find_parent_element (WEBKIT_DOM_NODE (image), "A");
+
+       if (link) {
+               if (!url || !*url) {
+                       webkit_dom_node_insert_before (
+                               webkit_dom_node_get_parent_node (
+                                       WEBKIT_DOM_NODE (link)),
+                               WEBKIT_DOM_NODE (image),
+                               WEBKIT_DOM_NODE (link), NULL);
+                       webkit_dom_node_remove_child (
+                               webkit_dom_node_get_parent_node (
+                                       WEBKIT_DOM_NODE (link)),
+                               WEBKIT_DOM_NODE (link), NULL);
+               } else {
+                       webkit_dom_html_anchor_element_set_href (
+                               WEBKIT_DOM_HTML_ANCHOR_ELEMENT (link), url);
+               }
+       } else {
+               if (url && *url) {
+                       WebKitDOMDocument *document;
+
+                       document = webkit_dom_node_get_owner_document (
+                                       WEBKIT_DOM_NODE (image));
+                       link = webkit_dom_document_create_element (
+                                       document, "A", NULL);
+
+                       webkit_dom_html_anchor_element_set_href (
+                               WEBKIT_DOM_HTML_ANCHOR_ELEMENT (link), url);
+
+                       webkit_dom_node_insert_before (
+                               webkit_dom_node_get_parent_node (
+                                       WEBKIT_DOM_NODE (image)),
+                               WEBKIT_DOM_NODE (link),
+                               WEBKIT_DOM_NODE (image), NULL);
+
+                       webkit_dom_node_append_child (
+                               WEBKIT_DOM_NODE (link),
+                               WEBKIT_DOM_NODE (image), NULL);
+               }
+       }
+}
+
+gchar *
+e_dialogs_dom_image_get_element_url (EEditorPage *editor_page)
+{
+       gchar *value = NULL;
+       WebKitDOMElement *image, *link;
+
+       g_return_val_if_fail (E_IS_EDITOR_PAGE (editor_page), NULL);
+
+       image = get_current_image_element (e_editor_page_get_document (editor_page));
+       link = dom_node_find_parent_element (WEBKIT_DOM_NODE (image), "A");
+
+       if (link)
+               value = webkit_dom_element_get_attribute (link, "href");
+
+       return value;
+}
+
+/* ******************** Link Dialog ***************** */
+
+void
+e_dialogs_dom_link_commit (EEditorPage *editor_page,
+                          const gchar *url,
+                          const gchar *inner_text)
+{
+       WebKitDOMDocument *document;
+       WebKitDOMElement *link;
+
+       g_return_if_fail (E_IS_EDITOR_PAGE (editor_page));
+
+       document = e_editor_page_get_document (editor_page);
+       link = webkit_dom_document_get_element_by_id (document, "-x-evo-current-anchor");
+
+       if (link) {
+               WebKitDOMElement *element;
+
+               webkit_dom_html_anchor_element_set_href (
+                       WEBKIT_DOM_HTML_ANCHOR_ELEMENT (link), url);
+               webkit_dom_html_element_set_inner_text (
+                       WEBKIT_DOM_HTML_ELEMENT (link), inner_text, NULL);
+
+               element = webkit_dom_document_create_element (document, "SPAN", NULL);
+               webkit_dom_element_set_id (element, "-x-evo-selection-end-marker");
+               webkit_dom_node_insert_before (
+                       webkit_dom_node_get_parent_node (WEBKIT_DOM_NODE (link)),
+                       WEBKIT_DOM_NODE (element),
+                       webkit_dom_node_get_next_sibling (WEBKIT_DOM_NODE (link)),
+                       NULL);
+
+               element = webkit_dom_document_create_element (document, "SPAN", NULL);
+               webkit_dom_element_set_id (element, "-x-evo-selection-start-marker");
+               webkit_dom_node_insert_before (
+                       webkit_dom_node_get_parent_node (WEBKIT_DOM_NODE (link)),
+                       WEBKIT_DOM_NODE (element),
+                       webkit_dom_node_get_next_sibling (WEBKIT_DOM_NODE (link)),
+                       NULL);
+
+               e_editor_dom_selection_restore (editor_page);
+       } else {
+               WebKitDOMDOMWindow *dom_window = NULL;
+               WebKitDOMDOMSelection *dom_selection = NULL;
+               WebKitDOMRange *range = NULL;
+
+               dom_window = webkit_dom_document_get_default_view (document);
+               dom_selection = webkit_dom_dom_window_get_selection (dom_window);
+               g_clear_object (&dom_window);
+
+               e_editor_dom_selection_restore (editor_page);
+               range = webkit_dom_dom_selection_get_range_at (dom_selection, 0, NULL);
+               if (webkit_dom_range_get_collapsed (range, NULL)) {
+                       WebKitDOMElement *selection_marker;
+                       WebKitDOMElement *anchor;
+
+                       e_editor_dom_selection_save (editor_page);
+                       selection_marker = webkit_dom_document_get_element_by_id (
+                               document, "-x-evo-selection-start-marker");
+                       anchor = webkit_dom_document_create_element (document, "A", NULL);
+                       webkit_dom_element_set_attribute (anchor, "href", url, NULL);
+                       webkit_dom_element_set_id (anchor, "-x-evo-current-anchor");
+                       webkit_dom_html_element_set_inner_text (
+                               WEBKIT_DOM_HTML_ELEMENT (anchor), inner_text, NULL);
+
+                       webkit_dom_node_insert_before (
+                               webkit_dom_node_get_parent_node (
+                                       WEBKIT_DOM_NODE (selection_marker)),
+                               WEBKIT_DOM_NODE (anchor),
+                               WEBKIT_DOM_NODE (selection_marker),
+                               NULL);
+                       e_editor_dom_selection_restore (editor_page);
+               } else {
+                       gchar *text;
+
+                       text = webkit_dom_range_get_text (range);
+                       if (text && *text) {
+                               EEditorUndoRedoManager *manager;
+                               EEditorHistoryEvent *ev;
+
+                               e_editor_dom_create_link (editor_page, url);
+
+                               manager = e_editor_page_get_undo_redo_manager (editor_page);
+                               ev = e_editor_undo_redo_manager_get_current_history_event (manager);
+
+                               ev->data.dom.from =
+                                       WEBKIT_DOM_NODE (webkit_dom_document_create_text_node (document, 
text));
+
+                               webkit_dom_dom_selection_collapse_to_end (dom_selection, NULL);
+                       }
+                       g_free (text);
+               }
+
+               g_clear_object (&range);
+               g_clear_object (&dom_selection);
+       }
+}
+
+void
+e_dialogs_dom_link_dialog_on_close (EEditorPage *editor_page)
+{
+       WebKitDOMDocument *document;
+       WebKitDOMElement *link;
+
+       g_return_if_fail (E_IS_EDITOR_PAGE (editor_page));
+
+       document = e_editor_page_get_document (editor_page);
+
+       link = webkit_dom_document_get_element_by_id (document, "-x-evo-current-anchor");
+       if (link) {
+               EEditorUndoRedoManager *manager;
+               EEditorHistoryEvent *ev;
+
+               manager = e_editor_page_get_undo_redo_manager (editor_page);
+               ev = e_editor_undo_redo_manager_get_current_history_event (manager);
+               if (ev->type == HISTORY_LINK_DIALOG) {
+                       ev->data.dom.to = webkit_dom_node_clone_node_with_error (
+                               WEBKIT_DOM_NODE (link), TRUE, NULL);
+
+                       if (ev->data.dom.from && webkit_dom_node_is_equal_node (ev->data.dom.from, 
ev->data.dom.to))
+                               e_editor_undo_redo_manager_remove_current_history_event (manager);
+                       else
+                               e_editor_dom_selection_get_coordinates (
+                                       editor_page, &ev->after.start.x, &ev->after.start.y, 
&ev->after.end.x, &ev->after.end.y);
+               }
+               webkit_dom_element_remove_attribute (link, "id");
+       }
+}
+
+void
+e_dialogs_dom_link_dialog_on_open (EEditorPage *editor_page)
+{
+       EEditorUndoRedoManager *manager;
+       WebKitDOMDocument *document;
+       WebKitDOMElement *link = NULL;
+       WebKitDOMNode *node_under_mouse_click;
+
+       g_return_if_fail (E_IS_EDITOR_PAGE (editor_page));
+
+       document = e_editor_page_get_document (editor_page);
+
+       node_under_mouse_click = e_editor_page_get_node_under_mouse_click (editor_page);
+       if (node_under_mouse_click && WEBKIT_DOM_IS_HTML_ANCHOR_ELEMENT (node_under_mouse_click)) {
+               link = WEBKIT_DOM_ELEMENT (node_under_mouse_click);
+       } else {
+               if (!(link = webkit_dom_document_get_element_by_id (document, "-x-evo-current-anchor"))) {
+                       if (node_under_mouse_click) {
+                               link = dom_node_find_parent_element (node_under_mouse_click, "A");
+                       } else {
+                               WebKitDOMElement *selection_start;
+
+                               e_editor_dom_selection_save (editor_page);
+
+                               selection_start = webkit_dom_document_get_element_by_id (
+                                       document, "-x-evo-selection-start-marker");
+
+                               link = dom_node_find_parent_element (WEBKIT_DOM_NODE (selection_start), "A");
+
+                               e_editor_dom_selection_restore (editor_page);
+                       }
+               }
+       }
+
+       if (link)
+               webkit_dom_element_set_id (link, "-x-evo-current-anchor");
+
+       manager = e_editor_page_get_undo_redo_manager (editor_page);
+       if (!e_editor_undo_redo_manager_is_operation_in_progress (manager)) {
+               EEditorHistoryEvent *ev;
+
+               ev = g_new0 (EEditorHistoryEvent, 1);
+               ev->type = HISTORY_LINK_DIALOG;
+
+               e_editor_dom_selection_get_coordinates (
+                       editor_page, &ev->before.start.x, &ev->before.start.y, &ev->before.end.x, 
&ev->before.end.y);
+               if (link)
+                       ev->data.dom.from = webkit_dom_node_clone_node_with_error (
+                               WEBKIT_DOM_NODE (link), TRUE, NULL);
+               else
+                       ev->data.dom.from = NULL;
+               e_editor_undo_redo_manager_insert_history_event (manager, ev);
+       }
+}
+
+GVariant *
+e_dialogs_dom_link_show (EEditorPage *editor_page)
+{
+       GVariant *result = NULL;
+       WebKitDOMDocument *document;
+       WebKitDOMElement *link;
+
+       g_return_val_if_fail (E_IS_EDITOR_PAGE (editor_page), NULL);
+
+       document = e_editor_page_get_document (editor_page);
+
+       e_editor_dom_selection_save (editor_page);
+
+       link = webkit_dom_document_get_element_by_id (document, "-x-evo-current-anchor");
+       if (link) {
+               gchar *href, *text;
+
+               href = webkit_dom_element_get_attribute (link, "href");
+               text = webkit_dom_html_element_get_inner_text (
+                       WEBKIT_DOM_HTML_ELEMENT (link));
+
+               result = g_variant_new ("(ss)", href, text);
+
+               g_free (text);
+               g_free (href);
+       } else {
+               gchar *text;
+               WebKitDOMDOMWindow *dom_window = NULL;
+               WebKitDOMDOMSelection *dom_selection = NULL;
+               WebKitDOMRange *range = NULL;
+
+               dom_window = webkit_dom_document_get_default_view (document);
+               dom_selection = webkit_dom_dom_window_get_selection (dom_window);
+               g_clear_object (&dom_window);
+
+               /* No selection at all */
+               if (!dom_selection || webkit_dom_dom_selection_get_range_count (dom_selection) < 1)
+                       result = g_variant_new ("(ss)", "", "");
+
+               range = webkit_dom_dom_selection_get_range_at (dom_selection, 0, NULL);
+               text = webkit_dom_range_get_text (range);
+               if (text)
+                       result = g_variant_new ("(ss)", "", text);
+
+               g_free (text);
+
+               g_clear_object (&range);
+               g_clear_object (&dom_selection);
+       }
+
+       return result;
+}
+
+/* ******************** Page Dialog ***************** */
+
+void
+e_dialogs_dom_page_save_history (EEditorPage *editor_page)
+{
+       EEditorUndoRedoManager *manager;
+       WebKitDOMDocument *document;
+
+       g_return_if_fail (E_IS_EDITOR_PAGE (editor_page));
+
+       document = e_editor_page_get_document (editor_page);
+
+       manager = e_editor_page_get_undo_redo_manager (editor_page);
+       if (!e_editor_undo_redo_manager_is_operation_in_progress (manager)) {
+               EEditorHistoryEvent *ev;
+               WebKitDOMHTMLElement *body;
+
+               ev = g_new0 (EEditorHistoryEvent, 1);
+               ev->type = HISTORY_PAGE_DIALOG;
+
+               e_editor_dom_selection_get_coordinates (editor_page, &ev->before.start.x, 
&ev->before.start.y, &ev->before.end.x, &ev->before.end.y);
+               body = webkit_dom_document_get_body (document);
+               ev->data.dom.from = webkit_dom_node_clone_node_with_error (WEBKIT_DOM_NODE (body), FALSE, 
NULL);
+
+               e_editor_undo_redo_manager_insert_history_event (manager, ev);
+       }
+}
+
+void
+e_dialogs_dom_page_save_history_on_exit (EEditorPage *editor_page)
+{
+       EEditorHistoryEvent *ev = NULL;
+       EEditorUndoRedoManager *manager;
+       WebKitDOMDocument *document;
+       WebKitDOMHTMLElement *body;
+
+       g_return_if_fail (E_IS_EDITOR_PAGE (editor_page));
+
+       document = e_editor_page_get_document (editor_page);
+
+       manager = e_editor_page_get_undo_redo_manager (editor_page);
+       ev = e_editor_undo_redo_manager_get_current_history_event (manager);
+       body = webkit_dom_document_get_body (document);
+       ev->data.dom.to = webkit_dom_node_clone_node_with_error (WEBKIT_DOM_NODE (body), FALSE, NULL);
+
+       if (!webkit_dom_node_is_equal_node (ev->data.dom.from, ev->data.dom.to)) {
+               e_editor_dom_selection_get_coordinates (editor_page, &ev->after.start.x, &ev->after.start.y, 
&ev->after.end.x, &ev->after.end.y);
+       } else {
+               e_editor_undo_redo_manager_remove_current_history_event (manager);
+       }
+}
+
+/* ******************** Spell Check Dialog ***************** */
+
+static gboolean
+select_next_word (WebKitDOMDOMSelection *dom_selection)
+{
+       gulong anchor_offset, focus_offset;
+       WebKitDOMNode *anchor, *focus;
+
+       anchor = webkit_dom_dom_selection_get_anchor_node (dom_selection);
+       anchor_offset = webkit_dom_dom_selection_get_anchor_offset (dom_selection);
+
+       focus = webkit_dom_dom_selection_get_focus_node (dom_selection);
+       focus_offset = webkit_dom_dom_selection_get_focus_offset (dom_selection);
+
+       /* Jump _behind_ next word */
+       webkit_dom_dom_selection_modify (dom_selection, "move", "forward", "word");
+       /* Jump before the word */
+       webkit_dom_dom_selection_modify (dom_selection, "move", "backward", "word");
+       /* Select it */
+       webkit_dom_dom_selection_modify (dom_selection, "extend", "forward", "word");
+
+       /* If the selection didn't change, then we have most probably
+        * reached the end of document - return FALSE */
+       return !((anchor == webkit_dom_dom_selection_get_anchor_node (dom_selection)) &&
+                (anchor_offset == webkit_dom_dom_selection_get_anchor_offset (dom_selection)) &&
+                (focus == webkit_dom_dom_selection_get_focus_node (dom_selection)) &&
+                (focus_offset == webkit_dom_dom_selection_get_focus_offset (dom_selection)));
+}
+
+static gboolean
+select_previous_word (WebKitDOMDOMSelection *dom_selection)
+{
+       WebKitDOMNode *old_anchor_node;
+       WebKitDOMNode *new_anchor_node;
+       gulong old_anchor_offset;
+       gulong new_anchor_offset;
+
+       old_anchor_node = webkit_dom_dom_selection_get_anchor_node (dom_selection);
+       old_anchor_offset = webkit_dom_dom_selection_get_anchor_offset (dom_selection);
+
+       /* Jump on the beginning of current word */
+       webkit_dom_dom_selection_modify (dom_selection, "move", "backward", "word");
+       /* Jump before previous word */
+       webkit_dom_dom_selection_modify (dom_selection, "move", "backward", "word");
+       /* Select it */
+       webkit_dom_dom_selection_modify (dom_selection, "extend", "forward", "word");
+
+       /* If the selection start didn't change, then we have most probably
+        * reached the beginnig of document. Return FALSE */
+
+       new_anchor_node = webkit_dom_dom_selection_get_anchor_node (dom_selection);
+       new_anchor_offset = webkit_dom_dom_selection_get_anchor_offset (dom_selection);
+
+       return (new_anchor_node != old_anchor_node) ||
+               (new_anchor_offset != old_anchor_offset);
+}
+
+static gchar *
+e_dialogs_dom_spell_check_run (EEditorPage *editor_page,
+                              gboolean run_next,
+                              const gchar *from_word,
+                              const gchar * const *languages)
+{
+       gulong start_offset = 0, end_offset = 0;
+       WebKitDOMDocument *document;
+       WebKitDOMDOMSelection *dom_selection = NULL;
+       WebKitDOMDOMWindow *dom_window = NULL;
+       WebKitDOMNode *start = NULL, *end = NULL;
+
+       g_return_val_if_fail (E_IS_EDITOR_PAGE (editor_page), NULL);
+
+       document = e_editor_page_get_document (editor_page);
+       dom_window = webkit_dom_document_get_default_view (document);
+       dom_selection = webkit_dom_dom_window_get_selection (dom_window);
+       g_clear_object (&dom_window);
+
+       if (!from_word || !*from_word) {
+               if (run_next) {
+                       webkit_dom_dom_selection_modify (
+                               dom_selection, "move", "left", "documentboundary");
+               } else {
+                       webkit_dom_dom_selection_modify (
+                               dom_selection, "move", "right", "documentboundary");
+                       webkit_dom_dom_selection_modify (
+                               dom_selection, "extend", "backward", "word");
+               }
+       } else {
+               /* Remember last selected word */
+               start = webkit_dom_dom_selection_get_anchor_node (dom_selection);
+               end = webkit_dom_dom_selection_get_focus_node (dom_selection);
+               start_offset = webkit_dom_dom_selection_get_anchor_offset (dom_selection);
+               end_offset = webkit_dom_dom_selection_get_focus_offset (dom_selection);
+       }
+
+       while ((run_next ? select_next_word (dom_selection) : select_previous_word (dom_selection))) {
+               WebKitDOMRange *range = NULL;
+               gchar *word;
+
+               range = webkit_dom_dom_selection_get_range_at (dom_selection, 0, NULL);
+               word = webkit_dom_range_get_text (range);
+               g_clear_object (&range);
+
+               if (!e_editor_page_check_word_spelling (editor_page, word, languages)) {
+                       /* Found misspelled word! */
+                       return word;
+               }
+
+               g_free (word);
+       }
+
+       /* Restore the selection to contain the last misspelled word. This is
+        * reached only when we reach the beginning/end of the document */
+       if (start && end)
+               webkit_dom_dom_selection_set_base_and_extent (
+                       dom_selection, start, start_offset, end, end_offset, NULL);
+
+       g_clear_object (&dom_selection);
+
+       return NULL;
+}
+
+gchar *
+e_dialogs_dom_spell_check_next (EEditorPage *editor_page,
+                               const gchar *from_word,
+                               const gchar * const *languages)
+{
+       g_return_val_if_fail (E_IS_EDITOR_PAGE (editor_page), NULL);
+
+       return e_dialogs_dom_spell_check_run (editor_page, TRUE, from_word, languages);
+}
+
+gchar *
+e_dialogs_dom_spell_check_prev (EEditorPage *editor_page,
+                               const gchar *from_word,
+                               const gchar * const *languages)
+{
+       g_return_val_if_fail (E_IS_EDITOR_PAGE (editor_page), NULL);
+
+       return e_dialogs_dom_spell_check_run (editor_page, FALSE, from_word, languages);
+}
+
+/* ******************** Table Dialog ***************** */
+
+static WebKitDOMHTMLTableElement *
+get_current_table_element (WebKitDOMDocument *document)
+{
+       return WEBKIT_DOM_HTML_TABLE_ELEMENT (webkit_dom_document_get_element_by_id (document, 
"-x-evo-current-table"));
+}
+
+void
+e_dialogs_dom_table_set_row_count (EEditorPage *editor_page,
+                                  gulong expected_count)
+{
+       WebKitDOMDocument *document;
+       WebKitDOMHTMLCollection *rows = NULL, *cells = NULL;
+       WebKitDOMHTMLTableElement *table_element;
+       WebKitDOMHTMLTableRowElement *row;
+       gulong ii, rows_current_count, cells_current_count;
+
+       g_return_if_fail (E_IS_EDITOR_PAGE (editor_page));
+
+       document = e_editor_page_get_document (editor_page);
+
+       table_element = get_current_table_element (document);
+       if (!table_element)
+               return;
+
+       rows = webkit_dom_html_table_element_get_rows (table_element);
+       rows_current_count = webkit_dom_html_collection_get_length (rows);
+
+       if (rows_current_count < 1) {
+               g_clear_object (&rows);
+               return;
+       }
+
+       row = WEBKIT_DOM_HTML_TABLE_ROW_ELEMENT (webkit_dom_html_collection_item (rows, 0));
+       cells = webkit_dom_html_table_row_element_get_cells (row);
+       cells_current_count = webkit_dom_html_collection_get_length (cells);
+       g_object_unref (row);
+
+       if (rows_current_count < expected_count) {
+               for (ii = 0; ii < expected_count - rows_current_count; ii++) {
+                       WebKitDOMHTMLElement *new_row;
+                       gulong jj;
+
+                       new_row = webkit_dom_html_table_element_insert_row (
+                               table_element, -1, NULL);
+
+                       for (jj = 0; jj < cells_current_count; jj++)
+                               webkit_dom_html_table_row_element_insert_cell (
+                                       WEBKIT_DOM_HTML_TABLE_ROW_ELEMENT (new_row), -1, NULL);
+               }
+       } else if (rows_current_count > expected_count) {
+               for (ii = 0; ii < rows_current_count - expected_count; ii++) {
+                       webkit_dom_html_table_element_delete_row (
+                               table_element, -1, NULL);
+               }
+       }
+       g_clear_object (&cells);
+       g_clear_object (&rows);
+}
+
+gulong
+e_dialogs_dom_table_get_row_count (EEditorPage *editor_page)
+{
+       WebKitDOMDocument *document;
+       WebKitDOMHTMLTableElement *table_element;
+       WebKitDOMHTMLCollection *rows = NULL;
+       glong count;
+
+       g_return_val_if_fail (E_IS_EDITOR_PAGE (editor_page), 0);
+
+       document = e_editor_page_get_document (editor_page);
+
+       table_element = get_current_table_element (document);
+       if (!table_element)
+               return 0;
+
+       rows = webkit_dom_html_table_element_get_rows (table_element);
+
+       count = webkit_dom_html_collection_get_length (rows);
+       g_clear_object (&rows);
+
+       return count;
+}
+
+void
+e_dialogs_dom_table_set_column_count (EEditorPage *editor_page,
+                                     gulong expected_columns)
+{
+       WebKitDOMDocument *document;
+       WebKitDOMHTMLTableElement *table_element;
+       WebKitDOMHTMLCollection *rows = NULL;
+       gulong ii, row_count;
+
+       g_return_if_fail (E_IS_EDITOR_PAGE (editor_page));
+
+       document = e_editor_page_get_document (editor_page);
+
+       table_element = get_current_table_element (document);
+       if (!table_element)
+               return;
+
+       rows = webkit_dom_html_table_element_get_rows (table_element);
+       row_count = webkit_dom_html_collection_get_length (rows);
+
+       for (ii = 0; ii < row_count; ii++) {
+               WebKitDOMHTMLTableRowElement *row;
+               WebKitDOMHTMLCollection *cells = NULL;
+               gulong jj, current_columns;
+
+               row = WEBKIT_DOM_HTML_TABLE_ROW_ELEMENT (
+                       webkit_dom_html_collection_item (rows, ii));
+
+               cells = webkit_dom_html_table_row_element_get_cells (row);
+               current_columns = webkit_dom_html_collection_get_length (cells);
+
+               if (current_columns < expected_columns) {
+                       for (jj = 0; jj < expected_columns - current_columns; jj++) {
+                               webkit_dom_html_table_row_element_insert_cell (
+                                       row, -1, NULL);
+                       }
+               } else if (expected_columns < current_columns) {
+                       for (jj = 0; jj < current_columns - expected_columns; jj++) {
+                               webkit_dom_html_table_row_element_delete_cell (
+                                       row, -1, NULL);
+                       }
+               }
+               g_object_unref (row);
+               g_clear_object (&cells);
+       }
+       g_clear_object (&rows);
+}
+
+gulong
+e_dialogs_dom_table_get_column_count (EEditorPage *editor_page)
+{
+       WebKitDOMDocument *document;
+       WebKitDOMHTMLTableElement *table_element;
+       WebKitDOMHTMLCollection *rows = NULL, *columns = NULL;
+       WebKitDOMNode *row;
+       glong count;
+
+       g_return_val_if_fail (E_IS_EDITOR_PAGE (editor_page), 0);
+
+       document = e_editor_page_get_document (editor_page);
+
+       table_element = get_current_table_element (document);
+       if (!table_element)
+               return 0;
+
+       rows = webkit_dom_html_table_element_get_rows (table_element);
+       row = webkit_dom_html_collection_item (rows, 0);
+
+       columns = webkit_dom_html_table_row_element_get_cells (
+               WEBKIT_DOM_HTML_TABLE_ROW_ELEMENT (row));
+
+       count = webkit_dom_html_collection_get_length (columns);
+
+       g_object_unref (row);
+       g_clear_object (&rows);
+       g_clear_object (&columns);
+
+       return count;
+}
+
+static WebKitDOMElement *
+create_table (EEditorPage *editor_page)
+{
+       WebKitDOMDocument *document;
+       WebKitDOMElement *table, *br, *caret, *element, *cell;
+       WebKitDOMNode *clone;
+       gboolean empty = FALSE;
+       gchar *text_content;
+       gint i;
+
+       g_return_val_if_fail (E_IS_EDITOR_PAGE (editor_page), NULL);
+
+       document = e_editor_page_get_document (editor_page);
+
+       /* Default 3x3 table */
+       table = webkit_dom_document_create_element (document, "TABLE", NULL);
+       for (i = 0; i < 3; i++) {
+               WebKitDOMHTMLElement *row;
+               gint j;
+
+               row = webkit_dom_html_table_element_insert_row (
+                       WEBKIT_DOM_HTML_TABLE_ELEMENT (table), -1, NULL);
+
+               for (j = 0; j < 3; j++) {
+                       webkit_dom_html_table_row_element_insert_cell (
+                               WEBKIT_DOM_HTML_TABLE_ROW_ELEMENT (row), -1, NULL);
+               }
+       }
+
+       webkit_dom_element_set_id (table, "-x-evo-current-table");
+
+       e_editor_dom_selection_save (editor_page);
+       caret = webkit_dom_document_get_element_by_id (
+               document, "-x-evo-selection-end-marker");
+
+
+       element = get_parent_block_element (WEBKIT_DOM_NODE (caret));
+       text_content = webkit_dom_node_get_text_content (WEBKIT_DOM_NODE (element));
+       empty = text_content && !*text_content;
+       g_free (text_content);
+
+       clone = webkit_dom_node_clone_node_with_error (WEBKIT_DOM_NODE (element), FALSE, NULL);
+       br = webkit_dom_document_create_element (document, "BR", NULL);
+       webkit_dom_node_append_child (clone, WEBKIT_DOM_NODE (br), NULL);
+       webkit_dom_node_insert_before (
+               webkit_dom_node_get_parent_node (WEBKIT_DOM_NODE (element)),
+               clone,
+               webkit_dom_node_get_next_sibling (WEBKIT_DOM_NODE (element)),
+               NULL);
+
+       /* Move caret to the first cell */
+       cell = webkit_dom_element_query_selector (table, "td", NULL);
+       webkit_dom_node_append_child (
+               WEBKIT_DOM_NODE (cell), WEBKIT_DOM_NODE (caret), NULL);
+       caret = webkit_dom_document_get_element_by_id (
+               document, "-x-evo-selection-start-marker");
+       webkit_dom_node_insert_before (
+               WEBKIT_DOM_NODE (cell),
+               WEBKIT_DOM_NODE (caret),
+               webkit_dom_node_get_last_child (WEBKIT_DOM_NODE (cell)),
+               NULL);
+
+       /* Insert the table into body unred the current block (if current block is not empty)
+        * otherwise replace the current block. */
+       if (empty) {
+               webkit_dom_node_replace_child (
+                       webkit_dom_node_get_parent_node (WEBKIT_DOM_NODE (element)),
+                       WEBKIT_DOM_NODE (table),
+                       WEBKIT_DOM_NODE (element),
+                       NULL);
+       } else {
+               webkit_dom_node_insert_before (
+                       webkit_dom_node_get_parent_node (WEBKIT_DOM_NODE (element)),
+                       WEBKIT_DOM_NODE (table),
+                       webkit_dom_node_get_next_sibling (WEBKIT_DOM_NODE (element)),
+                       NULL);
+       }
+
+       e_editor_dom_selection_restore (editor_page);
+
+       e_editor_page_emit_content_changed (editor_page);
+
+       return table;
+}
+
+gboolean
+e_dialogs_dom_table_show (EEditorPage *editor_page)
+{
+       WebKitDOMDocument *document;
+       WebKitDOMDOMWindow *dom_window = NULL;
+       WebKitDOMDOMSelection *dom_selection = NULL;
+       WebKitDOMElement *table = NULL;
+       EEditorUndoRedoManager *manager;
+       gboolean created = FALSE;
+
+       g_return_val_if_fail (E_IS_EDITOR_PAGE (editor_page), FALSE);
+
+       document = e_editor_page_get_document (editor_page);
+       dom_window = webkit_dom_document_get_default_view (document);
+       dom_selection = webkit_dom_dom_window_get_selection (dom_window);
+       g_clear_object (&dom_window);
+       if (dom_selection && (webkit_dom_dom_selection_get_range_count (dom_selection) > 0)) {
+               WebKitDOMRange *range = NULL;
+
+               range = webkit_dom_dom_selection_get_range_at (dom_selection, 0, NULL);
+               table = dom_node_find_parent_element (
+                       webkit_dom_range_get_start_container (range, NULL), "TABLE");
+               g_clear_object (&range);
+
+               if (table) {
+                       webkit_dom_element_set_id (table, "-x-evo-current-table");
+               } else {
+                       table = create_table (editor_page);
+                       created = TRUE;
+               }
+       }
+
+       manager = e_editor_page_get_undo_redo_manager (editor_page);
+       if (!e_editor_undo_redo_manager_is_operation_in_progress (manager)) {
+               EEditorHistoryEvent *ev;
+
+               ev = g_new0 (EEditorHistoryEvent, 1);
+               ev->type = HISTORY_TABLE_DIALOG;
+
+               e_editor_dom_selection_get_coordinates (editor_page, &ev->before.start.x, 
&ev->before.start.y, &ev->before.end.x, &ev->before.end.y);
+               if (!created)
+                       ev->data.dom.from = webkit_dom_node_clone_node_with_error (
+                               WEBKIT_DOM_NODE (table), TRUE, NULL);
+               else
+                       ev->data.dom.from = NULL;
+
+               e_editor_undo_redo_manager_insert_history_event (manager, ev);
+       }
+
+       g_clear_object (&dom_selection);
+
+       return created;
+}
+
+void
+e_dialogs_dom_table_save_history_on_exit (EEditorPage *editor_page)
+{
+       EEditorHistoryEvent *ev = NULL;
+       EEditorUndoRedoManager *manager;
+       WebKitDOMDocument *document;
+       WebKitDOMElement *element;
+
+       g_return_if_fail (E_IS_EDITOR_PAGE (editor_page));
+
+       document = e_editor_page_get_document (editor_page);
+
+       element = WEBKIT_DOM_ELEMENT (get_current_table_element (document));
+       g_return_if_fail (element != NULL);
+
+       webkit_dom_element_remove_attribute (element, "id");
+
+       manager = e_editor_page_get_undo_redo_manager (editor_page);
+       ev = e_editor_undo_redo_manager_get_current_history_event (manager);
+       ev->data.dom.to = webkit_dom_node_clone_node_with_error (
+               WEBKIT_DOM_NODE (element), TRUE, NULL);
+
+       if (ev->data.dom.from && webkit_dom_node_is_equal_node (ev->data.dom.from, ev->data.dom.to))
+               e_editor_undo_redo_manager_remove_current_history_event (manager);
+       else
+               e_editor_dom_selection_get_coordinates (editor_page, &ev->after.start.x, &ev->after.start.y, 
&ev->after.end.x, &ev->after.end.y);
+}
diff --git a/modules/webkit-editor/web-extension/e-dialogs-dom-functions.h 
b/modules/webkit-editor/web-extension/e-dialogs-dom-functions.h
new file mode 100644
index 0000000..db28b49
--- /dev/null
+++ b/modules/webkit-editor/web-extension/e-dialogs-dom-functions.h
@@ -0,0 +1,134 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2016 Red Hat, Inc. (www.redhat.com)
+ *
+ * This library 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 library 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 Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef E_DIALOGS_DOM_FUNCTIONS_H
+#define E_DIALOGS_DOM_FUNCTIONS_H
+
+#include <webkit2/webkit-web-extension.h>
+
+#include "e-editor-page.h"
+
+G_BEGIN_DECLS
+
+/* ******************** Cell Dialog ***************** */
+
+void           e_dialogs_dom_cell_mark_current_cell_element
+                                               (EEditorPage *editor_page,
+                                                const gchar *id);
+void           e_dialogs_dom_cell_save_history_on_exit
+                                               (EEditorPage *editor_page);
+void           e_dialogs_dom_cell_set_element_v_align
+                                               (EEditorPage *editor_page,
+                                                const gchar *v_align,
+                                                guint scope);
+void           e_dialogs_dom_cell_set_element_align
+                                               (EEditorPage *editor_page,
+                                                const gchar *align,
+                                                guint scope);
+void           e_dialogs_dom_cell_set_element_no_wrap
+                                               (EEditorPage *editor_page,
+                                                gboolean wrap_text,
+                                                guint scope);
+void           e_dialogs_dom_cell_set_element_header_style
+                                               (EEditorPage *editor_page,
+                                                gboolean header_style,
+                                                guint scope);
+void           e_dialogs_dom_cell_set_element_width
+                                               (EEditorPage *editor_page,
+                                                const gchar *width,
+                                                guint scope);
+void           e_dialogs_dom_cell_set_element_col_span
+                                               (EEditorPage *editor_page,
+                                                glong span,
+                                                guint scope);
+void           e_dialogs_dom_cell_set_element_row_span
+                                               (EEditorPage *editor_page,
+                                                glong span,
+                                                guint scope);
+void           e_dialogs_dom_cell_set_element_bg_color
+                                               (EEditorPage *editor_page,
+                                                const gchar *color,
+                                                guint scope);
+
+/* ******************** HRule Dialog ***************** */
+
+gboolean       e_dialogs_dom_h_rule_find_hrule (EEditorPage *editor_page);
+void           e_dialogs_dom_h_rule_dialog_on_close
+                                               (EEditorPage *editor_page);
+
+/* ******************** Image Dialog ***************** */
+
+void           e_dialogs_dom_image_mark_image  (EEditorPage *editor_page);
+void           e_dialogs_dom_image_save_history_on_exit
+                                               (EEditorPage *editor_page);
+void           e_dialogs_dom_image_set_element_url
+                                               (EEditorPage *editor_page,
+                                                const gchar *url);
+gchar *                e_dialogs_dom_image_get_element_url
+                                               (EEditorPage *editor_page);
+
+/* ******************** Link Dialog ***************** */
+
+void           e_dialogs_dom_link_commit       (EEditorPage *editor_page,
+                                                const gchar *url,
+                                                const gchar *inner_text);
+GVariant *     e_dialogs_dom_link_show         (EEditorPage *editor_page);
+void           e_dialogs_dom_link_dialog_on_open
+                                               (EEditorPage *editor_page);
+void           e_dialogs_dom_link_dialog_on_close
+                                               (EEditorPage *editor_page);
+
+/* ******************** Page Dialog ***************** */
+
+void           e_dialogs_dom_page_save_history (EEditorPage *editor_page);
+void           e_dialogs_dom_page_save_history_on_exit
+                                               (EEditorPage *editor_page);
+
+/* ******************** Spell Check Dialog ***************** */
+
+gchar *        e_dialogs_dom_spell_check_prev  (EEditorPage *editor_page,
+                                                const gchar *from_word,
+                                                const gchar * const *languages);
+
+gchar *        e_dialogs_dom_spell_check_next  (EEditorPage *editor_page,
+                                                const gchar *from_word,
+                                                const gchar * const *languages);
+
+/* ******************** Table Dialog ***************** */
+
+void           e_dialogs_dom_table_set_row_count
+                                               (EEditorPage *editor_page,
+                                                gulong expected_count);
+
+gulong         e_dialogs_dom_table_get_row_count
+                                               (EEditorPage *editor_page);
+
+void           e_dialogs_dom_table_set_column_count
+                                               (EEditorPage *editor_page,
+                                                gulong expected_columns);
+
+gulong         e_dialogs_dom_table_get_column_count
+                                               (EEditorPage *editor_page);
+
+gboolean       e_dialogs_dom_table_show        (EEditorPage *editor_page);
+
+void           e_dialogs_dom_table_save_history_on_exit
+                                               (EEditorPage *editor_page);
+
+G_END_DECLS
+
+#endif /* E_DIALOGS_DOM_FUNCTIONS_H */
diff --git a/modules/webkit-editor/web-extension/e-editor-dom-functions.c 
b/modules/webkit-editor/web-extension/e-editor-dom-functions.c
new file mode 100644
index 0000000..ecc833d
--- /dev/null
+++ b/modules/webkit-editor/web-extension/e-editor-dom-functions.c
@@ -0,0 +1,17386 @@
+/*
+ * Copyright (C) 2016 Red Hat, Inc. (www.redhat.com)
+ *
+ * This library 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 library 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 Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <string.h>
+
+#define WEBKIT_DOM_USE_UNSTABLE_API
+#include <webkitdom/WebKitDOMDocumentUnstable.h>
+#include <webkitdom/WebKitDOMDocumentFragmentUnstable.h>
+#include <webkitdom/WebKitDOMDOMSelection.h>
+#include <webkitdom/WebKitDOMDOMWindowUnstable.h>
+#include <webkitdom/WebKitDOMHTMLElementUnstable.h>
+#include <webkitdom/WebKitDOMRangeUnstable.h>
+#undef WEBKIT_DOM_USE_UNSTABLE_API
+
+#include "web-extensions/e-dom-utils.h"
+
+#include "e-editor-page.h"
+#include "e-editor-undo-redo-manager.h"
+
+#include "e-editor-dom-functions.h"
+
+#define HTML_KEY_CODE_BACKSPACE 8
+#define HTML_KEY_CODE_RETURN 13
+#define HTML_KEY_CODE_CONTROL 17
+#define HTML_KEY_CODE_SPACE 32
+#define HTML_KEY_CODE_DELETE 46
+#define HTML_KEY_CODE_TABULATOR 9
+
+/* ******************** Tests ******************** */
+
+static gchar *
+workaround_spaces (const gchar *text)
+{
+       GString *tmp;
+       gchar *str = NULL;
+
+       tmp = e_str_replace_string (text, "&nbsp;", " ");
+       if (tmp) {
+               str = g_string_free (tmp, FALSE);
+               text = str;
+       }
+
+       tmp = e_str_replace_string (text, " ", " ");
+       if (tmp) {
+               g_free (str);
+               str = g_string_free (tmp, FALSE);
+       } else if (!str) {
+               str = g_strdup (text);
+       }
+
+       return str;
+}
+
+gboolean
+e_editor_dom_test_html_equal (WebKitDOMDocument *document,
+                             const gchar *html1,
+                             const gchar *html2)
+{
+       WebKitDOMElement *elem1, *elem2;
+       gchar *str1, *str2;
+       gboolean res = FALSE;
+       GError *error = NULL;
+
+       g_return_val_if_fail (WEBKIT_DOM_IS_DOCUMENT (document), FALSE);
+       g_return_val_if_fail (html1 != NULL, FALSE);
+       g_return_val_if_fail (html2 != NULL, FALSE);
+
+       elem1 = webkit_dom_document_create_element (document, "TestHtmlEqual", &error);
+       if (error || !elem1) {
+               g_warning ("%s: Failed to create elem1: %s", G_STRFUNC, error ? error->message : "Unknown 
error");
+               g_clear_error (&error);
+               return FALSE;
+       }
+
+       elem2 = webkit_dom_document_create_element (document, "TestHtmlEqual", &error);
+       if (error || !elem2) {
+               g_warning ("%s: Failed to create elem2: %s", G_STRFUNC, error ? error->message : "Unknown 
error");
+               g_clear_error (&error);
+               return FALSE;
+       }
+
+       /* FIXME WK2: Workaround when &nbsp; is used instead of regular spaces. (Placed by WebKit?) */
+       str1 = workaround_spaces (html1);
+       str2 = workaround_spaces (html2);
+
+       webkit_dom_element_set_inner_html (elem1, str1, &error);
+       if (!error) {
+               webkit_dom_element_set_inner_html (elem2, str2, &error);
+
+               if (!error) {
+                       webkit_dom_node_normalize (WEBKIT_DOM_NODE (elem1));
+                       webkit_dom_node_normalize (WEBKIT_DOM_NODE (elem2));
+
+                       res = webkit_dom_node_is_equal_node (WEBKIT_DOM_NODE (elem1), WEBKIT_DOM_NODE 
(elem2));
+               } else {
+                       g_warning ("%s: Failed to set inner html2: %s", G_STRFUNC, error->message);
+               }
+       } else {
+               g_warning ("%s: Failed to set inner html1: %s", G_STRFUNC, error->message);
+       }
+
+       if (res && (g_strcmp0 (html1, str1) != 0 || g_strcmp0 (html2, str2) != 0))
+               g_warning ("%s: Applied the '&nbsp;' workaround", G_STRFUNC);
+
+       g_clear_error (&error);
+       g_free (str1);
+       g_free (str2);
+
+       return res;
+}
+
+/* ******************** Actions ******************** */
+
+static WebKitDOMElement *
+get_table_cell_element (WebKitDOMDocument *document)
+{
+       return webkit_dom_document_get_element_by_id (document, "-x-evo-current-cell");
+}
+
+static void
+prepare_history_for_table (EEditorPage *editor_page,
+                           WebKitDOMElement *table,
+                           EEditorHistoryEvent *ev)
+{
+       ev->type = HISTORY_TABLE_DIALOG;
+
+       e_editor_dom_selection_get_coordinates (editor_page, &ev->before.start.x, &ev->before.start.y, 
&ev->before.end.x, &ev->before.end.y);
+
+       ev->data.dom.from = webkit_dom_node_clone_node_with_error (
+               WEBKIT_DOM_NODE (table), TRUE, NULL);
+}
+
+
+static void
+save_history_for_table (EEditorPage *editor_page,
+                        WebKitDOMElement *table,
+                        EEditorHistoryEvent *ev)
+{
+       EEditorUndoRedoManager *manager;
+
+       if (table)
+               ev->data.dom.to = webkit_dom_node_clone_node_with_error (
+                       WEBKIT_DOM_NODE (table), TRUE, NULL);
+       else
+               ev->data.dom.to = NULL;
+
+       e_editor_dom_selection_get_coordinates (editor_page,
+               &ev->after.start.x, &ev->after.start.y, &ev->after.end.x, &ev->after.end.y);
+
+       manager = e_editor_page_get_undo_redo_manager (editor_page);
+       e_editor_undo_redo_manager_insert_history_event (manager, ev);
+}
+
+void
+e_editor_dom_delete_cell_contents (EEditorPage *editor_page)
+{
+       WebKitDOMDocument *document;
+       WebKitDOMNode *node;
+       WebKitDOMElement *cell, *table_cell, *table;
+       EEditorHistoryEvent *ev = NULL;
+
+       g_return_if_fail (E_IS_EDITOR_PAGE (editor_page));
+
+       document = e_editor_page_get_document (editor_page);
+
+       table_cell = get_table_cell_element (document);
+       g_return_if_fail (table_cell != NULL);
+
+       cell = dom_node_find_parent_element (WEBKIT_DOM_NODE (table_cell), "TD");
+       if (!cell)
+               cell = dom_node_find_parent_element (WEBKIT_DOM_NODE (table_cell), "TH");
+       g_return_if_fail (cell != NULL);
+
+       table = dom_node_find_parent_element (WEBKIT_DOM_NODE (cell), "TABLE");
+       g_return_if_fail (table != NULL);
+
+       ev = g_new0 (EEditorHistoryEvent, 1);
+       prepare_history_for_table (editor_page, table, ev);
+
+       while ((node = webkit_dom_node_get_first_child (WEBKIT_DOM_NODE (cell))))
+               remove_node (node);
+
+       save_history_for_table (editor_page, table, ev);
+}
+
+void
+e_editor_dom_delete_column (EEditorPage *editor_page)
+{
+       WebKitDOMDocument *document;
+       WebKitDOMElement *cell, *table, *table_cell;
+       WebKitDOMHTMLCollection *rows = NULL;
+       EEditorHistoryEvent *ev = NULL;
+       gulong index, length, ii;
+
+       g_return_if_fail (E_IS_EDITOR_PAGE (editor_page));
+
+       document = e_editor_page_get_document (editor_page);
+
+       table_cell = get_table_cell_element (document);
+       g_return_if_fail (table_cell != NULL);
+
+       /* Find TD in which the selection starts */
+       cell = dom_node_find_parent_element (WEBKIT_DOM_NODE (table_cell), "TD");
+       if (!cell)
+               cell = dom_node_find_parent_element (WEBKIT_DOM_NODE (table_cell), "TH");
+       g_return_if_fail (cell != NULL);
+
+       table = dom_node_find_parent_element (WEBKIT_DOM_NODE (cell), "TABLE");
+       g_return_if_fail (table != NULL);
+
+       ev = g_new0 (EEditorHistoryEvent, 1);
+       prepare_history_for_table (editor_page, table, ev);
+
+       rows = webkit_dom_html_table_element_get_rows (
+                       WEBKIT_DOM_HTML_TABLE_ELEMENT (table));
+       length = webkit_dom_html_collection_get_length (rows);
+
+       index = webkit_dom_html_table_cell_element_get_cell_index (
+                       WEBKIT_DOM_HTML_TABLE_CELL_ELEMENT (cell));
+
+       for (ii = 0; ii < length; ii++) {
+               WebKitDOMNode *row;
+
+               row = webkit_dom_html_collection_item (rows, ii);
+
+               webkit_dom_html_table_row_element_delete_cell (
+                       WEBKIT_DOM_HTML_TABLE_ROW_ELEMENT (row), index, NULL);
+               g_object_unref (row);
+       }
+
+       g_clear_object (&rows);
+
+       save_history_for_table (editor_page, table, ev);
+}
+
+void
+e_editor_dom_delete_row (EEditorPage *editor_page)
+{
+       WebKitDOMDocument *document;
+       WebKitDOMElement *row, *table, *table_cell;
+       EEditorHistoryEvent *ev = NULL;
+
+       g_return_if_fail (E_IS_EDITOR_PAGE (editor_page));
+
+       document = e_editor_page_get_document (editor_page);
+
+       table_cell = get_table_cell_element (document);
+       g_return_if_fail (table_cell != NULL);
+
+       row = dom_node_find_parent_element (WEBKIT_DOM_NODE (table_cell), "TR");
+       g_return_if_fail (row != NULL);
+
+       table = dom_node_find_parent_element (WEBKIT_DOM_NODE (table_cell), "TABLE");
+       g_return_if_fail (table != NULL);
+
+       ev = g_new0 (EEditorHistoryEvent, 1);
+       prepare_history_for_table (editor_page, table, ev);
+
+       remove_node (WEBKIT_DOM_NODE (row));
+
+       save_history_for_table (editor_page, table, ev);
+}
+
+void
+e_editor_dom_delete_table (EEditorPage *editor_page)
+{
+       WebKitDOMDocument *document;
+       WebKitDOMElement *table, *table_cell;
+       EEditorHistoryEvent *ev = NULL;
+
+       g_return_if_fail (E_IS_EDITOR_PAGE (editor_page));
+
+       document = e_editor_page_get_document (editor_page);
+
+       table_cell = get_table_cell_element (document);
+       g_return_if_fail (table_cell != NULL);
+
+       table = dom_node_find_parent_element (WEBKIT_DOM_NODE (table_cell), "TABLE");
+       g_return_if_fail (table != NULL);
+
+       ev = g_new0 (EEditorHistoryEvent, 1);
+       prepare_history_for_table (editor_page, table, ev);
+
+       remove_node (WEBKIT_DOM_NODE (table));
+
+       save_history_for_table (editor_page, NULL, ev);
+}
+
+void
+e_editor_dom_insert_column_after (EEditorPage *editor_page)
+{
+       WebKitDOMDocument *document;
+       WebKitDOMElement *cell, *row, *table_cell, *table;
+       EEditorHistoryEvent *ev = NULL;
+       gulong index;
+
+       g_return_if_fail (E_IS_EDITOR_PAGE (editor_page));
+
+       document = e_editor_page_get_document (editor_page);
+
+       table_cell = get_table_cell_element (document);
+       g_return_if_fail (table_cell != NULL);
+
+       cell = dom_node_find_parent_element (WEBKIT_DOM_NODE (table_cell), "TD");
+       if (!cell)
+               cell = dom_node_find_parent_element (WEBKIT_DOM_NODE (table_cell), "TH");
+       g_return_if_fail (cell != NULL);
+
+       row = dom_node_find_parent_element (WEBKIT_DOM_NODE (cell), "TR");
+       g_return_if_fail (row != NULL);
+
+       table = dom_node_find_parent_element (WEBKIT_DOM_NODE (table_cell), "TABLE");
+       g_return_if_fail (table != NULL);
+
+       ev = g_new0 (EEditorHistoryEvent, 1);
+       prepare_history_for_table (editor_page, table, ev);
+
+       /* Get the first row in the table */
+       row = WEBKIT_DOM_ELEMENT (
+               webkit_dom_node_get_first_child (
+                       webkit_dom_node_get_parent_node (WEBKIT_DOM_NODE (row))));
+
+       index = webkit_dom_html_table_cell_element_get_cell_index (
+                       WEBKIT_DOM_HTML_TABLE_CELL_ELEMENT (cell));
+
+       while (row) {
+               webkit_dom_html_table_row_element_insert_cell (
+                       WEBKIT_DOM_HTML_TABLE_ROW_ELEMENT (row), index + 1, NULL);
+
+               row = WEBKIT_DOM_ELEMENT (
+                       webkit_dom_node_get_next_sibling (WEBKIT_DOM_NODE (row)));
+       }
+
+       save_history_for_table (editor_page, table, ev);
+}
+
+void
+e_editor_dom_insert_column_before (EEditorPage *editor_page)
+{
+       WebKitDOMDocument *document;
+       WebKitDOMElement *cell, *row, *table_cell, *table;
+       EEditorHistoryEvent *ev = NULL;
+       gulong index;
+
+       g_return_if_fail (E_IS_EDITOR_PAGE (editor_page));
+
+       document = e_editor_page_get_document (editor_page);
+
+       table_cell = get_table_cell_element (document);
+       g_return_if_fail (table_cell != NULL);
+
+       cell = dom_node_find_parent_element (WEBKIT_DOM_NODE (table_cell), "TD");
+       if (!cell) {
+               cell = dom_node_find_parent_element (WEBKIT_DOM_NODE (table_cell), "TH");
+       }
+       g_return_if_fail (cell != NULL);
+
+       row = dom_node_find_parent_element (WEBKIT_DOM_NODE (table_cell), "TR");
+       g_return_if_fail (row != NULL);
+
+       table = dom_node_find_parent_element (WEBKIT_DOM_NODE (table_cell), "TABLE");
+       g_return_if_fail (table != NULL);
+
+       ev = g_new0 (EEditorHistoryEvent, 1);
+       prepare_history_for_table (editor_page, table, ev);
+
+       /* Get the first row in the table */
+       row = WEBKIT_DOM_ELEMENT (
+               webkit_dom_node_get_first_child (
+                       webkit_dom_node_get_parent_node (WEBKIT_DOM_NODE (row))));
+
+       index = webkit_dom_html_table_cell_element_get_cell_index (
+                       WEBKIT_DOM_HTML_TABLE_CELL_ELEMENT (cell));
+
+       while (row) {
+               webkit_dom_html_table_row_element_insert_cell (
+                       WEBKIT_DOM_HTML_TABLE_ROW_ELEMENT (row), index - 1, NULL);
+
+               row = WEBKIT_DOM_ELEMENT (
+                       webkit_dom_node_get_next_sibling (WEBKIT_DOM_NODE (row)));
+       }
+
+       save_history_for_table (editor_page, table, ev);
+}
+
+void
+e_editor_dom_insert_row_above (EEditorPage *editor_page)
+{
+       WebKitDOMDocument *document;
+       WebKitDOMElement *row, *table, *table_cell;
+       WebKitDOMHTMLCollection *cells = NULL;
+       WebKitDOMHTMLElement *new_row;
+       EEditorHistoryEvent *ev = NULL;
+       gulong index, cell_count, ii;
+
+       g_return_if_fail (E_IS_EDITOR_PAGE (editor_page));
+
+       document = e_editor_page_get_document (editor_page);
+
+       table_cell = get_table_cell_element (document);
+       g_return_if_fail (table_cell != NULL);
+
+       row = dom_node_find_parent_element (WEBKIT_DOM_NODE (table_cell), "TR");
+       g_return_if_fail (row != NULL);
+
+       table = dom_node_find_parent_element (WEBKIT_DOM_NODE (row), "TABLE");
+       g_return_if_fail (table != NULL);
+
+       ev = g_new0 (EEditorHistoryEvent, 1);
+       prepare_history_for_table (editor_page, table, ev);
+
+       index = webkit_dom_html_table_row_element_get_row_index (
+                       WEBKIT_DOM_HTML_TABLE_ROW_ELEMENT (row));
+
+       new_row = webkit_dom_html_table_element_insert_row (
+                       WEBKIT_DOM_HTML_TABLE_ELEMENT (table), index, NULL);
+
+       cells = webkit_dom_html_table_row_element_get_cells (
+                       WEBKIT_DOM_HTML_TABLE_ROW_ELEMENT (row));
+       cell_count = webkit_dom_html_collection_get_length (cells);
+       for (ii = 0; ii < cell_count; ii++) {
+               webkit_dom_html_table_row_element_insert_cell (
+                       WEBKIT_DOM_HTML_TABLE_ROW_ELEMENT (new_row), -1, NULL);
+       }
+
+       g_clear_object (&cells);
+
+       save_history_for_table (editor_page, table, ev);
+}
+
+void
+e_editor_dom_insert_row_below (EEditorPage *editor_page)
+{
+       WebKitDOMDocument *document;
+       WebKitDOMElement *row, *table, *table_cell;
+       WebKitDOMHTMLCollection *cells = NULL;
+       WebKitDOMHTMLElement *new_row;
+       EEditorHistoryEvent *ev = NULL;
+       gulong index, cell_count, ii;
+
+       g_return_if_fail (E_IS_EDITOR_PAGE (editor_page));
+
+       document = e_editor_page_get_document (editor_page);
+
+       table_cell = get_table_cell_element (document);
+       g_return_if_fail (table_cell != NULL);
+
+       row = dom_node_find_parent_element (WEBKIT_DOM_NODE (table_cell), "TR");
+       g_return_if_fail (row != NULL);
+
+       table = dom_node_find_parent_element (WEBKIT_DOM_NODE (row), "TABLE");
+       g_return_if_fail (table != NULL);
+
+       ev = g_new0 (EEditorHistoryEvent, 1);
+       prepare_history_for_table (editor_page, table, ev);
+
+       index = webkit_dom_html_table_row_element_get_row_index (
+                       WEBKIT_DOM_HTML_TABLE_ROW_ELEMENT (row));
+
+       new_row = webkit_dom_html_table_element_insert_row (
+                       WEBKIT_DOM_HTML_TABLE_ELEMENT (table), index + 1, NULL);
+
+       cells = webkit_dom_html_table_row_element_get_cells (
+                       WEBKIT_DOM_HTML_TABLE_ROW_ELEMENT (row));
+       cell_count = webkit_dom_html_collection_get_length (cells);
+       for (ii = 0; ii < cell_count; ii++) {
+               webkit_dom_html_table_row_element_insert_cell (
+                       WEBKIT_DOM_HTML_TABLE_ROW_ELEMENT (new_row), -1, NULL);
+       }
+
+       g_clear_object (&cells);
+
+       save_history_for_table (editor_page, table, ev);
+}
+
+void
+e_editor_dom_save_history_for_cut (EEditorPage *editor_page)
+{
+       WebKitDOMDocument *document;
+       WebKitDOMDocumentFragment *fragment;
+       WebKitDOMDOMWindow *dom_window = NULL;
+       WebKitDOMDOMSelection *dom_selection = NULL;
+       WebKitDOMRange *range = NULL;
+       EEditorHistoryEvent *ev;
+       EEditorUndoRedoManager *manager;
+
+       g_return_if_fail (E_IS_EDITOR_PAGE (editor_page));
+
+       document = e_editor_page_get_document (editor_page);
+       dom_window = webkit_dom_document_get_default_view (document);
+       dom_selection = webkit_dom_dom_window_get_selection (dom_window);
+       g_clear_object (&dom_window);
+
+       if (!webkit_dom_dom_selection_get_range_count (dom_selection) ||
+           webkit_dom_dom_selection_get_is_collapsed (dom_selection)) {
+               g_clear_object (&dom_selection);
+               return;
+       }
+
+       range = webkit_dom_dom_selection_get_range_at (dom_selection, 0, NULL);
+
+       ev = g_new0 (EEditorHistoryEvent, 1);
+       ev->type = HISTORY_DELETE;
+
+       e_editor_dom_selection_get_coordinates (editor_page,
+               &ev->before.start.x,
+               &ev->before.start.y,
+               &ev->before.end.x,
+               &ev->before.end.y);
+
+       ev->after.start.x = ev->before.start.x;
+       ev->after.start.y = ev->before.start.y;
+       ev->after.end.x = ev->before.start.x;
+       ev->after.end.y = ev->before.start.y;
+
+       /* Save the fragment. */
+       fragment = webkit_dom_range_clone_contents (range, NULL);
+       g_clear_object (&dom_selection);
+       g_clear_object (&range);
+       ev->data.fragment = g_object_ref (fragment);
+
+       manager = e_editor_page_get_undo_redo_manager (editor_page);
+       e_editor_undo_redo_manager_insert_history_event (manager, ev);
+       e_editor_page_set_dont_save_history_in_body_input (editor_page, TRUE);
+}
+
+/* ******************** View ******************** */
+
+/*
+ * e_editor_dom_exec_command:
+ * @document: a #WebKitDOMDocument
+ * @command: an #EContentEditorCommand to execute
+ * @value: value of the command (or @NULL if the command does not require value)
+ *
+ * The function will fail when @value is @NULL or empty but the current @command
+ * requires a value to be passed. The @value is ignored when the @command does
+ * not expect any value.
+ *
+ * Returns: @TRUE when the command was succesfully executed, @FALSE otherwise.
+ */
+gboolean
+e_editor_dom_exec_command (EEditorPage *editor_page,
+                          EContentEditorCommand command,
+                          const gchar *value)
+{
+       const gchar *cmd_str = 0;
+       gboolean has_value = FALSE;
+
+       g_return_val_if_fail (E_IS_EDITOR_PAGE (editor_page), FALSE);
+
+#define CHECK_COMMAND(cmd,str,val) case cmd:\
+       if (val) {\
+               g_return_val_if_fail (value && *value, FALSE);\
+       }\
+       has_value = val; \
+       cmd_str = str;\
+       break;
+
+       switch (command) {
+               CHECK_COMMAND (E_CONTENT_EDITOR_COMMAND_BACKGROUND_COLOR, "BackColor", TRUE)
+               CHECK_COMMAND (E_CONTENT_EDITOR_COMMAND_BOLD, "Bold", FALSE)
+               CHECK_COMMAND (E_CONTENT_EDITOR_COMMAND_COPY, "Copy", FALSE)
+               CHECK_COMMAND (E_CONTENT_EDITOR_COMMAND_CREATE_LINK, "CreateLink", TRUE)
+               CHECK_COMMAND (E_CONTENT_EDITOR_COMMAND_CUT, "Cut", FALSE)
+               CHECK_COMMAND (E_CONTENT_EDITOR_COMMAND_DEFAULT_PARAGRAPH_SEPARATOR, 
"DefaultParagraphSeparator", FALSE)
+               CHECK_COMMAND (E_CONTENT_EDITOR_COMMAND_DELETE, "Delete", FALSE)
+               CHECK_COMMAND (E_CONTENT_EDITOR_COMMAND_FIND_STRING, "FindString", TRUE)
+               CHECK_COMMAND (E_CONTENT_EDITOR_COMMAND_FONT_NAME, "FontName", TRUE)
+               CHECK_COMMAND (E_CONTENT_EDITOR_COMMAND_FONT_SIZE, "FontSize", TRUE)
+               CHECK_COMMAND (E_CONTENT_EDITOR_COMMAND_FONT_SIZE_DELTA, "FontSizeDelta", TRUE)
+               CHECK_COMMAND (E_CONTENT_EDITOR_COMMAND_FORE_COLOR, "ForeColor", TRUE)
+               CHECK_COMMAND (E_CONTENT_EDITOR_COMMAND_FORMAT_BLOCK, "FormatBlock", TRUE)
+               CHECK_COMMAND (E_CONTENT_EDITOR_COMMAND_FORWARD_DELETE, "ForwardDelete", FALSE)
+               CHECK_COMMAND (E_CONTENT_EDITOR_COMMAND_HILITE_COLOR, "HiliteColor", TRUE)
+               CHECK_COMMAND (E_CONTENT_EDITOR_COMMAND_INDENT, "Indent", FALSE)
+               CHECK_COMMAND (E_CONTENT_EDITOR_COMMAND_INSERT_HORIZONTAL_RULE, "InsertHorizontalRule", FALSE)
+               CHECK_COMMAND (E_CONTENT_EDITOR_COMMAND_INSERT_HTML, "InsertHTML", TRUE)
+               CHECK_COMMAND (E_CONTENT_EDITOR_COMMAND_INSERT_IMAGE, "InsertImage", TRUE)
+               CHECK_COMMAND (E_CONTENT_EDITOR_COMMAND_INSERT_LINE_BREAK, "InsertLineBreak", FALSE)
+               CHECK_COMMAND (E_CONTENT_EDITOR_COMMAND_INSERT_NEW_LINE_IN_QUOTED_CONTENT, 
"InsertNewlineInQuotedContent", FALSE)
+               CHECK_COMMAND (E_CONTENT_EDITOR_COMMAND_INSERT_ORDERED_LIST, "InsertOrderedList", FALSE)
+               CHECK_COMMAND (E_CONTENT_EDITOR_COMMAND_INSERT_PARAGRAPH, "InsertParagraph", FALSE)
+               CHECK_COMMAND (E_CONTENT_EDITOR_COMMAND_INSERT_TEXT, "InsertText", TRUE)
+               CHECK_COMMAND (E_CONTENT_EDITOR_COMMAND_INSERT_UNORDERED_LIST, "InsertUnorderedList", FALSE)
+               CHECK_COMMAND (E_CONTENT_EDITOR_COMMAND_ITALIC, "Italic", FALSE)
+               CHECK_COMMAND (E_CONTENT_EDITOR_COMMAND_JUSTIFY_CENTER, "JustifyCenter", FALSE)
+               CHECK_COMMAND (E_CONTENT_EDITOR_COMMAND_JUSTIFY_FULL, "JustifyFull", FALSE)
+               CHECK_COMMAND (E_CONTENT_EDITOR_COMMAND_JUSTIFY_LEFT, "JustifyLeft", FALSE)
+               CHECK_COMMAND (E_CONTENT_EDITOR_COMMAND_JUSTIFY_NONE, "JustifyNone", FALSE)
+               CHECK_COMMAND (E_CONTENT_EDITOR_COMMAND_JUSTIFY_RIGHT, "JustifyRight", FALSE)
+               CHECK_COMMAND (E_CONTENT_EDITOR_COMMAND_OUTDENT, "Outdent", FALSE)
+               CHECK_COMMAND (E_CONTENT_EDITOR_COMMAND_PASTE, "Paste", FALSE)
+               CHECK_COMMAND (E_CONTENT_EDITOR_COMMAND_PASTE_AND_MATCH_STYLE, "PasteAndMatchStyle", FALSE)
+               CHECK_COMMAND (E_CONTENT_EDITOR_COMMAND_PASTE_AS_PLAIN_TEXT, "PasteAsPlainText", FALSE)
+               CHECK_COMMAND (E_CONTENT_EDITOR_COMMAND_PRINT, "Print", FALSE)
+               CHECK_COMMAND (E_CONTENT_EDITOR_COMMAND_REDO, "Redo", FALSE)
+               CHECK_COMMAND (E_CONTENT_EDITOR_COMMAND_REMOVE_FORMAT, "RemoveFormat", FALSE)
+               CHECK_COMMAND (E_CONTENT_EDITOR_COMMAND_SELECT_ALL, "SelectAll", FALSE)
+               CHECK_COMMAND (E_CONTENT_EDITOR_COMMAND_STRIKETHROUGH, "Strikethrough", FALSE)
+               CHECK_COMMAND (E_CONTENT_EDITOR_COMMAND_STYLE_WITH_CSS, "StyleWithCSS", TRUE)
+               CHECK_COMMAND (E_CONTENT_EDITOR_COMMAND_SUBSCRIPT, "Subscript", FALSE)
+               CHECK_COMMAND (E_CONTENT_EDITOR_COMMAND_SUPERSCRIPT, "Superscript", FALSE)
+               CHECK_COMMAND (E_CONTENT_EDITOR_COMMAND_TRANSPOSE, "Transpose", FALSE)
+               CHECK_COMMAND (E_CONTENT_EDITOR_COMMAND_UNDERLINE, "Underline", FALSE)
+               CHECK_COMMAND (E_CONTENT_EDITOR_COMMAND_UNDO, "Undo", FALSE)
+               CHECK_COMMAND (E_CONTENT_EDITOR_COMMAND_UNLINK, "Unlink", FALSE)
+               CHECK_COMMAND (E_CONTENT_EDITOR_COMMAND_UNSELECT, "Unselect", FALSE)
+               CHECK_COMMAND (E_CONTENT_EDITOR_COMMAND_USE_CSS, "UseCSS", TRUE)
+       }
+
+       e_editor_page_set_dont_save_history_in_body_input (editor_page, TRUE);
+
+       return webkit_dom_document_exec_command (
+               e_editor_page_get_document (editor_page), cmd_str, FALSE, has_value ? value : "" );
+}
+
+static void
+perform_spell_check (WebKitDOMDOMSelection *dom_selection,
+                     WebKitDOMRange *start_range,
+                     WebKitDOMRange *end_range)
+{
+       WebKitDOMRange *actual = start_range;
+
+       /* FIXME WK2: this doesn't work, the cursor is moved, but the spellcheck is not updated */
+       /* Go through all words to spellcheck them. To avoid this we have to wait for
+        * http://www.w3.org/html/wg/drafts/html/master/editing.html#dom-forcespellcheck */
+       /* We are moving forward word by word until we hit the text on the end. */
+       while (actual && webkit_dom_range_compare_boundary_points (end_range, WEBKIT_DOM_RANGE_END_TO_END, 
actual, NULL) != 0) {
+               if (actual != start_range)
+                       g_object_unref (actual);
+               webkit_dom_dom_selection_modify (
+                       dom_selection, "move", "forward", "word");
+               actual = webkit_dom_dom_selection_get_range_at (
+                       dom_selection, 0, NULL);
+       }
+       g_clear_object (&actual);
+}
+
+void
+e_editor_dom_force_spell_check_for_current_paragraph (EEditorPage *editor_page)
+{
+       WebKitDOMDocument *document;
+       WebKitDOMDOMSelection *dom_selection = NULL;
+       WebKitDOMDOMWindow *dom_window = NULL;
+       WebKitDOMElement *selection_start_marker, *selection_end_marker;
+       WebKitDOMElement *parent, *element;
+       WebKitDOMRange *end_range = NULL, *actual = NULL;
+       WebKitDOMText *text;
+
+       g_return_if_fail (E_IS_EDITOR_PAGE (editor_page));
+
+       document = e_editor_page_get_document (editor_page);
+
+       if (!e_editor_page_get_inline_spelling_enabled (editor_page))
+               return;
+
+       document = e_editor_page_get_document (editor_page);
+       element = webkit_dom_document_query_selector (
+               document, "body[spellcheck=true]", NULL);
+
+       if (!element)
+               return;
+
+       if (!webkit_dom_node_get_first_child (WEBKIT_DOM_NODE (element)))
+               return;
+
+       e_editor_dom_selection_save (editor_page);
+
+       selection_start_marker = webkit_dom_document_query_selector (
+               document, "span#-x-evo-selection-start-marker", NULL);
+       selection_end_marker = webkit_dom_document_query_selector (
+               document, "span#-x-evo-selection-end-marker", NULL);
+
+       if (!selection_start_marker || !selection_end_marker)
+               return;
+
+       /* Block callbacks of selection-changed signal as we don't want to
+        * recount all the block format things in EEditorSelection and here as well
+        * when we are moving with caret */
+       e_editor_page_block_selection_changed (editor_page);
+
+       parent = get_parent_block_element (WEBKIT_DOM_NODE (selection_end_marker));
+
+       /* Append some text on the end of the element */
+       text = webkit_dom_document_create_text_node (document, "-x-evo-end");
+       webkit_dom_node_append_child (
+               WEBKIT_DOM_NODE (parent),
+               WEBKIT_DOM_NODE (text),
+               NULL);
+
+       parent = get_parent_block_element (WEBKIT_DOM_NODE (selection_start_marker));
+
+       /* Create range that's pointing on the end of this text */
+       end_range = webkit_dom_document_create_range (document);
+       webkit_dom_range_select_node_contents (
+               end_range, WEBKIT_DOM_NODE (text), NULL);
+       webkit_dom_range_collapse (end_range, FALSE, NULL);
+
+       /* Move on the beginning of the paragraph */
+       dom_window = webkit_dom_document_get_default_view (document);
+       dom_selection = webkit_dom_dom_window_get_selection (dom_window);
+
+       actual = webkit_dom_document_create_range (document);
+       webkit_dom_range_select_node_contents (
+               actual, WEBKIT_DOM_NODE (parent), NULL);
+       webkit_dom_range_collapse (actual, TRUE, NULL);
+       webkit_dom_dom_selection_remove_all_ranges (dom_selection);
+       webkit_dom_dom_selection_add_range (dom_selection, actual);
+       g_clear_object (&actual);
+
+       actual = webkit_dom_dom_selection_get_range_at (dom_selection, 0, NULL);
+       perform_spell_check (dom_selection, actual, end_range);
+
+       g_clear_object (&dom_selection);
+       g_clear_object (&dom_window);
+       g_clear_object (&end_range);
+       g_clear_object (&actual);
+
+       /* Remove the text that we inserted on the end of the paragraph */
+       remove_node (WEBKIT_DOM_NODE (text));
+
+       /* Unblock the callbacks */
+       e_editor_page_unblock_selection_changed (editor_page);
+
+       e_editor_dom_selection_restore (editor_page);
+}
+
+static void
+refresh_spell_check (EEditorPage *editor_page,
+                     gboolean enable_spell_check)
+{
+       WebKitDOMDocument *document;
+       WebKitDOMDOMSelection *dom_selection = NULL;
+       WebKitDOMDOMWindow *dom_window = NULL;
+       WebKitDOMElement *selection_start_marker, *selection_end_marker;
+       WebKitDOMHTMLElement *body;
+       WebKitDOMRange *end_range = NULL, *actual = NULL;
+       WebKitDOMText *text;
+
+       g_return_if_fail (E_IS_EDITOR_PAGE (editor_page));
+
+       document = e_editor_page_get_document (editor_page);
+       body = webkit_dom_document_get_body (document);
+
+       if (!webkit_dom_node_get_first_child (WEBKIT_DOM_NODE (body)))
+               return;
+
+       /* Enable/Disable spellcheck in composer */
+       webkit_dom_element_set_attribute (
+               WEBKIT_DOM_ELEMENT (body),
+               "spellcheck",
+               enable_spell_check ? "true" : "false",
+               NULL);
+       webkit_dom_html_element_set_spellcheck (body, FALSE);
+       webkit_dom_html_element_set_spellcheck (body, enable_spell_check);
+       return;
+
+       e_editor_dom_selection_save (editor_page);
+
+       selection_start_marker = webkit_dom_document_query_selector (
+               document, "span#-x-evo-selection-start-marker", NULL);
+       selection_end_marker = webkit_dom_document_query_selector (
+               document, "span#-x-evo-selection-end-marker", NULL);
+
+       /* Sometimes the web view is not focused, so we have to save the selection
+        * manually into the body */
+       if (!selection_start_marker || !selection_end_marker) {
+               WebKitDOMNode *child;
+
+               child = webkit_dom_node_get_first_child (WEBKIT_DOM_NODE (body));
+               if (!WEBKIT_DOM_IS_HTML_ELEMENT (child))
+                       return;
+
+               dom_add_selection_markers_into_element_start (
+                       document,
+                       WEBKIT_DOM_ELEMENT (child),
+                       &selection_start_marker,
+                       &selection_end_marker);
+       }
+
+       /* Block callbacks of selection-changed signal as we don't want to
+        * recount all the block format things in EEditorSelection and here as well
+        * when we are moving with caret */
+       e_editor_page_block_selection_changed (editor_page);
+
+       /* Append some text on the end of the body */
+       text = webkit_dom_document_create_text_node (document, "-x-evo-end");
+       webkit_dom_node_append_child (
+               WEBKIT_DOM_NODE (body), WEBKIT_DOM_NODE (text), NULL);
+
+       /* Create range that's pointing on the end of this text */
+       end_range = webkit_dom_document_create_range (document);
+       webkit_dom_range_select_node_contents (
+               end_range, WEBKIT_DOM_NODE (text), NULL);
+       webkit_dom_range_collapse (end_range, FALSE, NULL);
+
+       dom_window = webkit_dom_document_get_default_view (document);
+       dom_selection = webkit_dom_dom_window_get_selection (dom_window);
+
+       /* Move on the beginning of the document */
+       webkit_dom_dom_selection_modify (
+               dom_selection, "move", "backward", "documentboundary");
+
+       actual = webkit_dom_dom_selection_get_range_at (dom_selection, 0, NULL);
+       perform_spell_check (dom_selection, actual, end_range);
+
+       g_clear_object (&dom_selection);
+       g_clear_object (&dom_window);
+       g_clear_object (&end_range);
+       g_clear_object (&actual);
+
+       /* Remove the text that we inserted on the end of the body */
+       remove_node (WEBKIT_DOM_NODE (text));
+
+       /* Unblock the callbacks */
+       e_editor_page_unblock_selection_changed (editor_page);
+
+       e_editor_dom_selection_restore (editor_page);
+}
+
+void
+e_editor_dom_turn_spell_check_off (EEditorPage *editor_page)
+{
+       g_return_if_fail (E_IS_EDITOR_PAGE (editor_page));
+
+       refresh_spell_check (editor_page, FALSE);
+}
+
+void
+e_editor_dom_force_spell_check_in_viewport (EEditorPage *editor_page)
+{
+       WebKitDOMDocument *document;
+       WebKitDOMDOMSelection *dom_selection = NULL;
+       WebKitDOMDOMWindow *dom_window = NULL;
+       WebKitDOMElement *last_element;
+       WebKitDOMHTMLElement *body;
+       WebKitDOMRange *end_range = NULL, *actual = NULL;
+       WebKitDOMText *text;
+       glong viewport_height;
+
+       g_return_if_fail (E_IS_EDITOR_PAGE (editor_page));
+
+       if (!e_editor_page_get_inline_spelling_enabled (editor_page))
+               return;
+
+       document = e_editor_page_get_document (editor_page);
+       body = WEBKIT_DOM_HTML_ELEMENT (webkit_dom_document_query_selector (
+               document, "body[spellcheck=true]", NULL));
+
+       if (!body) {
+               body = webkit_dom_document_get_body (document);
+               webkit_dom_element_set_attribute (
+                       WEBKIT_DOM_ELEMENT (body), "spellcheck", "true", NULL);
+       }
+
+       if (!webkit_dom_node_get_first_child (WEBKIT_DOM_NODE (body)))
+               return;
+
+       e_editor_dom_selection_save (editor_page);
+
+       /* Block callbacks of selection-changed signal as we don't want to
+        * recount all the block format things in EEditorSelection and here as well
+        * when we are moving with caret */
+       e_editor_page_block_selection_changed (editor_page);
+
+       /* We have to add 10 px offset as otherwise just the HTML element will be returned */
+       actual = webkit_dom_document_caret_range_from_point (document, 10, 10);
+       if (!actual)
+               goto out;
+
+       /* Append some text on the end of the body */
+       text = webkit_dom_document_create_text_node (document, "-x-evo-end");
+
+       dom_window = webkit_dom_document_get_default_view (document);
+       dom_selection = webkit_dom_dom_window_get_selection (dom_window);
+
+       /* We have to add 10 px offset as otherwise just the HTML element will be returned */
+       viewport_height = webkit_dom_dom_window_get_inner_height (dom_window);
+       last_element = webkit_dom_document_element_from_point (document, 10, viewport_height - 10);
+       if (last_element && !WEBKIT_DOM_IS_HTML_HTML_ELEMENT (last_element) &&
+           !WEBKIT_DOM_IS_HTML_BODY_ELEMENT (last_element)) {
+               WebKitDOMElement *parent;
+
+               parent = get_parent_block_element (WEBKIT_DOM_NODE (last_element));
+               webkit_dom_node_append_child (
+                       WEBKIT_DOM_NODE (parent), WEBKIT_DOM_NODE (text), NULL);
+       } else
+               webkit_dom_node_append_child (
+                       WEBKIT_DOM_NODE (body), WEBKIT_DOM_NODE (text), NULL);
+
+       /* Create range that's pointing on the end of viewport */
+       end_range = webkit_dom_document_create_range (document);
+       webkit_dom_range_select_node_contents (
+               end_range, WEBKIT_DOM_NODE (text), NULL);
+       webkit_dom_range_collapse (end_range, FALSE, NULL);
+
+       webkit_dom_dom_selection_remove_all_ranges (dom_selection);
+       webkit_dom_dom_selection_add_range (dom_selection, actual);
+       perform_spell_check (dom_selection, actual, end_range);
+
+       g_clear_object (&dom_selection);
+       g_clear_object (&dom_window);
+       g_clear_object (&end_range);
+       g_clear_object (&actual);
+
+       /* Remove the text that we inserted on the end of the body */
+       remove_node (WEBKIT_DOM_NODE (text));
+
+ out:
+       /* Unblock the callbacks */
+       e_editor_page_unblock_selection_changed (editor_page);
+
+       e_editor_dom_selection_restore (editor_page);
+}
+
+void
+e_editor_dom_force_spell_check (EEditorPage *editor_page)
+{
+       g_return_if_fail (E_IS_EDITOR_PAGE (editor_page));
+
+       if (e_editor_page_get_inline_spelling_enabled (editor_page))
+               refresh_spell_check (editor_page, TRUE);
+}
+
+gboolean
+e_editor_dom_node_is_citation_node (WebKitDOMNode *node)
+{
+       gchar *value;
+
+       if (!WEBKIT_DOM_IS_HTML_QUOTE_ELEMENT (node))
+               return FALSE;
+
+       value = webkit_dom_element_get_attribute (WEBKIT_DOM_ELEMENT (node), "type");
+
+       /* citation == <blockquote type='cite'> */
+       if (value && g_strcmp0 (value, "cite") == 0) {
+               g_free (value);
+               return TRUE;
+       } else {
+               g_free (value);
+               return FALSE;
+       }
+}
+
+gint
+e_editor_dom_get_citation_level (WebKitDOMNode *node,
+                                gboolean set_plaintext_quoted)
+{
+       WebKitDOMNode *parent = node;
+       gint level = 0;
+
+       while (parent && !WEBKIT_DOM_IS_HTML_BODY_ELEMENT (parent)) {
+               if (WEBKIT_DOM_IS_HTML_QUOTE_ELEMENT (parent) &&
+                   webkit_dom_element_has_attribute (WEBKIT_DOM_ELEMENT (parent), "type"))
+                       level++;
+
+               parent = webkit_dom_node_get_parent_node (parent);
+       }
+
+       return level;
+}
+
+static gchar *
+get_quotation_for_level (gint quote_level)
+{
+       gint ii;
+       GString *output = g_string_new ("");
+
+       for (ii = 0; ii < quote_level; ii++) {
+               g_string_append (output, "<span class=\"-x-evo-quote-character\">");
+               g_string_append (output, QUOTE_SYMBOL);
+               g_string_append (output, " ");
+               g_string_append (output, "</span>");
+       }
+
+       return g_string_free (output, FALSE);
+}
+
+void
+e_editor_dom_quote_plain_text_element_after_wrapping (EEditorPage *editor_page,
+                                                     WebKitDOMElement *element,
+                                                     gint quote_level)
+{
+       WebKitDOMDocument *document;
+       WebKitDOMNodeList *list = NULL;
+       WebKitDOMNode *quoted_node;
+       gint length, ii;
+       gchar *quotation;
+
+       g_return_if_fail (E_IS_EDITOR_PAGE (editor_page));
+       g_return_if_fail (element != NULL);
+
+       document = e_editor_page_get_document (editor_page);
+
+       quoted_node = WEBKIT_DOM_NODE (
+               webkit_dom_document_create_element (document, "SPAN", NULL));
+       webkit_dom_element_set_class_name (
+               WEBKIT_DOM_ELEMENT (quoted_node), "-x-evo-quoted");
+       quotation = get_quotation_for_level (quote_level);
+       webkit_dom_element_set_inner_html (
+               WEBKIT_DOM_ELEMENT (quoted_node), quotation, NULL);
+
+       list = webkit_dom_element_query_selector_all (
+               element, "br.-x-evo-wrap-br", NULL);
+       webkit_dom_node_insert_before (
+               WEBKIT_DOM_NODE (element),
+               quoted_node,
+               webkit_dom_node_get_first_child (WEBKIT_DOM_NODE (element)),
+               NULL);
+
+       length = webkit_dom_node_list_get_length (list);
+       for (ii = 0; ii < length; ii++) {
+               WebKitDOMNode *br = webkit_dom_node_list_item (list, ii);
+
+               webkit_dom_node_insert_before (
+                       webkit_dom_node_get_parent_node (br),
+                       webkit_dom_node_clone_node_with_error (quoted_node, TRUE, NULL),
+                       webkit_dom_node_get_next_sibling (br),
+                       NULL);
+               g_object_unref (br);
+       }
+
+       g_clear_object (&list);
+       g_free (quotation);
+}
+
+static gboolean
+return_pressed_in_empty_line (EEditorPage *editor_page)
+{
+       WebKitDOMNode *node;
+       WebKitDOMRange *range = NULL;
+
+       range = e_editor_dom_get_current_range (editor_page);
+       if (!range)
+               return FALSE;
+
+       node = webkit_dom_range_get_start_container (range, NULL);
+       if (!WEBKIT_DOM_IS_TEXT (node)) {
+               WebKitDOMNode *first_child;
+
+               first_child = webkit_dom_node_get_first_child (node);
+               if (first_child && WEBKIT_DOM_IS_ELEMENT (first_child) &&
+                   element_has_class (WEBKIT_DOM_ELEMENT (first_child), "-x-evo-quoted")) {
+                       WebKitDOMNode *prev_sibling;
+
+                       prev_sibling = webkit_dom_node_get_previous_sibling (node);
+                       if (!prev_sibling) {
+                               gboolean collapsed;
+
+                               collapsed = webkit_dom_range_get_collapsed (range, NULL);
+                               g_clear_object (&range);
+                               return collapsed;
+                       }
+               }
+       }
+
+       g_clear_object (&range);
+
+       return FALSE;
+}
+
+WebKitDOMNode *
+e_editor_dom_get_parent_block_node_from_child (WebKitDOMNode *node)
+{
+       WebKitDOMNode *parent = node;
+
+       if (!WEBKIT_DOM_IS_ELEMENT (parent) ||
+           e_editor_dom_is_selection_position_node (parent))
+               parent = webkit_dom_node_get_parent_node (parent);
+
+       if (element_has_class (WEBKIT_DOM_ELEMENT (parent), "-x-evo-quoted") ||
+           element_has_class (WEBKIT_DOM_ELEMENT (parent), "-x-evo-quote-character") ||
+           element_has_class (WEBKIT_DOM_ELEMENT (parent), "-x-evo-signature") ||
+           element_has_class (WEBKIT_DOM_ELEMENT (parent), "-x-evo-resizable-wrapper") ||
+           WEBKIT_DOM_IS_HTML_ANCHOR_ELEMENT (parent) ||
+           element_has_tag (WEBKIT_DOM_ELEMENT (parent), "b") ||
+           element_has_tag (WEBKIT_DOM_ELEMENT (parent), "i") ||
+           element_has_tag (WEBKIT_DOM_ELEMENT (parent), "u"))
+               parent = webkit_dom_node_get_parent_node (parent);
+
+       if (element_has_class (WEBKIT_DOM_ELEMENT (parent), "-x-evo-quoted") ||
+           element_has_class (WEBKIT_DOM_ELEMENT (parent), "Apple-tab-span") ||
+           element_has_class (WEBKIT_DOM_ELEMENT (parent), "-x-evo-resizable-wrapper"))
+               parent = webkit_dom_node_get_parent_node (parent);
+
+       return parent;
+}
+
+WebKitDOMElement *
+e_editor_dom_wrap_and_quote_element (EEditorPage *editor_page,
+                                    WebKitDOMElement *element)
+{
+       gint citation_level;
+       WebKitDOMElement *tmp_element = element;
+
+       g_return_val_if_fail (WEBKIT_DOM_IS_ELEMENT (element), element);
+
+       if (e_editor_page_get_html_mode (editor_page))
+               return element;
+
+       citation_level = e_editor_dom_get_citation_level (WEBKIT_DOM_NODE (element), FALSE);
+
+       e_editor_dom_remove_quoting_from_element (element);
+       e_editor_dom_remove_wrapping_from_element (element);
+
+       if (WEBKIT_DOM_IS_HTML_PARAGRAPH_ELEMENT (element) &&
+           webkit_dom_element_has_attribute (element, "data-evo-paragraph")) {
+               gint word_wrap_length, length;
+
+               word_wrap_length = e_editor_page_get_word_wrap_length (editor_page);
+               length = word_wrap_length - 2 * citation_level;
+               tmp_element = e_editor_dom_wrap_paragraph_length (
+                       editor_page, element, length);
+       }
+
+       if (citation_level > 0) {
+
+               webkit_dom_node_normalize (WEBKIT_DOM_NODE (tmp_element));
+               e_editor_dom_quote_plain_text_element_after_wrapping (
+                       editor_page, tmp_element, citation_level);
+       }
+
+       return tmp_element;
+}
+
+WebKitDOMElement *
+e_editor_dom_insert_new_line_into_citation (EEditorPage *editor_page,
+                                           const gchar *html_to_insert)
+{
+       WebKitDOMDocument *document;
+       WebKitDOMElement *element, *paragraph = NULL;
+       WebKitDOMNode *last_block;
+       gboolean html_mode = FALSE, ret_val, avoid_editor_call;
+
+       g_return_val_if_fail (E_IS_EDITOR_PAGE (editor_page), NULL);
+
+       document = e_editor_page_get_document (editor_page);
+       html_mode = e_editor_page_get_html_mode (editor_page);
+
+       avoid_editor_call = return_pressed_in_empty_line (editor_page);
+
+       if (avoid_editor_call) {
+               WebKitDOMElement *selection_start_marker;
+               WebKitDOMNode *current_block, *parent, *parent_block, *block_clone;
+
+               e_editor_dom_selection_save (editor_page);
+
+               selection_start_marker = webkit_dom_document_get_element_by_id (
+                       document, "-x-evo-selection-start-marker");
+
+               current_block = e_editor_dom_get_parent_block_node_from_child (
+                       WEBKIT_DOM_NODE (selection_start_marker));
+
+               block_clone = webkit_dom_node_clone_node_with_error (current_block, TRUE, NULL);
+               /* Find selection start marker and restore it after the new line
+                * is inserted */
+               selection_start_marker = webkit_dom_element_query_selector (
+                       WEBKIT_DOM_ELEMENT (block_clone), "#-x-evo-selection-start-marker", NULL);
+
+               /* Find parent node that is immediate child of the BODY */
+               /* Build the same structure of parent nodes of the current block */
+               parent_block = current_block;
+               parent = webkit_dom_node_get_parent_node (parent_block);
+               while (parent && !WEBKIT_DOM_IS_HTML_BODY_ELEMENT (parent)) {
+                       WebKitDOMNode *node;
+
+                       parent_block = parent;
+                       node = webkit_dom_node_clone_node_with_error (parent_block, FALSE, NULL);
+                       webkit_dom_node_append_child (node, block_clone, NULL);
+                       block_clone = node;
+                       parent = webkit_dom_node_get_parent_node (parent_block);
+               }
+
+               paragraph = e_editor_dom_get_paragraph_element (editor_page, -1, 0);
+
+               webkit_dom_node_append_child (
+                       WEBKIT_DOM_NODE (paragraph),
+                       WEBKIT_DOM_NODE (
+                               webkit_dom_document_create_element (document, "BR", NULL)),
+                       NULL);
+
+               /* Insert the selection markers to right place */
+               webkit_dom_node_insert_before (
+                       WEBKIT_DOM_NODE (paragraph),
+                       webkit_dom_node_get_next_sibling (WEBKIT_DOM_NODE (selection_start_marker)),
+                       webkit_dom_node_get_first_child (WEBKIT_DOM_NODE (paragraph)),
+                       NULL);
+               webkit_dom_node_insert_before (
+                       WEBKIT_DOM_NODE (paragraph),
+                       WEBKIT_DOM_NODE (selection_start_marker),
+                       webkit_dom_node_get_first_child (WEBKIT_DOM_NODE (paragraph)),
+                       NULL);
+
+               /* Insert the cloned nodes before the BODY parent node */
+               webkit_dom_node_insert_before (
+                       webkit_dom_node_get_parent_node (parent_block),
+                       block_clone,
+                       parent_block,
+                       NULL);
+
+               /* Insert the new empty paragraph before the BODY parent node */
+               webkit_dom_node_insert_before (
+                       webkit_dom_node_get_parent_node (parent_block),
+                       WEBKIT_DOM_NODE (paragraph),
+                       parent_block,
+                       NULL);
+
+               /* Remove the old block (its copy was moved to the right place) */
+               remove_node (current_block);
+
+               e_editor_dom_selection_restore (editor_page);
+
+               return NULL;
+       } else {
+               e_editor_dom_remove_input_event_listener_from_body (editor_page);
+               e_editor_page_block_selection_changed (editor_page);
+
+               ret_val = e_editor_dom_exec_command (
+                       editor_page, E_CONTENT_EDITOR_COMMAND_INSERT_NEW_LINE_IN_QUOTED_CONTENT, NULL);
+
+               e_editor_page_unblock_selection_changed (editor_page);
+               e_editor_dom_register_input_event_listener_on_body (editor_page);
+
+               if (!ret_val)
+                       return NULL;
+
+               element = webkit_dom_document_query_selector (
+                       document, "body>br", NULL);
+
+               if (!element)
+                       return NULL;
+       }
+
+       last_block = webkit_dom_node_get_previous_sibling (WEBKIT_DOM_NODE (element));
+       while (last_block && e_editor_dom_node_is_citation_node (last_block))
+               last_block = webkit_dom_node_get_last_child (last_block);
+
+       if (last_block) {
+               WebKitDOMNode *last_child;
+
+               if ((last_child = webkit_dom_node_get_last_child (last_block))) {
+                       if (WEBKIT_DOM_IS_ELEMENT (last_child) &&
+                           element_has_class (WEBKIT_DOM_ELEMENT (last_child), "-x-evo-quoted"))
+                               webkit_dom_node_append_child (
+                                       last_block,
+                                       WEBKIT_DOM_NODE (
+                                               webkit_dom_document_create_element (
+                                                       document, "br", NULL)),
+                                       NULL);
+               }
+       }
+
+       if (!html_mode) {
+               WebKitDOMNode *sibling;
+
+               sibling = webkit_dom_node_get_next_sibling (WEBKIT_DOM_NODE (element));
+
+               if (WEBKIT_DOM_IS_HTML_QUOTE_ELEMENT (sibling)) {
+                       WebKitDOMNode *node;
+
+                       node = webkit_dom_node_get_first_child (sibling);
+                       while (node && e_editor_dom_node_is_citation_node (node))
+                               node = webkit_dom_node_get_first_child (node);
+
+                       /* Rewrap and requote nodes that were created by split. */
+                       if (WEBKIT_DOM_IS_ELEMENT (node))
+                               e_editor_dom_wrap_and_quote_element (editor_page, WEBKIT_DOM_ELEMENT (node));
+
+                       if (WEBKIT_DOM_IS_ELEMENT (last_block))
+                               e_editor_dom_wrap_and_quote_element (editor_page, WEBKIT_DOM_ELEMENT 
(last_block));
+
+                       e_editor_dom_force_spell_check_in_viewport (editor_page);
+               }
+       }
+
+       if (html_to_insert && *html_to_insert) {
+               paragraph = e_editor_dom_prepare_paragraph (editor_page, FALSE);
+               webkit_dom_element_set_inner_html (
+                       paragraph, html_to_insert, NULL);
+               if (!webkit_dom_element_query_selector (paragraph, "#-x-evo-selection-start-marker", NULL))
+                       dom_add_selection_markers_into_element_end (
+                               document, paragraph, NULL, NULL);
+       } else
+               paragraph = e_editor_dom_prepare_paragraph (editor_page, TRUE);
+
+       webkit_dom_node_insert_before (
+               webkit_dom_node_get_parent_node (WEBKIT_DOM_NODE (element)),
+               WEBKIT_DOM_NODE (paragraph),
+               WEBKIT_DOM_NODE (element),
+               NULL);
+
+       remove_node (WEBKIT_DOM_NODE (element));
+
+       e_editor_dom_selection_restore (editor_page);
+
+       return paragraph;
+}
+
+/* For purpose of this function see e-mail-formatter-quote.c */
+static void
+put_body_in_citation (WebKitDOMDocument *document)
+{
+       WebKitDOMElement *cite_body = webkit_dom_document_query_selector (
+               document, "span.-x-evo-cite-body", NULL);
+
+       if (cite_body) {
+               WebKitDOMHTMLElement *body = webkit_dom_document_get_body (document);
+               WebKitDOMNode *citation;
+               WebKitDOMNode *sibling;
+
+               citation = WEBKIT_DOM_NODE (
+                       webkit_dom_document_create_element (document, "blockquote", NULL));
+               webkit_dom_element_set_id (WEBKIT_DOM_ELEMENT (citation), "-x-evo-main-cite");
+               webkit_dom_element_set_attribute (WEBKIT_DOM_ELEMENT (citation), "type", "cite", NULL);
+
+               webkit_dom_node_insert_before (
+                       WEBKIT_DOM_NODE (body),
+                       citation,
+                       webkit_dom_node_get_first_child (WEBKIT_DOM_NODE (body)),
+                       NULL);
+
+               while ((sibling = webkit_dom_node_get_next_sibling (citation)))
+                       webkit_dom_node_append_child (citation, sibling, NULL);
+
+               remove_node (WEBKIT_DOM_NODE (cite_body));
+       }
+}
+
+/* For purpose of this function see e-mail-formatter-quote.c */
+static void
+move_elements_to_body (EEditorPage *editor_page)
+{
+       WebKitDOMDocument *document;
+       WebKitDOMHTMLElement *body;
+       WebKitDOMNodeList *list = NULL;
+       gint ii;
+
+       g_return_if_fail (E_IS_EDITOR_PAGE (editor_page));
+
+       document = e_editor_page_get_document (editor_page);
+       body = webkit_dom_document_get_body (document);
+       list = webkit_dom_document_query_selector_all (
+               document, "div[data-headers]", NULL);
+       for (ii = webkit_dom_node_list_get_length (list) - 1; ii >= 0; ii--) {
+               WebKitDOMNode *node = webkit_dom_node_list_item (list, ii);
+
+               webkit_dom_element_remove_attribute (
+                       WEBKIT_DOM_ELEMENT (node), "data-headers");
+               webkit_dom_node_insert_before (
+                       WEBKIT_DOM_NODE (body),
+                       node,
+                       webkit_dom_node_get_first_child (
+                               WEBKIT_DOM_NODE (body)),
+                       NULL);
+
+               g_object_unref (node);
+       }
+       g_clear_object (&list);
+
+       list = webkit_dom_document_query_selector_all (
+               document, "span.-x-evo-to-body[data-credits]", NULL);
+       for (ii = webkit_dom_node_list_get_length (list) - 1; ii >= 0; ii--) {
+               char *credits;
+               WebKitDOMElement *element;
+               WebKitDOMNode *node = webkit_dom_node_list_item (list, ii);
+
+               element = e_editor_dom_get_paragraph_element (editor_page, -1, 0);
+               credits = webkit_dom_element_get_attribute (WEBKIT_DOM_ELEMENT (node), "data-credits");
+               if (credits)
+                       webkit_dom_html_element_set_inner_text (WEBKIT_DOM_HTML_ELEMENT (element), credits, 
NULL);
+               g_free (credits);
+
+               webkit_dom_node_insert_before (
+                       WEBKIT_DOM_NODE (body),
+                       WEBKIT_DOM_NODE (element),
+                       webkit_dom_node_get_first_child (
+                               WEBKIT_DOM_NODE (body)),
+                       NULL);
+
+               remove_node (node);
+               g_object_unref (node);
+       }
+       g_clear_object (&list);
+}
+
+static void
+repair_gmail_blockquotes (WebKitDOMDocument *document)
+{
+       WebKitDOMNodeList *list = NULL;
+       gint ii, length;
+
+       list = webkit_dom_document_query_selector_all (
+               document, "blockquote.gmail_quote", NULL);
+       length = webkit_dom_node_list_get_length (list);
+       for (ii = 0; ii < length; ii++) {
+               WebKitDOMNode *node = webkit_dom_node_list_item (list, ii);
+
+               webkit_dom_element_remove_attribute (WEBKIT_DOM_ELEMENT (node), "class");
+               webkit_dom_element_remove_attribute (WEBKIT_DOM_ELEMENT (node), "style");
+               webkit_dom_element_set_attribute (WEBKIT_DOM_ELEMENT (node), "type", "cite", NULL);
+
+               if (!WEBKIT_DOM_IS_HTML_BR_ELEMENT (webkit_dom_node_get_last_child (node)))
+                       webkit_dom_node_append_child (
+                               node,
+                               WEBKIT_DOM_NODE (
+                                       webkit_dom_document_create_element (
+                                               document, "br", NULL)),
+                               NULL);
+               g_object_unref (node);
+       }
+       g_clear_object (&list);
+}
+
+static void
+remove_thunderbird_signature (WebKitDOMDocument *document)
+{
+       WebKitDOMElement *signature;
+
+       signature = webkit_dom_document_query_selector (
+               document, "pre.moz-signature", NULL);
+       if (signature)
+               remove_node (WEBKIT_DOM_NODE (signature));
+}
+
+void
+e_editor_dom_check_magic_links (EEditorPage *editor_page,
+                               gboolean include_space_by_user)
+{
+       WebKitDOMDocument *document;
+       WebKitDOMNode *node;
+       WebKitDOMRange *range = NULL;
+       gchar *node_text;
+       gchar **urls;
+       gboolean include_space = FALSE;
+       gboolean is_email_address = FALSE;
+       gboolean return_key_pressed;
+       GRegex *regex = NULL;
+       GMatchInfo *match_info;
+       gint start_pos_url, end_pos_url;
+       gboolean has_selection;
+
+       g_return_if_fail (E_IS_EDITOR_PAGE (editor_page));
+
+       if (!e_editor_page_get_magic_links_enabled (editor_page))
+               return;
+
+       return_key_pressed = e_editor_page_get_return_key_pressed (editor_page);
+       document = e_editor_page_get_document (editor_page);
+
+       if (include_space_by_user)
+               include_space = TRUE;
+       else
+               include_space = e_editor_page_get_space_key_pressed (editor_page);
+
+       range = e_editor_dom_get_current_range (editor_page);
+       node = webkit_dom_range_get_end_container (range, NULL);
+       has_selection = !webkit_dom_range_get_collapsed (range, NULL);
+       g_clear_object (&range);
+
+       if (return_key_pressed) {
+               WebKitDOMNode* block;
+
+               block = e_editor_dom_get_parent_block_node_from_child (node);
+               /* Get previous block */
+               if (!(block = webkit_dom_node_get_previous_sibling (block)))
+                       return;
+
+               /* If block is quoted content, get the last block there */
+               while (block && WEBKIT_DOM_IS_HTML_QUOTE_ELEMENT (block))
+                       block = webkit_dom_node_get_last_child (block);
+
+               /* Get the last non-empty node */
+               node = webkit_dom_node_get_last_child (block);
+               if (WEBKIT_DOM_IS_CHARACTER_DATA (node) &&
+                   webkit_dom_character_data_get_length (WEBKIT_DOM_CHARACTER_DATA (node)) == 0)
+                       node = webkit_dom_node_get_previous_sibling (node);
+       } else {
+               e_editor_dom_selection_save (editor_page);
+               if (has_selection) {
+                       WebKitDOMElement *selection_end_marker;
+
+                       selection_end_marker = webkit_dom_document_get_element_by_id (
+                               document, "-x-evo-selection-end-marker");
+
+                       node = webkit_dom_node_get_previous_sibling (
+                               WEBKIT_DOM_NODE (selection_end_marker));
+               }
+       }
+
+       if (!node || WEBKIT_DOM_IS_HTML_ANCHOR_ELEMENT (node))
+               goto out;
+
+       if (!WEBKIT_DOM_IS_TEXT (node)) {
+               if (webkit_dom_node_has_child_nodes (node))
+                       node = webkit_dom_node_get_first_child (node);
+               if (!WEBKIT_DOM_IS_TEXT (node))
+                       goto out;
+       }
+
+       node_text = webkit_dom_text_get_whole_text (WEBKIT_DOM_TEXT (node));
+       if (!(node_text && *node_text) || !g_utf8_validate (node_text, -1, NULL)) {
+               g_free (node_text);
+               goto out;
+       }
+
+       if (strstr (node_text, "@") && !strstr (node_text, "://")) {
+               is_email_address = TRUE;
+               regex = g_regex_new (include_space ? E_MAIL_PATTERN_SPACE : E_MAIL_PATTERN, 0, 0, NULL);
+       } else
+               regex = g_regex_new (include_space ? URL_PATTERN_SPACE : URL_PATTERN, 0, 0, NULL);
+
+       if (!regex) {
+               g_free (node_text);
+               goto out;
+       }
+
+       g_regex_match_all (regex, node_text, G_REGEX_MATCH_NOTEMPTY, &match_info);
+       urls = g_match_info_fetch_all (match_info);
+
+       if (urls) {
+               const gchar *end_of_match = NULL;
+               gchar *final_url, *url_end_raw, *url_text;
+               glong url_start, url_end, url_length;
+               WebKitDOMNode *url_text_node;
+               WebKitDOMElement *anchor;
+
+               g_match_info_fetch_pos (match_info, 0, &start_pos_url, &end_pos_url);
+
+               /* Get start and end position of url in node's text because positions
+                * that we get from g_match_info_fetch_pos are not UTF-8 aware */
+               url_end_raw = g_strndup(node_text, end_pos_url);
+               url_end = g_utf8_strlen (url_end_raw, -1);
+               url_length = g_utf8_strlen (urls[0], -1);
+
+               end_of_match = url_end_raw + end_pos_url - (include_space ? 3 : 2);
+               /* URLs are extremely unlikely to end with any punctuation, so
+                * strip any trailing punctuation off from link and put it after
+                * the link. Do the same for any closing double-quotes as well. */
+               while (end_of_match && end_of_match != url_end_raw && strchr (URL_INVALID_TRAILING_CHARS, 
*end_of_match)) {
+                       url_length--;
+                       url_end--;
+                       end_of_match--;
+               }
+
+               url_start = url_end - url_length;
+
+               webkit_dom_text_split_text (
+                       WEBKIT_DOM_TEXT (node),
+                       include_space ? url_end - 1 : url_end,
+                       NULL);
+
+               webkit_dom_text_split_text (
+                       WEBKIT_DOM_TEXT (node), url_start, NULL);
+               url_text_node = webkit_dom_node_get_next_sibling (node);
+               url_text = webkit_dom_character_data_get_data (
+                       WEBKIT_DOM_CHARACTER_DATA (url_text_node));
+
+               if (g_str_has_prefix (url_text, "www."))
+                       final_url = g_strconcat ("http://"; , url_text, NULL);
+               else if (is_email_address)
+                       final_url = g_strconcat ("mailto:"; , url_text, NULL);
+               else
+                       final_url = g_strdup (url_text);
+
+               /* Create and prepare new anchor element */
+               anchor = webkit_dom_document_create_element (document, "A", NULL);
+
+               webkit_dom_element_set_inner_html (anchor, url_text, NULL);
+
+               webkit_dom_html_anchor_element_set_href (
+                       WEBKIT_DOM_HTML_ANCHOR_ELEMENT (anchor),
+                       final_url);
+
+               /* Insert new anchor element into document */
+               webkit_dom_node_replace_child (
+                       webkit_dom_node_get_parent_node (node),
+                       WEBKIT_DOM_NODE (anchor),
+                       WEBKIT_DOM_NODE (url_text_node),
+                       NULL);
+
+               g_free (url_end_raw);
+               g_free (final_url);
+               g_free (url_text);
+       } else {
+               gboolean appending_to_link = FALSE;
+               gchar *href, *text, *url, *text_to_append = NULL;
+               gint diff;
+               WebKitDOMElement *parent;
+               WebKitDOMNode *prev_sibling;
+
+               parent = webkit_dom_node_get_parent_element (node);
+               prev_sibling = webkit_dom_node_get_previous_sibling (node);
+
+               /* If previous sibling is ANCHOR and actual text node is not beginning with
+                * space => we're appending to link */
+               if (WEBKIT_DOM_IS_HTML_ANCHOR_ELEMENT (prev_sibling)) {
+                       text_to_append = webkit_dom_node_get_text_content (node);
+                       if (text_to_append && *text_to_append &&
+                           !strstr (text_to_append, " ") &&
+                           !(strchr (URL_INVALID_TRAILING_CHARS, *text_to_append) &&
+                             !(*text_to_append == '?' && strlen(text_to_append) > 1)) &&
+                           !g_str_has_prefix (text_to_append, UNICODE_NBSP)) {
+
+                               appending_to_link = TRUE;
+                               parent = WEBKIT_DOM_ELEMENT (prev_sibling);
+                               /* If the node(text) contains the some of unwanted characters
+                                * split it into two nodes and select the right one. */
+                               if (g_str_has_suffix (text_to_append, UNICODE_NBSP) ||
+                                   g_str_has_suffix (text_to_append, UNICODE_ZERO_WIDTH_SPACE)) {
+                                       webkit_dom_text_split_text (
+                                               WEBKIT_DOM_TEXT (node),
+                                               g_utf8_strlen (text_to_append, -1) - 1,
+                                               NULL);
+                                       g_free (text_to_append);
+                                       text_to_append = webkit_dom_node_get_text_content (node);
+                               }
+                       }
+               }
+
+               /* If parent is ANCHOR => we're editing the link */
+               if ((!WEBKIT_DOM_IS_HTML_ANCHOR_ELEMENT (parent) && !appending_to_link) || !text_to_append) {
+                       g_match_info_free (match_info);
+                       g_regex_unref (regex);
+                       g_free (node_text);
+                       g_free (text_to_append);
+                       goto out;
+               }
+
+               /* edit only if href and description are the same */
+               href = webkit_dom_html_anchor_element_get_href (
+                       WEBKIT_DOM_HTML_ANCHOR_ELEMENT (parent));
+
+               if (appending_to_link) {
+                       gchar *inner_text;
+
+                       inner_text =
+                               webkit_dom_html_element_get_inner_text (
+                                       WEBKIT_DOM_HTML_ELEMENT (parent)),
+
+                       text = g_strconcat (inner_text, text_to_append, NULL);
+                       g_free (inner_text);
+               } else
+                       text = webkit_dom_html_element_get_inner_text (
+                                       WEBKIT_DOM_HTML_ELEMENT (parent));
+
+               element_remove_class (parent, "-x-evo-visited-link");
+
+               if (strstr (href, "://") && !strstr (text, "://")) {
+                       url = strstr (href, "://") + 3;
+                       diff = strlen (text) - strlen (url);
+
+                       if (text [strlen (text) - 1] != '/')
+                               diff++;
+
+                       if ((g_strcmp0 (url, text) != 0 && ABS (diff) == 1) || appending_to_link) {
+                               gchar *inner_html, *protocol, *new_href;
+
+                               protocol = g_strndup (href, strstr (href, "://") - href + 3);
+                               inner_html = webkit_dom_element_get_inner_html (parent);
+                               new_href = g_strconcat (
+                                       protocol, inner_html, appending_to_link ? text_to_append : "", NULL);
+
+                               webkit_dom_html_anchor_element_set_href (
+                                       WEBKIT_DOM_HTML_ANCHOR_ELEMENT (parent),
+                                       new_href);
+
+                               if (appending_to_link) {
+                                       webkit_dom_html_element_insert_adjacent_html (
+                                               WEBKIT_DOM_HTML_ELEMENT (parent),
+                                               "beforeend",
+                                               text_to_append,
+                                               NULL);
+
+                                       remove_node (node);
+                               }
+
+                               g_free (new_href);
+                               g_free (protocol);
+                               g_free (inner_html);
+                       }
+               } else {
+                       diff = strlen (text) - strlen (href);
+                       if (text [strlen (text) - 1] != '/')
+                               diff++;
+
+                       if ((g_strcmp0 (href, text) != 0 && ABS (diff) == 1) || appending_to_link) {
+                               gchar *inner_html;
+                               gchar *new_href;
+
+                               inner_html = webkit_dom_element_get_inner_html (parent);
+                               new_href = g_strconcat (
+                                               inner_html,
+                                               appending_to_link ? text_to_append : "",
+                                               NULL);
+
+                               webkit_dom_html_anchor_element_set_href (
+                                       WEBKIT_DOM_HTML_ANCHOR_ELEMENT (parent),
+                                       new_href);
+
+                               if (appending_to_link) {
+                                       webkit_dom_html_element_insert_adjacent_html (
+                                               WEBKIT_DOM_HTML_ELEMENT (parent),
+                                               "beforeend",
+                                               text_to_append,
+                                               NULL);
+
+                                       remove_node (node);
+                               }
+
+                               g_free (new_href);
+                               g_free (inner_html);
+                       }
+
+               }
+               g_free (text_to_append);
+               g_free (text);
+               g_free (href);
+       }
+
+       g_match_info_free (match_info);
+       g_regex_unref (regex);
+       g_free (node_text);
+
+ out:
+       if (!return_key_pressed)
+               e_editor_dom_selection_restore (editor_page);
+}
+
+void
+e_editor_dom_embed_style_sheet (EEditorPage *editor_page,
+                               const gchar *style_sheet_content)
+{
+       WebKitDOMDocument *document;
+       WebKitDOMElement *sheet;
+
+       g_return_if_fail (E_IS_EDITOR_PAGE (editor_page));
+
+       document = e_editor_page_get_document (editor_page);
+
+       e_dom_utils_create_and_add_css_style_sheet (document, "-x-evo-composer-sheet");
+
+       sheet = webkit_dom_document_get_element_by_id (document, "-x-evo-composer-sheet");
+       webkit_dom_element_set_attribute (
+               sheet,
+               "type",
+               "text/css",
+               NULL);
+
+       webkit_dom_element_set_inner_html (sheet, style_sheet_content, NULL);
+}
+
+void
+e_editor_dom_remove_embedded_style_sheet (EEditorPage *editor_page)
+{
+       WebKitDOMDocument *document;
+       WebKitDOMElement *sheet;
+
+       g_return_if_fail (E_IS_EDITOR_PAGE (editor_page));
+
+       document = e_editor_page_get_document (editor_page);
+
+       sheet = webkit_dom_document_get_element_by_id (
+               document, "-x-evo-composer-sheet");
+
+       remove_node (WEBKIT_DOM_NODE (sheet));
+}
+
+static void
+insert_delete_event (EEditorPage *editor_page,
+                     WebKitDOMRange *range)
+{
+       EEditorHistoryEvent *ev;
+       WebKitDOMDocumentFragment *fragment;
+       EEditorUndoRedoManager *manager;
+
+       g_return_if_fail (E_IS_EDITOR_PAGE (editor_page));
+
+       manager = e_editor_page_get_undo_redo_manager (editor_page);
+
+       if (e_editor_undo_redo_manager_is_operation_in_progress (manager))
+               return;
+
+       ev = g_new0 (EEditorHistoryEvent, 1);
+       ev->type = HISTORY_DELETE;
+
+       fragment = webkit_dom_range_clone_contents (range, NULL);
+       ev->data.fragment = fragment;
+
+       e_editor_dom_selection_get_coordinates (editor_page,
+               &ev->before.start.x,
+               &ev->before.start.y,
+               &ev->before.end.x,
+               &ev->before.end.y);
+
+       ev->after.start.x = ev->before.start.x;
+       ev->after.start.y = ev->before.start.y;
+       ev->after.end.x = ev->before.start.x;
+       ev->after.end.y = ev->before.start.y;
+
+       e_editor_undo_redo_manager_insert_history_event (manager, ev);
+
+       ev = g_new0 (EEditorHistoryEvent, 1);
+       ev->type = HISTORY_AND;
+
+       e_editor_undo_redo_manager_insert_history_event (manager, ev);
+}
+
+/* Based on original use_pictograms() from GtkHTML */
+static const gchar *emoticons_chars =
+       /*  0 */ "DO)(|/PQ*!"
+       /* 10 */ "S\0:-\0:\0:-\0"
+       /* 20 */ ":\0:;=-\"\0:;"
+       /* 30 */ "B\"|\0:-'\0:X"
+       /* 40 */ "\0:\0:-\0:\0:-"
+       /* 50 */ "\0:\0:-\0:\0:-"
+       /* 60 */ "\0:\0:\0:-\0:\0"
+       /* 70 */ ":-\0:\0:-\0:\0";
+static gint emoticons_states[] = {
+       /*  0 */  12,  17,  22,  34,  43,  48,  53,  58,  65,  70,
+       /* 10 */  75,   0, -15,  15,   0, -15,   0, -17,  20,   0,
+       /* 20 */ -17,   0, -14, -20, -14,  28,  63,   0, -14, -20,
+       /* 30 */  -3,  63, -18,   0, -12,  38,  41,   0, -12,  -2,
+       /* 40 */   0,  -4,   0, -10,  46,   0, -10,   0, -19,  51,
+       /* 50 */   0, -19,   0, -11,  56,   0, -11,   0, -13,  61,
+       /* 60 */   0, -13,   0,  -6,   0,  68,  -7,   0,  -7,   0,
+       /* 70 */ -16,  73,   0, -16,   0, -21,  78,   0, -21,   0 };
+static const gchar *emoticons_icon_names[] = {
+       "face-angel",
+       "face-angry",
+       "face-cool",
+       "face-crying",
+       "face-devilish",
+       "face-embarrassed",
+       "face-kiss",
+       "face-laugh",           /* not used */
+       "face-monkey",          /* not used */
+       "face-plain",
+       "face-raspberry",
+       "face-sad",
+       "face-sick",
+       "face-smile",
+       "face-smile-big",
+       "face-smirk",
+       "face-surprise",
+       "face-tired",
+       "face-uncertain",
+       "face-wink",
+       "face-worried"
+};
+
+typedef struct _EmoticonLoadContext {
+       EEmoticon *emoticon;
+       EEditorPage *editor_page;
+       gchar *content_type;
+       gchar *name;
+} EmoticonLoadContext;
+
+static EmoticonLoadContext *
+emoticon_load_context_new (EEditorPage *editor_page,
+                           EEmoticon *emoticon)
+{
+       EmoticonLoadContext *load_context;
+
+       load_context = g_slice_new0 (EmoticonLoadContext);
+       load_context->emoticon = emoticon;
+       load_context->editor_page = editor_page;
+
+       return load_context;
+}
+
+static void
+emoticon_load_context_free (EmoticonLoadContext *load_context)
+{
+       g_free (load_context->content_type);
+       g_free (load_context->name);
+       g_slice_free (EmoticonLoadContext, load_context);
+}
+
+static void
+emoticon_insert_span (EEmoticon *emoticon,
+                      EmoticonLoadContext *load_context,
+                      WebKitDOMElement *span)
+{
+       EEditorHistoryEvent *ev = NULL;
+       EEditorUndoRedoManager *manager;
+       EEditorPage *editor_page = load_context->editor_page;
+       gboolean misplaced_selection = FALSE, smiley_written;
+       gchar *node_text = NULL;
+       const gchar *emoticon_start;
+       WebKitDOMDocument *document;
+       WebKitDOMElement *selection_start_marker, *selection_end_marker;
+       WebKitDOMNode *node, *insert_before, *prev_sibling, *next_sibling;
+       WebKitDOMNode *selection_end_marker_parent, *inserted_node;
+       WebKitDOMRange *range = NULL;
+
+       g_return_if_fail (E_IS_EDITOR_PAGE (editor_page));
+
+       document = e_editor_page_get_document (editor_page);
+       smiley_written = e_editor_page_get_is_smiley_written (editor_page);
+       manager = e_editor_page_get_undo_redo_manager (editor_page);
+
+       if (e_editor_dom_selection_is_collapsed (editor_page)) {
+               e_editor_dom_selection_save (editor_page);
+
+               selection_start_marker = webkit_dom_document_get_element_by_id (
+                       document, "-x-evo-selection-start-marker");
+               selection_end_marker = webkit_dom_document_get_element_by_id (
+                       document, "-x-evo-selection-end-marker");
+
+               if (!smiley_written) {
+                       if (!e_editor_undo_redo_manager_is_operation_in_progress (manager)) {
+                               ev = g_new0 (EEditorHistoryEvent, 1);
+                               if (e_editor_page_get_unicode_smileys_enabled (editor_page))
+                                       ev->type = HISTORY_INPUT;
+                               else {
+                                       ev->type = HISTORY_SMILEY;
+
+                                       e_editor_dom_selection_get_coordinates (editor_page,
+                                               &ev->before.start.x,
+                                               &ev->before.start.y,
+                                               &ev->before.end.x,
+                                               &ev->before.end.y);
+                               }
+                       }
+               }
+       } else {
+               WebKitDOMRange *tmp_range = NULL;
+
+               tmp_range = e_editor_dom_get_current_range (editor_page);
+               insert_delete_event (editor_page, tmp_range);
+               g_clear_object (&tmp_range);
+
+               e_editor_dom_exec_command (editor_page, E_CONTENT_EDITOR_COMMAND_DELETE, NULL);
+
+               if (!smiley_written) {
+                       if (!e_editor_undo_redo_manager_is_operation_in_progress (manager)) {
+                               ev = g_new0 (EEditorHistoryEvent, 1);
+
+                               if (e_editor_page_get_unicode_smileys_enabled (editor_page))
+                                       ev->type = HISTORY_INPUT;
+                               else {
+                                       ev->type = HISTORY_SMILEY;
+
+                                       e_editor_dom_selection_get_coordinates (editor_page,
+                                               &ev->before.start.x,
+                                               &ev->before.start.y,
+                                               &ev->before.end.x,
+                                               &ev->before.end.y);
+                               }
+                       }
+               }
+
+               e_editor_dom_selection_save (editor_page);
+
+               selection_start_marker = webkit_dom_document_get_element_by_id (
+                       document, "-x-evo-selection-start-marker");
+               selection_end_marker = webkit_dom_document_get_element_by_id (
+                       document, "-x-evo-selection-end-marker");
+       }
+
+       /* If the selection was not saved, move it into the first child of body */
+       if (!selection_start_marker || !selection_end_marker) {
+               WebKitDOMHTMLElement *body;
+               WebKitDOMNode *child;
+
+               body = webkit_dom_document_get_body (document);
+               child = webkit_dom_node_get_first_child (WEBKIT_DOM_NODE (body));
+
+               dom_add_selection_markers_into_element_start (
+                       document,
+                       WEBKIT_DOM_ELEMENT (child),
+                       &selection_start_marker,
+                       &selection_end_marker);
+
+               if (ev && !e_editor_page_get_unicode_smileys_enabled (editor_page))
+                       e_editor_dom_selection_get_coordinates (editor_page,
+                               &ev->before.start.x,
+                               &ev->before.start.y,
+                               &ev->before.end.x,
+                               &ev->before.end.y);
+       }
+
+       /* Sometimes selection end marker is in body. Move it into next sibling */
+       selection_end_marker_parent = e_editor_dom_get_parent_block_node_from_child (
+               WEBKIT_DOM_NODE (selection_end_marker));
+       if (WEBKIT_DOM_IS_HTML_BODY_ELEMENT (selection_end_marker_parent)) {
+               webkit_dom_node_insert_before (
+                       webkit_dom_node_get_parent_node (
+                               WEBKIT_DOM_NODE (selection_start_marker)),
+                       WEBKIT_DOM_NODE (selection_end_marker),
+                       WEBKIT_DOM_NODE (selection_start_marker),
+                       NULL);
+               if (ev && !e_editor_page_get_unicode_smileys_enabled (editor_page))
+                       e_editor_dom_selection_get_coordinates (editor_page,
+                               &ev->before.start.x,
+                               &ev->before.start.y,
+                               &ev->before.end.x,
+                               &ev->before.end.y);
+       }
+       selection_end_marker_parent = webkit_dom_node_get_parent_node (
+               WEBKIT_DOM_NODE (selection_end_marker));
+
+       /* Determine before what node we have to insert the smiley */
+       insert_before = WEBKIT_DOM_NODE (selection_start_marker);
+       prev_sibling = webkit_dom_node_get_previous_sibling (
+               WEBKIT_DOM_NODE (selection_start_marker));
+       if (prev_sibling) {
+               if (webkit_dom_node_is_same_node (
+                       prev_sibling, WEBKIT_DOM_NODE (selection_end_marker))) {
+                       insert_before = WEBKIT_DOM_NODE (selection_end_marker);
+               } else {
+                       prev_sibling = webkit_dom_node_get_previous_sibling (prev_sibling);
+                       if (prev_sibling &&
+                           webkit_dom_node_is_same_node (
+                               prev_sibling, WEBKIT_DOM_NODE (selection_end_marker))) {
+                               insert_before = WEBKIT_DOM_NODE (selection_end_marker);
+                       }
+               }
+       } else
+               insert_before = WEBKIT_DOM_NODE (selection_start_marker);
+
+       /* Look if selection is misplaced - that means that the selection was
+        * restored before the previously inserted smiley in situations when we
+        * are writing more smileys in a row */
+       next_sibling = webkit_dom_node_get_next_sibling (WEBKIT_DOM_NODE (selection_end_marker));
+       if (next_sibling && WEBKIT_DOM_IS_ELEMENT (next_sibling))
+               if (element_has_class (WEBKIT_DOM_ELEMENT (next_sibling), "-x-evo-smiley-wrapper"))
+                       misplaced_selection = TRUE;
+
+       range = e_editor_dom_get_current_range (editor_page);
+       node = webkit_dom_range_get_end_container (range, NULL);
+       g_clear_object (&range);
+       if (WEBKIT_DOM_IS_TEXT (node))
+               node_text = webkit_dom_text_get_whole_text (WEBKIT_DOM_TEXT (node));
+
+       if (misplaced_selection) {
+               /* Insert smiley and selection markers after it */
+               webkit_dom_node_insert_before (
+                       webkit_dom_node_get_parent_node (insert_before),
+                       WEBKIT_DOM_NODE (selection_start_marker),
+                       webkit_dom_node_get_next_sibling (next_sibling),
+                       NULL);
+               webkit_dom_node_insert_before (
+                       webkit_dom_node_get_parent_node (insert_before),
+                       WEBKIT_DOM_NODE (selection_end_marker),
+                       webkit_dom_node_get_next_sibling (next_sibling),
+                       NULL);
+               if (e_editor_page_get_unicode_smileys_enabled (editor_page))
+                       inserted_node = webkit_dom_node_insert_before (
+                               webkit_dom_node_get_parent_node (insert_before),
+                               webkit_dom_node_get_first_child (WEBKIT_DOM_NODE (span)),
+                               webkit_dom_node_get_next_sibling (next_sibling),
+                               NULL);
+               else
+                       inserted_node = webkit_dom_node_insert_before (
+                               webkit_dom_node_get_parent_node (insert_before),
+                               WEBKIT_DOM_NODE (span),
+                               webkit_dom_node_get_next_sibling (next_sibling),
+                               NULL);
+       } else {
+               if (e_editor_page_get_unicode_smileys_enabled (editor_page))
+                       inserted_node = webkit_dom_node_insert_before (
+                               webkit_dom_node_get_parent_node (insert_before),
+                               webkit_dom_node_get_first_child (WEBKIT_DOM_NODE (span)),
+                               insert_before,
+                               NULL);
+               else
+                       inserted_node = webkit_dom_node_insert_before (
+                               webkit_dom_node_get_parent_node (insert_before),
+                               WEBKIT_DOM_NODE (span),
+                               insert_before,
+                               NULL);
+       }
+
+       if (!e_editor_page_get_unicode_smileys_enabled (editor_page)) {
+               /* &#8203 == UNICODE_ZERO_WIDTH_SPACE */
+               webkit_dom_html_element_insert_adjacent_html (
+                       WEBKIT_DOM_HTML_ELEMENT (span), "afterend", "&#8203;", NULL);
+       }
+
+       if (ev) {
+               WebKitDOMDocumentFragment *fragment;
+               WebKitDOMNode *node;
+
+               fragment = webkit_dom_document_create_document_fragment (document);
+               node = webkit_dom_node_append_child (
+                       WEBKIT_DOM_NODE (fragment),
+                       webkit_dom_node_clone_node_with_error (WEBKIT_DOM_NODE (inserted_node), TRUE, NULL),
+                       NULL);
+               if (e_editor_page_get_unicode_smileys_enabled (editor_page)) {
+                       webkit_dom_node_append_child (
+                               WEBKIT_DOM_NODE (fragment),
+                               WEBKIT_DOM_NODE (
+                                       dom_create_selection_marker (document, TRUE)),
+                               NULL);
+                       webkit_dom_node_append_child (
+                               WEBKIT_DOM_NODE (fragment),
+                               WEBKIT_DOM_NODE (
+                                       dom_create_selection_marker (document, FALSE)),
+                               NULL);
+               } else
+                       webkit_dom_html_element_insert_adjacent_html (
+                               WEBKIT_DOM_HTML_ELEMENT (node), "afterend", "&#8203;", NULL);
+               ev->data.fragment = fragment;
+       }
+
+       /* Remove the text that represents the text version of smiley that was
+        * written into the composer. */
+       if (node_text && smiley_written) {
+               emoticon_start = g_utf8_strrchr (
+                       node_text, -1, g_utf8_get_char (emoticon->text_face));
+               /* Check if the written smiley is really the one that we inserted. */
+               if (emoticon_start) {
+                       /* The written smiley is the same as text version. */
+                       if (g_str_has_prefix (emoticon_start, emoticon->text_face)) {
+                               webkit_dom_character_data_delete_data (
+                                       WEBKIT_DOM_CHARACTER_DATA (node),
+                                       g_utf8_strlen (node_text, -1) - strlen (emoticon_start),
+                                       strlen (emoticon->text_face),
+                                       NULL);
+                       } else if (strstr (emoticon->text_face, "-")) {
+                               gboolean same = TRUE, compensate = FALSE;
+                               gint ii = 0, jj = 0;
+
+                               /* Try to recognize smileys without the dash e.g. :). */
+                               while (emoticon_start[ii] && emoticon->text_face[jj]) {
+                                       if (emoticon_start[ii] == emoticon->text_face[jj]) {
+                                               if (emoticon->text_face[jj+1] && emoticon->text_face[jj+1] == 
'-') {
+                                                       ii++;
+                                                       jj+=2;
+                                                       compensate = TRUE;
+                                               } else {
+                                                       ii++;
+                                                       jj++;
+                                               }
+                                       } else {
+                                               same = FALSE;
+                                               break;
+                                       }
+                               }
+
+                               if (same) {
+                                       webkit_dom_character_data_delete_data (
+                                               WEBKIT_DOM_CHARACTER_DATA (node),
+                                               g_utf8_strlen (node_text, -1) - strlen (emoticon_start),
+                                               ii,
+                                               NULL);
+                               }
+                               /* If we recognize smiley without dash, but we inserted
+                                * the text version with dash we need it insert new
+                                * history input event with that dash. */
+                               if (compensate)
+                                       e_editor_undo_redo_manager_insert_dash_history_event (manager);
+                       }
+               }
+
+               e_editor_page_set_is_smiley_written (editor_page, FALSE);
+       }
+
+       if (ev) {
+               e_editor_dom_selection_get_coordinates (editor_page,
+                       &ev->after.start.x,
+                       &ev->after.start.y,
+                       &ev->after.end.x,
+                       &ev->after.end.y);
+               e_editor_undo_redo_manager_insert_history_event (manager, ev);
+       }
+
+       e_editor_dom_selection_restore (editor_page);
+
+       e_editor_page_emit_content_changed (editor_page);
+
+       g_free (node_text);
+}
+
+static void
+emoticon_read_async_cb (GFile *file,
+                        GAsyncResult *result,
+                        EmoticonLoadContext *load_context)
+{
+       EEmoticon *emoticon = load_context->emoticon;
+       EEditorPage *editor_page = load_context->editor_page;
+       GError *error = NULL;
+       gboolean html_mode;
+       gchar *mime_type;
+       gchar *base64_encoded, *output, *data;
+       GFileInputStream *input_stream;
+       GOutputStream *output_stream;
+       gssize size;
+       WebKitDOMElement *wrapper, *image, *smiley_text;
+       WebKitDOMDocument *document;
+
+       input_stream = g_file_read_finish (file, result, &error);
+       g_return_if_fail (!error && input_stream);
+
+       output_stream = g_memory_output_stream_new (NULL, 0, g_realloc, g_free);
+
+       size = g_output_stream_splice (
+               output_stream, G_INPUT_STREAM (input_stream),
+               G_OUTPUT_STREAM_SPLICE_NONE, NULL, &error);
+
+       if (error || (size == -1))
+               goto out;
+
+       mime_type = g_content_type_get_mime_type (load_context->content_type);
+
+       data = g_memory_output_stream_get_data (G_MEMORY_OUTPUT_STREAM (output_stream));
+       base64_encoded = g_base64_encode ((const guchar *) data, size);
+       output = g_strconcat ("data:", mime_type, ";base64,", base64_encoded, NULL);
+
+       html_mode = e_editor_page_get_html_mode (editor_page);
+       document = e_editor_page_get_document (editor_page);
+
+       /* Insert span with image representation and another one with text
+        * representation and hide/show them dependant on active composer mode */
+       wrapper = webkit_dom_document_create_element (document, "SPAN", NULL);
+       if (html_mode)
+               webkit_dom_element_set_attribute (
+                       wrapper, "class", "-x-evo-smiley-wrapper -x-evo-resizable-wrapper", NULL);
+       else
+               webkit_dom_element_set_attribute (
+                       wrapper, "class", "-x-evo-smiley-wrapper", NULL);
+
+       image = webkit_dom_document_create_element (document, "IMG", NULL);
+       webkit_dom_element_set_attribute (image, "src", output, NULL);
+       webkit_dom_element_set_attribute (image, "data-inline", "", NULL);
+       webkit_dom_element_set_attribute (image, "data-name", load_context->name, NULL);
+       webkit_dom_element_set_attribute (image, "alt", emoticon->text_face, NULL);
+       webkit_dom_element_set_attribute (image, "class", "-x-evo-smiley-img", NULL);
+       webkit_dom_node_append_child (
+               WEBKIT_DOM_NODE (wrapper), WEBKIT_DOM_NODE (image), NULL);
+
+       smiley_text = webkit_dom_document_create_element (document, "SPAN", NULL);
+       webkit_dom_element_set_attribute (smiley_text, "class", "-x-evo-smiley-text", NULL);
+       webkit_dom_html_element_set_inner_text (
+               WEBKIT_DOM_HTML_ELEMENT (smiley_text), emoticon->text_face, NULL);
+       webkit_dom_node_append_child (
+               WEBKIT_DOM_NODE (wrapper), WEBKIT_DOM_NODE (smiley_text), NULL);
+
+       emoticon_insert_span (emoticon, load_context, wrapper);
+
+       g_free (base64_encoded);
+       g_free (output);
+       g_free (mime_type);
+       g_object_unref (output_stream);
+ out:
+       emoticon_load_context_free (load_context);
+}
+
+static void
+emoticon_query_info_async_cb (GFile *file,
+                              GAsyncResult *result,
+                              EmoticonLoadContext *load_context)
+{
+       GError *error = NULL;
+       GFileInfo *info;
+
+       info = g_file_query_info_finish (file, result, &error);
+       g_return_if_fail (!error && info);
+
+       load_context->content_type = g_strdup (g_file_info_get_content_type (info));
+       load_context->name = g_strdup (g_file_info_get_name (info));
+
+       g_file_read_async (
+               file, G_PRIORITY_DEFAULT, NULL,
+               (GAsyncReadyCallback) emoticon_read_async_cb, load_context);
+
+       g_object_unref (info);
+}
+
+void
+e_editor_dom_insert_smiley (EEditorPage *editor_page,
+                           EEmoticon *emoticon)
+{
+       WebKitDOMDocument *document;
+       GFile *file;
+       gchar *filename_uri;
+       EmoticonLoadContext *load_context;
+
+       g_return_if_fail (E_IS_EDITOR_PAGE (editor_page));
+
+       document = e_editor_page_get_document (editor_page);
+
+       if (e_editor_page_get_unicode_smileys_enabled (editor_page)) {
+               WebKitDOMElement *wrapper;
+
+               wrapper = webkit_dom_document_create_element (document, "SPAN", NULL);
+               webkit_dom_html_element_set_inner_text (
+                       WEBKIT_DOM_HTML_ELEMENT (wrapper), emoticon->unicode_character, NULL);
+
+               load_context = emoticon_load_context_new (editor_page, emoticon);
+               emoticon_insert_span (emoticon, load_context, wrapper);
+               emoticon_load_context_free (load_context);
+       } else {
+               filename_uri = e_emoticon_get_uri (emoticon);
+               g_return_if_fail (filename_uri != NULL);
+
+               load_context = emoticon_load_context_new (editor_page, emoticon);
+
+               file = g_file_new_for_uri (filename_uri);
+               g_file_query_info_async (
+                       file,  "standard::*", G_FILE_QUERY_INFO_NONE,
+                       G_PRIORITY_DEFAULT, NULL,
+                       (GAsyncReadyCallback) emoticon_query_info_async_cb, load_context);
+
+               g_free (filename_uri);
+               g_object_unref (file);
+       }
+}
+
+void
+e_editor_dom_insert_smiley_by_name (EEditorPage *editor_page,
+                                   const gchar *name)
+{
+       const EEmoticon *emoticon;
+
+       g_return_if_fail (E_IS_EDITOR_PAGE (editor_page));
+
+       emoticon = e_emoticon_chooser_lookup_emoticon (name);
+       e_editor_page_set_is_smiley_written (editor_page, FALSE);
+       e_editor_dom_insert_smiley (editor_page, (EEmoticon *) emoticon);
+}
+
+void
+e_editor_dom_check_magic_smileys (EEditorPage *editor_page)
+{
+       WebKitDOMNode *node;
+       WebKitDOMRange *range = NULL;
+       gint pos, state, relative, start;
+       gchar *node_text;
+       gunichar uc;
+
+       g_return_if_fail (E_IS_EDITOR_PAGE (editor_page));
+
+       if (!e_editor_page_get_magic_smileys_enabled (editor_page))
+               return;
+
+       range = e_editor_dom_get_current_range (editor_page);
+       node = webkit_dom_range_get_end_container (range, NULL);
+       if (!WEBKIT_DOM_IS_TEXT (node)) {
+               g_clear_object (&range);
+               return;
+       }
+
+       node_text = webkit_dom_text_get_whole_text (WEBKIT_DOM_TEXT (node));
+       if (node_text == NULL) {
+               g_clear_object (&range);
+               return;
+       }
+
+       start = webkit_dom_range_get_end_offset (range, NULL) - 1;
+       pos = start;
+       state = 0;
+       while (pos >= 0) {
+               uc = g_utf8_get_char (g_utf8_offset_to_pointer (node_text, pos));
+               relative = 0;
+               while (emoticons_chars[state + relative]) {
+                       if (emoticons_chars[state + relative] == uc)
+                               break;
+                       relative++;
+               }
+               state = emoticons_states[state + relative];
+               /* 0 .. not found, -n .. found n-th */
+               if (state <= 0)
+                       break;
+               pos--;
+       }
+
+       /* Special case needed to recognize angel and devilish. */
+       if (pos > 0 && state == -14) {
+               uc = g_utf8_get_char (g_utf8_offset_to_pointer (node_text, pos - 1));
+               if (uc == 'O') {
+                       state = -1;
+                       pos--;
+               } else if (uc == '>') {
+                       state = -5;
+                       pos--;
+               }
+       }
+
+       if (state < 0) {
+               const EEmoticon *emoticon;
+
+               if (pos > 0) {
+                       uc = g_utf8_get_char (g_utf8_offset_to_pointer (node_text, pos - 1));
+                       if (!g_unichar_isspace (uc)) {
+                               g_free (node_text);
+                               g_clear_object (&range);
+                               return;
+                       }
+               }
+
+               emoticon = e_emoticon_chooser_lookup_emoticon (
+                       emoticons_icon_names[-state - 1]);
+               e_editor_page_set_is_smiley_written (editor_page, TRUE);
+               e_editor_dom_insert_smiley (editor_page, (EEmoticon *) emoticon);
+       }
+
+       g_clear_object (&range);
+       g_free (node_text);
+}
+
+static void
+dom_set_links_active (WebKitDOMDocument *document,
+                      gboolean active)
+{
+       WebKitDOMElement *style;
+
+       style = webkit_dom_document_get_element_by_id (document, "-x-evo-style-a");
+       if (style)
+               remove_node (WEBKIT_DOM_NODE (style));
+
+       if (!active) {
+               WebKitDOMHTMLHeadElement *head;
+               head = webkit_dom_document_get_head (document);
+
+               style = webkit_dom_document_create_element (document, "STYLE", NULL);
+               webkit_dom_element_set_id (style, "-x-evo-style-a");
+               webkit_dom_element_set_attribute (style, "type", "text/css", NULL);
+               webkit_dom_html_element_set_inner_text (
+                       WEBKIT_DOM_HTML_ELEMENT (style), "a { cursor: text; }", NULL);
+
+               webkit_dom_node_append_child (
+                       WEBKIT_DOM_NODE (head), WEBKIT_DOM_NODE (style), NULL);
+       }
+}
+
+static void
+fix_paragraph_structure_after_pressing_enter_after_smiley (WebKitDOMDocument *document)
+{
+       WebKitDOMElement *element;
+
+       element = webkit_dom_document_query_selector (
+               document, "span.-x-evo-smiley-wrapper > br", NULL);
+
+       if (element) {
+               WebKitDOMNode *parent;
+
+               parent = webkit_dom_node_get_parent_node (WEBKIT_DOM_NODE (element));
+               webkit_dom_element_set_inner_html (
+                       webkit_dom_node_get_parent_element (parent),
+                       UNICODE_ZERO_WIDTH_SPACE,
+                       NULL);
+       }
+}
+
+static gboolean
+fix_paragraph_structure_after_pressing_enter (EEditorPage *editor_page)
+{
+       WebKitDOMDocument *document;
+       WebKitDOMNode *body;
+       WebKitDOMNodeList *list;
+       gboolean prev_is_heading = FALSE;
+       gint ii, length;
+
+       g_return_val_if_fail (E_IS_EDITOR_PAGE (editor_page), FALSE);
+
+       document = e_editor_page_get_document (editor_page);
+       body = WEBKIT_DOM_NODE (webkit_dom_document_get_body (document));
+
+       /* When pressing Enter on empty line in the list (or after heading elements)
+        * WebKit will end that list and inserts <div><br></div> so mark it for wrapping. */
+       list = webkit_dom_document_query_selector_all (
+               document, "body > div > br", NULL);
+
+       length = webkit_dom_node_list_get_length (list);
+       for (ii = 0; ii < length; ii++) {
+               WebKitDOMNode *prev_sibling;
+               WebKitDOMNode *node = webkit_dom_node_get_parent_node (
+                       webkit_dom_node_list_item (list, ii));
+
+               prev_sibling = webkit_dom_node_get_previous_sibling (node);
+               if (prev_sibling && WEBKIT_DOM_IS_HTML_HEADING_ELEMENT (prev_sibling))
+                       prev_is_heading = TRUE;
+
+               webkit_dom_node_replace_child (
+                       body,
+                       WEBKIT_DOM_NODE (e_editor_dom_prepare_paragraph (editor_page, FALSE)),
+                       node,
+                       NULL);
+
+               g_object_unref (node);
+       }
+       g_object_unref (list);
+
+       return prev_is_heading;
+}
+
+static gboolean
+surround_text_with_paragraph_if_needed (EEditorPage *editor_page,
+                                        WebKitDOMNode *node)
+{
+       WebKitDOMNode *next_sibling = webkit_dom_node_get_next_sibling (node);
+       WebKitDOMNode *prev_sibling = webkit_dom_node_get_previous_sibling (node);
+       WebKitDOMNode *parent = webkit_dom_node_get_parent_node (node);
+       WebKitDOMElement *element;
+
+       g_return_val_if_fail (E_IS_EDITOR_PAGE (editor_page), FALSE);
+
+       /* All text in composer has to be written in div elements, so if
+        * we are writing something straight to the body, surround it with
+        * paragraph */
+       if (WEBKIT_DOM_IS_TEXT (node) &&
+           (WEBKIT_DOM_IS_HTML_BODY_ELEMENT (parent) ||
+            WEBKIT_DOM_IS_HTML_TABLE_CELL_ELEMENT (parent))) {
+               element = e_editor_dom_put_node_into_paragraph (editor_page, node, TRUE);
+               if (WEBKIT_DOM_IS_HTML_TABLE_CELL_ELEMENT (parent))
+                       webkit_dom_element_remove_attribute (element, "style");
+
+               if (WEBKIT_DOM_IS_HTML_BR_ELEMENT (next_sibling))
+                       remove_node (next_sibling);
+
+               /* Tab character */
+               if (WEBKIT_DOM_IS_ELEMENT (prev_sibling) &&
+                   element_has_class (WEBKIT_DOM_ELEMENT (prev_sibling), "Apple-tab-span")) {
+                       webkit_dom_node_insert_before (
+                               WEBKIT_DOM_NODE (element),
+                               prev_sibling,
+                               webkit_dom_node_get_first_child (
+                                       WEBKIT_DOM_NODE (element)),
+                               NULL);
+               }
+
+               return TRUE;
+       }
+
+       return FALSE;
+}
+
+static gboolean
+selection_is_in_table (WebKitDOMDocument *document,
+                       gboolean *first_cell,
+                       WebKitDOMNode **table_node)
+{
+       WebKitDOMDOMWindow *dom_window = NULL;
+       WebKitDOMDOMSelection *dom_selection = NULL;
+       WebKitDOMNode *node, *parent;
+       WebKitDOMRange *range = NULL;
+
+       dom_window = webkit_dom_document_get_default_view (document);
+       dom_selection = webkit_dom_dom_window_get_selection (dom_window);
+       g_clear_object (&dom_window);
+
+       if (first_cell != NULL)
+               *first_cell = FALSE;
+
+       if (table_node != NULL)
+               *table_node = NULL;
+
+       if (webkit_dom_dom_selection_get_range_count (dom_selection) < 1) {
+               g_clear_object (&dom_selection);
+               return FALSE;
+       }
+
+       range = webkit_dom_dom_selection_get_range_at (dom_selection, 0, NULL);
+       node = webkit_dom_range_get_start_container (range, NULL);
+       g_clear_object (&dom_selection);
+
+       parent = node;
+       while (parent && !WEBKIT_DOM_IS_HTML_BODY_ELEMENT (parent)) {
+               if (WEBKIT_DOM_IS_HTML_TABLE_CELL_ELEMENT (parent)) {
+                       if (first_cell != NULL) {
+                               if (!webkit_dom_node_get_previous_sibling (parent)) {
+                                       gboolean on_start = TRUE;
+                                       WebKitDOMNode *tmp;
+
+                                       tmp = webkit_dom_node_get_previous_sibling (node);
+                                       if (!tmp && WEBKIT_DOM_IS_TEXT (node))
+                                               on_start = webkit_dom_range_get_start_offset (range, NULL) == 
0;
+                                       else if (tmp)
+                                               on_start = FALSE;
+
+                                       if (on_start) {
+                                               node = webkit_dom_node_get_parent_node (parent);
+                                               if (node && WEBKIT_DOM_HTML_TABLE_ROW_ELEMENT (node))
+                                                       if (!webkit_dom_node_get_previous_sibling (node))
+                                                               *first_cell = TRUE;
+                                       }
+                               }
+                       } else {
+                               g_clear_object (&range);
+                               return TRUE;
+                       }
+               }
+               if (WEBKIT_DOM_IS_HTML_TABLE_ELEMENT (parent)) {
+                       if (table_node != NULL)
+                               *table_node = parent;
+                       else {
+                               g_clear_object (&range);
+                               return TRUE;
+                       }
+               }
+               parent = webkit_dom_node_get_parent_node (parent);
+       }
+
+       g_clear_object (&range);
+
+       if (table_node == NULL)
+               return FALSE;
+
+       return *table_node != NULL;
+}
+
+static gboolean
+jump_to_next_table_cell (WebKitDOMDocument *document,
+                         gboolean jump_back)
+{
+       WebKitDOMDOMWindow *dom_window = NULL;
+       WebKitDOMDOMSelection *dom_selection = NULL;
+       WebKitDOMNode *node, *cell;
+       WebKitDOMRange *range = NULL;
+
+       if (!selection_is_in_table (document, NULL, NULL))
+               return FALSE;
+
+       dom_window = webkit_dom_document_get_default_view (document);
+       dom_selection = webkit_dom_dom_window_get_selection (dom_window);
+       g_clear_object (&dom_window);
+       range = webkit_dom_dom_selection_get_range_at (dom_selection, 0, NULL);
+       node = webkit_dom_range_get_start_container (range, NULL);
+
+       cell = node;
+       while (cell && !WEBKIT_DOM_IS_HTML_TABLE_CELL_ELEMENT (cell)) {
+               cell = webkit_dom_node_get_parent_node (cell);
+       }
+
+       if (!WEBKIT_DOM_IS_HTML_TABLE_CELL_ELEMENT (cell)) {
+               g_clear_object (&range);
+               g_clear_object (&dom_selection);
+               return FALSE;
+       }
+
+       if (jump_back) {
+               /* Get previous cell */
+               node = webkit_dom_node_get_previous_sibling (cell);
+               if (!node || !WEBKIT_DOM_IS_HTML_TABLE_CELL_ELEMENT (node)) {
+                       /* No cell, go one row up. */
+                       node = webkit_dom_node_get_parent_node (cell);
+                       node = webkit_dom_node_get_previous_sibling (node);
+                       if (node && WEBKIT_DOM_IS_HTML_TABLE_ROW_ELEMENT (node)) {
+                               node = webkit_dom_node_get_last_child (node);
+                       } else {
+                               /* No row above, move to the block before table. */
+                               node = webkit_dom_node_get_parent_node (cell);
+                               while (!WEBKIT_DOM_IS_HTML_BODY_ELEMENT (webkit_dom_node_get_parent_node 
(node)))
+                                       node = webkit_dom_node_get_parent_node (node);
+
+                               node = webkit_dom_node_get_previous_sibling (node);
+                       }
+               }
+       } else {
+               /* Get next cell */
+               node = webkit_dom_node_get_next_sibling (cell);
+               if (!node || !WEBKIT_DOM_IS_HTML_TABLE_CELL_ELEMENT (node)) {
+                       /* No cell, go one row below. */
+                       node = webkit_dom_node_get_parent_node (cell);
+                       node = webkit_dom_node_get_next_sibling (node);
+                       if (node && WEBKIT_DOM_IS_HTML_TABLE_ROW_ELEMENT (node)) {
+                               node = webkit_dom_node_get_first_child (node);
+                       } else {
+                               /* No row below, move to the block after table. */
+                               node = webkit_dom_node_get_parent_node (cell);
+                               while (!WEBKIT_DOM_IS_HTML_BODY_ELEMENT (webkit_dom_node_get_parent_node 
(node)))
+                                       node = webkit_dom_node_get_parent_node (node);
+
+                               node = webkit_dom_node_get_next_sibling (node);
+                       }
+               }
+       }
+
+       if (!node) {
+               g_clear_object (&range);
+               g_clear_object (&dom_selection);
+               return FALSE;
+       }
+
+       webkit_dom_range_select_node_contents (range, node, NULL);
+       webkit_dom_range_collapse (range, TRUE, NULL);
+       webkit_dom_dom_selection_remove_all_ranges (dom_selection);
+       webkit_dom_dom_selection_add_range (dom_selection, range);
+       g_clear_object (&range);
+       g_clear_object (&dom_selection);
+
+       return TRUE;
+}
+
+static gboolean
+save_history_before_event_in_table (EEditorPage *editor_page,
+                                    WebKitDOMRange *range)
+{
+       WebKitDOMNode *node;
+       WebKitDOMElement *block;
+
+       g_return_val_if_fail (E_IS_EDITOR_PAGE (editor_page), FALSE);
+
+       node = webkit_dom_range_get_start_container (range, NULL);
+       if (WEBKIT_DOM_IS_HTML_TABLE_CELL_ELEMENT (node))
+               block = WEBKIT_DOM_ELEMENT (node);
+       else
+               block = get_parent_block_element (node);
+
+       if (block && WEBKIT_DOM_IS_HTML_TABLE_CELL_ELEMENT (block)) {
+               EEditorUndoRedoManager *manager;
+               EEditorHistoryEvent *ev;
+
+               ev = g_new0 (EEditorHistoryEvent, 1);
+               ev->type = HISTORY_TABLE_INPUT;
+
+               if (block) {
+                       e_editor_dom_selection_save (editor_page);
+                       ev->data.dom.from = webkit_dom_node_clone_node_with_error (WEBKIT_DOM_NODE (block), 
TRUE, NULL);
+                       e_editor_dom_selection_restore (editor_page);
+               } else
+                       ev->data.dom.from = NULL;
+
+               e_editor_dom_selection_get_coordinates (editor_page,
+                       &ev->before.start.x,
+                       &ev->before.start.y,
+                       &ev->before.end.x,
+                       &ev->before.end.y);
+
+               manager = e_editor_page_get_undo_redo_manager (editor_page);
+               e_editor_undo_redo_manager_insert_history_event (manager, ev);
+
+               return TRUE;
+       }
+
+       return FALSE;
+}
+
+static gboolean
+insert_tabulator (EEditorPage *editor_page)
+{
+       EEditorUndoRedoManager *manager;
+       EEditorHistoryEvent *ev = NULL;
+       gboolean success;
+
+       g_return_val_if_fail (E_IS_EDITOR_PAGE (editor_page), FALSE);
+
+       manager = e_editor_page_get_undo_redo_manager (editor_page);
+
+       if (!e_editor_undo_redo_manager_is_operation_in_progress (manager)) {
+               ev = g_new0 (EEditorHistoryEvent, 1);
+               ev->type = HISTORY_INPUT;
+
+               if (!e_editor_dom_selection_is_collapsed (editor_page)) {
+                       WebKitDOMRange *tmp_range = NULL;
+
+                       tmp_range = e_editor_dom_get_current_range (editor_page);
+                       insert_delete_event (editor_page, tmp_range);
+                       g_clear_object (&tmp_range);
+               }
+
+               e_editor_dom_selection_get_coordinates (editor_page,
+                       &ev->before.start.x,
+                       &ev->before.start.y,
+                       &ev->before.end.x,
+                       &ev->before.end.y);
+
+               ev->before.end.x = ev->before.start.x;
+               ev->before.end.y = ev->before.start.y;
+       }
+
+       success = e_editor_dom_exec_command (editor_page, E_CONTENT_EDITOR_COMMAND_INSERT_TEXT, "\t");
+
+       if (ev) {
+               if (success) {
+                       WebKitDOMDocument *document;
+                       WebKitDOMElement *element;
+                       WebKitDOMDocumentFragment *fragment;
+
+                       document = e_editor_page_get_document (editor_page);
+
+                       e_editor_dom_selection_get_coordinates (editor_page,
+                               &ev->after.start.x,
+                               &ev->after.start.y,
+                               &ev->after.end.x,
+                               &ev->after.end.y);
+
+                       fragment = webkit_dom_document_create_document_fragment (document);
+                       element = webkit_dom_document_create_element (document, "span", NULL);
+                       webkit_dom_html_element_set_inner_text (
+                               WEBKIT_DOM_HTML_ELEMENT (element), "\t", NULL);
+                       webkit_dom_element_set_attribute (
+                               element, "class", "Apple-tab-span", NULL);
+                       webkit_dom_element_set_attribute (
+                               element, "style", "white-space:pre", NULL);
+                       webkit_dom_node_append_child (
+                               WEBKIT_DOM_NODE (fragment), WEBKIT_DOM_NODE (element), NULL);
+                       webkit_dom_node_append_child (
+                               WEBKIT_DOM_NODE (fragment),
+                               WEBKIT_DOM_NODE (dom_create_selection_marker (document, TRUE)),
+                               NULL);
+                       webkit_dom_node_append_child (
+                               WEBKIT_DOM_NODE (fragment),
+                               WEBKIT_DOM_NODE (dom_create_selection_marker (document, FALSE)),
+                               NULL);
+                       ev->data.fragment = fragment;
+
+                       e_editor_undo_redo_manager_insert_history_event (manager, ev);
+                       e_editor_page_emit_content_changed (editor_page);
+               } else {
+                       e_editor_undo_redo_manager_remove_current_history_event (manager);
+                       e_editor_undo_redo_manager_remove_current_history_event (manager);
+                       g_free (ev);
+               }
+       }
+
+       return success;
+}
+
+static void
+body_keypress_event_cb (WebKitDOMElement *element,
+                        WebKitDOMUIEvent *event,
+                        EEditorPage *editor_page)
+{
+       WebKitDOMDocument *document;
+       WebKitDOMDOMWindow *dom_window = NULL;
+       WebKitDOMDOMSelection *dom_selection = NULL;
+       WebKitDOMRange *range = NULL;
+
+       g_return_if_fail (E_IS_EDITOR_PAGE (editor_page));
+
+       document = webkit_dom_node_get_owner_document (WEBKIT_DOM_NODE (element));
+       dom_window = webkit_dom_document_get_default_view (document);
+       dom_selection = webkit_dom_dom_window_get_selection (dom_window);
+       g_clear_object (&dom_window);
+       range = webkit_dom_dom_selection_get_range_at (dom_selection, 0, NULL);
+
+       if (!webkit_dom_range_get_collapsed (range, NULL))
+               insert_delete_event (editor_page, range);
+
+       g_clear_object (&dom_selection);
+       g_clear_object (&range);
+}
+
+void
+e_editor_dom_set_monospace_font_family_on_body (WebKitDOMElement *body,
+                                                gboolean html_mode)
+{
+       /* If copying some content in view, WebKit adds various information about
+        * the content's style (such as color, font size, ..) to the resulting HTML
+        * to correctly apply the style when pasting the content later. The thing
+        * is that in plain text mode the only font allowed is the monospaced one,
+        * but we are forcing it through user style sheet in WebKitWebSettings and
+        * sadly WebKit doesn't count with it, so when the content is pasted,
+        * WebKit wraps it inside SPANs and sets the font-family style on them.
+        * The problem is that when we switch to the HTML mode, the pasted content
+        * will have the monospaced font set. To avoid it we need to set the
+        * font-family style to the body, so WebKit will know about it and will
+        * avoid the described behaviour. */
+       /* When we are deleting a content from the PRE elements we need to turn
+        * this off, otherwise we will end with the same unwanted behavior (the
+        * text between the caret and the end of the element will be wrapped
+        * inside a SPAN element. */
+       if (!html_mode) {
+               element_rename_attribute (WEBKIT_DOM_ELEMENT (body), "data-style", "style");
+               webkit_dom_element_set_attribute (
+                       WEBKIT_DOM_ELEMENT (body),
+                       "style",
+                       "font-family: Monospace;",
+                       NULL);
+       } else {
+               element_rename_attribute (WEBKIT_DOM_ELEMENT (body), "style", "data-style");
+       }
+}
+
+static void
+body_keydown_event_cb (WebKitDOMElement *element,
+                       WebKitDOMUIEvent *event,
+                       EEditorPage *editor_page)
+{
+       gboolean backspace_key, delete_key, space_key, return_key;
+       gboolean shift_key, control_key;
+       glong key_code;
+       WebKitDOMDocument *document;
+       WebKitDOMDOMWindow *dom_window = NULL;
+       WebKitDOMDOMSelection *dom_selection = NULL;
+       WebKitDOMRange *range = NULL;
+
+       g_return_if_fail (E_IS_EDITOR_PAGE (editor_page));
+
+       document = webkit_dom_node_get_owner_document (WEBKIT_DOM_NODE (element));
+
+       key_code = webkit_dom_ui_event_get_key_code (event);
+       delete_key = key_code == HTML_KEY_CODE_DELETE;
+       return_key = key_code == HTML_KEY_CODE_RETURN;
+       backspace_key = key_code == HTML_KEY_CODE_BACKSPACE;
+       space_key = key_code == HTML_KEY_CODE_SPACE;
+
+       if (key_code == HTML_KEY_CODE_CONTROL) {
+               dom_set_links_active (document, TRUE);
+               return;
+       }
+
+       e_editor_page_set_dont_save_history_in_body_input (editor_page, delete_key || backspace_key);
+
+       e_editor_page_set_return_key_pressed (editor_page, return_key);
+       e_editor_page_set_space_key_pressed (editor_page, space_key);
+
+       if (!(delete_key || return_key || backspace_key || space_key))
+               return;
+
+       shift_key = webkit_dom_keyboard_event_get_shift_key (WEBKIT_DOM_KEYBOARD_EVENT (event));
+       control_key = webkit_dom_keyboard_event_get_ctrl_key (WEBKIT_DOM_KEYBOARD_EVENT (event));
+
+       if (key_code == HTML_KEY_CODE_TABULATOR) {
+               if (jump_to_next_table_cell (document, shift_key)) {
+                       webkit_dom_event_prevent_default (WEBKIT_DOM_EVENT (event));
+                       goto out;
+               }
+
+               if (!shift_key && insert_tabulator (editor_page))
+                       webkit_dom_event_prevent_default (WEBKIT_DOM_EVENT (event));
+
+               goto out;
+       }
+
+       if (return_key && e_editor_dom_key_press_event_process_return_key (editor_page)) {
+               webkit_dom_event_prevent_default (WEBKIT_DOM_EVENT (event));
+               goto out;
+       }
+
+       if (backspace_key && e_editor_dom_key_press_event_process_backspace_key (editor_page)) {
+               webkit_dom_event_prevent_default (WEBKIT_DOM_EVENT (event));
+               goto out;
+       }
+
+       if (delete_key || backspace_key) {
+               if (e_editor_dom_key_press_event_process_delete_or_backspace_key (editor_page, key_code, 
control_key, delete_key))
+                       webkit_dom_event_prevent_default (WEBKIT_DOM_EVENT (event));
+               else if (!e_editor_page_get_html_mode (editor_page))
+                       e_editor_dom_set_monospace_font_family_on_body (element, TRUE);
+               goto out;
+       }
+
+       dom_window = webkit_dom_document_get_default_view (document);
+       dom_selection = webkit_dom_dom_window_get_selection (dom_window);
+       g_clear_object (&dom_window);
+       range = webkit_dom_dom_selection_get_range_at (dom_selection, 0, NULL);
+
+       if (save_history_before_event_in_table (editor_page, range))
+               goto out;
+
+       if (return_key) {
+               EEditorHistoryEvent *ev;
+               EEditorUndoRedoManager *manager;
+
+               /* Insert new history event for Return to have the right coordinates.
+                * The fragment will be added later. */
+               ev = g_new0 (EEditorHistoryEvent, 1);
+               ev->type = HISTORY_INPUT;
+
+               manager = e_editor_page_get_undo_redo_manager (editor_page);
+
+               e_editor_dom_selection_get_coordinates (editor_page,
+                       &ev->before.start.x,
+                       &ev->before.start.y,
+                       &ev->before.end.x,
+                       &ev->before.end.y);
+               e_editor_undo_redo_manager_insert_history_event (manager, ev);
+       }
+ out:
+       g_clear_object (&range);
+       g_clear_object (&dom_selection);
+}
+
+static gboolean
+save_history_after_event_in_table (EEditorPage *editor_page)
+{
+       WebKitDOMDocument *document;
+       WebKitDOMDOMWindow *dom_window = NULL;
+       WebKitDOMDOMSelection *dom_selection = NULL;
+       WebKitDOMElement *element;
+       WebKitDOMNode *node;
+       WebKitDOMRange *range = NULL;
+       EEditorHistoryEvent *ev;
+       EEditorUndoRedoManager *manager;
+
+       g_return_val_if_fail (E_IS_EDITOR_PAGE (editor_page), FALSE);
+
+       document = e_editor_page_get_document (editor_page);
+       dom_window = webkit_dom_document_get_default_view (document);
+       dom_selection = webkit_dom_dom_window_get_selection (dom_window);
+       g_clear_object (&dom_window);
+
+       if (!webkit_dom_dom_selection_get_range_count (dom_selection)) {
+               g_clear_object (&dom_selection);
+               return FALSE;
+       }
+       range = webkit_dom_dom_selection_get_range_at (dom_selection, 0, NULL);
+
+       /* Find if writing into table. */
+       node = webkit_dom_range_get_start_container (range, NULL);
+       if (WEBKIT_DOM_IS_HTML_TABLE_CELL_ELEMENT (node))
+               element = WEBKIT_DOM_ELEMENT (node);
+       else
+               element = get_parent_block_element (node);
+
+       g_clear_object (&dom_selection);
+       g_clear_object (&range);
+
+       manager = e_editor_page_get_undo_redo_manager (editor_page);
+       /* If writing to table we have to create different history event. */
+       if (WEBKIT_DOM_IS_HTML_TABLE_CELL_ELEMENT (element)) {
+               ev = e_editor_undo_redo_manager_get_current_history_event (manager);
+               if (ev->type != HISTORY_TABLE_INPUT)
+                       return FALSE;
+       } else
+               return FALSE;
+
+       e_editor_dom_selection_save (editor_page);
+
+       e_editor_dom_selection_get_coordinates (editor_page,
+               &ev->after.start.x,
+               &ev->after.start.y,
+               &ev->after.end.x,
+               &ev->after.end.y);
+
+       ev->data.dom.to = webkit_dom_node_clone_node_with_error (WEBKIT_DOM_NODE (element), TRUE, NULL);
+
+       e_editor_dom_selection_restore (editor_page);
+
+       return TRUE;
+}
+
+static void
+save_history_for_input (EEditorPage *editor_page)
+{
+       WebKitDOMDocument *document;
+       WebKitDOMDocumentFragment *fragment;
+       WebKitDOMDOMWindow *dom_window = NULL;
+       WebKitDOMDOMSelection *dom_selection = NULL;
+       WebKitDOMRange *range = NULL, *range_clone = NULL;
+       WebKitDOMNode *start_container;
+       EEditorHistoryEvent *ev;
+       EEditorUndoRedoManager *manager;
+       glong offset;
+
+       g_return_if_fail (E_IS_EDITOR_PAGE (editor_page));
+
+       document = e_editor_page_get_document (editor_page);
+       manager = e_editor_page_get_undo_redo_manager (editor_page);
+       dom_window = webkit_dom_document_get_default_view (document);
+       dom_selection = webkit_dom_dom_window_get_selection (dom_window);
+       g_clear_object (&dom_window);
+
+       if (!webkit_dom_dom_selection_get_range_count (dom_selection)) {
+               g_clear_object (&dom_selection);
+               return;
+       }
+
+       if (e_editor_page_get_return_key_pressed (editor_page)) {
+               ev = e_editor_undo_redo_manager_get_current_history_event (manager);
+               if (ev->type != HISTORY_INPUT) {
+                       g_clear_object (&dom_selection);
+                       return;
+               }
+       } else {
+               ev = g_new0 (EEditorHistoryEvent, 1);
+               ev->type = HISTORY_INPUT;
+       }
+
+       e_editor_page_block_selection_changed (editor_page);
+
+       e_editor_dom_selection_get_coordinates (editor_page,
+               &ev->after.start.x,
+               &ev->after.start.y,
+               &ev->after.end.x,
+               &ev->after.end.y);
+
+       range = webkit_dom_dom_selection_get_range_at (dom_selection, 0, NULL);
+       range_clone = webkit_dom_range_clone_range (range, NULL);
+       offset = webkit_dom_range_get_start_offset (range_clone, NULL);
+       start_container = webkit_dom_range_get_start_container (range_clone, NULL);
+       if (offset > 0)
+               webkit_dom_range_set_start (
+                       range_clone,
+                       start_container,
+                       offset - 1,
+                       NULL);
+       fragment = webkit_dom_range_clone_contents (range_clone, NULL);
+       /* We have to specially handle Return key press */
+       if (e_editor_page_get_return_key_pressed (editor_page)) {
+               WebKitDOMElement *element_start, *element_end;
+               WebKitDOMNode *parent_start, *parent_end, *node;
+
+               element_start = webkit_dom_document_create_element (document, "span", NULL);
+               webkit_dom_range_surround_contents (range, WEBKIT_DOM_NODE (element_start), NULL);
+               webkit_dom_dom_selection_modify (dom_selection, "move", "left", "character");
+               g_clear_object (&range);
+               range = webkit_dom_dom_selection_get_range_at (dom_selection, 0, NULL);
+               element_end = webkit_dom_document_create_element (document, "span", NULL);
+               webkit_dom_range_surround_contents (range, WEBKIT_DOM_NODE (element_end), NULL);
+
+               parent_start = webkit_dom_node_get_parent_node (WEBKIT_DOM_NODE (element_start));
+               parent_end = webkit_dom_node_get_parent_node (WEBKIT_DOM_NODE (element_end));
+
+               while (parent_start && parent_end && !webkit_dom_node_is_same_node (parent_start, 
parent_end)) {
+                       webkit_dom_node_insert_before (
+                               WEBKIT_DOM_NODE (fragment),
+                               webkit_dom_node_clone_node_with_error (parent_start, FALSE, NULL),
+                               webkit_dom_node_get_first_child (WEBKIT_DOM_NODE (fragment)),
+                               NULL);
+                       parent_start = webkit_dom_node_get_parent_node (parent_start);
+                       parent_end = webkit_dom_node_get_parent_node (parent_end);
+               }
+
+               node = webkit_dom_node_get_first_child (WEBKIT_DOM_NODE (fragment));
+               while (webkit_dom_node_get_next_sibling (node)) {
+                       WebKitDOMNode *last_child;
+
+                       last_child = webkit_dom_node_get_last_child (WEBKIT_DOM_NODE (fragment));
+                       webkit_dom_node_append_child (
+                               webkit_dom_node_get_previous_sibling (last_child),
+                               last_child,
+                               NULL);
+               }
+
+               node = webkit_dom_node_get_last_child (WEBKIT_DOM_NODE (fragment));
+               while (webkit_dom_node_get_last_child (node)) {
+                       node = webkit_dom_node_get_last_child (node);
+               }
+
+               webkit_dom_node_append_child (
+                       node,
+                       WEBKIT_DOM_NODE (
+                               webkit_dom_document_create_element (document, "br", NULL)),
+                       NULL);
+               webkit_dom_node_append_child (
+                       node,
+                       WEBKIT_DOM_NODE (
+                               dom_create_selection_marker (document, TRUE)),
+                       NULL);
+               webkit_dom_node_append_child (
+                       node,
+                       WEBKIT_DOM_NODE (
+                               dom_create_selection_marker (document, FALSE)),
+                       NULL);
+
+               remove_node (WEBKIT_DOM_NODE (element_start));
+               remove_node (WEBKIT_DOM_NODE (element_end));
+
+               g_object_set_data (
+                       G_OBJECT (fragment), "history-return-key", GINT_TO_POINTER (1));
+
+               webkit_dom_dom_selection_modify (dom_selection, "move", "right", "character");
+       } else {
+               webkit_dom_node_append_child (
+                       WEBKIT_DOM_NODE (fragment),
+                       WEBKIT_DOM_NODE (
+                               dom_create_selection_marker (document, TRUE)),
+                       NULL);
+               webkit_dom_node_append_child (
+                       WEBKIT_DOM_NODE (fragment),
+                       WEBKIT_DOM_NODE (
+                               dom_create_selection_marker (document, FALSE)),
+                       NULL);
+       }
+
+       g_clear_object (&dom_selection);
+       g_clear_object (&range);
+       g_clear_object (&range_clone);
+
+       e_editor_page_unblock_selection_changed (editor_page);
+
+       ev->data.fragment = fragment;
+
+       if (!e_editor_page_get_return_key_pressed (editor_page))
+               e_editor_undo_redo_manager_insert_history_event (manager, ev);
+}
+
+typedef struct _TimeoutContext TimeoutContext;
+
+struct _TimeoutContext {
+       EEditorPage *editor_page;
+};
+
+static void
+timeout_context_free (TimeoutContext *context)
+{
+       g_slice_free (TimeoutContext, context);
+}
+
+static gboolean
+force_spell_check_on_timeout (TimeoutContext *context)
+{
+       e_editor_dom_force_spell_check_in_viewport (context->editor_page);
+       e_editor_page_set_spell_check_on_scroll_event_source_id (context->editor_page, 0);
+       return FALSE;
+}
+
+static void
+body_scroll_event_cb (WebKitDOMElement *element,
+                      WebKitDOMEvent *event,
+                      EEditorPage *editor_page)
+{
+       TimeoutContext *context;
+       guint id;
+
+       g_return_if_fail (E_IS_EDITOR_PAGE (editor_page));
+
+       if (!e_editor_page_get_inline_spelling_enabled (editor_page))
+               return;
+
+       context = g_slice_new0 (TimeoutContext);
+       context->editor_page = editor_page;
+
+       id = e_editor_page_get_spell_check_on_scroll_event_source_id (editor_page);
+       if (id > 0)
+               g_source_remove (id);
+
+       id = g_timeout_add_seconds_full (
+               1,
+               G_PRIORITY_DEFAULT,
+               (GSourceFunc)force_spell_check_on_timeout,
+               context,
+               (GDestroyNotify)timeout_context_free);
+
+       e_editor_page_set_spell_check_on_scroll_event_source_id (editor_page, id);
+}
+
+void
+e_editor_dom_body_input_event_process (EEditorPage *editor_page,
+                                      WebKitDOMEvent *event)
+{
+       WebKitDOMDocument *document;
+       WebKitDOMNode *node;
+       WebKitDOMRange *range = NULL;
+       EEditorUndoRedoManager *manager;
+       gboolean do_spell_check = FALSE;
+       gboolean html_mode;
+
+       g_return_if_fail (E_IS_EDITOR_PAGE (editor_page));
+
+       document = e_editor_page_get_document (editor_page);
+       range = e_editor_dom_get_current_range (editor_page);
+
+       manager = e_editor_page_get_undo_redo_manager (editor_page);
+
+       html_mode = e_editor_page_get_html_mode (editor_page);
+       e_editor_page_emit_content_changed (editor_page);
+
+       if (e_editor_undo_redo_manager_is_operation_in_progress (manager)) {
+               e_editor_undo_redo_manager_set_operation_in_progress (manager, FALSE);
+               e_editor_page_set_dont_save_history_in_body_input (editor_page, FALSE);
+               do_spell_check = TRUE;
+               goto out;
+       }
+
+       /* When the Backspace is pressed in a bulleted list item with just one
+        * character left in it, WebKit will create another BR element in the
+        * item. */
+       if (!html_mode) {
+               WebKitDOMElement *element;
+
+               element = webkit_dom_document_query_selector (
+                       document, "ul > li > br + br", NULL);
+
+               if (element)
+                       remove_node (WEBKIT_DOM_NODE (element));
+       }
+
+       if (!save_history_after_event_in_table (editor_page)) {
+               if (!e_editor_page_get_dont_save_history_in_body_input (editor_page))
+                       save_history_for_input (editor_page);
+               else
+                       do_spell_check = TRUE;
+       }
+
+       /* Don't try to look for smileys if we are deleting text. */
+       if (!e_editor_page_get_dont_save_history_in_body_input (editor_page))
+               e_editor_dom_check_magic_smileys (editor_page);
+
+       e_editor_page_set_dont_save_history_in_body_input (editor_page, FALSE);
+
+       if (e_editor_page_get_return_key_pressed (editor_page) ||
+           e_editor_page_get_space_key_pressed (editor_page)) {
+               e_editor_dom_check_magic_links (editor_page, FALSE);
+               if (e_editor_page_get_return_key_pressed (editor_page)) {
+                       if (fix_paragraph_structure_after_pressing_enter (editor_page) &&
+                           html_mode) {
+                               /* When the return is pressed in a H1-6 element, WebKit doesn't
+                                * continue with the same element, but creates normal paragraph,
+                                * so we have to unset the bold font. */
+                               e_editor_undo_redo_manager_set_operation_in_progress (manager, TRUE);
+                               e_editor_dom_selection_set_bold (editor_page, FALSE);
+                               e_editor_undo_redo_manager_set_operation_in_progress (manager, FALSE);
+                       }
+
+                       fix_paragraph_structure_after_pressing_enter_after_smiley (document);
+
+                       do_spell_check = TRUE;
+               }
+       } else {
+               WebKitDOMNode *node;
+
+               node = webkit_dom_range_get_end_container (range, NULL);
+
+               if (surround_text_with_paragraph_if_needed (editor_page, node)) {
+                       WebKitDOMElement *element;
+
+                       element = webkit_dom_document_get_element_by_id (
+                               document, "-x-evo-selection-start-marker");
+                       node = webkit_dom_node_get_previous_sibling (WEBKIT_DOM_NODE (element));
+                       e_editor_dom_selection_restore (editor_page);
+               }
+
+               if (WEBKIT_DOM_IS_TEXT (node)) {
+                       gchar *text;
+
+                       text = webkit_dom_node_get_text_content (node);
+
+                       if (text && *text && *text != ' ' && !g_str_has_prefix (text, UNICODE_NBSP)) {
+                               gboolean valid = FALSE;
+
+                               if (*text == '?' && strlen (text) > 1)
+                                       valid = TRUE;
+                               else if (!strchr (URL_INVALID_TRAILING_CHARS, *text))
+                                       valid = TRUE;
+
+                               if (valid) {
+                                       WebKitDOMNode *prev_sibling;
+
+                                       prev_sibling = webkit_dom_node_get_previous_sibling (node);
+
+                                       if (WEBKIT_DOM_IS_HTML_ANCHOR_ELEMENT (prev_sibling))
+                                               e_editor_dom_check_magic_links (editor_page, FALSE);
+                               }
+                       }
+                       g_free (text);
+               }
+       }
+
+       node = webkit_dom_range_get_end_container (range, NULL);
+
+       /* After toggling monospaced format, we are using UNICODE_ZERO_WIDTH_SPACE
+        * to move caret into right space. When this callback is called it is not
+        * necessary anymore so remove it */
+       if (html_mode) {
+               WebKitDOMElement *parent = webkit_dom_node_get_parent_element (node);
+
+               if (parent) {
+                       WebKitDOMNode *prev_sibling;
+
+                       prev_sibling = webkit_dom_node_get_previous_sibling (
+                               WEBKIT_DOM_NODE (parent));
+
+                       if (prev_sibling && WEBKIT_DOM_IS_TEXT (prev_sibling)) {
+                               gchar *text = webkit_dom_node_get_text_content (
+                                       prev_sibling);
+
+                               if (g_strcmp0 (text, UNICODE_ZERO_WIDTH_SPACE) == 0)
+                                       remove_node (prev_sibling);
+
+                               g_free (text);
+                       }
+
+               }
+       }
+
+       /* If text before caret includes UNICODE_ZERO_WIDTH_SPACE character, remove it */
+       if (WEBKIT_DOM_IS_TEXT (node)) {
+               gchar *text = webkit_dom_character_data_get_data (WEBKIT_DOM_CHARACTER_DATA (node));
+               glong length = webkit_dom_character_data_get_length (WEBKIT_DOM_CHARACTER_DATA (node));
+               WebKitDOMNode *parent;
+
+               /* We have to preserve empty paragraphs with just UNICODE_ZERO_WIDTH_SPACE
+                * character as when we will remove it it will collapse */
+               if (length > 1) {
+                       if (g_str_has_prefix (text, UNICODE_ZERO_WIDTH_SPACE))
+                               webkit_dom_character_data_replace_data (
+                                       WEBKIT_DOM_CHARACTER_DATA (node), 0, 1, "", NULL);
+                       else if (g_str_has_suffix (text, UNICODE_ZERO_WIDTH_SPACE))
+                               webkit_dom_character_data_replace_data (
+                                       WEBKIT_DOM_CHARACTER_DATA (node), length - 1, 1, "", NULL);
+               }
+               g_free (text);
+
+               parent = webkit_dom_node_get_parent_node (node);
+               if (WEBKIT_DOM_IS_HTML_PARAGRAPH_ELEMENT (parent) &&
+                   !webkit_dom_element_has_attribute (WEBKIT_DOM_ELEMENT (parent), "data-evo-paragraph")) {
+                       if (html_mode)
+                               webkit_dom_element_set_attribute (
+                                       WEBKIT_DOM_ELEMENT (parent),
+                                       "data-evo-paragraph",
+                                       "",
+                                       NULL);
+                       else
+                               e_editor_dom_set_paragraph_style (
+                                       editor_page, WEBKIT_DOM_ELEMENT (parent), -1, 0, NULL);
+               }
+
+               /* When new smiley is added we have to use UNICODE_HIDDEN_SPACE to set the
+                * caret position to right place. It is removed when user starts typing. But
+                * when the user will press left arrow he will move the caret into
+                * smiley wrapper. If he will start to write there we have to move the written
+                * text out of the wrapper and move caret to right place */
+               if (WEBKIT_DOM_IS_ELEMENT (parent) &&
+                   element_has_class (WEBKIT_DOM_ELEMENT (parent), "-x-evo-smiley-text")) {
+                       gchar *text;
+                       WebKitDOMCharacterData *data;
+                       WebKitDOMText *text_node;
+
+                       /* Split out the newly written character to its own text node, */
+                       data = WEBKIT_DOM_CHARACTER_DATA (node);
+                       parent = webkit_dom_node_get_parent_node (parent);
+                       text = webkit_dom_character_data_substring_data (
+                               data,
+                               webkit_dom_character_data_get_length (data) - 1,
+                               1,
+                               NULL);
+                       webkit_dom_character_data_delete_data (
+                               data,
+                               webkit_dom_character_data_get_length (data) - 1,
+                               1,
+                               NULL);
+                       text_node = webkit_dom_document_create_text_node (document, text);
+                       g_free (text);
+
+                       webkit_dom_node_insert_before (
+                               webkit_dom_node_get_parent_node (parent),
+                               WEBKIT_DOM_NODE (
+                                       dom_create_selection_marker (document, FALSE)),
+                               webkit_dom_node_get_next_sibling (parent),
+                               NULL);
+                       webkit_dom_node_insert_before (
+                               webkit_dom_node_get_parent_node (parent),
+                               WEBKIT_DOM_NODE (
+                                       dom_create_selection_marker (document, TRUE)),
+                               webkit_dom_node_get_next_sibling (parent),
+                               NULL);
+                       /* Move the text node outside of smiley. */
+                       webkit_dom_node_insert_before (
+                               webkit_dom_node_get_parent_node (parent),
+                               WEBKIT_DOM_NODE (text_node),
+                               webkit_dom_node_get_next_sibling (parent),
+                               NULL);
+                       e_editor_dom_selection_restore (editor_page);
+               }
+       }
+
+       /* Writing into quoted content */
+       if (html_mode) {
+               gint citation_level;
+               WebKitDOMElement *selection_start_marker, *selection_end_marker;
+               WebKitDOMNode *node, *parent;
+
+               node = webkit_dom_range_get_end_container (range, NULL);
+
+               citation_level = e_editor_dom_get_citation_level (node, FALSE);
+               if (citation_level == 0)
+                       goto out;
+
+               selection_start_marker = webkit_dom_document_query_selector (
+                       document, "span#-x-evo-selection-start-marker", NULL);
+               if (selection_start_marker)
+                       goto out;
+
+               e_editor_dom_selection_save (editor_page);
+
+               selection_start_marker = webkit_dom_document_query_selector (
+                       document, "span#-x-evo-selection-start-marker", NULL);
+               selection_end_marker = webkit_dom_document_query_selector (
+                       document, "span#-x-evo-selection-end-marker", NULL);
+               /* If the selection was not saved, move it into the first child of body */
+               if (!selection_start_marker || !selection_end_marker) {
+                       WebKitDOMHTMLElement *body;
+                       WebKitDOMNode *child;
+
+                       body = webkit_dom_document_get_body (document);
+                       child = webkit_dom_node_get_first_child (WEBKIT_DOM_NODE (body));
+
+                       dom_add_selection_markers_into_element_start (
+                               document,
+                               WEBKIT_DOM_ELEMENT (child),
+                               &selection_start_marker,
+                               &selection_end_marker);
+               }
+
+               /* We have to process elements only inside normal block */
+               parent = WEBKIT_DOM_NODE (get_parent_block_element (
+                       WEBKIT_DOM_NODE (selection_start_marker)));
+               if (WEBKIT_DOM_IS_HTML_PRE_ELEMENT (parent)) {
+                       e_editor_dom_selection_restore (editor_page);
+                       goto out;
+               }
+
+               if (selection_start_marker) {
+                       gchar *content;
+                       gint text_length, word_wrap_length, length;
+                       WebKitDOMElement *block;
+                       gboolean remove_quoting = FALSE;
+
+                       word_wrap_length = e_editor_page_get_word_wrap_length (editor_page);
+                       length = word_wrap_length - 2 * citation_level;
+
+                       block = WEBKIT_DOM_ELEMENT (parent);
+                       if (webkit_dom_element_query_selector (
+                               WEBKIT_DOM_ELEMENT (block), ".-x-evo-quoted", NULL)) {
+                               WebKitDOMNode *prev_sibling;
+
+                               prev_sibling = webkit_dom_node_get_previous_sibling (
+                                       WEBKIT_DOM_NODE (selection_end_marker));
+
+                               if (WEBKIT_DOM_IS_ELEMENT (prev_sibling))
+                                       remove_quoting = element_has_class (
+                                               WEBKIT_DOM_ELEMENT (prev_sibling), "-x-evo-quoted");
+                       }
+
+                       content = webkit_dom_node_get_text_content (WEBKIT_DOM_NODE (block));
+                       text_length = g_utf8_strlen (content, -1);
+                       g_free (content);
+
+                       /* Wrap and quote the line */
+                       if (!remove_quoting && text_length >= word_wrap_length) {
+                               e_editor_dom_remove_quoting_from_element (block);
+
+                               block = e_editor_dom_wrap_paragraph_length (editor_page, block, length);
+                               webkit_dom_node_normalize (WEBKIT_DOM_NODE (block));
+                               e_editor_dom_quote_plain_text_element_after_wrapping (
+                                       editor_page, WEBKIT_DOM_ELEMENT (block), citation_level);
+                               selection_start_marker = webkit_dom_document_query_selector (
+                                       document, "span#-x-evo-selection-start-marker", NULL);
+                               if (!selection_start_marker)
+                                       dom_add_selection_markers_into_element_end (
+                                               document,
+                                               WEBKIT_DOM_ELEMENT (block),
+                                               NULL,
+                                               NULL);
+
+                               e_editor_dom_selection_restore (editor_page);
+                               do_spell_check = TRUE;
+                               goto out;
+                       }
+               }
+               e_editor_dom_selection_restore (editor_page);
+       }
+ out:
+       if (do_spell_check)
+               e_editor_dom_force_spell_check_for_current_paragraph (editor_page);
+
+       g_clear_object (&range);
+}
+
+static void
+body_input_event_cb (WebKitDOMElement *element,
+                     WebKitDOMEvent *event,
+                     EEditorPage *editor_page)
+{
+       g_return_if_fail (E_IS_EDITOR_PAGE (editor_page));
+
+       e_editor_dom_body_input_event_process (editor_page, event);
+}
+
+void
+e_editor_dom_remove_input_event_listener_from_body (EEditorPage *editor_page)
+{
+       g_return_if_fail (E_IS_EDITOR_PAGE (editor_page));
+
+       if (!e_editor_page_get_body_input_event_removed (editor_page)) {
+               WebKitDOMDocument *document;
+
+               document = e_editor_page_get_document (editor_page);
+
+               webkit_dom_event_target_remove_event_listener (
+                       WEBKIT_DOM_EVENT_TARGET (webkit_dom_document_get_body (document)),
+                       "input",
+                       G_CALLBACK (body_input_event_cb),
+                       FALSE);
+
+               e_editor_page_set_body_input_event_removed (editor_page, TRUE);
+       }
+}
+
+void
+e_editor_dom_register_input_event_listener_on_body (EEditorPage *editor_page)
+{
+       g_return_if_fail (E_IS_EDITOR_PAGE (editor_page));
+
+       if (e_editor_page_get_body_input_event_removed (editor_page)) {
+               WebKitDOMDocument *document;
+
+               document = e_editor_page_get_document (editor_page);
+
+               webkit_dom_event_target_add_event_listener (
+                       WEBKIT_DOM_EVENT_TARGET (webkit_dom_document_get_body (document)),
+                       "input",
+                       G_CALLBACK (body_input_event_cb),
+                       FALSE,
+                       editor_page);
+
+               e_editor_page_set_body_input_event_removed (editor_page, FALSE);
+       }
+}
+
+static void
+remove_empty_blocks (WebKitDOMDocument *document)
+{
+       gint ii, length;
+       WebKitDOMNodeList *list = NULL;
+
+       list = webkit_dom_document_query_selector_all (
+       document, "blockquote[type=cite] > :empty", NULL);
+
+       length = webkit_dom_node_list_get_length (list);
+       for (ii = 0; ii < length; ii++) {
+               WebKitDOMNode *node = webkit_dom_node_list_item (list, ii);
+               remove_node (node);
+               g_object_unref (node);
+       }
+       g_clear_object (&list);
+
+       list = webkit_dom_document_query_selector_all (
+               document, "blockquote[type=cite]:empty", NULL);
+
+       length = webkit_dom_node_list_get_length (list);
+       for  (ii = 0; ii < length; ii++) {
+               WebKitDOMNode *node = webkit_dom_node_list_item (list, ii);
+               remove_node (node);
+               g_object_unref (node);
+       }
+       g_clear_object (&list);
+}
+
+/* Following two functions are used when deleting the selection inside
+ * the quoted content. The thing is that normally the quote marks are not
+ * selectable by user. But this caused a lof of problems for WebKit when removing
+ * the selection. This will avoid it as when the delete or backspace key is pressed
+ * we will make the quote marks user selectable so they will act as any other text.
+ * On HTML keyup event callback we will make them again non-selectable. */
+void
+e_editor_dom_disable_quote_marks_select (EEditorPage *editor_page)
+{
+       WebKitDOMDocument *document;
+       WebKitDOMHTMLHeadElement *head;
+       WebKitDOMElement *style_element;
+
+       g_return_if_fail (E_IS_EDITOR_PAGE (editor_page));
+
+       document = e_editor_page_get_document (editor_page);
+       head = webkit_dom_document_get_head (document);
+
+       if (!webkit_dom_document_get_element_by_id (document, "-x-evo-quote-style")) {
+               style_element = webkit_dom_document_create_element (document, "style", NULL);
+               webkit_dom_element_set_id (style_element, "-x-evo-quote-style");
+               webkit_dom_element_set_attribute (style_element, "type", "text/css", NULL);
+               webkit_dom_element_set_inner_html (
+                       style_element,
+                       ".-x-evo-quoted { -webkit-user-select: none; }",
+                       NULL);
+               webkit_dom_node_append_child (
+                       WEBKIT_DOM_NODE (head), WEBKIT_DOM_NODE (style_element), NULL);
+       }
+}
+
+static void
+enable_quote_marks_select (WebKitDOMDocument *document)
+{
+       WebKitDOMElement *style_element;
+
+       if ((style_element = webkit_dom_document_get_element_by_id (document, "-x-evo-quote-style")))
+               remove_node (WEBKIT_DOM_NODE (style_element));
+}
+
+void
+e_editor_dom_remove_node_and_parents_if_empty (WebKitDOMNode *node)
+{
+       WebKitDOMNode *parent;
+
+       parent = webkit_dom_node_get_parent_node (WEBKIT_DOM_NODE (node));
+
+       remove_node (WEBKIT_DOM_NODE (node));
+
+       while (parent && !WEBKIT_DOM_IS_HTML_BODY_ELEMENT (parent)) {
+               WebKitDOMNode *tmp;
+
+               tmp = webkit_dom_node_get_parent_node (parent);
+               remove_node_if_empty (parent);
+               parent = tmp;
+       }
+}
+
+void
+e_editor_dom_merge_siblings_if_necessary (EEditorPage *editor_page,
+                                         WebKitDOMDocumentFragment *deleted_content)
+{
+       WebKitDOMDocument *document;
+       WebKitDOMElement *element, *prev_element;
+       WebKitDOMNode *child;
+       WebKitDOMNodeList *list = NULL;
+       gboolean equal_nodes;
+       gint ii, length;
+
+       g_return_if_fail (E_IS_EDITOR_PAGE (editor_page));
+
+       document = e_editor_page_get_document (editor_page);
+
+       if ((element = webkit_dom_document_get_element_by_id (document, "-x-evo-main-cite")))
+               webkit_dom_element_remove_attribute (element, "id");
+
+       element = webkit_dom_document_query_selector (document, "blockquote:not([data-evo-query-skip]) + 
blockquote", NULL);
+       if (!element)
+               goto signature;
+ repeat:
+       child = webkit_dom_node_get_previous_sibling (WEBKIT_DOM_NODE (element));
+       if (WEBKIT_DOM_IS_ELEMENT (child))
+               prev_element = WEBKIT_DOM_ELEMENT (child);
+       else
+               goto signature;
+
+       equal_nodes = webkit_dom_node_is_equal_node (
+               webkit_dom_node_clone_node_with_error (WEBKIT_DOM_NODE (element), FALSE, NULL),
+               webkit_dom_node_clone_node_with_error (WEBKIT_DOM_NODE (prev_element), FALSE, NULL));
+
+       if (equal_nodes) {
+               if (webkit_dom_element_get_child_element_count (element) >
+                   webkit_dom_element_get_child_element_count (prev_element)) {
+                       while ((child = webkit_dom_node_get_first_child (WEBKIT_DOM_NODE (element))))
+                               webkit_dom_node_append_child (
+                                       WEBKIT_DOM_NODE (prev_element), child, NULL);
+                       remove_node (WEBKIT_DOM_NODE (element));
+               } else {
+                       while ((child = webkit_dom_node_get_last_child (WEBKIT_DOM_NODE (prev_element))))
+                               webkit_dom_node_insert_before (
+                                       WEBKIT_DOM_NODE (element),
+                                       child,
+                                       webkit_dom_node_get_first_child (
+                                               WEBKIT_DOM_NODE (element)),
+                                       NULL);
+                       remove_node (WEBKIT_DOM_NODE (prev_element));
+               }
+       } else
+               webkit_dom_element_set_attribute (element, "data-evo-query-skip", "", NULL);
+
+       element = webkit_dom_document_query_selector (document, "blockquote:not([data-evo-query-skip]) + 
blockquote", NULL);
+       if (element)
+               goto repeat;
+
+ signature:
+       list = webkit_dom_document_query_selector_all (
+               document, "blockquote[data-evo-query-skip]", NULL);
+       length = webkit_dom_node_list_get_length (list);
+       for  (ii = 0; ii < length; ii++) {
+               WebKitDOMNode *node = webkit_dom_node_list_item (list, ii);
+               webkit_dom_element_remove_attribute (
+                       WEBKIT_DOM_ELEMENT (node), "data-evo-query-skip");
+               g_object_unref (node);
+       }
+       g_clear_object (&list);
+
+       if (!deleted_content)
+               return;
+
+       /* Replace the corrupted signatures with the right one. */
+       element = webkit_dom_document_query_selector (
+               document, ".-x-evo-signature-wrapper + .-x-evo-signature-wrapper", NULL);
+       if (element) {
+               WebKitDOMElement *right_signature;
+
+               right_signature = webkit_dom_document_fragment_query_selector (
+                       deleted_content, ".-x-evo-signature-wrapper", NULL);
+               remove_node (webkit_dom_node_get_previous_sibling (WEBKIT_DOM_NODE (element)));
+               webkit_dom_node_replace_child (
+                       webkit_dom_node_get_parent_node (WEBKIT_DOM_NODE (element)),
+                       webkit_dom_node_clone_node_with_error (WEBKIT_DOM_NODE (right_signature), TRUE, NULL),
+                       WEBKIT_DOM_NODE (element),
+                       NULL);
+       }
+}
+
+/* This will fix the structure after the situations where some text
+ * inside the quoted content is selected and afterwards deleted with
+ * BackSpace or Delete. */
+void
+e_editor_dom_body_key_up_event_process_backspace_or_delete (EEditorPage *editor_page,
+                                                           gboolean delete)
+{
+       WebKitDOMDocument *document;
+       WebKitDOMElement *selection_start_marker, *selection_end_marker;
+       WebKitDOMNode *parent, *node;
+       gint level;
+
+       g_return_if_fail (E_IS_EDITOR_PAGE (editor_page));
+
+       if (e_editor_page_get_html_mode (editor_page))
+               return;
+
+       document = e_editor_page_get_document (editor_page);
+       e_editor_dom_disable_quote_marks_select (editor_page);
+       /* Remove empty blocks if presented. */
+       remove_empty_blocks (document);
+
+       e_editor_dom_selection_save (editor_page);
+       selection_start_marker = webkit_dom_document_get_element_by_id (
+               document, "-x-evo-selection-start-marker");
+       selection_end_marker = webkit_dom_document_get_element_by_id (
+               document, "-x-evo-selection-end-marker");
+
+       /* If we deleted a selection the caret will be inside the quote marks, fix it. */
+       parent = webkit_dom_node_get_parent_node (WEBKIT_DOM_NODE (selection_start_marker));
+       if (element_has_class (WEBKIT_DOM_ELEMENT (parent), "-x-evo-quote-character")) {
+               webkit_dom_node_insert_before (
+                       webkit_dom_node_get_parent_node (
+                               webkit_dom_node_get_parent_node (parent)),
+                       WEBKIT_DOM_NODE (selection_end_marker),
+                       webkit_dom_node_get_next_sibling (
+                               webkit_dom_node_get_parent_node (parent)),
+                       NULL);
+               webkit_dom_node_insert_before (
+                       webkit_dom_node_get_parent_node (
+                               webkit_dom_node_get_parent_node (parent)),
+                       WEBKIT_DOM_NODE (selection_start_marker),
+                       webkit_dom_node_get_next_sibling (
+                               webkit_dom_node_get_parent_node (parent)),
+                       NULL);
+       }
+
+       /* Under some circumstances we will end with block inside the citation
+        * that has the quote marks removed and we have to reinsert them back. */
+       level = e_editor_dom_get_citation_level (WEBKIT_DOM_NODE (selection_start_marker), FALSE);
+       node = webkit_dom_node_get_next_sibling (WEBKIT_DOM_NODE (selection_end_marker));
+       if (level > 0 && node && !WEBKIT_DOM_IS_HTML_BR_ELEMENT (node)) {
+               WebKitDOMElement *block;
+
+               block = WEBKIT_DOM_ELEMENT (e_editor_dom_get_parent_block_node_from_child (
+                       WEBKIT_DOM_NODE (selection_start_marker)));
+
+               e_editor_dom_remove_quoting_from_element (block);
+               if (webkit_dom_element_has_attribute (block, "data-evo-paragraph")) {
+                       gint length, word_wrap_length;
+
+                       word_wrap_length = e_editor_page_get_word_wrap_length (editor_page);
+                       length =  word_wrap_length - 2 * level;
+                       block = e_editor_dom_wrap_paragraph_length (editor_page, block, length);
+                       webkit_dom_node_normalize (WEBKIT_DOM_NODE (block));
+               }
+               e_editor_dom_quote_plain_text_element_after_wrapping (editor_page, block, level);
+       } else if (level > 0 && !node) {
+               WebKitDOMNode *prev_sibling;
+
+               prev_sibling = webkit_dom_node_get_previous_sibling (
+                       WEBKIT_DOM_NODE (selection_start_marker));
+               if (WEBKIT_DOM_IS_ELEMENT (prev_sibling) &&
+                   element_has_class (WEBKIT_DOM_ELEMENT (prev_sibling), "-x-evo-quoted") &&
+                   !webkit_dom_node_get_previous_sibling (prev_sibling))
+                       webkit_dom_node_append_child (
+                               parent,
+                               WEBKIT_DOM_NODE (webkit_dom_document_create_element (document, "br", NULL)),
+                               NULL);
+       }
+
+       e_editor_dom_merge_siblings_if_necessary (editor_page, NULL);
+
+       e_editor_dom_selection_restore (editor_page);
+       e_editor_dom_force_spell_check_for_current_paragraph (editor_page);
+}
+
+void
+e_editor_dom_body_key_up_event_process_return_key (EEditorPage *editor_page)
+{
+       WebKitDOMDocument *document;
+       WebKitDOMElement *selection_start_marker, *selection_end_marker;
+       WebKitDOMNode *parent;
+
+       g_return_if_fail (E_IS_EDITOR_PAGE (editor_page));
+
+       /* If the return is pressed in an unordered list in plain text mode
+        * the caret is moved to the "*" character before the newly inserted
+        * item. It looks like it is not enough that the item has BR element
+        * inside, but we have to again use the zero width space character
+        * to fix the situation. */
+       if (e_editor_page_get_html_mode (editor_page))
+               return;
+
+       /* FIXME WK2 this is called twice */
+       /* e_editor_dom_selection_save (editor_page); */
+
+       document = e_editor_page_get_document (editor_page);
+       e_editor_dom_selection_save (editor_page);
+
+       selection_start_marker = webkit_dom_document_get_element_by_id (
+               document, "-x-evo-selection-start-marker");
+       selection_end_marker = webkit_dom_document_get_element_by_id (
+               document, "-x-evo-selection-end-marker");
+
+       parent = webkit_dom_node_get_parent_node (WEBKIT_DOM_NODE (selection_start_marker));
+       if (!WEBKIT_DOM_IS_HTML_LI_ELEMENT (parent) ||
+           !WEBKIT_DOM_IS_HTML_U_LIST_ELEMENT (webkit_dom_node_get_parent_node (parent))) {
+               e_editor_dom_selection_restore (editor_page);
+               return;
+       }
+
+       if (!webkit_dom_node_get_previous_sibling (WEBKIT_DOM_NODE (selection_start_marker)) &&
+           (!webkit_dom_node_get_next_sibling (WEBKIT_DOM_NODE (selection_end_marker)) ||
+            WEBKIT_DOM_IS_HTML_BR_ELEMENT (webkit_dom_node_get_next_sibling (WEBKIT_DOM_NODE 
(selection_end_marker)))))
+               webkit_dom_html_element_insert_adjacent_text (
+                       WEBKIT_DOM_HTML_ELEMENT (parent),
+                       "afterbegin",
+                       UNICODE_ZERO_WIDTH_SPACE,
+                       NULL);
+
+       e_editor_dom_selection_restore (editor_page);
+}
+
+static void
+body_keyup_event_cb (WebKitDOMElement *element,
+                     WebKitDOMUIEvent *event,
+                     EEditorPage *editor_page)
+{
+       WebKitDOMDocument *document;
+       glong key_code;
+
+       g_return_if_fail (E_IS_EDITOR_PAGE (editor_page));
+
+       document = webkit_dom_node_get_owner_document (WEBKIT_DOM_NODE (element));
+       if (!e_editor_page_is_composition_in_progress (editor_page))
+               e_editor_dom_register_input_event_listener_on_body (editor_page);
+
+       if (!e_editor_dom_selection_is_collapsed (editor_page))
+               return;
+
+       key_code = webkit_dom_ui_event_get_key_code (event);
+       if (key_code == HTML_KEY_CODE_BACKSPACE || key_code == HTML_KEY_CODE_DELETE) {
+               if (!e_editor_page_get_html_mode (editor_page)) {
+                       WebKitDOMHTMLElement *body;
+
+                       body = webkit_dom_document_get_body (document);
+
+                       e_editor_dom_set_monospace_font_family_on_body (WEBKIT_DOM_ELEMENT (body), FALSE);
+               }
+               e_editor_dom_body_key_up_event_process_backspace_or_delete (editor_page, key_code == 
HTML_KEY_CODE_DELETE);
+
+               /* The content was wrapped and the coordinates
+                * of caret could be changed, so renew them. But
+                * only do that when we are not redoing a history
+                * event, otherwise it would modify the history. */
+               if (e_editor_page_get_renew_history_after_coordinates (editor_page)) {
+                       EEditorHistoryEvent *ev = NULL;
+                       EEditorUndoRedoManager *manager;
+
+                       manager = e_editor_page_get_undo_redo_manager (editor_page);
+                       ev = e_editor_undo_redo_manager_get_current_history_event (manager);
+                       e_editor_dom_selection_get_coordinates (editor_page,
+                               &ev->after.start.x,
+                               &ev->after.start.y,
+                               &ev->after.end.x,
+                               &ev->after.end.y);
+               }
+       } else if (key_code == HTML_KEY_CODE_CONTROL)
+               dom_set_links_active (document, FALSE);
+       else if (key_code == HTML_KEY_CODE_RETURN)
+               e_editor_dom_body_key_up_event_process_return_key (editor_page);
+}
+
+static void
+fix_structure_after_pasting_multiline_content (WebKitDOMNode *node)
+{
+       WebKitDOMNode *first_child, *parent;
+
+       /* When pasting content that does not contain just the
+        * one line text WebKit inserts all the content after the
+        * first line into one element. So we have to take it out
+        * of this element and insert it after that element. */
+       parent = webkit_dom_node_get_parent_node (node);
+       if (WEBKIT_DOM_IS_HTML_BODY_ELEMENT (parent))
+               return;
+       first_child = webkit_dom_node_get_first_child (parent);
+       while (first_child) {
+               WebKitDOMNode *next_child =
+                       webkit_dom_node_get_next_sibling  (first_child);
+               if (webkit_dom_node_has_child_nodes (first_child))
+                       webkit_dom_node_insert_before (
+                               webkit_dom_node_get_parent_node (parent),
+                               first_child,
+                               parent,
+                               NULL);
+               first_child = next_child;
+       }
+}
+
+static gboolean
+delete_hidden_space (EEditorPage *editor_page)
+{
+       WebKitDOMDocument *document;
+       WebKitDOMElement *selection_start_marker, *selection_end_marker, *block;
+       gint citation_level;
+
+       g_return_val_if_fail (E_IS_EDITOR_PAGE (editor_page), FALSE);
+
+       document = e_editor_page_get_document (editor_page);
+
+       selection_start_marker = webkit_dom_document_query_selector (
+               document, "span#-x-evo-selection-start-marker", NULL);
+       selection_end_marker = webkit_dom_document_query_selector (
+               document, "span#-x-evo-selection-end-marker", NULL);
+
+       if (!selection_start_marker || !selection_end_marker)
+               return FALSE;
+
+       block = WEBKIT_DOM_ELEMENT (e_editor_dom_get_parent_block_node_from_child (
+               WEBKIT_DOM_NODE (selection_start_marker)));
+
+       citation_level = e_editor_dom_get_citation_level (
+               WEBKIT_DOM_NODE (selection_start_marker), FALSE);
+
+       if (selection_start_marker && citation_level > 0) {
+               EEditorUndoRedoManager *manager;
+               EEditorHistoryEvent *ev = NULL;
+               WebKitDOMNode *node;
+               WebKitDOMDocumentFragment *fragment;
+
+               manager = e_editor_page_get_undo_redo_manager (editor_page);
+
+               node = webkit_dom_node_get_previous_sibling (WEBKIT_DOM_NODE (selection_start_marker));
+               if (!(WEBKIT_DOM_IS_ELEMENT (node) &&
+                     element_has_class (WEBKIT_DOM_ELEMENT (node), "-x-evo-quoted")))
+                       return FALSE;
+
+               node = webkit_dom_node_get_previous_sibling (node);
+               if (!(WEBKIT_DOM_IS_ELEMENT (node) &&
+                     element_has_class (WEBKIT_DOM_ELEMENT (node), "-x-evo-wrap-br")))
+                       return FALSE;
+
+               node = webkit_dom_node_get_previous_sibling (node);
+               if (!(WEBKIT_DOM_IS_ELEMENT (node) &&
+                     webkit_dom_element_has_attribute (WEBKIT_DOM_ELEMENT (node), "data-hidden-space")))
+                       return FALSE;
+
+               ev = g_new0 (EEditorHistoryEvent, 1);
+               ev->type = HISTORY_DELETE;
+
+               e_editor_dom_selection_get_coordinates (editor_page, &ev->before.start.x, 
&ev->before.start.y, &ev->before.end.x, &ev->before.end.y);
+
+               remove_node (node);
+
+               e_editor_dom_wrap_and_quote_element (editor_page, block);
+
+               fragment = webkit_dom_document_create_document_fragment (document);
+               webkit_dom_node_append_child (
+                       WEBKIT_DOM_NODE (fragment),
+                       WEBKIT_DOM_NODE (
+                               webkit_dom_document_create_text_node (document, " ")),
+                       NULL);
+               ev->data.fragment = fragment;
+
+               e_editor_dom_selection_get_coordinates (editor_page, &ev->after.start.x, &ev->after.start.y, 
&ev->after.end.x, &ev->after.end.y);
+
+               e_editor_undo_redo_manager_insert_history_event (manager, ev);
+
+               return TRUE;
+       }
+
+       return FALSE;
+}
+
+gboolean
+e_editor_dom_move_quoted_block_level_up (EEditorPage *editor_page)
+{
+       WebKitDOMDocument *document;
+       WebKitDOMElement *selection_start_marker, *selection_end_marker;
+       WebKitDOMNode *block;
+       EEditorHistoryEvent *ev = NULL;
+       EEditorUndoRedoManager *manager;
+       gboolean html_mode;
+       gint citation_level, success = FALSE;
+
+       g_return_val_if_fail (E_IS_EDITOR_PAGE (editor_page), FALSE);
+
+       document = e_editor_page_get_document (editor_page);
+       manager = e_editor_page_get_undo_redo_manager (editor_page);
+       html_mode = e_editor_page_get_html_mode (editor_page);
+
+       selection_start_marker = webkit_dom_document_query_selector (
+               document, "span#-x-evo-selection-start-marker", NULL);
+       selection_end_marker = webkit_dom_document_query_selector (
+               document, "span#-x-evo-selection-end-marker", NULL);
+
+       if (!selection_start_marker || !selection_end_marker)
+               return FALSE;
+
+       block = e_editor_dom_get_parent_block_node_from_child (WEBKIT_DOM_NODE (selection_start_marker));
+
+       citation_level = e_editor_dom_get_citation_level (
+               WEBKIT_DOM_NODE (selection_start_marker), FALSE);
+
+       if (selection_start_marker && citation_level > 0) {
+               if (webkit_dom_element_query_selector (
+                       WEBKIT_DOM_ELEMENT (block), ".-x-evo-quoted", NULL)) {
+
+                       WebKitDOMNode *prev_sibling;
+
+                       webkit_dom_node_normalize (block);
+
+                       prev_sibling = webkit_dom_node_get_previous_sibling (
+                               WEBKIT_DOM_NODE (selection_start_marker));
+
+                       if (!prev_sibling) {
+                               WebKitDOMNode *parent;
+
+                               parent = webkit_dom_node_get_parent_node (
+                                       WEBKIT_DOM_NODE (selection_start_marker));
+                               if (WEBKIT_DOM_IS_HTML_ANCHOR_ELEMENT (parent))
+                                       prev_sibling = webkit_dom_node_get_previous_sibling (parent);
+                       }
+
+                       if (WEBKIT_DOM_IS_ELEMENT (prev_sibling))
+                               success = element_has_class (
+                                       WEBKIT_DOM_ELEMENT (prev_sibling), "-x-evo-quoted");
+
+                       /* We really have to be in the beginning of paragraph and
+                        * not on the beginning of some line in the paragraph */
+                       if (success && webkit_dom_node_get_previous_sibling (prev_sibling))
+                               success = FALSE;
+               }
+
+               if (html_mode)
+                       success = WEBKIT_DOM_IS_HTML_QUOTE_ELEMENT (
+                               webkit_dom_node_get_parent_element (block));
+       }
+
+       if (!success)
+               return FALSE;
+
+       if (!e_editor_undo_redo_manager_is_operation_in_progress (manager)) {
+               ev = g_new0 (EEditorHistoryEvent, 1);
+               ev->type = HISTORY_UNQUOTE;
+
+               e_editor_dom_selection_get_coordinates (editor_page, &ev->before.start.x, 
&ev->before.start.y, &ev->before.end.x, &ev->before.end.y);
+               ev->data.dom.from = webkit_dom_node_clone_node_with_error (block, TRUE, NULL);
+       }
+
+       if (citation_level == 1) {
+               gchar *inner_html;
+               WebKitDOMElement *paragraph, *element;
+
+               inner_html = webkit_dom_element_get_inner_html (WEBKIT_DOM_ELEMENT (block));
+               webkit_dom_element_set_id (WEBKIT_DOM_ELEMENT (block), "-x-evo-to-remove");
+
+               paragraph = e_editor_dom_insert_new_line_into_citation (editor_page, inner_html);
+               g_free (inner_html);
+
+               if (paragraph) {
+                       webkit_dom_node_insert_before (
+                               WEBKIT_DOM_NODE (paragraph),
+                               WEBKIT_DOM_NODE (selection_start_marker),
+                               webkit_dom_node_get_first_child (
+                                       WEBKIT_DOM_NODE (paragraph)),
+                               NULL);
+                       webkit_dom_node_insert_before (
+                               WEBKIT_DOM_NODE (paragraph),
+                               WEBKIT_DOM_NODE (selection_end_marker),
+                               webkit_dom_node_get_first_child (
+                                       WEBKIT_DOM_NODE (paragraph)),
+                               NULL);
+
+                       e_editor_dom_remove_quoting_from_element (paragraph);
+                       e_editor_dom_remove_wrapping_from_element (paragraph);
+
+                       /* Moving PRE block from citation to body */
+                       if (WEBKIT_DOM_IS_HTML_PRE_ELEMENT (block)) {
+                               WebKitDOMElement *pre;
+                               WebKitDOMNode *child;
+
+                               pre = webkit_dom_document_create_element (document, "pre", NULL);
+                               webkit_dom_node_insert_before (
+                                       webkit_dom_node_get_parent_node (
+                                               WEBKIT_DOM_NODE (paragraph)),
+                                       WEBKIT_DOM_NODE (pre),
+                                       WEBKIT_DOM_NODE (paragraph),
+                                       NULL);
+
+                               while ((child = webkit_dom_node_get_first_child (WEBKIT_DOM_NODE 
(paragraph))))
+                                       webkit_dom_node_append_child (WEBKIT_DOM_NODE (pre), child, NULL);
+
+                               remove_node (WEBKIT_DOM_NODE (paragraph));
+                               paragraph = pre;
+                       }
+               }
+
+               if (block)
+                       remove_node (block);
+
+               while ((element = webkit_dom_document_get_element_by_id (document, "-x-evo-to-remove")))
+                       remove_node (WEBKIT_DOM_NODE (element));
+
+               if (paragraph)
+                       remove_node_if_empty (
+                               webkit_dom_node_get_next_sibling (
+                                       WEBKIT_DOM_NODE (paragraph)));
+       }
+
+       if (citation_level > 1) {
+               WebKitDOMNode *parent;
+
+               if (html_mode) {
+                       webkit_dom_node_insert_before (
+                               block,
+                               WEBKIT_DOM_NODE (selection_start_marker),
+                               webkit_dom_node_get_first_child (block),
+                               NULL);
+                       webkit_dom_node_insert_before (
+                               block,
+                               WEBKIT_DOM_NODE (selection_end_marker),
+                               webkit_dom_node_get_first_child (block),
+                               NULL);
+
+               }
+
+               e_editor_dom_remove_quoting_from_element (WEBKIT_DOM_ELEMENT (block));
+               e_editor_dom_remove_wrapping_from_element (WEBKIT_DOM_ELEMENT (block));
+
+               parent = webkit_dom_node_get_parent_node (block);
+
+               if (!webkit_dom_node_get_previous_sibling (block)) {
+                       /* Currect block is in the beginning of citation, just move it
+                        * before the citation where already is */
+                       webkit_dom_node_insert_before (
+                               webkit_dom_node_get_parent_node (parent),
+                               block,
+                               parent,
+                               NULL);
+               } else if (!webkit_dom_node_get_next_sibling (block)) {
+                       /* Currect block is at the end of the citation, just move it
+                        * after the citation where already is */
+                       webkit_dom_node_insert_before (
+                               webkit_dom_node_get_parent_node (parent),
+                               block,
+                               webkit_dom_node_get_next_sibling (parent),
+                               NULL);
+               } else {
+                       /* Current block is somewhere in the middle of the citation
+                        * so we need to split the citation and insert the block into
+                        * the citation that is one level lower */
+                       WebKitDOMNode *clone, *child;
+
+                       clone = webkit_dom_node_clone_node_with_error (parent, FALSE, NULL);
+
+                       /* Move nodes that are after the currect block into the
+                        * new blockquote */
+                       child = webkit_dom_node_get_next_sibling (block);
+                       while (child) {
+                               WebKitDOMNode *next = webkit_dom_node_get_next_sibling (child);
+                               webkit_dom_node_append_child (clone, child, NULL);
+                               child = next;
+                       }
+
+                       clone = webkit_dom_node_insert_before (
+                               webkit_dom_node_get_parent_node (parent),
+                               clone,
+                               webkit_dom_node_get_next_sibling (parent),
+                               NULL);
+
+                       webkit_dom_node_insert_before (
+                               webkit_dom_node_get_parent_node (parent),
+                               block,
+                               clone,
+                               NULL);
+               }
+
+               e_editor_dom_wrap_and_quote_element (editor_page, WEBKIT_DOM_ELEMENT (block));
+       }
+
+       if (ev) {
+               e_editor_dom_selection_get_coordinates (editor_page,
+                       &ev->after.start.x,
+                       &ev->after.start.y,
+                       &ev->after.end.x,
+                       &ev->after.end.y);
+               e_editor_undo_redo_manager_insert_history_event (manager, ev);
+       }
+
+       return success;
+}
+
+static gboolean
+prevent_from_deleting_last_element_in_body (WebKitDOMDocument *document)
+{
+       gboolean ret_val = FALSE;
+       WebKitDOMHTMLElement *body;
+       WebKitDOMNode *node;
+
+       body = webkit_dom_document_get_body (document);
+
+       node = webkit_dom_node_get_first_child (WEBKIT_DOM_NODE (body));
+       if (!node || !webkit_dom_node_get_next_sibling (node)) {
+               gchar *content;
+
+               content = webkit_dom_node_get_text_content (WEBKIT_DOM_NODE (body));
+
+               if (!content || !*content)
+                       ret_val = TRUE;
+
+               g_free (content);
+
+               if (webkit_dom_element_query_selector (WEBKIT_DOM_ELEMENT (body), "img", NULL))
+                       ret_val = FALSE;
+       }
+
+       return ret_val;
+}
+
+static void
+insert_quote_symbols (WebKitDOMDocument *document,
+                      WebKitDOMHTMLElement *element,
+                      gint quote_level)
+{
+       gchar *quotation;
+       WebKitDOMElement *quote_element;
+
+       if (!WEBKIT_DOM_IS_ELEMENT (element))
+               return;
+
+       quotation = get_quotation_for_level (quote_level);
+
+       quote_element = webkit_dom_document_create_element (document, "span", NULL);
+       element_add_class (quote_element, "-x-evo-quoted");
+
+       webkit_dom_element_set_inner_html (quote_element, quotation, NULL);
+       webkit_dom_node_insert_before (
+               WEBKIT_DOM_NODE (element),
+               WEBKIT_DOM_NODE (quote_element),
+               webkit_dom_node_get_first_child (WEBKIT_DOM_NODE (element)),
+               NULL);
+
+       g_free (quotation);
+}
+
+static void
+quote_node (WebKitDOMDocument *document,
+            WebKitDOMNode *node,
+            gint quote_level)
+{
+       WebKitDOMNode *parent, *next_sibling;
+
+       /* Don't quote when we are not in citation */
+       if (quote_level == 0)
+               return;
+
+       if (WEBKIT_DOM_IS_COMMENT (node))
+               return;
+
+       if (WEBKIT_DOM_IS_ELEMENT (node)) {
+               insert_quote_symbols (document, WEBKIT_DOM_HTML_ELEMENT (node), quote_level);
+               return;
+       }
+
+       next_sibling = webkit_dom_node_get_next_sibling (node);
+
+       /* Skip the BR between first blockquote and pre */
+       if (quote_level == 1 && next_sibling && WEBKIT_DOM_IS_HTML_PRE_ELEMENT (next_sibling))
+               return;
+
+       parent = webkit_dom_node_get_parent_node (node);
+
+       insert_quote_symbols (
+               document, WEBKIT_DOM_HTML_ELEMENT (parent), quote_level);
+}
+
+static void
+insert_quote_symbols_before_node (WebKitDOMDocument *document,
+                                  WebKitDOMNode *node,
+                                  gint quote_level,
+                                  gboolean is_html_node)
+{
+       gboolean skip, wrap_br;
+       gchar *quotation;
+       WebKitDOMElement *element;
+
+       quotation = get_quotation_for_level (quote_level);
+       element = webkit_dom_document_create_element (document, "SPAN", NULL);
+       element_add_class (element, "-x-evo-quoted");
+       webkit_dom_element_set_inner_html (element, quotation, NULL);
+
+       /* Don't insert temporary BR before BR that is used for wrapping */
+       skip = WEBKIT_DOM_IS_HTML_BR_ELEMENT (node);
+       wrap_br = element_has_class (WEBKIT_DOM_ELEMENT (node), "-x-evo-wrap-br");
+       skip = skip && wrap_br;
+
+       if (is_html_node && !skip) {
+               WebKitDOMElement *new_br;
+
+               new_br = webkit_dom_document_create_element (document, "br", NULL);
+               element_add_class (new_br, "-x-evo-temp-br");
+
+               webkit_dom_node_insert_before (
+                       webkit_dom_node_get_parent_node (node),
+                       WEBKIT_DOM_NODE (new_br),
+                       node,
+                       NULL);
+       }
+
+       webkit_dom_node_insert_before (
+               webkit_dom_node_get_parent_node (node),
+               WEBKIT_DOM_NODE (element),
+               node,
+               NULL);
+
+       if (is_html_node && !wrap_br)
+               remove_node (node);
+
+       g_free (quotation);
+}
+
+static gboolean
+check_if_suppress_next_node (WebKitDOMNode *node)
+{
+       if (!node)
+               return FALSE;
+
+       if (node && WEBKIT_DOM_IS_ELEMENT (node))
+               if (e_editor_dom_is_selection_position_node (node))
+                       if (!webkit_dom_node_get_previous_sibling (node))
+                               return FALSE;
+
+       return TRUE;
+}
+
+static void
+quote_br_node (WebKitDOMNode *node,
+               gint quote_level)
+{
+       gchar *quotation, *content;
+
+       quotation = get_quotation_for_level (quote_level);
+
+       content = g_strconcat (
+               "<span class=\"-x-evo-quoted\">",
+               quotation,
+               "</span><br class=\"-x-evo-temp-br\">",
+               NULL);
+
+       webkit_dom_element_set_outer_html (
+               WEBKIT_DOM_ELEMENT (node),
+               content,
+               NULL);
+
+       g_free (content);
+       g_free (quotation);
+}
+
+static void
+quote_plain_text_recursive (WebKitDOMDocument *document,
+                            WebKitDOMNode *block,
+                            WebKitDOMNode *start_node,
+                            gint quote_level)
+{
+       gboolean skip_node = FALSE;
+       gboolean move_next = FALSE;
+       gboolean suppress_next = FALSE;
+       gboolean is_html_node = FALSE;
+       gboolean next = FALSE;
+       WebKitDOMNode *node, *next_sibling, *prev_sibling;
+
+       node = webkit_dom_node_get_first_child (block);
+
+       while (node) {
+               gchar *text_content;
+
+               skip_node = FALSE;
+               move_next = FALSE;
+               is_html_node = FALSE;
+
+               if (WEBKIT_DOM_IS_COMMENT (node) ||
+                   WEBKIT_DOM_IS_HTML_META_ELEMENT (node) ||
+                   WEBKIT_DOM_IS_HTML_STYLE_ELEMENT (node) ||
+                   WEBKIT_DOM_IS_HTML_IMAGE_ELEMENT (node)) {
+
+                       move_next = TRUE;
+                       goto next_node;
+               }
+
+               prev_sibling = webkit_dom_node_get_previous_sibling (node);
+               next_sibling = webkit_dom_node_get_next_sibling (node);
+
+               if (WEBKIT_DOM_IS_TEXT (node)) {
+                       /* Start quoting after we are in blockquote */
+                       if (quote_level > 0 && !suppress_next) {
+                               /* When quoting text node, we are wrappering it and
+                                * afterwards replacing it with that wrapper, thus asking
+                                * for next_sibling after quoting will return NULL bacause
+                                * that node don't exist anymore */
+                               quote_node (document, node, quote_level);
+                               node = next_sibling;
+                               skip_node = TRUE;
+                       }
+
+                       goto next_node;
+               }
+
+               if (!(WEBKIT_DOM_IS_ELEMENT (node) || WEBKIT_DOM_IS_HTML_ELEMENT (node)))
+                       goto next_node;
+
+               if (e_editor_dom_is_selection_position_node (node)) {
+                       /* If there is collapsed selection in the beginning of line
+                        * we cannot suppress first text that is after the end of
+                        * selection */
+                       suppress_next = check_if_suppress_next_node (prev_sibling);
+                       if (suppress_next)
+                               next = FALSE;
+                       move_next = TRUE;
+                       goto next_node;
+               }
+
+               if (!WEBKIT_DOM_IS_HTML_ANCHOR_ELEMENT (node) &&
+                   webkit_dom_element_get_child_element_count (WEBKIT_DOM_ELEMENT (node)) != 0)
+                       goto with_children;
+
+               /* Even in plain text mode we can have some basic html element
+                * like anchor and others. When Forwaring e-mail as Quoted EMFormat
+                * generates header that contatains <b> tags (bold font).
+                * We have to treat these elements separately to avoid
+                * modifications of theirs inner texts */
+               is_html_node =
+                       WEBKIT_DOM_IS_HTML_ANCHOR_ELEMENT (node) ||
+                       element_has_tag (WEBKIT_DOM_ELEMENT (node), "b") ||
+                       element_has_tag (WEBKIT_DOM_ELEMENT (node), "i") ||
+                       element_has_tag (WEBKIT_DOM_ELEMENT (node), "u") ||
+                       element_has_class (WEBKIT_DOM_ELEMENT (node), "Apple-tab-span");
+
+               if (is_html_node) {
+                       gboolean wrap_br;
+
+                       wrap_br =
+                               prev_sibling &&
+                               WEBKIT_DOM_IS_HTML_BR_ELEMENT (prev_sibling) &&
+                               element_has_class (
+                                       WEBKIT_DOM_ELEMENT (prev_sibling), "-x-evo-wrap-br");
+
+                       if (!prev_sibling || wrap_br) {
+                               insert_quote_symbols_before_node (
+                                       document, node, quote_level, FALSE);
+                               if (!prev_sibling && next_sibling && WEBKIT_DOM_IS_TEXT (next_sibling))
+                                       suppress_next = TRUE;
+                       }
+
+                       if (WEBKIT_DOM_IS_HTML_BR_ELEMENT (prev_sibling) && !wrap_br)
+                               insert_quote_symbols_before_node (
+                                       document, prev_sibling, quote_level, TRUE);
+
+                       move_next = TRUE;
+                       goto next_node;
+               }
+
+               /* If element doesn't have children, we can quote it */
+               if (e_editor_dom_node_is_citation_node (node)) {
+                       /* Citation with just text inside */
+                       quote_node (document, node, quote_level + 1);
+
+                       move_next = TRUE;
+                       goto next_node;
+               }
+
+               if (!WEBKIT_DOM_IS_HTML_BR_ELEMENT (node)) {
+                       if (WEBKIT_DOM_IS_HTML_ANCHOR_ELEMENT (prev_sibling)) {
+                               move_next = TRUE;
+                               goto next_node;
+                       }
+                       goto not_br;
+               } else if (element_has_id (WEBKIT_DOM_ELEMENT (node), "-x-evo-first-br") ||
+                          element_has_id (WEBKIT_DOM_ELEMENT (node), "-x-evo-last-br")) {
+                       quote_br_node (node, quote_level);
+                       node = next_sibling;
+                       skip_node = TRUE;
+                       goto next_node;
+               }
+
+               if (WEBKIT_DOM_IS_HTML_BR_ELEMENT (prev_sibling)) {
+                       quote_br_node (prev_sibling, quote_level);
+                       node = next_sibling;
+                       skip_node = TRUE;
+                       goto next_node;
+               }
+
+               if (!prev_sibling && !next_sibling) {
+                       WebKitDOMNode *parent = webkit_dom_node_get_parent_node (node);
+
+                       if (WEBKIT_DOM_IS_HTML_DIV_ELEMENT (parent) ||
+                           WEBKIT_DOM_IS_HTML_PRE_ELEMENT (parent) ||
+                           (WEBKIT_DOM_IS_HTML_QUOTE_ELEMENT (parent) &&
+                            !e_editor_dom_node_is_citation_node (parent))) {
+                               insert_quote_symbols_before_node (
+                                       document, node, quote_level, FALSE);
+
+                               goto next_node;
+                       }
+               }
+
+               if (e_editor_dom_node_is_citation_node (prev_sibling)) {
+                       insert_quote_symbols_before_node (
+                               document, node, quote_level, FALSE);
+                       goto next_node;
+               }
+
+               if (WEBKIT_DOM_IS_HTML_BR_ELEMENT (node) &&
+                   !next_sibling && WEBKIT_DOM_IS_ELEMENT (prev_sibling) &&
+                   e_editor_dom_is_selection_position_node (prev_sibling)) {
+                       insert_quote_symbols_before_node (
+                               document, node, quote_level, FALSE);
+                       goto next_node;
+               }
+
+               if (WEBKIT_DOM_IS_HTML_BR_ELEMENT (node)) {
+                       move_next = TRUE;
+                       goto next_node;
+               }
+
+ not_br:
+               text_content = webkit_dom_node_get_text_content (node);
+               if (text_content && !*text_content) {
+                       g_free (text_content);
+                       move_next = TRUE;
+                       goto next_node;
+               }
+               g_free (text_content);
+
+               quote_node (document, node, quote_level);
+
+               move_next = TRUE;
+               goto next_node;
+
+ with_children:
+               if (e_editor_dom_node_is_citation_node (node)) {
+                       /* Go deeper and increase level */
+                       quote_plain_text_recursive (
+                               document, node, start_node, quote_level + 1);
+                       move_next = TRUE;
+               } else {
+                       quote_plain_text_recursive (
+                               document, node, start_node, quote_level);
+                       move_next = TRUE;
+               }
+ next_node:
+               if (next) {
+                       suppress_next = FALSE;
+                       next = FALSE;
+               }
+
+               if (suppress_next)
+                       next = TRUE;
+
+               if (!skip_node) {
+                       /* Move to next node */
+                       if (!move_next && webkit_dom_node_has_child_nodes (node)) {
+                               node = webkit_dom_node_get_first_child (node);
+                       } else if (webkit_dom_node_get_next_sibling (node)) {
+                               node = webkit_dom_node_get_next_sibling (node);
+                       } else {
+                               return;
+                       }
+               }
+       }
+}
+
+WebKitDOMElement *
+e_editor_dom_quote_plain_text_element (EEditorPage *editor_page,
+                                      WebKitDOMElement *element)
+{
+       WebKitDOMDocument *document;
+       WebKitDOMNode *element_clone;
+       WebKitDOMNodeList *list = NULL;
+       gint ii, length, level;
+
+       g_return_val_if_fail (E_IS_EDITOR_PAGE (editor_page), NULL);
+
+       document = e_editor_page_get_document (editor_page);
+       element_clone = webkit_dom_node_clone_node_with_error (WEBKIT_DOM_NODE (element), TRUE, NULL);
+       level = e_editor_dom_get_citation_level (WEBKIT_DOM_NODE (element), TRUE);
+
+       /* Remove old quote characters if the exists */
+       list = webkit_dom_element_query_selector_all (
+               WEBKIT_DOM_ELEMENT (element_clone), "span.-x-evo-quoted", NULL);
+       length = webkit_dom_node_list_get_length (list);
+       for (ii = 0; ii < length; ii++) {
+               WebKitDOMNode *node = webkit_dom_node_list_item (list, ii);
+               remove_node (node);
+               g_object_unref (node);
+       }
+       g_clear_object (&list);
+
+       webkit_dom_node_normalize (element_clone);
+       quote_plain_text_recursive (
+               document, element_clone, element_clone, level);
+
+       /* Replace old element with one, that is quoted */
+       webkit_dom_node_replace_child (
+               webkit_dom_node_get_parent_node (WEBKIT_DOM_NODE (element)),
+               element_clone,
+               WEBKIT_DOM_NODE (element),
+               NULL);
+
+       return WEBKIT_DOM_ELEMENT (element_clone);
+}
+
+/*
+ * dom_quote_plain_text:
+ *
+ * Quote text inside citation blockquotes in plain text mode.
+ *
+ * As this function is cloning and replacing all citation blockquotes keep on
+ * mind that any pointers to nodes inside these blockquotes will be invalidated.
+ */
+static WebKitDOMElement *
+dom_quote_plain_text (WebKitDOMDocument *document)
+{
+       WebKitDOMHTMLElement *body;
+       WebKitDOMNode *body_clone;
+       WebKitDOMNamedNodeMap *attributes = NULL;
+       WebKitDOMNodeList *list = NULL;
+       WebKitDOMElement *element;
+       gint ii, length;
+       gulong attributes_length;
+
+       /* Check if the document is already quoted */
+       element = webkit_dom_document_query_selector (
+               document, ".-x-evo-quoted", NULL);
+       if (element)
+               return NULL;
+
+       body = webkit_dom_document_get_body (document);
+       body_clone = webkit_dom_node_clone_node_with_error (WEBKIT_DOM_NODE (body), TRUE, NULL);
+
+       /* Clean unwanted spaces before and after blockquotes */
+       list = webkit_dom_element_query_selector_all (
+               WEBKIT_DOM_ELEMENT (body_clone), "blockquote[type|=cite]", NULL);
+       length = webkit_dom_node_list_get_length (list);
+       for (ii = 0; ii < length; ii++) {
+               WebKitDOMNode *blockquote = webkit_dom_node_list_item (list, ii);
+               WebKitDOMNode *prev_sibling = webkit_dom_node_get_previous_sibling (blockquote);
+               WebKitDOMNode *next_sibling = webkit_dom_node_get_next_sibling (blockquote);
+
+               if (prev_sibling && WEBKIT_DOM_IS_HTML_BR_ELEMENT (prev_sibling))
+                       remove_node (prev_sibling);
+
+               if (next_sibling && WEBKIT_DOM_IS_HTML_BR_ELEMENT (next_sibling))
+                       remove_node (next_sibling);
+
+               if (webkit_dom_node_has_child_nodes (blockquote)) {
+                       WebKitDOMNode *child = webkit_dom_node_get_first_child (blockquote);
+                       if (WEBKIT_DOM_IS_HTML_BR_ELEMENT (child))
+                               remove_node (child);
+               }
+               g_object_unref (blockquote);
+       }
+       g_clear_object (&list);
+
+       webkit_dom_node_normalize (body_clone);
+       quote_plain_text_recursive (document, body_clone, body_clone, 0);
+
+       /* Copy attributes */
+       attributes = webkit_dom_element_get_attributes (WEBKIT_DOM_ELEMENT (body));
+       attributes_length = webkit_dom_named_node_map_get_length (attributes);
+       for (ii = 0; ii < attributes_length; ii++) {
+               gchar *name, *value;
+               WebKitDOMNode *node = webkit_dom_named_node_map_item (attributes, ii);
+
+               name = webkit_dom_node_get_local_name (node);
+               value = webkit_dom_node_get_node_value (node);
+
+               webkit_dom_element_set_attribute (
+                       WEBKIT_DOM_ELEMENT (body_clone), name, value, NULL);
+
+               g_object_unref (node);
+               g_free (name);
+               g_free (value);
+       }
+       g_clear_object (&attributes);
+
+       /* Replace old BODY with one, that is quoted */
+       webkit_dom_node_replace_child (
+               webkit_dom_node_get_parent_node (WEBKIT_DOM_NODE (body)),
+               body_clone,
+               WEBKIT_DOM_NODE (body),
+               NULL);
+
+       return WEBKIT_DOM_ELEMENT (body_clone);
+}
+
+/*
+ * dom_dequote_plain_text:
+ *
+ * Dequote already quoted plain text in editor.
+ * Editor have to be quoted with e_html_editor_view_quote_plain_text otherwise
+ * it's not working.
+ */
+static void
+dom_dequote_plain_text (WebKitDOMDocument *document)
+{
+       WebKitDOMNodeList *paragraphs = NULL;
+       gint length, ii;
+
+       paragraphs = webkit_dom_document_query_selector_all (
+               document, "blockquote[type=cite]", NULL);
+       length = webkit_dom_node_list_get_length (paragraphs);
+       for (ii = 0; ii < length; ii++) {
+               WebKitDOMElement *element;
+
+               element = WEBKIT_DOM_ELEMENT (webkit_dom_node_list_item (paragraphs, ii));
+
+               if (e_editor_dom_node_is_citation_node (WEBKIT_DOM_NODE (element)))
+                       e_editor_dom_remove_quoting_from_element (element);
+
+               g_object_unref (element);
+       }
+       g_clear_object (&paragraphs);
+}
+
+static gboolean
+create_anchor_for_link (const GMatchInfo *info,
+                        GString *res,
+                        gpointer data)
+{
+       gboolean link_surrounded, with_nbsp = FALSE;
+       gint offset = 0, truncate_from_end = 0;
+       gint match_start, match_end;
+       gchar *match_with_nbsp, *match_without_nbsp;
+       const gchar *end_of_match = NULL;
+       const gchar *match, *match_extra_characters;
+
+       match_with_nbsp = g_match_info_fetch (info, 1);
+       /* E-mail addresses will be here. */
+       match_without_nbsp = g_match_info_fetch (info, 0);
+
+       if (!match_with_nbsp || (strstr (match_with_nbsp, "&nbsp;") && !g_str_has_prefix (match_with_nbsp, 
"&nbsp;"))) {
+               match = match_without_nbsp;
+               match_extra_characters = match_with_nbsp;
+               g_match_info_fetch_pos (info, 0, &match_start, &match_end);
+               with_nbsp = TRUE;
+       } else {
+               match = match_with_nbsp;
+               match_extra_characters = match_without_nbsp;
+               g_match_info_fetch_pos (info, 1, &match_start, &match_end);
+       }
+
+       if (g_str_has_prefix (match, "&nbsp;"))
+               offset += 6;
+
+       end_of_match = match + match_end - match_start - 1;
+       /* Taken from camel-url-scanner.c */
+       /* URLs are extremely unlikely to end with any punctuation, so
+        * strip any trailing punctuation off from link and put it after
+        * the link. Do the same for any closing double-quotes as well. */
+       while (end_of_match && end_of_match != match && strchr (URL_INVALID_TRAILING_CHARS, *end_of_match)) {
+               truncate_from_end++;
+               end_of_match--;
+       }
+       end_of_match++;
+
+       link_surrounded =
+               g_str_has_suffix (res->str, "&lt;");
+
+       if (link_surrounded) {
+               if (end_of_match && *end_of_match && strlen (match) > strlen (end_of_match) + 3)
+                       link_surrounded = link_surrounded && g_str_has_prefix (end_of_match - 3, "&gt;");
+               else
+                       link_surrounded = link_surrounded && g_str_has_suffix (match, "&gt;");
+
+               if (link_surrounded) {
+                       /* ";" is already counted by code above */
+                       truncate_from_end += 3;
+                       end_of_match -= 3;
+               }
+       }
+
+       g_string_append (res, "<a href=\"");
+       if (strstr (match, "@") && !strstr (match, "://"))
+               g_string_append (res, "mailto:";);
+       g_string_append (res, match + offset);
+       if (truncate_from_end > 0)
+               g_string_truncate (res, res->len - truncate_from_end);
+
+       g_string_append (res, "\">");
+       g_string_append (res, match + offset);
+       if (truncate_from_end > 0)
+               g_string_truncate (res, res->len - truncate_from_end);
+
+       g_string_append (res, "</a>");
+
+       if (truncate_from_end > 0)
+               g_string_append (res, end_of_match);
+
+       if (!with_nbsp && match_extra_characters)
+               g_string_append (res, match_extra_characters + (match_end - match_start));
+
+       g_free (match_with_nbsp);
+       g_free (match_without_nbsp);
+
+       return FALSE;
+}
+
+static gboolean
+replace_to_nbsp (const GMatchInfo *info,
+                 GString *res)
+{
+       gchar *match;
+       gint ii = 0;
+
+       match = g_match_info_fetch (info, 0);
+
+       while (match[ii] != '\0') {
+               if (match[ii] == ' ') {
+                       /* Alone spaces or spaces before/after tabulator. */
+                       g_string_append (res, "&nbsp;");
+               } else if (match[ii] == '\t') {
+                       /* Replace tabs with their WebKit HTML representation. */
+                       g_string_append (res, "<span class=\"Apple-tab-span\" 
style=\"white-space:pre\">\t</span>");
+               }
+
+               ii++;
+       }
+
+       g_free (match);
+
+       return FALSE;
+}
+
+static gboolean
+surround_links_with_anchor (const gchar *text)
+{
+       return (strstr (text, "http") || strstr (text, "ftp") ||
+               strstr (text, "www") || strstr (text, "@"));
+}
+
+static void
+append_new_block (WebKitDOMElement *parent,
+                  WebKitDOMElement **block)
+{
+       webkit_dom_node_append_child (
+               WEBKIT_DOM_NODE (parent),
+               WEBKIT_DOM_NODE (*block),
+               NULL);
+
+       *block = NULL;
+}
+
+static WebKitDOMElement *
+create_and_append_new_block (EEditorPage *editor_page,
+                             WebKitDOMElement *parent,
+                             WebKitDOMElement *block_template,
+                             const gchar *content)
+{
+       WebKitDOMElement *block;
+
+       g_return_val_if_fail (E_IS_EDITOR_PAGE (editor_page), NULL);
+
+       block = WEBKIT_DOM_ELEMENT (webkit_dom_node_clone_node_with_error (
+               WEBKIT_DOM_NODE (block_template), FALSE, NULL));
+
+       webkit_dom_element_set_inner_html (block, content, NULL);
+
+       append_new_block (parent, &block);
+
+       return block;
+}
+
+static void
+append_citation_mark (WebKitDOMDocument *document,
+                      WebKitDOMElement *parent,
+                      const gchar *citation_mark_text)
+{
+       WebKitDOMText *text;
+
+       text = webkit_dom_document_create_text_node (document, citation_mark_text);
+
+       webkit_dom_node_append_child (
+               WEBKIT_DOM_NODE (parent),
+               WEBKIT_DOM_NODE (text),
+               NULL);
+}
+
+static void
+replace_selection_markers (gchar **text)
+{
+       if (!text)
+               return;
+
+       if (strstr (*text, "##SELECTION_START##")) {
+               GString *tmp;
+
+               tmp = e_str_replace_string (
+                       *text,
+                       "##SELECTION_START##",
+                       "<span id=\"-x-evo-selection-start-marker\"></span>");
+
+               g_free (*text);
+               *text = g_string_free (tmp, FALSE);
+       }
+
+       if (strstr (*text, "##SELECTION_END##")) {
+               GString *tmp;
+
+               tmp = e_str_replace_string (
+                       *text,
+                       "##SELECTION_END##",
+                       "<span id=\"-x-evo-selection-end-marker\"></span>");
+
+               g_free (*text);
+               *text = g_string_free (tmp, FALSE);
+       }
+}
+
+static GString *
+remove_new_lines_around_citations (const gchar *input)
+{
+       GString *str = NULL;
+       const gchar *p, *next;
+
+       str = g_string_new ("");
+
+       /* Remove the new lines around citations:
+        * Replace <br><br>##CITATION_START## with <br>##CITATION_START##
+        * Replace ##CITATION_START##<br><br> with ##CITATION_START##<br>
+        * Replace <br>##CITATION_END## with ##CITATION_END## */
+       p = input;
+       while (next = strstr (p, "##CITATION_"), next) {
+               gchar citation_type = 0;
+
+               if (p < next)
+                       g_string_append_len (str, p, next - p);
+
+               if (next + 11)
+                       citation_type = next[11];
+               /* ##CITATION_START## */
+               if (citation_type == 'S') {
+                       if (g_str_has_suffix (str->str, "<br><br>") ||
+                           g_str_has_suffix (str->str, "<br><br>"))
+                               g_string_truncate (str, str->len - 4);
+
+                       if (g_str_has_prefix (next + 11, "START##<br><br>")) {
+                               g_string_append (str, "##CITATION_START##<br>");
+                               p = next + 26;
+                               continue;
+                       }
+               } else if (citation_type == 'E') {
+                       if (g_str_has_suffix (str->str, "<br>"))
+                               g_string_truncate (str, str->len - 4);
+               }
+
+               g_string_append (str, "##CITATION_");
+
+               p = next + 11;
+       }
+
+       g_string_append (str, p);
+
+       return str;
+}
+
+static GString *
+replace_citation_marks_to_citations (const gchar *input)
+{
+       GString *str = NULL;
+       const gchar *p, *next;
+
+       str = g_string_new ("");
+
+       /* Replaces text markers with actual HTML blockquotes */
+       p = input;
+       while (next = strstr (p, "##CITATION_"), next) {
+               gchar citation_type = 0;
+
+               if (p < next)
+                       g_string_append_len (str, p, next - p);
+
+               if (next + 11)
+                       citation_type = next[11];
+               /* ##CITATION_START## */
+               if (citation_type == 'S') {
+                       g_string_append (str, "<blockquote type=\"cite\">");
+                       p = next + 18;
+               } else if (citation_type == 'E') {
+                       g_string_append (str, "</blockquote>");
+                       p = next + 16;
+               } else
+                       p = next + 11;
+       }
+
+       g_string_append (str, p);
+
+       return str;
+}
+
+/* This parses the HTML code (that contains just text, &nbsp; and BR elements)
+ * into blocks.
+ * HTML code in that format we can get by taking innerText from some element,
+ * setting it to another one and finally getting innerHTML from it */
+static void
+parse_html_into_blocks (EEditorPage *editor_page,
+                        WebKitDOMElement *parent,
+                        WebKitDOMElement *passed_block_template,
+                        const gchar *input)
+{
+       gboolean has_citation = FALSE, processing_last = FALSE;
+       const gchar *prev_br, *next_br;
+       GString *html = NULL;
+       GRegex *regex_nbsp = NULL, *regex_link = NULL, *regex_email = NULL;
+       WebKitDOMDocument *document;
+       WebKitDOMElement *block_template = passed_block_template;
+
+       g_return_if_fail (E_IS_EDITOR_PAGE (editor_page));
+
+       document = e_editor_page_get_document (editor_page);
+       webkit_dom_element_set_inner_html (parent, "", NULL);
+
+       if (!block_template) {
+               gboolean use_paragraphs;
+               GSettings *settings;
+
+               settings = e_util_ref_settings ("org.gnome.evolution.mail");
+
+               use_paragraphs = g_settings_get_boolean (
+                       settings, "composer-wrap-quoted-text-in-replies");
+
+               if (use_paragraphs)
+                       block_template = e_editor_dom_get_paragraph_element (editor_page, -1, 0);
+               else
+                       block_template = webkit_dom_document_create_element (document, "pre", NULL);
+
+               g_object_unref (settings);
+       }
+
+       /* Replace the tabulators with SPAN elements that corresponds to them.
+        * If not inserting the content into the PRE element also replace single
+        * spaces on the beginning of line, 2+ spaces and with non breaking
+        * spaces. */
+       if (WEBKIT_DOM_IS_HTML_PRE_ELEMENT (block_template))
+               regex_nbsp = g_regex_new ("\x9", 0, 0, NULL);
+       else
+               regex_nbsp = g_regex_new ("^\\s{1}|\\s{2,}|\x9|\\s$", 0, 0, NULL);
+
+
+       html = remove_new_lines_around_citations (input);
+
+       prev_br = html->str;
+       next_br = strstr (prev_br, "<br>");
+       processing_last = !next_br;
+
+       while (next_br || processing_last) {
+               const gchar *citation_start = NULL, *citation_end = NULL;
+               const gchar *rest = NULL, *with_br = NULL;
+               gchar *to_process = NULL, *to_insert = NULL;
+               guint to_insert_start = 0, to_insert_end = 0;
+
+               if (!next_br) {
+                       to_process = g_strdup (prev_br);
+                       processing_last = TRUE;
+               } else if ((to_process = g_utf8_substring (prev_br, 0, g_utf8_pointer_to_offset (prev_br, 
next_br))) && !*to_process && !processing_last) {
+                       g_free (to_process);
+                       to_process = g_strdup (next_br);
+                       processing_last = TRUE;
+               }
+               to_insert_end = g_utf8_strlen (to_process, -1);
+
+               if ((with_br = strstr (to_process, "<br>"))) {
+                       if (with_br == to_process)
+                               to_insert_start += 4;
+               }
+               if ((citation_start = strstr (to_process, "##CITATION_START"))) {
+                       if (with_br && citation_start == with_br + 4)
+                               to_insert_start += 18; /* + ## */
+                       else
+                               to_insert_end -= 18; /* + ## */
+                       has_citation = TRUE;
+               }
+               if ((citation_end = strstr (to_process, "##CITATION_END")))
+                       to_insert_end -= 16; /* + ## */
+
+               /* First BR */
+               if (with_br && prev_br == html->str)
+                       create_and_append_new_block (
+                               editor_page, parent, block_template, "<br id=\"-x-evo-first-br\">");
+
+               if (with_br && citation_start && citation_start == with_br + 4) {
+                       create_and_append_new_block (
+                               editor_page, parent, block_template, "<br>");
+
+                       append_citation_mark (document, parent, "##CITATION_START##");
+               }
+
+               if ((to_insert = g_utf8_substring (to_process, to_insert_start, to_insert_end)) && 
*to_insert) {
+                       gboolean empty = FALSE;
+                       gchar *truncated = g_strdup (to_insert);
+                       gchar *rest_to_insert;
+
+                       empty = !*truncated && strlen (to_insert) > 0;
+
+                       rest_to_insert = g_regex_replace_eval (
+                               regex_nbsp,
+                               empty ? rest : truncated,
+                               -1,
+                               0,
+                               0,
+                               (GRegexEvalCallback) replace_to_nbsp,
+                               NULL,
+                               NULL);
+                       g_free (truncated);
+
+                       replace_selection_markers (&rest_to_insert);
+
+                       if (surround_links_with_anchor (rest_to_insert)) {
+                               gboolean is_email_address =
+                                       strstr (rest_to_insert, "@") &&
+                                       !strstr (rest_to_insert, "://");
+
+                               if (is_email_address && !regex_email)
+                                       regex_email = g_regex_new (E_MAIL_PATTERN, 0, 0, NULL);
+                               if (!is_email_address && !regex_link)
+                                       regex_link = g_regex_new (URL_PATTERN, 0, 0, NULL);
+
+                               truncated = g_regex_replace_eval (
+                                       is_email_address ? regex_email : regex_link,
+                                       rest_to_insert,
+                                       -1,
+                                       0,
+                                       G_REGEX_MATCH_NOTEMPTY,
+                                       create_anchor_for_link,
+                                       NULL,
+                                       NULL);
+
+                               g_free (rest_to_insert);
+                               rest_to_insert = truncated;
+                       }
+
+                       create_and_append_new_block (
+                               editor_page, parent, block_template, rest_to_insert);
+
+                       g_free (rest_to_insert);
+               } else if (to_insert && !citation_start)
+                       create_and_append_new_block (
+                               editor_page, parent, block_template, "<br>");
+
+               g_free (to_insert);
+
+               if (with_br && citation_start && citation_start != with_br + 4)
+                       append_citation_mark (document, parent, "##CITATION_START##");
+
+               if (citation_end)
+                       append_citation_mark (document, parent, "##CITATION_END##");
+
+               g_free (to_process);
+
+               prev_br = next_br;
+               next_br = (prev_br && *prev_br) ? strstr (prev_br + 1, "<br>") : NULL;
+               if (!next_br && !processing_last) {
+                       if (!prev_br)
+                               break;
+
+                       if (g_utf8_strlen (prev_br, -1) > 4) {
+                               next_br = prev_br;
+                       } else {
+                               WebKitDOMNode *child;
+
+                               child = webkit_dom_node_get_last_child (
+                                       WEBKIT_DOM_NODE (parent));
+                               if (child) {
+                                       child = webkit_dom_node_get_first_child (child);
+                                       if (child && WEBKIT_DOM_IS_HTML_BR_ELEMENT (child)) {
+                                               /* If the processed HTML contained just
+                                                * the BR don't overwrite its id. */
+                                               if (!element_has_id (WEBKIT_DOM_ELEMENT (child), 
"-x-evo-first-br"))
+                                                       webkit_dom_element_set_id (
+                                                               WEBKIT_DOM_ELEMENT (child),
+                                                               "-x-evo-last-br");
+                                       } else if (!webkit_dom_document_query_selector (document, 
".-x-evo-signature-wrapper", NULL)) {
+                                               /* FIXME WK2 - the signature could not be inserted at this 
point
+                                                * this is the reason why there is an extra NL on the very 
end of
+                                                * quoted text in reply. */
+                                               create_and_append_new_block (
+                                                       editor_page, parent, block_template, "<br>");
+                                       }
+                               } else {
+                                       create_and_append_new_block (
+                                               editor_page, parent, block_template, "<br>");
+                               }
+                               break;
+                       }
+                       processing_last = TRUE;
+               } else if (processing_last && !prev_br && !next_br) {
+                       break;
+               }
+       }
+
+       if (has_citation) {
+               gchar *inner_html;
+               GString *parsed;
+
+               /* Replace text markers with actual HTML blockquotes */
+               inner_html = webkit_dom_element_get_inner_html (parent);
+               parsed = replace_citation_marks_to_citations (inner_html);
+               webkit_dom_element_set_inner_html (parent, parsed->str, NULL);
+
+               g_free (inner_html);
+               g_string_free (parsed, TRUE);
+       }
+
+       g_string_free (html, TRUE);
+
+       if (regex_email != NULL)
+               g_regex_unref (regex_email);
+       if (regex_link != NULL)
+               g_regex_unref (regex_link);
+       g_regex_unref (regex_nbsp);
+}
+
+void
+e_editor_dom_quote_and_insert_text_into_selection (EEditorPage *editor_page,
+                                                  const gchar *text,
+                                                  gboolean is_html)
+{
+       WebKitDOMDocument *document;
+       WebKitDOMElement *blockquote, *element, *selection_start;
+       WebKitDOMNode *node;
+       EEditorHistoryEvent *ev = NULL;
+       EEditorUndoRedoManager *manager;
+       gchar *inner_html;
+
+       g_return_if_fail (E_IS_EDITOR_PAGE (editor_page));
+
+       if (!text || !*text)
+               return;
+
+       document = e_editor_page_get_document (editor_page);
+
+       if (is_html) {
+               element = webkit_dom_document_create_element (document, "div", NULL);
+
+               if (strstr (text, "\n")) {
+                       GRegex *regex;
+                       gchar *tmp;
+
+                       /* Strip new lines between tags to avoid unwanted line breaks. */
+                       regex = g_regex_new ("\\>[\\s]+\\<", 0, 0, NULL);
+                       tmp = g_regex_replace (regex, text, -1, 0, "> <", 0, NULL);
+                       webkit_dom_element_set_inner_html (element, tmp, NULL);
+                       g_free (tmp);
+                       g_regex_unref (regex);
+               } else {
+                       webkit_dom_element_set_inner_html (element, text, NULL);
+               }
+       } else {
+               /* This is a trick to escape any HTML characters (like <, > or &).
+                * <textarea> automatically replaces all these unsafe characters
+                * by &lt;, &gt; etc. */
+               element = webkit_dom_document_create_element (document, "textarea", NULL);
+               webkit_dom_html_element_set_inner_text (WEBKIT_DOM_HTML_ELEMENT (element), text, NULL);
+       }
+
+       inner_html = webkit_dom_element_get_inner_html (element);
+
+       e_editor_dom_selection_save (editor_page);
+
+       manager = e_editor_page_get_undo_redo_manager (editor_page);
+       if (!e_editor_undo_redo_manager_is_operation_in_progress (manager)) {
+               ev = g_new0 (EEditorHistoryEvent, 1);
+               ev->type = HISTORY_PASTE_QUOTED;
+
+               e_editor_dom_selection_get_coordinates (editor_page,
+                       &ev->before.start.x,
+                       &ev->before.start.y,
+                       &ev->before.end.x,
+                       &ev->before.end.y);
+
+               ev->data.string.from = NULL;
+               ev->data.string.to = g_strdup (text);
+       }
+
+       blockquote = webkit_dom_document_create_element (document, "blockquote", NULL);
+       webkit_dom_element_set_attribute (blockquote, "type", "cite", NULL);
+
+       selection_start = webkit_dom_document_get_element_by_id (
+               document, "-x-evo-selection-start-marker");
+       node = webkit_dom_node_get_previous_sibling (WEBKIT_DOM_NODE (selection_start));
+       /* Check if block is empty. If so, replace it otherwise insert the quoted
+        * content after current block. */
+       if (!node || WEBKIT_DOM_IS_HTML_BR_ELEMENT (node)) {
+               node = webkit_dom_node_get_next_sibling (
+                       WEBKIT_DOM_NODE (selection_start));
+               node = webkit_dom_node_get_next_sibling (node);
+               if (!node || WEBKIT_DOM_IS_HTML_BR_ELEMENT (node)) {
+                       webkit_dom_node_replace_child (
+                               webkit_dom_node_get_parent_node (
+                                       webkit_dom_node_get_parent_node (
+                                               WEBKIT_DOM_NODE (selection_start))),
+                               WEBKIT_DOM_NODE (blockquote),
+                               webkit_dom_node_get_parent_node (WEBKIT_DOM_NODE (selection_start)),
+                               NULL);
+               }
+       } else {
+               webkit_dom_node_insert_before (
+                       WEBKIT_DOM_NODE (webkit_dom_document_get_body (document)),
+                       WEBKIT_DOM_NODE (blockquote),
+                       webkit_dom_node_get_next_sibling (
+                               webkit_dom_node_get_parent_node (
+                                       WEBKIT_DOM_NODE (selection_start))),
+                       NULL);
+       }
+
+       parse_html_into_blocks (editor_page, blockquote, NULL, inner_html);
+
+       if (e_editor_page_get_html_mode (editor_page)) {
+               node = webkit_dom_node_get_last_child (WEBKIT_DOM_NODE (blockquote));
+       } else {
+               gint word_wrap_length;
+
+               word_wrap_length = e_editor_page_get_word_wrap_length (editor_page);
+               node = webkit_dom_node_get_first_child (WEBKIT_DOM_NODE (blockquote));
+               while (node) {
+                       WebKitDOMNode *next_sibling;
+
+                       node = WEBKIT_DOM_NODE (e_editor_dom_wrap_paragraph_length (editor_page, 
WEBKIT_DOM_ELEMENT (node), word_wrap_length - 2));
+
+                       webkit_dom_node_normalize (node);
+                       e_editor_dom_quote_plain_text_element_after_wrapping (editor_page, WEBKIT_DOM_ELEMENT 
(node), 1);
+
+                       next_sibling = webkit_dom_node_get_next_sibling (node);
+                       if (!next_sibling)
+                               break;
+
+                       node = next_sibling;
+               }
+       }
+
+       dom_add_selection_markers_into_element_end (
+               document, WEBKIT_DOM_ELEMENT (node), NULL, NULL);
+
+       e_editor_dom_selection_restore (editor_page);
+
+       if (ev) {
+               e_editor_dom_selection_get_coordinates (editor_page,
+                       &ev->after.start.x,
+                       &ev->after.start.y,
+                       &ev->after.end.x,
+                       &ev->after.end.y);
+               e_editor_undo_redo_manager_insert_history_event (manager, ev);
+       }
+
+       e_editor_dom_force_spell_check_in_viewport (editor_page);
+       e_editor_page_emit_content_changed (editor_page);
+
+       g_free (inner_html);
+}
+
+static void
+mark_citation (WebKitDOMElement *citation)
+{
+       webkit_dom_html_element_insert_adjacent_text (
+               WEBKIT_DOM_HTML_ELEMENT (citation),
+               "beforebegin",
+               "##CITATION_START##",
+               NULL);
+
+       webkit_dom_html_element_insert_adjacent_text (
+               WEBKIT_DOM_HTML_ELEMENT (citation),
+               "afterend",
+               "##CITATION_END##",
+               NULL);
+
+       element_add_class (citation, "marked");
+}
+
+static gint
+create_text_markers_for_citations_in_element (WebKitDOMElement *element)
+{
+       gint count = 0;
+       WebKitDOMElement *citation;
+
+       citation = webkit_dom_element_query_selector (
+               element, "blockquote[type=cite]:not(.marked)", NULL);
+
+       while (citation) {
+               mark_citation (citation);
+               count ++;
+
+               citation = webkit_dom_element_query_selector (
+                       element, "blockquote[type=cite]:not(.marked)", NULL);
+       }
+
+       return count;
+}
+
+static void
+create_text_markers_for_selection_in_element (WebKitDOMElement *element)
+{
+       WebKitDOMElement *selection_marker;
+
+       selection_marker = webkit_dom_element_query_selector (
+               element, "#-x-evo-selection-start-marker", NULL);
+       if (selection_marker)
+               webkit_dom_html_element_insert_adjacent_text (
+                       WEBKIT_DOM_HTML_ELEMENT (selection_marker),
+                       "afterend",
+                       "##SELECTION_START##",
+                       NULL);
+
+       selection_marker = webkit_dom_element_query_selector (
+               element, "#-x-evo-selection-end-marker", NULL);
+       if (selection_marker)
+               webkit_dom_html_element_insert_adjacent_text (
+                       WEBKIT_DOM_HTML_ELEMENT (selection_marker),
+                       "afterend",
+                       "##SELECTION_END##",
+                       NULL);
+}
+
+static void
+quote_plain_text_elements_after_wrapping_in_document (EEditorPage *editor_page)
+{
+       WebKitDOMDocument *document;
+       WebKitDOMNodeList *list = NULL;
+       gint length, ii;
+
+       g_return_if_fail (E_IS_EDITOR_PAGE (editor_page));
+
+       document = e_editor_page_get_document (editor_page);
+
+       /* Also quote the PRE elements as well. */
+       list = webkit_dom_document_query_selector_all (
+               document, "blockquote[type=cite] > p[data-evo-paragraph], blockquote[type=cite] > pre", NULL);
+
+       length = webkit_dom_node_list_get_length (list);
+       for (ii = 0; ii < length; ii++) {
+               gint citation_level;
+               WebKitDOMNode *child;
+
+               child = webkit_dom_node_list_item (list, ii);
+               citation_level = e_editor_dom_get_citation_level (child, TRUE);
+               e_editor_dom_quote_plain_text_element_after_wrapping (editor_page, WEBKIT_DOM_ELEMENT 
(child), citation_level);
+               g_object_unref (child);
+       }
+       g_clear_object (&list);
+}
+
+static void
+clear_attributes (EEditorPage *editor_page)
+{
+       WebKitDOMDocument *document;
+       WebKitDOMNamedNodeMap *attributes = NULL;
+       WebKitDOMHTMLElement *body;
+       WebKitDOMHTMLHeadElement *head;
+       WebKitDOMElement *document_element;
+       gint length, ii;
+
+       g_return_if_fail (E_IS_EDITOR_PAGE (editor_page));
+
+       document = e_editor_page_get_document (editor_page);
+       body = webkit_dom_document_get_body (document);
+       head = webkit_dom_document_get_head (document);
+       document_element = webkit_dom_document_get_document_element (document);
+
+       /* Remove all attributes from HTML element */
+       attributes = webkit_dom_element_get_attributes (document_element);
+       length = webkit_dom_named_node_map_get_length (attributes);
+       for (ii = length - 1; ii >= 0; ii--) {
+               WebKitDOMNode *node = webkit_dom_named_node_map_item (attributes, ii);
+
+               webkit_dom_element_remove_attribute_node (
+                       document_element, WEBKIT_DOM_ATTR (node), NULL);
+               g_object_unref (node);
+       }
+       g_clear_object (&attributes);
+
+       /* Remove everything from HEAD element */
+       while (webkit_dom_node_get_first_child (WEBKIT_DOM_NODE (head)))
+               remove_node (webkit_dom_node_get_first_child (WEBKIT_DOM_NODE (head)));
+
+       /* Make the quote marks non-selectable. */
+       e_editor_dom_disable_quote_marks_select (editor_page);
+
+       /* Remove non Evolution attributes from BODY element */
+       attributes = webkit_dom_element_get_attributes (WEBKIT_DOM_ELEMENT (body));
+       length = webkit_dom_named_node_map_get_length (attributes);
+       for (ii = length - 1; ii >= 0; ii--) {
+               gchar *name;
+               WebKitDOMNode *node = webkit_dom_named_node_map_item (attributes, ii);
+
+               name = webkit_dom_node_get_local_name (node);
+
+               if (!g_str_has_prefix (name, "data-") && (g_strcmp0 (name, "spellcheck") != 0))
+                       webkit_dom_element_remove_attribute_node (
+                               WEBKIT_DOM_ELEMENT (body),
+                               WEBKIT_DOM_ATTR (node),
+                               NULL);
+
+               g_object_unref (node);
+               g_free (name);
+       }
+       g_clear_object (&attributes);
+}
+
+static void
+body_compositionstart_event_cb (WebKitDOMElement *element,
+                                WebKitDOMUIEvent *event,
+                                EEditorPage *editor_page)
+{
+       g_return_if_fail (E_IS_EDITOR_PAGE (editor_page));
+
+       e_editor_page_set_composition_in_progress (editor_page, TRUE);
+       e_editor_dom_remove_input_event_listener_from_body (editor_page);
+}
+
+static void
+body_compositionend_event_cb (WebKitDOMElement *element,
+                              WebKitDOMUIEvent *event,
+                              EEditorPage *editor_page)
+{
+       g_return_if_fail (E_IS_EDITOR_PAGE (editor_page));
+
+       e_editor_page_set_composition_in_progress (editor_page, FALSE);
+       e_editor_dom_remove_input_event_listener_from_body (editor_page);
+}
+
+static void
+register_html_events_handlers (EEditorPage *editor_page,
+                              WebKitDOMHTMLElement *body)
+{
+       g_return_if_fail (E_IS_EDITOR_PAGE (editor_page));
+
+       webkit_dom_event_target_add_event_listener (
+               WEBKIT_DOM_EVENT_TARGET (body),
+               "keydown",
+               G_CALLBACK (body_keydown_event_cb),
+               FALSE,
+               editor_page);
+
+       webkit_dom_event_target_add_event_listener (
+               WEBKIT_DOM_EVENT_TARGET (body),
+               "keypress",
+               G_CALLBACK (body_keypress_event_cb),
+               FALSE,
+               editor_page);
+
+       webkit_dom_event_target_add_event_listener (
+               WEBKIT_DOM_EVENT_TARGET (body),
+               "keyup",
+               G_CALLBACK (body_keyup_event_cb),
+               FALSE,
+               editor_page);
+
+       webkit_dom_event_target_add_event_listener (
+               WEBKIT_DOM_EVENT_TARGET (body),
+               "compositionstart",
+               G_CALLBACK (body_compositionstart_event_cb),
+               FALSE,
+               editor_page);
+
+       webkit_dom_event_target_add_event_listener (
+               WEBKIT_DOM_EVENT_TARGET (body),
+               "compositionend",
+               G_CALLBACK (body_compositionend_event_cb),
+               FALSE,
+               editor_page);
+}
+
+void
+e_editor_dom_convert_content (EEditorPage *editor_page,
+                             const gchar *preferred_text)
+{
+       WebKitDOMDocument *document;
+       WebKitDOMElement *paragraph, *content_wrapper, *top_signature;
+       WebKitDOMElement *cite_body, *signature, *wrapper;
+       WebKitDOMHTMLElement *body;
+       WebKitDOMNodeList *list = NULL;
+       WebKitDOMNode *node;
+       WebKitDOMDOMWindow *dom_window = NULL;
+       gboolean start_bottom, empty = FALSE;
+       gchar *inner_html;
+       gint ii, length;
+       GSettings *settings;
+
+       g_return_if_fail (E_IS_EDITOR_PAGE (editor_page));
+
+       document = e_editor_page_get_document (editor_page);
+       settings = e_util_ref_settings ("org.gnome.evolution.mail");
+       start_bottom = g_settings_get_boolean (settings, "composer-reply-start-bottom");
+       g_object_unref (settings);
+
+       dom_window = webkit_dom_document_get_default_view (document);
+       body = webkit_dom_document_get_body (document);
+       /* Wrapper that will represent the new body. */
+       wrapper = webkit_dom_document_create_element (document, "div", NULL);
+
+       cite_body = webkit_dom_document_query_selector (
+               document, "span.-x-evo-cite-body", NULL);
+
+       /* content_wrapper when the processed text will be placed. */
+       content_wrapper = webkit_dom_document_create_element (
+               document, cite_body ? "blockquote" : "div", NULL);
+       if (cite_body) {
+               webkit_dom_element_set_attribute (content_wrapper, "type", "cite", NULL);
+               webkit_dom_element_set_attribute (content_wrapper, "id", "-x-evo-main-cite", NULL);
+       }
+
+       webkit_dom_node_append_child (
+               WEBKIT_DOM_NODE (wrapper), WEBKIT_DOM_NODE (content_wrapper), NULL);
+
+       /* Remove all previously inserted paragraphs. */
+       list = webkit_dom_document_query_selector_all (
+               document, "p[data-evo-paragraph]:not([data-headers])", NULL);
+       length = webkit_dom_node_list_get_length (list);
+       for (ii = 0; ii < length; ii++) {
+               WebKitDOMNode *node = webkit_dom_node_list_item (list, ii);
+               remove_node (node);
+               g_object_unref (node);
+       }
+       g_clear_object (&list);
+
+       /* Insert the paragraph where the caret will be. */
+       paragraph = e_editor_dom_prepare_paragraph (editor_page, TRUE);
+       webkit_dom_element_set_id (paragraph, "-x-evo-input-start");
+       webkit_dom_node_insert_before (
+               WEBKIT_DOM_NODE (wrapper),
+               WEBKIT_DOM_NODE (paragraph),
+               start_bottom ?
+                       webkit_dom_node_get_next_sibling (WEBKIT_DOM_NODE (content_wrapper)) :
+                       WEBKIT_DOM_NODE (content_wrapper),
+               NULL);
+
+       /* Insert signature (if presented) to the right position. */
+       top_signature = webkit_dom_document_query_selector (
+               document, ".-x-evo-top-signature", NULL);
+       signature = webkit_dom_document_query_selector (
+               document, ".-x-evo-signature-wrapper", NULL);
+       if (signature) {
+               if (top_signature) {
+                       WebKitDOMElement *spacer;
+
+                       webkit_dom_node_insert_before (
+                               WEBKIT_DOM_NODE (wrapper),
+                               WEBKIT_DOM_NODE (signature),
+                               start_bottom ?
+                                       WEBKIT_DOM_NODE (content_wrapper) :
+                                       webkit_dom_node_get_next_sibling (
+                                               WEBKIT_DOM_NODE (paragraph)),
+                               NULL);
+                       /* Insert NL after the signature */
+                       spacer = e_editor_dom_prepare_paragraph (editor_page, FALSE);
+                       element_add_class (spacer, "-x-evo-top-signature-spacer");
+                       webkit_dom_node_insert_before (
+                               WEBKIT_DOM_NODE (wrapper),
+                               WEBKIT_DOM_NODE (spacer),
+                               webkit_dom_node_get_next_sibling (
+                                       WEBKIT_DOM_NODE (signature)),
+                               NULL);
+               } else {
+                       webkit_dom_node_insert_before (
+                               WEBKIT_DOM_NODE (wrapper),
+                               WEBKIT_DOM_NODE (signature),
+                               webkit_dom_node_get_next_sibling (WEBKIT_DOM_NODE (
+                                       start_bottom ? paragraph : content_wrapper)),
+                               NULL);
+               }
+       }
+
+       /* Move credits to the body */
+       list = webkit_dom_document_query_selector_all (
+               document, "span.-x-evo-to-body[data-credits]", NULL);
+       length = webkit_dom_node_list_get_length (list);
+       for (ii = 0; ii < length; ii++) {
+               char *credits;
+               WebKitDOMElement *element;
+               WebKitDOMNode *node = webkit_dom_node_list_item (list, ii);
+
+               element = e_editor_dom_get_paragraph_element (editor_page, -1, 0);
+               credits = webkit_dom_element_get_attribute (WEBKIT_DOM_ELEMENT (node), "data-credits");
+               if (credits)
+                       webkit_dom_html_element_set_inner_text (WEBKIT_DOM_HTML_ELEMENT (element), credits, 
NULL);
+               g_free (credits);
+
+               webkit_dom_node_insert_before (
+                       WEBKIT_DOM_NODE (wrapper),
+                       WEBKIT_DOM_NODE (element),
+                       WEBKIT_DOM_NODE (content_wrapper),
+                       NULL);
+
+               remove_node (node);
+               g_object_unref (node);
+       }
+       g_clear_object (&list);
+
+       /* Move headers to body */
+       list = webkit_dom_document_query_selector_all (
+               document, "div[data-headers]", NULL);
+       length = webkit_dom_node_list_get_length (list);
+       for (ii = 0; ii < length; ii++) {
+               WebKitDOMNode *node;
+
+               node = webkit_dom_node_list_item (list, ii);
+               webkit_dom_element_remove_attribute (
+                       WEBKIT_DOM_ELEMENT (node), "data-headers");
+               e_editor_dom_set_paragraph_style (editor_page, WEBKIT_DOM_ELEMENT (node), -1, 0, NULL);
+               webkit_dom_node_insert_before (
+                       WEBKIT_DOM_NODE (wrapper),
+                       node,
+                       WEBKIT_DOM_NODE (content_wrapper),
+                       NULL);
+
+               g_object_unref (node);
+       }
+       g_clear_object (&list);
+
+       repair_gmail_blockquotes (document);
+       remove_thunderbird_signature (document);
+       create_text_markers_for_citations_in_element (WEBKIT_DOM_ELEMENT (body));
+
+       if (preferred_text && *preferred_text)
+               webkit_dom_html_element_set_inner_text (
+                       WEBKIT_DOM_HTML_ELEMENT (content_wrapper), preferred_text, NULL);
+       else {
+               gchar *inner_text;
+
+               inner_text = webkit_dom_html_element_get_inner_text (body);
+               webkit_dom_html_element_set_inner_text (
+                       WEBKIT_DOM_HTML_ELEMENT (content_wrapper), inner_text, NULL);
+
+               g_free (inner_text);
+       }
+
+       inner_html = webkit_dom_element_get_inner_html (content_wrapper);
+
+       /* Replace the old body with the new one. */
+       node = webkit_dom_node_clone_node_with_error (WEBKIT_DOM_NODE (body), FALSE, NULL);
+       webkit_dom_node_replace_child (
+               webkit_dom_node_get_parent_node (WEBKIT_DOM_NODE (body)),
+               node,
+               WEBKIT_DOM_NODE (body),
+               NULL);
+       body = WEBKIT_DOM_HTML_ELEMENT (node);
+
+       /* Copy all to nodes to the new body. */
+       while ((node = webkit_dom_node_get_last_child (WEBKIT_DOM_NODE (wrapper)))) {
+               webkit_dom_node_insert_before (
+                       WEBKIT_DOM_NODE (body),
+                       WEBKIT_DOM_NODE (node),
+                       webkit_dom_node_get_first_child (WEBKIT_DOM_NODE (body)),
+                       NULL);
+       }
+       remove_node (WEBKIT_DOM_NODE (wrapper));
+
+       if (inner_html && !*inner_html)
+               empty = TRUE;
+
+       length = webkit_dom_element_get_child_element_count (WEBKIT_DOM_ELEMENT (body));
+       if (length <= 1) {
+               empty = TRUE;
+               if (length == 1) {
+                       WebKitDOMNode *child;
+
+                       child = webkit_dom_node_get_first_child (WEBKIT_DOM_NODE (body));
+                       empty = child && WEBKIT_DOM_IS_HTML_BR_ELEMENT (child);
+               }
+       }
+
+       if (preferred_text && *preferred_text)
+               empty = FALSE;
+
+       if (!empty)
+               parse_html_into_blocks (editor_page, content_wrapper, NULL, inner_html);
+       else
+               webkit_dom_node_append_child (
+                       WEBKIT_DOM_NODE (content_wrapper),
+                       WEBKIT_DOM_NODE (e_editor_dom_prepare_paragraph (editor_page, FALSE)),
+                       NULL);
+
+       if (!cite_body) {
+               if (!empty) {
+                       WebKitDOMNode *child;
+
+                       while ((child = webkit_dom_node_get_first_child (WEBKIT_DOM_NODE (content_wrapper)))) 
{
+                               webkit_dom_node_insert_before (
+                                       webkit_dom_node_get_parent_node (WEBKIT_DOM_NODE (content_wrapper)),
+                                       child,
+                                       WEBKIT_DOM_NODE (content_wrapper),
+                                       NULL);
+                       }
+               }
+
+               remove_node (WEBKIT_DOM_NODE (content_wrapper));
+       }
+
+       /* If not editing a message, don't add any new block and just place
+        * the caret in the beginning of content. We want to have the same
+        * behaviour when editing message as new or we start replying on top. */
+       if (!signature && !start_bottom) {
+               WebKitDOMNode *child;
+
+               remove_node (WEBKIT_DOM_NODE (paragraph));
+               child = webkit_dom_node_get_first_child (WEBKIT_DOM_NODE (body));
+               if (child)
+                       dom_add_selection_markers_into_element_start (
+                               document, WEBKIT_DOM_ELEMENT (child), NULL, NULL);
+       }
+
+       if ((paragraph = webkit_dom_document_get_element_by_id (document, "-x-evo-last-br")))
+               webkit_dom_element_remove_attribute (paragraph, "id");
+       if ((paragraph = webkit_dom_document_get_element_by_id (document, "-x-evo-first-br")))
+               webkit_dom_element_remove_attribute (paragraph, "id");
+
+       e_editor_dom_merge_siblings_if_necessary (editor_page, NULL);
+
+       if (!e_editor_page_get_html_mode (editor_page)) {
+               e_editor_dom_wrap_paragraphs_in_document (editor_page);
+
+               quote_plain_text_elements_after_wrapping_in_document (editor_page);
+       }
+
+       clear_attributes (editor_page);
+
+       e_editor_dom_selection_restore (editor_page);
+       e_editor_dom_force_spell_check_in_viewport (editor_page);
+
+       /* Register on input event that is called when the content (body) is modified */
+       webkit_dom_event_target_add_event_listener (
+               WEBKIT_DOM_EVENT_TARGET (body),
+               "input",
+               G_CALLBACK (body_input_event_cb),
+               FALSE,
+               editor_page);
+
+       webkit_dom_event_target_add_event_listener (
+               WEBKIT_DOM_EVENT_TARGET (dom_window),
+               "scroll",
+               G_CALLBACK (body_scroll_event_cb),
+               FALSE,
+               editor_page);
+
+       /* Intentionally leak the WebKitDOMDOMWindow object here as otherwise the
+        * callback won't be set up. */
+
+       register_html_events_handlers (editor_page, body);
+       e_editor_dom_set_monospace_font_family_on_body (
+               WEBKIT_DOM_ELEMENT (body), e_editor_page_get_html_mode (editor_page));
+
+       g_free (inner_html);
+}
+
+void
+e_editor_dom_convert_and_insert_html_into_selection (EEditorPage *editor_page,
+                                                    const gchar *html,
+                                                    gboolean is_html)
+{
+       WebKitDOMDocument *document;
+       WebKitDOMElement *selection_start_marker, *selection_end_marker, *element;
+       WebKitDOMNode *node, *current_block;
+       EEditorHistoryEvent *ev = NULL;
+       EEditorUndoRedoManager *manager;
+       gboolean has_selection;
+       gchar *inner_html;
+       gint citation_level;
+
+       g_return_if_fail (E_IS_EDITOR_PAGE (editor_page));
+
+       document = e_editor_page_get_document (editor_page);
+
+       e_editor_dom_remove_input_event_listener_from_body (editor_page);
+
+       e_editor_dom_selection_save (editor_page);
+       selection_start_marker = webkit_dom_document_get_element_by_id (
+               document, "-x-evo-selection-start-marker");
+       selection_end_marker = webkit_dom_document_get_element_by_id (
+               document, "-x-evo-selection-end-marker");
+       current_block = e_editor_dom_get_parent_block_node_from_child (
+               WEBKIT_DOM_NODE (selection_start_marker));
+       if (WEBKIT_DOM_IS_HTML_BODY_ELEMENT (current_block))
+               current_block = NULL;
+
+       manager = e_editor_page_get_undo_redo_manager (editor_page);
+       if (!e_editor_undo_redo_manager_is_operation_in_progress (manager)) {
+               gboolean collapsed;
+
+               ev = g_new0 (EEditorHistoryEvent, 1);
+               ev->type = HISTORY_PASTE;
+/* FIXME WK2
+               ev->type = HISTORY_PASTE_AS_TEXT;*/
+
+               collapsed = e_editor_dom_selection_is_collapsed (editor_page);
+               e_editor_dom_selection_get_coordinates (editor_page,
+                       &ev->before.start.x,
+                       &ev->before.start.y,
+                       &ev->before.end.x,
+                       &ev->before.end.y);
+
+               if (!collapsed) {
+                       ev->before.end.x = ev->before.start.x;
+                       ev->before.end.y = ev->before.start.y;
+               }
+
+               ev->data.string.from = NULL;
+               ev->data.string.to = g_strdup (html);
+       }
+
+       element = webkit_dom_document_create_element (document, "div", NULL);
+       if (is_html) {
+               gchar *inner_text;
+
+               if (strstr (html, "\n")) {
+                       GRegex *regex;
+                       gchar *tmp;
+
+                       /* Strip new lines between tags to avoid unwanted line breaks. */
+                       regex = g_regex_new ("\\>[\\s]+\\<", 0, 0, NULL);
+                       tmp = g_regex_replace (
+                               regex, html, -1, 0, "> <", 0, NULL);
+                       webkit_dom_element_set_inner_html (element, tmp, NULL);
+                       g_free (tmp);
+                       g_regex_unref (regex);
+               } else {
+                       webkit_dom_element_set_inner_html (element, html, NULL);
+               }
+
+               inner_text = webkit_dom_html_element_get_inner_text (
+                       WEBKIT_DOM_HTML_ELEMENT (element));
+               webkit_dom_html_element_set_inner_text (
+                       WEBKIT_DOM_HTML_ELEMENT (element), inner_text, NULL);
+
+               g_free (inner_text);
+       } else
+               webkit_dom_html_element_set_inner_text (
+                       WEBKIT_DOM_HTML_ELEMENT (element), html, NULL);
+
+       inner_html = webkit_dom_element_get_inner_html (element);
+       parse_html_into_blocks (editor_page, element, WEBKIT_DOM_ELEMENT (current_block), inner_html);
+
+       g_free (inner_html);
+
+       has_selection = !e_editor_dom_selection_is_collapsed (editor_page);
+       if (has_selection && !e_editor_undo_redo_manager_is_operation_in_progress (manager)) {
+               WebKitDOMRange *range = NULL;
+
+               range = e_editor_dom_get_current_range (editor_page);
+               insert_delete_event (editor_page, range);
+               g_clear_object (&range);
+
+               /* Remove the text that was meant to be replaced by the pasted text */
+               e_editor_dom_exec_command (editor_page, E_CONTENT_EDITOR_COMMAND_DELETE, NULL);
+
+               e_editor_dom_selection_save (editor_page);
+
+               selection_start_marker = webkit_dom_document_get_element_by_id (
+                       document, "-x-evo-selection-start-marker");
+               selection_end_marker = webkit_dom_document_get_element_by_id (
+                       document, "-x-evo-selection-end-marker");
+               current_block = e_editor_dom_get_parent_block_node_from_child (
+                       WEBKIT_DOM_NODE (selection_start_marker));
+               if (WEBKIT_DOM_IS_HTML_BODY_ELEMENT (current_block))
+                       current_block = NULL;
+       }
+
+       citation_level = e_editor_dom_get_citation_level (WEBKIT_DOM_NODE (selection_end_marker), FALSE);
+       /* Pasting into the citation */
+       if (citation_level > 0) {
+               gint length;
+               gint word_wrap_length;
+               WebKitDOMElement *br;
+               WebKitDOMNode *first_paragraph, *last_paragraph;
+               WebKitDOMNode *child, *parent, *current_block;
+
+               first_paragraph = webkit_dom_node_get_first_child (
+                       WEBKIT_DOM_NODE (element));
+               last_paragraph = webkit_dom_node_get_last_child (
+                       WEBKIT_DOM_NODE (element));
+
+               word_wrap_length = e_editor_page_get_word_wrap_length (editor_page);
+               length = word_wrap_length - 2 * citation_level;
+
+               /* Pasting text that was parsed just into one paragraph */
+               if (webkit_dom_node_is_same_node (first_paragraph, last_paragraph)) {
+                       WebKitDOMNode *child, *parent, *parent_block;
+
+                       parent_block = e_editor_dom_get_parent_block_node_from_child (
+                               WEBKIT_DOM_NODE (selection_start_marker));
+
+                       e_editor_dom_remove_quoting_from_element (WEBKIT_DOM_ELEMENT (parent_block));
+                       e_editor_dom_remove_wrapping_from_element (WEBKIT_DOM_ELEMENT (parent_block));
+
+                       parent = webkit_dom_node_get_parent_node (WEBKIT_DOM_NODE (selection_start_marker));
+                       while ((child = webkit_dom_node_get_first_child (first_paragraph))) {
+                               if (WEBKIT_DOM_IS_HTML_ANCHOR_ELEMENT (parent) &&
+                                   WEBKIT_DOM_IS_HTML_ANCHOR_ELEMENT (child)) {
+                                       WebKitDOMNode *anchor_child;
+
+                                       while ((anchor_child = webkit_dom_node_get_first_child (child)))
+                                               webkit_dom_node_insert_before (
+                                                       webkit_dom_node_get_parent_node (
+                                                               WEBKIT_DOM_NODE (selection_start_marker)),
+                                                       anchor_child,
+                                                       WEBKIT_DOM_NODE (selection_start_marker),
+                                                       NULL);
+                                       remove_node (child);
+                               } else
+                                       webkit_dom_node_insert_before (
+                                               webkit_dom_node_get_parent_node (
+                                                       WEBKIT_DOM_NODE (selection_start_marker)),
+                                               child,
+                                               WEBKIT_DOM_NODE (selection_start_marker),
+                                               NULL);
+                       }
+
+                       if (WEBKIT_DOM_IS_HTML_ANCHOR_ELEMENT (parent)) {
+                               gchar *text_content;
+
+                               text_content = webkit_dom_node_get_text_content (parent);
+
+                               webkit_dom_element_set_attribute (
+                                       WEBKIT_DOM_ELEMENT (parent),
+                                       "href",
+                                       text_content,
+                                       NULL);
+                               g_free (text_content);
+                       }
+
+                       parent_block = WEBKIT_DOM_NODE (
+                               e_editor_dom_wrap_paragraph_length (editor_page, WEBKIT_DOM_ELEMENT 
(parent_block), length));
+                       webkit_dom_node_normalize (parent_block);
+                       e_editor_dom_quote_plain_text_element_after_wrapping (editor_page, WEBKIT_DOM_ELEMENT 
(parent_block), citation_level);
+
+                       e_editor_dom_selection_restore (editor_page);
+
+                       g_object_unref (element);
+                       goto out;
+               }
+
+               /* Pasting content parsed into the multiple paragraphs */
+               parent = e_editor_dom_get_parent_block_node_from_child (
+                       WEBKIT_DOM_NODE (selection_start_marker));
+
+               e_editor_dom_remove_quoting_from_element (WEBKIT_DOM_ELEMENT (parent));
+               e_editor_dom_remove_wrapping_from_element (WEBKIT_DOM_ELEMENT (parent));
+
+               /* Move the elements from the first paragraph before the selection start element */
+               while ((child = webkit_dom_node_get_first_child (first_paragraph)))
+                       webkit_dom_node_insert_before (
+                               parent,
+                               child,
+                               WEBKIT_DOM_NODE (selection_start_marker),
+                               NULL);
+
+               remove_node (first_paragraph);
+
+               /* If the BR element is on the last position, remove it as we don't need it */
+               child = webkit_dom_node_get_last_child (parent);
+               if (WEBKIT_DOM_IS_HTML_BR_ELEMENT (child))
+                       remove_node (child);
+
+               parent = e_editor_dom_get_parent_block_node_from_child (
+                       WEBKIT_DOM_NODE (selection_end_marker));
+
+               child = webkit_dom_node_get_next_sibling (
+                       WEBKIT_DOM_NODE (selection_end_marker));
+               /* Move the elements that are in the same paragraph as the selection end
+                * on the end of pasted text, but avoid BR on the end of paragraph */
+               while (child) {
+                       WebKitDOMNode *next_child =
+                               webkit_dom_node_get_next_sibling  (child);
+                       if (!(!next_child && WEBKIT_DOM_IS_HTML_BR_ELEMENT (child)))
+                               webkit_dom_node_append_child (last_paragraph, child, NULL);
+                       child = next_child;
+               }
+
+               current_block = e_editor_dom_get_parent_block_node_from_child (
+                       WEBKIT_DOM_NODE (selection_start_marker));
+
+               dom_remove_selection_markers (document);
+
+               /* Caret will be restored on the end of pasted text */
+               webkit_dom_node_append_child (
+                       last_paragraph,
+                       WEBKIT_DOM_NODE (dom_create_selection_marker (document, TRUE)),
+                       NULL);
+
+               webkit_dom_node_append_child (
+                       last_paragraph,
+                       WEBKIT_DOM_NODE (dom_create_selection_marker (document, FALSE)),
+                       NULL);
+
+               /* Insert the paragraph with the end of the pasted text after
+                * the paragraph that contains the selection end */
+               webkit_dom_node_insert_before (
+                       webkit_dom_node_get_parent_node (parent),
+                       last_paragraph,
+                       webkit_dom_node_get_next_sibling (parent),
+                       NULL);
+
+               /* Wrap, quote and move all paragraphs from pasted text into the body */
+               while ((child = webkit_dom_node_get_first_child (WEBKIT_DOM_NODE (element)))) {
+                       child = WEBKIT_DOM_NODE (e_editor_dom_wrap_paragraph_length (
+                               editor_page, WEBKIT_DOM_ELEMENT (child), length));
+                       e_editor_dom_quote_plain_text_element_after_wrapping (editor_page, WEBKIT_DOM_ELEMENT 
(child), citation_level);
+                       webkit_dom_node_insert_before (
+                               webkit_dom_node_get_parent_node (last_paragraph),
+                               child,
+                               last_paragraph,
+                               NULL);
+               }
+
+               webkit_dom_node_normalize (last_paragraph);
+
+               last_paragraph = WEBKIT_DOM_NODE (
+                       e_editor_dom_wrap_paragraph_length (
+                               editor_page, WEBKIT_DOM_ELEMENT (last_paragraph), length));
+               e_editor_dom_quote_plain_text_element_after_wrapping (editor_page, WEBKIT_DOM_ELEMENT 
(last_paragraph), citation_level);
+
+               e_editor_dom_remove_quoting_from_element (WEBKIT_DOM_ELEMENT (parent));
+               e_editor_dom_remove_wrapping_from_element (WEBKIT_DOM_ELEMENT (parent));
+
+               current_block = WEBKIT_DOM_NODE (e_editor_dom_wrap_paragraph_length (
+                       editor_page, WEBKIT_DOM_ELEMENT (current_block), length));
+               e_editor_dom_quote_plain_text_element_after_wrapping (editor_page, WEBKIT_DOM_ELEMENT 
(current_block), citation_level);
+
+               if ((br = webkit_dom_document_get_element_by_id (document, "-x-evo-last-br")))
+                       webkit_dom_element_remove_attribute (br, "class");
+
+               if ((br = webkit_dom_document_get_element_by_id (document, "-x-evo-first-br")))
+                       webkit_dom_element_remove_attribute (br, "class");
+
+               if (ev) {
+                       e_editor_dom_selection_get_coordinates (editor_page,
+                               &ev->after.start.x,
+                               &ev->after.start.y,
+                               &ev->after.end.x,
+                               &ev->after.end.y);
+                       e_editor_undo_redo_manager_insert_history_event (manager, ev);
+               }
+
+               e_editor_dom_selection_restore (editor_page);
+
+               g_object_unref (element);
+               goto out;
+       }
+
+       remove_node (WEBKIT_DOM_NODE (selection_start_marker));
+       remove_node (WEBKIT_DOM_NODE (selection_end_marker));
+
+       /* If the text to insert was converted just to one block, pass just its
+        * text to WebKit otherwise WebKit will insert unwanted block with
+        * extra new line. */
+       if (!webkit_dom_node_get_next_sibling (webkit_dom_node_get_first_child (WEBKIT_DOM_NODE (element))))
+               inner_html = webkit_dom_element_get_inner_html (
+                       WEBKIT_DOM_ELEMENT (webkit_dom_node_get_first_child (WEBKIT_DOM_NODE (element))));
+       else
+               inner_html = webkit_dom_element_get_inner_html (WEBKIT_DOM_ELEMENT (element));
+
+       e_editor_dom_exec_command (editor_page, E_CONTENT_EDITOR_COMMAND_INSERT_HTML, inner_html);
+
+       if (g_str_has_suffix (inner_html, " "))
+               e_editor_dom_exec_command (editor_page, E_CONTENT_EDITOR_COMMAND_INSERT_TEXT, " ");
+
+       g_free (inner_html);
+
+       g_object_unref (element);
+       e_editor_dom_selection_save (editor_page);
+
+       element = webkit_dom_document_query_selector (
+               document, "* > br#-x-evo-first-br", NULL);
+       if (element) {
+               WebKitDOMNode *sibling;
+               WebKitDOMNode *parent;
+
+               parent = webkit_dom_node_get_parent_node (
+                       WEBKIT_DOM_NODE (element));
+
+               sibling = webkit_dom_node_get_previous_sibling (parent);
+               if (sibling)
+                       remove_node (WEBKIT_DOM_NODE (parent));
+               else
+                       webkit_dom_element_remove_attribute (element, "class");
+       }
+
+       element = webkit_dom_document_query_selector (
+               document, "* > br#-x-evo-last-br", NULL);
+       if (element) {
+               WebKitDOMNode *parent;
+               WebKitDOMNode *child;
+
+               parent = webkit_dom_node_get_parent_node (
+                       WEBKIT_DOM_NODE (element));
+
+               node = webkit_dom_node_get_next_sibling (WEBKIT_DOM_NODE (parent));
+               if (node) {
+                       node = webkit_dom_node_get_first_child (node);
+                       if (node) {
+                               inner_html = webkit_dom_node_get_text_content (node);
+                               if (g_str_has_prefix (inner_html, UNICODE_NBSP))
+                                       webkit_dom_character_data_replace_data (
+                                               WEBKIT_DOM_CHARACTER_DATA (node), 0, 1, "", NULL);
+                               g_free (inner_html);
+                       }
+               }
+
+               selection_end_marker = webkit_dom_document_get_element_by_id (
+                       document, "-x-evo-selection-end-marker");
+
+               if (has_selection) {
+                       /* Everything after the selection end marker have to be in separate
+                        * paragraph */
+                       child = webkit_dom_node_get_next_sibling (
+                               WEBKIT_DOM_NODE (selection_end_marker));
+                       /* Move the elements that are in the same paragraph as the selection end
+                        * on the end of pasted text, but avoid BR on the end of paragraph */
+                       while (child) {
+                               WebKitDOMNode *next_child =
+                                       webkit_dom_node_get_next_sibling  (child);
+                               if (!(!next_child && WEBKIT_DOM_IS_HTML_BR_ELEMENT (child)))
+                                       webkit_dom_node_append_child (parent, child, NULL);
+                               child = next_child;
+                       }
+
+                       remove_node (WEBKIT_DOM_NODE (element));
+
+                       webkit_dom_node_insert_before (
+                               webkit_dom_node_get_parent_node (
+                                       webkit_dom_node_get_parent_node (
+                                               WEBKIT_DOM_NODE (selection_end_marker))),
+                               parent,
+                               webkit_dom_node_get_next_sibling (
+                                       webkit_dom_node_get_parent_node (
+                                               WEBKIT_DOM_NODE (selection_end_marker))),
+                               NULL);
+                       node = parent;
+               } else {
+                       node = webkit_dom_node_get_next_sibling (parent);
+                       if (!node) {
+                               fix_structure_after_pasting_multiline_content (parent);
+                               if (!webkit_dom_node_get_first_child (parent))
+                                       remove_node (parent);
+                       }
+               }
+
+               if (node) {
+                       /* Restore caret on the end of pasted text */
+                       webkit_dom_node_insert_before (
+                               node,
+                               WEBKIT_DOM_NODE (selection_end_marker),
+                               webkit_dom_node_get_first_child (node),
+                               NULL);
+
+                       selection_start_marker = webkit_dom_document_get_element_by_id (
+                               document, "-x-evo-selection-start-marker");
+                       webkit_dom_node_insert_before (
+                               node,
+                               WEBKIT_DOM_NODE (selection_start_marker),
+                               webkit_dom_node_get_first_child (node),
+                               NULL);
+               }
+
+               if (element)
+                       webkit_dom_element_remove_attribute (element, "class");
+
+               if (webkit_dom_node_get_next_sibling (WEBKIT_DOM_NODE (parent)) && !has_selection)
+                       remove_node (parent);
+       } else {
+               /* When pasting the content that was copied from the composer, WebKit
+                * restores the selection wrongly, thus is saved wrongly and we have
+                * to fix it */
+               WebKitDOMNode *block, *parent, *clone1, *clone2;
+
+               selection_start_marker = webkit_dom_document_get_element_by_id (
+                       document, "-x-evo-selection-start-marker");
+               selection_end_marker = webkit_dom_document_get_element_by_id (
+                       document, "-x-evo-selection-end-marker");
+
+               block = e_editor_dom_get_parent_block_node_from_child (
+                       WEBKIT_DOM_NODE (selection_start_marker));
+               parent = webkit_dom_node_get_parent_node (block);
+               webkit_dom_element_remove_attribute (WEBKIT_DOM_ELEMENT (parent), "id");
+
+               /* Check if WebKit created wrong structure */
+               clone1 = webkit_dom_node_clone_node_with_error (WEBKIT_DOM_NODE (block), FALSE, NULL);
+               clone2 = webkit_dom_node_clone_node_with_error (WEBKIT_DOM_NODE (parent), FALSE, NULL);
+               if (webkit_dom_node_is_equal_node (clone1, clone2) ||
+                   (WEBKIT_DOM_IS_HTML_DIV_ELEMENT (clone1) && WEBKIT_DOM_IS_HTML_DIV_ELEMENT (clone2) &&
+                    !element_has_class (WEBKIT_DOM_ELEMENT (clone2), "-x-evo-indented"))) {
+                       fix_structure_after_pasting_multiline_content (block);
+                       if (g_strcmp0 (html, "\n") == 0) {
+                               WebKitDOMElement *br;
+
+                               br = webkit_dom_document_create_element (document, "br", NULL);
+                               webkit_dom_node_append_child (
+                                       parent, WEBKIT_DOM_NODE (br), NULL);
+
+                               webkit_dom_node_insert_before (
+                                       parent,
+                                       WEBKIT_DOM_NODE (selection_start_marker),
+                                       webkit_dom_node_get_last_child (parent),
+                                       NULL);
+                       } else if (!webkit_dom_node_get_first_child (parent))
+                               remove_node (parent);
+               }
+
+               webkit_dom_node_insert_before (
+                       webkit_dom_node_get_parent_node (
+                               WEBKIT_DOM_NODE (selection_start_marker)),
+                       WEBKIT_DOM_NODE (selection_end_marker),
+                       webkit_dom_node_get_next_sibling (
+                               WEBKIT_DOM_NODE (selection_start_marker)),
+                       NULL);
+       }
+
+       if (ev) {
+               e_editor_dom_selection_get_coordinates (editor_page,
+                       &ev->after.start.x,
+                       &ev->after.start.y,
+                       &ev->after.end.x,
+                       &ev->after.end.y);
+               e_editor_undo_redo_manager_insert_history_event (manager, ev);
+       }
+
+       e_editor_dom_selection_restore (editor_page);
+ out:
+       e_editor_dom_check_magic_links (editor_page, FALSE);
+       e_editor_dom_force_spell_check_in_viewport (editor_page);
+       e_editor_dom_scroll_to_caret (editor_page);
+
+       e_editor_dom_register_input_event_listener_on_body (editor_page);
+
+       e_editor_page_emit_content_changed (editor_page);
+}
+
+static gint
+get_indentation_level (WebKitDOMElement *element)
+{
+       WebKitDOMElement *parent;
+       gint level = 0;
+
+       if (!element)
+               return 0;
+
+       if (element_has_class (element, "-x-evo-indented"))
+               level++;
+
+       parent = webkit_dom_node_get_parent_element (WEBKIT_DOM_NODE (element));
+       /* Count level of indentation */
+       while (parent && !WEBKIT_DOM_IS_HTML_BODY_ELEMENT (parent)) {
+               if (element_has_class (parent, "-x-evo-indented"))
+                       level++;
+
+               parent = webkit_dom_node_get_parent_element (WEBKIT_DOM_NODE (parent));
+       }
+
+       return level;
+}
+
+static void
+process_indented_element (WebKitDOMElement *element)
+{
+       gchar *spaces;
+       WebKitDOMNode *child;
+
+       if (!element)
+               return;
+
+       spaces = g_strnfill (4 * get_indentation_level (element), ' ');
+
+       child = webkit_dom_node_get_first_child (WEBKIT_DOM_NODE (element));
+       while (child) {
+               /* If next sibling is indented blockqoute skip it,
+                * it will be processed afterwards */
+               if (WEBKIT_DOM_IS_ELEMENT (child) &&
+                   element_has_class (WEBKIT_DOM_ELEMENT (child), "-x-evo-indented"))
+                       child = webkit_dom_node_get_next_sibling (child);
+
+               if (WEBKIT_DOM_IS_TEXT (child)) {
+                       gchar *text_content;
+                       gchar *indented_text;
+
+                       text_content = webkit_dom_text_get_whole_text (WEBKIT_DOM_TEXT (child));
+                       indented_text = g_strconcat (spaces, text_content, NULL);
+
+                       webkit_dom_text_replace_whole_text (
+                               WEBKIT_DOM_TEXT (child),
+                               indented_text,
+                               NULL);
+
+                       g_free (text_content);
+                       g_free (indented_text);
+               }
+
+               if (!child)
+                       break;
+
+               /* Move to next node */
+               if (webkit_dom_node_has_child_nodes (child))
+                       child = webkit_dom_node_get_first_child (child);
+               else if (webkit_dom_node_get_next_sibling (child))
+                       child = webkit_dom_node_get_next_sibling (child);
+               else {
+                       if (webkit_dom_node_is_equal_node (WEBKIT_DOM_NODE (element), child))
+                               break;
+
+                       child = webkit_dom_node_get_parent_node (child);
+                       if (child)
+                               child = webkit_dom_node_get_next_sibling (child);
+               }
+       }
+       g_free (spaces);
+
+       webkit_dom_element_remove_attribute (element, "style");
+}
+
+static void
+process_quote_nodes (WebKitDOMElement *blockquote)
+{
+       WebKitDOMNodeList *list = NULL;
+       int jj, length;
+
+       /* Replace quote nodes with symbols */
+       list = webkit_dom_element_query_selector_all (
+               blockquote, "span.-x-evo-quoted", NULL);
+       length = webkit_dom_node_list_get_length (list);
+       for (jj = 0; jj < length; jj++) {
+               WebKitDOMNode *quoted_node;
+               gchar *text_content;
+
+               quoted_node = webkit_dom_node_list_item (list, jj);
+               text_content = webkit_dom_node_get_text_content (quoted_node);
+               webkit_dom_element_set_outer_html (
+                       WEBKIT_DOM_ELEMENT (quoted_node), text_content, NULL);
+
+               g_free (text_content);
+               g_object_unref (quoted_node);
+       }
+       g_clear_object (&list);
+}
+
+/* Taken from GtkHTML */
+static gchar *
+get_alpha_value (gint value,
+                 gboolean lower)
+{
+       GString *str;
+       gchar *rv;
+       gint add = lower ? 'a' : 'A';
+
+       str = g_string_new (". ");
+
+       do {
+               g_string_prepend_c (str, ((value - 1) % 26) + add);
+               value = (value - 1) / 26;
+       } while (value);
+
+       rv = str->str;
+       g_string_free (str, FALSE);
+
+       return rv;
+}
+
+/* Taken from GtkHTML */
+static gchar *
+get_roman_value (gint value,
+                 gboolean lower)
+{
+       GString *str;
+       const gchar *base = "IVXLCDM";
+       gchar *rv;
+       gint b, r, add = lower ? 'a' - 'A' : 0;
+
+       if (value > 3999)
+               return g_strdup ("?. ");
+
+       str = g_string_new (". ");
+
+       for (b = 0; value > 0 && b < 7 - 1; b += 2, value /= 10) {
+               r = value % 10;
+               if (r != 0) {
+                       if (r < 4) {
+                               for (; r; r--)
+                                       g_string_prepend_c (str, base[b] + add);
+                       } else if (r == 4) {
+                               g_string_prepend_c (str, base[b + 1] + add);
+                               g_string_prepend_c (str, base[b] + add);
+                       } else if (r == 5) {
+                               g_string_prepend_c (str, base[b + 1] + add);
+                       } else if (r < 9) {
+                               for (; r > 5; r--)
+                                       g_string_prepend_c (str, base[b] + add);
+                               g_string_prepend_c (str, base[b + 1] + add);
+                       } else if (r == 9) {
+                               g_string_prepend_c (str, base[b + 2] + add);
+                               g_string_prepend_c (str, base[b] + add);
+                       }
+               }
+       }
+
+       rv = str->str;
+       g_string_free (str, FALSE);
+
+       return rv;
+}
+
+static void
+process_list_to_plain_text (EEditorPage *editor_page,
+                            WebKitDOMElement *element,
+                            gint level,
+                            GString *output)
+{
+       EContentEditorBlockFormat format;
+       EContentEditorAlignment alignment;
+       gint counter = 1;
+       gboolean empty = TRUE;
+       gchar *indent_per_level;
+       WebKitDOMNode *item;
+       gint word_wrap_length;
+
+       g_return_if_fail (E_IS_EDITOR_PAGE (editor_page));
+
+       indent_per_level = g_strnfill (SPACES_PER_LIST_LEVEL, ' ');
+       word_wrap_length = e_editor_page_get_word_wrap_length (editor_page);
+       format = dom_get_list_format_from_node (
+               WEBKIT_DOM_NODE (element));
+
+       /* Process list items to plain text */
+       item = webkit_dom_node_get_first_child (WEBKIT_DOM_NODE (element));
+       while (item) {
+               if (WEBKIT_DOM_IS_HTML_BR_ELEMENT (item))
+                       g_string_append (output, "\n");
+
+               if (WEBKIT_DOM_IS_HTML_LI_ELEMENT (item)) {
+                       gchar *space = NULL, *item_str = NULL;
+                       gint ii = 0;
+                       WebKitDOMElement *wrapped;
+                       GString *item_value = g_string_new ("");
+
+                       empty = FALSE;
+
+                       alignment = e_editor_dom_get_list_alignment_from_node (
+                               WEBKIT_DOM_NODE (item));
+
+                       wrapped = webkit_dom_element_query_selector (
+                               WEBKIT_DOM_ELEMENT (item), ".-x-evo-wrap-br", NULL);
+                       /* Wrapped text */
+                       if (wrapped) {
+                               WebKitDOMNode *node = webkit_dom_node_get_first_child (item);
+                               GString *line = g_string_new ("");
+
+                               while (node) {
+                                       if (WEBKIT_DOM_IS_HTML_BR_ELEMENT (node) &&
+                                           element_has_class (WEBKIT_DOM_ELEMENT (node), "-x-evo-wrap-br")) {
+                                               g_string_append (line, "\n");
+                                               /* put spaces before line characters -> wordwraplength - 
indentation */
+                                               for (ii = 0; ii < level; ii++)
+                                                       g_string_append (line, indent_per_level);
+                                               if (WEBKIT_DOM_IS_HTML_O_LIST_ELEMENT (element))
+                                                       g_string_append (line, indent_per_level);
+                                               g_string_append (item_value, line->str);
+                                               g_string_erase (line, 0, -1);
+                                       } else {
+                                               /* append text from node to line */
+                                               gchar *text_content;
+                                               text_content = webkit_dom_node_get_text_content (node);
+                                               g_string_append (line, text_content);
+                                               g_free (text_content);
+                                       }
+                                       node = webkit_dom_node_get_next_sibling (node);
+                               }
+
+                               if (alignment == E_CONTENT_EDITOR_ALIGNMENT_LEFT)
+                                       g_string_append (item_value, line->str);
+
+                               if (alignment == E_CONTENT_EDITOR_ALIGNMENT_CENTER) {
+                                       gchar *fill = NULL;
+                                       gint fill_length;
+
+                                       fill_length = word_wrap_length - g_utf8_strlen (line->str, -1);
+                                       fill_length -= ii * SPACES_PER_LIST_LEVEL;
+                                       if (WEBKIT_DOM_IS_HTML_O_LIST_ELEMENT (element))
+                                               fill_length += SPACES_PER_LIST_LEVEL;
+                                       fill_length /= 2;
+
+                                       if (fill_length < 0)
+                                               fill_length = 0;
+
+                                       fill = g_strnfill (fill_length, ' ');
+
+                                       g_string_append (item_value, fill);
+                                       g_string_append (item_value, line->str);
+                                       g_free (fill);
+                               }
+
+                               if (alignment == E_CONTENT_EDITOR_ALIGNMENT_RIGHT) {
+                                       gchar *fill = NULL;
+                                       gint fill_length;
+
+                                       fill_length = word_wrap_length - g_utf8_strlen (line->str, -1);
+                                       fill_length -= ii * SPACES_PER_LIST_LEVEL;
+
+                                       if (fill_length < 0)
+                                               fill_length = 0;
+
+                                       fill = g_strnfill (fill_length, ' ');
+
+                                       g_string_append (item_value, fill);
+                                       g_string_append (item_value, line->str);
+                                       g_free (fill);
+                               }
+                               g_string_free (line, TRUE);
+                               /* that same here */
+                       } else {
+                               gchar *text_content =
+                                       webkit_dom_node_get_text_content (item);
+
+                               g_string_append (item_value, text_content);
+                               g_free (text_content);
+                       }
+
+                       if (format == E_CONTENT_EDITOR_BLOCK_FORMAT_UNORDERED_LIST) {
+                               space = g_strnfill (SPACES_PER_LIST_LEVEL - 2, ' ');
+                               item_str = g_strdup_printf (
+                                       "%s* %s", space, item_value->str);
+                               g_free (space);
+                       }
+
+                       if (format == E_CONTENT_EDITOR_BLOCK_FORMAT_ORDERED_LIST) {
+                               gint length = 1, tmp = counter, spaces_count;
+
+                               while ((tmp = tmp / 10) > 1)
+                                       length++;
+
+                               if (tmp == 1)
+                                       length++;
+
+                               spaces_count = SPACES_ORDERED_LIST_FIRST_LEVEL - 2 - length;
+                               if (spaces_count > 0)
+                                       space = g_strnfill (spaces_count, ' ');
+
+                               item_str = g_strdup_printf (
+                                       "%s%d. %s", space && *space ? space : "", counter, item_value->str);
+                               g_free (space);
+                       }
+
+                       if (format > E_CONTENT_EDITOR_BLOCK_FORMAT_ORDERED_LIST) {
+                               gchar *value, spaces_count;
+
+                               if (format == E_CONTENT_EDITOR_BLOCK_FORMAT_ORDERED_LIST_ALPHA)
+                                       value = get_alpha_value (counter, FALSE);
+                               else
+                                       value = get_roman_value (counter, FALSE);
+
+                               spaces_count = SPACES_ORDERED_LIST_FIRST_LEVEL - strlen (value);
+                               if (spaces_count > 0)
+                                       space = g_strnfill (spaces_count, ' ');
+                               item_str = g_strdup_printf (
+                                       "%s%s%s", space && *space ? space : "" , value, item_value->str);
+                               g_free (space);
+                               g_free (value);
+                       }
+
+                       if (alignment == E_CONTENT_EDITOR_ALIGNMENT_LEFT) {
+                               for (ii = 0; ii < level - 1; ii++) {
+                                       g_string_append (output, indent_per_level);
+                               }
+                               if (WEBKIT_DOM_IS_HTML_U_LIST_ELEMENT (element))
+                                       if (dom_node_find_parent_element (item, "OL"))
+                                               g_string_append (output, indent_per_level);
+                               g_string_append (output, item_str);
+                       }
+
+                       if (alignment == E_CONTENT_EDITOR_ALIGNMENT_RIGHT) {
+                               if (!wrapped) {
+                                       gchar *fill = NULL;
+                                       gint fill_length;
+
+                                       fill_length = word_wrap_length - g_utf8_strlen (item_str, -1);
+                                       fill_length -= ii * SPACES_PER_LIST_LEVEL;
+
+                                       if (fill_length < 0)
+                                               fill_length = 0;
+
+                                       if (g_str_has_suffix (item_str, " "))
+                                               fill_length++;
+
+                                       fill = g_strnfill (fill_length, ' ');
+
+                                       g_string_append (output, fill);
+                                       g_free (fill);
+                               }
+                               if (g_str_has_suffix (item_str, " "))
+                                       g_string_append_len (output, item_str, g_utf8_strlen (item_str, -1) - 
1);
+                               else
+                                       g_string_append (output, item_str);
+                       }
+
+                       if (alignment == E_CONTENT_EDITOR_ALIGNMENT_CENTER) {
+                               if (!wrapped) {
+                                       gchar *fill = NULL;
+                                       gint fill_length = 0;
+
+                                       for (ii = 0; ii < level - 1; ii++)
+                                               g_string_append (output, indent_per_level);
+
+                                       fill_length = word_wrap_length - g_utf8_strlen (item_str, -1);
+                                       fill_length -= ii * SPACES_PER_LIST_LEVEL;
+                                       if (WEBKIT_DOM_IS_HTML_O_LIST_ELEMENT (element))
+                                               fill_length += SPACES_PER_LIST_LEVEL;
+                                       fill_length /= 2;
+
+                                       if (fill_length < 0)
+                                               fill_length = 0;
+
+                                       if (g_str_has_suffix (item_str, " "))
+                                               fill_length++;
+
+                                       fill = g_strnfill (fill_length, ' ');
+
+                                       g_string_append (output, fill);
+                                       g_free (fill);
+                               }
+                               if (g_str_has_suffix (item_str, " "))
+                                       g_string_append_len (output, item_str, g_utf8_strlen (item_str, -1) - 
1);
+                               else
+                                       g_string_append (output, item_str);
+                       }
+
+                       counter++;
+                       item = webkit_dom_node_get_next_sibling (item);
+                       if (item)
+                               g_string_append (output, "\n");
+
+                       g_free (item_str);
+                       g_string_free (item_value, TRUE);
+               } else if (node_is_list (item)) {
+                       process_list_to_plain_text (
+                               editor_page, WEBKIT_DOM_ELEMENT (item), level + 1, output);
+                       item = webkit_dom_node_get_next_sibling (item);
+               } else {
+                       item = webkit_dom_node_get_next_sibling (item);
+               }
+       }
+
+       if (webkit_dom_node_get_next_sibling (WEBKIT_DOM_NODE (element)) && !empty)
+               g_string_append (output, "\n");
+
+       g_free (indent_per_level);
+}
+
+static void
+remove_base_attributes (WebKitDOMElement *element)
+{
+       webkit_dom_element_remove_attribute (element, "class");
+       webkit_dom_element_remove_attribute (element, "id");
+       webkit_dom_element_remove_attribute (element, "name");
+}
+
+static void
+remove_evolution_attributes (WebKitDOMElement *element)
+{
+       webkit_dom_element_remove_attribute (element, "data-evo-paragraph");
+       webkit_dom_element_remove_attribute (element, "data-converted");
+       webkit_dom_element_remove_attribute (element, "data-edit-as-new");
+       webkit_dom_element_remove_attribute (element, "data-evo-draft");
+       webkit_dom_element_remove_attribute (element, "data-inline");
+       webkit_dom_element_remove_attribute (element, "data-uri");
+       webkit_dom_element_remove_attribute (element, "data-message");
+       webkit_dom_element_remove_attribute (element, "data-name");
+       webkit_dom_element_remove_attribute (element, "data-new-message");
+       webkit_dom_element_remove_attribute (element, "data-user-wrapped");
+       webkit_dom_element_remove_attribute (element, "data-evo-plain-text");
+       webkit_dom_element_remove_attribute (element, "data-plain-text-style");
+       webkit_dom_element_remove_attribute (element, "data-style");
+       webkit_dom_element_remove_attribute (element, "spellcheck");
+}
+
+static void
+convert_element_from_html_to_plain_text (EEditorPage *editor_page,
+                                         WebKitDOMElement *element,
+                                         gboolean *wrap,
+                                         gboolean *quote)
+{
+       WebKitDOMDocument *document;
+       WebKitDOMElement *top_signature, *signature, *blockquote, *main_blockquote;
+       WebKitDOMNode *signature_clone, *from;
+       WebKitDOMNodeList *list = NULL;
+       gint blockquotes_count, ii, length;
+       gchar *inner_text, *inner_html;
+
+       g_return_if_fail (E_IS_EDITOR_PAGE (editor_page));
+
+       document = e_editor_page_get_document (editor_page);
+       top_signature = webkit_dom_element_query_selector (
+               element, ".-x-evo-top-signature", NULL);
+       signature = webkit_dom_element_query_selector (
+               element, "span.-x-evo-signature", NULL);
+       main_blockquote = webkit_dom_element_query_selector (
+               element, "#-x-evo-main-cite", NULL);
+
+       blockquote = webkit_dom_document_create_element (
+               document, "blockquote", NULL);
+
+       if (main_blockquote) {
+               webkit_dom_element_set_attribute (
+                       blockquote, "type", "cite", NULL);
+               from = WEBKIT_DOM_NODE (main_blockquote);
+       } else {
+               if (signature) {
+                       WebKitDOMNode *parent = webkit_dom_node_get_parent_node (
+                               WEBKIT_DOM_NODE (signature));
+                       signature_clone = webkit_dom_node_clone_node_with_error (parent, TRUE, NULL);
+                       remove_node (parent);
+               }
+               from = WEBKIT_DOM_NODE (element);
+       }
+
+       blockquotes_count = create_text_markers_for_citations_in_element (WEBKIT_DOM_ELEMENT (from));
+       create_text_markers_for_selection_in_element (WEBKIT_DOM_ELEMENT (from));
+
+       /* Add the missing BR elements on the end of DIV and P elements to
+        * preserve the line breaks. But we need to do that just in case that
+        * there is another element that contains text. */
+       list = webkit_dom_element_query_selector_all (WEBKIT_DOM_ELEMENT (from), "div, p", NULL);
+       length = webkit_dom_node_list_get_length (list);
+       for (ii = 0; ii < length; ii++) {
+               gboolean insert = TRUE;
+               WebKitDOMNode *node, *next_sibling;
+
+               node = webkit_dom_node_list_item (list, ii);
+               next_sibling = webkit_dom_node_get_next_sibling (node);
+
+               if (!next_sibling)
+                       insert = FALSE;
+
+               while (insert && next_sibling) {
+                       if (!webkit_dom_node_has_child_nodes (next_sibling) &&
+                           !webkit_dom_node_get_next_sibling (next_sibling))
+                               insert = FALSE;
+                       next_sibling = webkit_dom_node_get_next_sibling (next_sibling);
+               }
+
+               if (insert && !WEBKIT_DOM_IS_HTML_BR_ELEMENT (webkit_dom_node_get_last_child (node)))
+                       webkit_dom_node_append_child (
+                               node,
+                               WEBKIT_DOM_NODE (webkit_dom_document_create_element (document, "br", NULL)),
+                               NULL);
+
+               g_object_unref (node);
+       }
+       g_clear_object (&list);
+
+       inner_text = webkit_dom_html_element_get_inner_text (
+               WEBKIT_DOM_HTML_ELEMENT (from));
+
+       webkit_dom_html_element_set_inner_text (
+               WEBKIT_DOM_HTML_ELEMENT (blockquote), inner_text, NULL);
+
+       inner_html = webkit_dom_element_get_inner_html (blockquote);
+
+       parse_html_into_blocks (editor_page,
+               main_blockquote ? blockquote : WEBKIT_DOM_ELEMENT (element),
+               NULL,
+               inner_html);
+
+       if (main_blockquote) {
+               webkit_dom_node_replace_child (
+                       webkit_dom_node_get_parent_node (
+                               WEBKIT_DOM_NODE (main_blockquote)),
+                       WEBKIT_DOM_NODE (blockquote),
+                       WEBKIT_DOM_NODE (main_blockquote),
+                       NULL);
+
+               remove_evolution_attributes (WEBKIT_DOM_ELEMENT (element));
+       } else {
+               WebKitDOMNode *first_child;
+
+               if (signature) {
+                       if (!top_signature) {
+                               signature_clone = webkit_dom_node_append_child (
+                                       WEBKIT_DOM_NODE (element),
+                                       signature_clone,
+                                       NULL);
+                       } else {
+                               webkit_dom_node_insert_before (
+                                       WEBKIT_DOM_NODE (element),
+                                       signature_clone,
+                                       webkit_dom_node_get_first_child (
+                                               WEBKIT_DOM_NODE (element)),
+                                       NULL);
+                       }
+               }
+
+               first_child = webkit_dom_node_get_first_child (
+                       WEBKIT_DOM_NODE (element));
+               if (first_child) {
+                       if (!webkit_dom_node_has_child_nodes (first_child)) {
+                               webkit_dom_element_set_inner_html (
+                                       WEBKIT_DOM_ELEMENT (first_child),
+                                       "<br>",
+                                       NULL);
+                       }
+                       dom_add_selection_markers_into_element_start (
+                               document, WEBKIT_DOM_ELEMENT (first_child), NULL, NULL);
+               }
+       }
+
+       if (wrap)
+               *wrap = TRUE;
+       if (quote)
+               *quote = main_blockquote || blockquotes_count > 0;
+
+       webkit_dom_element_set_attribute (
+               WEBKIT_DOM_ELEMENT (element), "data-converted", "", NULL);
+
+       g_free (inner_text);
+       g_free (inner_html);
+}
+
+void
+e_editor_dom_convert_element_from_html_to_plain_text (EEditorPage *editor_page,
+                                                     WebKitDOMElement *element)
+{
+       g_return_if_fail (E_IS_EDITOR_PAGE (editor_page));
+
+       convert_element_from_html_to_plain_text (editor_page, element, NULL, NULL);
+}
+
+static void
+process_node_to_plain_text_changing_composer_mode (EEditorPage *editor_page,
+                                                   WebKitDOMNode *source)
+{
+       WebKitDOMElement *element;
+       WebKitDOMNamedNodeMap *attributes = NULL;
+       gint length, ii;
+
+       g_return_if_fail (E_IS_EDITOR_PAGE (editor_page));
+
+       attributes = webkit_dom_element_get_attributes (WEBKIT_DOM_ELEMENT (source));
+       length = webkit_dom_named_node_map_get_length (attributes);
+       for (ii = 0; ii < length; ii++) {
+               gchar *name = NULL;
+               WebKitDOMNode *attribute;
+
+               attribute = webkit_dom_named_node_map_item (attributes, ii);
+
+               name = webkit_dom_node_get_local_name (attribute);
+
+               if (g_strcmp0 (name, "bgcolor") == 0 ||
+                   g_strcmp0 (name, "text") == 0 ||
+                   g_strcmp0 (name, "vlink") == 0 ||
+                   g_strcmp0 (name, "link") == 0) {
+
+                       webkit_dom_element_remove_attribute_node (
+                               WEBKIT_DOM_ELEMENT (source),
+                               WEBKIT_DOM_ATTR (attribute),
+                               NULL);
+                       length--;
+               }
+               g_free (name);
+               g_object_unref (attribute);
+       }
+       g_clear_object (&attributes);
+
+       /* Signature */
+       element = webkit_dom_element_query_selector (
+               WEBKIT_DOM_ELEMENT (source), "div.-x-evo-signature-wrapper", NULL);
+       if (element) {
+               WebKitDOMNode *first_child;
+
+               first_child = webkit_dom_node_get_first_child (WEBKIT_DOM_NODE (element));
+
+               convert_element_from_html_to_plain_text (
+                       editor_page, WEBKIT_DOM_ELEMENT (first_child), NULL, NULL);
+       }
+}
+
+/* This function is different than the others there as this needs to go through
+ * the DOM node by node and generate the plain text of their content. For some
+ * it will just take the text content, but for example the lists are not that
+ * easy. */
+static void
+process_node_to_plain_text_for_exporting (EEditorPage *editor_page,
+                                          WebKitDOMNode *source,
+                                          GString *buffer)
+{
+       WebKitDOMNodeList *nodes = NULL;
+       gboolean html_mode;
+       gchar *content = NULL;
+       gint ii, nodes_length;
+
+       html_mode = e_editor_page_get_html_mode (editor_page);
+
+       nodes = webkit_dom_node_get_child_nodes (source);
+       nodes_length = webkit_dom_node_list_get_length (nodes);
+
+       for (ii = 0; ii < nodes_length; ii++) {
+               WebKitDOMNode *child;
+               gboolean skip_node = FALSE;
+
+               child = webkit_dom_node_list_item (nodes, ii);
+
+               if (WEBKIT_DOM_IS_TEXT (child)) {
+                       gchar *class;
+                       const gchar *css_align = NULL;
+                       GRegex *regex;
+
+                       content = webkit_dom_node_get_text_content (child);
+                       if (strstr (content, UNICODE_ZERO_WIDTH_SPACE)) {
+                               gchar *tmp;
+
+                               regex = g_regex_new (UNICODE_ZERO_WIDTH_SPACE, 0, 0, NULL);
+                               tmp = g_regex_replace (
+                                       regex, content, -1, 0, "", 0, NULL);
+                               g_free (content);
+                               content = tmp;
+                               g_regex_unref (regex);
+                       }
+
+                       class = webkit_dom_element_get_class_name (WEBKIT_DOM_ELEMENT (source));
+                       if (class && (css_align = strstr (class, "-x-evo-align-"))) {
+                               gchar *content_with_align;
+                               gint word_wrap_length = e_editor_page_get_word_wrap_length (editor_page);
+
+                               if (!g_str_has_prefix (css_align + 13, "left")) {
+                                       gchar *align;
+                                       gint length;
+
+                                       if (g_str_has_prefix (css_align + 13, "center"))
+                                               length = (word_wrap_length - g_utf8_strlen (content, -1)) / 2;
+                                       else
+                                               length = word_wrap_length - g_utf8_strlen (content, -1);
+
+                                       if (length < 0)
+                                               length = 0;
+
+                                       if (g_str_has_suffix (content, " ")) {
+                                               char *tmp;
+
+                                               length++;
+                                               align = g_strnfill (length, ' ');
+
+                                               tmp = g_strndup (content, g_utf8_strlen (content, -1) -1);
+
+                                               content_with_align = g_strconcat (
+                                                       align, tmp, NULL);
+                                               g_free (tmp);
+                                       } else {
+                                               align = g_strnfill (length, ' ');
+
+                                               content_with_align = g_strconcat (
+                                                       align, content, NULL);
+                                       }
+
+                                       g_free (content);
+                                       g_free (align);
+                                       content = content_with_align;
+                               }
+                       }
+
+                       g_free (class);
+
+                       g_string_append (buffer, content);
+
+                       goto next;
+               }
+
+               if (!WEBKIT_DOM_IS_ELEMENT (child))
+                       goto next;
+
+               if (element_has_class (WEBKIT_DOM_ELEMENT (child), "Apple-tab-span")) {
+                       content = webkit_dom_node_get_text_content (child);
+                       g_string_append (buffer, content);
+                       g_free (content);
+                       skip_node = TRUE;
+                       goto next;
+               }
+
+               if (WEBKIT_DOM_IS_HTML_QUOTE_ELEMENT (child))
+                       process_quote_nodes (WEBKIT_DOM_ELEMENT (child));
+
+               if (WEBKIT_DOM_IS_HTML_DIV_ELEMENT (child) &&
+                   element_has_class (WEBKIT_DOM_ELEMENT (child), "-x-evo-indented"))
+                       process_indented_element (WEBKIT_DOM_ELEMENT (child));
+
+               if (node_is_list (child)) {
+                       process_list_to_plain_text (editor_page, WEBKIT_DOM_ELEMENT (child), 1, buffer);
+                       skip_node = TRUE;
+                       goto next;
+               }
+
+               if (element_has_class (WEBKIT_DOM_ELEMENT (child), "-x-evo-resizable-wrapper") &&
+                   !element_has_class (WEBKIT_DOM_ELEMENT (child), "-x-evo-smiley-wrapper")) {
+                       skip_node = TRUE;
+                       goto next;
+               }
+
+               /* Signature */
+               if (WEBKIT_DOM_IS_HTML_DIV_ELEMENT (child) &&
+                   element_has_class (WEBKIT_DOM_ELEMENT (child), "-x-evo-signature-wrapper")) {
+                       WebKitDOMNode *first_child;
+                       gchar *id;
+
+                       first_child = webkit_dom_node_get_first_child (child);
+
+                       skip_node = TRUE;
+                       /* Don't generate any text if the signature is set to None. */
+                       id = webkit_dom_element_get_id (WEBKIT_DOM_ELEMENT (first_child));
+                       if (g_strcmp0 (id, "none") == 0) {
+                               g_free (id);
+
+                               remove_node (child);
+                               goto next;
+                       }
+                       g_free (id);
+
+                       if (html_mode) {
+                               convert_element_from_html_to_plain_text (
+                                       editor_page, WEBKIT_DOM_ELEMENT (first_child), NULL, NULL);
+                               skip_node = FALSE;
+                       }
+
+                       goto next;
+               }
+
+               /* Replace smileys with their text representation */
+               if (element_has_class (WEBKIT_DOM_ELEMENT (child), "-x-evo-smiley-wrapper")) {
+                       WebKitDOMNode *text_version;
+
+                       text_version = webkit_dom_node_get_last_child (child);
+                       content = webkit_dom_html_element_get_inner_text (
+                               WEBKIT_DOM_HTML_ELEMENT (text_version));
+                       g_string_append (buffer, content);
+                       g_free (content);
+                       skip_node = TRUE;
+                       goto next;
+               }
+
+               if (WEBKIT_DOM_IS_HTML_BR_ELEMENT (child)) {
+                       g_string_append (buffer, "\n");
+                       goto next;
+               }
+
+               if (WEBKIT_DOM_IS_HTML_ANCHOR_ELEMENT (child)) {
+                       content = webkit_dom_html_element_get_inner_text (
+                               WEBKIT_DOM_HTML_ELEMENT (child));
+                       g_string_append (buffer, content);
+                       g_free (content);
+                       skip_node = TRUE;
+               }
+ next:
+               if (!skip_node && webkit_dom_node_has_child_nodes (child))
+                       process_node_to_plain_text_for_exporting (editor_page, child, buffer);
+               g_object_unref (child);
+       }
+       g_clear_object (&nodes);
+
+       if (!g_str_has_suffix (buffer->str, "\n") &&
+            (WEBKIT_DOM_IS_HTML_DIV_ELEMENT (source) ||
+             WEBKIT_DOM_IS_HTML_PARAGRAPH_ELEMENT (source) ||
+             WEBKIT_DOM_IS_HTML_PRE_ELEMENT (source) ||
+             WEBKIT_DOM_IS_HTML_QUOTE_ELEMENT (source)))
+               g_string_append (buffer, "\n");
+
+       if (g_str_has_suffix (buffer->str, "\n") &&
+           WEBKIT_DOM_IS_HTML_BODY_ELEMENT (source))
+               g_string_truncate (buffer, buffer->len - 1);
+}
+
+static void
+process_node_to_html_changing_composer_mode (EEditorPage *editor_page,
+                                             WebKitDOMNode *source)
+{
+}
+
+static void
+process_node_to_html_for_exporting (EEditorPage *editor_page,
+                                    WebKitDOMNode *source)
+{
+       WebKitDOMNodeList *list = NULL;
+       WebKitDOMHTMLCollection *collection = NULL;
+       WebKitDOMElement *element;
+       WebKitDOMDocument *document;
+       gint ii, length;
+
+       document = webkit_dom_node_get_owner_document (source);
+
+       remove_evolution_attributes (WEBKIT_DOM_ELEMENT (source));
+
+       /* Aligned elements */
+       list = webkit_dom_element_query_selector_all (WEBKIT_DOM_ELEMENT (source), 
"[class*=\"-x-evo-align\"]", NULL);
+       length = webkit_dom_node_list_get_length (list);
+
+       for (ii = 0; ii < length; ii++) {
+               gchar *class = NULL;
+               WebKitDOMNode *node;
+               gboolean center = FALSE;
+
+               node = webkit_dom_node_list_item (list, ii);
+               class = webkit_dom_element_get_class_name (WEBKIT_DOM_ELEMENT (node));
+               center = g_strrstr (class, "center") != NULL;
+               if (center || g_strrstr (class, "right")) {
+                       if (WEBKIT_DOM_IS_HTML_LI_ELEMENT (node))
+                               webkit_dom_element_set_attribute (
+                                       WEBKIT_DOM_ELEMENT (node),
+                                       "style",
+                                       center ?
+                                               "list-style-position: inside; text-align: center" :
+                                               "list-style-position: inside; text-align: right",
+                                       NULL);
+                       else
+                               webkit_dom_element_set_attribute (
+                                       WEBKIT_DOM_ELEMENT (node),
+                                       "style",
+                                       center ?
+                                               "text-align: center" :
+                                               "text-align: right",
+                                       NULL);
+               }
+               element_remove_class (WEBKIT_DOM_ELEMENT (node), "-x-evo-align-left");
+               element_remove_class (WEBKIT_DOM_ELEMENT (node), "-x-evo-align-center");
+               element_remove_class (WEBKIT_DOM_ELEMENT (node), "-x-evo-align-right");
+               g_free (class);
+               g_object_unref (node);
+       }
+       g_clear_object (&list);
+
+       /* Indented elements */
+       list = webkit_dom_element_query_selector_all (
+                       WEBKIT_DOM_ELEMENT (source), ".-x-evo-indented", NULL);
+       length = webkit_dom_node_list_get_length (list);
+       for (ii = 0; ii < length; ii++) {
+               WebKitDOMNode *node;
+
+               node = webkit_dom_node_list_item (list, ii);
+               element_remove_class (WEBKIT_DOM_ELEMENT (node), "-x-evo-indented");
+               remove_evolution_attributes (WEBKIT_DOM_ELEMENT (node));
+
+               g_object_unref (node);
+       }
+       g_clear_object (&list);
+
+       /* Tab characters */
+       list = webkit_dom_element_query_selector_all (
+               WEBKIT_DOM_ELEMENT (source), ".Apple-tab-span", NULL);
+       length = webkit_dom_node_list_get_length (list);
+       for (ii = 0; ii < length; ii++) {
+               gchar *text_content;
+               WebKitDOMNode *node;
+
+               node = webkit_dom_node_list_item (list, ii);
+               text_content = webkit_dom_node_get_text_content (node);
+               webkit_dom_node_insert_before (
+                       webkit_dom_node_get_parent_node (node),
+                       WEBKIT_DOM_NODE (webkit_dom_document_create_text_node (document, text_content)),
+                       node,
+                       NULL);
+
+               remove_node (node);
+               g_object_unref (node);
+       }
+       g_clear_object (&list);
+
+       list = webkit_dom_element_query_selector_all (
+                       WEBKIT_DOM_ELEMENT (source), ".-x-evo-quoted", NULL);
+       length = webkit_dom_node_list_get_length (list);
+       for (ii = 0; ii < length; ii++) {
+               WebKitDOMNode *quoted_node;
+               gchar *text_content;
+
+               quoted_node = webkit_dom_node_list_item (list, ii);
+               text_content = webkit_dom_node_get_text_content (quoted_node);
+               webkit_dom_element_set_outer_html (
+                       WEBKIT_DOM_ELEMENT (quoted_node), text_content, NULL);
+
+               g_free (text_content);
+               g_object_unref (quoted_node);
+       }
+       g_clear_object (&list);
+
+       /* Images */
+       list = webkit_dom_element_query_selector_all (
+               WEBKIT_DOM_ELEMENT (source), ".-x-evo-resizable-wrapper:not(.-x-evo-smiley-wrapper)", NULL);
+       length = webkit_dom_node_list_get_length (list);
+       for (ii = 0; ii < length; ii++) {
+               WebKitDOMNode *node, *image;
+
+               node = webkit_dom_node_list_item (list, ii);
+               image = webkit_dom_node_get_first_child (node);
+
+               if (WEBKIT_DOM_IS_HTML_IMAGE_ELEMENT (image)) {
+                       remove_evolution_attributes (
+                               WEBKIT_DOM_ELEMENT (image));
+
+                       webkit_dom_node_replace_child (
+                               webkit_dom_node_get_parent_node (node), image, node, NULL);
+               }
+
+               g_object_unref (node);
+       }
+       g_clear_object (&list);
+
+       /* Signature */
+       element = webkit_dom_element_query_selector (
+               WEBKIT_DOM_ELEMENT (source), "div.-x-evo-signature-wrapper", NULL);
+       if (element) {
+               WebKitDOMNode *first_child;
+               gchar *id;
+
+               /* Don't generate any text if the signature is set to None. */
+               first_child = webkit_dom_node_get_first_child (WEBKIT_DOM_NODE (element));
+               id = webkit_dom_element_get_id (WEBKIT_DOM_ELEMENT (first_child));
+               if (g_strcmp0 (id, "none") == 0) {
+                       remove_node (WEBKIT_DOM_NODE (element));
+               } else {
+                       remove_base_attributes (element);
+                       remove_base_attributes (WEBKIT_DOM_ELEMENT (first_child));
+                       remove_evolution_attributes (WEBKIT_DOM_ELEMENT (first_child));
+               }
+               g_free (id);
+       }
+
+       /* Smileys */
+       list = webkit_dom_element_query_selector_all (
+               WEBKIT_DOM_ELEMENT (source), ".-x-evo-smiley-wrapper", NULL);
+       length = webkit_dom_node_list_get_length (list);
+       for (ii = 0; ii < length; ii++) {
+               WebKitDOMNode *node;
+               WebKitDOMElement *img;
+
+               node = webkit_dom_node_list_item (list, ii);
+               img = WEBKIT_DOM_ELEMENT (webkit_dom_node_get_first_child (node));
+
+               remove_evolution_attributes (img);
+               remove_base_attributes (img);
+
+               webkit_dom_node_replace_child (
+                       webkit_dom_node_get_parent_node (node),
+                       WEBKIT_DOM_NODE (img),
+                       node,
+                       NULL);
+
+               g_object_unref (node);
+       }
+       g_clear_object (&list);
+
+       collection = webkit_dom_element_get_elements_by_tag_name_as_html_collection (
+               WEBKIT_DOM_ELEMENT (source), "pre");
+       length = webkit_dom_html_collection_get_length (collection);
+       for (ii = 0; ii < length; ii++) {
+               WebKitDOMNode *node;
+
+               node = webkit_dom_html_collection_item (collection, ii);
+               remove_evolution_attributes (WEBKIT_DOM_ELEMENT (node));
+               g_object_unref (node);
+       }
+       g_clear_object (&collection);
+
+       list = webkit_dom_element_query_selector_all (
+               WEBKIT_DOM_ELEMENT (source), "p[data-evo-paragraph]", NULL);
+       length = webkit_dom_node_list_get_length (list);
+       for (ii = 0; ii < length; ii++) {
+               WebKitDOMNode *node;
+
+               node = webkit_dom_node_list_item (list, ii);
+               remove_evolution_attributes (WEBKIT_DOM_ELEMENT (node));
+               remove_base_attributes (WEBKIT_DOM_ELEMENT (node));
+               g_object_unref (node);
+       }
+       g_clear_object (&list);
+
+       list = webkit_dom_element_query_selector_all (
+               WEBKIT_DOM_ELEMENT (source), "br.-x-evo-wrap-br", NULL);
+       length = webkit_dom_node_list_get_length (list);
+       for (ii = 0; ii < length; ii++) {
+               WebKitDOMNode *node;
+
+               node = webkit_dom_node_list_item (list, ii);
+               webkit_dom_element_remove_attribute (WEBKIT_DOM_ELEMENT (node), "class");
+               g_object_unref (node);
+       }
+       g_clear_object (&list);
+}
+
+static void
+remove_image_attributes_from_element (WebKitDOMElement *element)
+{
+       webkit_dom_element_remove_attribute (element, "background");
+       webkit_dom_element_remove_attribute (element, "data-uri");
+       webkit_dom_element_remove_attribute (element, "data-inline");
+       webkit_dom_element_remove_attribute (element, "data-name");
+}
+
+static void
+remove_background_images_in_element (WebKitDOMElement *element)
+{
+       gint length, ii;
+       WebKitDOMNodeList *images = NULL;
+
+       images = webkit_dom_element_query_selector_all (
+               element, "[background][data-inline]", NULL);
+
+       length = webkit_dom_node_list_get_length (images);
+       for (ii = 0; ii < length; ii++) {
+               WebKitDOMElement *image = WEBKIT_DOM_ELEMENT (
+                       webkit_dom_node_list_item (images, ii));
+
+               remove_image_attributes_from_element (image);
+               g_object_unref (image);
+       }
+       g_clear_object (&images);
+
+       remove_image_attributes_from_element (element);
+}
+
+static void
+remove_images_in_element (WebKitDOMElement *element)
+{
+       gint length, ii;
+       WebKitDOMNodeList *images = NULL;
+
+       images = webkit_dom_element_query_selector_all (
+               element, "img:not(.-x-evo-smiley-img)", NULL);
+
+       length = webkit_dom_node_list_get_length (images);
+       for (ii = 0; ii < length; ii++) {
+               WebKitDOMNode *node = webkit_dom_node_list_item (images, ii);
+               remove_node (node);
+               g_object_unref (node);
+       }
+       g_clear_object (&images);
+}
+
+static void
+remove_images (WebKitDOMDocument *document)
+{
+       remove_images_in_element (
+               WEBKIT_DOM_ELEMENT (webkit_dom_document_get_body (document)));
+}
+
+static void
+toggle_smileys (EEditorPage *editor_page)
+{
+       WebKitDOMDocument *document;
+       WebKitDOMNodeList *smileys = NULL;
+       gboolean html_mode;
+       gint length;
+       gint ii;
+
+       g_return_if_fail (E_IS_EDITOR_PAGE (editor_page));
+
+       document = e_editor_page_get_document (editor_page);
+       html_mode = e_editor_page_get_html_mode (editor_page);
+
+       smileys = webkit_dom_document_query_selector_all (
+               document, "img.-x-evo-smiley-img", NULL);
+
+       length = webkit_dom_node_list_get_length (smileys);
+       for (ii = 0; ii < length; ii++) {
+               WebKitDOMNode *img = webkit_dom_node_list_item (smileys, ii);
+               WebKitDOMElement *parent = webkit_dom_node_get_parent_element (img);
+
+               if (html_mode)
+                       element_add_class (parent, "-x-evo-resizable-wrapper");
+               else
+                       element_remove_class (parent, "-x-evo-resizable-wrapper");
+               g_object_unref (img);
+       }
+       g_clear_object (&smileys);
+}
+
+static void
+toggle_paragraphs_style_in_element (EEditorPage *editor_page,
+                                    WebKitDOMElement *element,
+                                    gboolean html_mode)
+{
+       gint ii, length;
+       WebKitDOMNodeList *paragraphs = NULL;
+
+       paragraphs = webkit_dom_element_query_selector_all (
+               element, ":not(td) > [data-evo-paragraph]", NULL);
+
+       length = webkit_dom_node_list_get_length (paragraphs);
+
+       for (ii = 0; ii < length; ii++) {
+               gchar *style;
+               const gchar *css_align;
+               WebKitDOMNode *node = webkit_dom_node_list_item (paragraphs, ii);
+
+               if (html_mode) {
+                       style = webkit_dom_element_get_attribute (
+                               WEBKIT_DOM_ELEMENT (node), "style");
+
+                       if (style && (css_align = strstr (style, "text-align: "))) {
+                               webkit_dom_element_set_attribute (
+                                       WEBKIT_DOM_ELEMENT (node),
+                                       "style",
+                                       g_str_has_prefix (css_align + 12, "center") ?
+                                               "text-align: center" :
+                                               "text-align: right",
+                                       NULL);
+                       } else {
+                               /* In HTML mode the paragraphs don't have width limit */
+                               webkit_dom_element_remove_attribute (
+                                       WEBKIT_DOM_ELEMENT (node), "style");
+                       }
+                       g_free (style);
+               } else {
+                       WebKitDOMNode *parent;
+
+                       parent = webkit_dom_node_get_parent_node (node);
+                       /* If the paragraph is inside indented paragraph don't set
+                        * the style as it will be inherited */
+                       if (WEBKIT_DOM_IS_HTML_BODY_ELEMENT (parent) && node_is_list (node)) {
+                               gint offset;
+
+                               offset = WEBKIT_DOM_IS_HTML_U_LIST_ELEMENT (node) ?
+                                       SPACES_PER_LIST_LEVEL : SPACES_ORDERED_LIST_FIRST_LEVEL;
+                               /* In plain text mode the paragraphs have width limit */
+                               e_editor_dom_set_paragraph_style (
+                                       editor_page, WEBKIT_DOM_ELEMENT (node), -1, -offset, NULL);
+                       } else if (!element_has_class (WEBKIT_DOM_ELEMENT (parent), "-x-evo-indented")) {
+                               const gchar *style_to_add = "";
+                               style = webkit_dom_element_get_attribute (
+                                       WEBKIT_DOM_ELEMENT (node), "style");
+
+                               if (style && (css_align = strstr (style, "text-align: "))) {
+                                       style_to_add = g_str_has_prefix (
+                                               css_align + 12, "center") ?
+                                                       "text-align: center;" :
+                                                       "text-align: right;";
+                               }
+
+                               /* In plain text mode the paragraphs have width limit */
+                               e_editor_dom_set_paragraph_style (
+                                       editor_page, WEBKIT_DOM_ELEMENT (node), -1, 0, style_to_add);
+
+                               g_free (style);
+                       }
+               }
+               g_object_unref (node);
+       }
+       g_clear_object (&paragraphs);
+}
+
+static void
+toggle_paragraphs_style (EEditorPage *editor_page)
+{
+       WebKitDOMDocument *document;
+
+       g_return_if_fail (E_IS_EDITOR_PAGE (editor_page));
+
+       document = e_editor_page_get_document (editor_page);
+
+       toggle_paragraphs_style_in_element (
+               editor_page,
+               WEBKIT_DOM_ELEMENT (webkit_dom_document_get_body (document)),
+               e_editor_page_get_html_mode (editor_page));
+}
+
+gchar *
+e_editor_dom_process_content_for_draft (EEditorPage *editor_page,
+                                       gboolean only_inner_body)
+{
+       WebKitDOMDocument *document;
+       WebKitDOMHTMLElement *body;
+       WebKitDOMElement *document_element;
+       WebKitDOMNodeList *list = NULL;
+       WebKitDOMNode *document_element_clone;
+       gboolean selection_saved = FALSE;
+       gchar *content;
+       gint ii, length;
+
+       g_return_val_if_fail (E_IS_EDITOR_PAGE (editor_page), NULL);
+
+       document = e_editor_page_get_document (editor_page);
+       body = webkit_dom_document_get_body (document);
+
+       webkit_dom_element_set_attribute (
+               WEBKIT_DOM_ELEMENT (body), "data-evo-draft", "", NULL);
+
+       if (webkit_dom_document_get_element_by_id (document, "-x-evo-selection-start-marker"))
+               selection_saved = TRUE;
+
+       if (!selection_saved)
+               e_editor_dom_selection_save (editor_page);
+
+       document_element = webkit_dom_document_get_document_element (document);
+
+       document_element_clone = webkit_dom_node_clone_node_with_error (
+               WEBKIT_DOM_NODE (document_element), TRUE, NULL);
+
+       list = webkit_dom_element_query_selector_all (
+               WEBKIT_DOM_ELEMENT (document_element_clone), "a.-x-evo-visited-link", NULL);
+       length = webkit_dom_node_list_get_length (list);
+       for (ii = 0; ii < length; ii++) {
+               WebKitDOMNode *anchor;
+
+               anchor = webkit_dom_node_list_item (list, ii);
+               webkit_dom_element_remove_attribute (WEBKIT_DOM_ELEMENT (anchor), "class");
+               g_object_unref (anchor);
+       }
+       g_clear_object (&list);
+
+       list = webkit_dom_element_query_selector_all (
+               WEBKIT_DOM_ELEMENT (document_element_clone), "#-x-evo-input-start", NULL);
+       length = webkit_dom_node_list_get_length (list);
+       for (ii = 0; ii < length; ii++) {
+               WebKitDOMNode *node;
+
+               node = webkit_dom_node_list_item (list, ii);
+               webkit_dom_element_remove_attribute (WEBKIT_DOM_ELEMENT (node), "id");
+               g_object_unref (node);
+       }
+       g_clear_object (&list);
+
+       if (only_inner_body) {
+               WebKitDOMElement *body;
+               WebKitDOMNode *first_child;
+
+               body = webkit_dom_element_query_selector (
+                       WEBKIT_DOM_ELEMENT (document_element_clone), "body", NULL);
+
+               first_child = webkit_dom_node_get_first_child (WEBKIT_DOM_NODE (body));
+
+               if (!e_editor_page_get_html_mode (editor_page))
+                       webkit_dom_element_set_attribute (
+                               WEBKIT_DOM_ELEMENT (first_child),
+                               "data-evo-signature-plain-text-mode",
+                               "",
+                               NULL);
+
+               content = webkit_dom_element_get_inner_html (body);
+
+               if (!e_editor_page_get_html_mode (editor_page))
+                       webkit_dom_element_remove_attribute (
+                               WEBKIT_DOM_ELEMENT (first_child),
+                               "data-evo-signature-plain-text-mode");
+       } else
+               content = webkit_dom_element_get_outer_html (
+                       WEBKIT_DOM_ELEMENT (document_element_clone));
+
+       webkit_dom_element_remove_attribute (
+               WEBKIT_DOM_ELEMENT (body), "data-evo-draft");
+
+       e_editor_dom_selection_restore (editor_page);
+       e_editor_dom_force_spell_check_in_viewport (editor_page);
+
+       if (selection_saved)
+               e_editor_dom_selection_save (editor_page);
+
+       return content;
+}
+
+static void
+toggle_indented_elements (EEditorPage *editor_page)
+{
+       gboolean html_mode;
+       gint ii, length;
+       WebKitDOMDocument *document;
+       WebKitDOMNodeList *list = NULL;
+
+       g_return_if_fail (E_IS_EDITOR_PAGE (editor_page));
+
+       document = e_editor_page_get_document (editor_page);
+       html_mode = e_editor_page_get_html_mode (editor_page);
+       list = webkit_dom_document_query_selector_all (document, ".-x-evo-indented", NULL);
+       length = webkit_dom_node_list_get_length (list);
+
+       for (ii = 0; ii < length; ii++) {
+               WebKitDOMNode *node = webkit_dom_node_list_item (list, ii);
+
+               if (html_mode)
+                       dom_element_swap_attributes (WEBKIT_DOM_ELEMENT (node), "style", 
"data-plain-text-style");
+               else
+                       dom_element_swap_attributes (WEBKIT_DOM_ELEMENT (node), "data-plain-text-style", 
"style");
+               g_object_unref (node);
+       }
+       g_clear_object (&list);
+}
+
+static void
+process_content_to_html_changing_composer_mode (EEditorPage *editor_page)
+{
+       WebKitDOMDocument *document;
+       WebKitDOMNode *body;
+       WebKitDOMElement *blockquote;
+
+       g_return_if_fail (E_IS_EDITOR_PAGE (editor_page));
+
+       document = e_editor_page_get_document (editor_page);
+       body = WEBKIT_DOM_NODE (webkit_dom_document_get_body (document));
+
+       webkit_dom_element_remove_attribute (
+               WEBKIT_DOM_ELEMENT (body), "data-evo-plain-text");
+       blockquote = webkit_dom_document_query_selector (
+               document, "blockquote[type|=cite]", NULL);
+
+       if (blockquote)
+               dom_dequote_plain_text (document);
+
+       toggle_paragraphs_style (editor_page);
+       toggle_smileys (editor_page);
+       remove_images (document);
+       e_editor_dom_remove_wrapping_from_element (WEBKIT_DOM_ELEMENT (body));
+
+       process_node_to_html_changing_composer_mode (editor_page, body);
+}
+
+static void
+wrap_paragraphs_in_quoted_content (EEditorPage *editor_page)
+{
+       WebKitDOMDocument *document;
+       WebKitDOMNodeList *paragraphs = NULL;
+       gint ii, length;
+
+       g_return_if_fail (E_IS_EDITOR_PAGE (editor_page));
+
+       document = e_editor_page_get_document (editor_page);
+       paragraphs = webkit_dom_document_query_selector_all (
+               document, "blockquote[type=cite] > [data-evo-paragraph]", NULL);
+
+       length = webkit_dom_node_list_get_length (paragraphs);
+       for (ii = 0; ii < length; ii++) {
+               WebKitDOMNode *paragraph;
+
+               paragraph = webkit_dom_node_list_item (paragraphs, ii);
+
+               e_editor_dom_wrap_paragraph (editor_page, WEBKIT_DOM_ELEMENT (paragraph));
+
+               g_object_unref (paragraph);
+       }
+       g_clear_object (&paragraphs);
+}
+
+static void
+process_content_to_plain_text_changing_composer_mode (EEditorPage *editor_page)
+{
+       WebKitDOMDocument *document;
+       WebKitDOMNode *body, *head, *node;
+       WebKitDOMElement *blockquote;
+
+       g_return_if_fail (E_IS_EDITOR_PAGE (editor_page));
+
+       document = e_editor_page_get_document (editor_page);
+       body = WEBKIT_DOM_NODE (webkit_dom_document_get_body (document));
+       head = WEBKIT_DOM_NODE (webkit_dom_document_get_head (document));
+
+       while ((node = webkit_dom_node_get_last_child (head)))
+               remove_node (node);
+
+       e_editor_dom_selection_save (editor_page);
+
+       webkit_dom_element_remove_attribute (
+               WEBKIT_DOM_ELEMENT (body), "data-user-colors");
+
+       webkit_dom_element_set_attribute (
+               WEBKIT_DOM_ELEMENT (body), "data-evo-plain-text", "", NULL);
+
+       blockquote = webkit_dom_document_query_selector (
+               document, "blockquote[type|=cite]", NULL);
+
+       if (blockquote) {
+               wrap_paragraphs_in_quoted_content (editor_page);
+               quote_plain_text_elements_after_wrapping_in_document (editor_page);
+       }
+
+       toggle_paragraphs_style (editor_page);
+       toggle_smileys (editor_page);
+       toggle_indented_elements (editor_page);
+       remove_images (document);
+       remove_background_images_in_element (WEBKIT_DOM_ELEMENT (body));
+
+       process_node_to_plain_text_changing_composer_mode (editor_page, body);
+
+       e_editor_dom_selection_restore (editor_page);
+       e_editor_dom_force_spell_check_in_viewport (editor_page);
+}
+
+gchar *
+e_editor_dom_process_content_to_plain_text_for_exporting (EEditorPage *editor_page)
+{
+       WebKitDOMDocument *document;
+       WebKitDOMNode *body, *source;
+       WebKitDOMNodeList *list = NULL;
+       WebKitDOMDOMWindow *dom_window = NULL;
+       WebKitDOMDOMSelection *dom_selection = NULL;
+       gboolean wrap = FALSE, quote = FALSE, remove_last_new_line = FALSE;
+       gint length, ii;
+       GString *plain_text;
+
+       g_return_val_if_fail (E_IS_EDITOR_PAGE (editor_page), NULL);
+
+       document = e_editor_page_get_document (editor_page);
+       plain_text = g_string_sized_new (1024);
+
+       body = WEBKIT_DOM_NODE (webkit_dom_document_get_body (document));
+       source = webkit_dom_node_clone_node_with_error (WEBKIT_DOM_NODE (body), TRUE, NULL);
+
+       e_editor_dom_selection_save (editor_page);
+
+       /* If composer is in HTML mode we have to move the content to plain version */
+       if (e_editor_page_get_html_mode (editor_page)) {
+               if (e_editor_dom_check_if_conversion_needed (editor_page)) {
+                       WebKitDOMElement *wrapper;
+                       WebKitDOMNode *child;
+
+                       wrapper = webkit_dom_document_create_element (document, "div", NULL);
+                       webkit_dom_element_set_id (wrapper, "-x-evo-html-to-plain-text-wrapper");
+                       while ((child = webkit_dom_node_get_first_child (source))) {
+                               webkit_dom_node_append_child (
+                                       WEBKIT_DOM_NODE (wrapper),
+                                       child,
+                                       NULL);
+                       }
+
+                       list = webkit_dom_element_query_selector_all (
+                               wrapper, "#-x-evo-input-start", NULL);
+
+                       length = webkit_dom_node_list_get_length (list);
+                       for (ii = 0; ii < length; ii++) {
+                               WebKitDOMNode *paragraph;
+
+                               paragraph = webkit_dom_node_list_item (list, ii);
+
+                               webkit_dom_element_remove_attribute (
+                                       WEBKIT_DOM_ELEMENT (paragraph), "id");
+                       }
+                       g_clear_object (&list);
+
+                       remove_images_in_element (wrapper);
+
+                       list = webkit_dom_element_query_selector_all (
+                               wrapper, "#-x-evo-html-to-plain-text-wrapper > :matches(ul, ol)", NULL);
+
+                       length = webkit_dom_node_list_get_length (list);
+                       for (ii = 0; ii < length; ii++) {
+                               WebKitDOMElement *list_pre;
+                               WebKitDOMNode *item;
+                               GString *list_plain_text;
+
+                               item = webkit_dom_node_list_item (list, ii);
+
+                               list_plain_text = g_string_new ("");
+
+                               process_list_to_plain_text (
+                                       editor_page, WEBKIT_DOM_ELEMENT (item), 1, list_plain_text);
+
+                               list_pre = webkit_dom_document_create_element (document, "pre", NULL);
+                               webkit_dom_html_element_set_inner_text (
+                                       WEBKIT_DOM_HTML_ELEMENT (list_pre),
+                                       g_string_free (list_plain_text, FALSE),
+                                       NULL);
+                               webkit_dom_node_replace_child (
+                                       WEBKIT_DOM_NODE (wrapper),
+                                       WEBKIT_DOM_NODE (list_pre),
+                                       item,
+                                       NULL);
+                               g_object_unref (item);
+                       }
+                       g_clear_object (&list);
+
+                       convert_element_from_html_to_plain_text (
+                               editor_page, wrapper, &wrap, &quote);
+
+                       source = WEBKIT_DOM_NODE (wrapper);
+
+                       remove_last_new_line = TRUE;
+               } else {
+                       toggle_paragraphs_style_in_element (
+                               editor_page, WEBKIT_DOM_ELEMENT (source), FALSE);
+                       remove_images_in_element (
+                               WEBKIT_DOM_ELEMENT (source));
+                       remove_background_images_in_element (
+                               WEBKIT_DOM_ELEMENT (source));
+               }
+       }
+
+       list = webkit_dom_element_query_selector_all (
+               WEBKIT_DOM_ELEMENT (source), "[data-evo-paragraph]", NULL);
+
+       dom_window = webkit_dom_document_get_default_view (document);
+       dom_selection = webkit_dom_dom_window_get_selection (dom_window);
+       webkit_dom_dom_selection_collapse_to_end (dom_selection, NULL);
+       g_clear_object (&dom_window);
+       g_clear_object (&dom_selection);
+
+       length = webkit_dom_node_list_get_length (list);
+       for (ii = 0; ii < length; ii++) {
+               WebKitDOMNode *paragraph;
+
+               paragraph = webkit_dom_node_list_item (list, ii);
+
+               if (node_is_list (paragraph)) {
+                       WebKitDOMNode *item = webkit_dom_node_get_first_child (paragraph);
+
+                       while (item) {
+                               WebKitDOMNode *next_item =
+                                       webkit_dom_node_get_next_sibling (item);
+
+                               if (WEBKIT_DOM_IS_HTML_LI_ELEMENT (item))
+                                       e_editor_dom_wrap_paragraph (editor_page, WEBKIT_DOM_ELEMENT (item));
+
+                               item = next_item;
+                       }
+               } else if (!webkit_dom_element_query_selector (WEBKIT_DOM_ELEMENT (paragraph), 
".-x-evo-wrap-br,.-x-evo-quoted", NULL)) {
+                       /* Dont't try to wrap the already wrapped content. */
+                       e_editor_dom_wrap_paragraph (editor_page, WEBKIT_DOM_ELEMENT (paragraph));
+               }
+               g_object_unref (paragraph);
+       }
+       g_clear_object (&list);
+
+       list = webkit_dom_element_query_selector_all (
+               WEBKIT_DOM_ELEMENT (source), "#-x-evo-selection-start-marker, #-x-evo-selection-end-marker", 
NULL);
+
+       length = webkit_dom_node_list_get_length (list);
+       for (ii = 0; ii < length; ii++) {
+               WebKitDOMNode *node = webkit_dom_node_list_item (list, ii);
+               WebKitDOMNode *parent = webkit_dom_node_get_parent_node (node);
+
+               remove_node (node);
+               g_object_unref (node);
+               webkit_dom_node_normalize (parent);
+       }
+       g_clear_object (&list);
+
+       if (quote)
+               quote_plain_text_recursive (document, source, source, 0);
+       else if (e_editor_page_get_html_mode (editor_page)) {
+               WebKitDOMElement *citation;
+
+               citation = webkit_dom_element_query_selector (
+                       WEBKIT_DOM_ELEMENT (source), "blockquote[type=cite]", NULL);
+               if (citation)
+                       quote_plain_text_recursive (document, source, source, 0);
+       }
+
+       process_node_to_plain_text_for_exporting (editor_page, source, plain_text);
+       /* Truncate the extra new line on the end of generated text as the
+        * check inside the previous function is based on whether the processed
+        * node is BODY or not, but in this case the content is wrapped in DIV. */
+       if (remove_last_new_line)
+               g_string_truncate (plain_text, plain_text->len - 1);
+
+       e_editor_dom_selection_restore (editor_page);
+
+       /* Return text content between <body> and </body> */
+       return g_string_free (plain_text, FALSE);
+}
+
+static void
+restore_image (WebKitDOMDocument *document,
+               const gchar *id,
+               const gchar *element_src)
+{
+       gchar *selector;
+       gint length, ii;
+       WebKitDOMNodeList *list = NULL;
+
+       selector = g_strconcat ("[data-inline][background=\"cid:", id, "\"]", NULL);
+       list = webkit_dom_document_query_selector_all (document, selector, NULL);
+       length = webkit_dom_node_list_get_length (list);
+       for (ii = 0; ii < length; ii++) {
+               WebKitDOMElement *element = WEBKIT_DOM_ELEMENT (
+                       webkit_dom_node_list_item (list, ii));
+
+               webkit_dom_element_set_attribute (element, "background", element_src, NULL);
+               g_object_unref (element);
+       }
+       g_free (selector);
+       g_clear_object (&list);
+
+       selector = g_strconcat ("[data-inline][src=\"cid:", id, "\"]", NULL);
+       list = webkit_dom_document_query_selector_all (document, selector, NULL);
+       length = webkit_dom_node_list_get_length (list);
+       for (ii = 0; ii < length; ii++) {
+               WebKitDOMElement *element = WEBKIT_DOM_ELEMENT (
+                       webkit_dom_node_list_item (list, ii));
+
+               webkit_dom_element_set_attribute (element, "src", element_src, NULL);
+               g_object_unref (element);
+       }
+       g_free (selector);
+       g_clear_object (&list);
+}
+
+void
+e_editor_dom_restore_images (EEditorPage *editor_page,
+                            GVariant *inline_images_to_restore)
+{
+       WebKitDOMDocument *document;
+       const gchar *element_src, *name, *id;
+       GVariantIter *iter;
+
+       g_return_if_fail (E_IS_EDITOR_PAGE (editor_page));
+
+       document = e_editor_page_get_document (editor_page);
+
+       g_variant_get (inline_images_to_restore, "a(sss)", &iter);
+       while (g_variant_iter_loop (iter, "(&s&s&s)", &element_src, &name, &id))
+               restore_image (document, id, element_src);
+
+       g_variant_iter_free (iter);
+}
+
+gchar *
+e_editor_dom_process_content_to_html_for_exporting (EEditorPage *editor_page)
+{
+       WebKitDOMDocument *document;
+       WebKitDOMElement *element;
+       WebKitDOMNode *node, *document_clone;
+       WebKitDOMNodeList *list = NULL;
+       GSettings *settings;
+       gint ii, length;
+       gchar *html_content;
+       gboolean send_editor_colors = FALSE;
+
+       g_return_val_if_fail (E_IS_EDITOR_PAGE (editor_page), NULL);
+
+       document = e_editor_page_get_document (editor_page);
+
+       document_clone = webkit_dom_node_clone_node_with_error (
+               WEBKIT_DOM_NODE (webkit_dom_document_get_document_element (document)), TRUE, NULL);
+       element = webkit_dom_element_query_selector (
+               WEBKIT_DOM_ELEMENT (document_clone), "style#-x-evo-quote-style", NULL);
+       if (element)
+               remove_node (WEBKIT_DOM_NODE (element));
+       element = webkit_dom_element_query_selector (
+               WEBKIT_DOM_ELEMENT (document_clone), "style#-x-evo-a-color-style", NULL);
+       if (element)
+               remove_node (WEBKIT_DOM_NODE (element));
+       element = webkit_dom_element_query_selector (
+               WEBKIT_DOM_ELEMENT (document_clone), "style#-x-evo-a-color-style-visited", NULL);
+       if (element)
+               remove_node (WEBKIT_DOM_NODE (element));
+       /* When the Ctrl + Enter is pressed for sending, the links are activated. */
+       element = webkit_dom_element_query_selector (
+               WEBKIT_DOM_ELEMENT (document_clone), "style#-x-evo-style-a", NULL);
+       if (element)
+               remove_node (WEBKIT_DOM_NODE (element));
+       node = WEBKIT_DOM_NODE (webkit_dom_element_query_selector (
+               WEBKIT_DOM_ELEMENT (document_clone), "body", NULL));
+       element = webkit_dom_element_query_selector (
+               WEBKIT_DOM_ELEMENT (node), "#-x-evo-selection-start-marker", NULL);
+       if (element)
+               remove_node (WEBKIT_DOM_NODE (element));
+       element = webkit_dom_element_query_selector (
+               WEBKIT_DOM_ELEMENT (node), "#-x-evo-selection-end-marker", NULL);
+       if (element)
+               remove_node (WEBKIT_DOM_NODE (element));
+
+       settings = e_util_ref_settings ("org.gnome.evolution.mail");
+       send_editor_colors = g_settings_get_boolean (settings, "composer-inherit-theme-colors");
+       g_object_unref (settings);
+
+       if (webkit_dom_element_has_attribute (WEBKIT_DOM_ELEMENT (node), "data-user-colors")) {
+               webkit_dom_element_remove_attribute (WEBKIT_DOM_ELEMENT (node), "data-user-colors");
+       } else if (!send_editor_colors) {
+               webkit_dom_element_remove_attribute (WEBKIT_DOM_ELEMENT (node), "bgcolor");
+               webkit_dom_element_remove_attribute (WEBKIT_DOM_ELEMENT (node), "text");
+               webkit_dom_element_remove_attribute (WEBKIT_DOM_ELEMENT (node), "link");
+               webkit_dom_element_remove_attribute (WEBKIT_DOM_ELEMENT (node), "vlink");
+       }
+
+       list = webkit_dom_element_query_selector_all (
+               WEBKIT_DOM_ELEMENT (node), "span[data-hidden-space]", NULL);
+       length = webkit_dom_node_list_get_length (list);
+       for (ii = 0; ii < length; ii++) {
+               WebKitDOMNode *hidden_space_node;
+
+               hidden_space_node = webkit_dom_node_list_item (list, ii);
+               remove_node (hidden_space_node);
+               g_object_unref (hidden_space_node);
+       }
+       g_clear_object (&list);
+
+       list = webkit_dom_element_query_selector_all (
+               WEBKIT_DOM_ELEMENT (node), "[data-style]", NULL);
+       length = webkit_dom_node_list_get_length (list);
+       for (ii = 0; ii < length; ii++) {
+               WebKitDOMNode *data_style_node;
+
+               data_style_node = webkit_dom_node_list_item (list, ii);
+
+               element_rename_attribute (WEBKIT_DOM_ELEMENT (data_style_node), "data-style", "style");
+               g_object_unref (data_style_node);
+       }
+       g_clear_object (&list);
+
+       process_node_to_html_for_exporting (editor_page, node);
+
+       html_content = webkit_dom_element_get_outer_html (
+               WEBKIT_DOM_ELEMENT (document_clone));
+
+       g_object_unref (document_clone);
+
+       return html_content;
+}
+
+void
+e_editor_dom_convert_when_changing_composer_mode (EEditorPage *editor_page)
+{
+       WebKitDOMDocument *document;
+       WebKitDOMHTMLElement *body;
+       gboolean quote = FALSE, wrap = FALSE;
+
+       g_return_if_fail (E_IS_EDITOR_PAGE (editor_page));
+
+       document = e_editor_page_get_document (editor_page);
+       body = webkit_dom_document_get_body (document);
+
+       convert_element_from_html_to_plain_text (
+               editor_page, WEBKIT_DOM_ELEMENT (body), &wrap, &quote);
+
+       if (wrap)
+               e_editor_dom_wrap_paragraphs_in_document (editor_page);
+
+       if (quote) {
+               e_editor_dom_selection_save (editor_page);
+               if (wrap)
+                       quote_plain_text_elements_after_wrapping_in_document (editor_page);
+               else
+                       body = WEBKIT_DOM_HTML_ELEMENT (dom_quote_plain_text (document));
+               e_editor_dom_selection_restore (editor_page);
+       }
+
+       toggle_paragraphs_style (editor_page);
+       toggle_smileys (editor_page);
+       remove_images (document);
+       remove_background_images_in_element (WEBKIT_DOM_ELEMENT (body));
+
+       clear_attributes (editor_page);
+
+       if (!e_editor_page_get_html_mode (editor_page))
+               webkit_dom_element_set_attribute (
+                       WEBKIT_DOM_ELEMENT (body), "data-evo-plain-text", "", NULL);
+       else
+               webkit_dom_element_remove_attribute (
+                       WEBKIT_DOM_ELEMENT (body), "data-evo-plain-text");
+
+       e_editor_dom_force_spell_check_in_viewport (editor_page);
+       e_editor_dom_scroll_to_caret (editor_page);
+}
+
+static void
+set_base64_to_element_attribute (GHashTable *inline_images,
+                                 WebKitDOMElement *element,
+                                 const gchar *attribute)
+{
+       gchar *attribute_value;
+       const gchar *base64_src;
+
+       attribute_value = webkit_dom_element_get_attribute (element, attribute);
+
+       if (attribute_value && (base64_src = g_hash_table_lookup (inline_images, attribute_value)) != NULL) {
+               const gchar *base64_data = strstr (base64_src, ";") + 1;
+               gchar *name;
+               glong name_length;
+
+               name_length =
+                       g_utf8_strlen (base64_src, -1) -
+                       g_utf8_strlen (base64_data, -1) - 1;
+               name = g_strndup (base64_src, name_length);
+
+               webkit_dom_element_set_attribute (element, "data-inline", "", NULL);
+               webkit_dom_element_set_attribute (element, "data-name", name, NULL);
+               webkit_dom_element_set_attribute (element, attribute, base64_data, NULL);
+
+               g_free (name);
+       }
+       g_free (attribute_value);
+}
+
+static void
+change_cid_images_src_to_base64 (EEditorPage *editor_page)
+{
+       WebKitDOMDocument *document;
+       WebKitDOMElement *document_element;
+       WebKitDOMNamedNodeMap *attributes = NULL;
+       WebKitDOMNodeList *list = NULL;
+       GHashTable *inline_images;
+       gint ii, length;
+
+       g_return_if_fail (E_IS_EDITOR_PAGE (editor_page));
+
+       document = e_editor_page_get_document (editor_page);
+       inline_images = e_editor_page_get_inline_images (editor_page);
+
+       document_element = webkit_dom_document_get_document_element (document);
+
+       list = webkit_dom_document_query_selector_all (document, "img[src^=\"cid:\"]", NULL);
+       length = webkit_dom_node_list_get_length (list);
+       for (ii = 0; ii < length; ii++) {
+               WebKitDOMNode *node = webkit_dom_node_list_item (list, ii);
+
+               set_base64_to_element_attribute (inline_images, WEBKIT_DOM_ELEMENT (node), "src");
+               g_object_unref (node);
+       }
+       g_clear_object (&list);
+
+       /* Namespaces */
+       attributes = webkit_dom_element_get_attributes (document_element);
+       length = webkit_dom_named_node_map_get_length (attributes);
+       for (ii = 0; ii < length; ii++) {
+               gchar *name;
+               WebKitDOMNode *node = webkit_dom_named_node_map_item (attributes, ii);
+
+               name = webkit_dom_node_get_local_name (node);
+
+               if (g_str_has_prefix (name, "xmlns:")) {
+                       const gchar *ns = name + 6;
+                       gchar *attribute_ns = g_strconcat (ns, ":src", NULL);
+                       gchar *selector = g_strconcat ("img[", ns, "\\:src^=\"cid:\"]", NULL);
+                       gint ns_length, jj;
+
+                       list = webkit_dom_document_query_selector_all (
+                               document, selector, NULL);
+                       ns_length = webkit_dom_node_list_get_length (list);
+                       for (jj = 0; jj < ns_length; jj++) {
+                               WebKitDOMNode *node = webkit_dom_node_list_item (list, jj);
+
+                               set_base64_to_element_attribute (
+                                       inline_images, WEBKIT_DOM_ELEMENT (node), attribute_ns);
+                               g_object_unref (node);
+                       }
+
+                       g_clear_object (&list);
+                       g_free (attribute_ns);
+                       g_free (selector);
+               }
+               g_object_unref (node);
+               g_free (name);
+       }
+       g_clear_object (&attributes);
+
+       list = webkit_dom_document_query_selector_all (
+               document, "[background^=\"cid:\"]", NULL);
+       length = webkit_dom_node_list_get_length (list);
+       for (ii = 0; ii < length; ii++) {
+               WebKitDOMNode *node = webkit_dom_node_list_item (list, ii);
+
+               set_base64_to_element_attribute (
+                       inline_images, WEBKIT_DOM_ELEMENT (node), "background");
+               g_object_unref (node);
+       }
+       g_clear_object (&list);
+}
+
+static void
+adapt_to_editor_dom_changes (WebKitDOMDocument *document)
+{
+       WebKitDOMHTMLCollection *collection = NULL;
+       gint ii, length;
+
+       /* Normal block code div.-x-evo-paragraph replaced by p[data-evo-paragraph] */
+       collection = webkit_dom_document_get_elements_by_class_name_as_html_collection (document, 
"-x-evo-paragraph");
+       length = webkit_dom_html_collection_get_length (collection);
+       for (ii = 0; ii < length; ii++) {
+               WebKitDOMNode *node, *child;
+               WebKitDOMElement *element;
+               gchar *style;
+
+               node = webkit_dom_html_collection_item (collection, ii);
+               element = webkit_dom_document_create_element (document, "p", NULL);
+               webkit_dom_element_set_attribute (element, "data-evo-paragraph", "", NULL);
+
+               webkit_dom_node_insert_before (
+                       webkit_dom_node_get_parent_node (node),
+                       WEBKIT_DOM_NODE (element),
+                       node,
+                       NULL);
+
+               while ((child = webkit_dom_node_get_first_child (node)))
+                       webkit_dom_node_append_child (WEBKIT_DOM_NODE (element), child, NULL);
+
+               style = webkit_dom_element_get_attribute (WEBKIT_DOM_ELEMENT (node), "style");
+               if (style)
+                       webkit_dom_element_set_attribute (element, "style", style, NULL);
+
+               remove_node (node);
+               g_object_unref (node);
+       }
+       g_clear_object (&collection);
+}
+
+void
+e_editor_dom_process_content_after_load (EEditorPage *editor_page)
+{
+       gboolean html_mode;
+       WebKitDOMDocument *document;
+       WebKitDOMHTMLElement *body;
+       WebKitDOMDOMWindow *dom_window = NULL;
+
+       g_return_if_fail (E_IS_EDITOR_PAGE (editor_page));
+
+       document = e_editor_page_get_document (editor_page);
+
+       /* Don't use CSS when possible to preserve compatibility with older
+        * versions of Evolution or other MUAs */
+       e_editor_dom_exec_command (editor_page, E_CONTENT_EDITOR_COMMAND_STYLE_WITH_CSS, "false");
+       e_editor_dom_exec_command (editor_page, E_CONTENT_EDITOR_COMMAND_DEFAULT_PARAGRAPH_SEPARATOR, "p");
+
+       body = webkit_dom_document_get_body (document);
+
+       webkit_dom_element_remove_attribute (WEBKIT_DOM_ELEMENT (body), "style");
+       html_mode = e_editor_page_get_html_mode (editor_page);
+       if (!html_mode)
+               webkit_dom_element_set_attribute (
+                       WEBKIT_DOM_ELEMENT (body), "data-evo-plain-text", "", NULL);
+
+       if (e_editor_page_get_convert_in_situ (editor_page)) {
+               e_editor_dom_convert_content (editor_page, NULL);
+               /* Make the quote marks non-selectable. */
+               e_editor_dom_disable_quote_marks_select (editor_page);
+               dom_set_links_active (document, FALSE);
+               e_editor_page_set_convert_in_situ (editor_page, FALSE);
+
+               e_editor_dom_register_input_event_listener_on_body (editor_page);
+               register_html_events_handlers (editor_page, body);
+
+               return;
+       }
+
+       adapt_to_editor_dom_changes (document);
+
+       /* Make the quote marks non-selectable. */
+       e_editor_dom_disable_quote_marks_select (editor_page);
+       dom_set_links_active (document, FALSE);
+       put_body_in_citation (document);
+       move_elements_to_body (editor_page);
+       repair_gmail_blockquotes (document);
+       remove_thunderbird_signature (document);
+
+       if (webkit_dom_element_has_attribute (WEBKIT_DOM_ELEMENT (body), "data-evo-draft")) {
+               /* Restore the selection how it was when the draft was saved */
+               e_editor_dom_move_caret_into_element (editor_page, WEBKIT_DOM_ELEMENT (body), FALSE);
+               e_editor_dom_selection_restore (editor_page);
+               e_editor_dom_remove_embedded_style_sheet (editor_page);
+       }
+
+       /* The composer body could be empty in some case (loading an empty string
+        * or empty HTML. In that case create the initial paragraph. */
+       if (!webkit_dom_node_get_first_child (WEBKIT_DOM_NODE (body))) {
+               WebKitDOMElement *paragraph;
+
+               paragraph = e_editor_dom_prepare_paragraph (editor_page, TRUE);
+               webkit_dom_element_set_id (paragraph, "-x-evo-input-start");
+               webkit_dom_node_append_child (
+                       WEBKIT_DOM_NODE (body), WEBKIT_DOM_NODE (paragraph), NULL);
+               e_editor_dom_selection_restore (editor_page);
+       }
+
+       /* Register on input event that is called when the content (body) is modified */
+       e_editor_dom_register_input_event_listener_on_body (editor_page);
+       register_html_events_handlers (editor_page, body);
+
+       e_editor_dom_fix_file_uri_images (editor_page);
+       change_cid_images_src_to_base64 (editor_page);
+
+       if (e_editor_page_get_inline_spelling_enabled (editor_page))
+               e_editor_dom_force_spell_check (editor_page);
+       else
+               e_editor_dom_turn_spell_check_off (editor_page);
+
+       e_editor_dom_set_monospace_font_family_on_body (
+               WEBKIT_DOM_ELEMENT (body), e_editor_page_get_html_mode (editor_page));
+
+       dom_window = webkit_dom_document_get_default_view (document);
+
+       webkit_dom_event_target_add_event_listener (
+               WEBKIT_DOM_EVENT_TARGET (dom_window),
+               "scroll",
+               G_CALLBACK (body_scroll_event_cb),
+               FALSE,
+               editor_page);
+
+       /* Intentionally leak the WebKitDOMDOMWindow object here as otherwise the
+        * callback won't be set up. */
+}
+
+GVariant *
+e_editor_dom_get_inline_images_data (EEditorPage *editor_page,
+                                    const gchar *uid_domain)
+{
+       WebKitDOMDocument *document;
+       WebKitDOMNodeList *list = NULL;
+       GVariant *result = NULL;
+       GVariantBuilder *builder = NULL;
+       GHashTable *added = NULL;
+       gint length, ii;
+
+       g_return_val_if_fail (E_IS_EDITOR_PAGE (editor_page), NULL);
+
+       document = e_editor_page_get_document (editor_page);
+       list = webkit_dom_document_query_selector_all (document, "img[data-inline]", NULL);
+
+       length = webkit_dom_node_list_get_length (list);
+       if (length == 0) {
+               g_clear_object (&list);
+               goto background;
+       }
+
+       builder = g_variant_builder_new (G_VARIANT_TYPE ("a(sss)"));
+
+       added = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
+       for (ii = 0; ii < length; ii++) {
+               const gchar *id;
+               gchar *cid = NULL;
+               WebKitDOMNode *node = webkit_dom_node_list_item (list, ii);
+               gchar *src = webkit_dom_element_get_attribute (
+                       WEBKIT_DOM_ELEMENT (node), "src");
+
+               if (!src)
+                       continue;
+
+               if ((id = g_hash_table_lookup (added, src)) != NULL) {
+                       cid = g_strdup_printf ("cid:%s", id);
+                       g_free (src);
+               } else {
+                       gchar *data_name = webkit_dom_element_get_attribute (
+                               WEBKIT_DOM_ELEMENT (node), "data-name");
+
+                       if (data_name) {
+                               gchar *new_id;
+
+                               new_id = camel_header_msgid_generate (uid_domain);
+                               g_variant_builder_add (
+                                       builder, "(sss)", src, data_name, new_id);
+                               cid = g_strdup_printf ("cid:%s", new_id);
+
+                               g_hash_table_insert (added, src, new_id);
+                       }
+                       g_free (data_name);
+               }
+               webkit_dom_element_set_attribute (
+                       WEBKIT_DOM_ELEMENT (node), "src", cid, NULL);
+               g_object_unref (node);
+               g_free (cid);
+       }
+       g_clear_object (&list);
+
+ background:
+       list = webkit_dom_document_query_selector_all (
+               document, "[data-inline][background]", NULL);
+       length = webkit_dom_node_list_get_length (list);
+       if (length == 0)
+               goto out;
+       if (!builder)
+               builder = g_variant_builder_new (G_VARIANT_TYPE ("a(sss)"));
+       if (!added)
+               added = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
+
+       for (ii = 0; ii < length; ii++) {
+               const gchar *id;
+               gchar *cid = NULL;
+               WebKitDOMNode *node = webkit_dom_node_list_item (list, ii);
+               gchar *src = webkit_dom_element_get_attribute (
+                       WEBKIT_DOM_ELEMENT (node), "background");
+
+               if (!src)
+                       continue;
+
+               if ((id = g_hash_table_lookup (added, src)) != NULL) {
+                       cid = g_strdup_printf ("cid:%s", id);
+                       webkit_dom_element_set_attribute (
+                               WEBKIT_DOM_ELEMENT (node), "background", cid, NULL);
+                       g_free (src);
+               } else {
+                       gchar *data_name = webkit_dom_element_get_attribute (
+                               WEBKIT_DOM_ELEMENT (node), "data-name");
+
+                       if (data_name) {
+                               gchar *new_id;
+
+                               new_id = camel_header_msgid_generate (uid_domain);
+                               g_variant_builder_add (
+                                       builder, "(sss)", src, data_name, new_id);
+                               cid = g_strdup_printf ("cid:%s", new_id);
+
+                               g_hash_table_insert (added, src, new_id);
+
+                               webkit_dom_element_set_attribute (
+                                       WEBKIT_DOM_ELEMENT (node), "background", cid, NULL);
+                       }
+                       g_free (data_name);
+               }
+               g_free (cid);
+               g_object_unref (node);
+       }
+ out:
+       g_clear_object (&list);
+       if (added)
+               g_hash_table_destroy (added);
+
+       if (builder) {
+               result = g_variant_new ("a(sss)", builder);
+               g_variant_builder_unref (builder);
+       }
+
+       return result;
+}
+
+static gboolean
+pasting_quoted_content (const gchar *content)
+{
+       /* Check if the content we are pasting is a quoted content from composer.
+        * If it is, we can't use WebKit to paste it as it would leave the formatting
+        * on the content. */
+       return g_str_has_prefix (
+               content,
+               "<meta http-equiv=\"content-type\" content=\"text/html; "
+               "charset=utf-8\"><blockquote type=\"cite\"") &&
+               strstr (content, "\"-x-evo-");
+}
+
+/*
+ * e_editor_dom_insert_html:
+ * @selection: an #EEditorSelection
+ * @html_text: an HTML code to insert
+ *
+ * Insert @html_text into document at current cursor position. When a text range
+ * is selected, it will be replaced by @html_text.
+ */
+void
+e_editor_dom_insert_html (EEditorPage *editor_page,
+                         const gchar *html_text)
+{
+       WebKitDOMDocument *document;
+       EEditorHistoryEvent *ev = NULL;
+       EEditorUndoRedoManager *manager;
+       gboolean html_mode;
+
+       g_return_if_fail (E_IS_EDITOR_PAGE (editor_page));
+       g_return_if_fail (html_text != NULL);
+
+       document = e_editor_page_get_document (editor_page);
+
+       manager = e_editor_page_get_undo_redo_manager (editor_page);
+       if (!e_editor_undo_redo_manager_is_operation_in_progress (manager)) {
+               gboolean collapsed;
+
+               ev = g_new0 (EEditorHistoryEvent, 1);
+               ev->type = HISTORY_INSERT_HTML;
+
+               collapsed = e_editor_dom_selection_is_collapsed (editor_page);
+               e_editor_dom_selection_get_coordinates (editor_page,
+                       &ev->before.start.x,
+                       &ev->before.start.y,
+                       &ev->before.end.x,
+                       &ev->before.end.y);
+
+               if (!collapsed) {
+                       ev->before.end.x = ev->before.start.x;
+                       ev->before.end.y = ev->before.start.y;
+               }
+
+               ev->data.string.from = NULL;
+               ev->data.string.to = g_strdup (html_text);
+       }
+
+       html_mode = e_editor_page_get_html_mode (editor_page);
+       if (html_mode ||
+           (e_editor_page_is_pasting_content_from_itself (editor_page) &&
+           !pasting_quoted_content (html_text))) {
+               if (!e_editor_dom_selection_is_collapsed (editor_page)) {
+                       EEditorHistoryEvent *event;
+                       WebKitDOMDocumentFragment *fragment;
+                       WebKitDOMRange *range = NULL;
+
+                       event = g_new0 (EEditorHistoryEvent, 1);
+                       event->type = HISTORY_DELETE;
+
+                       range = e_editor_dom_get_current_range (editor_page);
+                       fragment = webkit_dom_range_clone_contents (range, NULL);
+                       g_clear_object (&range);
+                       event->data.fragment = fragment;
+
+                       e_editor_dom_selection_get_coordinates (editor_page,
+                               &event->before.start.x,
+                               &event->before.start.y,
+                               &event->before.end.x,
+                               &event->before.end.y);
+
+                       event->after.start.x = event->before.start.x;
+                       event->after.start.y = event->before.start.y;
+                       event->after.end.x = event->before.start.x;
+                       event->after.end.y = event->before.start.y;
+
+                       e_editor_undo_redo_manager_insert_history_event (manager, event);
+
+                       event = g_new0 (EEditorHistoryEvent, 1);
+                       event->type = HISTORY_AND;
+
+                       e_editor_undo_redo_manager_insert_history_event (manager, event);
+               }
+
+               e_editor_dom_exec_command (editor_page, E_CONTENT_EDITOR_COMMAND_INSERT_HTML, html_text);
+               e_editor_dom_fix_file_uri_images (editor_page);
+               if (strstr (html_text, "id=\"-x-evo-selection-start-marker\""))
+                       e_editor_dom_selection_restore (editor_page);
+
+               if (!html_mode) {
+                       WebKitDOMNodeList *list = NULL;
+                       gint ii, length;
+
+                       list = webkit_dom_document_query_selector_all (
+                               document, "span[style^=font-family]", NULL);
+                       length = webkit_dom_node_list_get_length (list);
+                       if (length > 0)
+                               e_editor_dom_selection_save (editor_page);
+
+                       for (ii = 0; ii < length; ii++) {
+                               WebKitDOMNode *span, *child;
+
+                               span = webkit_dom_node_list_item (list, ii);
+                               while ((child = webkit_dom_node_get_first_child (span)))
+                                       webkit_dom_node_insert_before (
+                                               webkit_dom_node_get_parent_node (span),
+                                               child,
+                                               span,
+                                               NULL);
+
+                               remove_node (span);
+                               g_object_unref (span);
+                       }
+                       g_clear_object (&list);
+
+                       if (length > 0)
+                               e_editor_dom_selection_restore (editor_page);
+               }
+
+               e_editor_dom_check_magic_links (editor_page, FALSE);
+               e_editor_dom_force_spell_check (editor_page);
+               e_editor_dom_scroll_to_caret (editor_page);
+       } else
+               e_editor_dom_convert_and_insert_html_into_selection (editor_page, html_text, TRUE);
+
+       if (ev) {
+               e_editor_dom_selection_get_coordinates (editor_page,
+                       &ev->after.start.x,
+                       &ev->after.start.y,
+                       &ev->after.end.x,
+                       &ev->after.end.y);
+
+               e_editor_undo_redo_manager_insert_history_event (manager, ev);
+       }
+}
+
+static void
+save_history_for_delete_or_backspace (EEditorPage *editor_page,
+                                      gboolean delete_key,
+                                      gboolean control_key)
+{
+       WebKitDOMDocument *document;
+       WebKitDOMDocumentFragment *fragment = NULL;
+       WebKitDOMDOMWindow *dom_window = NULL;
+       WebKitDOMDOMSelection *dom_selection = NULL;
+       WebKitDOMRange *range = NULL;
+       EEditorHistoryEvent *ev = NULL;
+       EEditorUndoRedoManager *manager;
+
+       g_return_if_fail (E_IS_EDITOR_PAGE (editor_page));
+
+       document = e_editor_page_get_document (editor_page);
+       dom_window = webkit_dom_document_get_default_view (document);
+       dom_selection = webkit_dom_dom_window_get_selection (dom_window);
+       g_clear_object (&dom_window);
+
+       if (!webkit_dom_dom_selection_get_range_count (dom_selection)) {
+               g_clear_object (&dom_selection);
+               return;
+       }
+
+       range = webkit_dom_dom_selection_get_range_at (dom_selection, 0, NULL);
+
+       /* Check if we can delete something */
+       if (webkit_dom_range_get_collapsed (range, NULL)) {
+               WebKitDOMRange *tmp_range = NULL;
+
+               webkit_dom_dom_selection_modify (
+                       dom_selection, "move", delete_key ? "right" : "left", "character");
+
+               tmp_range = webkit_dom_dom_selection_get_range_at (dom_selection, 0, NULL);
+               if (webkit_dom_range_compare_boundary_points (tmp_range, WEBKIT_DOM_RANGE_END_TO_END, range, 
NULL) == 0) {
+                       g_clear_object (&dom_selection);
+                       g_clear_object (&range);
+                       g_clear_object (&tmp_range);
+
+                       return;
+               }
+
+               webkit_dom_dom_selection_modify (
+                       dom_selection, "move", delete_key ? "left" : "right", "character");
+
+               g_clear_object (&tmp_range);
+       }
+
+       if (save_history_before_event_in_table (editor_page, range)) {
+               g_clear_object (&range);
+               g_clear_object (&dom_selection);
+               return;
+       }
+
+       ev = g_new0 (EEditorHistoryEvent, 1);
+       ev->type = HISTORY_DELETE;
+
+       e_editor_dom_selection_get_coordinates (editor_page, &ev->before.start.x, &ev->before.start.y, 
&ev->before.end.x, &ev->before.end.y);
+       g_clear_object (&range);
+       range = webkit_dom_dom_selection_get_range_at (dom_selection, 0, NULL);
+
+       if (webkit_dom_range_get_collapsed (range, NULL)) {
+               gboolean removing_from_anchor = FALSE;
+               WebKitDOMRange *range_clone = NULL;
+               WebKitDOMNode *node, *next_block = NULL;
+
+               e_editor_page_block_selection_changed (editor_page);
+
+               range_clone = webkit_dom_range_clone_range (range, NULL);
+               if (control_key) {
+                       WebKitDOMRange *tmp_range = NULL;
+
+                       /* Control + Delete/Backspace deletes previous/next word. */
+                       webkit_dom_dom_selection_modify (
+                               dom_selection, "move", delete_key ? "right" : "left", "word");
+                       tmp_range = webkit_dom_dom_selection_get_range_at (dom_selection, 0, NULL);
+                       if (delete_key)
+                               webkit_dom_range_set_end (
+                                       range_clone,
+                                       webkit_dom_range_get_end_container (tmp_range, NULL),
+                                       webkit_dom_range_get_end_offset (tmp_range, NULL),
+                                       NULL);
+                       else
+                               webkit_dom_range_set_start (
+                                       range_clone,
+                                       webkit_dom_range_get_start_container (tmp_range, NULL),
+                                       webkit_dom_range_get_start_offset (tmp_range, NULL),
+                                       NULL);
+                       g_clear_object (&tmp_range);
+               } else {
+                       typedef WebKitDOMNode * (*GetSibling)(WebKitDOMNode *node);
+                       WebKitDOMNode *container, *sibling;
+                       WebKitDOMElement *selection_marker;
+
+                       GetSibling get_sibling = delete_key ?
+                               webkit_dom_node_get_next_sibling :
+                               webkit_dom_node_get_previous_sibling;
+
+                       container = webkit_dom_range_get_end_container (range_clone, NULL);
+                       sibling = get_sibling (container);
+
+                       selection_marker = webkit_dom_document_get_element_by_id (
+                               document,
+                               delete_key ?
+                                       "-x-evo-selection-end-marker" :
+                                       "-x-evo-selection-start-marker");
+
+                       if (selection_marker) {
+                               WebKitDOMNode *tmp_sibling;
+
+                               tmp_sibling = get_sibling (WEBKIT_DOM_NODE (selection_marker));
+                               if (!tmp_sibling || (WEBKIT_DOM_IS_HTML_BR_ELEMENT (tmp_sibling) &&
+                                   !element_has_class (WEBKIT_DOM_ELEMENT (tmp_sibling), "-x-evo-wrap-br")))
+                                       sibling = WEBKIT_DOM_NODE (selection_marker);
+                       }
+
+                       if (e_editor_dom_is_selection_position_node (sibling)) {
+                               if ((node = get_sibling (sibling)))
+                                       node = get_sibling (node);
+                               if (node) {
+                                       if (WEBKIT_DOM_IS_ELEMENT (node) &&
+                                           webkit_dom_element_has_attribute (WEBKIT_DOM_ELEMENT (node), 
"data-hidden-space")) {
+                                               fragment = webkit_dom_document_create_document_fragment 
(document);
+                                               webkit_dom_node_append_child (
+                                                       WEBKIT_DOM_NODE (fragment),
+                                                       WEBKIT_DOM_NODE (
+                                                               webkit_dom_document_create_text_node 
(document, " ")),
+                                                       NULL);
+                                       } else if (delete_key) {
+                                               webkit_dom_range_set_start (
+                                                       range_clone, node, 0, NULL);
+                                               webkit_dom_range_set_end (
+                                                       range_clone, node, 1, NULL);
+                                       }
+                               } else {
+                                       WebKitDOMRange *tmp_range = NULL, *actual_range = NULL;
+
+                                       actual_range = webkit_dom_dom_selection_get_range_at (dom_selection, 
0, NULL);
+
+                                       webkit_dom_dom_selection_modify (
+                                               dom_selection, "move", delete_key ? "right" : "left", 
"character");
+
+                                       tmp_range = webkit_dom_dom_selection_get_range_at (dom_selection, 0, 
NULL);
+                                       if (webkit_dom_range_compare_boundary_points (tmp_range, 
WEBKIT_DOM_RANGE_END_TO_END, actual_range, NULL) != 0) {
+                                               WebKitDOMNode *actual_block;
+                                               WebKitDOMNode *tmp_block;
+
+                                               actual_block = e_editor_dom_get_parent_block_node_from_child 
(container);
+
+                                               tmp_block = delete_key ?
+                                                       webkit_dom_range_get_end_container (tmp_range, NULL) :
+                                                       webkit_dom_range_get_start_container (tmp_range, 
NULL);
+                                               tmp_block = e_editor_dom_get_parent_block_node_from_child 
(tmp_block);
+
+                                               webkit_dom_dom_selection_modify (
+                                                       dom_selection, "move", delete_key ? "left" : "right", 
"character");
+
+                                               if (tmp_block) {
+                                                       fragment = 
webkit_dom_document_create_document_fragment (document);
+                                                       if (delete_key) {
+                                                               webkit_dom_node_append_child (
+                                                                       WEBKIT_DOM_NODE (fragment),
+                                                                       webkit_dom_node_clone_node_with_error 
(actual_block, TRUE, NULL),
+                                                                       NULL);
+                                                               webkit_dom_node_append_child (
+                                                                       WEBKIT_DOM_NODE (fragment),
+                                                                       webkit_dom_node_clone_node_with_error 
(tmp_block, TRUE, NULL),
+                                                                       NULL);
+                                                               if (delete_key)
+                                                                       next_block = tmp_block;
+                                                       } else {
+                                                               webkit_dom_node_append_child (
+                                                                       WEBKIT_DOM_NODE (fragment),
+                                                                       webkit_dom_node_clone_node_with_error 
(tmp_block, TRUE, NULL),
+                                                                       NULL);
+                                                               webkit_dom_node_append_child (
+                                                                       WEBKIT_DOM_NODE (fragment),
+                                                                       webkit_dom_node_clone_node_with_error 
(actual_block, TRUE, NULL),
+                                                                       NULL);
+                                                       }
+                                                       g_object_set_data (
+                                                               G_OBJECT (fragment),
+                                                               "history-concatenating-blocks",
+                                                               GINT_TO_POINTER (1));
+                                               }
+                                       }
+                                       g_clear_object (&tmp_range);
+                                       g_clear_object (&actual_range);
+                               }
+                       } else {
+                               glong offset;
+
+                               /* FIXME This code is wrong for unicode smileys. */
+                               offset = webkit_dom_range_get_start_offset (range_clone, NULL);
+
+                               if (delete_key)
+                                       webkit_dom_range_set_end (
+                                               range_clone, container, offset + 1, NULL);
+                               else
+                                       webkit_dom_range_set_start (
+                                               range_clone, container, offset - 1, NULL);
+
+                               removing_from_anchor = WEBKIT_DOM_IS_HTML_ANCHOR_ELEMENT (
+                                       webkit_dom_node_get_parent_node (container));
+                       }
+               }
+
+
+               if (!fragment)
+                       fragment = webkit_dom_range_clone_contents (range_clone, NULL);
+               if (removing_from_anchor)
+                       g_object_set_data (
+                               G_OBJECT (fragment),
+                               "history-removing-from-anchor",
+                               GINT_TO_POINTER (1));
+               node = webkit_dom_node_get_first_child (WEBKIT_DOM_NODE (fragment));
+               if (!node) {
+                       g_free (ev);
+                       e_editor_page_unblock_selection_changed (editor_page);
+                       g_clear_object (&range);
+                       g_clear_object (&range_clone);
+                       g_clear_object (&dom_selection);
+                       g_warning ("History event was not saved for %s key", delete_key ? "Delete" : 
"Backspace");
+                       return;
+               }
+
+               if (control_key) {
+                       if (delete_key) {
+                               ev->after.start.x = ev->before.start.x;
+                               ev->after.start.y = ev->before.start.y;
+                               ev->after.end.x = ev->before.end.x;
+                               ev->after.end.y = ev->before.end.y;
+
+                               webkit_dom_range_collapse (range_clone, TRUE, NULL);
+                               webkit_dom_dom_selection_remove_all_ranges (dom_selection);
+                               webkit_dom_dom_selection_add_range (dom_selection, range_clone);
+                       } else {
+                               gboolean selection_saved = FALSE;
+                               WebKitDOMRange *tmp_range = NULL;
+
+                               if (webkit_dom_document_get_element_by_id (document, 
"-x-evo-selection-start-marker"))
+                                       selection_saved = TRUE;
+
+                               if (selection_saved)
+                                       e_editor_dom_selection_restore (editor_page);
+
+                               tmp_range = webkit_dom_range_clone_range (range_clone, NULL);
+                               /* Prepare the selection to the right position after
+                                * delete and save it. */
+                               webkit_dom_range_collapse (range_clone, TRUE, NULL);
+                               webkit_dom_dom_selection_remove_all_ranges (dom_selection);
+                               webkit_dom_dom_selection_add_range (dom_selection, range_clone);
+                               e_editor_dom_selection_get_coordinates (editor_page, &ev->after.start.x, 
&ev->after.start.y, &ev->after.end.x, &ev->after.end.y);
+                               /* Restore the selection where it was before the
+                                * history event was saved. */
+                               webkit_dom_range_collapse (tmp_range, FALSE, NULL);
+                               webkit_dom_dom_selection_remove_all_ranges (dom_selection);
+                               webkit_dom_dom_selection_add_range (dom_selection, tmp_range);
+                               g_clear_object (&tmp_range);
+
+                               if (selection_saved)
+                                       e_editor_dom_selection_save (editor_page);
+                       }
+               } else {
+                       gboolean selection_saved = FALSE;
+
+                       if (webkit_dom_document_get_element_by_id (document, "-x-evo-selection-start-marker"))
+                               selection_saved = TRUE;
+
+                       if (selection_saved)
+                               e_editor_dom_selection_restore (editor_page);
+
+                       if (delete_key) {
+                               e_editor_dom_selection_get_coordinates (editor_page, &ev->after.start.x, 
&ev->after.start.y, &ev->after.end.x, &ev->after.end.y);
+                       } else {
+                               webkit_dom_dom_selection_modify (dom_selection, "move", "left", "character");
+                               e_editor_dom_selection_get_coordinates (editor_page, &ev->after.start.x, 
&ev->after.start.y, &ev->after.end.x, &ev->after.end.y);
+                               webkit_dom_dom_selection_modify (dom_selection, "move", "right", "character");
+
+                               ev->after.end.x = ev->after.start.x;
+                               ev->after.end.y = ev->after.start.y;
+                       }
+
+                       if (selection_saved)
+                               e_editor_dom_selection_save (editor_page);
+               }
+
+               g_clear_object (&range_clone);
+
+               if (delete_key) {
+                       if (!WEBKIT_DOM_IS_ELEMENT (node)) {
+                               webkit_dom_node_insert_before (
+                                       WEBKIT_DOM_NODE (fragment),
+                                       WEBKIT_DOM_NODE (
+                                               dom_create_selection_marker (document, FALSE)),
+                                       webkit_dom_node_get_first_child (WEBKIT_DOM_NODE (fragment)),
+                                       NULL);
+                               webkit_dom_node_insert_before (
+                                       WEBKIT_DOM_NODE (fragment),
+                                       WEBKIT_DOM_NODE (
+                                               dom_create_selection_marker (document, TRUE)),
+                                       webkit_dom_node_get_first_child (WEBKIT_DOM_NODE (fragment)),
+                                       NULL);
+                       }
+               } else {
+                       if (!WEBKIT_DOM_IS_ELEMENT (node)) {
+                               webkit_dom_node_append_child (
+                                       WEBKIT_DOM_NODE (fragment),
+                                       WEBKIT_DOM_NODE (
+                                               dom_create_selection_marker (document, TRUE)),
+                                       NULL);
+                               webkit_dom_node_append_child (
+                                       WEBKIT_DOM_NODE (fragment),
+                                       WEBKIT_DOM_NODE (
+                                               dom_create_selection_marker (document, FALSE)),
+                                       NULL);
+                       }
+               }
+
+               /* If concatenating two blocks with pressing Delete on the end
+                * of the previous one and the next node contain content that
+                * is wrapped on multiple lines, the last line will by separated
+                * by WebKit to the separate block. To avoid it let's remove
+                * all quoting and wrapping from the next paragraph. */
+               if (next_block) {
+                       e_editor_dom_remove_wrapping_from_element (WEBKIT_DOM_ELEMENT (next_block));
+                       e_editor_dom_remove_quoting_from_element (WEBKIT_DOM_ELEMENT (next_block));
+               }
+
+               e_editor_page_unblock_selection_changed (editor_page);
+       } else {
+               WebKitDOMElement *tmp_element;
+               WebKitDOMNode *sibling;
+
+               ev->after.start.x = ev->before.start.x;
+               ev->after.start.y = ev->before.start.y;
+               ev->after.end.x = ev->before.start.x;
+               ev->after.end.y = ev->before.start.y;
+
+               fragment = webkit_dom_range_clone_contents (range, NULL);
+
+               tmp_element = webkit_dom_document_fragment_query_selector (
+                       fragment, "#-x-evo-selection-start-marker", NULL);
+               if (tmp_element)
+                       remove_node (WEBKIT_DOM_NODE (tmp_element));
+
+               tmp_element = webkit_dom_document_fragment_query_selector (
+                       fragment, "#-x-evo-selection-end-marker", NULL);
+               if (tmp_element)
+                       remove_node (WEBKIT_DOM_NODE (tmp_element));
+
+               /* If any empty blockquote is presented, remove it. */
+               tmp_element = webkit_dom_document_query_selector (
+                       document, "blockquote[type=cite]:empty", NULL);
+               if (tmp_element)
+                       remove_node (WEBKIT_DOM_NODE (tmp_element));
+
+               /* Selection starts in the beginning of blockquote. */
+               tmp_element = webkit_dom_document_get_element_by_id (
+                       document, "-x-evo-selection-start-marker");
+               sibling = webkit_dom_node_get_next_sibling (WEBKIT_DOM_NODE (tmp_element));
+               if (sibling && WEBKIT_DOM_IS_ELEMENT (sibling) &&
+                   element_has_class (WEBKIT_DOM_ELEMENT (sibling), "-x-evo-quoted")) {
+                       WebKitDOMNode *child;
+
+                       tmp_element = webkit_dom_document_get_element_by_id (
+                               document, "-x-evo-selection-end-marker");
+
+                       /* If there is no text after the selection end it means that
+                        * the block will be replaced with block that is body's descendant
+                        * and not the blockquote's one. Also if the selection started
+                        * in the beginning of blockquote we have to insert the quote
+                        * characters into the deleted content to correctly restore
+                        * them during undo/redo operations. */
+                       if (!(tmp_element && webkit_dom_node_get_next_sibling (WEBKIT_DOM_NODE 
(tmp_element)))) {
+                               child = webkit_dom_node_get_first_child (WEBKIT_DOM_NODE (fragment));
+                               while (child && WEBKIT_DOM_IS_HTML_QUOTE_ELEMENT (child))
+                                       child = webkit_dom_node_get_first_child (child);
+
+                               child = webkit_dom_node_get_first_child (child);
+                               if (child && (WEBKIT_DOM_IS_TEXT (child) ||
+                                   (WEBKIT_DOM_IS_ELEMENT (child) &&
+                                    !element_has_class (WEBKIT_DOM_ELEMENT (child), "-x-evo-quoted")))) {
+                                       webkit_dom_node_insert_before (
+                                               webkit_dom_node_get_parent_node (child),
+                                               webkit_dom_node_clone_node_with_error (sibling, TRUE, NULL),
+                                               child,
+                                               NULL);
+                               }
+                       }
+               }
+
+               /* When we were cloning the range above and the range contained
+                * quoted content there will still be blockquote missing in the
+                * final range. Let's modify the fragment and add it there. */
+               tmp_element = webkit_dom_document_get_element_by_id (
+                       document, "-x-evo-selection-end-marker");
+               if (tmp_element) {
+                       WebKitDOMNode *node;
+
+                       node = WEBKIT_DOM_NODE (tmp_element);
+                       while (!WEBKIT_DOM_IS_HTML_BODY_ELEMENT (webkit_dom_node_get_parent_node (node)))
+                               node = webkit_dom_node_get_parent_node (node);
+
+                       if (node && WEBKIT_DOM_IS_HTML_QUOTE_ELEMENT (node)) {
+                               WebKitDOMNode *last_child;
+
+                               last_child = webkit_dom_node_get_last_child (WEBKIT_DOM_NODE (fragment));
+
+                               if (last_child && !WEBKIT_DOM_IS_HTML_QUOTE_ELEMENT (last_child)) {
+                                       WebKitDOMDocumentFragment *tmp_fragment;
+                                       WebKitDOMNode *clone;
+
+                                       tmp_fragment = webkit_dom_document_create_document_fragment 
(document);
+                                       clone = webkit_dom_node_clone_node_with_error (node, FALSE, NULL);
+                                       clone = webkit_dom_node_append_child (
+                                               WEBKIT_DOM_NODE (tmp_fragment), clone, NULL);
+                                       webkit_dom_node_append_child (clone, WEBKIT_DOM_NODE (fragment), 
NULL);
+                                       fragment = tmp_fragment;
+                               }
+                       }
+               }
+
+               /* FIXME Ugly hack */
+               /* If the deleted selection contained the signature (or at least its
+                * part) replace it with the unchanged signature to correctly perform
+                * undo operation. */
+               tmp_element = webkit_dom_document_fragment_query_selector (fragment, 
".-x-evo-signature-wrapper", NULL);
+               if (tmp_element) {
+                       WebKitDOMElement *signature;
+
+                       signature = webkit_dom_document_query_selector (document, 
".-x-evo-signature-wrapper", NULL);
+                       if (signature) {
+                               webkit_dom_node_replace_child (
+                                       webkit_dom_node_get_parent_node (WEBKIT_DOM_NODE (tmp_element)),
+                                       webkit_dom_node_clone_node_with_error (WEBKIT_DOM_NODE (signature), 
TRUE, NULL),
+                                       WEBKIT_DOM_NODE (tmp_element),
+                                       NULL);
+                       }
+               }
+       }
+
+       g_clear_object (&range);
+       g_clear_object (&dom_selection);
+
+       g_object_set_data (G_OBJECT (fragment), "history-delete-key", GINT_TO_POINTER (delete_key));
+       g_object_set_data (G_OBJECT (fragment), "history-control-key", GINT_TO_POINTER (control_key));
+
+       ev->data.fragment = fragment;
+
+       manager = e_editor_page_get_undo_redo_manager (editor_page);
+       e_editor_undo_redo_manager_insert_history_event (manager, ev);
+}
+
+gboolean
+e_editor_dom_fix_structure_after_delete_before_quoted_content (EEditorPage *editor_page,
+                                                              glong key_code,
+                                                              gboolean control_key,
+                                                              gboolean delete_key)
+{
+       WebKitDOMDocument *document;
+       WebKitDOMElement *selection_start_marker, *selection_end_marker;
+       WebKitDOMNode *block, *node;
+       gboolean collapsed = FALSE;
+
+       g_return_val_if_fail (E_IS_EDITOR_PAGE (editor_page), FALSE);
+
+       document = e_editor_page_get_document (editor_page);
+       collapsed = e_editor_dom_selection_is_collapsed (editor_page);
+
+       e_editor_dom_selection_save (editor_page);
+
+       selection_start_marker = webkit_dom_document_query_selector (
+               document, "span#-x-evo-selection-start-marker", NULL);
+       selection_end_marker = webkit_dom_document_query_selector (
+               document, "span#-x-evo-selection-end-marker", NULL);
+
+       if (!selection_start_marker || !selection_end_marker)
+               return FALSE;
+
+       if (collapsed) {
+               WebKitDOMNode *next_block;
+
+               block = e_editor_dom_get_parent_block_node_from_child (
+                       WEBKIT_DOM_NODE (selection_start_marker));
+
+               next_block = webkit_dom_node_get_next_sibling (block);
+
+               /* Next block is quoted content */
+               if (!WEBKIT_DOM_IS_HTML_QUOTE_ELEMENT (next_block))
+                       goto restore;
+
+               /* Delete was pressed in block without any content */
+               if (webkit_dom_node_get_previous_sibling (WEBKIT_DOM_NODE (selection_start_marker)))
+                       goto restore;
+
+               /* If there is just BR element go ahead */
+               node = webkit_dom_node_get_next_sibling (WEBKIT_DOM_NODE (selection_end_marker));
+               if (node && !WEBKIT_DOM_IS_HTML_BR_ELEMENT (node))
+                       goto restore;
+               else {
+                       if (key_code != ~0)
+                               save_history_for_delete_or_backspace (
+                                       editor_page, key_code == HTML_KEY_CODE_DELETE, control_key);
+
+                       /* Remove the empty block and move caret to the right place. */
+                       remove_node (block);
+
+                       if (delete_key) {
+                               /* To the beginning of the next block. */
+                               e_editor_dom_move_caret_into_element (editor_page, WEBKIT_DOM_ELEMENT 
(next_block), TRUE);
+                       } else {
+                               WebKitDOMNode *prev_block;
+
+                               /* On the end of previous block. */
+                               prev_block = webkit_dom_node_get_previous_sibling (next_block);
+                               while (prev_block && WEBKIT_DOM_IS_HTML_QUOTE_ELEMENT (prev_block))
+                                       prev_block = webkit_dom_node_get_last_child (prev_block);
+
+                               if (prev_block)
+                                       e_editor_dom_move_caret_into_element (editor_page, WEBKIT_DOM_ELEMENT 
(prev_block), FALSE);
+                       }
+
+                       return TRUE;
+               }
+       } else {
+               WebKitDOMNode *end_block, *parent;
+
+               /* Let the quote marks be selectable to nearly correctly remove the
+                * selection. Corrections after are done in body_keyup_event_cb. */
+               enable_quote_marks_select (document);
+
+               parent = webkit_dom_node_get_parent_node (
+                       WEBKIT_DOM_NODE (selection_start_marker));
+               if (WEBKIT_DOM_IS_HTML_ANCHOR_ELEMENT (parent) ||
+                   element_has_tag (WEBKIT_DOM_ELEMENT (parent), "b") ||
+                   element_has_tag (WEBKIT_DOM_ELEMENT (parent), "i") ||
+                   element_has_tag (WEBKIT_DOM_ELEMENT (parent), "u"))
+                       node = webkit_dom_node_get_previous_sibling (parent);
+               else
+                       node = webkit_dom_node_get_previous_sibling (
+                               WEBKIT_DOM_NODE (selection_start_marker));
+
+               if (!node || !WEBKIT_DOM_IS_ELEMENT (node))
+                       goto restore;
+
+               if (!element_has_class (WEBKIT_DOM_ELEMENT (node), "-x-evo-quoted"))
+                       goto restore;
+
+               block = e_editor_dom_get_parent_block_node_from_child (
+                       WEBKIT_DOM_NODE (selection_start_marker));
+               end_block = e_editor_dom_get_parent_block_node_from_child (
+                       WEBKIT_DOM_NODE (selection_end_marker));
+
+               /* Situation where the start of the selection is in the beginning
+               + * of the block in quoted content and the end in the beginning of
+               + * content that is after the citation or the selection end is in
+               + * the end of the quoted content (showed by ^). We have to
+               + * mark the start block to correctly restore the structure
+               + * afterwards.
+               *
+               * > |xxx
+               * > xxx^
+               * |xxx
+               */
+               if (e_editor_dom_get_citation_level (end_block, FALSE) > 0) {
+                       WebKitDOMNode *parent;
+
+                       if (webkit_dom_node_get_next_sibling (end_block))
+                               goto restore;
+
+                       parent = webkit_dom_node_get_parent_node (end_block);
+                       while (parent && WEBKIT_DOM_IS_HTML_QUOTE_ELEMENT (parent)) {
+                               WebKitDOMNode *next_parent = webkit_dom_node_get_parent_node (parent);
+
+                               if (webkit_dom_node_get_next_sibling (parent) &&
+                                   !WEBKIT_DOM_IS_HTML_BODY_ELEMENT (next_parent))
+                                       goto restore;
+
+                               parent = next_parent;
+                       }
+               }
+       }
+
+ restore:
+       if (key_code != ~0)
+               save_history_for_delete_or_backspace (
+                       editor_page, key_code == HTML_KEY_CODE_DELETE, control_key);
+
+       e_editor_dom_selection_restore (editor_page);
+
+       return FALSE;
+}
+
+static gboolean
+split_citation (EEditorPage *editor_page)
+{
+       WebKitDOMDocument *document;
+       WebKitDOMElement *element;
+       EEditorHistoryEvent *ev = NULL;
+       EEditorUndoRedoManager *manager;
+
+       g_return_val_if_fail (E_IS_EDITOR_PAGE (editor_page), FALSE);
+
+       document = e_editor_page_get_document (editor_page);
+       manager = e_editor_page_get_undo_redo_manager (editor_page);
+
+       if (!e_editor_undo_redo_manager_is_operation_in_progress (manager)) {
+               WebKitDOMElement *selection_end;
+               WebKitDOMNode *sibling;
+
+               ev = g_new0 (EEditorHistoryEvent, 1);
+               ev->type = HISTORY_CITATION_SPLIT;
+
+               e_editor_dom_selection_save (editor_page);
+
+               e_editor_dom_selection_get_coordinates (editor_page, &ev->before.start.x, 
&ev->before.start.y, &ev->before.end.x, &ev->before.end.y);
+
+               if (!e_editor_dom_selection_is_collapsed (editor_page)) {
+                       WebKitDOMRange *range = NULL;
+
+                       range = e_editor_dom_get_current_range (editor_page);
+                       insert_delete_event (editor_page, range);
+
+                       g_clear_object (&range);
+
+                       ev->before.end.x = ev->before.start.x;
+                       ev->before.end.y = ev->before.start.y;
+               }
+
+               selection_end = webkit_dom_document_get_element_by_id (
+                       document, "-x-evo-selection-end-marker");
+
+               sibling = webkit_dom_node_get_next_sibling (WEBKIT_DOM_NODE (selection_end));
+               if (!sibling || (WEBKIT_DOM_IS_HTML_BR_ELEMENT (sibling) &&
+                   !element_has_class (WEBKIT_DOM_ELEMENT (sibling), "-x-evo-wrap-br"))) {
+                       WebKitDOMDocumentFragment *fragment;
+
+                       fragment = webkit_dom_document_create_document_fragment (document);
+                       ev->data.fragment = fragment;
+               } else
+                       ev->data.fragment = NULL;
+
+               e_editor_dom_selection_restore (editor_page);
+       }
+
+       element = e_editor_dom_insert_new_line_into_citation (editor_page, "");
+
+       if (ev) {
+               e_editor_dom_selection_get_coordinates (editor_page, &ev->after.start.x, &ev->after.start.y, 
&ev->after.end.x, &ev->after.end.y);
+
+               e_editor_undo_redo_manager_insert_history_event (manager, ev);
+       }
+
+       return element != NULL;
+}
+
+static gboolean
+delete_last_character_from_previous_line_in_quoted_block (EEditorPage *editor_page,
+                                                          glong key_code,
+                                                          guint state)
+{
+       WebKitDOMDocument *document;
+       WebKitDOMDocumentFragment *fragment = NULL;
+       WebKitDOMElement *element;
+       WebKitDOMNode *node, *beginning, *prev_sibling;
+       EEditorHistoryEvent *ev = NULL;
+       gboolean hidden_space = FALSE;
+
+       g_return_val_if_fail (E_IS_EDITOR_PAGE (editor_page), FALSE);
+
+       /* We have to be in quoted content. */
+       if (!e_editor_dom_selection_is_citation (editor_page))
+               return FALSE;
+
+       /* Selection is just caret. */
+       if (!e_editor_dom_selection_is_collapsed (editor_page))
+               return FALSE;
+
+       document = e_editor_page_get_document (editor_page);
+
+       e_editor_dom_selection_save (editor_page);
+
+       element = webkit_dom_document_get_element_by_id (
+               document, "-x-evo-selection-start-marker");
+
+       /* Before the caret are just quote characters */
+       beginning = webkit_dom_node_get_previous_sibling (WEBKIT_DOM_NODE (element));
+       if (!(beginning && WEBKIT_DOM_IS_ELEMENT (beginning))) {
+               WebKitDOMNode *parent;
+
+               parent = webkit_dom_node_get_parent_node (WEBKIT_DOM_NODE (element));
+               if (WEBKIT_DOM_IS_HTML_ANCHOR_ELEMENT (parent))
+                       beginning = webkit_dom_node_get_previous_sibling (parent);
+               else
+                       goto out;
+       }
+
+       /* Before the text is the beginning of line. */
+       if (!(element_has_class (WEBKIT_DOM_ELEMENT (beginning), "-x-evo-quoted")))
+               goto out;
+
+       /* If we are just on the beginning of the line and not on the beginning of
+        * the block we need to remove the last character ourselves as well, otherwise
+        * WebKit will put the caret to wrong position. */
+       if (!(prev_sibling = webkit_dom_node_get_previous_sibling (beginning)))
+               goto out;
+
+       if (key_code != ~0) {
+               ev = g_new0 (EEditorHistoryEvent, 1);
+               ev->type = HISTORY_DELETE;
+
+               e_editor_dom_selection_get_coordinates (editor_page,
+                       &ev->before.start.x,
+                       &ev->before.start.y,
+                       &ev->before.end.x,
+                       &ev->before.end.y);
+
+               fragment = webkit_dom_document_create_document_fragment (document);
+       }
+
+       if (WEBKIT_DOM_IS_HTML_BR_ELEMENT (prev_sibling)) {
+               if (key_code != ~0)
+                       webkit_dom_node_append_child (WEBKIT_DOM_NODE (fragment), prev_sibling, NULL);
+               else
+                       remove_node (prev_sibling);
+       }
+
+       prev_sibling = webkit_dom_node_get_previous_sibling (beginning);
+       if (WEBKIT_DOM_IS_ELEMENT (prev_sibling) &&
+           webkit_dom_element_has_attribute (WEBKIT_DOM_ELEMENT (prev_sibling), "data-hidden-space")) {
+               hidden_space = TRUE;
+               if (key_code != ~0)
+                       webkit_dom_node_insert_before (
+                               WEBKIT_DOM_NODE (fragment),
+                               prev_sibling,
+                               webkit_dom_node_get_first_child (WEBKIT_DOM_NODE (fragment)),
+                               NULL);
+               else
+                       remove_node (prev_sibling);
+       }
+
+       node = webkit_dom_node_get_previous_sibling (beginning);
+
+       if (key_code != ~0)
+               webkit_dom_node_append_child (WEBKIT_DOM_NODE (fragment), beginning, NULL);
+       else
+               remove_node (beginning);
+
+       if (!hidden_space) {
+               if (key_code != ~0) {
+                       gchar *data;
+
+                       data = webkit_dom_character_data_substring_data (
+                               WEBKIT_DOM_CHARACTER_DATA (node),
+                               webkit_dom_character_data_get_length (
+                                       WEBKIT_DOM_CHARACTER_DATA (node)) -1,
+                               1,
+                               NULL);
+
+                       webkit_dom_node_append_child (
+                               WEBKIT_DOM_NODE (fragment),
+                               WEBKIT_DOM_NODE (
+                                       webkit_dom_document_create_text_node (document, data)),
+                               NULL);
+
+                       g_free (data);
+               }
+
+               webkit_dom_character_data_delete_data (
+                       WEBKIT_DOM_CHARACTER_DATA (node),
+                       webkit_dom_character_data_get_length (
+                               WEBKIT_DOM_CHARACTER_DATA (node)) -1,
+                       1,
+                       NULL);
+       }
+
+       if (key_code != ~0) {
+               EEditorUndoRedoManager *manager;
+
+               e_editor_dom_selection_get_coordinates (editor_page,
+                       &ev->after.start.x,
+                       &ev->after.start.y,
+                       &ev->after.end.x,
+                       &ev->after.end.y);
+
+               ev->data.fragment = fragment;
+
+               manager = e_editor_page_get_undo_redo_manager (editor_page);
+               e_editor_undo_redo_manager_insert_history_event (manager, ev);
+       }
+
+       e_editor_dom_selection_restore (editor_page);
+
+       return TRUE;
+ out:
+       e_editor_dom_selection_restore (editor_page);
+
+       return FALSE;
+}
+
+gboolean
+e_editor_dom_delete_last_character_on_line_in_quoted_block (EEditorPage *editor_page,
+                                                           glong key_code,
+                                                           gboolean control_key)
+{
+       WebKitDOMDocument *document;
+       WebKitDOMElement *element;
+       WebKitDOMNode *node, *beginning, *next_sibling;
+       gboolean success = FALSE;
+
+       g_return_val_if_fail (E_IS_EDITOR_PAGE (editor_page), FALSE);
+
+       document = e_editor_page_get_document (editor_page);
+
+       /* We have to be in quoted content. */
+       if (!e_editor_dom_selection_is_citation (editor_page))
+               return FALSE;
+
+       /* Selection is just caret. */
+       if (!e_editor_dom_selection_is_collapsed (editor_page))
+               return FALSE;
+
+       e_editor_dom_selection_save (editor_page);
+
+       element = webkit_dom_document_get_element_by_id (
+               document, "-x-evo-selection-start-marker");
+
+       /* selection end marker */
+       node = webkit_dom_node_get_next_sibling (WEBKIT_DOM_NODE (element));
+
+       /* We have to be on the end of line. */
+       next_sibling = webkit_dom_node_get_next_sibling (node);
+       if (next_sibling &&
+           (!WEBKIT_DOM_IS_HTML_BR_ELEMENT (next_sibling) ||
+            webkit_dom_node_get_next_sibling (next_sibling)))
+               goto out;
+
+       /* Before the caret is just text. */
+       node = webkit_dom_node_get_previous_sibling (WEBKIT_DOM_NODE (element));
+       if (!(node && WEBKIT_DOM_IS_TEXT (node)))
+               goto out;
+
+       /* There is just one character. */
+       if (webkit_dom_character_data_get_length (WEBKIT_DOM_CHARACTER_DATA (node)) != 1)
+               goto out;
+
+       beginning = webkit_dom_node_get_previous_sibling (WEBKIT_DOM_NODE (node));
+       if (!(beginning && WEBKIT_DOM_IS_ELEMENT (beginning)))
+               goto out;
+
+       /* Before the text is the beginning of line. */
+       if (!(element_has_class (WEBKIT_DOM_ELEMENT (beginning), "-x-evo-quoted")))
+               goto out;
+
+       if (!webkit_dom_node_get_previous_sibling (beginning))
+               goto out;
+
+       if (key_code != ~0)
+               save_history_for_delete_or_backspace (
+                       editor_page, key_code == HTML_KEY_CODE_DELETE, control_key);
+
+       element = webkit_dom_node_get_parent_element (beginning);
+       remove_node (WEBKIT_DOM_NODE (element));
+
+       success = TRUE;
+ out:
+       e_editor_dom_selection_restore (editor_page);
+
+       if (success)
+               e_editor_dom_insert_new_line_into_citation (editor_page, NULL);
+
+       return success;
+}
+
+static gboolean
+selection_is_in_empty_list_item (WebKitDOMNode *selection_start_marker)
+{
+       gchar *text;
+       WebKitDOMNode *sibling;
+
+       /* Selection needs to be collapsed. */
+       sibling = webkit_dom_node_get_next_sibling (WEBKIT_DOM_NODE (selection_start_marker));
+       if (!e_editor_dom_is_selection_position_node (sibling))
+               return FALSE;
+
+       /* After the selection end there could be just the BR element. */
+       sibling = webkit_dom_node_get_next_sibling (sibling);
+       if (sibling && !WEBKIT_DOM_IS_HTML_BR_ELEMENT (sibling))
+              return FALSE;
+
+       if (sibling && webkit_dom_node_get_next_sibling (sibling))
+               return FALSE;
+
+       sibling = webkit_dom_node_get_previous_sibling (WEBKIT_DOM_NODE (selection_start_marker));
+
+       if (!sibling)
+               return TRUE;
+
+       /* Only text node with the zero width space character is allowed. */
+       if (!WEBKIT_DOM_IS_TEXT (sibling))
+               return FALSE;
+
+       if (webkit_dom_node_get_previous_sibling (sibling))
+               return FALSE;
+
+       if (webkit_dom_character_data_get_length (WEBKIT_DOM_CHARACTER_DATA (sibling)) != 1)
+               return FALSE;
+
+       text = webkit_dom_character_data_get_data (WEBKIT_DOM_CHARACTER_DATA (sibling));
+       if (!(text && g_strcmp0 (text, UNICODE_ZERO_WIDTH_SPACE) == 0)) {
+               g_free (text);
+               return FALSE;
+       }
+
+       g_free (text);
+
+       return TRUE;
+}
+
+static gboolean
+return_pressed_in_image_wrapper (EEditorPage *editor_page)
+{
+       WebKitDOMDocument *document;
+       WebKitDOMDocumentFragment *fragment;
+       WebKitDOMElement *selection_start_marker;
+       WebKitDOMNode *parent, *block, *clone;
+       EEditorHistoryEvent *ev = NULL;
+       EEditorUndoRedoManager *manager;
+
+       g_return_val_if_fail (E_IS_EDITOR_PAGE (editor_page), FALSE);
+
+       document = e_editor_page_get_document (editor_page);
+
+       if (!e_editor_dom_selection_is_collapsed (editor_page))
+               return FALSE;
+
+       e_editor_dom_selection_save (editor_page);
+
+       selection_start_marker = webkit_dom_document_get_element_by_id (
+               document, "-x-evo-selection-start-marker");
+
+       parent = webkit_dom_node_get_parent_node (WEBKIT_DOM_NODE (selection_start_marker));
+       if (!element_has_class (WEBKIT_DOM_ELEMENT (parent), "-x-evo-resizable-wrapper")) {
+               e_editor_dom_selection_restore (editor_page);
+               return FALSE;
+       }
+
+       manager = e_editor_page_get_undo_redo_manager (editor_page);
+
+       if (!e_editor_undo_redo_manager_is_operation_in_progress (manager)) {
+               ev = g_new0 (EEditorHistoryEvent, 1);
+               ev->type = HISTORY_INPUT;
+
+               e_editor_dom_selection_get_coordinates (editor_page,
+                       &ev->before.start.x,
+                       &ev->before.start.y,
+                       &ev->before.end.x,
+                       &ev->before.end.y);
+
+               fragment = webkit_dom_document_create_document_fragment (document);
+
+               g_object_set_data (
+                       G_OBJECT (fragment), "history-return-key", GINT_TO_POINTER (1));
+       }
+
+       block = e_editor_dom_get_parent_block_node_from_child (
+               WEBKIT_DOM_NODE (selection_start_marker));
+
+       clone = webkit_dom_node_clone_node_with_error (block, FALSE, NULL);
+       webkit_dom_node_append_child (
+               clone, WEBKIT_DOM_NODE (webkit_dom_document_create_element (document, "br", NULL)), NULL);
+
+       webkit_dom_node_insert_before (
+               webkit_dom_node_get_parent_node (block),
+               clone,
+               block,
+               NULL);
+
+       if (ev) {
+               webkit_dom_node_append_child (
+                       WEBKIT_DOM_NODE (fragment),
+                       webkit_dom_node_clone_node_with_error (clone, TRUE, NULL),
+                       NULL);
+
+               e_editor_dom_selection_get_coordinates (editor_page,
+                       &ev->after.start.x,
+                       &ev->after.start.y,
+                       &ev->after.end.x,
+                       &ev->after.end.y);
+
+               ev->data.fragment = fragment;
+
+               e_editor_undo_redo_manager_insert_history_event (manager, ev);
+       }
+
+       e_editor_page_emit_content_changed (editor_page);
+
+       e_editor_dom_selection_restore (editor_page);
+
+       return TRUE;
+}
+
+static gboolean
+return_pressed_after_h_rule (EEditorPage *editor_page)
+{
+       WebKitDOMDocument *document;
+       WebKitDOMDocumentFragment *fragment;
+       WebKitDOMElement *selection_marker;
+       WebKitDOMNode *node, *block, *clone, *hr, *insert_before = NULL;
+       EEditorHistoryEvent *ev = NULL;
+       EEditorUndoRedoManager *manager;
+
+       g_return_val_if_fail (E_IS_EDITOR_PAGE (editor_page), FALSE);
+
+       document = e_editor_page_get_document (editor_page);
+
+       if (!e_editor_dom_selection_is_collapsed (editor_page))
+               return FALSE;
+
+       e_editor_dom_selection_save (editor_page);
+
+       manager = e_editor_page_get_undo_redo_manager (editor_page);
+
+       if (e_editor_undo_redo_manager_is_operation_in_progress (manager)) {
+               selection_marker = webkit_dom_document_get_element_by_id (
+                       document, "-x-evo-selection-end-marker");
+
+               hr = webkit_dom_node_get_parent_node (WEBKIT_DOM_NODE (selection_marker));
+               hr = webkit_dom_node_get_next_sibling (hr);
+               node = webkit_dom_node_get_next_sibling (WEBKIT_DOM_NODE (selection_marker));
+               if (node && !WEBKIT_DOM_IS_HTML_BR_ELEMENT (node) &&
+                   !WEBKIT_DOM_IS_HTML_HR_ELEMENT (hr)) {
+                       e_editor_dom_selection_restore (editor_page);
+                       return FALSE;
+               }
+
+               insert_before = webkit_dom_node_get_next_sibling (hr);
+       } else {
+               selection_marker = webkit_dom_document_get_element_by_id (
+                       document, "-x-evo-selection-start-marker");
+
+               node = webkit_dom_node_get_parent_node (WEBKIT_DOM_NODE (selection_marker));
+               hr = webkit_dom_node_get_previous_sibling (WEBKIT_DOM_NODE (selection_marker));
+               if (!WEBKIT_DOM_IS_HTML_BODY_ELEMENT (node) ||
+                   !WEBKIT_DOM_IS_HTML_HR_ELEMENT (hr)) {
+                       e_editor_dom_selection_restore (editor_page);
+                       return FALSE;
+               }
+
+               insert_before = WEBKIT_DOM_NODE (selection_marker);
+
+               ev = g_new0 (EEditorHistoryEvent, 1);
+               ev->type = HISTORY_INPUT;
+
+               e_editor_dom_selection_get_coordinates (editor_page,
+                       &ev->before.start.x,
+                       &ev->before.start.y,
+                       &ev->before.end.x,
+                       &ev->before.end.y);
+
+               fragment = webkit_dom_document_create_document_fragment (document);
+
+               g_object_set_data (
+                       G_OBJECT (fragment), "history-return-key", GINT_TO_POINTER (1));
+       }
+
+       block = webkit_dom_node_get_previous_sibling (hr);
+
+       clone = webkit_dom_node_clone_node_with_error (block, FALSE, NULL);
+
+       webkit_dom_node_append_child (
+               clone, WEBKIT_DOM_NODE (webkit_dom_document_create_element (document, "br", NULL)), NULL);
+
+       webkit_dom_node_insert_before (
+               webkit_dom_node_get_parent_node (hr), clone, insert_before, NULL);
+
+       dom_remove_selection_markers (document);
+
+       webkit_dom_node_append_child (
+               WEBKIT_DOM_NODE (clone),
+               WEBKIT_DOM_NODE (
+                       dom_create_selection_marker (document, TRUE)),
+               NULL);
+       webkit_dom_node_append_child (
+               WEBKIT_DOM_NODE (clone),
+               WEBKIT_DOM_NODE (
+                       dom_create_selection_marker (document, FALSE)),
+               NULL);
+
+       if (ev) {
+               webkit_dom_node_append_child (
+                       WEBKIT_DOM_NODE (fragment),
+                       webkit_dom_node_clone_node_with_error (clone, TRUE, NULL),
+                       NULL);
+
+               e_editor_dom_selection_get_coordinates (editor_page,
+                       &ev->after.start.x,
+                       &ev->after.start.y,
+                       &ev->after.end.x,
+                       &ev->after.end.y);
+
+               ev->data.fragment = fragment;
+
+               e_editor_undo_redo_manager_insert_history_event (manager, ev);
+       }
+
+       e_editor_page_emit_content_changed (editor_page);
+
+       e_editor_dom_selection_restore (editor_page);
+
+       return TRUE;
+}
+
+gboolean
+e_editor_dom_return_pressed_in_empty_list_item (EEditorPage *editor_page)
+{
+       WebKitDOMDocument *document;
+       WebKitDOMElement *selection_start_marker;
+       WebKitDOMNode *parent;
+
+       g_return_val_if_fail (E_IS_EDITOR_PAGE (editor_page), FALSE);
+
+       document = e_editor_page_get_document (editor_page);
+
+       if (!e_editor_dom_selection_is_collapsed (editor_page))
+               return FALSE;
+
+       e_editor_dom_selection_save (editor_page);
+
+       selection_start_marker = webkit_dom_document_get_element_by_id (
+               document, "-x-evo-selection-start-marker");
+
+       parent = webkit_dom_node_get_parent_node (WEBKIT_DOM_NODE (selection_start_marker));
+       if (!WEBKIT_DOM_IS_HTML_LI_ELEMENT (parent)) {
+               e_editor_dom_selection_restore (editor_page);
+               return FALSE;
+       }
+
+       if (selection_is_in_empty_list_item (WEBKIT_DOM_NODE (selection_start_marker))) {
+               EEditorHistoryEvent *ev = NULL;
+               EEditorUndoRedoManager *manager;
+               WebKitDOMDocumentFragment *fragment;
+               WebKitDOMElement *paragraph;
+               WebKitDOMNode *list;
+
+               manager = e_editor_page_get_undo_redo_manager (editor_page);
+
+               if (!e_editor_undo_redo_manager_is_operation_in_progress (manager)) {
+                       ev = g_new0 (EEditorHistoryEvent, 1);
+                       ev->type = HISTORY_INPUT;
+
+                       e_editor_dom_selection_get_coordinates (editor_page,
+                               &ev->before.start.x,
+                               &ev->before.start.y,
+                               &ev->before.end.x,
+                               &ev->before.end.y);
+
+                       fragment = webkit_dom_document_create_document_fragment (document);
+
+                       g_object_set_data (
+                               G_OBJECT (fragment), "history-return-key", GINT_TO_POINTER (1));
+               }
+
+               list = split_list_into_two (parent, -1);
+
+               if (ev) {
+                       webkit_dom_node_append_child (
+                               WEBKIT_DOM_NODE (fragment),
+                               parent,
+                               NULL);
+               } else {
+                       remove_node (parent);
+               }
+
+               paragraph = e_editor_dom_prepare_paragraph (editor_page, TRUE);
+
+               webkit_dom_node_insert_before (
+                       webkit_dom_node_get_parent_node (list),
+                       WEBKIT_DOM_NODE (paragraph),
+                       list,
+                       NULL);
+
+               remove_node_if_empty (list);
+
+               if (ev) {
+                       e_editor_dom_selection_get_coordinates (editor_page,
+                               &ev->after.start.x,
+                               &ev->after.start.y,
+                               &ev->after.end.x,
+                               &ev->after.end.y);
+
+                       ev->data.fragment = fragment;
+
+                       e_editor_undo_redo_manager_insert_history_event (manager, ev);
+               }
+
+               e_editor_dom_selection_restore (editor_page);
+
+               e_editor_page_emit_content_changed (editor_page);
+
+               return TRUE;
+       }
+
+       e_editor_dom_selection_restore (editor_page);
+
+       return FALSE;
+}
+
+static void
+process_smiley_on_delete_or_backspace (EEditorPage *editor_page)
+{
+       WebKitDOMDocument *document;
+       WebKitDOMElement *element;
+       WebKitDOMNode *parent;
+       gboolean in_smiley = FALSE;
+
+       g_return_if_fail (E_IS_EDITOR_PAGE (editor_page));
+
+       document = e_editor_page_get_document (editor_page);
+       e_editor_dom_selection_save (editor_page);
+       element = webkit_dom_document_get_element_by_id (
+               document, "-x-evo-selection-start-marker");
+
+       parent = webkit_dom_node_get_parent_node (WEBKIT_DOM_NODE (element));
+       if (WEBKIT_DOM_IS_ELEMENT (parent) &&
+           element_has_class (WEBKIT_DOM_ELEMENT (parent), "-x-evo-smiley-text"))
+               in_smiley = TRUE;
+       else {
+               if (e_editor_dom_selection_is_collapsed (editor_page)) {
+                       WebKitDOMNode *prev_sibling;
+
+                       prev_sibling = webkit_dom_node_get_previous_sibling (WEBKIT_DOM_NODE (element));
+                       if (prev_sibling && WEBKIT_DOM_IS_TEXT (prev_sibling)) {
+                               gchar *text = webkit_dom_character_data_get_data (
+                                       WEBKIT_DOM_CHARACTER_DATA (prev_sibling));
+
+                               if (g_strcmp0 (text, UNICODE_ZERO_WIDTH_SPACE) == 0) {
+                                       WebKitDOMNode *prev_prev_sibling;
+
+                                       prev_prev_sibling = webkit_dom_node_get_previous_sibling 
(prev_sibling);
+                                       if (WEBKIT_DOM_IS_ELEMENT (prev_prev_sibling) &&
+                                           element_has_class (WEBKIT_DOM_ELEMENT (prev_prev_sibling), 
"-x-evo-smiley-wrapper")) {
+                                               remove_node (prev_sibling);
+                                               in_smiley = TRUE;
+                                               parent = webkit_dom_node_get_last_child (prev_prev_sibling);
+                                       }
+                               }
+
+                               g_free (text);
+                       }
+               } else {
+                       element = webkit_dom_document_get_element_by_id (
+                               document, "-x-evo-selection-end-marker");
+
+                       parent = webkit_dom_node_get_parent_node (WEBKIT_DOM_NODE (element));
+                       if (WEBKIT_DOM_IS_ELEMENT (parent) &&
+                           element_has_class (WEBKIT_DOM_ELEMENT (parent), "-x-evo-smiley-text"))
+                               in_smiley = TRUE;
+               }
+       }
+
+       if (in_smiley) {
+               WebKitDOMNode *wrapper;
+
+               wrapper = webkit_dom_node_get_parent_node (parent);
+               if (!e_editor_page_get_html_mode (editor_page)) {
+                       WebKitDOMNode *child;
+
+                       while ((child = webkit_dom_node_get_first_child (parent)))
+                               webkit_dom_node_insert_before (
+                                       webkit_dom_node_get_parent_node (wrapper),
+                                       child,
+                                       wrapper,
+                                       NULL);
+               }
+               /* In the HTML mode the whole smiley will be removed. */
+               remove_node (wrapper);
+               /* FIXME history will be probably broken here */
+       }
+
+       e_editor_dom_selection_restore (editor_page);
+}
+
+gboolean
+e_editor_dom_key_press_event_process_return_key (EEditorPage *editor_page)
+{
+       WebKitDOMDocument *document;
+       WebKitDOMNode *table = NULL;
+       gboolean first_cell = FALSE;
+
+       g_return_val_if_fail (E_IS_EDITOR_PAGE (editor_page), FALSE);
+
+       document = e_editor_page_get_document (editor_page);
+       /* Return pressed in the beginning of the first cell will insert
+        * new block before the table (and move the caret there) if none
+        * is already there, otherwise it will act as normal return. */
+       if (selection_is_in_table (document, &first_cell, &table) && first_cell) {
+               WebKitDOMNode *node;
+
+               node = webkit_dom_node_get_previous_sibling (table);
+               if (!node) {
+                       node = webkit_dom_node_get_next_sibling (table);
+                       node = webkit_dom_node_clone_node_with_error (node, FALSE, NULL);
+                       webkit_dom_node_append_child (
+                               node,
+                               WEBKIT_DOM_NODE (webkit_dom_document_create_element (
+                                       document, "br", NULL)),
+                               NULL);
+                       dom_add_selection_markers_into_element_start (
+                               document, WEBKIT_DOM_ELEMENT (node), NULL, NULL);
+                       webkit_dom_node_insert_before (
+                               webkit_dom_node_get_parent_node (table),
+                               node,
+                               table,
+                               NULL);
+                       e_editor_dom_selection_restore (editor_page);
+                       e_editor_page_emit_content_changed (editor_page);
+                       return TRUE;
+               }
+       }
+
+       /* When user presses ENTER in a citation block, WebKit does
+        * not break the citation automatically, so we need to use
+        * the special command to do it. */
+       if (e_editor_dom_selection_is_citation (editor_page)) {
+               e_editor_dom_remove_input_event_listener_from_body (editor_page);
+               if (split_citation (editor_page)) {
+                       e_editor_page_set_return_key_pressed (editor_page, TRUE);
+                       e_editor_dom_check_magic_links (editor_page, FALSE);
+                       e_editor_page_set_return_key_pressed (editor_page, FALSE);
+                       e_editor_page_emit_content_changed (editor_page);
+
+                       return TRUE;
+               }
+               return FALSE;
+       }
+
+       /* If the ENTER key is pressed inside an empty list item then the list
+        * is broken into two and empty paragraph is inserted between lists. */
+       if (e_editor_dom_return_pressed_in_empty_list_item (editor_page))
+               return TRUE;
+
+       if (return_pressed_in_image_wrapper (editor_page))
+               return TRUE;
+
+       if (return_pressed_after_h_rule (editor_page))
+               return TRUE;
+
+       return FALSE;
+}
+
+static gboolean
+remove_empty_bulleted_list_item (EEditorPage *editor_page)
+{
+       WebKitDOMDocument *document;
+       WebKitDOMElement *selection_start;
+       WebKitDOMNode *parent;
+       EEditorUndoRedoManager *manager;
+
+       g_return_val_if_fail (E_IS_EDITOR_PAGE (editor_page), FALSE);
+
+       document = e_editor_page_get_document (editor_page);
+       manager = e_editor_page_get_undo_redo_manager (editor_page);
+       e_editor_dom_selection_save (editor_page);
+
+       selection_start = webkit_dom_document_get_element_by_id (
+               document, "-x-evo-selection-start-marker");
+
+       parent = webkit_dom_node_get_parent_node (WEBKIT_DOM_NODE (selection_start));
+       while (parent && !node_is_list_or_item (parent))
+               parent = webkit_dom_node_get_parent_node (parent);
+
+       if (!parent)
+               goto out;
+
+       if (selection_is_in_empty_list_item (WEBKIT_DOM_NODE (selection_start))) {
+               EEditorHistoryEvent *ev = NULL;
+               WebKitDOMDocumentFragment *fragment;
+               WebKitDOMNode *prev_item;
+
+               prev_item = webkit_dom_node_get_previous_sibling (parent);
+
+               if (!e_editor_undo_redo_manager_is_operation_in_progress (manager)) {
+                       /* Insert new history event for Return to have the right coordinates.
+                        * The fragment will be added later. */
+                       ev = g_new0 (EEditorHistoryEvent, 1);
+                       ev->type = HISTORY_DELETE;
+
+                       e_editor_dom_selection_get_coordinates (editor_page,
+                               &ev->before.start.x,
+                               &ev->before.start.y,
+                               &ev->before.end.x,
+                               &ev->before.end.y);
+
+                       fragment = webkit_dom_document_create_document_fragment (document);
+               }
+
+               if (ev) {
+                       if (prev_item)
+                               webkit_dom_node_append_child (
+                                       WEBKIT_DOM_NODE (fragment),
+                                       webkit_dom_node_clone_node_with_error (prev_item, TRUE, NULL),
+                                       NULL);
+
+                       webkit_dom_node_append_child (
+                               WEBKIT_DOM_NODE (fragment),
+                               parent,
+                               NULL);
+               } else
+                       remove_node (parent);
+
+               if (prev_item)
+                       dom_add_selection_markers_into_element_end (
+                               document, WEBKIT_DOM_ELEMENT (prev_item), NULL, NULL);
+
+               if (ev) {
+                       e_editor_dom_selection_get_coordinates (editor_page,
+                               &ev->after.start.x,
+                               &ev->after.start.y,
+                               &ev->after.end.x,
+                               &ev->after.end.y);
+
+                       ev->data.fragment = fragment;
+
+                       e_editor_undo_redo_manager_insert_history_event (manager, ev);
+               }
+
+               e_editor_page_emit_content_changed (editor_page);
+               e_editor_dom_selection_restore (editor_page);
+
+               return TRUE;
+       }
+ out:
+       e_editor_dom_selection_restore (editor_page);
+
+       return FALSE;
+}
+
+gboolean
+e_editor_dom_key_press_event_process_backspace_key (EEditorPage *editor_page)
+{
+       g_return_val_if_fail (E_IS_EDITOR_PAGE (editor_page), FALSE);
+
+       /* BackSpace pressed in the beginning of quoted content changes
+        * format to normal and inserts text into body */
+       if (e_editor_dom_selection_is_collapsed (editor_page)) {
+               e_editor_dom_selection_save (editor_page);
+               if (e_editor_dom_move_quoted_block_level_up (editor_page) || delete_hidden_space 
(editor_page)) {
+                       e_editor_dom_selection_restore (editor_page);
+                       e_editor_dom_force_spell_check_for_current_paragraph (editor_page);
+                       e_editor_page_emit_content_changed (editor_page);
+                       return TRUE;
+               }
+               e_editor_dom_selection_restore (editor_page);
+       }
+
+       /* BackSpace in indented block decrease indent level by one */
+       if (e_editor_dom_selection_is_indented (editor_page) &&
+           e_editor_dom_selection_is_collapsed (editor_page)) {
+               WebKitDOMDocument *document;
+               WebKitDOMElement *selection_start;
+               WebKitDOMNode *prev_sibling;
+
+               document = e_editor_page_get_document (editor_page);
+
+               e_editor_dom_selection_save (editor_page);
+               selection_start = webkit_dom_document_get_element_by_id (
+                       document, "-x-evo-selection-start-marker");
+
+               /* Empty text node before caret */
+               prev_sibling = webkit_dom_node_get_previous_sibling (
+                       WEBKIT_DOM_NODE (selection_start));
+               if (prev_sibling && WEBKIT_DOM_IS_TEXT (prev_sibling))
+                       if (webkit_dom_character_data_get_length (WEBKIT_DOM_CHARACTER_DATA (prev_sibling)) 
== 0)
+                               prev_sibling = webkit_dom_node_get_previous_sibling (prev_sibling);
+
+               e_editor_dom_selection_restore (editor_page);
+               if (!prev_sibling) {
+                       e_editor_dom_selection_unindent (editor_page);
+                       e_editor_page_emit_content_changed (editor_page);
+                       return TRUE;
+               }
+       }
+
+       /* BackSpace pressed in an empty item in the bulleted list removes it. */
+       if (!e_editor_page_get_html_mode (editor_page) && e_editor_dom_selection_is_collapsed (editor_page) &&
+           remove_empty_bulleted_list_item (editor_page))
+               return TRUE;
+
+
+       if (prevent_from_deleting_last_element_in_body (e_editor_page_get_document (editor_page)))
+               return TRUE;
+
+       return FALSE;
+}
+
+gboolean
+e_editor_dom_key_press_event_process_delete_or_backspace_key (EEditorPage *editor_page,
+                                                             glong key_code,
+                                                             gboolean control_key,
+                                                             gboolean delete)
+{
+       WebKitDOMDocument *document;
+       gboolean html_mode;
+       gboolean local_delete;
+
+       g_return_val_if_fail (E_IS_EDITOR_PAGE (editor_page), FALSE);
+
+       document = e_editor_page_get_document (editor_page);
+       html_mode = e_editor_page_get_html_mode (editor_page);
+       local_delete = (key_code == HTML_KEY_CODE_DELETE) || delete;
+
+       if (e_editor_page_get_magic_smileys_enabled (editor_page)) {
+               /* If deleting something in a smiley it won't be a smiley
+                * anymore (at least from Evolution' POV), so remove all
+                * the elements that are hidden in the wrapper and leave
+                * just the text. Also this ensures that when a smiley is
+                * recognized and we press the BackSpace key we won't delete
+                * the UNICODE_HIDDEN_SPACE, but we will correctly delete
+                * the last character of smiley. */
+               process_smiley_on_delete_or_backspace (editor_page);
+       }
+
+       if (!local_delete && !html_mode &&
+           e_editor_dom_delete_last_character_on_line_in_quoted_block (editor_page, key_code, control_key))
+               goto out;
+
+       if (!local_delete && !html_mode &&
+           delete_last_character_from_previous_line_in_quoted_block (editor_page, key_code, control_key))
+               goto out;
+
+       if (e_editor_dom_fix_structure_after_delete_before_quoted_content (editor_page, key_code, 
control_key, FALSE))
+               goto out;
+
+       if (local_delete) {
+               WebKitDOMElement *selection_start_marker;
+               WebKitDOMNode *sibling, *block, *next_block;
+
+               /* This needs to be performed just in plain text mode
+                * and when the selection is collapsed. */
+               if (html_mode || !e_editor_dom_selection_is_collapsed (editor_page))
+                       return FALSE;
+
+               e_editor_dom_selection_save (editor_page);
+
+               selection_start_marker = webkit_dom_document_get_element_by_id (
+                       document, "-x-evo-selection-start-marker");
+               sibling = webkit_dom_node_get_previous_sibling (
+                       WEBKIT_DOM_NODE (selection_start_marker));
+               /* Check if the key was pressed in the beginning of block. */
+               if (!(sibling && WEBKIT_DOM_IS_ELEMENT (sibling) &&
+                     element_has_class (WEBKIT_DOM_ELEMENT (sibling), "-x-evo-quoted"))) {
+                       e_editor_dom_selection_restore (editor_page);
+                       return FALSE;
+               }
+
+               sibling = webkit_dom_node_get_next_sibling (
+                       WEBKIT_DOM_NODE (selection_start_marker));
+               sibling = webkit_dom_node_get_next_sibling (sibling);
+
+               /* And also the current block was empty. */
+               if (!(!sibling || (sibling && WEBKIT_DOM_IS_HTML_BR_ELEMENT (sibling) &&
+                     !element_has_class (WEBKIT_DOM_ELEMENT (sibling), "-x-evo-wrap-br")))) {
+                       e_editor_dom_selection_restore (editor_page);
+                       return FALSE;
+               }
+
+               block = e_editor_dom_get_parent_block_node_from_child (
+                       WEBKIT_DOM_NODE (selection_start_marker));
+               next_block = webkit_dom_node_get_next_sibling (block);
+
+               remove_node (block);
+
+               e_editor_dom_move_caret_into_element (editor_page, WEBKIT_DOM_ELEMENT (next_block), TRUE);
+
+               goto out;
+       } else {
+               /* Concatenating a non-quoted block with Backspace key to the
+                * previous block that is inside a quoted content. */
+               WebKitDOMElement *selection_start_marker;
+               WebKitDOMNode *node, *block, *prev_block, *last_child, *child;
+
+               if (html_mode || !e_editor_dom_selection_is_collapsed (editor_page) ||
+                   e_editor_dom_selection_is_citation (editor_page))
+                       return FALSE;
+
+               e_editor_dom_selection_save (editor_page);
+
+               selection_start_marker = webkit_dom_document_get_element_by_id (
+                       document, "-x-evo-selection-start-marker");
+
+               node = webkit_dom_node_get_previous_sibling (WEBKIT_DOM_NODE (selection_start_marker));
+               if (node) {
+                       e_editor_dom_selection_restore (editor_page);
+                       return FALSE;
+               }
+
+               remove_empty_blocks (document);
+
+               block = e_editor_dom_get_parent_block_node_from_child (
+                       WEBKIT_DOM_NODE (selection_start_marker));
+
+               prev_block = webkit_dom_node_get_previous_sibling (block);
+               if (!prev_block || !WEBKIT_DOM_IS_HTML_QUOTE_ELEMENT (prev_block)) {
+                       e_editor_dom_selection_restore (editor_page);
+                       return FALSE;
+               }
+
+               last_child = webkit_dom_node_get_last_child (prev_block);
+               while (last_child && WEBKIT_DOM_IS_HTML_QUOTE_ELEMENT (last_child))
+                       last_child = webkit_dom_node_get_last_child (last_child);
+
+               if (!last_child) {
+                       e_editor_dom_selection_restore (editor_page);
+                       return FALSE;
+               }
+
+               e_editor_dom_remove_wrapping_from_element (WEBKIT_DOM_ELEMENT (last_child));
+               e_editor_dom_remove_quoting_from_element (WEBKIT_DOM_ELEMENT (last_child));
+
+               node = webkit_dom_node_get_last_child (last_child);
+               if (WEBKIT_DOM_IS_HTML_BR_ELEMENT (node))
+                       remove_node (node);
+
+               while ((child = webkit_dom_node_get_first_child (block)))
+                       webkit_dom_node_append_child (last_child, child, NULL);
+
+               remove_node (block);
+
+               if (WEBKIT_DOM_IS_ELEMENT (last_child))
+                       e_editor_dom_wrap_and_quote_element (editor_page, WEBKIT_DOM_ELEMENT (last_child));
+
+               e_editor_dom_selection_restore (editor_page);
+
+               goto out;
+       }
+
+       return FALSE;
+ out:
+       e_editor_dom_force_spell_check_for_current_paragraph (editor_page);
+       e_editor_page_emit_content_changed (editor_page);
+
+       return TRUE;
+}
+
+static gboolean
+contains_forbidden_elements (WebKitDOMDocument *document)
+{
+       WebKitDOMElement *body, *element;
+
+       body = WEBKIT_DOM_ELEMENT (webkit_dom_document_get_body (document));
+
+       /* Try to find disallowed elements in the plain text mode */
+       element = webkit_dom_element_query_selector (
+               body,
+               ":not("
+               /* Basic elements used as blocks allowed in the plain text mode */
+               "p[data-evo-paragraph], pre, ul, ol, li, blockquote[type=cite], "
+               /* Other elements */
+               "br, a, "
+               /* Indented elements */
+               ".-x-evo-indented, "
+               /* Signature */
+               ".-x-evo-signature-wrapper, .-x-evo-signature, "
+               /* Smileys */
+               ".-x-evo-smiley-wrapper, .-x-evo-smiley-img, .-x-evo-smiley-text, "
+               /* Selection markers */
+               "#-x-evo-selection-start-marker, #-x-evo-selection-end-marker"
+               ")",
+               NULL);
+
+       if (element)
+               return TRUE;
+
+       /* Try to find disallowed elements relationship in the plain text */
+       element = webkit_dom_element_query_selector (
+               body,
+               ":not("
+               /* Body descendants */
+               "body > :matches(blockquote[type=cite], .-x-evo-signature-wrapper), "
+               /* Main blocks and indented blocks */
+               ":matches(body, .-x-evo-indented) > :matches(pre, p, ul, ol, .-x-evo-indented), "
+               /* Blockquote descendants */
+               "blockquote[type=cite] > :matches(pre, p, blockquote[type=cite]), "
+               /* Block descendants */
+               ":matches(pre, p, li) > :matches(br, span, a), "
+               /* Lists */
+               ":matches(ul, ol) > :matches(ul, ol, li), "
+               /* Smileys */
+               ".-x-evo-smiley-wrapper > :matches(.-x-evo-smiley-img, .-x-evo-smiley-text), "
+               /* Signature */
+               ".-x-evo-signature-wrapper > .-x-evo-signature"
+               ")",
+               NULL);
+
+       return element ? TRUE : FALSE;
+}
+
+gboolean
+e_editor_dom_check_if_conversion_needed (EEditorPage *editor_page)
+{
+       WebKitDOMDocument *document;
+       gboolean html_mode, convert = FALSE;
+
+       g_return_val_if_fail (E_IS_EDITOR_PAGE (editor_page), FALSE);
+
+       document = e_editor_page_get_document (editor_page);
+       html_mode = e_editor_page_get_html_mode (editor_page);
+
+       if (html_mode)
+               convert = contains_forbidden_elements (document);
+
+       return convert;
+}
+
+void
+e_editor_dom_process_content_after_mode_change (EEditorPage *editor_page)
+{
+       WebKitDOMDocument *document;
+       EEditorUndoRedoManager *manager;
+       gboolean html_mode;
+
+       g_return_if_fail (E_IS_EDITOR_PAGE (editor_page));
+
+       document = e_editor_page_get_document (editor_page);
+       html_mode = e_editor_page_get_html_mode (editor_page);
+
+       if (html_mode)
+               process_content_to_html_changing_composer_mode (editor_page);
+       else
+               process_content_to_plain_text_changing_composer_mode (editor_page);
+
+       e_editor_dom_set_monospace_font_family_on_body (
+               WEBKIT_DOM_ELEMENT (webkit_dom_document_get_body (document)), html_mode);
+
+       manager = e_editor_page_get_undo_redo_manager (editor_page);
+       e_editor_undo_redo_manager_clean_history (manager);
+}
+
+guint
+e_editor_dom_get_caret_offset (EEditorPage *editor_page)
+{
+       WebKitDOMDocument *document;
+       WebKitDOMDOMWindow *dom_window = NULL;
+       WebKitDOMDOMSelection *dom_selection = NULL;
+       WebKitDOMNode *anchor;
+       WebKitDOMRange *range = NULL;
+       guint ret_val;
+       gchar *text;
+
+       g_return_val_if_fail (E_IS_EDITOR_PAGE (editor_page), 0);
+
+       document = e_editor_page_get_document (editor_page);
+       dom_window = webkit_dom_document_get_default_view (document);
+       dom_selection = webkit_dom_dom_window_get_selection (dom_window);
+       g_clear_object (&dom_window);
+
+       if (webkit_dom_dom_selection_get_range_count (dom_selection) < 1) {
+               g_clear_object (&dom_selection);
+               return 0;
+       }
+
+       webkit_dom_dom_selection_collapse_to_start (dom_selection, NULL);
+       /* Select the text from the current caret position to the beginning of the line. */
+       webkit_dom_dom_selection_modify (dom_selection, "extend", "left", "lineBoundary");
+
+       range = webkit_dom_dom_selection_get_range_at (dom_selection, 0, NULL);
+       anchor = webkit_dom_dom_selection_get_anchor_node (dom_selection);
+       text = webkit_dom_range_to_string (range, NULL);
+       ret_val = strlen (text);
+       g_free (text);
+
+       webkit_dom_dom_selection_collapse_to_end (dom_selection, NULL);
+
+       /* In the plain text mode we need to increase the return value by 2 per
+        * citation level because of "> ". */
+       if (!e_editor_page_get_html_mode (editor_page)) {
+               WebKitDOMNode *parent = anchor;
+
+               while (parent && !WEBKIT_DOM_IS_HTML_BODY_ELEMENT (parent)) {
+                       if (WEBKIT_DOM_IS_HTML_QUOTE_ELEMENT (parent))
+                               ret_val += 2;
+
+                       parent = webkit_dom_node_get_parent_node (parent);
+               }
+       }
+
+       g_clear_object (&range);
+       g_clear_object (&dom_selection);
+
+       return ret_val;
+}
+
+guint
+e_editor_dom_get_caret_position (EEditorPage *editor_page)
+{
+       WebKitDOMDocument *document;
+       WebKitDOMHTMLElement *body;
+       WebKitDOMDOMWindow *dom_window = NULL;
+       WebKitDOMDOMSelection *dom_selection = NULL;
+       WebKitDOMRange *range = NULL, *range_clone = NULL;
+       guint ret_val;
+       gchar *text;
+
+       g_return_val_if_fail (E_IS_EDITOR_PAGE (editor_page), 0);
+
+       document = e_editor_page_get_document (editor_page);
+       dom_window = webkit_dom_document_get_default_view (document);
+       dom_selection = webkit_dom_dom_window_get_selection (dom_window);
+       g_clear_object (&dom_window);
+
+       if (webkit_dom_dom_selection_get_range_count (dom_selection) < 1) {
+               g_clear_object (&dom_selection);
+               return 0;
+       }
+
+       range = webkit_dom_dom_selection_get_range_at (dom_selection, 0, NULL);
+       range_clone = webkit_dom_range_clone_range (range, NULL);
+
+       body = webkit_dom_document_get_body (document);
+       /* Select the text from the beginning of the body to the current caret. */
+       webkit_dom_range_set_start_before (
+               range_clone, webkit_dom_node_get_first_child (WEBKIT_DOM_NODE (body)), NULL);
+
+       /* This is returning a text without new lines! */
+       text = webkit_dom_range_to_string (range_clone, NULL);
+       ret_val = strlen (text);
+       g_free (text);
+
+       g_clear_object (&range_clone);
+       g_clear_object (&range);
+       g_clear_object (&dom_selection);
+
+       return ret_val;
+}
+
+void
+e_editor_dom_save_history_for_drop (EEditorPage *editor_page)
+{
+       WebKitDOMDocument *document;
+       WebKitDOMDocumentFragment *fragment;
+       WebKitDOMDOMSelection *dom_selection = NULL;
+       WebKitDOMDOMWindow *dom_window = NULL;
+       WebKitDOMNodeList *list = NULL;
+       WebKitDOMRange *range = NULL;
+       EEditorUndoRedoManager *manager;
+       EEditorHistoryEvent *event;
+       gint ii, length;
+
+       g_return_if_fail (E_IS_EDITOR_PAGE (editor_page));
+
+       document = e_editor_page_get_document (editor_page);
+       manager = e_editor_page_get_undo_redo_manager (editor_page);
+
+       /* When the image is DnD inside the view WebKit removes the wrapper that
+        * is used for resizing the image, so we have to recreate it again. */
+       list = webkit_dom_document_query_selector_all (document, ":not(span) > img[data-inline]", NULL);
+       length = webkit_dom_node_list_get_length (list);
+       for (ii = 0; ii < length; ii++) {
+               WebKitDOMElement *element;
+               WebKitDOMNode *node = webkit_dom_node_list_item (list, ii);
+
+               element = webkit_dom_document_create_element (document, "span", NULL);
+               webkit_dom_element_set_class_name (element, "-x-evo-resizable-wrapper");
+
+               webkit_dom_node_insert_before (
+                       webkit_dom_node_get_parent_node (node),
+                       WEBKIT_DOM_NODE (element),
+                       node,
+                       NULL);
+
+               webkit_dom_node_append_child (WEBKIT_DOM_NODE (element), node, NULL);
+               g_object_unref (node);
+       }
+       g_clear_object (&list);
+
+       /* When the image is moved the new selection is created after after it, so
+        * lets collapse the selection to have the caret right after the image. */
+       dom_window = webkit_dom_document_get_default_view (document);
+       dom_selection = webkit_dom_dom_window_get_selection (dom_window);
+       g_clear_object (&dom_window);
+
+       range = webkit_dom_dom_selection_get_range_at (dom_selection, 0, NULL);
+
+       /* Remove the last inserted history event as this one was inserted in
+        * body_input_event_cb and is wrong as its type is HISTORY_INPUT. */
+       /* FIXME we could probably disable the HTML input event callback while
+        * doing DnD within the view */
+       /* FIXME WK2 - what if e_editor_undo_redo_manager_get_current_history_event() returns NULL? */
+       if (((EEditorHistoryEvent *) (e_editor_undo_redo_manager_get_current_history_event (manager)))->type 
== HISTORY_INPUT)
+               e_editor_undo_redo_manager_remove_current_history_event (manager);
+
+       event = g_new0 (EEditorHistoryEvent, 1);
+       event->type = HISTORY_INSERT_HTML;
+
+       /* Get the dropped content. It's easy as it is selected by WebKit. */
+       fragment = webkit_dom_range_clone_contents (range, NULL);
+       event->data.string.from = NULL;
+       /* Get the HTML content of the dropped content. */
+       event->data.string.to = dom_get_node_inner_html (WEBKIT_DOM_NODE (fragment));
+
+       e_editor_dom_selection_get_coordinates (editor_page,
+               &event->before.start.x,
+               &event->before.start.y,
+               &event->before.end.x,
+               &event->before.end.y);
+
+       event->before.end.x = event->before.start.x;
+       event->before.end.y = event->before.start.y;
+
+       if (length > 0)
+               webkit_dom_dom_selection_collapse_to_start (dom_selection, NULL);
+       else
+               webkit_dom_dom_selection_collapse_to_end (dom_selection, NULL);
+
+       e_editor_dom_selection_get_coordinates (editor_page,
+               &event->after.start.x,
+               &event->after.start.y,
+               &event->after.end.x,
+               &event->after.end.y);
+
+       e_editor_undo_redo_manager_insert_history_event (manager, event);
+
+       if (!e_editor_page_get_html_mode (editor_page)) {
+               list = webkit_dom_document_query_selector_all (
+                       document, "span[style^=font-family]", NULL);
+               length = webkit_dom_node_list_get_length (list);
+               if (length > 0)
+                       e_editor_dom_selection_save (editor_page);
+
+               for (ii = 0; ii < length; ii++) {
+                       WebKitDOMNode *span, *child;
+
+                       span = webkit_dom_node_list_item (list, ii);
+                       while ((child = webkit_dom_node_get_first_child (span)))
+                               webkit_dom_node_insert_before (
+                                       webkit_dom_node_get_parent_node (span),
+                                       child,
+                                       span,
+                                       NULL);
+
+                       remove_node (span);
+                       g_object_unref (span);
+               }
+               g_clear_object (&list);
+
+               if (length > 0)
+                       e_editor_dom_selection_restore (editor_page);
+       }
+
+       e_editor_dom_force_spell_check_in_viewport (editor_page);
+
+       g_clear_object (&range);
+       g_clear_object (&dom_selection);
+}
+
+void
+e_editor_dom_drag_and_drop_end (EEditorPage *editor_page)
+{
+       g_return_if_fail (E_IS_EDITOR_PAGE (editor_page));
+
+       e_editor_dom_save_history_for_drop (editor_page);
+}
+
+static void
+dom_set_link_color_in_document (EEditorPage *editor_page,
+                                const gchar *color,
+                                gboolean visited)
+{
+       WebKitDOMDocument *document;
+       WebKitDOMHTMLHeadElement *head;
+       WebKitDOMElement *style_element;
+       WebKitDOMHTMLElement *body;
+       gchar *color_str = NULL;
+       const gchar *style_id;
+
+       g_return_if_fail (E_IS_EDITOR_PAGE (editor_page));
+       g_return_if_fail (color != NULL);
+
+       style_id = visited ? "-x-evo-a-color-style-visited" : "-x-evo-a-color-style";
+
+       document = e_editor_page_get_document (editor_page);
+       head = webkit_dom_document_get_head (document);
+       body = webkit_dom_document_get_body (document);
+
+       style_element = webkit_dom_document_get_element_by_id (document, style_id);
+       if (!style_element) {
+               style_element = webkit_dom_document_create_element (document, "style", NULL);
+               webkit_dom_element_set_id (style_element, style_id);
+               webkit_dom_element_set_attribute (style_element, "type", "text/css", NULL);
+               webkit_dom_node_append_child (
+                       WEBKIT_DOM_NODE (head), WEBKIT_DOM_NODE (style_element), NULL);
+       }
+
+       color_str = g_strdup_printf (
+               visited ? "a.-x-evo-visited-link { color: %s; }" : "a { color: %s; }", color);
+       webkit_dom_element_set_inner_html (style_element, color_str, NULL);
+       g_free (color_str);
+
+       if (visited)
+               webkit_dom_html_body_element_set_v_link (
+                       WEBKIT_DOM_HTML_BODY_ELEMENT (body), color);
+       else
+               webkit_dom_html_body_element_set_link (
+                       WEBKIT_DOM_HTML_BODY_ELEMENT (body), color);
+}
+
+void
+e_editor_dom_set_link_color (EEditorPage *editor_page,
+                            const gchar *color)
+{
+       g_return_if_fail (E_IS_EDITOR_PAGE (editor_page));
+
+       dom_set_link_color_in_document (editor_page, color, FALSE);
+}
+
+void
+e_editor_dom_set_visited_link_color (EEditorPage *editor_page,
+                                    const gchar *color)
+{
+       g_return_if_fail (E_IS_EDITOR_PAGE (editor_page));
+
+       dom_set_link_color_in_document (editor_page, color, TRUE);
+}
+
+void
+e_editor_dom_fix_file_uri_images (EEditorPage *editor_page)
+{
+       WebKitDOMDocument *document;
+       WebKitDOMNodeList *list = NULL;
+       gint ii, length;
+
+       g_return_if_fail (E_IS_EDITOR_PAGE (editor_page));
+
+       document = e_editor_page_get_document (editor_page);
+
+       list = webkit_dom_document_query_selector_all (
+               document, "img[src^=\"file://\"]", NULL);
+       length = webkit_dom_node_list_get_length (list);
+
+       for (ii = 0; ii < length; ii++) {
+               WebKitDOMNode *node;
+               gchar *uri;
+
+               node = webkit_dom_node_list_item (list, ii);
+               uri = webkit_dom_element_get_attribute (WEBKIT_DOM_ELEMENT (node), "src");
+               g_free (uri);
+       }
+
+       g_clear_object (&list);
+}
+
+/* ******************** Selection ******************** */
+
+void
+e_editor_dom_replace_base64_image_src (EEditorPage *editor_page,
+                                      const gchar *selector,
+                                      const gchar *base64_content,
+                                      const gchar *filename,
+                                      const gchar *uri)
+{
+       WebKitDOMDocument *document;
+       WebKitDOMElement *element;
+
+       g_return_if_fail (E_IS_EDITOR_PAGE (editor_page));
+
+       document = e_editor_page_get_document (editor_page);
+       element = webkit_dom_document_query_selector (document, selector, NULL);
+
+       if (WEBKIT_DOM_IS_HTML_IMAGE_ELEMENT (element))
+               webkit_dom_html_image_element_set_src (
+                       WEBKIT_DOM_HTML_IMAGE_ELEMENT (element),
+                       base64_content);
+       else
+               webkit_dom_element_set_attribute (
+                       element, "background", base64_content, NULL);
+
+       webkit_dom_element_set_attribute (element, "data-uri", uri, NULL);
+       webkit_dom_element_set_attribute (element, "data-inline", "", NULL);
+       webkit_dom_element_set_attribute (
+               element, "data-name", filename ? filename : "", NULL);
+}
+
+WebKitDOMRange *
+e_editor_dom_get_current_range (EEditorPage *editor_page)
+{
+       WebKitDOMDocument *document;
+       WebKitDOMDOMWindow *dom_window = NULL;
+       WebKitDOMDOMSelection *dom_selection = NULL;
+       WebKitDOMRange *range = NULL;
+
+       g_return_val_if_fail (E_IS_EDITOR_PAGE (editor_page), NULL);
+
+       document = e_editor_page_get_document (editor_page);
+       dom_window = webkit_dom_document_get_default_view (document);
+       if (!dom_window)
+               return NULL;
+
+       dom_selection = webkit_dom_dom_window_get_selection (dom_window);
+       if (!WEBKIT_DOM_IS_DOM_SELECTION (dom_selection)) {
+               g_clear_object (&dom_window);
+               return NULL;
+       }
+
+       if (webkit_dom_dom_selection_get_range_count (dom_selection) < 1)
+               goto exit;
+
+       range = webkit_dom_dom_selection_get_range_at (dom_selection, 0, NULL);
+ exit:
+       g_clear_object (&dom_selection);
+       g_clear_object (&dom_window);
+
+       return range;
+}
+
+void
+e_editor_dom_move_caret_into_element (EEditorPage *editor_page,
+                                     WebKitDOMElement *element,
+                                     gboolean to_start)
+{
+       WebKitDOMDocument *document;
+       WebKitDOMDOMWindow *dom_window = NULL;
+       WebKitDOMDOMSelection *dom_selection = NULL;
+       WebKitDOMRange *range = NULL;
+
+       g_return_if_fail (E_IS_EDITOR_PAGE (editor_page));
+
+       if (!element)
+               return;
+
+       document = e_editor_page_get_document (editor_page);
+       dom_window = webkit_dom_document_get_default_view (document);
+       dom_selection = webkit_dom_dom_window_get_selection (dom_window);
+       range = webkit_dom_document_create_range (document);
+
+       webkit_dom_range_select_node_contents (
+               range, WEBKIT_DOM_NODE (element), NULL);
+       webkit_dom_range_collapse (range, to_start, NULL);
+       webkit_dom_dom_selection_remove_all_ranges (dom_selection);
+       webkit_dom_dom_selection_add_range (dom_selection, range);
+
+       g_clear_object (&range);
+       g_clear_object (&dom_selection);
+       g_clear_object (&dom_window);
+}
+
+void
+e_editor_dom_insert_base64_image (EEditorPage *editor_page,
+                                 const gchar *base64_content,
+                                 const gchar *filename,
+                                 const gchar *uri)
+{
+       WebKitDOMDocument *document;
+       WebKitDOMElement *element, *selection_start_marker, *resizable_wrapper;
+       WebKitDOMText *text;
+       EEditorHistoryEvent *ev = NULL;
+       EEditorUndoRedoManager *manager;
+
+       g_return_if_fail (E_IS_EDITOR_PAGE (editor_page));
+
+       document = e_editor_page_get_document (editor_page);
+       manager = e_editor_page_get_undo_redo_manager (editor_page);
+
+       if (!e_editor_dom_selection_is_collapsed (editor_page)) {
+               EEditorHistoryEvent *ev;
+               WebKitDOMDocumentFragment *fragment;
+               WebKitDOMRange *range = NULL;
+
+               ev = g_new0 (EEditorHistoryEvent, 1);
+               ev->type = HISTORY_DELETE;
+
+               range = e_editor_dom_get_current_range (editor_page);
+               fragment = webkit_dom_range_clone_contents (range, NULL);
+               g_clear_object (&range);
+               ev->data.fragment = fragment;
+
+               e_editor_dom_selection_get_coordinates (editor_page,
+                       &ev->before.start.x,
+                       &ev->before.start.y,
+                       &ev->before.end.x,
+                       &ev->before.end.y);
+
+               ev->after.start.x = ev->before.start.x;
+               ev->after.start.y = ev->before.start.y;
+               ev->after.end.x = ev->before.start.x;
+               ev->after.end.y = ev->before.start.y;
+
+               e_editor_undo_redo_manager_insert_history_event (manager, ev);
+
+               ev = g_new0 (EEditorHistoryEvent, 1);
+               ev->type = HISTORY_AND;
+
+               e_editor_undo_redo_manager_insert_history_event (manager, ev);
+               e_editor_dom_exec_command (editor_page, E_CONTENT_EDITOR_COMMAND_DELETE, NULL);
+       }
+
+       e_editor_dom_selection_save (editor_page);
+       selection_start_marker = webkit_dom_document_query_selector (
+               document, "span#-x-evo-selection-start-marker", NULL);
+
+       if (!e_editor_undo_redo_manager_is_operation_in_progress (manager)) {
+               ev = g_new0 (EEditorHistoryEvent, 1);
+               ev->type = HISTORY_IMAGE;
+
+               e_editor_dom_selection_get_coordinates (editor_page,
+                       &ev->before.start.x,
+                       &ev->before.start.y,
+                       &ev->before.end.x,
+                       &ev->before.end.y);
+       }
+
+       resizable_wrapper =
+               webkit_dom_document_create_element (document, "span", NULL);
+       webkit_dom_element_set_attribute (
+               resizable_wrapper, "class", "-x-evo-resizable-wrapper", NULL);
+
+       element = webkit_dom_document_create_element (document, "img", NULL);
+       webkit_dom_html_image_element_set_src (
+               WEBKIT_DOM_HTML_IMAGE_ELEMENT (element),
+               base64_content);
+       webkit_dom_element_set_attribute (
+               WEBKIT_DOM_ELEMENT (element), "data-uri", uri, NULL);
+       webkit_dom_element_set_attribute (
+               WEBKIT_DOM_ELEMENT (element), "data-inline", "", NULL);
+       webkit_dom_element_set_attribute (
+               WEBKIT_DOM_ELEMENT (element), "data-name",
+               filename ? filename : "", NULL);
+       webkit_dom_node_append_child (
+               WEBKIT_DOM_NODE (resizable_wrapper),
+               WEBKIT_DOM_NODE (element),
+               NULL);
+
+       webkit_dom_node_insert_before (
+               webkit_dom_node_get_parent_node (
+                       WEBKIT_DOM_NODE (selection_start_marker)),
+               WEBKIT_DOM_NODE (resizable_wrapper),
+               WEBKIT_DOM_NODE (selection_start_marker),
+               NULL);
+
+       /* We have to again use UNICODE_ZERO_WIDTH_SPACE character to restore
+        * caret on right position */
+       text = webkit_dom_document_create_text_node (
+               document, UNICODE_ZERO_WIDTH_SPACE);
+
+       webkit_dom_node_insert_before (
+               webkit_dom_node_get_parent_node (
+                       WEBKIT_DOM_NODE (selection_start_marker)),
+               WEBKIT_DOM_NODE (text),
+               WEBKIT_DOM_NODE (selection_start_marker),
+               NULL);
+
+       if (ev) {
+               WebKitDOMDocumentFragment *fragment;
+               WebKitDOMNode *node;
+
+               fragment = webkit_dom_document_create_document_fragment (document);
+               node = webkit_dom_node_append_child (
+                       WEBKIT_DOM_NODE (fragment),
+                       webkit_dom_node_clone_node_with_error (WEBKIT_DOM_NODE (resizable_wrapper), TRUE, 
NULL),
+                       NULL);
+
+               webkit_dom_html_element_insert_adjacent_html (
+                       WEBKIT_DOM_HTML_ELEMENT (node), "afterend", "&#8203;", NULL);
+               ev->data.fragment = fragment;
+
+               e_editor_dom_selection_get_coordinates (editor_page,
+                       &ev->after.start.x,
+                       &ev->after.start.y,
+                       &ev->after.end.x,
+                       &ev->after.end.y);
+
+               e_editor_undo_redo_manager_insert_history_event (manager, ev);
+       }
+
+       e_editor_dom_selection_restore (editor_page);
+       e_editor_dom_force_spell_check_for_current_paragraph (editor_page);
+       e_editor_dom_scroll_to_caret (editor_page);
+}
+
+/* ************************ image_load_and_insert_async() ************************ */
+
+typedef struct _ImageLoadContext {
+       EEditorPage *editor_page;
+       GInputStream *input_stream;
+       GOutputStream *output_stream;
+       GFile *file;
+       GFileInfo *file_info;
+       goffset total_num_bytes;
+       gssize bytes_read;
+       const gchar *content_type;
+       const gchar *filename;
+       const gchar *selector;
+       gchar buffer[4096];
+} ImageLoadContext;
+
+/* Forward Declaration */
+static void
+image_load_stream_read_cb (GInputStream *input_stream,
+                           GAsyncResult *result,
+                           ImageLoadContext *load_context);
+
+static ImageLoadContext *
+image_load_context_new (EEditorPage *editor_page)
+{
+       ImageLoadContext *load_context;
+
+       load_context = g_slice_new0 (ImageLoadContext);
+       load_context->editor_page = editor_page;
+
+       return load_context;
+}
+
+static void
+image_load_context_free (ImageLoadContext *load_context)
+{
+       if (load_context->input_stream != NULL)
+               g_object_unref (load_context->input_stream);
+
+       if (load_context->output_stream != NULL)
+               g_object_unref (load_context->output_stream);
+
+       if (load_context->file_info != NULL)
+               g_object_unref (load_context->file_info);
+
+       if (load_context->file != NULL)
+               g_object_unref (load_context->file);
+
+       g_slice_free (ImageLoadContext, load_context);
+}
+
+static void
+image_load_finish (ImageLoadContext *load_context)
+{
+       EEditorPage *editor_page;
+       GMemoryOutputStream *output_stream;
+       const gchar *selector;
+       gchar *base64_encoded, *mime_type, *output, *uri;
+       gsize size;
+       gpointer data;
+
+       output_stream = G_MEMORY_OUTPUT_STREAM (load_context->output_stream);
+       editor_page = load_context->editor_page;
+       mime_type = g_content_type_get_mime_type (load_context->content_type);
+
+       data = g_memory_output_stream_get_data (output_stream);
+       size = g_memory_output_stream_get_data_size (output_stream);
+       uri = g_file_get_uri (load_context->file);
+
+       base64_encoded = g_base64_encode ((const guchar *) data, size);
+       output = g_strconcat ("data:", mime_type, ";base64,", base64_encoded, NULL);
+       selector = load_context->selector;
+       if (selector && *selector)
+               e_editor_dom_replace_base64_image_src (editor_page, selector, output, load_context->filename, 
uri);
+       else
+               e_editor_dom_insert_base64_image (editor_page, output, load_context->filename, uri);
+
+       g_free (base64_encoded);
+       g_free (output);
+       g_free (mime_type);
+       g_free (uri);
+
+       image_load_context_free (load_context);
+}
+
+static void
+image_load_write_cb (GOutputStream *output_stream,
+                     GAsyncResult *result,
+                     ImageLoadContext *load_context)
+{
+       GInputStream *input_stream;
+       gssize bytes_written;
+       GError *error = NULL;
+
+       bytes_written = g_output_stream_write_finish (
+               output_stream, result, &error);
+
+       if (error) {
+               image_load_context_free (load_context);
+               return;
+       }
+
+       input_stream = load_context->input_stream;
+
+       if (bytes_written < load_context->bytes_read) {
+               g_memmove (
+                       load_context->buffer,
+                       load_context->buffer + bytes_written,
+                       load_context->bytes_read - bytes_written);
+               load_context->bytes_read -= bytes_written;
+
+               g_output_stream_write_async (
+                       output_stream,
+                       load_context->buffer,
+                       load_context->bytes_read,
+                       G_PRIORITY_DEFAULT, NULL,
+                       (GAsyncReadyCallback) image_load_write_cb,
+                       load_context);
+       } else
+               g_input_stream_read_async (
+                       input_stream,
+                       load_context->buffer,
+                       sizeof (load_context->buffer),
+                       G_PRIORITY_DEFAULT, NULL,
+                       (GAsyncReadyCallback) image_load_stream_read_cb,
+                       load_context);
+}
+
+static void
+image_load_stream_read_cb (GInputStream *input_stream,
+                           GAsyncResult *result,
+                           ImageLoadContext *load_context)
+{
+       GOutputStream *output_stream;
+       gssize bytes_read;
+       GError *error = NULL;
+
+       bytes_read = g_input_stream_read_finish (
+               input_stream, result, &error);
+
+       if (error) {
+               image_load_context_free (load_context);
+               return;
+       }
+
+       if (bytes_read == 0) {
+               image_load_finish (load_context);
+               return;
+       }
+
+       output_stream = load_context->output_stream;
+       load_context->bytes_read = bytes_read;
+
+       g_output_stream_write_async (
+               output_stream,
+               load_context->buffer,
+               load_context->bytes_read,
+               G_PRIORITY_DEFAULT, NULL,
+               (GAsyncReadyCallback) image_load_write_cb,
+               load_context);
+}
+
+static void
+image_load_file_read_cb (GFile *file,
+                         GAsyncResult *result,
+                         ImageLoadContext *load_context)
+{
+       GFileInputStream *input_stream;
+       GOutputStream *output_stream;
+       GError *error = NULL;
+
+       /* Input stream might be NULL, so don't use cast macro. */
+       input_stream = g_file_read_finish (file, result, &error);
+       load_context->input_stream = (GInputStream *) input_stream;
+
+       if (error) {
+               image_load_context_free (load_context);
+               return;
+       }
+
+       /* Load the contents into a GMemoryOutputStream. */
+       output_stream = g_memory_output_stream_new (
+               NULL, 0, g_realloc, g_free);
+
+       load_context->output_stream = output_stream;
+
+       g_input_stream_read_async (
+               load_context->input_stream,
+               load_context->buffer,
+               sizeof (load_context->buffer),
+               G_PRIORITY_DEFAULT, NULL,
+               (GAsyncReadyCallback) image_load_stream_read_cb,
+               load_context);
+}
+
+static void
+image_load_query_info_cb (GFile *file,
+                          GAsyncResult *result,
+                          ImageLoadContext *load_context)
+{
+       GFileInfo *file_info;
+       GError *error = NULL;
+
+       file_info = g_file_query_info_finish (file, result, &error);
+       if (error) {
+               image_load_context_free (load_context);
+               return;
+       }
+
+       load_context->content_type = g_file_info_get_content_type (file_info);
+       load_context->total_num_bytes = g_file_info_get_size (file_info);
+       load_context->filename = g_file_info_get_name (file_info);
+
+       g_file_read_async (
+               file, G_PRIORITY_DEFAULT,
+               NULL, (GAsyncReadyCallback)
+               image_load_file_read_cb, load_context);
+}
+
+static void
+image_load_and_insert_async (EEditorPage *editor_page,
+                             const gchar *selector,
+                             const gchar *uri)
+{
+       ImageLoadContext *load_context;
+       GFile *file;
+
+       g_return_if_fail (E_IS_EDITOR_PAGE (editor_page));
+       g_return_if_fail (uri && *uri);
+
+       file = g_file_new_for_uri (uri);
+       g_return_if_fail (file != NULL);
+
+       load_context = image_load_context_new (editor_page);
+       load_context->file = file;
+       if (selector && *selector)
+               load_context->selector = g_strdup (selector);
+
+       g_file_query_info_async (
+               file, "standard::*",
+               G_FILE_QUERY_INFO_NONE,G_PRIORITY_DEFAULT,
+               NULL, (GAsyncReadyCallback)
+               image_load_query_info_cb, load_context);
+}
+
+void
+e_editor_dom_insert_image (EEditorPage *editor_page,
+                          const gchar *uri)
+{
+       g_return_if_fail (E_IS_EDITOR_PAGE (editor_page));
+
+       if (!e_editor_page_get_html_mode (editor_page))
+               return;
+
+       if (strstr (uri, ";base64,")) {
+               if (g_str_has_prefix (uri, "data:"))
+                       e_editor_dom_insert_base64_image (editor_page, uri, "", "");
+               if (strstr (uri, ";data")) {
+                       const gchar *base64_data = strstr (uri, ";") + 1;
+                       gchar *filename;
+                       glong filename_length;
+
+                       filename_length =
+                               g_utf8_strlen (uri, -1) -
+                               g_utf8_strlen (base64_data, -1) - 1;
+                       filename = g_strndup (uri, filename_length);
+
+                       e_editor_dom_insert_base64_image (editor_page, base64_data, filename, "");
+                       g_free (filename);
+               }
+       } else
+               image_load_and_insert_async (editor_page, NULL, uri);
+}
+
+void
+e_editor_dom_replace_image_src (EEditorPage *editor_page,
+                               const gchar *selector,
+                               const gchar *uri)
+{
+       g_return_if_fail (E_IS_EDITOR_PAGE (editor_page));
+
+       if (strstr (uri, ";base64,")) {
+               if (g_str_has_prefix (uri, "data:"))
+                       e_editor_dom_replace_base64_image_src (
+                               editor_page, selector, uri, "", "");
+               if (strstr (uri, ";data")) {
+                       const gchar *base64_data = strstr (uri, ";") + 1;
+                       gchar *filename;
+                       glong filename_length;
+
+                       filename_length =
+                               g_utf8_strlen (uri, -1) -
+                               g_utf8_strlen (base64_data, -1) - 1;
+                       filename = g_strndup (uri, filename_length);
+
+                       e_editor_dom_replace_base64_image_src (
+                               editor_page, selector, base64_data, filename, "");
+                       g_free (filename);
+               }
+       } else
+               image_load_and_insert_async (editor_page, selector, uri);
+}
+
+/*
+ * e_html_editor_selection_unlink:
+ * @selection: an #EEditorSelection
+ *
+ * Removes any links (&lt;A&gt; elements) from current selection or at current
+ * cursor position.
+ */
+void
+e_editor_dom_selection_unlink (EEditorPage *editor_page)
+{
+       WebKitDOMDocument *document;
+       WebKitDOMDOMWindow *dom_window = NULL;
+       WebKitDOMDOMSelection *dom_selection = NULL;
+       WebKitDOMRange *range = NULL;
+       WebKitDOMElement *link;
+       EEditorUndoRedoManager *manager;
+       gchar *text;
+
+       g_return_if_fail (E_IS_EDITOR_PAGE (editor_page));
+
+       document = e_editor_page_get_document (editor_page);
+       dom_window = webkit_dom_document_get_default_view (document);
+       dom_selection = webkit_dom_dom_window_get_selection (dom_window);
+
+       range = webkit_dom_dom_selection_get_range_at (dom_selection, 0, NULL);
+       link = dom_node_find_parent_element (
+               webkit_dom_range_get_start_container (range, NULL), "A");
+
+       g_clear_object (&dom_selection);
+       g_clear_object (&dom_window);
+
+       if (!link) {
+               WebKitDOMNode *node;
+
+               /* get element that was clicked on */
+               node = webkit_dom_range_get_common_ancestor_container (range, NULL);
+               if (node && !WEBKIT_DOM_IS_HTML_ANCHOR_ELEMENT (node)) {
+                       link = dom_node_find_parent_element (node, "A");
+                       if (link && !WEBKIT_DOM_IS_HTML_ANCHOR_ELEMENT (link)) {
+                               g_clear_object (&range);
+                               return;
+                       } else
+                               link = WEBKIT_DOM_ELEMENT (node);
+               }
+       }
+
+       g_clear_object (&range);
+
+       if (!link)
+               return;
+
+       manager = e_editor_page_get_undo_redo_manager (editor_page);
+       if (!e_editor_undo_redo_manager_is_operation_in_progress (manager)) {
+               EEditorHistoryEvent *ev;
+               WebKitDOMDocumentFragment *fragment;
+
+               ev = g_new0 (EEditorHistoryEvent, 1);
+               ev->type = HISTORY_REMOVE_LINK;
+
+               e_editor_dom_selection_get_coordinates (editor_page,
+                       &ev->before.start.x,
+                       &ev->before.start.y,
+                       &ev->before.end.x,
+                       &ev->before.end.y);
+
+               fragment = webkit_dom_document_create_document_fragment (document);
+               webkit_dom_node_append_child (
+                       WEBKIT_DOM_NODE (fragment),
+                       webkit_dom_node_clone_node_with_error (WEBKIT_DOM_NODE (link), TRUE, NULL),
+                       NULL);
+               ev->data.fragment = fragment;
+
+               e_editor_undo_redo_manager_insert_history_event (manager, ev);
+       }
+
+       text = webkit_dom_html_element_get_inner_text (
+               WEBKIT_DOM_HTML_ELEMENT (link));
+       webkit_dom_element_set_outer_html (link, text, NULL);
+       g_free (text);
+}
+
+/*
+ * e_html_editor_selection_create_link:
+ * @document: a @WebKitDOMDocument
+ * @uri: destination of the new link
+ *
+ * Converts current selection into a link pointing to @url.
+ */
+void
+e_editor_dom_create_link (EEditorPage *editor_page,
+                         const gchar *uri)
+{
+       g_return_if_fail (E_IS_EDITOR_PAGE (editor_page));
+       g_return_if_fail (uri != NULL && *uri != '\0');
+
+       e_editor_dom_exec_command (editor_page, E_CONTENT_EDITOR_COMMAND_CREATE_LINK, uri);
+}
+
+static gint
+get_list_level (WebKitDOMNode *node)
+{
+       gint level = 0;
+
+       while (node && !WEBKIT_DOM_IS_HTML_BODY_ELEMENT (node)) {
+               if (node_is_list (node))
+                       level++;
+               node = webkit_dom_node_get_parent_node (node);
+       }
+
+       return level;
+}
+
+static void
+set_ordered_list_type_to_element (WebKitDOMElement *list,
+                                  EContentEditorBlockFormat format)
+{
+       if (format == E_CONTENT_EDITOR_BLOCK_FORMAT_ORDERED_LIST)
+               webkit_dom_element_remove_attribute (list, "type");
+       else if (format == E_CONTENT_EDITOR_BLOCK_FORMAT_ORDERED_LIST_ALPHA)
+               webkit_dom_element_set_attribute (list, "type", "A", NULL);
+       else if (format == E_CONTENT_EDITOR_BLOCK_FORMAT_ORDERED_LIST_ROMAN)
+               webkit_dom_element_set_attribute (list, "type", "I", NULL);
+}
+
+static const gchar *
+get_css_alignment_value_class (EContentEditorAlignment alignment)
+{
+       if (alignment == E_CONTENT_EDITOR_ALIGNMENT_LEFT)
+               return ""; /* Left is by default on ltr */
+
+       if (alignment == E_CONTENT_EDITOR_ALIGNMENT_CENTER)
+               return "-x-evo-align-center";
+
+       if (alignment == E_CONTENT_EDITOR_ALIGNMENT_RIGHT)
+               return "-x-evo-align-right";
+
+       return "";
+}
+
+/*
+ * e_html_editor_selection_get_alignment:
+ * @selection: #an EEditorSelection
+ *
+ * Returns alignment of current paragraph
+ *
+ * Returns: #EContentEditorAlignment
+ */
+static EContentEditorAlignment
+dom_get_alignment (EEditorPage *editor_page)
+{
+       WebKitDOMDocument *document;
+       WebKitDOMCSSStyleDeclaration *style = NULL;
+       WebKitDOMDOMWindow *dom_window = NULL;
+       WebKitDOMElement *element;
+       WebKitDOMNode *node;
+       WebKitDOMRange *range = NULL;
+       EContentEditorAlignment alignment;
+       gchar *value;
+
+       g_return_val_if_fail (E_IS_EDITOR_PAGE (editor_page), E_CONTENT_EDITOR_ALIGNMENT_LEFT);
+
+       document = e_editor_page_get_document (editor_page);
+       range = e_editor_dom_get_current_range (editor_page);
+       if (!range)
+               return E_CONTENT_EDITOR_ALIGNMENT_LEFT;
+
+       node = webkit_dom_range_get_start_container (range, NULL);
+       g_clear_object (&range);
+       if (!node)
+               return E_CONTENT_EDITOR_ALIGNMENT_LEFT;
+
+       if (WEBKIT_DOM_IS_ELEMENT (node))
+               element = WEBKIT_DOM_ELEMENT (node);
+       else
+               element = WEBKIT_DOM_ELEMENT (e_editor_dom_get_parent_block_node_from_child (node));
+
+       if (WEBKIT_DOM_IS_HTML_LI_ELEMENT (element)) {
+               if (element_has_class (element, "-x-evo-align-right"))
+                       alignment = E_CONTENT_EDITOR_ALIGNMENT_RIGHT;
+               else if (element_has_class (element, "-x-evo-align-center"))
+                       alignment = E_CONTENT_EDITOR_ALIGNMENT_CENTER;
+               else
+                       alignment = E_CONTENT_EDITOR_ALIGNMENT_LEFT;
+
+               return alignment;
+       }
+
+       dom_window = webkit_dom_document_get_default_view (document);
+       style = webkit_dom_dom_window_get_computed_style (dom_window, element, NULL);
+       value = webkit_dom_css_style_declaration_get_property_value (style, "text-align");
+
+       if (!value || !*value ||
+           (g_ascii_strncasecmp (value, "left", 4) == 0)) {
+               alignment = E_CONTENT_EDITOR_ALIGNMENT_LEFT;
+       } else if (g_ascii_strncasecmp (value, "center", 6) == 0) {
+               alignment = E_CONTENT_EDITOR_ALIGNMENT_CENTER;
+       } else if (g_ascii_strncasecmp (value, "right", 5) == 0) {
+               alignment = E_CONTENT_EDITOR_ALIGNMENT_RIGHT;
+       } else {
+               alignment = E_CONTENT_EDITOR_ALIGNMENT_LEFT;
+       }
+
+       g_clear_object (&dom_window);
+       g_clear_object (&style);
+       g_free (value);
+
+       return alignment;
+}
+
+static gint
+set_word_wrap_length (EEditorPage *editor_page,
+                      gint user_word_wrap_length)
+{
+       g_return_val_if_fail (E_IS_EDITOR_PAGE (editor_page), 0);
+
+       /* user_word_wrap_length < 0, set block width to word_wrap_length
+        * user_word_wrap_length ==  0, no width limit set,
+        * user_word_wrap_length > 0, set width limit to given value */
+       return (user_word_wrap_length < 0) ?
+               e_editor_page_get_word_wrap_length (editor_page) : user_word_wrap_length;
+}
+
+void
+e_editor_dom_set_paragraph_style (EEditorPage *editor_page,
+                                 WebKitDOMElement *element,
+                                 gint width,
+                                 gint offset,
+                                 const gchar *style_to_add)
+{
+       WebKitDOMNode *parent;
+       gchar *style = NULL;
+       gint word_wrap_length;
+
+       g_return_if_fail (E_IS_EDITOR_PAGE (editor_page));
+
+       word_wrap_length = set_word_wrap_length (editor_page, width);
+       webkit_dom_element_set_attribute (element, "data-evo-paragraph", "", NULL);
+
+       /* Don't set the alignment for nodes as they are handled separately. */
+       if (!node_is_list (WEBKIT_DOM_NODE (element))) {
+               EContentEditorAlignment alignment;
+
+               alignment = dom_get_alignment (editor_page);
+               element_add_class (element, get_css_alignment_value_class (alignment));
+       }
+
+       parent = webkit_dom_node_get_parent_node (WEBKIT_DOM_NODE (element));
+       /* Don't set the width limit to sub-blocks as the width limit is inhered
+        * from its parents. */
+       if (!e_editor_page_get_html_mode (editor_page) &&
+           (!parent || WEBKIT_DOM_IS_HTML_BODY_ELEMENT (parent))) {
+               style = g_strdup_printf (
+                       "width: %dch;%s%s",
+                       (word_wrap_length + offset),
+                       style_to_add && *style_to_add ? " " : "",
+                       style_to_add && *style_to_add ? style_to_add : "");
+       } else {
+               if (style_to_add && *style_to_add)
+                       style = g_strdup_printf ("%s", style_to_add);
+       }
+       if (style) {
+               webkit_dom_element_set_attribute (element, "style", style, NULL);
+               g_free (style);
+       }
+}
+
+static WebKitDOMElement *
+create_list_element (EEditorPage *editor_page,
+                     EContentEditorBlockFormat format,
+                    gint level,
+                     gboolean html_mode)
+{
+       WebKitDOMDocument *document;
+       WebKitDOMElement *list;
+       gboolean inserting_unordered_list;
+
+       g_return_val_if_fail (E_IS_EDITOR_PAGE (editor_page), NULL);
+
+       document = e_editor_page_get_document (editor_page);
+       inserting_unordered_list = format == E_CONTENT_EDITOR_BLOCK_FORMAT_UNORDERED_LIST;
+
+       list = webkit_dom_document_create_element (
+               document, inserting_unordered_list  ? "UL" : "OL", NULL);
+
+       if (!inserting_unordered_list)
+               set_ordered_list_type_to_element (list, format);
+
+       if (level >= 0 && !html_mode) {
+               gint offset;
+
+               offset = (level + 1) * SPACES_PER_LIST_LEVEL;
+
+               offset += !inserting_unordered_list ?
+                       SPACES_ORDERED_LIST_FIRST_LEVEL - SPACES_PER_LIST_LEVEL: 0;
+
+               e_editor_dom_set_paragraph_style (editor_page, list, -1, -offset, NULL);
+
+               if (inserting_unordered_list)
+                       webkit_dom_element_set_attribute (list, "data-evo-plain-text", "", NULL);
+       }
+
+       return list;
+}
+
+static gboolean
+indent_list (EEditorPage *editor_page)
+{
+       WebKitDOMDocument *document;
+       WebKitDOMElement *selection_start_marker, *selection_end_marker;
+       WebKitDOMNode *item, *next_item;
+       gboolean after_selection_end = FALSE;
+
+       g_return_val_if_fail (E_IS_EDITOR_PAGE (editor_page), FALSE);
+
+       document = e_editor_page_get_document (editor_page);
+       selection_start_marker = webkit_dom_document_query_selector (
+               document, "span#-x-evo-selection-start-marker", NULL);
+
+       selection_end_marker = webkit_dom_document_query_selector (
+               document, "span#-x-evo-selection-end-marker", NULL);
+
+       item = e_editor_dom_get_parent_block_node_from_child (
+               WEBKIT_DOM_NODE (selection_start_marker));
+
+       if (WEBKIT_DOM_IS_HTML_LI_ELEMENT (item)) {
+               gboolean html_mode = e_editor_page_get_html_mode (editor_page);
+               WebKitDOMElement *list;
+               WebKitDOMNode *source_list = webkit_dom_node_get_parent_node (item);
+               EContentEditorBlockFormat format;
+
+               format = dom_get_list_format_from_node (source_list);
+
+               list = create_list_element (
+                       editor_page, format, get_list_level (item), html_mode);
+
+               element_add_class (list, "-x-evo-indented");
+
+               webkit_dom_node_insert_before (
+                       source_list, WEBKIT_DOM_NODE (list), item, NULL);
+
+               while (item && !after_selection_end) {
+                       after_selection_end = webkit_dom_node_contains (
+                               item, WEBKIT_DOM_NODE (selection_end_marker));
+
+                       next_item = webkit_dom_node_get_next_sibling (item);
+
+                       webkit_dom_node_append_child (
+                               WEBKIT_DOM_NODE (list), item, NULL);
+
+                       item = next_item;
+               }
+
+               merge_lists_if_possible (WEBKIT_DOM_NODE (list));
+       }
+
+       return after_selection_end;
+}
+
+static void
+dom_set_indented_style (EEditorPage *editor_page,
+                        WebKitDOMElement *element,
+                        gint width)
+{
+       gchar *style;
+       gint word_wrap_length;
+
+       g_return_if_fail (E_IS_EDITOR_PAGE (editor_page));
+
+       word_wrap_length = set_word_wrap_length (editor_page, width);
+       webkit_dom_element_set_class_name (element, "-x-evo-indented");
+
+       if (e_editor_page_get_html_mode (editor_page) || word_wrap_length == 0) {
+               style = g_strdup_printf ("margin-left: %dch;", SPACES_PER_INDENTATION);
+
+               if (word_wrap_length != 0) {
+                       gchar *plain_text_style;
+
+                       plain_text_style = g_strdup_printf (
+                               "margin-left: %dch; word-wrap: normal; width: %dch;",
+                               SPACES_PER_INDENTATION, word_wrap_length);
+
+                       webkit_dom_element_set_attribute (
+                               element, "data-plain-text-style", plain_text_style, NULL);
+                       g_free (plain_text_style);
+               }
+       } else {
+               style = g_strdup_printf (
+                       "margin-left: %dch; word-wrap: normal; width: %dch;",
+                       SPACES_PER_INDENTATION, word_wrap_length);
+       }
+
+       webkit_dom_element_set_attribute (element, "style", style, NULL);
+       g_free (style);
+}
+
+static WebKitDOMElement *
+dom_get_indented_element (EEditorPage *editor_page,
+                          gint width)
+{
+       WebKitDOMDocument *document;
+       WebKitDOMElement *element;
+
+       g_return_val_if_fail (E_IS_EDITOR_PAGE (editor_page), NULL);
+
+       document = e_editor_page_get_document (editor_page);
+       element = webkit_dom_document_create_element (document, "DIV", NULL);
+       dom_set_indented_style (editor_page, element, width);
+
+       return element;
+}
+
+static WebKitDOMNode *
+indent_block (EEditorPage *editor_page,
+              WebKitDOMNode *block,
+              gint width)
+{
+       WebKitDOMElement *element;
+       WebKitDOMNode *sibling, *tmp;
+
+       g_return_val_if_fail (E_IS_EDITOR_PAGE (editor_page), NULL);
+
+       sibling = webkit_dom_node_get_previous_sibling (block);
+       if (WEBKIT_DOM_IS_ELEMENT (sibling) &&
+           element_has_class (WEBKIT_DOM_ELEMENT (sibling), "-x-evo-indented")) {
+               element = WEBKIT_DOM_ELEMENT (sibling);
+       } else {
+               element = dom_get_indented_element (editor_page, width);
+
+               webkit_dom_node_insert_before (
+                       webkit_dom_node_get_parent_node (block),
+                       WEBKIT_DOM_NODE (element),
+                       block,
+                       NULL);
+       }
+
+       /* Remove style and let the paragraph inherit it from parent */
+       if (webkit_dom_element_has_attribute (WEBKIT_DOM_ELEMENT (block), "data-evo-paragraph"))
+               webkit_dom_element_remove_attribute (
+                       WEBKIT_DOM_ELEMENT (block), "style");
+
+       tmp = webkit_dom_node_append_child (
+               WEBKIT_DOM_NODE (element),
+               block,
+               NULL);
+
+       sibling = webkit_dom_node_get_next_sibling (WEBKIT_DOM_NODE (element));
+
+       while (WEBKIT_DOM_IS_ELEMENT (sibling) &&
+              element_has_class (WEBKIT_DOM_ELEMENT (sibling), "-x-evo-indented")) {
+               WebKitDOMNode *next_sibling;
+               WebKitDOMNode *child;
+
+               next_sibling = webkit_dom_node_get_next_sibling (WEBKIT_DOM_NODE (sibling));
+
+               while ((child = webkit_dom_node_get_first_child (WEBKIT_DOM_NODE (sibling)))) {
+                       webkit_dom_node_append_child (
+                               WEBKIT_DOM_NODE (element),
+                               child,
+                               NULL);
+               }
+               remove_node (sibling);
+               sibling = next_sibling;
+       }
+
+       return tmp;
+}
+
+static WebKitDOMNode *
+get_list_item_node_from_child (WebKitDOMNode *child)
+{
+       WebKitDOMNode *parent = webkit_dom_node_get_parent_node (child);
+
+       while (parent && !WEBKIT_DOM_IS_HTML_LI_ELEMENT (parent))
+               parent = webkit_dom_node_get_parent_node (parent);
+
+       return parent;
+}
+
+static WebKitDOMNode *
+get_list_node_from_child (WebKitDOMNode *child)
+{
+       WebKitDOMNode *parent = get_list_item_node_from_child (child);
+
+       return webkit_dom_node_get_parent_node (parent);
+}
+
+static gboolean
+do_format_change_list_to_block (EEditorPage *editor_page,
+                                EContentEditorBlockFormat format,
+                                WebKitDOMNode *item,
+                                const gchar *value)
+{
+       WebKitDOMDocument *document;
+       WebKitDOMElement *element, *selection_end;
+       WebKitDOMNode *node, *source_list;
+       gboolean after_end = FALSE;
+       gint level;
+
+       g_return_val_if_fail (E_IS_EDITOR_PAGE (editor_page), FALSE);
+
+       document = e_editor_page_get_document (editor_page);
+       selection_end = webkit_dom_document_query_selector (
+               document, "span#-x-evo-selection-end-marker", NULL);
+
+       source_list = webkit_dom_node_get_parent_node (item);
+       while (source_list) {
+               WebKitDOMNode *parent;
+
+               parent = webkit_dom_node_get_parent_node (source_list);
+               if (node_is_list (parent))
+                       source_list = parent;
+               else
+                       break;
+       }
+
+       if (webkit_dom_node_contains (source_list, WEBKIT_DOM_NODE (selection_end)))
+               source_list = split_list_into_two (item, -1);
+       else {
+               source_list = webkit_dom_node_get_next_sibling (source_list);
+       }
+
+       /* Process all nodes that are in selection one by one */
+       while (item && WEBKIT_DOM_IS_HTML_LI_ELEMENT (item)) {
+               WebKitDOMNode *next_item;
+
+               next_item = webkit_dom_node_get_next_sibling (WEBKIT_DOM_NODE (item));
+               if (!next_item) {
+                       WebKitDOMNode *parent;
+                       WebKitDOMNode *tmp = item;
+
+                       while (tmp) {
+                               parent = webkit_dom_node_get_parent_node (WEBKIT_DOM_NODE (tmp));
+                               if (!node_is_list (parent))
+                                       break;
+
+                               next_item = webkit_dom_node_get_next_sibling (parent);
+                               if (node_is_list (next_item)) {
+                                       next_item = webkit_dom_node_get_first_child (next_item);
+                                       break;
+                               } else if (next_item && !WEBKIT_DOM_IS_HTML_LI_ELEMENT (next_item)) {
+                                       next_item = webkit_dom_node_get_next_sibling (next_item);
+                                       break;
+                               } else if (WEBKIT_DOM_IS_HTML_LI_ELEMENT (next_item)) {
+                                       break;
+                               }
+                               tmp = parent;
+                       }
+               } else if (node_is_list (next_item)) {
+                       next_item = webkit_dom_node_get_first_child (next_item);
+               } else if (!WEBKIT_DOM_IS_HTML_LI_ELEMENT (next_item)) {
+                       next_item = webkit_dom_node_get_next_sibling (item);
+                       continue;
+               }
+
+               if (!after_end) {
+                       after_end = webkit_dom_node_contains (item, WEBKIT_DOM_NODE (selection_end));
+
+                       level = get_indentation_level (WEBKIT_DOM_ELEMENT (item));
+
+                       if (format == E_CONTENT_EDITOR_BLOCK_FORMAT_PARAGRAPH) {
+                               element = e_editor_dom_get_paragraph_element (editor_page, -1, 0);
+                       } else
+                               element = webkit_dom_document_create_element (
+                                       document, value, NULL);
+
+                       while ((node = webkit_dom_node_get_first_child (item)))
+                               webkit_dom_node_append_child (
+                                       WEBKIT_DOM_NODE (element), node, NULL);
+
+                       webkit_dom_node_insert_before (
+                               webkit_dom_node_get_parent_node (source_list),
+                               WEBKIT_DOM_NODE (element),
+                               source_list,
+                               NULL);
+
+                       if (level > 0) {
+                               gint final_width = 0;
+
+                               node = WEBKIT_DOM_NODE (element);
+
+                               if (webkit_dom_element_has_attribute (element, "data-evo-paragraph"))
+                                       final_width = e_editor_page_get_word_wrap_length (editor_page) -
+                                               SPACES_PER_INDENTATION * level;
+
+                               while (level--)
+                                       node = indent_block (editor_page, node, final_width);
+                       }
+
+                       e_editor_dom_remove_node_and_parents_if_empty (item);
+               } else
+                       break;
+
+               item = next_item;
+       }
+
+       remove_node_if_empty (source_list);
+
+       return after_end;
+}
+
+static void
+format_change_list_to_block (EEditorPage *editor_page,
+                             EContentEditorBlockFormat format,
+                             const gchar *value)
+{
+       WebKitDOMDocument *document;
+       WebKitDOMElement *selection_start;
+       WebKitDOMNode *item;
+
+       g_return_if_fail (E_IS_EDITOR_PAGE (editor_page));
+
+       document = e_editor_page_get_document (editor_page);
+       selection_start = webkit_dom_document_query_selector (
+               document, "span#-x-evo-selection-start-marker", NULL);
+
+       item = get_list_item_node_from_child (WEBKIT_DOM_NODE (selection_start));
+
+       do_format_change_list_to_block (editor_page, format, item, value);
+}
+
+static WebKitDOMNode *
+get_parent_indented_block (WebKitDOMNode *node)
+{
+       WebKitDOMNode *parent, *block = NULL;
+
+       parent = webkit_dom_node_get_parent_node (node);
+       if (element_has_class (WEBKIT_DOM_ELEMENT (parent), "-x-evo-indented"))
+               block = parent;
+
+       while (parent && !WEBKIT_DOM_IS_HTML_BODY_ELEMENT (parent)) {
+               if (element_has_class (WEBKIT_DOM_ELEMENT (parent), "-x-evo-indented"))
+                       block = parent;
+               parent = webkit_dom_node_get_parent_node (parent);
+       }
+
+       return block;
+}
+
+static WebKitDOMElement*
+get_element_for_inspection (WebKitDOMRange *range)
+{
+       WebKitDOMNode *node;
+
+       node = webkit_dom_range_get_end_container (range, NULL);
+       /* No selection or whole body selected */
+       if (WEBKIT_DOM_IS_HTML_BODY_ELEMENT (node))
+               return NULL;
+
+       return WEBKIT_DOM_ELEMENT (get_parent_indented_block (node));
+}
+
+static EContentEditorAlignment
+dom_get_alignment_from_node (WebKitDOMNode *node)
+{
+       EContentEditorAlignment alignment;
+       gchar *value;
+       WebKitDOMCSSStyleDeclaration *style = NULL;
+
+       style = webkit_dom_element_get_style (WEBKIT_DOM_ELEMENT (node));
+       value = webkit_dom_css_style_declaration_get_property_value (style, "text-align");
+
+       if (!value || !*value ||
+           (g_ascii_strncasecmp (value, "left", 4) == 0)) {
+               alignment = E_CONTENT_EDITOR_ALIGNMENT_LEFT;
+       } else if (g_ascii_strncasecmp (value, "center", 6) == 0) {
+               alignment = E_CONTENT_EDITOR_ALIGNMENT_CENTER;
+       } else if (g_ascii_strncasecmp (value, "right", 5) == 0) {
+               alignment = E_CONTENT_EDITOR_ALIGNMENT_RIGHT;
+       } else {
+               alignment = E_CONTENT_EDITOR_ALIGNMENT_LEFT;
+       }
+
+       g_clear_object (&style);
+       g_free (value);
+
+       return alignment;
+}
+
+/*
+ * e_html_editor_selection_indent:
+ * @selection: an #EEditorSelection
+ *
+ * Indents current paragraph by one level.
+ */
+void
+e_editor_dom_selection_indent (EEditorPage *editor_page)
+{
+       WebKitDOMDocument *document;
+       WebKitDOMElement *selection_start_marker, *selection_end_marker;
+       WebKitDOMNode *block;
+       EEditorHistoryEvent *ev = NULL;
+       EEditorUndoRedoManager *manager;
+       gboolean after_selection_start = FALSE, after_selection_end = FALSE;
+
+       g_return_if_fail (E_IS_EDITOR_PAGE (editor_page));
+
+       document = e_editor_page_get_document (editor_page);
+       e_editor_dom_selection_save (editor_page);
+
+       manager = e_editor_page_get_undo_redo_manager (editor_page);
+
+       selection_start_marker = webkit_dom_document_query_selector (
+               document, "span#-x-evo-selection-start-marker", NULL);
+
+       selection_end_marker = webkit_dom_document_query_selector (
+               document, "span#-x-evo-selection-end-marker", NULL);
+
+       /* If the selection was not saved, move it into the first child of body */
+       if (!selection_start_marker || !selection_end_marker) {
+               WebKitDOMHTMLElement *body;
+               WebKitDOMNode *child;
+
+               body = webkit_dom_document_get_body (document);
+               child = webkit_dom_node_get_first_child (WEBKIT_DOM_NODE (body));
+
+               dom_add_selection_markers_into_element_start (
+                       document,
+                       WEBKIT_DOM_ELEMENT (child),
+                       &selection_start_marker,
+                       &selection_end_marker);
+       }
+
+       if (!e_editor_undo_redo_manager_is_operation_in_progress (manager)) {
+               ev = g_new0 (EEditorHistoryEvent, 1);
+               ev->type = HISTORY_INDENT;
+
+               e_editor_dom_selection_get_coordinates (editor_page,
+                       &ev->before.start.x,
+                       &ev->before.start.y,
+                       &ev->before.end.x,
+                       &ev->before.end.y);
+
+               ev->data.style.from = 1;
+               ev->data.style.to = 1;
+       }
+
+       block = get_parent_indented_block (
+               WEBKIT_DOM_NODE (selection_start_marker));
+       if (!block)
+               block = e_editor_dom_get_parent_block_node_from_child (
+                       WEBKIT_DOM_NODE (selection_start_marker));
+
+       while (block && !after_selection_end) {
+               gint ii, length, level, word_wrap_length, final_width = 0;
+               WebKitDOMNode *next_block;
+               WebKitDOMNodeList *list = NULL;
+
+               word_wrap_length = e_editor_page_get_word_wrap_length (editor_page);
+
+               next_block = webkit_dom_node_get_next_sibling (block);
+
+               list = webkit_dom_element_query_selector_all (
+                       WEBKIT_DOM_ELEMENT (block),
+                       ".-x-evo-indented > *:not(.-x-evo-indented):not(li)",
+                       NULL);
+
+               after_selection_end = webkit_dom_node_contains (
+                       block, WEBKIT_DOM_NODE (selection_end_marker));
+
+               length = webkit_dom_node_list_get_length (list);
+               if (length == 0 && node_is_list_or_item (block)) {
+                       after_selection_end = indent_list (editor_page);
+                       goto next;
+               }
+
+               if (length == 0) {
+                       if (!after_selection_start) {
+                               after_selection_start = webkit_dom_node_contains (
+                                       block, WEBKIT_DOM_NODE (selection_start_marker));
+                               if (!after_selection_start)
+                                       goto next;
+                       }
+
+                       if (webkit_dom_element_has_attribute (WEBKIT_DOM_ELEMENT (block), 
"data-evo-paragraph")) {
+                               level = get_indentation_level (WEBKIT_DOM_ELEMENT (block));
+
+                               final_width = word_wrap_length - SPACES_PER_INDENTATION * (level + 1);
+                               if (final_width < MINIMAL_PARAGRAPH_WIDTH &&
+                               !e_editor_page_get_html_mode (editor_page))
+                                       goto next;
+                       }
+
+                       indent_block (editor_page, block, final_width);
+
+                       if (after_selection_end)
+                               goto next;
+               }
+
+               for (ii = 0; ii < length; ii++) {
+                       WebKitDOMNode *block_to_process;
+
+                       block_to_process = webkit_dom_node_list_item (list, ii);
+
+                       after_selection_end = webkit_dom_node_contains (
+                               block_to_process, WEBKIT_DOM_NODE (selection_end_marker));
+
+                       if (!after_selection_start) {
+                               after_selection_start = webkit_dom_node_contains (
+                                       block_to_process,
+                                       WEBKIT_DOM_NODE (selection_start_marker));
+                               if (!after_selection_start) {
+                                       g_object_unref (block_to_process);
+                                       continue;
+                               }
+                       }
+
+                       if (webkit_dom_element_has_attribute (WEBKIT_DOM_ELEMENT (block_to_process), 
"data-evo-paragraph")) {
+                               level = get_indentation_level (
+                                       WEBKIT_DOM_ELEMENT (block_to_process));
+
+                               final_width = word_wrap_length - SPACES_PER_INDENTATION * (level + 1);
+                               if (final_width < MINIMAL_PARAGRAPH_WIDTH &&
+                                   !e_editor_page_get_html_mode (editor_page)) {
+                                       g_object_unref (block_to_process);
+                                       continue;
+                               }
+                       }
+
+                       indent_block (editor_page, block_to_process, final_width);
+
+                       g_object_unref (block_to_process);
+                       if (after_selection_end)
+                               break;
+               }
+
+ next:
+               g_clear_object (&list);
+
+               if (!after_selection_end)
+                       block = next_block;
+       }
+
+       if (ev) {
+               e_editor_dom_selection_get_coordinates (editor_page,
+                       &ev->after.start.x,
+                       &ev->after.start.y,
+                       &ev->after.end.x,
+                       &ev->after.end.y);
+               e_editor_undo_redo_manager_insert_history_event (manager, ev);
+       }
+
+       e_editor_dom_selection_restore (editor_page);
+       e_editor_dom_force_spell_check_for_current_paragraph (editor_page);
+}
+
+static void
+unindent_list (WebKitDOMDocument *document)
+{
+       gboolean after = FALSE;
+       WebKitDOMElement *new_list;
+       WebKitDOMElement *selection_start_marker, *selection_end_marker;
+       WebKitDOMNode *source_list, *source_list_clone, *current_list, *item;
+       WebKitDOMNode *prev_item;
+
+       selection_start_marker = webkit_dom_document_query_selector (
+               document, "span#-x-evo-selection-start-marker", NULL);
+       selection_end_marker = webkit_dom_document_query_selector (
+               document, "span#-x-evo-selection-end-marker", NULL);
+
+       if (!selection_start_marker || !selection_end_marker)
+               return;
+
+       /* Copy elements from previous block to list */
+       item = e_editor_dom_get_parent_block_node_from_child (
+               WEBKIT_DOM_NODE (selection_start_marker));
+       source_list = webkit_dom_node_get_parent_node (item);
+       new_list = WEBKIT_DOM_ELEMENT (
+               webkit_dom_node_clone_node_with_error (source_list, FALSE, NULL));
+       current_list = source_list;
+       source_list_clone = webkit_dom_node_clone_node_with_error (source_list, FALSE, NULL);
+
+       webkit_dom_node_insert_before (
+               webkit_dom_node_get_parent_node (source_list),
+               WEBKIT_DOM_NODE (source_list_clone),
+               webkit_dom_node_get_next_sibling (source_list),
+               NULL);
+
+       if (element_has_class (WEBKIT_DOM_ELEMENT (source_list), "-x-evo-indented"))
+               element_add_class (WEBKIT_DOM_ELEMENT (new_list), "-x-evo-indented");
+
+       prev_item = source_list;
+
+       while (item) {
+               WebKitDOMNode *next_item = webkit_dom_node_get_next_sibling (item);
+
+               if (WEBKIT_DOM_IS_HTML_LI_ELEMENT (item)) {
+                       if (after)
+                               prev_item = webkit_dom_node_append_child (
+                                       source_list_clone, WEBKIT_DOM_NODE (item), NULL);
+                       else
+                               prev_item = webkit_dom_node_insert_before (
+                                       webkit_dom_node_get_parent_node (prev_item),
+                                       item,
+                                       webkit_dom_node_get_next_sibling (prev_item),
+                                       NULL);
+               }
+
+               if (webkit_dom_node_contains (item, WEBKIT_DOM_NODE (selection_end_marker)))
+                       after = TRUE;
+
+               if (!next_item) {
+                       if (after)
+                               break;
+
+                       current_list = webkit_dom_node_get_next_sibling (current_list);
+                       next_item = webkit_dom_node_get_first_child (current_list);
+               }
+               item = next_item;
+       }
+
+       remove_node_if_empty (source_list_clone);
+       remove_node_if_empty (source_list);
+}
+
+static void
+unindent_block (EEditorPage *editor_page,
+                WebKitDOMNode *block)
+{
+       WebKitDOMElement *element;
+       WebKitDOMElement *prev_blockquote = NULL, *next_blockquote = NULL;
+       WebKitDOMNode *block_to_process, *node_clone = NULL, *child;
+       EContentEditorAlignment alignment;
+       gboolean before_node = TRUE;
+       gint word_wrap_length, level, width;
+
+       g_return_if_fail (E_IS_EDITOR_PAGE (editor_page));
+
+       block_to_process = block;
+
+       alignment = dom_get_alignment_from_node (block_to_process);
+       element = webkit_dom_node_get_parent_element (block_to_process);
+
+       if (!WEBKIT_DOM_IS_HTML_DIV_ELEMENT (element) &&
+           !element_has_class (element, "-x-evo-indented"))
+               return;
+
+       element_add_class (WEBKIT_DOM_ELEMENT (block_to_process), "-x-evo-to-unindent");
+
+       level = get_indentation_level (element);
+       word_wrap_length = e_editor_page_get_word_wrap_length (editor_page);
+       width = word_wrap_length - SPACES_PER_INDENTATION * level;
+
+       /* Look if we have previous siblings, if so, we have to
+        * create new blockquote that will include them */
+       if (webkit_dom_node_get_previous_sibling (block_to_process))
+               prev_blockquote = dom_get_indented_element (editor_page, width);
+
+       /* Look if we have next siblings, if so, we have to
+        * create new blockquote that will include them */
+       if (webkit_dom_node_get_next_sibling (block_to_process))
+               next_blockquote = dom_get_indented_element (editor_page, width);
+
+       /* Copy nodes that are before / after the element that we want to unindent */
+       while ((child = webkit_dom_node_get_first_child (WEBKIT_DOM_NODE (element)))) {
+               if (webkit_dom_node_is_equal_node (child, block_to_process)) {
+                       before_node = FALSE;
+                       node_clone = webkit_dom_node_clone_node_with_error (child, TRUE, NULL);
+                       remove_node (child);
+                       continue;
+               }
+
+               webkit_dom_node_append_child (
+                       before_node ?
+                               WEBKIT_DOM_NODE (prev_blockquote) :
+                               WEBKIT_DOM_NODE (next_blockquote),
+                       child,
+                       NULL);
+       }
+
+       if (node_clone) {
+               element_remove_class (WEBKIT_DOM_ELEMENT (node_clone), "-x-evo-to-unindent");
+
+               /* Insert blockqoute with nodes that were before the element that we want to unindent */
+               if (prev_blockquote) {
+                       if (webkit_dom_node_has_child_nodes (WEBKIT_DOM_NODE (prev_blockquote))) {
+                               webkit_dom_node_insert_before (
+                                       webkit_dom_node_get_parent_node (WEBKIT_DOM_NODE (element)),
+                                       WEBKIT_DOM_NODE (prev_blockquote),
+                                       WEBKIT_DOM_NODE (element),
+                                       NULL);
+                       }
+               }
+
+               if (level == 1 && webkit_dom_element_has_attribute (WEBKIT_DOM_ELEMENT (node_clone), 
"data-evo-paragraph")) {
+                       e_editor_dom_set_paragraph_style (
+                               editor_page, WEBKIT_DOM_ELEMENT (node_clone), word_wrap_length, 0, NULL);
+                       element_add_class (
+                               WEBKIT_DOM_ELEMENT (node_clone),
+                               get_css_alignment_value_class (alignment));
+               }
+
+               /* Insert the unindented element */
+               webkit_dom_node_insert_before (
+                       webkit_dom_node_get_parent_node (WEBKIT_DOM_NODE (element)),
+                       node_clone,
+                       WEBKIT_DOM_NODE (element),
+                       NULL);
+       } else {
+               g_warn_if_reached ();
+       }
+
+       /* Insert blockqoute with nodes that were after the element that we want to unindent */
+       if (next_blockquote) {
+               if (webkit_dom_node_has_child_nodes (WEBKIT_DOM_NODE (next_blockquote))) {
+                       webkit_dom_node_insert_before (
+                               webkit_dom_node_get_parent_node (WEBKIT_DOM_NODE (element)),
+                               WEBKIT_DOM_NODE (next_blockquote),
+                               WEBKIT_DOM_NODE (element),
+                               NULL);
+               }
+       }
+
+       /* Remove old blockquote */
+       remove_node (WEBKIT_DOM_NODE (element));
+}
+
+/*
+ * dom_unindent:
+ * @selection: an #EEditorSelection
+ *
+ * Unindents current paragraph by one level.
+ */
+void
+e_editor_dom_selection_unindent (EEditorPage *editor_page)
+{
+       WebKitDOMDocument *document;
+       WebKitDOMElement *selection_start_marker, *selection_end_marker;
+       WebKitDOMNode *block;
+       EEditorHistoryEvent *ev = NULL;
+       EEditorUndoRedoManager *manager;
+       gboolean after_selection_start = FALSE, after_selection_end = FALSE;
+
+       g_return_if_fail (E_IS_EDITOR_PAGE (editor_page));
+
+       document = e_editor_page_get_document (editor_page);
+       e_editor_dom_selection_save (editor_page);
+
+       selection_start_marker = webkit_dom_document_query_selector (
+               document, "span#-x-evo-selection-start-marker", NULL);
+       selection_end_marker = webkit_dom_document_query_selector (
+               document, "span#-x-evo-selection-end-marker", NULL);
+
+       /* If the selection was not saved, move it into the first child of body */
+       if (!selection_start_marker || !selection_end_marker) {
+               WebKitDOMHTMLElement *body;
+               WebKitDOMNode *child;
+
+               body = webkit_dom_document_get_body (document);
+               child = webkit_dom_node_get_first_child (WEBKIT_DOM_NODE (body));
+
+               dom_add_selection_markers_into_element_start (
+                       document,
+                       WEBKIT_DOM_ELEMENT (child),
+                       &selection_start_marker,
+                       &selection_end_marker);
+       }
+
+       manager = e_editor_page_get_undo_redo_manager (editor_page);
+       if (!e_editor_undo_redo_manager_is_operation_in_progress (manager)) {
+               ev = g_new0 (EEditorHistoryEvent, 1);
+               ev->type = HISTORY_INDENT;
+
+               e_editor_dom_selection_get_coordinates (editor_page,
+                       &ev->before.start.x,
+                       &ev->before.start.y,
+                       &ev->before.end.x,
+                       &ev->before.end.y);
+       }
+
+       block = get_parent_indented_block (
+               WEBKIT_DOM_NODE (selection_start_marker));
+       if (!block)
+               block = e_editor_dom_get_parent_block_node_from_child (
+                       WEBKIT_DOM_NODE (selection_start_marker));
+
+       while (block && !after_selection_end) {
+               gint ii, length;
+               WebKitDOMNode *next_block;
+               WebKitDOMNodeList *list = NULL;
+
+               next_block = webkit_dom_node_get_next_sibling (block);
+
+               list = webkit_dom_element_query_selector_all (
+                       WEBKIT_DOM_ELEMENT (block),
+                       ".-x-evo-indented > *:not(.-x-evo-indented):not(li)",
+                       NULL);
+
+               after_selection_end = webkit_dom_node_contains (
+                       block, WEBKIT_DOM_NODE (selection_end_marker));
+
+               length = webkit_dom_node_list_get_length (list);
+               if (length == 0 && node_is_list_or_item (block)) {
+                       unindent_list (document);
+                       goto next;
+               }
+
+               if (length == 0) {
+                       if (!after_selection_start) {
+                               after_selection_start = webkit_dom_node_contains (
+                                       block, WEBKIT_DOM_NODE (selection_start_marker));
+                               if (!after_selection_start)
+                                       goto next;
+                       }
+
+                       unindent_block (editor_page, block);
+
+                       if (after_selection_end)
+                               goto next;
+               }
+
+               for (ii = 0; ii < length; ii++) {
+                       WebKitDOMNode *block_to_process;
+
+                       block_to_process = webkit_dom_node_list_item (list, ii);
+
+                       after_selection_end = webkit_dom_node_contains (
+                               block_to_process,
+                               WEBKIT_DOM_NODE (selection_end_marker));
+
+                       if (!after_selection_start) {
+                               after_selection_start = webkit_dom_node_contains (
+                                       block_to_process,
+                                       WEBKIT_DOM_NODE (selection_start_marker));
+                               if (!after_selection_start) {
+                                       g_object_unref (block_to_process);
+                                       continue;
+                               }
+                       }
+
+                       unindent_block (editor_page, block_to_process);
+
+                       g_object_unref (block_to_process);
+                       if (after_selection_end)
+                               break;
+               }
+ next:
+               g_clear_object (&list);
+               block = next_block;
+       }
+
+       if (ev) {
+               e_editor_dom_selection_get_coordinates (editor_page,
+                       &ev->after.start.x,
+                       &ev->after.start.y,
+                       &ev->after.end.x,
+                       &ev->after.end.y);
+               e_editor_undo_redo_manager_insert_history_event (manager, ev);
+       }
+
+       e_editor_dom_selection_restore (editor_page);
+
+       e_editor_dom_force_spell_check_for_current_paragraph (editor_page);
+}
+
+static WebKitDOMNode *
+in_empty_block_in_quoted_content (WebKitDOMNode *element)
+{
+       WebKitDOMNode *first_child, *next_sibling;
+
+       first_child = webkit_dom_node_get_first_child (element);
+       if (!WEBKIT_DOM_IS_ELEMENT (first_child))
+               return NULL;
+
+       if (!element_has_class (WEBKIT_DOM_ELEMENT (first_child), "-x-evo-quoted"))
+               return NULL;
+
+       next_sibling = webkit_dom_node_get_next_sibling (first_child);
+       if (WEBKIT_DOM_IS_HTML_BR_ELEMENT (next_sibling))
+               return next_sibling;
+
+       if (!WEBKIT_DOM_IS_ELEMENT (next_sibling))
+               return NULL;
+
+       if (!element_has_id (WEBKIT_DOM_ELEMENT (next_sibling), "-x-evo-selection-start-marker"))
+               return NULL;
+
+       next_sibling = webkit_dom_node_get_next_sibling (next_sibling);
+       if (WEBKIT_DOM_IS_HTML_BR_ELEMENT (next_sibling))
+               return next_sibling;
+
+       return NULL;
+}
+
+/*
+ * e_html_editor_selection_save:
+ * @selection: an #EEditorSelection
+ *
+ * Saves current cursor position or current selection range. The selection can
+ * be later restored by calling e_html_editor_selection_restore().
+ *
+ * Note that calling e_html_editor_selection_save() overwrites previously saved
+ * position.
+ *
+ * Note that this method inserts special markings into the HTML code that are
+ * used to later restore the selection. It can happen that by deleting some
+ * segments of the document some of the markings are deleted too. In that case
+ * restoring the selection by e_html_editor_selection_restore() can fail. Also by
+ * moving text segments (Cut & Paste) can result in moving the markings
+ * elsewhere, thus e_html_editor_selection_restore() will restore the selection
+ * incorrectly.
+ *
+ * It is recommended to use this method only when you are not planning to make
+ * bigger changes to content or structure of the document (formatting changes
+ * are usually OK).
+ */
+void
+e_editor_dom_selection_save (EEditorPage *editor_page)
+{
+       WebKitDOMDocument *document;
+       WebKitDOMDOMWindow *dom_window = NULL;
+       WebKitDOMDOMSelection *dom_selection = NULL;
+       WebKitDOMRange *range = NULL;
+       WebKitDOMNode *container, *next_sibling, *marker_node;
+       WebKitDOMNode *split_node, *parent_node, *anchor;
+       WebKitDOMElement *start_marker = NULL, *end_marker = NULL;
+       gboolean collapsed = FALSE;
+       glong offset, anchor_offset;
+
+       g_return_if_fail (E_IS_EDITOR_PAGE (editor_page));
+
+       document = e_editor_page_get_document (editor_page);
+
+       /* First remove all markers (if present) */
+       dom_remove_selection_markers (document);
+
+       dom_window = webkit_dom_document_get_default_view (document);
+       dom_selection = webkit_dom_dom_window_get_selection (dom_window);
+       g_clear_object (&dom_window);
+
+       if (webkit_dom_dom_selection_get_range_count (dom_selection) < 1) {
+               g_clear_object (&dom_selection);
+               return;
+       }
+
+       range = webkit_dom_dom_selection_get_range_at (dom_selection, 0, NULL);
+       if (!range) {
+               g_clear_object (&dom_selection);
+               return;
+       }
+
+       anchor = webkit_dom_dom_selection_get_anchor_node (dom_selection);
+       anchor_offset = webkit_dom_dom_selection_get_anchor_offset (dom_selection);
+
+       collapsed = webkit_dom_range_get_collapsed (range, NULL);
+       start_marker = dom_create_selection_marker (document, TRUE);
+
+       container = webkit_dom_range_get_start_container (range, NULL);
+       offset = webkit_dom_range_get_start_offset (range, NULL);
+       parent_node = webkit_dom_node_get_parent_node (container);
+
+       if (webkit_dom_node_is_same_node (anchor, container) && offset == anchor_offset)
+               webkit_dom_element_set_attribute (start_marker, "data-anchor", "", NULL);
+
+       if (element_has_class (WEBKIT_DOM_ELEMENT (parent_node), "-x-evo-quote-character")) {
+               WebKitDOMNode *node;
+
+               node = webkit_dom_node_get_parent_node (
+               webkit_dom_node_get_parent_node (parent_node));
+
+               if ((next_sibling = in_empty_block_in_quoted_content (node))) {
+                       webkit_dom_node_insert_before (
+                               webkit_dom_node_get_parent_node (next_sibling),
+                               WEBKIT_DOM_NODE (start_marker),
+                               next_sibling,
+                               NULL);
+               } else {
+                       webkit_dom_node_insert_before (
+                               node,
+                               WEBKIT_DOM_NODE (start_marker),
+                               webkit_dom_node_get_next_sibling (
+                                       webkit_dom_node_get_parent_node (parent_node)),
+                               NULL);
+               }
+               goto insert_end_marker;
+       } else if (element_has_class (WEBKIT_DOM_ELEMENT (parent_node), "-x-evo-smiley-text")) {
+               WebKitDOMNode *node;
+
+               node = webkit_dom_node_get_parent_node (parent_node);
+               if (offset == 0) {
+                       marker_node = webkit_dom_node_insert_before (
+                               webkit_dom_node_get_parent_node (node),
+                               WEBKIT_DOM_NODE (start_marker),
+                               webkit_dom_node_get_next_sibling (node),
+                               NULL);
+                       goto insert_end_marker;
+               }
+       } else if (element_has_class (WEBKIT_DOM_ELEMENT (parent_node), "Apple-tab-span") && offset == 1) {
+                       marker_node = webkit_dom_node_insert_before (
+                               webkit_dom_node_get_parent_node (parent_node),
+                               WEBKIT_DOM_NODE (start_marker),
+                               webkit_dom_node_get_next_sibling (parent_node),
+                               NULL);
+                       goto insert_end_marker;
+       }
+
+       if (WEBKIT_DOM_IS_TEXT (container)) {
+               if (offset != 0) {
+                       WebKitDOMText *split_text;
+
+                       split_text = webkit_dom_text_split_text (
+                               WEBKIT_DOM_TEXT (container), offset, NULL);
+                       split_node = WEBKIT_DOM_NODE (split_text);
+               } else {
+                       marker_node = webkit_dom_node_insert_before (
+                               parent_node,
+                               WEBKIT_DOM_NODE (start_marker),
+                               container,
+                               NULL);
+                       goto insert_end_marker;
+               }
+       } else if (WEBKIT_DOM_IS_HTML_LI_ELEMENT (container)) {
+               marker_node = webkit_dom_node_insert_before (
+                       container,
+                       WEBKIT_DOM_NODE (start_marker),
+                       webkit_dom_node_get_first_child (container),
+                       NULL);
+               goto insert_end_marker;
+       } else if (WEBKIT_DOM_IS_HTML_TABLE_CELL_ELEMENT (container)) {
+               marker_node = webkit_dom_node_insert_before (
+                       container,
+                       WEBKIT_DOM_NODE (start_marker),
+                       webkit_dom_node_get_first_child (container),
+                       NULL);
+               goto insert_end_marker;
+       } else {
+               /* Insert the selection marker on the right position in
+                * an empty paragraph in the quoted content */
+               if ((next_sibling = in_empty_block_in_quoted_content (container))) {
+                       marker_node = webkit_dom_node_insert_before (
+                               container,
+                               WEBKIT_DOM_NODE (start_marker),
+                               next_sibling,
+                               NULL);
+                       goto insert_end_marker;
+               }
+               if (!webkit_dom_node_get_previous_sibling (container)) {
+                       marker_node = webkit_dom_node_insert_before (
+                               container,
+                               WEBKIT_DOM_NODE (start_marker),
+                               webkit_dom_node_get_first_child (container),
+                               NULL);
+                       goto insert_end_marker;
+               } else if (!webkit_dom_node_get_next_sibling (container)) {
+                       WebKitDOMNode *tmp;
+
+                       tmp = webkit_dom_node_get_last_child (container);
+                       if (tmp && WEBKIT_DOM_IS_HTML_BR_ELEMENT (tmp))
+                               marker_node = webkit_dom_node_insert_before (
+                                       container,
+                                       WEBKIT_DOM_NODE (start_marker),
+                                       tmp,
+                                       NULL);
+                       else
+                               marker_node = webkit_dom_node_append_child (
+                                       container,
+                                       WEBKIT_DOM_NODE (start_marker),
+                                       NULL);
+                       goto insert_end_marker;
+               } else {
+                       if (webkit_dom_node_get_first_child (container)) {
+                               marker_node = webkit_dom_node_insert_before (
+                                       container,
+                                       WEBKIT_DOM_NODE (start_marker),
+                                       webkit_dom_node_get_first_child (container),
+                                       NULL);
+                               goto insert_end_marker;
+                       }
+                       split_node = container;
+               }
+       }
+
+       /* Don't save selection straight into body */
+       if (WEBKIT_DOM_IS_HTML_BODY_ELEMENT (split_node)) {
+               g_clear_object (&range);
+               g_clear_object (&dom_selection);
+               return;
+       }
+
+       if (!split_node) {
+               marker_node = webkit_dom_node_insert_before (
+                       container,
+                       WEBKIT_DOM_NODE (start_marker),
+                       webkit_dom_node_get_first_child (
+                               WEBKIT_DOM_NODE (container)),
+                       NULL);
+       } else {
+               marker_node = WEBKIT_DOM_NODE (start_marker);
+               parent_node = webkit_dom_node_get_parent_node (split_node);
+
+               webkit_dom_node_insert_before (
+                       parent_node, marker_node, split_node, NULL);
+       }
+
+       webkit_dom_node_normalize (parent_node);
+
+ insert_end_marker:
+       end_marker = dom_create_selection_marker (document, FALSE);
+
+       if (collapsed) {
+               webkit_dom_node_insert_before (
+                       webkit_dom_node_get_parent_node (WEBKIT_DOM_NODE (start_marker)),
+                       WEBKIT_DOM_NODE (end_marker),
+                       webkit_dom_node_get_next_sibling (WEBKIT_DOM_NODE (start_marker)),
+                       NULL);
+               goto out;
+       }
+
+       container = webkit_dom_range_get_end_container (range, NULL);
+       offset = webkit_dom_range_get_end_offset (range, NULL);
+       parent_node = webkit_dom_node_get_parent_node (container);
+
+       if (webkit_dom_node_is_same_node (anchor, container) && offset == anchor_offset)
+               webkit_dom_element_set_attribute (end_marker, "data-anchor", "", NULL);
+
+       if (element_has_class (WEBKIT_DOM_ELEMENT (parent_node), "-x-evo-quote-character")) {
+               WebKitDOMNode *node;
+
+               node = webkit_dom_node_get_parent_node (
+               webkit_dom_node_get_parent_node (parent_node));
+
+               if ((next_sibling = in_empty_block_in_quoted_content (node))) {
+                       webkit_dom_node_insert_before (
+                               webkit_dom_node_get_parent_node (next_sibling),
+                               WEBKIT_DOM_NODE (end_marker),
+                               next_sibling,
+                               NULL);
+               } else {
+                       webkit_dom_node_insert_before (
+                               node,
+                               WEBKIT_DOM_NODE (end_marker),
+                               webkit_dom_node_get_next_sibling (
+                                       webkit_dom_node_get_parent_node (parent_node)),
+                               NULL);
+               }
+               goto out;
+       }
+
+       if (WEBKIT_DOM_IS_TEXT (container)) {
+               if (offset != 0) {
+                       WebKitDOMText *split_text;
+
+                       split_text = webkit_dom_text_split_text (
+                               WEBKIT_DOM_TEXT (container), offset, NULL);
+                       split_node = WEBKIT_DOM_NODE (split_text);
+               } else {
+                       marker_node = webkit_dom_node_insert_before (
+                               parent_node, WEBKIT_DOM_NODE (end_marker), container, NULL);
+                       goto check;
+
+               }
+       } else if (WEBKIT_DOM_IS_HTML_LI_ELEMENT (container)) {
+               webkit_dom_node_append_child (
+                       container, WEBKIT_DOM_NODE (end_marker), NULL);
+               goto out;
+       } else {
+               /* Insert the selection marker on the right position in
+                * an empty paragraph in the quoted content */
+               if ((next_sibling = in_empty_block_in_quoted_content (container))) {
+                       webkit_dom_node_insert_before (
+                               container,
+                               WEBKIT_DOM_NODE (end_marker),
+                               next_sibling,
+                               NULL);
+                       goto out;
+               }
+               if (!webkit_dom_node_get_previous_sibling (container)) {
+                       split_node = parent_node;
+               } else if (!webkit_dom_node_get_next_sibling (container) &&
+                          !WEBKIT_DOM_IS_HTML_BODY_ELEMENT (parent_node)) {
+                       split_node = parent_node;
+                       split_node = webkit_dom_node_get_next_sibling (split_node);
+               } else
+                       split_node = container;
+       }
+
+       /* Don't save selection straight into body */
+       if (WEBKIT_DOM_IS_HTML_BODY_ELEMENT (split_node)) {
+               remove_node (WEBKIT_DOM_NODE (start_marker));
+               return;
+       }
+
+       marker_node = WEBKIT_DOM_NODE (end_marker);
+
+       if (split_node) {
+               parent_node = webkit_dom_node_get_parent_node (split_node);
+
+               if (WEBKIT_DOM_IS_HTML_BODY_ELEMENT (parent_node)) {
+                       if (offset == 0)
+                               webkit_dom_node_insert_before (
+                                       split_node,
+                                       marker_node,
+                                       webkit_dom_node_get_first_child (split_node),
+                                       NULL);
+                       else
+                               webkit_dom_node_append_child (
+                                       webkit_dom_node_get_previous_sibling (split_node),
+                                       marker_node,
+                                       NULL);
+               } else
+                       webkit_dom_node_insert_before (
+                               parent_node, marker_node, split_node, NULL);
+       } else {
+                WebKitDOMNode *first_child;
+
+               first_child = webkit_dom_node_get_first_child (container);
+               if (offset == 0 && WEBKIT_DOM_IS_TEXT (first_child))
+                       webkit_dom_node_insert_before (
+                               WEBKIT_DOM_NODE (container), marker_node, webkit_dom_node_get_first_child 
(container), NULL);
+               else
+                       webkit_dom_node_append_child (
+                               WEBKIT_DOM_NODE (container), marker_node, NULL);
+       }
+
+        webkit_dom_node_normalize (parent_node);
+
+ check:
+       if ((next_sibling = webkit_dom_node_get_next_sibling (marker_node))) {
+               if (!WEBKIT_DOM_IS_ELEMENT (next_sibling))
+                       next_sibling = webkit_dom_node_get_next_sibling (next_sibling);
+               /* If the selection is collapsed ensure that the selection start marker
+                * is before the end marker */
+               if (next_sibling && webkit_dom_node_is_same_node (next_sibling, WEBKIT_DOM_NODE 
(start_marker))) {
+                       webkit_dom_node_insert_before (
+                               webkit_dom_node_get_parent_node (marker_node),
+                               next_sibling,
+                               marker_node,
+                               NULL);
+               }
+       }
+ out:
+       if (!collapsed) {
+               if (start_marker && end_marker) {
+                       webkit_dom_range_set_start_after (range, WEBKIT_DOM_NODE (start_marker), NULL);
+                       webkit_dom_range_set_end_before (range, WEBKIT_DOM_NODE (end_marker), NULL);
+               } else {
+                       g_warn_if_reached ();
+               }
+
+               webkit_dom_dom_selection_remove_all_ranges (dom_selection);
+               webkit_dom_dom_selection_add_range (dom_selection, range);
+       }
+
+       g_clear_object (&range);
+       g_clear_object (&dom_selection);
+}
+
+gboolean
+e_editor_dom_is_selection_position_node (WebKitDOMNode *node)
+{
+       WebKitDOMElement *element;
+
+       if (!node || !WEBKIT_DOM_IS_ELEMENT (node))
+               return FALSE;
+
+       element = WEBKIT_DOM_ELEMENT (node);
+
+       return element_has_id (element, "-x-evo-selection-start-marker") ||
+              element_has_id (element, "-x-evo-selection-end-marker");
+}
+
+/*
+ * e_html_editor_selection_restore:
+ * @selection: an #EEditorSelection
+ *
+ * Restores cursor position or selection range that was saved by
+ * e_html_editor_selection_save().
+ *
+ * Note that calling this function without calling e_html_editor_selection_save()
+ * before is a programming error and the behavior is undefined.
+ */
+void
+e_editor_dom_selection_restore (EEditorPage *editor_page)
+{
+       WebKitDOMDocument *document;
+       WebKitDOMElement *marker;
+       WebKitDOMNode *selection_start_marker, *selection_end_marker;
+       WebKitDOMNode *parent_start, *parent_end, *anchor;
+       WebKitDOMRange *range = NULL;
+       WebKitDOMDOMSelection *dom_selection = NULL;
+       WebKitDOMDOMWindow *dom_window = NULL;
+       gboolean start_is_anchor = FALSE;
+       glong offset;
+
+       g_return_if_fail (E_IS_EDITOR_PAGE (editor_page));
+
+       document = e_editor_page_get_document (editor_page);
+       dom_window = webkit_dom_document_get_default_view (document);
+       dom_selection = webkit_dom_dom_window_get_selection (dom_window);
+       range = webkit_dom_dom_selection_get_range_at (dom_selection, 0, NULL);
+       g_clear_object (&dom_window);
+       if (!range) {
+               WebKitDOMHTMLElement *body;
+
+               range = webkit_dom_document_create_range (document);
+               body = webkit_dom_document_get_body (document);
+
+               webkit_dom_range_select_node_contents (range, WEBKIT_DOM_NODE (body), NULL);
+               webkit_dom_range_collapse (range, TRUE, NULL);
+               webkit_dom_dom_selection_add_range (dom_selection, range);
+       }
+
+       selection_start_marker = webkit_dom_range_get_start_container (range, NULL);
+       if (selection_start_marker) {
+               gboolean ok = FALSE;
+               selection_start_marker =
+                       webkit_dom_node_get_next_sibling (selection_start_marker);
+
+               ok = e_editor_dom_is_selection_position_node (selection_start_marker);
+
+               if (ok) {
+                       ok = FALSE;
+                       if (webkit_dom_range_get_collapsed (range, NULL)) {
+                               selection_end_marker = webkit_dom_node_get_next_sibling (
+                                       selection_start_marker);
+
+                               ok = e_editor_dom_is_selection_position_node (selection_end_marker);
+                               if (ok) {
+                                       WebKitDOMNode *next_sibling;
+
+                                       next_sibling = webkit_dom_node_get_next_sibling 
(selection_end_marker);
+
+                                       if (next_sibling && !WEBKIT_DOM_IS_HTML_BR_ELEMENT (next_sibling)) {
+                                               parent_start = webkit_dom_node_get_parent_node 
(selection_end_marker);
+
+                                               remove_node (selection_start_marker);
+                                               remove_node (selection_end_marker);
+
+                                               webkit_dom_node_normalize (parent_start);
+                                               g_clear_object (&range);
+                                               g_clear_object (&dom_selection);
+                                               return;
+                                       }
+                               }
+                       }
+               }
+       }
+
+       g_clear_object (&range);
+       range = webkit_dom_document_create_range (document);
+       if (!range) {
+               g_clear_object (&dom_selection);
+               return;
+       }
+
+       marker = webkit_dom_document_get_element_by_id (
+               document, "-x-evo-selection-start-marker");
+       if (!marker) {
+               marker = webkit_dom_document_get_element_by_id (
+                       document, "-x-evo-selection-end-marker");
+               if (marker)
+                       remove_node (WEBKIT_DOM_NODE (marker));
+               g_clear_object (&dom_selection);
+               g_clear_object (&range);
+               return;
+       }
+
+       start_is_anchor = webkit_dom_element_has_attribute (marker, "data-anchor");
+       parent_start = webkit_dom_node_get_parent_node (WEBKIT_DOM_NODE (marker));
+
+       webkit_dom_range_set_start_after (range, WEBKIT_DOM_NODE (marker), NULL);
+       remove_node (WEBKIT_DOM_NODE (marker));
+
+       marker = webkit_dom_document_get_element_by_id (
+               document, "-x-evo-selection-end-marker");
+       if (!marker) {
+               marker = webkit_dom_document_get_element_by_id (
+                       document, "-x-evo-selection-start-marker");
+               if (marker)
+                       remove_node (WEBKIT_DOM_NODE (marker));
+               g_clear_object (&dom_selection);
+               g_clear_object (&range);
+               return;
+       }
+
+       parent_end = webkit_dom_node_get_parent_node (WEBKIT_DOM_NODE (marker));
+
+       webkit_dom_range_set_end_before (range, WEBKIT_DOM_NODE (marker), NULL);
+       remove_node (WEBKIT_DOM_NODE (marker));
+
+       webkit_dom_dom_selection_remove_all_ranges (dom_selection);
+       if (webkit_dom_node_is_same_node (parent_start, parent_end))
+               webkit_dom_node_normalize (parent_start);
+       else {
+               webkit_dom_node_normalize (parent_start);
+               webkit_dom_node_normalize (parent_end);
+       }
+
+       if (start_is_anchor) {
+               anchor = webkit_dom_range_get_end_container (range, NULL);
+               offset = webkit_dom_range_get_end_offset (range, NULL);
+
+               webkit_dom_range_collapse (range, TRUE, NULL);
+       } else {
+               anchor = webkit_dom_range_get_start_container (range, NULL);
+               offset = webkit_dom_range_get_start_offset (range, NULL);
+
+               webkit_dom_range_collapse (range, FALSE, NULL);
+       }
+       webkit_dom_dom_selection_add_range (dom_selection, range);
+       webkit_dom_dom_selection_extend (dom_selection, anchor, offset, NULL);
+
+       g_clear_object (&dom_selection);
+       g_clear_object (&range);
+}
+
+static gint
+find_where_to_break_line (WebKitDOMCharacterData *node,
+                          gint max_length)
+{
+       gboolean last_break_position_is_dash = FALSE;
+       gchar *str, *text_start;
+       gunichar uc;
+       gint pos = 1, last_break_position = 0, ret_val = 0;
+
+       text_start = webkit_dom_character_data_get_data (node);
+
+       str = text_start;
+       do {
+               uc = g_utf8_get_char (str);
+               if (!uc) {
+                       ret_val = pos <= max_length ? pos : last_break_position > 0 ? last_break_position - 1 
: 0;
+                       goto out;
+               }
+
+               if ((g_unichar_isspace (uc) && !(g_unichar_break_type (uc) == 
G_UNICODE_BREAK_NON_BREAKING_GLUE)) ||
+                    *str == '-') {
+                       if ((last_break_position_is_dash = *str == '-')) {
+                               /* There was no space before the dash */
+                               if (pos - 1 != last_break_position) {
+                                       gchar *rest;
+
+                                       rest = g_utf8_next_char (str);
+                                       if (rest && *rest) {
+                                               gunichar next_char;
+
+                                               /* There is no space after the dash */
+                                               next_char = g_utf8_get_char (rest);
+                                               if (g_unichar_isspace (next_char))
+                                                       last_break_position_is_dash = FALSE;
+                                               else
+                                                       last_break_position = pos;
+                                       } else
+                                               last_break_position_is_dash = FALSE;
+                               } else
+                                       last_break_position_is_dash = FALSE;
+                       } else
+                               last_break_position = pos;
+               }
+
+               if ((pos == max_length)) {
+                       /* Look one character after the limit to check if there
+                        * is a space (skip dash) that we are allowed to break at, if so
+                        * break it there. */
+                       if (*str) {
+                               str = g_utf8_next_char (str);
+                               uc = g_utf8_get_char (str);
+
+                               if ((g_unichar_isspace (uc) &&
+                                   !(g_unichar_break_type (uc) == G_UNICODE_BREAK_NON_BREAKING_GLUE)))
+                                       last_break_position = ++pos;
+                       }
+                       break;
+               }
+
+               pos++;
+               str = g_utf8_next_char (str);
+       } while (*str);
+
+       if (last_break_position != 0)
+               ret_val = last_break_position - 1;
+ out:
+       g_free (text_start);
+
+       /* Always break after the dash character. */
+       if (last_break_position_is_dash)
+               ret_val++;
+
+       /* No character to break at is found. We should split at max_length, but
+        * we will leave the decision on caller as it depends on context. */
+       if (ret_val == 0 && last_break_position == 0)
+               ret_val = -1;
+
+       return ret_val;
+}
+
+/*
+ * e_html_editor_selection_is_collapsed:
+ * @selection: an #EEditorSelection
+ *
+ * Returns if selection is collapsed.
+ *
+ * Returns: Whether the selection is collapsed (just caret) or not (someting is selected).
+ */
+gboolean
+e_editor_dom_selection_is_collapsed (EEditorPage *editor_page)
+{
+       WebKitDOMDocument *document;
+       WebKitDOMDOMWindow *dom_window = NULL;
+       WebKitDOMDOMSelection *dom_selection = NULL;
+       gboolean collapsed;
+
+       g_return_val_if_fail (E_IS_EDITOR_PAGE (editor_page), FALSE);
+
+       document = e_editor_page_get_document (editor_page);
+       if (!(dom_window = webkit_dom_document_get_default_view (document)))
+               return FALSE;
+
+       if (!(dom_selection = webkit_dom_dom_window_get_selection (dom_window))) {
+               g_clear_object (&dom_window);
+               return FALSE;
+       }
+
+       collapsed = webkit_dom_dom_selection_get_is_collapsed (dom_selection);
+
+       g_clear_object (&dom_selection);
+
+       return collapsed;
+}
+
+void
+e_editor_dom_scroll_to_caret (EEditorPage *editor_page)
+{
+       WebKitDOMDocument *document;
+       WebKitDOMDOMWindow *dom_window = NULL;
+       WebKitDOMElement *selection_start_marker;
+       glong element_top, element_left;
+       glong window_top, window_left, window_right, window_bottom;
+
+       g_return_if_fail (E_IS_EDITOR_PAGE (editor_page));
+
+       document = e_editor_page_get_document (editor_page);
+       e_editor_dom_selection_save (editor_page);
+
+       selection_start_marker = webkit_dom_document_get_element_by_id (
+               document, "-x-evo-selection-start-marker");
+       if (!selection_start_marker)
+               return;
+
+       dom_window = webkit_dom_document_get_default_view (document);
+
+       window_top = webkit_dom_dom_window_get_scroll_y (dom_window);
+       window_left = webkit_dom_dom_window_get_scroll_x (dom_window);
+       window_bottom = window_top + webkit_dom_dom_window_get_inner_height (dom_window);
+       window_right = window_left + webkit_dom_dom_window_get_inner_width (dom_window);
+
+       element_left = webkit_dom_element_get_offset_left (selection_start_marker);
+       element_top = webkit_dom_element_get_offset_top (selection_start_marker);
+
+       /* Check if caret is inside viewport, if not move to it */
+       if (!(element_top >= window_top && element_top <= window_bottom &&
+            element_left >= window_left && element_left <= window_right)) {
+               webkit_dom_element_scroll_into_view (selection_start_marker, TRUE);
+       }
+
+       e_editor_dom_selection_restore (editor_page);
+
+       g_clear_object (&dom_window);
+}
+
+static void
+mark_and_remove_trailing_space (WebKitDOMDocument *document,
+                                WebKitDOMNode *node)
+{
+       WebKitDOMElement *element;
+
+       element = webkit_dom_document_create_element (document, "SPAN", NULL);
+       webkit_dom_element_set_attribute (element, "data-hidden-space", "", NULL);
+       webkit_dom_node_insert_before (
+               webkit_dom_node_get_parent_node (node),
+               WEBKIT_DOM_NODE (element),
+               webkit_dom_node_get_next_sibling (node),
+               NULL);
+       webkit_dom_character_data_replace_data (
+               WEBKIT_DOM_CHARACTER_DATA (node),
+               webkit_dom_character_data_get_length (WEBKIT_DOM_CHARACTER_DATA (node)),
+               1,
+               "",
+               NULL);
+}
+
+static void
+mark_and_remove_leading_space (WebKitDOMDocument *document,
+                               WebKitDOMNode *node)
+{
+       WebKitDOMElement *element;
+
+       element = webkit_dom_document_create_element (document, "SPAN", NULL);
+       webkit_dom_element_set_attribute (element, "data-hidden-space", "", NULL);
+       webkit_dom_node_insert_before (
+               webkit_dom_node_get_parent_node (node),
+               WEBKIT_DOM_NODE (element),
+               node,
+               NULL);
+       webkit_dom_character_data_replace_data (
+               WEBKIT_DOM_CHARACTER_DATA (node), 0, 1, "", NULL);
+}
+
+static WebKitDOMElement *
+wrap_lines (EEditorPage *editor_page,
+            WebKitDOMNode *block,
+           gboolean remove_all_br,
+           gint length_to_wrap,
+           gint word_wrap_length)
+{
+       WebKitDOMDocument *document;
+       WebKitDOMElement *selection_start_marker, *selection_end_marker;
+       WebKitDOMNode *node, *start_node, *block_clone = NULL;
+       WebKitDOMNode *start_point = NULL, *first_child, *last_child;
+       guint line_length;
+       gulong length_left;
+       gchar *text_content;
+       gboolean compensated = FALSE;
+       gboolean check_next_node = FALSE;
+
+       g_return_val_if_fail (E_IS_EDITOR_PAGE (editor_page), NULL);
+
+       document = e_editor_page_get_document (editor_page);
+
+       if (!webkit_dom_node_has_child_nodes (block))
+               return WEBKIT_DOM_ELEMENT (block);
+
+       /* Avoid wrapping when the block contains just the BR element alone
+        * or with selection markers. */
+       if ((first_child = webkit_dom_node_get_first_child (block)) &&
+            WEBKIT_DOM_IS_HTML_BR_ELEMENT (first_child)) {
+               WebKitDOMNode *next_sibling;
+
+               if ((next_sibling = webkit_dom_node_get_next_sibling (first_child))) {
+                      if (e_editor_dom_is_selection_position_node (next_sibling) &&
+                          (next_sibling = webkit_dom_node_get_next_sibling (next_sibling)) &&
+                          e_editor_dom_is_selection_position_node (next_sibling) &&
+                          !webkit_dom_node_get_next_sibling (next_sibling))
+                               return WEBKIT_DOM_ELEMENT (block);
+               } else
+                       return WEBKIT_DOM_ELEMENT (block);
+       }
+
+       block_clone = webkit_dom_node_clone_node_with_error (block, TRUE, NULL);
+
+       /* When we wrap, we are wrapping just the text after caret, text
+        * before the caret is already wrapped, so unwrap the text after
+        * the caret position */
+       selection_end_marker = webkit_dom_element_query_selector (
+               WEBKIT_DOM_ELEMENT (block_clone),
+               "span#-x-evo-selection-end-marker",
+               NULL);
+
+       if (selection_end_marker) {
+               WebKitDOMNode *nd = WEBKIT_DOM_NODE (selection_end_marker);
+
+               while (nd) {
+                       WebKitDOMNode *parent_node;
+                       WebKitDOMNode *next_nd = webkit_dom_node_get_next_sibling (nd);
+
+                       parent_node = webkit_dom_node_get_parent_node (nd);
+                       if (!next_nd && parent_node && !webkit_dom_node_is_same_node (parent_node, 
block_clone))
+                               next_nd = webkit_dom_node_get_next_sibling (parent_node);
+
+                       if (WEBKIT_DOM_IS_HTML_BR_ELEMENT (nd)) {
+                               if (remove_all_br)
+                                       remove_node (nd);
+                               else if (element_has_class (WEBKIT_DOM_ELEMENT (nd), "-x-evo-wrap-br"))
+                                       remove_node (nd);
+                       } else if (WEBKIT_DOM_IS_ELEMENT (nd) &&
+                                  webkit_dom_element_has_attribute (WEBKIT_DOM_ELEMENT (nd), 
"data-hidden-space"))
+                               webkit_dom_html_element_set_outer_text (
+                                       WEBKIT_DOM_HTML_ELEMENT (nd), " ", NULL);
+
+                       nd = next_nd;
+               }
+       } else {
+               gint ii, length;
+               WebKitDOMNodeList *list = NULL;
+
+               list = webkit_dom_element_query_selector_all (
+                       WEBKIT_DOM_ELEMENT (block_clone), "span[data-hidden-space]", NULL);
+               length = webkit_dom_node_list_get_length (list);
+               for (ii = 0; ii < length; ii++) {
+                       WebKitDOMNode *hidden_space_node;
+
+                       hidden_space_node = webkit_dom_node_list_item (list, ii);
+                       webkit_dom_html_element_set_outer_text (
+                               WEBKIT_DOM_HTML_ELEMENT (hidden_space_node), " ", NULL);
+                       g_object_unref (hidden_space_node);
+               }
+               g_clear_object (&list);
+       }
+
+       /* We have to start from the end of the last wrapped line */
+       selection_start_marker = webkit_dom_element_query_selector (
+               WEBKIT_DOM_ELEMENT (block_clone),
+               "span#-x-evo-selection-start-marker",
+               NULL);
+
+       if (selection_start_marker) {
+               gboolean first_removed = FALSE;
+               WebKitDOMNode *nd;
+
+               nd = webkit_dom_node_get_previous_sibling (
+                       WEBKIT_DOM_NODE (selection_start_marker));
+               while (nd) {
+                       WebKitDOMNode *prev_nd = webkit_dom_node_get_previous_sibling (nd);
+
+                       if (!prev_nd && !webkit_dom_node_is_same_node (webkit_dom_node_get_parent_node (nd), 
block_clone))
+                               prev_nd = webkit_dom_node_get_previous_sibling 
(webkit_dom_node_get_parent_node (nd));
+
+                       if (WEBKIT_DOM_IS_HTML_BR_ELEMENT (nd)) {
+                               if (first_removed) {
+                                       start_point = nd;
+                                       break;
+                               } else {
+                                       remove_node (nd);
+                                       first_removed = TRUE;
+                               }
+                       } else if (WEBKIT_DOM_IS_ELEMENT (nd) &&
+                                  webkit_dom_element_has_attribute (WEBKIT_DOM_ELEMENT (nd), 
"data-hidden-space")) {
+                               webkit_dom_html_element_set_outer_text (
+                                       WEBKIT_DOM_HTML_ELEMENT (nd), " ", NULL);
+                       } else if (!prev_nd) {
+                               WebKitDOMNode *parent;
+
+                               parent = webkit_dom_node_get_parent_node (nd);
+                               if (!WEBKIT_DOM_IS_HTML_ANCHOR_ELEMENT (parent))
+                                       start_point = nd;
+                       }
+
+                       nd = prev_nd;
+               }
+       }
+
+       webkit_dom_node_normalize (block_clone);
+       node = webkit_dom_node_get_first_child (block_clone);
+       if (node) {
+               text_content = webkit_dom_node_get_text_content (node);
+               if (g_strcmp0 ("\n", text_content) == 0)
+                       node = webkit_dom_node_get_next_sibling (node);
+               g_free (text_content);
+       }
+
+       if (start_point) {
+               if (WEBKIT_DOM_IS_HTML_BR_ELEMENT (start_point))
+                       node = webkit_dom_node_get_next_sibling (WEBKIT_DOM_NODE (start_point));
+               else
+                       node = start_point;
+               start_node = block_clone;
+       } else
+               start_node = node;
+
+       line_length = 0;
+       while (node) {
+               gint offset = 0;
+               WebKitDOMElement *element;
+
+               if (WEBKIT_DOM_IS_TEXT (node)) {
+                       const gchar *newline;
+                       WebKitDOMNode *next_sibling;
+
+                       /* If there is temporary hidden space we remove it */
+                       text_content = webkit_dom_node_get_text_content (node);
+                       if (strstr (text_content, UNICODE_ZERO_WIDTH_SPACE)) {
+                               if (g_str_has_prefix (text_content, UNICODE_ZERO_WIDTH_SPACE))
+                                       webkit_dom_character_data_delete_data (
+                                               WEBKIT_DOM_CHARACTER_DATA (node),
+                                               0,
+                                               1,
+                                               NULL);
+                               if (g_str_has_suffix (text_content, UNICODE_ZERO_WIDTH_SPACE))
+                                       webkit_dom_character_data_delete_data (
+                                               WEBKIT_DOM_CHARACTER_DATA (node),
+                                               g_utf8_strlen (text_content, -1) - 1,
+                                               1,
+                                               NULL);
+                               g_free (text_content);
+                               text_content = webkit_dom_node_get_text_content (node);
+                       }
+                       newline = strstr (text_content, "\n");
+
+                       next_sibling = node;
+                       while (newline) {
+                               next_sibling = WEBKIT_DOM_NODE (webkit_dom_text_split_text (
+                                       WEBKIT_DOM_TEXT (next_sibling),
+                                       g_utf8_pointer_to_offset (text_content, newline),
+                                       NULL));
+
+                               if (!next_sibling)
+                                       break;
+
+                               element = webkit_dom_document_create_element (
+                                       document, "BR", NULL);
+                               element_add_class (element, "-x-evo-wrap-br");
+
+                               webkit_dom_node_insert_before (
+                                       webkit_dom_node_get_parent_node (next_sibling),
+                                       WEBKIT_DOM_NODE (element),
+                                       next_sibling,
+                                       NULL);
+
+                               g_free (text_content);
+
+                               text_content = webkit_dom_node_get_text_content (next_sibling);
+                               if (g_str_has_prefix (text_content, "\n")) {
+                                       webkit_dom_character_data_delete_data (
+                                               WEBKIT_DOM_CHARACTER_DATA (next_sibling), 0, 1, NULL);
+                                       g_free (text_content);
+                                       text_content =
+                                               webkit_dom_node_get_text_content (next_sibling);
+                               }
+                               newline = strstr (text_content, "\n");
+                       }
+                       g_free (text_content);
+               } else if (WEBKIT_DOM_IS_ELEMENT (node)) {
+                       if (e_editor_dom_is_selection_position_node (node)) {
+                               if (line_length == 0) {
+                                       WebKitDOMNode *tmp_node;
+
+                                       tmp_node = webkit_dom_node_get_previous_sibling (node);
+                                       /* Only check if there is some node before the selection marker. */
+                                       if (tmp_node && !e_editor_dom_is_selection_position_node (tmp_node))
+                                               check_next_node = TRUE;
+                               }
+                               node = webkit_dom_node_get_next_sibling (node);
+                               continue;
+                       }
+
+                       check_next_node = FALSE;
+                       /* If element is ANCHOR we wrap it separately */
+                       if (WEBKIT_DOM_IS_HTML_ANCHOR_ELEMENT (node)) {
+                               glong anchor_length;
+                               WebKitDOMNode *next_sibling;
+
+                               text_content = webkit_dom_node_get_text_content (node);
+                               anchor_length = g_utf8_strlen (text_content, -1);
+                               g_free (text_content);
+
+                               next_sibling = webkit_dom_node_get_next_sibling (node);
+                               /* If the anchor doesn't fit on the line move the inner
+                                * nodes out of it and start to wrap them. */
+                               if ((line_length + anchor_length) > length_to_wrap) {
+                                       WebKitDOMNode *inner_node;
+
+                                       while ((inner_node = webkit_dom_node_get_first_child (node))) {
+                                               g_object_set_data (
+                                                       G_OBJECT (inner_node),
+                                                       "-x-evo-anchor-text",
+                                                       GINT_TO_POINTER (1));
+                                               webkit_dom_node_insert_before (
+                                                       webkit_dom_node_get_parent_node (node),
+                                                       inner_node,
+                                                       next_sibling,
+                                                       NULL);
+                                       }
+                                       next_sibling = webkit_dom_node_get_next_sibling (node);
+
+                                       remove_node (node);
+                                       node = next_sibling;
+                                       continue;
+                               }
+
+                               line_length += anchor_length;
+                               node = next_sibling;
+                               continue;
+                       }
+
+                       if (element_has_class (WEBKIT_DOM_ELEMENT (node), "Apple-tab-span")) {
+                               WebKitDOMNode *sibling;
+                               gint tab_length;
+
+                               sibling = webkit_dom_node_get_previous_sibling (node);
+                               if (sibling && WEBKIT_DOM_IS_ELEMENT (sibling) &&
+                                   element_has_class (WEBKIT_DOM_ELEMENT (sibling), "Apple-tab-span"))
+                                       tab_length = TAB_LENGTH;
+                               else {
+                                       tab_length = TAB_LENGTH - (line_length + compensated ? 0 : 
(word_wrap_length - length_to_wrap)) % TAB_LENGTH;
+                                       compensated = TRUE;
+                               }
+
+                               if (line_length + tab_length > length_to_wrap) {
+                                       if (webkit_dom_node_get_next_sibling (node)) {
+                                               element = webkit_dom_document_create_element (
+                                                       document, "BR", NULL);
+                                               element_add_class (element, "-x-evo-wrap-br");
+                                               node = webkit_dom_node_insert_before (
+                                                       webkit_dom_node_get_parent_node (node),
+                                                       WEBKIT_DOM_NODE (element),
+                                                       webkit_dom_node_get_next_sibling (node),
+                                                       NULL);
+                                       }
+                                       line_length = 0;
+                                       compensated = FALSE;
+                               } else
+                                       line_length += tab_length;
+
+                               sibling = webkit_dom_node_get_next_sibling (node);
+                               node = sibling;
+                               continue;
+                       }
+                       /* When we are not removing user-entered BR elements (lines wrapped by user),
+                        * we need to skip those elements */
+                       if (!remove_all_br && WEBKIT_DOM_IS_HTML_BR_ELEMENT (node)) {
+                               if (!element_has_class (WEBKIT_DOM_ELEMENT (node), "-x-evo-wrap-br")) {
+                                       line_length = 0;
+                                       compensated = FALSE;
+                                       node = webkit_dom_node_get_next_sibling (node);
+                                       continue;
+                               }
+                       }
+                       goto next_node;
+               } else {
+                       WebKitDOMNode *sibling;
+
+                       sibling = webkit_dom_node_get_next_sibling (node);
+                       node = sibling;
+                       continue;
+               }
+
+               /* If length of this node + what we already have is still less
+                * then length_to_wrap characters, then just concatenate it and
+                * continue to next node */
+               length_left = webkit_dom_character_data_get_length (
+                       WEBKIT_DOM_CHARACTER_DATA (node));
+
+               if ((length_left + line_length) <= length_to_wrap) {
+                       if (check_next_node)
+                               goto check_node;
+                       line_length += length_left;
+                       if (line_length == length_to_wrap) {
+                               line_length = 0;
+
+                               element = webkit_dom_document_create_element (document, "BR", NULL);
+                               element_add_class (element, "-x-evo-wrap-br");
+
+                               webkit_dom_node_insert_before (
+                                       webkit_dom_node_get_parent_node (node),
+                                       WEBKIT_DOM_NODE (element),
+                                       webkit_dom_node_get_next_sibling (node),
+                                       NULL);
+                       }
+                       goto next_node;
+               }
+
+               /* wrap until we have something */
+               while (node && (length_left + line_length) > length_to_wrap) {
+                       gboolean insert_and_continue;
+                       gint max_length;
+
+ check_node:
+                       insert_and_continue = FALSE;
+
+                       if (!WEBKIT_DOM_IS_CHARACTER_DATA (node))
+                               goto next_node;
+
+                       element = webkit_dom_document_create_element (document, "BR", NULL);
+                       element_add_class (element, "-x-evo-wrap-br");
+
+                       max_length = length_to_wrap - line_length;
+                       if (max_length < 0)
+                               max_length = length_to_wrap;
+                       else if (max_length == 0) {
+                               if (check_next_node) {
+                                       insert_and_continue = TRUE;
+                                       goto check;
+                               }
+
+                               /* Break before the current node and continue. */
+                               webkit_dom_node_insert_before (
+                                       webkit_dom_node_get_parent_node (node),
+                                       WEBKIT_DOM_NODE (element),
+                                       node,
+                                       NULL);
+                               line_length = 0;
+                               continue;
+                       }
+
+                       /* Allow anchors to break on any character. */
+                       if (g_object_steal_data (G_OBJECT (node), "-x-evo-anchor-text"))
+                               offset = max_length;
+                       else {
+                               /* Find where we can line-break the node so that it
+                                * effectively fills the rest of current row. */
+                               offset = find_where_to_break_line (
+                                       WEBKIT_DOM_CHARACTER_DATA (node), max_length);
+
+                               /* When pressing delete on the end of line to concatenate
+                                * the last word from the line and first word from the
+                                * next line we will end with the second word split
+                                * somewhere in the middle (to be precise it will be
+                                * split after the last character that will fit on the
+                                * previous line. To avoid that we need to put the
+                                * concatenated word on the next line. */
+                               if (offset == -1 || check_next_node) {
+                                       WebKitDOMNode *prev_sibling;
+
+ check:
+                                       check_next_node = FALSE;
+                                       prev_sibling = webkit_dom_node_get_previous_sibling (node);
+                                       if (prev_sibling && e_editor_dom_is_selection_position_node 
(prev_sibling)) {
+                                               WebKitDOMNode *prev_br = NULL;
+
+                                               prev_sibling = webkit_dom_node_get_previous_sibling 
(prev_sibling);
+
+                                               /* Collapsed selection */
+                                               if (prev_sibling && e_editor_dom_is_selection_position_node 
(prev_sibling))
+                                                       prev_sibling = webkit_dom_node_get_previous_sibling 
(prev_sibling);
+
+                                               if (prev_sibling && WEBKIT_DOM_IS_HTML_BR_ELEMENT 
(prev_sibling) &&
+                                                   element_has_class (WEBKIT_DOM_ELEMENT (prev_sibling), 
"-x-evo-wrap-br")) {
+                                                       prev_br = prev_sibling;
+                                                       prev_sibling = webkit_dom_node_get_previous_sibling 
(prev_sibling);
+                                               }
+
+                                               if (prev_sibling && WEBKIT_DOM_IS_CHARACTER_DATA 
(prev_sibling)) {
+                                                       gchar *data;
+                                                       glong text_length, length = 0;
+
+                                                       data = webkit_dom_character_data_get_data (
+                                                               WEBKIT_DOM_CHARACTER_DATA (prev_sibling));
+                                                       text_length = webkit_dom_character_data_get_length (
+                                                               WEBKIT_DOM_CHARACTER_DATA (prev_sibling));
+
+                                                       /* Find the last character where we can break. */
+                                                       while (text_length - length > 0) {
+                                                               if (strchr (" ", data[text_length - length - 
1])) {
+                                                                       length++;
+                                                                       break;
+                                                               } else if (data[text_length - length - 1] == 
'-' &&
+                                                                          text_length - length > 1 &&
+                                                                          !strchr (" ", data[text_length - 
length - 2]))
+                                                                       break;
+                                                               length++;
+                                                       }
+
+                                                       if (text_length != length) {
+                                                               WebKitDOMNode *nd;
+
+                                                               webkit_dom_text_split_text (
+                                                                       WEBKIT_DOM_TEXT (prev_sibling),
+                                                                       text_length - length,
+                                                                       NULL);
+
+                                                               if ((nd = webkit_dom_node_get_next_sibling 
(prev_sibling))) {
+                                                                       gchar *nd_content;
+
+                                                                       nd_content = 
webkit_dom_node_get_text_content (nd);
+                                                                       if (nd_content && *nd_content) {
+                                                                               if (*nd_content == ' ')
+                                                                                       
mark_and_remove_leading_space (document, nd);
+
+                                                                               if 
(!webkit_dom_node_get_next_sibling (nd) &&
+                                                                                   g_str_has_suffix 
(nd_content, " "))
+                                                                                       
mark_and_remove_trailing_space (document, nd);
+
+                                                                               g_free (nd_content);
+                                                                       }
+
+                                                                       if (nd) {
+                                                                               if (prev_br)
+                                                                                       remove_node (prev_br);
+                                                                                
webkit_dom_node_insert_before (
+                                                                                       
webkit_dom_node_get_parent_node (nd),
+                                                                                       WEBKIT_DOM_NODE 
(element),
+                                                                                       nd,
+                                                                                       NULL);
+
+                                                                               offset = 0;
+                                                                               line_length = length;
+                                                                               continue;
+                                                                       }
+                                                               }
+                                                       }
+                                               }
+                                       }
+                                       if (insert_and_continue) {
+                                               webkit_dom_node_insert_before (
+                                                       webkit_dom_node_get_parent_node (node),
+                                                       WEBKIT_DOM_NODE (element),
+                                                       node,
+                                                       NULL);
+
+                                               offset = 0;
+                                               line_length = 0;
+                                               insert_and_continue = FALSE;
+                                               continue;
+                                       }
+
+                                       offset = offset != -1 ? offset : max_length;
+                               }
+                       }
+
+                       if (offset >= 0) {
+                               WebKitDOMNode *nd;
+
+                               if (offset != length_left && offset != 0) {
+                                       webkit_dom_text_split_text (
+                                               WEBKIT_DOM_TEXT (node), offset, NULL);
+
+                                       nd = webkit_dom_node_get_next_sibling (node);
+                               } else
+                                       nd = node;
+
+                               if (nd) {
+                                       gboolean no_sibling = FALSE;
+                                       gchar *nd_content;
+
+                                       nd_content = webkit_dom_node_get_text_content (nd);
+                                       if (nd_content && *nd_content) {
+                                               if (*nd_content == ' ')
+                                                       mark_and_remove_leading_space (document, nd);
+
+                                               if (!webkit_dom_node_get_next_sibling (nd) &&
+                                                   length_left <= length_to_wrap &&
+                                                   g_str_has_suffix (nd_content, " ")) {
+                                                       mark_and_remove_trailing_space (document, nd);
+                                                       no_sibling = TRUE;
+                                               }
+
+                                               g_free (nd_content);
+                                       }
+
+                                       if (!no_sibling)
+                                               webkit_dom_node_insert_before (
+                                                       webkit_dom_node_get_parent_node (node),
+                                                       WEBKIT_DOM_NODE (element),
+                                                       nd,
+                                                       NULL);
+
+                                       offset = 0;
+
+                                       nd_content = webkit_dom_node_get_text_content (nd);
+                                       if (!*nd_content)
+                                               remove_node (nd);
+                                       g_free (nd_content);
+
+                                       if (no_sibling)
+                                               node = NULL;
+                                       else
+                                               node = webkit_dom_node_get_next_sibling (
+                                                       WEBKIT_DOM_NODE (element));
+                               } else {
+                                       webkit_dom_node_append_child (
+                                               webkit_dom_node_get_parent_node (node),
+                                               WEBKIT_DOM_NODE (element),
+                                               NULL);
+                               }
+                       }
+                       if (node && WEBKIT_DOM_IS_CHARACTER_DATA (node))
+                               length_left = webkit_dom_character_data_get_length (
+                                       WEBKIT_DOM_CHARACTER_DATA (node));
+
+                       line_length = 0;
+                       compensated = FALSE;
+               }
+               line_length += length_left - offset;
+ next_node:
+               if (!node)
+                       break;
+
+               if (WEBKIT_DOM_IS_HTML_LI_ELEMENT (node)) {
+                       line_length = 0;
+                       compensated = FALSE;
+               }
+
+               /* Move to next node */
+               if (webkit_dom_node_has_child_nodes (node)) {
+                       node = webkit_dom_node_get_first_child (node);
+               } else if (webkit_dom_node_get_next_sibling (node)) {
+                       node = webkit_dom_node_get_next_sibling (node);
+               } else {
+                       WebKitDOMNode *tmp_parent;
+
+                       if (webkit_dom_node_is_equal_node (node, start_node))
+                               break;
+
+                       /* Find a next node that we can process. */
+                       tmp_parent = webkit_dom_node_get_parent_node (node);
+                       if (tmp_parent && webkit_dom_node_get_next_sibling (tmp_parent))
+                               node = webkit_dom_node_get_next_sibling (tmp_parent);
+                       else {
+                               WebKitDOMNode *tmp;
+
+                               tmp = tmp_parent;
+                               /* Find a node that is not a start node (that would mean
+                                * that we already processed the whole block) and it has
+                                * a sibling that we can process. */
+                               while (tmp && !webkit_dom_node_is_equal_node (tmp, start_node) &&
+                                      !webkit_dom_node_get_next_sibling (tmp)) {
+                                       tmp = webkit_dom_node_get_parent_node (tmp);
+                               }
+
+                               /* If we found a node to process, let's process its
+                                * sibling, otherwise give up. */
+                               if (tmp)
+                                       node = webkit_dom_node_get_next_sibling (tmp);
+                               else
+                                       break;
+                       }
+               }
+       }
+
+       last_child = webkit_dom_node_get_last_child (block_clone);
+       if (last_child && WEBKIT_DOM_IS_HTML_BR_ELEMENT (last_child) &&
+           element_has_class (WEBKIT_DOM_ELEMENT (last_child), "-x-evo-wrap-br"))
+               remove_node (last_child);
+
+       webkit_dom_node_normalize (block_clone);
+
+       node = webkit_dom_node_get_parent_node (block);
+       if (node) {
+               /* Replace block with wrapped one */
+               webkit_dom_node_replace_child (
+                       node, block_clone, block, NULL);
+       }
+
+       return WEBKIT_DOM_ELEMENT (block_clone);
+}
+
+void
+e_editor_dom_remove_wrapping_from_element (WebKitDOMElement *element)
+{
+       WebKitDOMNodeList *list = NULL;
+       gint ii, length;
+
+       g_return_if_fail (element != NULL);
+
+       list = webkit_dom_element_query_selector_all (
+               element, "br.-x-evo-wrap-br", NULL);
+       length = webkit_dom_node_list_get_length (list);
+       for (ii = 0; ii < length; ii++) {
+               WebKitDOMNode *node = webkit_dom_node_list_item (list, ii);
+               WebKitDOMNode *parent;
+
+               parent = e_editor_dom_get_parent_block_node_from_child (node);
+               if (!webkit_dom_element_has_attribute (WEBKIT_DOM_ELEMENT (parent), "data-user-wrapped"))
+                       remove_node (node);
+               g_object_unref (node);
+       }
+
+       g_clear_object (&list);
+
+       list = webkit_dom_element_query_selector_all (
+               element, "span[data-hidden-space]", NULL);
+       length = webkit_dom_node_list_get_length (list);
+       for (ii = 0; ii < length; ii++) {
+               WebKitDOMNode *hidden_space_node;
+               WebKitDOMNode *parent;
+
+               hidden_space_node = webkit_dom_node_list_item (list, ii);
+               parent = e_editor_dom_get_parent_block_node_from_child (hidden_space_node);
+               if (!webkit_dom_element_has_attribute (WEBKIT_DOM_ELEMENT (parent), "data-user-wrapped")) {
+                       webkit_dom_html_element_set_outer_text (
+                               WEBKIT_DOM_HTML_ELEMENT (hidden_space_node), " ", NULL);
+               }
+               g_object_unref (hidden_space_node);
+       }
+       g_clear_object (&list);
+
+       webkit_dom_node_normalize (WEBKIT_DOM_NODE (element));
+}
+
+void
+e_editor_dom_remove_quoting_from_element (WebKitDOMElement *element)
+{
+       gint ii, length;
+       WebKitDOMNodeList *list = NULL;
+
+       g_return_if_fail (element != NULL);
+
+       list = webkit_dom_element_query_selector_all (
+               element, "span.-x-evo-quoted", NULL);
+       length = webkit_dom_node_list_get_length (list);
+       for (ii = 0; ii < length; ii++) {
+               WebKitDOMNode *node = webkit_dom_node_list_item (list, ii);
+               remove_node (node);
+               g_object_unref (node);
+       }
+       g_clear_object (&list);
+
+       list = webkit_dom_element_query_selector_all (
+               element, "br.-x-evo-temp-br", NULL);
+       length = webkit_dom_node_list_get_length (list);
+       for (ii = 0; ii < length; ii++) {
+               WebKitDOMNode *node = webkit_dom_node_list_item (list, ii);
+               remove_node (node);
+               g_object_unref (node);
+       }
+       g_clear_object (&list);
+
+       webkit_dom_node_normalize (WEBKIT_DOM_NODE (element));
+}
+
+WebKitDOMElement *
+e_editor_dom_get_paragraph_element (EEditorPage *editor_page,
+                                   gint width,
+                                   gint offset)
+{
+       WebKitDOMDocument *document;
+       WebKitDOMElement *element;
+
+       g_return_val_if_fail (E_IS_EDITOR_PAGE (editor_page), NULL);
+
+       document = e_editor_page_get_document (editor_page);
+       element = webkit_dom_document_create_element (document, "p", NULL);
+       e_editor_dom_set_paragraph_style (editor_page, element, width, offset, NULL);
+
+       return element;
+}
+
+WebKitDOMElement *
+e_editor_dom_put_node_into_paragraph (EEditorPage *editor_page,
+                                     WebKitDOMNode *node,
+                                     gboolean with_input)
+{
+       WebKitDOMDocument *document;
+       WebKitDOMRange *range = NULL;
+       WebKitDOMElement *container;
+
+       g_return_val_if_fail (E_IS_EDITOR_PAGE (editor_page), NULL);
+
+       document = e_editor_page_get_document (editor_page);
+       range = webkit_dom_document_create_range (document);
+       container = e_editor_dom_get_paragraph_element (editor_page, -1, 0);
+       webkit_dom_range_select_node (range, node, NULL);
+       webkit_dom_range_surround_contents (range, WEBKIT_DOM_NODE (container), NULL);
+       /* We have to move caret position inside this container */
+       if (with_input)
+               dom_add_selection_markers_into_element_end (document, container, NULL, NULL);
+
+       g_clear_object (&range);
+
+       return container;
+}
+
+static gint
+selection_get_citation_level (WebKitDOMNode *node)
+{
+       WebKitDOMNode *parent = webkit_dom_node_get_parent_node (node);
+       gint level = 0;
+
+       while (parent && !WEBKIT_DOM_IS_HTML_BODY_ELEMENT (parent)) {
+               if (WEBKIT_DOM_IS_HTML_QUOTE_ELEMENT (parent) &&
+                   webkit_dom_element_has_attribute (WEBKIT_DOM_ELEMENT (parent), "type"))
+                       level++;
+
+               parent = webkit_dom_node_get_parent_node (parent);
+       }
+
+       return level;
+}
+
+WebKitDOMElement *
+e_editor_dom_wrap_paragraph_length (EEditorPage *editor_page,
+                                   WebKitDOMElement *paragraph,
+                                   gint length)
+{
+       g_return_val_if_fail (E_IS_EDITOR_PAGE (editor_page), NULL);
+       g_return_val_if_fail (WEBKIT_DOM_IS_ELEMENT (paragraph), NULL);
+       g_return_val_if_fail (length >= MINIMAL_PARAGRAPH_WIDTH, NULL);
+
+       return wrap_lines (editor_page, WEBKIT_DOM_NODE (paragraph), FALSE, length,
+               e_editor_page_get_word_wrap_length (editor_page));
+}
+
+/*
+ * e_html_editor_selection_wrap_lines:
+ * @selection: an #EEditorSelection
+ *
+ * Wraps all lines in current selection to be 71 characters long.
+ */
+
+void
+e_editor_dom_selection_wrap (EEditorPage *editor_page)
+{
+       WebKitDOMDocument *document;
+       WebKitDOMElement *selection_start_marker, *selection_end_marker;
+       WebKitDOMNode *block, *next_block;
+       EEditorHistoryEvent *ev = NULL;
+       EEditorUndoRedoManager *manager;
+       gboolean after_selection_end = FALSE, html_mode;
+       gint word_wrap_length;
+
+       g_return_if_fail (E_IS_EDITOR_PAGE (editor_page));
+
+       document = e_editor_page_get_document (editor_page);
+       word_wrap_length = e_editor_page_get_word_wrap_length (editor_page);
+
+       e_editor_dom_selection_save (editor_page);
+       selection_start_marker = webkit_dom_document_query_selector (
+               document, "span#-x-evo-selection-start-marker", NULL);
+       selection_end_marker = webkit_dom_document_query_selector (
+               document, "span#-x-evo-selection-end-marker", NULL);
+
+       /* If the selection was not saved, move it into the first child of body */
+       if (!selection_start_marker || !selection_end_marker) {
+               WebKitDOMHTMLElement *body;
+               WebKitDOMNode *child;
+
+               body = webkit_dom_document_get_body (document);
+               child = webkit_dom_node_get_first_child (WEBKIT_DOM_NODE (body));
+
+               dom_add_selection_markers_into_element_start (
+                       document,
+                       WEBKIT_DOM_ELEMENT (child),
+                       &selection_start_marker,
+                       &selection_end_marker);
+       }
+
+       manager = e_editor_page_get_undo_redo_manager (editor_page);
+       if (!e_editor_undo_redo_manager_is_operation_in_progress (manager)) {
+               ev = g_new0 (EEditorHistoryEvent, 1);
+               ev->type = HISTORY_WRAP;
+
+               e_editor_dom_selection_get_coordinates (editor_page,
+                       &ev->before.start.x,
+                       &ev->before.start.y,
+                       &ev->before.end.x,
+                       &ev->before.end.y);
+
+               ev->data.style.from = 1;
+               ev->data.style.to = 1;
+       }
+
+       block = e_editor_dom_get_parent_block_node_from_child (
+               WEBKIT_DOM_NODE (selection_start_marker));
+
+       html_mode = e_editor_page_get_html_mode (editor_page);
+
+       /* Process all blocks that are in the selection one by one */
+       while (block && !after_selection_end) {
+               gboolean quoted = FALSE;
+               gint citation_level, quote;
+               WebKitDOMElement *wrapped_paragraph;
+
+               next_block = webkit_dom_node_get_next_sibling (block);
+
+               /* Don't try to wrap the 'Normal' blocks as they are already wrapped and*/
+               /* also skip blocks that we already wrapped with this function. */
+               if ((!html_mode && webkit_dom_element_has_attribute (WEBKIT_DOM_ELEMENT (block), 
"data-evo-paragraph")) ||
+                   webkit_dom_element_has_attribute (WEBKIT_DOM_ELEMENT (block), "data-user-wrapped")) {
+                       block = next_block;
+                       continue;
+               }
+
+               if (webkit_dom_element_query_selector (
+                       WEBKIT_DOM_ELEMENT (block), "span.-x-evo-quoted", NULL)) {
+                       quoted = TRUE;
+                       e_editor_dom_remove_quoting_from_element (WEBKIT_DOM_ELEMENT (block));
+               }
+
+               if (!html_mode)
+                       e_editor_dom_remove_wrapping_from_element (WEBKIT_DOM_ELEMENT (block));
+
+               after_selection_end = webkit_dom_node_contains (
+                       block, WEBKIT_DOM_NODE (selection_end_marker));
+
+               citation_level = selection_get_citation_level (block);
+               quote = citation_level ? citation_level * 2 : 0;
+
+               wrapped_paragraph = e_editor_dom_wrap_paragraph_length (
+                       editor_page, WEBKIT_DOM_ELEMENT (block), word_wrap_length - quote);
+
+               webkit_dom_element_set_attribute (
+                       wrapped_paragraph, "data-user-wrapped", "", NULL);
+
+               if (quoted && !html_mode)
+                       e_editor_dom_quote_plain_text_element (editor_page, wrapped_paragraph);
+
+               block = next_block;
+       }
+
+       if (ev) {
+               e_editor_dom_selection_get_coordinates (editor_page,
+                       &ev->after.start.x,
+                       &ev->after.start.y,
+                       &ev->after.end.x,
+                       &ev->after.end.y);
+               e_editor_undo_redo_manager_insert_history_event (manager, ev);
+       }
+
+       e_editor_dom_selection_restore (editor_page);
+
+       e_editor_dom_force_spell_check_in_viewport (editor_page);
+}
+
+void
+e_editor_dom_wrap_paragraphs_in_document (EEditorPage *editor_page)
+{
+       WebKitDOMDocument *document;
+       WebKitDOMNodeList *list = NULL;
+       gint ii, length;
+
+       g_return_if_fail (E_IS_EDITOR_PAGE (editor_page));
+
+       document = e_editor_page_get_document (editor_page);
+       list = webkit_dom_document_query_selector_all (
+               document, "[data-evo-paragraph]:not(#-x-evo-input-start)", NULL);
+
+       length = webkit_dom_node_list_get_length (list);
+
+       for (ii = 0; ii < length; ii++) {
+               gint word_wrap_length, quote, citation_level;
+               WebKitDOMNode *node = webkit_dom_node_list_item (list, ii);
+
+               citation_level = selection_get_citation_level (node);
+               quote = citation_level ? citation_level * 2 : 0;
+               word_wrap_length = e_editor_page_get_word_wrap_length (editor_page);
+
+               if (node_is_list (node)) {
+                       WebKitDOMNode *item = webkit_dom_node_get_first_child (node);
+
+                       while (item && WEBKIT_DOM_IS_HTML_LI_ELEMENT (item)) {
+                               e_editor_dom_wrap_paragraph_length (
+                                       editor_page, WEBKIT_DOM_ELEMENT (item), word_wrap_length - quote);
+                               item = webkit_dom_node_get_next_sibling (item);
+                       }
+               } else {
+                       e_editor_dom_wrap_paragraph_length (
+                               editor_page, WEBKIT_DOM_ELEMENT (node), word_wrap_length - quote);
+               }
+               g_object_unref (node);
+       }
+       g_clear_object (&list);
+}
+
+WebKitDOMElement *
+e_editor_dom_wrap_paragraph (EEditorPage *editor_page,
+                            WebKitDOMElement *paragraph)
+{
+       gint indentation_level, citation_level, quote;
+       gint word_wrap_length, final_width, offset = 0;
+
+       g_return_val_if_fail (E_IS_EDITOR_PAGE (editor_page), NULL);
+       g_return_val_if_fail (WEBKIT_DOM_IS_ELEMENT (paragraph), NULL);
+
+       indentation_level = get_indentation_level (paragraph);
+       citation_level = selection_get_citation_level (WEBKIT_DOM_NODE (paragraph));
+
+       if (node_is_list_or_item (WEBKIT_DOM_NODE (paragraph))) {
+               gint list_level = get_list_level (WEBKIT_DOM_NODE (paragraph));
+               indentation_level = 0;
+
+               if (list_level > 0)
+                       offset = list_level * -SPACES_PER_LIST_LEVEL;
+               else
+                       offset = -SPACES_PER_LIST_LEVEL;
+       }
+
+       quote = citation_level ? citation_level * 2 : 0;
+
+       word_wrap_length = e_editor_page_get_word_wrap_length (editor_page);
+       final_width = word_wrap_length - quote + offset;
+       final_width -= SPACES_PER_INDENTATION * indentation_level;
+
+       return e_editor_dom_wrap_paragraph_length (
+               editor_page, WEBKIT_DOM_ELEMENT (paragraph), final_width);
+}
+
+static gboolean
+get_has_style (EEditorPage *editor_page,
+               const gchar *style_tag)
+{
+       WebKitDOMNode *node;
+       WebKitDOMElement *element;
+       WebKitDOMRange *range = NULL;
+       gboolean result;
+       gint tag_len;
+
+       g_return_val_if_fail (E_IS_EDITOR_PAGE (editor_page), FALSE);
+
+       range = e_editor_dom_get_current_range (editor_page);
+       if (!range)
+               return FALSE;
+
+       node = webkit_dom_range_get_start_container (range, NULL);
+       if (WEBKIT_DOM_IS_ELEMENT (node))
+               element = WEBKIT_DOM_ELEMENT (node);
+       else
+               element = webkit_dom_node_get_parent_element (node);
+       g_clear_object (&range);
+
+       tag_len = strlen (style_tag);
+       result = FALSE;
+       while (!result && element) {
+               gchar *element_tag;
+               gboolean accept_citation = FALSE;
+
+               element_tag = webkit_dom_element_get_tag_name (element);
+
+               if (g_ascii_strncasecmp (style_tag, "citation", 8) == 0) {
+                       accept_citation = TRUE;
+                       result = WEBKIT_DOM_IS_HTML_QUOTE_ELEMENT (element);
+                       if (element_has_class (element, "-x-evo-indented"))
+                               result = FALSE;
+               } else {
+                       result = ((tag_len == strlen (element_tag)) &&
+                               (g_ascii_strncasecmp (element_tag, style_tag, tag_len) == 0));
+               }
+
+               /* Special case: <blockquote type=cite> marks quotation, while
+                * just <blockquote> is used for indentation. If the <blockquote>
+                * has type=cite, then ignore it unless style_tag is "citation" */
+               if (result && WEBKIT_DOM_IS_HTML_QUOTE_ELEMENT (element)) {
+                       if (webkit_dom_element_has_attribute (element, "type")) {
+                               gchar *type = webkit_dom_element_get_attribute (element, "type");
+                               if (!accept_citation && (type && g_ascii_strncasecmp (type, "cite", 4) == 0)) 
{
+                                       result = FALSE;
+                               }
+                               g_free (type);
+                       } else {
+                               if (accept_citation)
+                                       result = FALSE;
+                       }
+               }
+
+               g_free (element_tag);
+
+               if (result)
+                       break;
+
+               element = webkit_dom_node_get_parent_element (
+                       WEBKIT_DOM_NODE (element));
+       }
+
+       return result;
+}
+
+typedef gboolean (*IsRightFormatNodeFunc) (WebKitDOMElement *element);
+
+static gboolean
+dom_selection_is_font_format (EEditorPage *editor_page,
+                              IsRightFormatNodeFunc func,
+                              gboolean *previous_value)
+{
+       WebKitDOMDocument *document;
+       WebKitDOMDOMWindow *dom_window = NULL;
+       WebKitDOMDOMSelection *dom_selection = NULL;
+       WebKitDOMNode *start, *end, *sibling;
+       WebKitDOMRange *range = NULL;
+       gboolean ret_val = FALSE;
+
+       g_return_val_if_fail (E_IS_EDITOR_PAGE (editor_page), FALSE);
+
+       if (!e_editor_page_get_html_mode (editor_page))
+               goto out;
+
+       document = e_editor_page_get_document (editor_page);
+       dom_window = webkit_dom_document_get_default_view (document);
+       dom_selection = webkit_dom_dom_window_get_selection (dom_window);
+       g_clear_object (&dom_window);
+
+       if (!webkit_dom_dom_selection_get_range_count (dom_selection))
+               goto out;
+
+       range = webkit_dom_dom_selection_get_range_at (dom_selection, 0, NULL);
+       if (!range)
+               goto out;
+
+       if (webkit_dom_range_get_collapsed (range, NULL) && previous_value) {
+               WebKitDOMNode *node;
+               gchar* text_content;
+
+               node = webkit_dom_range_get_common_ancestor_container (range, NULL);
+               /* If we are changing the format of block we have to re-set the
+                * format property, otherwise it will be turned off because of no
+                * text in block. */
+               text_content = webkit_dom_node_get_text_content (node);
+               if (g_strcmp0 (text_content, "") == 0) {
+                       g_free (text_content);
+                       ret_val = *previous_value;
+                       goto out;
+               }
+               g_free (text_content);
+       }
+
+       /* Range without start or end point is a wrong range. */
+       start = webkit_dom_range_get_start_container (range, NULL);
+       end = webkit_dom_range_get_end_container (range, NULL);
+       if (!start || !end)
+               goto out;
+
+       if (WEBKIT_DOM_IS_TEXT (start))
+               start = webkit_dom_node_get_parent_node (start);
+       while (start && WEBKIT_DOM_IS_ELEMENT (start) && !WEBKIT_DOM_IS_HTML_BODY_ELEMENT (start)) {
+               /* Find the start point's parent node with given formatting. */
+               if (func (WEBKIT_DOM_ELEMENT (start))) {
+                       ret_val = TRUE;
+                       break;
+               }
+               start = webkit_dom_node_get_parent_node (start);
+       }
+
+       /* Start point doesn't have the given formatting. */
+       if (!ret_val)
+               goto out;
+
+       /* If the selection is collapsed, we can return early. */
+       if (webkit_dom_range_get_collapsed (range, NULL))
+               goto out;
+
+       /* The selection is in the same node and that node is supposed to have
+        * the same formatting (otherwise it is split up with formatting element. */
+       if (webkit_dom_node_is_same_node (
+               webkit_dom_range_get_start_container (range, NULL),
+               webkit_dom_range_get_end_container (range, NULL)))
+               goto out;
+
+       ret_val = FALSE;
+
+       if (WEBKIT_DOM_IS_TEXT (end))
+               end = webkit_dom_node_get_parent_node (end);
+       while (end && WEBKIT_DOM_IS_ELEMENT (end) && !WEBKIT_DOM_IS_HTML_BODY_ELEMENT (end)) {
+               /* Find the end point's parent node with given formatting. */
+               if (func (WEBKIT_DOM_ELEMENT (end))) {
+                       ret_val = TRUE;
+                       break;
+               }
+               end = webkit_dom_node_get_parent_node (end);
+       }
+
+       if (!ret_val)
+               goto out;
+
+       ret_val = FALSE;
+
+       /* Now go between the end points and check the inner nodes for format validity. */
+       sibling = start;
+       while ((sibling = webkit_dom_node_get_next_sibling (sibling))) {
+               if (webkit_dom_node_is_same_node (sibling, end)) {
+                       ret_val = TRUE;
+                       goto out;
+               }
+
+               if (WEBKIT_DOM_IS_TEXT (sibling))
+                       goto out;
+               else if (func (WEBKIT_DOM_ELEMENT (sibling)))
+                       continue;
+               else if (webkit_dom_node_get_first_child (sibling)) {
+                       WebKitDOMNode *first_child;
+
+                       first_child = webkit_dom_node_get_first_child (sibling);
+                       if (!webkit_dom_node_get_next_sibling (first_child))
+                               if (WEBKIT_DOM_IS_ELEMENT (first_child) && func (WEBKIT_DOM_ELEMENT 
(first_child)))
+                                       continue;
+                               else
+                                       goto out;
+                       else
+                               goto out;
+               } else
+                       goto out;
+       }
+
+       sibling = end;
+       while ((sibling = webkit_dom_node_get_previous_sibling (sibling))) {
+               if (webkit_dom_node_is_same_node (sibling, start))
+                       break;
+
+               if (WEBKIT_DOM_IS_TEXT (sibling))
+                       goto out;
+               else if (func (WEBKIT_DOM_ELEMENT (sibling)))
+                       continue;
+               else if (webkit_dom_node_get_first_child (sibling)) {
+                       WebKitDOMNode *first_child;
+
+                       first_child = webkit_dom_node_get_first_child (sibling);
+                       if (!webkit_dom_node_get_next_sibling (first_child))
+                               if (WEBKIT_DOM_IS_ELEMENT (first_child) && func (WEBKIT_DOM_ELEMENT 
(first_child)))
+                                       continue;
+                               else
+                                       goto out;
+                       else
+                               goto out;
+               } else
+                       goto out;
+       }
+
+       ret_val = TRUE;
+ out:
+       g_clear_object (&range);
+       g_clear_object (&dom_selection);
+
+       return ret_val;
+}
+
+static gboolean
+is_underline_element (WebKitDOMElement *element)
+{
+       if (!element || !WEBKIT_DOM_IS_ELEMENT (element))
+               return FALSE;
+
+       return element_has_tag (element, "u");
+}
+
+/*
+ * e_html_editor_selection_is_underline:
+ * @selection: an #EEditorSelection
+ *
+ * Returns whether current selection or letter at current cursor position
+ * is underlined.
+ *
+ * Returns @TRUE when selection is underlined, @FALSE otherwise.
+ */
+gboolean
+e_editor_dom_selection_is_underline (EEditorPage *editor_page)
+{
+       gboolean is_underline;
+
+       g_return_val_if_fail (E_IS_EDITOR_PAGE (editor_page), FALSE);
+
+       is_underline = e_editor_page_get_underline (editor_page);
+       is_underline = dom_selection_is_font_format (
+               editor_page, (IsRightFormatNodeFunc) is_underline_element, &is_underline);
+
+       return is_underline;
+}
+
+static WebKitDOMElement *
+set_font_style (WebKitDOMDocument *document,
+                const gchar *element_name,
+                gboolean value)
+{
+       WebKitDOMElement *element;
+       WebKitDOMNode *parent;
+
+       element = webkit_dom_document_get_element_by_id (document, "-x-evo-selection-end-marker");
+       parent = webkit_dom_node_get_parent_node (WEBKIT_DOM_NODE (element));
+       if (value) {
+               WebKitDOMNode *node;
+               WebKitDOMElement *el;
+               gchar *name;
+
+               el = webkit_dom_document_create_element (document, element_name, NULL);
+               webkit_dom_html_element_set_inner_text (
+                       WEBKIT_DOM_HTML_ELEMENT (el), UNICODE_ZERO_WIDTH_SPACE, NULL);
+
+               node = webkit_dom_node_get_previous_sibling (WEBKIT_DOM_NODE (element));
+               webkit_dom_node_append_child (
+                       WEBKIT_DOM_NODE (el), node, NULL);
+               name = webkit_dom_node_get_local_name (parent);
+               if (g_strcmp0 (name, element_name) == 0 && g_strcmp0 (name, "font") != 0)
+                       webkit_dom_node_insert_before (
+                               webkit_dom_node_get_parent_node (parent),
+                               WEBKIT_DOM_NODE (el),
+                               webkit_dom_node_get_next_sibling (parent),
+                               NULL);
+               else
+                       webkit_dom_node_insert_before (
+                               parent,
+                               WEBKIT_DOM_NODE (el),
+                               WEBKIT_DOM_NODE (element),
+                               NULL);
+               g_free (name);
+
+               webkit_dom_node_append_child (
+                       WEBKIT_DOM_NODE (el), WEBKIT_DOM_NODE (element), NULL);
+
+               return el;
+       } else {
+               gboolean no_sibling;
+               WebKitDOMNode *node, *sibling;
+
+               node = webkit_dom_node_get_previous_sibling (WEBKIT_DOM_NODE (element));
+
+               /* Turning the formatting in the middle of element. */
+               sibling = webkit_dom_node_get_next_sibling (WEBKIT_DOM_NODE (element));
+               no_sibling = sibling &&
+                       !WEBKIT_DOM_IS_HTML_BR_ELEMENT (sibling) &&
+                       !webkit_dom_node_get_next_sibling (sibling);
+
+               if (no_sibling) {
+                       WebKitDOMNode *clone;
+                       WebKitDOMNode *sibling;
+
+                       clone = webkit_dom_node_clone_node_with_error (
+                               WEBKIT_DOM_NODE (parent), FALSE, NULL);
+
+                       while ((sibling = webkit_dom_node_get_next_sibling (WEBKIT_DOM_NODE (element))))
+                               webkit_dom_node_insert_before (
+                                       clone,
+                                       sibling,
+                                       webkit_dom_node_get_first_child (clone),
+                                       NULL);
+
+                       webkit_dom_node_insert_before (
+                               webkit_dom_node_get_parent_node (parent),
+                               clone,
+                               webkit_dom_node_get_next_sibling (WEBKIT_DOM_NODE (parent)),
+                               NULL);
+               }
+
+               webkit_dom_node_insert_before (
+                       webkit_dom_node_get_parent_node (parent),
+                       WEBKIT_DOM_NODE (element),
+                       webkit_dom_node_get_next_sibling (parent),
+                       NULL);
+               webkit_dom_node_insert_before (
+                       webkit_dom_node_get_parent_node (parent),
+                       node,
+                       webkit_dom_node_get_next_sibling (parent),
+                       NULL);
+
+               if (WEBKIT_DOM_IS_HTML_BR_ELEMENT (sibling) && !no_sibling) {
+                       webkit_dom_node_insert_before (
+                               webkit_dom_node_get_parent_node (parent),
+                               node,
+                               webkit_dom_node_get_next_sibling (parent),
+                               NULL);
+               }
+
+               webkit_dom_html_element_insert_adjacent_text (
+                       WEBKIT_DOM_HTML_ELEMENT (parent),
+                       "afterend",
+                       UNICODE_ZERO_WIDTH_SPACE,
+                       NULL);
+
+               remove_node_if_empty (parent);
+       }
+
+       return NULL;
+}
+
+static void
+selection_set_font_style (EEditorPage *editor_page,
+                          EContentEditorCommand command,
+                          gboolean value)
+{
+       EEditorHistoryEvent *ev = NULL;
+       EEditorUndoRedoManager *manager;
+
+       g_return_if_fail (E_IS_EDITOR_PAGE (editor_page));
+
+       e_editor_dom_selection_save (editor_page);
+
+       manager = e_editor_page_get_undo_redo_manager (editor_page);
+       if (!e_editor_undo_redo_manager_is_operation_in_progress (manager)) {
+               ev = g_new0 (EEditorHistoryEvent, 1);
+               if (command == E_CONTENT_EDITOR_COMMAND_BOLD)
+                       ev->type = HISTORY_BOLD;
+               else if (command == E_CONTENT_EDITOR_COMMAND_ITALIC)
+                       ev->type = HISTORY_ITALIC;
+               else if (command == E_CONTENT_EDITOR_COMMAND_UNDERLINE)
+                       ev->type = HISTORY_UNDERLINE;
+               else if (command == E_CONTENT_EDITOR_COMMAND_STRIKETHROUGH)
+                       ev->type = HISTORY_STRIKETHROUGH;
+
+               e_editor_dom_selection_get_coordinates (editor_page,
+                       &ev->before.start.x,
+                       &ev->before.start.y,
+                       &ev->before.end.x,
+                       &ev->before.end.y);
+
+               ev->data.style.from = !value;
+               ev->data.style.to = value;
+       }
+
+       if (e_editor_dom_selection_is_collapsed (editor_page)) {
+               const gchar *element_name = NULL;
+
+               if (command == E_CONTENT_EDITOR_COMMAND_BOLD)
+                       element_name = "b";
+               else if (command == E_CONTENT_EDITOR_COMMAND_ITALIC)
+                       element_name = "i";
+               else if (command == E_CONTENT_EDITOR_COMMAND_UNDERLINE)
+                       element_name = "u";
+               else if (command == E_CONTENT_EDITOR_COMMAND_STRIKETHROUGH)
+                       element_name = "strike";
+
+               if (element_name)
+                       set_font_style (e_editor_page_get_document (editor_page), element_name, value);
+               e_editor_dom_selection_restore (editor_page);
+
+               goto exit;
+       }
+       e_editor_dom_selection_restore (editor_page);
+
+       e_editor_dom_exec_command (editor_page, command, NULL);
+exit:
+       if (ev) {
+               e_editor_dom_selection_get_coordinates (editor_page,
+                       &ev->after.start.x,
+                       &ev->after.start.y,
+                       &ev->after.end.x,
+                       &ev->after.end.y);
+               e_editor_undo_redo_manager_insert_history_event (manager, ev);
+       }
+
+       e_editor_dom_force_spell_check_for_current_paragraph (editor_page);
+}
+
+/*
+ * e_html_editor_selection_set_underline:
+ * @selection: an #EEditorSelection
+ * @underline: @TRUE to enable underline, @FALSE to disable
+ *
+ * Toggles underline formatting of current selection or letter at current
+ * cursor position, depending on whether @underline is @TRUE or @FALSE.
+ */
+void
+e_editor_dom_selection_set_underline (EEditorPage *editor_page,
+                                     gboolean underline)
+{
+       g_return_if_fail (E_IS_EDITOR_PAGE (editor_page));
+
+       if (e_editor_dom_selection_is_underline (editor_page) == underline)
+               return;
+
+       selection_set_font_style (
+               editor_page, E_CONTENT_EDITOR_COMMAND_UNDERLINE, underline);
+}
+
+static gboolean
+is_subscript_element (WebKitDOMElement *element)
+{
+       if (!element || !WEBKIT_DOM_IS_ELEMENT (element))
+               return FALSE;
+
+       return element_has_tag (element, "sub");
+}
+
+/*
+ * e_html_editor_selection_is_subscript:
+ * @selection: an #EEditorSelection
+ *
+ * Returns whether current selection or letter at current cursor position
+ * is in subscript.
+ *
+ * Returns @TRUE when selection is in subscript, @FALSE otherwise.
+ */
+gboolean
+e_editor_dom_selection_is_subscript (EEditorPage *editor_page)
+{
+       g_return_val_if_fail (E_IS_EDITOR_PAGE (editor_page), FALSE);
+
+       return dom_selection_is_font_format (
+               editor_page, (IsRightFormatNodeFunc) is_subscript_element, NULL);
+}
+
+/*
+ * e_html_editor_selection_set_subscript:
+ * @selection: an #EEditorSelection
+ * @subscript: @TRUE to enable subscript, @FALSE to disable
+ *
+ * Toggles subscript of current selection or letter at current cursor position,
+ * depending on whether @subscript is @TRUE or @FALSE.
+ */
+void
+e_editor_dom_selection_set_subscript (EEditorPage *editor_page,
+                                     gboolean subscript)
+{
+       g_return_if_fail (E_IS_EDITOR_PAGE (editor_page));
+
+       if (e_editor_dom_selection_is_subscript (editor_page) == subscript)
+               return;
+
+       e_editor_dom_exec_command (editor_page, E_CONTENT_EDITOR_COMMAND_SUBSCRIPT, NULL);
+}
+
+static gboolean
+is_superscript_element (WebKitDOMElement *element)
+{
+       if (!element || !WEBKIT_DOM_IS_ELEMENT (element))
+               return FALSE;
+
+       return element_has_tag (element, "sup");
+}
+
+/*
+ * e_html_editor_selection_is_superscript:
+ * @selection: an #EEditorSelection
+ *
+ * Returns whether current selection or letter at current cursor position
+ * is in superscript.
+ *
+ * Returns @TRUE when selection is in superscript, @FALSE otherwise.
+ */
+gboolean
+e_editor_dom_selection_is_superscript (EEditorPage *editor_page)
+{
+       g_return_val_if_fail (E_IS_EDITOR_PAGE (editor_page), FALSE);
+
+       return dom_selection_is_font_format (
+               editor_page, (IsRightFormatNodeFunc) is_superscript_element, NULL);
+}
+
+/*
+ * e_html_editor_selection_set_superscript:
+ * @selection: an #EEditorSelection
+ * @superscript: @TRUE to enable superscript, @FALSE to disable
+ *
+ * Toggles superscript of current selection or letter at current cursor position,
+ * depending on whether @superscript is @TRUE or @FALSE.
+ */
+void
+e_editor_dom_selection_set_superscript (EEditorPage *editor_page,
+                                       gboolean superscript)
+{
+       g_return_if_fail (E_IS_EDITOR_PAGE (editor_page));
+
+       if (e_editor_dom_selection_is_superscript (editor_page) == superscript)
+               return;
+
+       e_editor_dom_exec_command (editor_page, E_CONTENT_EDITOR_COMMAND_SUPERSCRIPT, NULL);
+}
+
+static gboolean
+is_strikethrough_element (WebKitDOMElement *element)
+{
+       if (!element || !WEBKIT_DOM_IS_ELEMENT (element))
+               return FALSE;
+
+       return element_has_tag (element, "strike");
+}
+
+/*
+ * e_html_editor_selection_is_strikethrough:
+ * @selection: an #EEditorSelection
+ *
+ * Returns whether current selection or letter at current cursor position
+ * is striked through.
+ *
+ * Returns @TRUE when selection is striked through, @FALSE otherwise.
+ */
+gboolean
+e_editor_dom_selection_is_strikethrough (EEditorPage *editor_page)
+{
+       gboolean is_strikethrough;
+
+       g_return_val_if_fail (E_IS_EDITOR_PAGE (editor_page), FALSE);
+
+       is_strikethrough = e_editor_page_get_strikethrough (editor_page);
+       is_strikethrough = dom_selection_is_font_format (
+               editor_page, (IsRightFormatNodeFunc) is_strikethrough_element, &is_strikethrough);
+
+       return is_strikethrough;
+}
+
+/*
+ * e_html_editor_selection_set_strikethrough:
+ * @selection: an #EEditorSelection
+ * @strikethrough: @TRUE to enable strikethrough, @FALSE to disable
+ *
+ * Toggles strike through formatting of current selection or letter at current
+ * cursor position, depending on whether @strikethrough is @TRUE or @FALSE.
+ */
+void
+e_editor_dom_selection_set_strikethrough (EEditorPage *editor_page,
+                                         gboolean strikethrough)
+{
+       g_return_if_fail (E_IS_EDITOR_PAGE (editor_page));
+
+       if (e_editor_dom_selection_is_strikethrough (editor_page) == strikethrough)
+               return;
+
+       selection_set_font_style (
+               editor_page, E_CONTENT_EDITOR_COMMAND_STRIKETHROUGH, strikethrough);
+}
+
+static gboolean
+is_monospace_element (WebKitDOMElement *element)
+{
+       gchar *value;
+       gboolean ret_val = FALSE;
+
+       if (!element)
+               return FALSE;
+
+       if (!WEBKIT_DOM_IS_HTML_FONT_ELEMENT (element))
+               return FALSE;
+
+       value = webkit_dom_element_get_attribute (element, "face");
+       if (value && g_strcmp0 (value, "monospace") == 0)
+               ret_val = TRUE;
+
+       g_free (value);
+
+       return ret_val;
+}
+
+/*
+ * e_html_editor_selection_is_monospaced:
+ * @selection: an #EEditorSelection
+ *
+ * Returns whether current selection or letter at current cursor position
+ * is monospaced.
+ *
+ * Returns @TRUE when selection is monospaced, @FALSE otherwise.
+ */
+gboolean
+e_editor_dom_selection_is_monospace (EEditorPage *editor_page)
+{
+       gboolean is_monospace;
+
+       g_return_val_if_fail (E_IS_EDITOR_PAGE (editor_page), FALSE);
+
+       is_monospace = e_editor_page_get_monospace (editor_page);
+       is_monospace = dom_selection_is_font_format (
+               editor_page, (IsRightFormatNodeFunc) is_monospace_element, &is_monospace);
+
+       return is_monospace;
+}
+
+static void
+monospace_selection (EEditorPage *editor_page,
+                     WebKitDOMElement *monospace_element)
+{
+       WebKitDOMDocument *document;
+       WebKitDOMElement *selection_start_marker, *selection_end_marker;
+       WebKitDOMNode *sibling, *node, *monospace, *block;
+       WebKitDOMNodeList *list = NULL;
+       gboolean selection_end = FALSE;
+       gboolean first = TRUE;
+       gint length, ii;
+
+       g_return_if_fail (E_IS_EDITOR_PAGE (editor_page));
+
+       document = e_editor_page_get_document (editor_page);
+       e_editor_dom_selection_save (editor_page);
+
+       selection_start_marker = webkit_dom_document_get_element_by_id (
+               document, "-x-evo-selection-start-marker");
+       selection_end_marker = webkit_dom_document_get_element_by_id (
+               document, "-x-evo-selection-end-marker");
+
+       block = WEBKIT_DOM_NODE (get_parent_block_element (WEBKIT_DOM_NODE (selection_start_marker)));
+
+       monospace = WEBKIT_DOM_NODE (monospace_element);
+       node = WEBKIT_DOM_NODE (selection_start_marker);
+       /* Go through first block in selection. */
+       while (block && node && !webkit_dom_node_is_same_node (block, node)) {
+               if (webkit_dom_node_get_next_sibling (node)) {
+                       /* Prepare the monospaced element. */
+                       monospace = webkit_dom_node_insert_before (
+                               webkit_dom_node_get_parent_node (node),
+                               first ? monospace : webkit_dom_node_clone_node_with_error (monospace, FALSE, 
NULL),
+                               first ? node : webkit_dom_node_get_next_sibling (node),
+                               NULL);
+               } else
+                       break;
+
+               /* Move the nodes into monospaced element. */
+               while (((sibling = webkit_dom_node_get_next_sibling (monospace)))) {
+                       webkit_dom_node_append_child (monospace, sibling, NULL);
+                       if (webkit_dom_node_is_same_node (WEBKIT_DOM_NODE (selection_end_marker), sibling)) {
+                               selection_end = TRUE;
+                               break;
+                       }
+               }
+
+               node = webkit_dom_node_get_parent_node (monospace);
+               first = FALSE;
+       }
+
+       /* Just one block was selected. */
+       if (selection_end)
+               goto out;
+
+       /* Middle blocks (blocks not containing the end of the selection. */
+       block = webkit_dom_node_get_next_sibling (block);
+       while (block && !selection_end) {
+               WebKitDOMNode *next_block;
+
+               selection_end = webkit_dom_node_contains (
+                       block, WEBKIT_DOM_NODE (selection_end_marker));
+
+               if (selection_end)
+                       break;
+
+               next_block = webkit_dom_node_get_next_sibling (block);
+
+               monospace = webkit_dom_node_insert_before (
+                       block,
+                       webkit_dom_node_clone_node_with_error (monospace, FALSE, NULL),
+                       webkit_dom_node_get_first_child (block),
+                       NULL);
+
+               while (((sibling = webkit_dom_node_get_next_sibling (monospace))))
+                       webkit_dom_node_append_child (monospace, sibling, NULL);
+
+               block = next_block;
+       }
+
+       /* Block containing the end of selection. */
+       node = WEBKIT_DOM_NODE (selection_end_marker);
+       while (block && node && !webkit_dom_node_is_same_node (block, node)) {
+               monospace = webkit_dom_node_insert_before (
+                       webkit_dom_node_get_parent_node (node),
+                       webkit_dom_node_clone_node_with_error (monospace, FALSE, NULL),
+                       webkit_dom_node_get_next_sibling (node),
+                       NULL);
+
+               while (((sibling = webkit_dom_node_get_previous_sibling (monospace)))) {
+                       webkit_dom_node_insert_before (
+                               monospace,
+                               sibling,
+                               webkit_dom_node_get_first_child (monospace),
+                               NULL);
+               }
+
+               node = webkit_dom_node_get_parent_node (monospace);
+       }
+ out:
+       /* Merge all the monospace elements inside other monospace elements. */
+       list = webkit_dom_document_query_selector_all (
+               document, "font[face=monospace] > font[face=monospace]", NULL);
+       length = webkit_dom_node_list_get_length (list);
+       for (ii = 0; ii < length; ii++) {
+               WebKitDOMNode *item;
+               WebKitDOMNode *child;
+
+               item = webkit_dom_node_list_item (list, ii);
+               while ((child = webkit_dom_node_get_first_child (item))) {
+                       webkit_dom_node_insert_before (
+                               webkit_dom_node_get_parent_node (item),
+                               child,
+                               item,
+                               NULL);
+               }
+               remove_node (item);
+               g_object_unref (item);
+       }
+       g_clear_object (&list);
+
+       /* Merge all the adjacent monospace elements. */
+       list = webkit_dom_document_query_selector_all (
+               document, "font[face=monospace] + font[face=monospace]", NULL);
+       length = webkit_dom_node_list_get_length (list);
+       for (ii = 0; ii < length; ii++) {
+               WebKitDOMNode *item;
+               WebKitDOMNode *child;
+
+               item = webkit_dom_node_list_item (list, ii);
+               /* The + CSS selector will return some false positives as it doesn't
+                * take text between elements into account so it will return this:
+                * <font face="monospace">xx</font>yy<font face="monospace">zz</font>
+                * as valid, but it isn't so we have to check if previous node
+                * is indeed element or not. */
+               if (WEBKIT_DOM_IS_ELEMENT (webkit_dom_node_get_previous_sibling (item))) {
+                       while ((child = webkit_dom_node_get_first_child (item))) {
+                               webkit_dom_node_append_child (
+                                       webkit_dom_node_get_previous_sibling (item), child, NULL);
+                       }
+                       remove_node (item);
+               }
+               g_object_unref (item);
+       }
+       g_clear_object (&list);
+
+       e_editor_dom_selection_restore (editor_page);
+}
+
+static void
+unmonospace_selection (EEditorPage *editor_page)
+{
+       WebKitDOMDocument *document;
+       WebKitDOMElement *selection_start_marker;
+       WebKitDOMElement *selection_end_marker;
+       WebKitDOMElement *selection_start_clone;
+       WebKitDOMElement *selection_end_clone;
+       WebKitDOMNode *sibling, *node;
+       WebKitDOMNode *block, *clone, *monospace;
+       gboolean selection_end = FALSE;
+
+       g_return_if_fail (E_IS_EDITOR_PAGE (editor_page));
+
+       document = e_editor_page_get_document (editor_page);
+       e_editor_dom_selection_save (editor_page);
+
+       selection_start_marker = webkit_dom_document_get_element_by_id (
+               document, "-x-evo-selection-start-marker");
+       selection_end_marker = webkit_dom_document_get_element_by_id (
+               document, "-x-evo-selection-end-marker");
+
+       block = WEBKIT_DOM_NODE (get_parent_block_element (WEBKIT_DOM_NODE (selection_start_marker)));
+
+       node = WEBKIT_DOM_NODE (selection_start_marker);
+       monospace = webkit_dom_node_get_parent_node (WEBKIT_DOM_NODE (selection_start_marker));
+       while (monospace && !is_monospace_element (WEBKIT_DOM_ELEMENT (monospace)))
+               monospace = webkit_dom_node_get_parent_node (monospace);
+
+       /* No monospaced element was found as a parent of selection start node. */
+       if (!monospace)
+               goto out;
+
+       /* Make a clone of current monospaced element. */
+       clone = webkit_dom_node_clone_node_with_error (monospace, TRUE, NULL);
+
+       /* First block */
+       /* Remove all the nodes that are after the selection start point as they
+        * will be in the cloned node. */
+       while (monospace && node && !webkit_dom_node_is_same_node (monospace, node)) {
+               WebKitDOMNode *tmp;
+               while (((sibling = webkit_dom_node_get_next_sibling (node))))
+                       remove_node (sibling);
+
+               tmp = webkit_dom_node_get_parent_node (node);
+               if (webkit_dom_node_get_next_sibling (node))
+                       remove_node (node);
+               node = tmp;
+       }
+
+       selection_start_clone = webkit_dom_element_query_selector (
+               WEBKIT_DOM_ELEMENT (clone), "#-x-evo-selection-start-marker", NULL);
+       selection_end_clone = webkit_dom_element_query_selector (
+               WEBKIT_DOM_ELEMENT (clone), "#-x-evo-selection-end-marker", NULL);
+
+       /* No selection start node in the block where it is supposed to be, return. */
+       if (!selection_start_clone)
+               goto out;
+
+       /* Remove all the nodes until we hit the selection start point as these
+        * nodes will stay monospaced and they are already in original element. */
+       node = webkit_dom_node_get_first_child (clone);
+       while (node) {
+               WebKitDOMNode *next_sibling;
+
+               next_sibling = webkit_dom_node_get_next_sibling (node);
+               if (webkit_dom_node_get_first_child (node)) {
+                       if (webkit_dom_node_contains (node, WEBKIT_DOM_NODE (selection_start_clone))) {
+                               node = webkit_dom_node_get_first_child (node);
+                               continue;
+                       } else
+                               remove_node (node);
+               } else if (webkit_dom_node_is_same_node (node, WEBKIT_DOM_NODE (selection_start_clone)))
+                       break;
+               else
+                       remove_node (node);
+
+               node = next_sibling;
+       }
+
+       /* Insert the clone into the tree. Do it after the previous clean up. If
+        * we would do it the other way the line would contain duplicated text nodes
+        * and the block would be expading and shrinking while we would modify it. */
+       webkit_dom_node_insert_before (
+               webkit_dom_node_get_parent_node (monospace),
+               clone,
+               webkit_dom_node_get_next_sibling (monospace),
+               NULL);
+
+       /* Move selection start point the right place. */
+       remove_node (WEBKIT_DOM_NODE (selection_start_marker));
+       webkit_dom_node_insert_before (
+               webkit_dom_node_get_parent_node (clone),
+               WEBKIT_DOM_NODE (selection_start_clone),
+               clone,
+               NULL);
+
+       /* Move all the nodes the are supposed to lose the monospace formatting
+        * out of monospaced element. */
+       node = webkit_dom_node_get_first_child (clone);
+       while (node) {
+               WebKitDOMNode *next_sibling;
+
+               next_sibling = webkit_dom_node_get_next_sibling (node);
+               if (webkit_dom_node_get_first_child (node)) {
+                       if (selection_end_clone &&
+                           webkit_dom_node_contains (node, WEBKIT_DOM_NODE (selection_end_clone))) {
+                               node = webkit_dom_node_get_first_child (node);
+                               continue;
+                       } else
+                               webkit_dom_node_insert_before (
+                                       webkit_dom_node_get_parent_node (clone),
+                                       node,
+                                       clone,
+                                       NULL);
+               } else if (selection_end_clone &&
+                          webkit_dom_node_is_same_node (node, WEBKIT_DOM_NODE (selection_end_clone))) {
+                       selection_end = TRUE;
+                       webkit_dom_node_insert_before (
+                               webkit_dom_node_get_parent_node (clone),
+                               node,
+                               clone,
+                               NULL);
+                       break;
+               } else
+                       webkit_dom_node_insert_before (
+                               webkit_dom_node_get_parent_node (clone),
+                               node,
+                               clone,
+                               NULL);
+
+               node = next_sibling;
+       }
+
+       if (!webkit_dom_node_get_first_child (clone))
+               remove_node (clone);
+
+       /* Just one block was selected and we hit the selection end point. */
+       if (selection_end)
+               goto out;
+
+       /* Middle blocks */
+       block = webkit_dom_node_get_next_sibling (block);
+       while (block && !selection_end) {
+               WebKitDOMNode *next_block, *child, *parent;
+               WebKitDOMElement *monospace_element;
+
+               selection_end = webkit_dom_node_contains (
+                       block, WEBKIT_DOM_NODE (selection_end_marker));
+
+               if (selection_end)
+                       break;
+
+               next_block = webkit_dom_node_get_next_sibling (block);
+
+               /* Find the monospaced element and move all the nodes from it and
+                * finally remove it. */
+               monospace_element = webkit_dom_element_query_selector (
+                       WEBKIT_DOM_ELEMENT (block), "font[face=monospace]", NULL);
+               if (!monospace_element)
+                       break;
+
+               parent = webkit_dom_node_get_parent_node (WEBKIT_DOM_NODE (monospace_element));
+               while  ((child = webkit_dom_node_get_first_child (WEBKIT_DOM_NODE (monospace_element)))) {
+                       webkit_dom_node_insert_before (
+                               parent, child, WEBKIT_DOM_NODE (monospace_element), NULL);
+               }
+
+               remove_node (WEBKIT_DOM_NODE (monospace_element));
+
+               block = next_block;
+       }
+
+       /* End block */
+       node = WEBKIT_DOM_NODE (selection_end_marker);
+       monospace = webkit_dom_node_get_parent_node (WEBKIT_DOM_NODE (selection_end_marker));
+       while (monospace && !is_monospace_element (WEBKIT_DOM_ELEMENT (monospace)))
+               monospace = webkit_dom_node_get_parent_node (monospace);
+
+       /* No monospaced element was found as a parent of selection end node. */
+       if (!monospace)
+               return;
+
+       clone = WEBKIT_DOM_NODE (monospace);
+       node = webkit_dom_node_get_first_child (clone);
+       /* Move all the nodes that are supposed to lose the monospaced formatting
+        * out of the monospaced element. */
+       while (node) {
+               WebKitDOMNode *next_sibling;
+
+               next_sibling = webkit_dom_node_get_next_sibling (node);
+               if (webkit_dom_node_get_first_child (node)) {
+                       if (webkit_dom_node_contains (node, WEBKIT_DOM_NODE (selection_end_marker))) {
+                               node = webkit_dom_node_get_first_child (node);
+                               continue;
+                       } else
+                               webkit_dom_node_insert_before (
+                                       webkit_dom_node_get_parent_node (clone),
+                                       node,
+                                       clone,
+                                       NULL);
+               } else if (webkit_dom_node_is_same_node (node, WEBKIT_DOM_NODE (selection_end_marker))) {
+                       selection_end = TRUE;
+                       webkit_dom_node_insert_before (
+                               webkit_dom_node_get_parent_node (clone),
+                               node,
+                               clone,
+                               NULL);
+                       break;
+               } else {
+                       webkit_dom_node_insert_before (
+                               webkit_dom_node_get_parent_node (clone),
+                               node,
+                               clone,
+                               NULL);
+               }
+
+               node = next_sibling;
+       }
+
+       if (!webkit_dom_node_get_first_child (clone))
+               remove_node (clone);
+ out:
+       e_editor_dom_selection_restore (editor_page);
+}
+
+/*
+ * e_html_editor_selection_set_monospaced:
+ * @selection: an #EEditorSelection
+ * @monospaced: @TRUE to enable monospaced, @FALSE to disable
+ *
+ * Toggles monospaced formatting of current selection or letter at current cursor
+ * position, depending on whether @monospaced is @TRUE or @FALSE.
+ */
+void
+e_editor_dom_selection_set_monospace (EEditorPage *editor_page,
+                                     gboolean value)
+{
+       WebKitDOMDocument *document;
+       WebKitDOMRange *range = NULL;
+       EEditorHistoryEvent *ev = NULL;
+       EEditorUndoRedoManager *manager;
+       guint font_size = 0;
+
+       g_return_if_fail (E_IS_EDITOR_PAGE (editor_page));
+
+       if ((e_editor_dom_selection_is_monospace (editor_page) ? 1 : 0) == (value ? 1 : 0))
+               return;
+
+       document = e_editor_page_get_document (editor_page);
+       range = e_editor_dom_get_current_range (editor_page);
+       if (!range)
+               return;
+
+       manager = e_editor_page_get_undo_redo_manager (editor_page);
+       if (!e_editor_undo_redo_manager_is_operation_in_progress (manager)) {
+               ev = g_new0 (EEditorHistoryEvent, 1);
+               ev->type = HISTORY_MONOSPACE;
+
+               e_editor_dom_selection_get_coordinates (editor_page,
+                       &ev->before.start.x,
+                       &ev->before.start.y,
+                       &ev->before.end.x,
+                       &ev->before.end.y);
+
+               ev->data.style.from = !value;
+               ev->data.style.to = value;
+       }
+
+       font_size = e_editor_page_get_font_size (editor_page);
+       if (font_size == 0)
+               font_size = E_CONTENT_EDITOR_FONT_SIZE_NORMAL;
+
+       if (value) {
+               WebKitDOMElement *monospace;
+
+               monospace = webkit_dom_document_create_element (
+                       document, "font", NULL);
+               webkit_dom_element_set_attribute (
+                       monospace, "face", "monospace", NULL);
+               if (font_size != 0) {
+                       gchar *font_size_str;
+
+                       font_size_str = g_strdup_printf ("%d", font_size);
+                       webkit_dom_element_set_attribute (
+                               monospace, "size", font_size_str, NULL);
+                       g_free (font_size_str);
+               }
+
+               if (!webkit_dom_range_get_collapsed (range, NULL))
+                       monospace_selection (editor_page, monospace);
+               else {
+                       /* https://bugs.webkit.org/show_bug.cgi?id=15256 */
+                       webkit_dom_element_set_inner_html (
+                               monospace,
+                               UNICODE_ZERO_WIDTH_SPACE,
+                               NULL);
+                       webkit_dom_range_insert_node (
+                               range, WEBKIT_DOM_NODE (monospace), NULL);
+
+                       e_editor_dom_move_caret_into_element (editor_page, monospace, FALSE);
+               }
+       } else {
+               gboolean is_bold = FALSE, is_italic = FALSE;
+               gboolean is_underline = FALSE, is_strikethrough = FALSE;
+               guint font_size = 0;
+               WebKitDOMElement *tt_element;
+               WebKitDOMNode *node;
+
+               node = webkit_dom_range_get_end_container (range, NULL);
+               if (WEBKIT_DOM_IS_ELEMENT (node) &&
+                   is_monospace_element (WEBKIT_DOM_ELEMENT (node))) {
+                       tt_element = WEBKIT_DOM_ELEMENT (node);
+               } else {
+                       tt_element = dom_node_find_parent_element (node, "FONT");
+
+                       if (!is_monospace_element (tt_element)) {
+                               g_clear_object (&range);
+                               g_free (ev);
+                               return;
+                       }
+               }
+
+               /* Save current formatting */
+               is_bold = e_editor_page_get_bold (editor_page);
+               is_italic = e_editor_page_get_italic (editor_page);
+               is_underline = e_editor_page_get_underline (editor_page);
+               is_strikethrough = e_editor_page_get_strikethrough (editor_page);
+
+               if (!e_editor_dom_selection_is_collapsed (editor_page))
+                       unmonospace_selection (editor_page);
+               else {
+                       e_editor_dom_selection_save (editor_page);
+                       set_font_style (document, "", FALSE);
+                       e_editor_dom_selection_restore (editor_page);
+               }
+
+               /* Re-set formatting */
+               if (is_bold)
+                       e_editor_dom_selection_set_bold (editor_page, TRUE);
+               if (is_italic)
+                       e_editor_dom_selection_set_italic (editor_page, TRUE);
+               if (is_underline)
+                       e_editor_dom_selection_set_underline (editor_page, TRUE);
+               if (is_strikethrough)
+                       e_editor_dom_selection_set_strikethrough (editor_page, TRUE);
+
+               if (font_size)
+                       e_editor_dom_selection_set_font_size (editor_page, font_size);
+       }
+
+       if (ev) {
+               e_editor_dom_selection_get_coordinates (editor_page,
+                       &ev->after.start.x,
+                       &ev->after.start.y,
+                       &ev->after.end.x,
+                       &ev->after.end.y);
+               e_editor_undo_redo_manager_insert_history_event (manager, ev);
+       }
+
+       e_editor_dom_force_spell_check_for_current_paragraph (editor_page);
+
+       g_clear_object (&range);
+}
+
+static gboolean
+is_bold_element (WebKitDOMElement *element)
+{
+       if (!element || !WEBKIT_DOM_IS_ELEMENT (element))
+               return FALSE;
+
+       if (element_has_tag (element, "b"))
+               return TRUE;
+
+       /* Headings are bold by default */
+       return WEBKIT_DOM_IS_HTML_HEADING_ELEMENT (element);
+}
+
+/*
+ * e_html_editor_selection_is_bold:
+ * @selection: an #EEditorSelection
+ *
+ * Returns whether current selection or letter at current cursor position
+ * is bold.
+ *
+ * Returns @TRUE when selection is bold, @FALSE otherwise.
+ */
+gboolean
+e_editor_dom_selection_is_bold (EEditorPage *editor_page)
+{
+       gboolean is_bold;
+
+       g_return_val_if_fail (E_IS_EDITOR_PAGE (editor_page), FALSE);
+
+       is_bold = e_editor_page_get_bold (editor_page);
+
+       is_bold = dom_selection_is_font_format (
+               editor_page, (IsRightFormatNodeFunc) is_bold_element, &is_bold);
+
+       return is_bold;
+}
+
+/*
+ * e_html_editor_selection_set_bold:
+ * @selection: an #EEditorSelection
+ * @bold: @TRUE to enable bold, @FALSE to disable
+ *
+ * Toggles bold formatting of current selection or letter at current cursor
+ * position, depending on whether @bold is @TRUE or @FALSE.
+ */
+void
+e_editor_dom_selection_set_bold (EEditorPage *editor_page,
+                                gboolean bold)
+{
+       g_return_if_fail (E_IS_EDITOR_PAGE (editor_page));
+
+       if (e_editor_dom_selection_is_bold (editor_page) == bold)
+               return;
+
+       selection_set_font_style (
+               editor_page, E_CONTENT_EDITOR_COMMAND_BOLD, bold);
+
+       e_editor_dom_force_spell_check_for_current_paragraph (editor_page);
+}
+
+static gboolean
+is_italic_element (WebKitDOMElement *element)
+{
+       if (!element || !WEBKIT_DOM_IS_ELEMENT (element))
+               return FALSE;
+
+       return element_has_tag (element, "i") || element_has_tag (element, "address");
+}
+
+/*
+ * e_html_editor_selection_is_italic:
+ * @selection: an #EEditorSelection
+ *
+ * Returns whether current selection or letter at current cursor position
+ * is italic.
+ *
+ * Returns @TRUE when selection is italic, @FALSE otherwise.
+ */
+gboolean
+e_editor_dom_selection_is_italic (EEditorPage *editor_page)
+{
+       gboolean is_italic;
+
+       g_return_val_if_fail (E_IS_EDITOR_PAGE (editor_page), FALSE);
+
+       is_italic = e_editor_page_get_italic (editor_page);
+       is_italic = dom_selection_is_font_format (
+               editor_page, (IsRightFormatNodeFunc) is_italic_element, &is_italic);
+
+       return is_italic;
+}
+
+/*
+ * e_html_editor_selection_set_italic:
+ * @selection: an #EEditorSelection
+ * @italic: @TRUE to enable italic, @FALSE to disable
+ *
+ * Toggles italic formatting of current selection or letter at current cursor
+ * position, depending on whether @italic is @TRUE or @FALSE.
+ */
+void
+e_editor_dom_selection_set_italic (EEditorPage *editor_page,
+                                  gboolean italic)
+{
+       g_return_if_fail (E_IS_EDITOR_PAGE (editor_page));
+
+       if (e_editor_dom_selection_is_italic (editor_page) == italic)
+               return;
+
+       selection_set_font_style (
+               editor_page, E_CONTENT_EDITOR_COMMAND_ITALIC, italic);
+}
+
+/*
+ * e_html_editor_selection_is_indented:
+ * @selection: an #EEditorSelection
+ *
+ * Returns whether current paragraph is indented. This does not include
+ * citations.  To check, whether paragraph is a citation, use
+ * e_html_editor_selection_is_citation().
+ *
+ * Returns: @TRUE when current paragraph is indented, @FALSE otherwise.
+ */
+gboolean
+e_editor_dom_selection_is_indented (EEditorPage *editor_page)
+{
+       WebKitDOMElement *element;
+       WebKitDOMRange *range = NULL;
+
+       g_return_val_if_fail (E_IS_EDITOR_PAGE (editor_page), FALSE);
+
+       range = e_editor_dom_get_current_range (editor_page);
+       if (!range)
+               return FALSE;
+
+       if (webkit_dom_range_get_collapsed (range, NULL)) {
+               element = get_element_for_inspection (range);
+               g_clear_object (&range);
+               return element_has_class (element, "-x-evo-indented");
+       } else {
+               WebKitDOMNode *node;
+               gboolean ret_val;
+
+               node = webkit_dom_range_get_end_container (range, NULL);
+               /* No selection or whole body selected */
+               if (WEBKIT_DOM_IS_HTML_BODY_ELEMENT (node))
+                       goto out;
+
+               element = WEBKIT_DOM_ELEMENT (get_parent_indented_block (node));
+               ret_val = element_has_class (element, "-x-evo-indented");
+               if (!ret_val)
+                       goto out;
+
+               node = webkit_dom_range_get_start_container (range, NULL);
+               /* No selection or whole body selected */
+               if (WEBKIT_DOM_IS_HTML_BODY_ELEMENT (node))
+                       goto out;
+
+               element = WEBKIT_DOM_ELEMENT (get_parent_indented_block (node));
+               ret_val = element_has_class (element, "-x-evo-indented");
+
+               g_clear_object (&range);
+
+               return ret_val;
+       }
+
+ out:
+       g_clear_object (&range);
+
+       return FALSE;
+}
+
+/*
+ * e_html_editor_selection_is_citation:
+ * @selection: an #EEditorSelection
+ *
+ * Returns whether current paragraph is a citation.
+ *
+ * Returns: @TRUE when current paragraph is a citation, @FALSE otherwise.
+ */
+gboolean
+e_editor_dom_selection_is_citation (EEditorPage *editor_page)
+{
+       WebKitDOMNode *node;
+       WebKitDOMRange *range = NULL;
+       gboolean ret_val;
+       gchar *value, *text_content;
+
+       g_return_val_if_fail (E_IS_EDITOR_PAGE (editor_page), FALSE);
+
+       range = e_editor_dom_get_current_range (editor_page);
+       if (!range)
+               return FALSE;
+
+       node = webkit_dom_range_get_common_ancestor_container (range, NULL);
+       g_clear_object (&range);
+
+       if (WEBKIT_DOM_IS_TEXT (node))
+               return get_has_style (editor_page, "citation");
+
+       text_content = webkit_dom_node_get_text_content (node);
+       if (g_strcmp0 (text_content, "") == 0) {
+               g_free (text_content);
+               return FALSE;
+       }
+       g_free (text_content);
+
+       value = webkit_dom_element_get_attribute (WEBKIT_DOM_ELEMENT (node), "type");
+       /* citation == <blockquote type='cite'> */
+       if (value && strstr (value, "cite"))
+               ret_val = TRUE;
+       else
+               ret_val = get_has_style (editor_page, "citation");
+
+       g_free (value);
+       return ret_val;
+}
+
+static gchar *
+get_font_property (EEditorPage *editor_page,
+                   const gchar *font_property)
+{
+       WebKitDOMRange *range = NULL;
+       WebKitDOMNode *node;
+       WebKitDOMElement *element;
+       gchar *value;
+
+       range = e_editor_dom_get_current_range (editor_page);
+       if (!range)
+               return NULL;
+
+       node = webkit_dom_range_get_common_ancestor_container (range, NULL);
+       g_clear_object (&range);
+       element = dom_node_find_parent_element (node, "FONT");
+       while (element && !WEBKIT_DOM_IS_HTML_BODY_ELEMENT (element) &&
+              !webkit_dom_element_has_attribute (element, font_property)) {
+               element = dom_node_find_parent_element (
+                       webkit_dom_node_get_parent_node (WEBKIT_DOM_NODE (element)), "FONT");
+       }
+
+       if (!element)
+               return NULL;
+
+       g_object_get (G_OBJECT (element), font_property, &value, NULL);
+
+       return value;
+}
+
+/*
+ * e_editor_dom_selection_get_font_size:
+ * @selection: an #EEditorSelection
+ *
+ * Returns point size of current selection or of letter at current cursor position.
+ */
+guint
+e_editor_dom_selection_get_font_size (EEditorPage *editor_page)
+{
+       gchar *size;
+       guint size_int;
+       gboolean increment;
+
+       g_return_val_if_fail (E_IS_EDITOR_PAGE (editor_page), 0);
+
+       size = get_font_property (editor_page, "size");
+       if (!(size && *size)) {
+               g_free (size);
+               return E_CONTENT_EDITOR_FONT_SIZE_NORMAL;
+       }
+
+       /* We don't support increments, but when going through a content that
+        * was not written in Evolution we can find it. In this case just report
+        * the normal size. */
+       /* FIXME: go through all parent and get the right value. */
+       increment = size[0] == '+' || size[0] == '-';
+       size_int = atoi (size);
+       g_free (size);
+
+       if (increment || size_int == 0)
+               return E_CONTENT_EDITOR_FONT_SIZE_NORMAL;
+
+       return size_int;
+}
+
+/*
+ * e_html_editor_selection_set_font_size:
+ * @selection: an #EEditorSelection
+ * @font_size: point size to apply
+ *
+ * Sets font size of current selection or of letter at current cursor position
+ * to @font_size.
+ */
+void
+e_editor_dom_selection_set_font_size (EEditorPage *editor_page,
+                                     EContentEditorFontSize font_size)
+{
+       WebKitDOMDocument *document;
+       EEditorUndoRedoManager *manager;
+       EEditorHistoryEvent *ev = NULL;
+       gchar *size_str;
+       guint current_font_size;
+
+       g_return_if_fail (E_IS_EDITOR_PAGE (editor_page));
+
+       document = e_editor_page_get_document (editor_page);
+       current_font_size = e_editor_dom_selection_get_font_size (editor_page);
+       if (current_font_size == font_size)
+               return;
+
+       e_editor_dom_selection_save (editor_page);
+
+       manager = e_editor_page_get_undo_redo_manager (editor_page);
+       if (!e_editor_undo_redo_manager_is_operation_in_progress (manager)) {
+               ev = g_new0 (EEditorHistoryEvent, 1);
+               ev->type = HISTORY_FONT_SIZE;
+
+               e_editor_dom_selection_get_coordinates (editor_page,
+                       &ev->before.start.x,
+                       &ev->before.start.y,
+                       &ev->before.end.x,
+                       &ev->before.end.y);
+
+               ev->data.style.from = current_font_size;
+               ev->data.style.to = font_size;
+       }
+
+       size_str = g_strdup_printf ("%d", font_size);
+
+       if (e_editor_dom_selection_is_collapsed (editor_page)) {
+               WebKitDOMElement *font;
+
+               font = set_font_style (document, "font", font_size != 3);
+               if (font)
+                       webkit_dom_element_set_attribute (font, "size", size_str, NULL);
+               e_editor_dom_selection_restore (editor_page);
+               goto exit;
+       }
+
+       e_editor_dom_selection_restore (editor_page);
+
+       e_editor_dom_exec_command (editor_page, E_CONTENT_EDITOR_COMMAND_FONT_SIZE, size_str);
+
+       /* Text in <font size="3"></font> (size 3 is our default size) is a little
+        * bit smaller than font outsize it. So move it outside of it. */
+       if (font_size == E_CONTENT_EDITOR_FONT_SIZE_NORMAL) {
+               WebKitDOMElement *element;
+
+               element = webkit_dom_document_query_selector (document, "font[size=\"3\"]", NULL);
+               if (element) {
+                       WebKitDOMNode *child;
+
+                       while ((child = webkit_dom_node_get_first_child (WEBKIT_DOM_NODE (element))))
+                               webkit_dom_node_insert_before (
+                                       webkit_dom_node_get_parent_node (WEBKIT_DOM_NODE (element)),
+                                       child,
+                                       WEBKIT_DOM_NODE (element),
+                                       NULL);
+
+                       remove_node (WEBKIT_DOM_NODE (element));
+               }
+       }
+
+ exit:
+       g_free (size_str);
+
+       if (ev) {
+               e_editor_dom_selection_get_coordinates (editor_page,
+                       &ev->after.start.x,
+                       &ev->after.start.y,
+                       &ev->after.end.x,
+                       &ev->after.end.y);
+
+               e_editor_undo_redo_manager_insert_history_event (manager, ev);
+       }
+}
+
+/*
+ * e_html_editor_selection_set_font_name:
+ * @selection: an #EEditorSelection
+ * @font_name: a font name to apply
+ *
+ * Sets font name of current selection or of letter at current cursor position
+ * to @font_name.
+ */
+void
+e_editor_dom_selection_set_font_name (EEditorPage *editor_page,
+                                     const gchar *font_name)
+{
+       g_return_if_fail (E_IS_EDITOR_PAGE (editor_page));
+
+       e_editor_dom_exec_command (editor_page, E_CONTENT_EDITOR_COMMAND_FONT_NAME, font_name);
+}
+
+/*
+ * e_html_editor_selection_get_font_name:
+ * @selection: an #EEditorSelection
+ *
+ * Returns name of font used in current selection or at letter at current cursor
+ * position.
+ *
+ * Returns: A string with font name. [transfer-none]
+ */
+gchar *
+e_editor_dom_selection_get_font_name (EEditorPage *editor_page)
+{
+       WebKitDOMNode *node;
+       WebKitDOMRange *range = NULL;
+       WebKitDOMCSSStyleDeclaration *css = NULL;
+       gchar *value;
+
+       g_return_val_if_fail (E_IS_EDITOR_PAGE (editor_page), NULL);
+
+       range = e_editor_dom_get_current_range (editor_page);
+       node = webkit_dom_range_get_common_ancestor_container (range, NULL);
+       g_clear_object (&range);
+
+       css = webkit_dom_element_get_style (WEBKIT_DOM_ELEMENT (node));
+       value = webkit_dom_css_style_declaration_get_property_value (css, "fontFamily");
+       g_clear_object (&css);
+
+       return value;
+}
+
+/*
+ * e_html_editor_selection_set_font_color:
+ * @selection: an #EEditorSelection
+ * @rgba: a #GdkRGBA
+ *
+ * Sets font color of current selection or letter at current cursor position to
+ * color defined in @rgba.
+ */
+void
+e_editor_dom_selection_set_font_color (EEditorPage *editor_page,
+                                      const gchar *color)
+{
+       EEditorUndoRedoManager *manager;
+       EEditorHistoryEvent *ev = NULL;
+
+       g_return_if_fail (E_IS_EDITOR_PAGE (editor_page));
+
+       manager = e_editor_page_get_undo_redo_manager (editor_page);
+       if (!e_editor_undo_redo_manager_is_operation_in_progress (manager)) {
+               ev = g_new0 (EEditorHistoryEvent, 1);
+               ev->type = HISTORY_FONT_COLOR;
+
+               e_editor_dom_selection_get_coordinates (editor_page,
+                       &ev->before.start.x,
+                       &ev->before.start.y,
+                       &ev->before.end.x,
+                       &ev->before.end.y);
+
+               ev->data.string.from = g_strdup (e_editor_page_get_font_color (editor_page));
+               ev->data.string.to = g_strdup (color);
+       }
+
+       e_editor_dom_exec_command (editor_page, E_CONTENT_EDITOR_COMMAND_FORE_COLOR, color);
+
+       if (ev) {
+               ev->after.start.x = ev->before.start.x;
+               ev->after.start.y = ev->before.start.y;
+               ev->after.end.x = ev->before.end.x;
+               ev->after.end.y = ev->before.end.y;
+
+               e_editor_undo_redo_manager_insert_history_event (manager, ev);
+       }
+}
+
+/*
+ * e_html_editor_selection_get_font_color:
+ * @selection: an #EEditorSelection
+ * @rgba: a #GdkRGBA object to be set to current font color
+ *
+ * Sets @rgba to contain color of current text selection or letter at current
+ * cursor position.
+ */
+gchar *
+e_editor_dom_selection_get_font_color (EEditorPage *editor_page)
+{
+       WebKitDOMDocument *document;
+       gchar *color;
+
+       g_return_val_if_fail (E_IS_EDITOR_PAGE (editor_page), NULL);
+
+       document = e_editor_page_get_document (editor_page);
+       color = get_font_property (editor_page, "color");
+       if (!(color && *color)) {
+               WebKitDOMHTMLElement *body;
+
+               body = webkit_dom_document_get_body (document);
+               g_free (color);
+               color = webkit_dom_html_body_element_get_text (WEBKIT_DOM_HTML_BODY_ELEMENT (body));
+               if (!(color && *color)) {
+                       g_free (color);
+                       return g_strdup ("#000000");
+               }
+       }
+
+       return color;
+}
+
+/*
+ * e_html_editor_selection_get_block_format:
+ * @selection: an #EEditorSelection
+ *
+ * Returns block format of current paragraph.
+ *
+ * Returns: #EContentEditorBlockFormat
+ */
+EContentEditorBlockFormat
+e_editor_dom_selection_get_block_format (EEditorPage *editor_page)
+{
+       WebKitDOMNode *node;
+       WebKitDOMRange *range = NULL;
+       WebKitDOMElement *element;
+       EContentEditorBlockFormat result;
+
+       g_return_val_if_fail (E_IS_EDITOR_PAGE (editor_page), E_CONTENT_EDITOR_BLOCK_FORMAT_NONE);
+
+       range = e_editor_dom_get_current_range (editor_page);
+       if (!range)
+               return E_CONTENT_EDITOR_BLOCK_FORMAT_PARAGRAPH;
+
+       node = webkit_dom_range_get_start_container (range, NULL);
+
+       if ((element = dom_node_find_parent_element (node, "UL"))) {
+               WebKitDOMElement *tmp_element;
+
+               tmp_element = dom_node_find_parent_element (node, "OL");
+               if (tmp_element) {
+                       if (webkit_dom_node_contains (WEBKIT_DOM_NODE (tmp_element), WEBKIT_DOM_NODE 
(element)))
+                               result = dom_get_list_format_from_node (WEBKIT_DOM_NODE (element));
+                       else
+                               result = dom_get_list_format_from_node (WEBKIT_DOM_NODE (tmp_element));
+               } else
+                       result = E_CONTENT_EDITOR_BLOCK_FORMAT_UNORDERED_LIST;
+       } else if ((element = dom_node_find_parent_element (node, "OL")) != NULL) {
+               WebKitDOMElement *tmp_element;
+
+               tmp_element = dom_node_find_parent_element (node, "UL");
+               if (tmp_element) {
+                       if (webkit_dom_node_contains (WEBKIT_DOM_NODE (element), WEBKIT_DOM_NODE 
(tmp_element)))
+                               result = dom_get_list_format_from_node (WEBKIT_DOM_NODE (element));
+                       else
+                               result = dom_get_list_format_from_node (WEBKIT_DOM_NODE (tmp_element));
+               } else
+                       result = dom_get_list_format_from_node (WEBKIT_DOM_NODE (element));
+       } else if (dom_node_find_parent_element (node, "PRE")) {
+               result = E_CONTENT_EDITOR_BLOCK_FORMAT_PRE;
+       } else if (dom_node_find_parent_element (node, "ADDRESS")) {
+               result = E_CONTENT_EDITOR_BLOCK_FORMAT_ADDRESS;
+       } else if (dom_node_find_parent_element (node, "H1")) {
+               result = E_CONTENT_EDITOR_BLOCK_FORMAT_H1;
+       } else if (dom_node_find_parent_element (node, "H2")) {
+               result = E_CONTENT_EDITOR_BLOCK_FORMAT_H2;
+       } else if (dom_node_find_parent_element (node, "H3")) {
+               result = E_CONTENT_EDITOR_BLOCK_FORMAT_H3;
+       } else if (dom_node_find_parent_element (node, "H4")) {
+               result = E_CONTENT_EDITOR_BLOCK_FORMAT_H4;
+       } else if (dom_node_find_parent_element (node, "H5")) {
+               result = E_CONTENT_EDITOR_BLOCK_FORMAT_H5;
+       } else if (dom_node_find_parent_element (node, "H6")) {
+               result = E_CONTENT_EDITOR_BLOCK_FORMAT_H6;
+       } else if ((element = dom_node_find_parent_element (node, "BLOCKQUOTE")) != NULL) {
+               result = E_CONTENT_EDITOR_BLOCK_FORMAT_PARAGRAPH;
+       } else if (dom_node_find_parent_element (node, "P")) {
+               result = E_CONTENT_EDITOR_BLOCK_FORMAT_PARAGRAPH;
+       } else {
+               result = E_CONTENT_EDITOR_BLOCK_FORMAT_PARAGRAPH;
+       }
+
+       g_clear_object (&range);
+
+       return result;
+}
+
+static void
+change_leading_space_to_nbsp (WebKitDOMNode *block)
+{
+       WebKitDOMNode *child;
+
+       if (!WEBKIT_DOM_IS_HTML_PRE_ELEMENT (block))
+               return;
+
+       if ((child = webkit_dom_node_get_first_child (block)) &&
+            WEBKIT_DOM_IS_CHARACTER_DATA (child)) {
+               gchar *data;
+
+               data = webkit_dom_character_data_substring_data (
+                       WEBKIT_DOM_CHARACTER_DATA (child), 0, 1, NULL);
+
+               if (data && *data == ' ')
+                       webkit_dom_character_data_replace_data (
+                               WEBKIT_DOM_CHARACTER_DATA (child), 0, 1, UNICODE_NBSP, NULL);
+               g_free (data);
+       }
+}
+
+static void
+change_trailing_space_in_block_to_nbsp (WebKitDOMNode *block)
+{
+       WebKitDOMNode *child;
+
+       if ((child = webkit_dom_node_get_last_child (block)) &&
+           WEBKIT_DOM_IS_CHARACTER_DATA (child)) {
+               gchar *tmp;
+               gulong length;
+
+               length = webkit_dom_character_data_get_length (
+                       WEBKIT_DOM_CHARACTER_DATA (child));
+
+               tmp = webkit_dom_character_data_substring_data (
+                       WEBKIT_DOM_CHARACTER_DATA (child), length - 1, 1, NULL);
+               if (tmp && *tmp == ' ') {
+                       webkit_dom_character_data_replace_data (
+                               WEBKIT_DOM_CHARACTER_DATA (child),
+                               length - 1,
+                               1,
+                               UNICODE_NBSP,
+                               NULL);
+               }
+               g_free (tmp);
+       }
+}
+
+static void
+change_space_before_selection_to_nbsp (WebKitDOMNode *node)
+{
+       WebKitDOMNode *prev_sibling;
+
+       if ((prev_sibling = webkit_dom_node_get_previous_sibling (node))) {
+               if (WEBKIT_DOM_IS_CHARACTER_DATA (prev_sibling)) {
+                       gchar *tmp;
+                       gulong length;
+
+                       length = webkit_dom_character_data_get_length (
+                               WEBKIT_DOM_CHARACTER_DATA (prev_sibling));
+
+                       tmp = webkit_dom_character_data_substring_data (
+                               WEBKIT_DOM_CHARACTER_DATA (prev_sibling), length - 1, 1, NULL);
+                       if (tmp && *tmp == ' ') {
+                               webkit_dom_character_data_replace_data (
+                                       WEBKIT_DOM_CHARACTER_DATA (prev_sibling),
+                                       length - 1,
+                                       1,
+                                       UNICODE_NBSP,
+                                       NULL);
+                       }
+                       g_free (tmp);
+               }
+       }
+}
+
+static gboolean
+process_block_to_block (EEditorPage *editor_page,
+                        EContentEditorBlockFormat format,
+                        const gchar *value,
+                        WebKitDOMNode *block,
+                        WebKitDOMNode *end_block,
+                        WebKitDOMNode *blockquote,
+                        gboolean html_mode)
+{
+       WebKitDOMDocument *document;
+       WebKitDOMNode *next_block;
+       gboolean after_selection_end = FALSE;
+
+       g_return_val_if_fail (E_IS_EDITOR_PAGE (editor_page), FALSE);
+
+       document = e_editor_page_get_document (editor_page);
+
+       while (!after_selection_end && block) {
+               gboolean quoted = FALSE;
+               gboolean empty = FALSE;
+               gchar *content;
+               gint citation_level = 0;
+               WebKitDOMNode *child;
+               WebKitDOMElement *element;
+
+               if (e_editor_dom_node_is_citation_node (block)) {
+                       gboolean finished;
+
+                       next_block = webkit_dom_node_get_next_sibling (block);
+                       finished = process_block_to_block (
+                               editor_page,
+                               format,
+                               value,
+                               webkit_dom_node_get_first_child (block),
+                               end_block,
+                               blockquote,
+                               html_mode);
+
+                       if (finished)
+                               return TRUE;
+
+                       block = next_block;
+
+                       continue;
+               }
+
+               if (webkit_dom_element_query_selector (
+                       WEBKIT_DOM_ELEMENT (block), "span.-x-evo-quoted", NULL)) {
+                       quoted = TRUE;
+                       e_editor_dom_remove_quoting_from_element (WEBKIT_DOM_ELEMENT (block));
+               }
+
+               if (!html_mode)
+                       e_editor_dom_remove_wrapping_from_element (WEBKIT_DOM_ELEMENT (block));
+
+               after_selection_end = webkit_dom_node_is_same_node (block, end_block);
+
+               next_block = webkit_dom_node_get_next_sibling (block);
+
+               if (node_is_list (block)) {
+                       WebKitDOMNode *item;
+
+                       item = webkit_dom_node_get_first_child (block);
+                       while (item && !WEBKIT_DOM_IS_HTML_LI_ELEMENT (item))
+                               item = webkit_dom_node_get_first_child (item);
+
+                       if (item && do_format_change_list_to_block (editor_page, format, item, value))
+                               return TRUE;
+
+                       block = next_block;
+
+                       continue;
+               }
+
+               if (format == E_CONTENT_EDITOR_BLOCK_FORMAT_PARAGRAPH)
+                       element = e_editor_dom_get_paragraph_element (editor_page, -1, 0);
+               else
+                       element = webkit_dom_document_create_element (
+                               document, value, NULL);
+
+               content = webkit_dom_node_get_text_content (block);
+
+               empty = !*content || (g_strcmp0 (content, UNICODE_ZERO_WIDTH_SPACE) == 0);
+               g_free (content);
+
+               change_leading_space_to_nbsp (block);
+               change_trailing_space_in_block_to_nbsp (block);
+
+               while ((child = webkit_dom_node_get_first_child (block))) {
+                       if (WEBKIT_DOM_IS_HTML_BR_ELEMENT (child))
+                               empty = FALSE;
+
+                       webkit_dom_node_append_child (
+                               WEBKIT_DOM_NODE (element), child, NULL);
+               }
+
+               if (empty) {
+                       WebKitDOMElement *br;
+
+                       br = webkit_dom_document_create_element (
+                               document, "BR", NULL);
+                       webkit_dom_node_append_child (
+                               WEBKIT_DOM_NODE (element), WEBKIT_DOM_NODE (br), NULL);
+               }
+
+               webkit_dom_node_insert_before (
+                       webkit_dom_node_get_parent_node (block),
+                       WEBKIT_DOM_NODE (element),
+                       block,
+                       NULL);
+
+               remove_node (block);
+
+               if (!next_block && !after_selection_end) {
+                       citation_level = selection_get_citation_level (WEBKIT_DOM_NODE (element));
+
+                       if (citation_level > 0) {
+                               next_block = webkit_dom_node_get_parent_node (WEBKIT_DOM_NODE (element));
+                               next_block = webkit_dom_node_get_next_sibling (next_block);
+                       }
+               }
+
+               block = next_block;
+
+               if (!html_mode && format == E_CONTENT_EDITOR_BLOCK_FORMAT_PARAGRAPH) {
+                       citation_level = selection_get_citation_level (WEBKIT_DOM_NODE (element));
+
+                       if (citation_level > 0) {
+                               gint quote, word_wrap_length;
+
+                               word_wrap_length =
+                                       e_editor_page_get_word_wrap_length (editor_page);
+                               quote = citation_level ? citation_level * 2 : 0;
+
+                               element = e_editor_dom_wrap_paragraph_length (
+                                       editor_page, element, word_wrap_length - quote);
+
+                       }
+               }
+
+               if (!html_mode && quoted) {
+                       if (citation_level > 0)
+                               e_editor_dom_quote_plain_text_element_after_wrapping (
+                                       editor_page, element, citation_level);
+                       else
+                               e_editor_dom_quote_plain_text_element (editor_page, element);
+               }
+       }
+
+       return after_selection_end;
+}
+
+static void
+format_change_block_to_block (EEditorPage *editor_page,
+                              EContentEditorBlockFormat format,
+                              const gchar *value)
+{
+       WebKitDOMDocument *document;
+       WebKitDOMElement *selection_start_marker, *selection_end_marker;
+       WebKitDOMNode *block, *end_block, *blockquote = NULL;
+       gboolean html_mode = FALSE;
+
+       g_return_if_fail (E_IS_EDITOR_PAGE (editor_page));
+
+       document = e_editor_page_get_document (editor_page);
+       selection_start_marker = webkit_dom_document_query_selector (
+               document, "span#-x-evo-selection-start-marker", NULL);
+       selection_end_marker = webkit_dom_document_query_selector (
+               document, "span#-x-evo-selection-end-marker", NULL);
+
+       /* If the selection was not saved, move it into the first child of body */
+       if (!selection_start_marker || !selection_end_marker) {
+               WebKitDOMHTMLElement *body;
+               WebKitDOMNode *child;
+
+               body = webkit_dom_document_get_body (document);
+               child = webkit_dom_node_get_first_child (WEBKIT_DOM_NODE (body));
+
+               dom_add_selection_markers_into_element_start (
+                       document,
+                       WEBKIT_DOM_ELEMENT (child),
+                       &selection_start_marker,
+                       &selection_end_marker);
+       }
+
+       block = e_editor_dom_get_parent_block_node_from_child (
+               WEBKIT_DOM_NODE (selection_start_marker));
+
+       html_mode = e_editor_page_get_html_mode (editor_page);
+
+       end_block = e_editor_dom_get_parent_block_node_from_child (
+               WEBKIT_DOM_NODE (selection_end_marker));
+
+       /* Process all blocks that are in the selection one by one */
+       process_block_to_block (
+               editor_page, format, value, block, end_block, blockquote, html_mode);
+}
+
+static void
+format_change_block_to_list (EEditorPage *editor_page,
+                             EContentEditorBlockFormat format)
+{
+       WebKitDOMDocument *document;
+       WebKitDOMElement *selection_start_marker, *selection_end_marker, *item, *list;
+       WebKitDOMNode *block, *next_block;
+       gboolean after_selection_end = FALSE, in_quote = FALSE;
+       gboolean html_mode = e_editor_page_get_html_mode (editor_page);
+
+       g_return_if_fail (E_IS_EDITOR_PAGE (editor_page));
+
+       document = e_editor_page_get_document (editor_page);
+       selection_start_marker = webkit_dom_document_query_selector (
+               document, "span#-x-evo-selection-start-marker", NULL);
+       selection_end_marker = webkit_dom_document_query_selector (
+               document, "span#-x-evo-selection-end-marker", NULL);
+
+       /* If the selection was not saved, move it into the first child of body */
+       if (!selection_start_marker || !selection_end_marker) {
+               WebKitDOMHTMLElement *body;
+               WebKitDOMNode *child;
+
+               body = webkit_dom_document_get_body (document);
+               child = webkit_dom_node_get_first_child (WEBKIT_DOM_NODE (body));
+
+               dom_add_selection_markers_into_element_start (
+                       document,
+                       WEBKIT_DOM_ELEMENT (child),
+                       &selection_start_marker,
+                       &selection_end_marker);
+       }
+
+       block = e_editor_dom_get_parent_block_node_from_child (
+               WEBKIT_DOM_NODE (selection_start_marker));
+
+       list = create_list_element (editor_page, format, 0, html_mode);
+
+       if (webkit_dom_element_query_selector (
+               WEBKIT_DOM_ELEMENT (block), "span.-x-evo-quoted", NULL)) {
+               WebKitDOMElement *element;
+               WebKitDOMDOMWindow *dom_window = NULL;
+               WebKitDOMDOMSelection *dom_selection = NULL;
+               WebKitDOMRange *range = NULL;
+
+               in_quote = TRUE;
+
+               dom_window = webkit_dom_document_get_default_view (document);
+               dom_selection = webkit_dom_dom_window_get_selection (dom_window);
+               range = webkit_dom_document_create_range (document);
+
+               webkit_dom_range_select_node (range, block, NULL);
+               webkit_dom_range_collapse (range, TRUE, NULL);
+               webkit_dom_dom_selection_remove_all_ranges (dom_selection);
+               webkit_dom_dom_selection_add_range (dom_selection, range);
+
+               g_clear_object (&range);
+               g_clear_object (&dom_selection);
+               g_clear_object (&dom_window);
+
+               e_editor_dom_remove_input_event_listener_from_body (editor_page);
+               e_editor_page_block_selection_changed (editor_page);
+
+               e_editor_dom_exec_command (
+                       editor_page, E_CONTENT_EDITOR_COMMAND_INSERT_NEW_LINE_IN_QUOTED_CONTENT, NULL);
+
+               e_editor_dom_register_input_event_listener_on_body (editor_page);
+               e_editor_page_unblock_selection_changed (editor_page);
+
+               element = webkit_dom_document_query_selector (
+                       document, "body>br", NULL);
+
+               webkit_dom_node_replace_child (
+                       webkit_dom_node_get_parent_node (WEBKIT_DOM_NODE (element)),
+                       WEBKIT_DOM_NODE (list),
+                       WEBKIT_DOM_NODE (element),
+                       NULL);
+
+               block = e_editor_dom_get_parent_block_node_from_child (
+                       WEBKIT_DOM_NODE (selection_start_marker));
+       } else
+               webkit_dom_node_insert_before (
+                       webkit_dom_node_get_parent_node (block),
+                       WEBKIT_DOM_NODE (list),
+                       block,
+                       NULL);
+
+       /* Process all blocks that are in the selection one by one */
+       while (block && !after_selection_end) {
+               gboolean empty = FALSE, block_is_list;
+               gchar *content;
+               WebKitDOMNode *child, *parent;
+
+               after_selection_end = webkit_dom_node_contains (
+                       block, WEBKIT_DOM_NODE (selection_end_marker));
+
+               next_block = webkit_dom_node_get_next_sibling (
+                       WEBKIT_DOM_NODE (block));
+
+               e_editor_dom_remove_wrapping_from_element (WEBKIT_DOM_ELEMENT (block));
+               e_editor_dom_remove_quoting_from_element (WEBKIT_DOM_ELEMENT (block));
+
+               item = webkit_dom_document_create_element (document, "LI", NULL);
+               content = webkit_dom_node_get_text_content (block);
+
+               empty = !*content || (g_strcmp0 (content, UNICODE_ZERO_WIDTH_SPACE) == 0);
+               g_free (content);
+
+               change_leading_space_to_nbsp (block);
+               change_trailing_space_in_block_to_nbsp (block);
+
+               block_is_list = node_is_list_or_item (block);
+
+               while ((child = webkit_dom_node_get_first_child (block))) {
+                       if (WEBKIT_DOM_IS_HTML_BR_ELEMENT (child))
+                               empty = FALSE;
+
+                       webkit_dom_node_append_child (
+                               WEBKIT_DOM_NODE (block_is_list ? list : item), child, NULL);
+               }
+
+               if (!block_is_list) {
+                       /* We have to use again the hidden space to move caret into newly inserted list */
+                       if (empty) {
+                               WebKitDOMElement *br;
+
+                               br = webkit_dom_document_create_element (
+                                       document, "BR", NULL);
+                               webkit_dom_node_append_child (
+                                       WEBKIT_DOM_NODE (item), WEBKIT_DOM_NODE (br), NULL);
+                       }
+
+                       webkit_dom_node_append_child (
+                               WEBKIT_DOM_NODE (list), WEBKIT_DOM_NODE (item), NULL);
+               }
+
+               parent = webkit_dom_node_get_parent_node (block);
+               remove_node (block);
+
+               if (in_quote) {
+                       /* Remove all parents if previously removed node was the
+                        * only one with text content */
+                       content = webkit_dom_node_get_text_content (parent);
+                       while (parent && content && !*content) {
+                               WebKitDOMNode *tmp = webkit_dom_node_get_parent_node (parent);
+
+                               remove_node (parent);
+                               parent = tmp;
+
+                               g_free (content);
+                               content = webkit_dom_node_get_text_content (parent);
+                       }
+                       g_free (content);
+               }
+
+               block = next_block;
+       }
+
+       merge_lists_if_possible (WEBKIT_DOM_NODE (list));
+}
+
+static WebKitDOMElement *
+do_format_change_list_to_list (WebKitDOMElement *list_to_process,
+                               WebKitDOMElement *new_list_template,
+                               EContentEditorBlockFormat to)
+{
+       EContentEditorBlockFormat current_format;
+
+       current_format = dom_get_list_format_from_node (
+               WEBKIT_DOM_NODE (list_to_process));
+       if (to == current_format) {
+               /* Same format, skip it. */
+               return list_to_process;
+       } else if (current_format >= E_CONTENT_EDITOR_BLOCK_FORMAT_ORDERED_LIST &&
+                  to >= E_CONTENT_EDITOR_BLOCK_FORMAT_ORDERED_LIST) {
+               /* Changing from ordered list type to another ordered list type. */
+               set_ordered_list_type_to_element (list_to_process, to);
+               return list_to_process;
+       } else {
+               WebKitDOMNode *clone, *child;
+
+               /* Create new list from template. */
+               clone = webkit_dom_node_clone_node_with_error (
+                       WEBKIT_DOM_NODE (new_list_template), FALSE, NULL);
+
+               /* Insert it before the list that we are processing. */
+               webkit_dom_node_insert_before (
+                       webkit_dom_node_get_parent_node (
+                               WEBKIT_DOM_NODE (list_to_process)),
+                       clone,
+                       WEBKIT_DOM_NODE (list_to_process),
+                       NULL);
+
+               /* Move all it children to the new one. */
+               while ((child = webkit_dom_node_get_first_child (WEBKIT_DOM_NODE (list_to_process))))
+                       webkit_dom_node_append_child (clone, child, NULL);
+
+               remove_node (WEBKIT_DOM_NODE (list_to_process));
+
+               return WEBKIT_DOM_ELEMENT (clone);
+       }
+
+       return NULL;
+}
+
+static void
+format_change_list_from_list (EEditorPage *editor_page,
+                              EContentEditorBlockFormat to,
+                              gboolean html_mode)
+{
+       WebKitDOMDocument *document;
+       WebKitDOMElement *selection_start_marker, *selection_end_marker, *new_list;
+       WebKitDOMNode *source_list, *source_list_clone, *current_list, *item;
+       gboolean after_selection_end = FALSE;
+
+       g_return_if_fail (E_IS_EDITOR_PAGE (editor_page));
+
+       document = e_editor_page_get_document (editor_page);
+       selection_start_marker = webkit_dom_document_query_selector (
+               document, "span#-x-evo-selection-start-marker", NULL);
+       selection_end_marker = webkit_dom_document_query_selector (
+               document, "span#-x-evo-selection-end-marker", NULL);
+
+       if (!selection_start_marker || !selection_end_marker)
+               return;
+
+       /* Copy elements from previous block to list */
+       item = get_list_item_node_from_child (WEBKIT_DOM_NODE (selection_start_marker));
+       source_list = webkit_dom_node_get_parent_node (item);
+       current_list = source_list;
+       source_list_clone = webkit_dom_node_clone_node_with_error (source_list, FALSE, NULL);
+
+       new_list = create_list_element (editor_page, to, 0, html_mode);
+
+       if (element_has_class (WEBKIT_DOM_ELEMENT (source_list), "-x-evo-indented"))
+               element_add_class (WEBKIT_DOM_ELEMENT (new_list), "-x-evo-indented");
+
+       while (item) {
+               gboolean selection_end;
+               WebKitDOMNode *next_item = webkit_dom_node_get_next_sibling (item);
+
+               selection_end = webkit_dom_node_contains (
+                       item, WEBKIT_DOM_NODE (selection_end_marker));
+
+               if (WEBKIT_DOM_IS_HTML_LI_ELEMENT (item)) {
+                       /* Actual node is an item, just copy it. */
+                       webkit_dom_node_append_child (
+                               after_selection_end ?
+                                       source_list_clone : WEBKIT_DOM_NODE (new_list),
+                               item,
+                               NULL);
+               } else if (node_is_list (item) && !selection_end && !after_selection_end) {
+                       /* Node is a list and it doesn't contain the selection end
+                        * marker, we can process the whole list. */
+                       gint ii;
+                       WebKitDOMNodeList *list = NULL;
+                       WebKitDOMElement *processed_list;
+
+                       list = webkit_dom_element_query_selector_all (
+                               WEBKIT_DOM_ELEMENT (item), "ol,ul", NULL);
+                       ii = webkit_dom_node_list_get_length (list);
+                       g_clear_object (&list);
+
+                       /* Process every sublist separately. */
+                       while (ii) {
+                               WebKitDOMElement *list_to_process;
+
+                               list_to_process = webkit_dom_element_query_selector (
+                                       WEBKIT_DOM_ELEMENT (item), "ol,ul", NULL);
+                               if (list_to_process)
+                                       do_format_change_list_to_list (list_to_process, new_list, to);
+                               ii--;
+                       }
+
+                       /* Process the current list. */
+                       processed_list = do_format_change_list_to_list (
+                               WEBKIT_DOM_ELEMENT (item), new_list, to);
+
+                       webkit_dom_node_append_child (
+                               after_selection_end ?
+                                       source_list_clone : WEBKIT_DOM_NODE (new_list),
+                               WEBKIT_DOM_NODE (processed_list),
+                               NULL);
+               } else if (node_is_list (item) && !after_selection_end) {
+                       /* Node is a list and it contains the selection end marker,
+                        * thus we have to process it until we find the marker. */
+                       gint ii;
+                       WebKitDOMNodeList *list = NULL;
+
+                       list = webkit_dom_element_query_selector_all (
+                               WEBKIT_DOM_ELEMENT (item), "ol,ul", NULL);
+                       ii = webkit_dom_node_list_get_length (list);
+                       g_clear_object (&list);
+
+                       /* No nested lists - process the items. */
+                       if (ii == 0) {
+                               WebKitDOMNode *clone, *child;
+
+                               clone = webkit_dom_node_clone_node_with_error (
+                                       WEBKIT_DOM_NODE (new_list), FALSE, NULL);
+
+                               webkit_dom_node_append_child (
+                                       after_selection_end ?
+                                               source_list_clone : WEBKIT_DOM_NODE (new_list),
+                                       clone,
+                                       NULL);
+
+                               while ((child = webkit_dom_node_get_first_child (item))) {
+                                       webkit_dom_node_append_child (clone, child, NULL);
+                                       if (webkit_dom_node_contains (child, WEBKIT_DOM_NODE 
(selection_end_marker)))
+                                               break;
+                               }
+
+                               if (webkit_dom_node_get_first_child (item))
+                                       webkit_dom_node_append_child (
+                                               after_selection_end ?
+                                                       source_list_clone : WEBKIT_DOM_NODE (new_list),
+                                               item,
+                                               NULL);
+                               else
+                                       remove_node (item);
+                       } else {
+                               gboolean done = FALSE;
+                               WebKitDOMNode *tmp_parent = WEBKIT_DOM_NODE (new_list);
+                               WebKitDOMNode *tmp_item = WEBKIT_DOM_NODE (item);
+
+                               while (!done) {
+                                       WebKitDOMNode *clone, *child;
+
+                                       clone = webkit_dom_node_clone_node_with_error (
+                                               WEBKIT_DOM_NODE (new_list), FALSE, NULL);
+
+                                       webkit_dom_node_append_child (
+                                               tmp_parent, clone, NULL);
+
+                                       while ((child = webkit_dom_node_get_first_child (tmp_item))) {
+                                               if (!webkit_dom_node_contains (child, WEBKIT_DOM_NODE 
(selection_end_marker))) {
+                                                       webkit_dom_node_append_child (clone, child, NULL);
+                                               } else if (WEBKIT_DOM_IS_HTML_LI_ELEMENT (child)) {
+                                                       webkit_dom_node_append_child (clone, child, NULL);
+                                                       done = TRUE;
+                                                       break;
+                                               } else {
+                                                       tmp_parent = clone;
+                                                       tmp_item = child;
+                                                       break;
+                                               }
+                                       }
+                               }
+                       }
+               } else {
+                       webkit_dom_node_append_child (
+                               after_selection_end ?
+                                       source_list_clone : WEBKIT_DOM_NODE (new_list),
+                               item,
+                               NULL);
+               }
+
+               if (selection_end) {
+                       source_list_clone = webkit_dom_node_clone_node_with_error (current_list, FALSE, NULL);
+                       remove_node_if_empty (current_list);
+                       after_selection_end = TRUE;
+               }
+
+               if (!next_item) {
+                       if (after_selection_end)
+                               break;
+
+                       current_list = webkit_dom_node_get_next_sibling (current_list);
+                       if (!node_is_list_or_item (current_list))
+                               break;
+                       if (node_is_list (current_list)) {
+                               next_item = webkit_dom_node_get_first_child (current_list);
+                               if (!node_is_list_or_item (next_item))
+                                       break;
+                       } else if (WEBKIT_DOM_IS_HTML_LI_ELEMENT (current_list)) {
+                               next_item = current_list;
+                               current_list = webkit_dom_node_get_parent_node (next_item);
+                       }
+               }
+
+               item = next_item;
+       }
+
+       webkit_dom_node_insert_before (
+               webkit_dom_node_get_parent_node (source_list),
+               WEBKIT_DOM_NODE (source_list_clone),
+               webkit_dom_node_get_next_sibling (source_list),
+               NULL);
+
+       if (webkit_dom_node_has_child_nodes (WEBKIT_DOM_NODE (new_list)))
+               webkit_dom_node_insert_before (
+                       webkit_dom_node_get_parent_node (source_list_clone),
+                       WEBKIT_DOM_NODE (new_list),
+                       source_list_clone,
+                       NULL);
+
+       remove_node_if_empty (source_list);
+
+       remove_node_if_empty (source_list_clone);
+
+       merge_lists_if_possible (WEBKIT_DOM_NODE (new_list));
+}
+
+static void
+format_change_list_to_list (EEditorPage *editor_page,
+                            EContentEditorBlockFormat format,
+                            gboolean html_mode)
+{
+       WebKitDOMDocument *document;
+       WebKitDOMElement *selection_start_marker, *selection_end_marker;
+       WebKitDOMNode *prev_list, *current_list, *next_list;
+       EContentEditorBlockFormat prev = 0, next = 0;
+       gboolean done = FALSE, indented = FALSE;
+       gboolean selection_starts_in_first_child, selection_ends_in_last_child;
+
+       g_return_if_fail (E_IS_EDITOR_PAGE (editor_page));
+
+       document = e_editor_page_get_document (editor_page);
+       selection_start_marker = webkit_dom_document_query_selector (
+               document, "span#-x-evo-selection-start-marker", NULL);
+       selection_end_marker = webkit_dom_document_query_selector (
+               document, "span#-x-evo-selection-end-marker", NULL);
+
+       current_list = get_list_node_from_child (
+               WEBKIT_DOM_NODE (selection_start_marker));
+
+       prev_list = get_list_node_from_child (
+               WEBKIT_DOM_NODE (selection_start_marker));
+
+       next_list = get_list_node_from_child (
+               WEBKIT_DOM_NODE (selection_end_marker));
+
+       selection_starts_in_first_child =
+               webkit_dom_node_contains (
+                       webkit_dom_node_get_first_child (current_list),
+                       WEBKIT_DOM_NODE (selection_start_marker));
+
+       selection_ends_in_last_child =
+               webkit_dom_node_contains (
+                       webkit_dom_node_get_last_child (current_list),
+                       WEBKIT_DOM_NODE (selection_end_marker));
+
+       indented = element_has_class (WEBKIT_DOM_ELEMENT (current_list), "-x-evo-indented");
+
+       if (!prev_list || !next_list || indented) {
+               format_change_list_from_list (editor_page, format, html_mode);
+               return;
+       }
+
+       if (webkit_dom_node_is_same_node (prev_list, next_list)) {
+               prev_list = webkit_dom_node_get_previous_sibling (
+                       webkit_dom_node_get_parent_node (
+                               webkit_dom_node_get_parent_node (
+                                       WEBKIT_DOM_NODE (selection_start_marker))));
+               next_list = webkit_dom_node_get_next_sibling (
+                       webkit_dom_node_get_parent_node (
+                               webkit_dom_node_get_parent_node (
+                                       WEBKIT_DOM_NODE (selection_end_marker))));
+               if (!prev_list || !next_list) {
+                       format_change_list_from_list (editor_page, format, html_mode);
+                       return;
+               }
+       }
+
+       prev = dom_get_list_format_from_node (prev_list);
+       next = dom_get_list_format_from_node (next_list);
+
+       if (format != E_CONTENT_EDITOR_BLOCK_FORMAT_NONE) {
+               if (format == prev && prev != E_CONTENT_EDITOR_BLOCK_FORMAT_NONE) {
+                       if (selection_starts_in_first_child && selection_ends_in_last_child) {
+                               done = TRUE;
+                               merge_list_into_list (current_list, prev_list, FALSE);
+                       }
+               }
+               if (format == next && next != E_CONTENT_EDITOR_BLOCK_FORMAT_NONE) {
+                       if (selection_starts_in_first_child && selection_ends_in_last_child) {
+                               done = TRUE;
+                               merge_list_into_list (next_list, prev_list, FALSE);
+                       }
+               }
+       }
+
+       if (done)
+               return;
+
+       format_change_list_from_list (editor_page, format, html_mode);
+}
+
+/*
+ * e_html_editor_selection_set_block_format:
+ * @selection: an #EEditorSelection
+ * @format: an #EContentEditorBlockFormat value
+ *
+ * Changes block format of current paragraph to @format.
+ */
+void
+e_editor_dom_selection_set_block_format (EEditorPage *editor_page,
+                                        EContentEditorBlockFormat format)
+{
+       WebKitDOMDocument *document;
+       WebKitDOMRange *range = NULL;
+       EContentEditorBlockFormat current_format;
+       EContentEditorAlignment current_alignment;
+       EEditorUndoRedoManager *manager;
+       EEditorHistoryEvent *ev = NULL;
+       const gchar *value;
+       gboolean from_list = FALSE, to_list = FALSE, html_mode = FALSE;
+
+       g_return_if_fail (E_IS_EDITOR_PAGE (editor_page));
+
+       document = e_editor_page_get_document (editor_page);
+       current_format = e_editor_dom_selection_get_block_format (editor_page);
+       if (current_format == format)
+               return;
+
+       switch (format) {
+               case E_CONTENT_EDITOR_BLOCK_FORMAT_H1:
+                       value = "H1";
+                       break;
+               case E_CONTENT_EDITOR_BLOCK_FORMAT_H2:
+                       value = "H2";
+                       break;
+               case E_CONTENT_EDITOR_BLOCK_FORMAT_H3:
+                       value = "H3";
+                       break;
+               case E_CONTENT_EDITOR_BLOCK_FORMAT_H4:
+                       value = "H4";
+                       break;
+               case E_CONTENT_EDITOR_BLOCK_FORMAT_H5:
+                       value = "H5";
+                       break;
+               case E_CONTENT_EDITOR_BLOCK_FORMAT_H6:
+                       value = "H6";
+                       break;
+               case E_CONTENT_EDITOR_BLOCK_FORMAT_PARAGRAPH:
+                       value = "P";
+                       break;
+               case E_CONTENT_EDITOR_BLOCK_FORMAT_PRE:
+                       value = "PRE";
+                       break;
+               case E_CONTENT_EDITOR_BLOCK_FORMAT_ADDRESS:
+                       value = "ADDRESS";
+                       break;
+               case E_CONTENT_EDITOR_BLOCK_FORMAT_ORDERED_LIST:
+               case E_CONTENT_EDITOR_BLOCK_FORMAT_ORDERED_LIST_ALPHA:
+               case E_CONTENT_EDITOR_BLOCK_FORMAT_ORDERED_LIST_ROMAN:
+                       to_list = TRUE;
+                       value = NULL;
+                       break;
+               case E_CONTENT_EDITOR_BLOCK_FORMAT_UNORDERED_LIST:
+                       to_list = TRUE;
+                       value = NULL;
+                       break;
+               case E_CONTENT_EDITOR_BLOCK_FORMAT_NONE:
+               default:
+                       value = NULL;
+                       break;
+       }
+
+       html_mode = e_editor_page_get_html_mode (editor_page);
+
+       from_list =
+               current_format >= E_CONTENT_EDITOR_BLOCK_FORMAT_UNORDERED_LIST;
+
+       range = e_editor_dom_get_current_range (editor_page);
+       if (!range)
+               return;
+
+       current_alignment = e_editor_page_get_alignment (editor_page);
+
+       e_editor_dom_selection_save (editor_page);
+
+       manager = e_editor_page_get_undo_redo_manager (editor_page);
+       if (!e_editor_undo_redo_manager_is_operation_in_progress (manager)) {
+               ev = g_new0 (EEditorHistoryEvent, 1);
+               ev->type = HISTORY_BLOCK_FORMAT;
+
+               e_editor_dom_selection_get_coordinates (editor_page,
+                       &ev->before.start.x,
+                       &ev->before.start.y,
+                       &ev->before.end.x,
+                       &ev->before.end.y);
+
+               ev->data.style.from = current_format;
+               ev->data.style.to = format;
+       }
+
+       g_clear_object (&range);
+
+       if (current_format == E_CONTENT_EDITOR_BLOCK_FORMAT_PRE) {
+               WebKitDOMElement *selection_marker;
+
+               selection_marker = webkit_dom_document_get_element_by_id (
+                       document, "-x-evo-selection-start-marker");
+               if (selection_marker)
+                       change_space_before_selection_to_nbsp (WEBKIT_DOM_NODE (selection_marker));
+               selection_marker = webkit_dom_document_get_element_by_id (
+                       document, "-x-evo-selection-end-marker");
+               if (selection_marker)
+                       change_space_before_selection_to_nbsp (WEBKIT_DOM_NODE (selection_marker));
+       }
+
+       if (from_list && to_list)
+               format_change_list_to_list (editor_page, format, html_mode);
+
+       if (!from_list && !to_list)
+               format_change_block_to_block (editor_page, format, value);
+
+       if (from_list && !to_list)
+               format_change_list_to_block (editor_page, format, value);
+
+       if (!from_list && to_list)
+               format_change_block_to_list (editor_page, format);
+
+       e_editor_dom_selection_restore (editor_page);
+
+       e_editor_dom_force_spell_check_for_current_paragraph (editor_page);
+
+       /* When changing the format we need to re-set the alignment */
+       e_editor_dom_selection_set_alignment (editor_page, current_alignment);
+
+       e_editor_page_emit_content_changed (editor_page);
+
+       if (ev) {
+               e_editor_dom_selection_get_coordinates (editor_page,
+                       &ev->after.start.x,
+                       &ev->after.start.y,
+                       &ev->after.end.x,
+                       &ev->after.end.y);
+               e_editor_undo_redo_manager_insert_history_event (manager, ev);
+       }
+}
+
+/*
+ * e_html_editor_selection_get_background_color:
+ * @selection: an #EEditorSelection
+ *
+ * Returns background color of currently selected text or letter at current
+ * cursor position.
+ *
+ * Returns: A string with code of current background color.
+ */
+gchar *
+e_editor_dom_selection_get_background_color (EEditorPage *editor_page)
+{
+       WebKitDOMNode *ancestor;
+       WebKitDOMRange *range = NULL;
+       WebKitDOMCSSStyleDeclaration *css = NULL;
+       gchar *value;
+
+       g_return_val_if_fail (E_IS_EDITOR_PAGE (editor_page), NULL);
+
+       range = e_editor_dom_get_current_range (editor_page);
+       ancestor = webkit_dom_range_get_common_ancestor_container (range, NULL);
+       css = webkit_dom_element_get_style (WEBKIT_DOM_ELEMENT (ancestor));
+/* FIXME WK2
+       g_free (selection->priv->background_color);
+       selection->priv->background_color =
+               webkit_dom_css_style_declaration_get_property_value (
+                       css, "background-color");*/
+
+       value = webkit_dom_css_style_declaration_get_property_value (css, "background-color");
+
+       g_clear_object (&css);
+       g_clear_object (&range);
+
+       return value;
+}
+
+/*
+ * e_html_editor_selection_set_background_color:
+ * @selection: an #EEditorSelection
+ * @color: code of new background color to set
+ *
+ * Changes background color of current selection or letter at current cursor
+ * position to @color.
+ */
+void
+e_editor_dom_selection_set_background_color (EEditorPage *editor_page,
+                                            const gchar *color)
+{
+       g_return_if_fail (E_IS_EDITOR_PAGE (editor_page));
+
+       e_editor_dom_exec_command (editor_page, E_CONTENT_EDITOR_COMMAND_BACKGROUND_COLOR, color);
+}
+
+/*
+ * e_html_editor_selection_get_alignment:
+ * @selection: #an EEditorSelection
+ *
+ * Returns alignment of current paragraph
+ *
+ * Returns: #EContentEditorAlignment
+ */
+EContentEditorAlignment
+e_editor_dom_selection_get_alignment (EEditorPage *editor_page)
+{
+       WebKitDOMCSSStyleDeclaration *style = NULL;
+       WebKitDOMElement *element;
+       WebKitDOMNode *node;
+       WebKitDOMRange *range = NULL;
+       EContentEditorAlignment alignment;
+       gchar *value;
+
+       g_return_val_if_fail (E_IS_EDITOR_PAGE (editor_page), E_CONTENT_EDITOR_ALIGNMENT_LEFT);
+
+       range = e_editor_dom_get_current_range (editor_page);
+       if (!range) {
+               alignment = E_CONTENT_EDITOR_ALIGNMENT_LEFT;
+               goto out;
+       }
+
+       node = webkit_dom_range_get_start_container (range, NULL);
+       g_clear_object (&range);
+       if (!node) {
+               alignment = E_CONTENT_EDITOR_ALIGNMENT_LEFT;
+               goto out;
+       }
+
+       if (WEBKIT_DOM_IS_ELEMENT (node))
+               element = WEBKIT_DOM_ELEMENT (node);
+       else
+               element = webkit_dom_node_get_parent_element (node);
+
+       if (element_has_class (element, "-x-evo-align-right")) {
+               alignment = E_CONTENT_EDITOR_ALIGNMENT_RIGHT;
+               goto out;
+       } else if (element_has_class (element, "-x-evo-align-center")) {
+               alignment = E_CONTENT_EDITOR_ALIGNMENT_CENTER;
+               goto out;
+       }
+
+       style = webkit_dom_element_get_style (element);
+       value = webkit_dom_css_style_declaration_get_property_value (style, "text-align");
+
+       if (!value || !*value ||
+           (g_ascii_strncasecmp (value, "left", 4) == 0)) {
+               alignment = E_CONTENT_EDITOR_ALIGNMENT_LEFT;
+       } else if (g_ascii_strncasecmp (value, "center", 6) == 0) {
+               alignment = E_CONTENT_EDITOR_ALIGNMENT_CENTER;
+       } else if (g_ascii_strncasecmp (value, "right", 5) == 0) {
+               alignment = E_CONTENT_EDITOR_ALIGNMENT_RIGHT;
+       } else {
+               alignment = E_CONTENT_EDITOR_ALIGNMENT_LEFT;
+       }
+
+       g_clear_object (&style);
+       g_free (value);
+
+ out:
+       return alignment;
+}
+
+static void
+set_block_alignment (WebKitDOMElement *element,
+                     const gchar *class)
+{
+       WebKitDOMElement *parent;
+
+       element_remove_class (element, "-x-evo-align-center");
+       element_remove_class (element, "-x-evo-align-right");
+       element_add_class (element, class);
+       parent = webkit_dom_node_get_parent_element (WEBKIT_DOM_NODE (element));
+       while (parent && !WEBKIT_DOM_IS_HTML_BODY_ELEMENT (parent)) {
+               element_remove_class (parent, "-x-evo-align-center");
+               element_remove_class (parent, "-x-evo-align-right");
+               parent = webkit_dom_node_get_parent_element (
+                       WEBKIT_DOM_NODE (parent));
+       }
+}
+
+/*
+ * e_html_editor_selection_set_alignment:
+ * @selection: an #EEditorSelection
+ * @alignment: an #EContentEditorAlignment value to apply
+ *
+ * Sets alignment of current paragraph to give @alignment.
+ */
+void
+e_editor_dom_selection_set_alignment (EEditorPage *editor_page,
+                                     EContentEditorAlignment alignment)
+{
+       WebKitDOMDocument *document;
+       WebKitDOMElement *selection_start_marker, *selection_end_marker;
+       WebKitDOMNode *block;
+       EContentEditorAlignment current_alignment;
+       EEditorUndoRedoManager *manager;
+       EEditorHistoryEvent *ev = NULL;
+       gboolean after_selection_end = FALSE;
+       const gchar *class = "";
+
+       g_return_if_fail (E_IS_EDITOR_PAGE (editor_page));
+
+       document = e_editor_page_get_document (editor_page);
+       current_alignment = e_editor_page_get_alignment (editor_page);
+
+       if (current_alignment == alignment)
+               return;
+
+       switch (alignment) {
+               case E_CONTENT_EDITOR_ALIGNMENT_CENTER:
+                       class = "-x-evo-align-center";
+                       break;
+
+               case E_CONTENT_EDITOR_ALIGNMENT_LEFT:
+                       break;
+
+               case E_CONTENT_EDITOR_ALIGNMENT_RIGHT:
+                       class = "-x-evo-align-right";
+                       break;
+       }
+
+       e_editor_dom_selection_save (editor_page);
+
+       selection_start_marker = webkit_dom_document_query_selector (
+               document, "span#-x-evo-selection-start-marker", NULL);
+       selection_end_marker = webkit_dom_document_query_selector (
+               document, "span#-x-evo-selection-end-marker", NULL);
+
+       if (!selection_start_marker)
+               return;
+
+       manager = e_editor_page_get_undo_redo_manager (editor_page);
+       if (!e_editor_undo_redo_manager_is_operation_in_progress (manager)) {
+               ev = g_new0 (EEditorHistoryEvent, 1);
+               ev->type = HISTORY_ALIGNMENT;
+
+               e_editor_dom_selection_get_coordinates (editor_page,
+                       &ev->before.start.x,
+                       &ev->before.start.y,
+                       &ev->before.end.x,
+                       &ev->before.end.y);
+               ev->data.style.from = current_alignment;
+               ev->data.style.to = alignment;
+       }
+
+       block = e_editor_dom_get_parent_block_node_from_child (
+               WEBKIT_DOM_NODE (selection_start_marker));
+
+       while (block && !after_selection_end) {
+               WebKitDOMNode *next_block;
+
+               next_block = webkit_dom_node_get_next_sibling (block);
+
+               after_selection_end = webkit_dom_node_contains (
+                       block, WEBKIT_DOM_NODE (selection_end_marker));
+
+               if (element_has_class (WEBKIT_DOM_ELEMENT (block), "-x-evo-indented")) {
+                       gint ii, length;
+                       WebKitDOMNodeList *list = NULL;
+
+                       list = webkit_dom_element_query_selector_all (
+                               WEBKIT_DOM_ELEMENT (block),
+                               ".-x-evo-indented > *:not(.-x-evo-indented):not(li)",
+                               NULL);
+                       length = webkit_dom_node_list_get_length (list);
+
+                       for (ii = 0; ii < length; ii++) {
+                               WebKitDOMNode *item = webkit_dom_node_list_item (list, ii);
+
+                               set_block_alignment (WEBKIT_DOM_ELEMENT (item), class);
+
+                               after_selection_end = webkit_dom_node_contains (
+                                       item, WEBKIT_DOM_NODE (selection_end_marker));
+                               g_object_unref (item);
+                               if (after_selection_end)
+                                       break;
+                       }
+
+                       g_clear_object (&list);
+               } else {
+                       set_block_alignment (WEBKIT_DOM_ELEMENT (block), class);
+               }
+
+               block = next_block;
+       }
+
+       if (ev) {
+               e_editor_dom_selection_get_coordinates (editor_page,
+                       &ev->after.start.x,
+                       &ev->after.start.y,
+                       &ev->after.end.x,
+                       &ev->after.end.y);
+               e_editor_undo_redo_manager_insert_history_event (manager, ev);
+       }
+
+       e_editor_dom_selection_restore (editor_page);
+
+       e_editor_dom_force_spell_check_for_current_paragraph (editor_page);
+}
+
+/*
+ * e_html_editor_selection_replace:
+ * @selection: an #EEditorSelection
+ * @replacement: a string to replace current selection with
+ *
+ * Replaces currently selected text with @replacement.
+ */
+void
+e_editor_dom_selection_replace (EEditorPage *editor_page,
+                               const gchar *replacement)
+{
+       EEditorHistoryEvent *ev = NULL;
+       EEditorUndoRedoManager *manager;
+
+       g_return_if_fail (E_IS_EDITOR_PAGE (editor_page));
+
+       manager = e_editor_page_get_undo_redo_manager (editor_page);
+
+       if (!e_editor_undo_redo_manager_is_operation_in_progress (manager)) {
+               WebKitDOMRange *range = NULL;
+
+               ev = g_new0 (EEditorHistoryEvent, 1);
+               ev->type = HISTORY_REPLACE;
+
+               e_editor_dom_selection_get_coordinates (editor_page,
+                       &ev->before.start.x,
+                       &ev->before.start.y,
+                       &ev->before.end.x,
+                       &ev->before.end.y);
+
+               range = e_editor_dom_get_current_range (editor_page);
+
+               ev->data.string.from = webkit_dom_range_get_text (range);
+               ev->data.string.to = g_strdup (replacement);
+
+               g_clear_object (&range);
+       }
+
+       e_editor_dom_exec_command (editor_page, E_CONTENT_EDITOR_COMMAND_INSERT_TEXT, replacement);
+
+       if (ev) {
+               e_editor_dom_selection_get_coordinates (editor_page,
+                       &ev->after.start.x,
+                       &ev->after.start.y,
+                       &ev->after.end.x,
+                       &ev->after.end.y);
+
+               e_editor_undo_redo_manager_insert_history_event (manager, ev);
+       }
+
+       e_editor_dom_force_spell_check_for_current_paragraph (editor_page);
+
+       e_editor_page_emit_content_changed (editor_page);
+}
+
+/*
+ * e_html_editor_selection_replace_caret_word:
+ * @selection: an #EEditorSelection
+ * @replacement: a string to replace current caret word with
+ *
+ * Replaces current word under cursor with @replacement.
+ */
+void
+e_editor_dom_replace_caret_word (EEditorPage *editor_page,
+                                const gchar *replacement)
+{
+       WebKitDOMDocument *document;
+       WebKitDOMDOMWindow *dom_window = NULL;
+       WebKitDOMDOMSelection *dom_selection = NULL;
+       WebKitDOMDocumentFragment *fragment;
+       WebKitDOMNode *node;
+       WebKitDOMRange *range = NULL;
+
+       g_return_if_fail (E_IS_EDITOR_PAGE (editor_page));
+
+       document = e_editor_page_get_document (editor_page);
+       dom_window = webkit_dom_document_get_default_view (document);
+       dom_selection = webkit_dom_dom_window_get_selection (dom_window);
+       g_clear_object (&dom_window);
+
+       e_editor_page_emit_content_changed (editor_page);
+       range = e_editor_dom_get_current_range (editor_page);
+       webkit_dom_range_expand (range, "word", NULL);
+       webkit_dom_dom_selection_add_range (dom_selection, range);
+
+       fragment = webkit_dom_range_extract_contents (range, NULL);
+
+       /* Get the text node to replace and leave other formatting nodes
+        * untouched (font color, boldness, ...). */
+       webkit_dom_node_normalize (WEBKIT_DOM_NODE (fragment));
+       node = webkit_dom_node_get_first_child (WEBKIT_DOM_NODE (fragment));
+       if (!WEBKIT_DOM_IS_TEXT (node)) {
+               while (node && WEBKIT_DOM_IS_ELEMENT (node))
+                       node = webkit_dom_node_get_first_child (node);
+       }
+
+       if (node && WEBKIT_DOM_IS_TEXT (node)) {
+               WebKitDOMText *text;
+
+               /* Replace the word */
+               text = webkit_dom_document_create_text_node (document, replacement);
+               webkit_dom_node_replace_child (
+                       webkit_dom_node_get_parent_node (node),
+                       WEBKIT_DOM_NODE (text),
+                       node,
+                       NULL);
+
+               /* Insert the word on current location. */
+               webkit_dom_range_insert_node (range, WEBKIT_DOM_NODE (fragment), NULL);
+
+               webkit_dom_dom_selection_collapse_to_end (dom_selection, NULL);
+       }
+
+       e_editor_dom_force_spell_check_for_current_paragraph (editor_page);
+
+       g_clear_object (&range);
+       g_clear_object (&dom_selection);
+}
+
+/*
+ * e_html_editor_selection_get_caret_word:
+ * @selection: an #EEditorSelection
+ *
+ * Returns word under cursor.
+ *
+ * Returns: A newly allocated string with current caret word or @NULL when there
+ * is no text under cursor or when selection is active. [transfer-full].
+ */
+gchar *
+e_editor_dom_get_caret_word (EEditorPage *editor_page)
+{
+       gchar *word;
+       WebKitDOMRange *range = NULL, *range_clone = NULL;
+
+       g_return_val_if_fail (E_IS_EDITOR_PAGE (editor_page), NULL);
+
+       range = e_editor_dom_get_current_range (editor_page);
+
+       /* Don't operate on the visible selection */
+       range_clone = webkit_dom_range_clone_range (range, NULL);
+       webkit_dom_range_expand (range_clone, "word", NULL);
+       word = webkit_dom_range_to_string (range_clone, NULL);
+
+       g_clear_object (&range);
+       g_clear_object (&range_clone);
+
+       return word;
+}
+
+/*
+ * e_html_editor_selection_get_list_alignment_from_node:
+ * @node: #an WebKitDOMNode
+ *
+ * Returns alignment of given list.
+ *
+ * Returns: #EContentEditorAlignment
+ */
+EContentEditorAlignment
+e_editor_dom_get_list_alignment_from_node (WebKitDOMNode *node)
+{
+       if (element_has_class (WEBKIT_DOM_ELEMENT (node), "-x-evo-align-center"))
+               return E_CONTENT_EDITOR_ALIGNMENT_CENTER;
+       if (element_has_class (WEBKIT_DOM_ELEMENT (node), "-x-evo-align-right"))
+               return E_CONTENT_EDITOR_ALIGNMENT_RIGHT;
+       else
+               return E_CONTENT_EDITOR_ALIGNMENT_LEFT;
+}
+
+WebKitDOMElement *
+e_editor_dom_prepare_paragraph (EEditorPage *editor_page,
+                               gboolean with_selection)
+{
+       WebKitDOMDocument *document;
+       WebKitDOMElement *element, *paragraph;
+
+       g_return_val_if_fail (E_IS_EDITOR_PAGE (editor_page), NULL);
+
+       document = e_editor_page_get_document (editor_page);
+       paragraph = e_editor_dom_get_paragraph_element (editor_page, -1, 0);
+
+       if (with_selection)
+               dom_add_selection_markers_into_element_start (
+                       document, paragraph, NULL, NULL);
+
+       element = webkit_dom_document_create_element (document, "BR", NULL);
+
+       webkit_dom_node_append_child (
+               WEBKIT_DOM_NODE (paragraph), WEBKIT_DOM_NODE (element), NULL);
+
+       return paragraph;
+}
+
+void
+e_editor_dom_selection_set_on_point (EEditorPage *editor_page,
+                                    guint x,
+                                    guint y)
+{
+       WebKitDOMDocument *document;
+       WebKitDOMRange *range = NULL;
+       WebKitDOMDOMWindow *dom_window = NULL;
+       WebKitDOMDOMSelection *dom_selection = NULL;
+
+       g_return_if_fail (E_IS_EDITOR_PAGE (editor_page));
+
+       document = e_editor_page_get_document (editor_page);
+       dom_window = webkit_dom_document_get_default_view (document);
+       dom_selection = webkit_dom_dom_window_get_selection (dom_window);
+
+       range = webkit_dom_document_caret_range_from_point (document, x, y);
+       webkit_dom_dom_selection_remove_all_ranges (dom_selection);
+       webkit_dom_dom_selection_add_range (dom_selection, range);
+
+       g_clear_object (&range);
+       g_clear_object (&dom_selection);
+       g_clear_object (&dom_window);
+}
+
+void
+e_editor_dom_selection_get_coordinates (EEditorPage *editor_page,
+                                       guint *start_x,
+                                       guint *start_y,
+                                       guint *end_x,
+                                       guint *end_y)
+{
+       WebKitDOMDocument *document;
+       WebKitDOMElement *element, *parent;
+       gboolean created_selection_markers = FALSE;
+       guint local_x = 0, local_y = 0;
+
+       g_return_if_fail (E_IS_EDITOR_PAGE (editor_page));
+       g_return_if_fail (start_x != NULL);
+       g_return_if_fail (start_y != NULL);
+       g_return_if_fail (end_x != NULL);
+       g_return_if_fail (end_y != NULL);
+
+       document = e_editor_page_get_document (editor_page);
+       element = webkit_dom_document_get_element_by_id (
+               document, "-x-evo-selection-start-marker");
+       if (!element) {
+               created_selection_markers = TRUE;
+               e_editor_dom_selection_save (editor_page);
+               element = webkit_dom_document_get_element_by_id (
+                       document, "-x-evo-selection-start-marker");
+               if (!element)
+                       return;
+       }
+
+       parent = element;
+       while (parent && !WEBKIT_DOM_IS_HTML_BODY_ELEMENT (parent)) {
+               local_x += (guint) webkit_dom_element_get_offset_left (parent);
+               local_y += (guint) webkit_dom_element_get_offset_top (parent);
+               parent = webkit_dom_element_get_offset_parent (parent);
+       }
+
+       if (start_x)
+               *start_x = local_x;
+       if (start_y)
+               *start_y = local_y;
+
+       if (e_editor_dom_selection_is_collapsed (editor_page)) {
+               *end_x = local_x;
+               *end_y = local_y;
+
+               if (created_selection_markers)
+                       e_editor_dom_selection_restore (editor_page);
+
+               goto workaroud;
+       }
+
+       element = webkit_dom_document_get_element_by_id (
+               document, "-x-evo-selection-end-marker");
+
+       local_x = 0;
+       local_y = 0;
+
+       parent = element;
+       while (parent && !WEBKIT_DOM_IS_HTML_BODY_ELEMENT (parent)) {
+               local_x += (guint) webkit_dom_element_get_offset_left (parent);
+               local_y += (guint) webkit_dom_element_get_offset_top (parent);
+               parent = webkit_dom_element_get_offset_parent (parent);
+       }
+
+       if (end_x)
+               *end_x = local_x;
+       if (end_y)
+               *end_y = local_y;
+
+       if (created_selection_markers)
+               e_editor_dom_selection_restore (editor_page);
+
+ workaroud:
+       /* Workaround for bug 749712 on the Evolution side. The cause of the bug
+        * is that WebKit is having problems determining the right line height
+        * for some fonts and font sizes (the right and wrong value differ by 1).
+        * To fix this we will add an extra one to the final top offset. This is
+        * safe to do even for fonts and font sizes that don't behave badly as we
+        * will still get the right element as we use fonts bigger than 1 pixel. */
+       *start_y += 1;
+       *end_y += 1;
+}
diff --git a/modules/webkit-editor/web-extension/e-editor-dom-functions.h 
b/modules/webkit-editor/web-extension/e-editor-dom-functions.h
new file mode 100644
index 0000000..747dd11
--- /dev/null
+++ b/modules/webkit-editor/web-extension/e-editor-dom-functions.h
@@ -0,0 +1,378 @@
+/*
+ * Copyright (C) 2016 Red Hat, Inc. (www.redhat.com)
+ *
+ * This library 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 library 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 Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef E_EDITOR_DOM_FUNCTIONS_H
+#define E_EDITOR_DOM_FUNCTIONS_H
+
+#include <webkitdom/webkitdom.h>
+
+#define E_UTIL_INCLUDE_WITHOUT_WEBKIT
+#include <e-util/e-util.h>
+#undef E_UTIL_INCLUDE_WITHOUT_WEBKIT
+
+#include "e-editor-page.h"
+
+#define UNICODE_ZERO_WIDTH_SPACE "\xe2\x80\x8b"
+#define UNICODE_NBSP "\xc2\xa0"
+
+/* stephenhay from https://mathiasbynens.be/demo/url-regex */
+#define URL_PROTOCOLS "news|telnet|nntp|file|https?|s?ftp|webcal|localhost|ssh"
+#define URL_PATTERN_BASE "(?=((?:(?:(?:" URL_PROTOCOLS 
")\\:\\/\\/)|(?:www\\.|ftp\\.))[^\\s\\/\\$\\.\\?#].[^\\s]*)"
+#define URL_PATTERN_NO_NBSP ")((?:(?!&nbsp;).)*)"
+#define URL_PATTERN URL_PATTERN_BASE URL_PATTERN_NO_NBSP
+#define URL_PATTERN_SPACE URL_PATTERN_BASE "\\s$" URL_PATTERN_NO_NBSP
+/* Taken from camel-url-scanner.c */
+#define URL_INVALID_TRAILING_CHARS ",.:;?!-|}])\""
+
+/* http://www.w3.org/TR/html5/forms.html#valid-e-mail-address */
+#define E_MAIL_PATTERN \
+       "[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}"\
+       "[a-zA-Z0-9])?(?:\\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*"
+
+#define E_MAIL_PATTERN_SPACE E_MAIL_PATTERN "\\s"
+
+#define QUOTE_SYMBOL ">"
+
+#define SPACES_PER_INDENTATION 3
+#define SPACES_PER_LIST_LEVEL 3
+#define SPACES_ORDERED_LIST_FIRST_LEVEL 6
+#define TAB_LENGTH 8
+#define MINIMAL_PARAGRAPH_WIDTH 5
+
+G_BEGIN_DECLS
+
+/* ******************** Tests ******************** */
+
+gboolean       e_editor_dom_test_html_equal    (WebKitDOMDocument *document,
+                                                const gchar *html1,
+                                                const gchar *html2);
+
+/* ******************** Actions ******************** */
+
+void           e_editor_dom_delete_cell_contents
+                                               (EEditorPage *editor_page);
+void           e_editor_dom_delete_column      (EEditorPage *editor_page);
+void           e_editor_dom_delete_row         (EEditorPage *editor_page);
+void           e_editor_dom_delete_table       (EEditorPage *editor_page);
+void           e_editor_dom_insert_column_after
+                                               (EEditorPage *editor_page);
+void           e_editor_dom_insert_column_before
+                                               (EEditorPage *editor_page);
+void           e_editor_dom_insert_row_above   (EEditorPage *editor_page);
+void           e_editor_dom_insert_row_below   (EEditorPage *editor_page);
+void           e_editor_dom_save_history_for_cut
+                                               (EEditorPage *editor_page);
+
+/* ******************** View ******************** */
+
+gboolean       e_editor_dom_exec_command       (EEditorPage *editor_page,
+                                                EContentEditorCommand command,
+                                                const gchar *value);
+void           e_editor_dom_force_spell_check_for_current_paragraph
+                                               (EEditorPage *editor_page);
+void           e_editor_dom_force_spell_check_in_viewport
+                                               (EEditorPage *editor_page);
+void           e_editor_dom_force_spell_check  (EEditorPage *editor_page);
+void           e_editor_dom_turn_spell_check_off
+                                               (EEditorPage *editor_page);
+void           e_editor_dom_embed_style_sheet  (EEditorPage *editor_page,
+                                                const gchar *style_sheet_content);
+void           e_editor_dom_remove_embedded_style_sheet
+                                               (EEditorPage *editor_page);
+void           e_editor_dom_register_input_event_listener_on_body
+                                               (EEditorPage *editor_page);
+void           e_editor_dom_remove_input_event_listener_from_body
+                                               (EEditorPage *editor_page);
+void           e_editor_dom_quote_and_insert_text_into_selection
+                                               (EEditorPage *editor_page,
+                                                const gchar *text,
+                                                gboolean is_html);
+void           e_editor_dom_check_magic_links  (EEditorPage *editor_page,
+                                                gboolean include_space_by_user);
+void           e_editor_dom_insert_smiley      (EEditorPage *editor_page,
+                                                 EEmoticon *emoticon);
+void           e_editor_dom_insert_smiley_by_name
+                                               (EEditorPage *editor_page,
+                                                const gchar *name);
+void           e_editor_dom_check_magic_smileys
+                                               (EEditorPage *editor_page);
+void           e_editor_dom_set_monospace_font_family_on_body
+                                               (WebKitDOMElement *body,
+                                                gboolean html_mode);
+void           e_editor_dom_convert_content    (EEditorPage *editor_page,
+                                                const gchar *preferred_text);
+void           e_editor_dom_convert_and_insert_html_into_selection
+                                               (EEditorPage *editor_page,
+                                                const gchar *html,
+                                                gboolean is_html);
+gboolean       e_editor_dom_node_is_citation_node
+                                               (WebKitDOMNode *node);
+void           e_editor_dom_quote_plain_text_element_after_wrapping
+                                               (EEditorPage *editor_page,
+                                                WebKitDOMElement *element,
+                                                gint quote_level);
+WebKitDOMNode * e_editor_dom_get_parent_block_node_from_child
+                                               (WebKitDOMNode *node);
+WebKitDOMElement *
+               e_editor_dom_insert_new_line_into_citation
+                                               (EEditorPage *editor_page,
+                                                const gchar *html_to_insert);
+WebKitDOMElement *
+               e_editor_dom_quote_plain_text_element
+                                               (EEditorPage *editor_page,
+                                                WebKitDOMElement *element);
+void           e_editor_dom_convert_when_changing_composer_mode
+                                               (EEditorPage *editor_page);
+void           e_editor_dom_process_content_after_load
+                                               (EEditorPage *editor_page);
+GVariant *     e_editor_dom_get_inline_images_data
+                                               (EEditorPage *editor_page,
+                                                const gchar *uid_domain);
+void           e_editor_dom_insert_html        (EEditorPage *editor_page,
+                                                const gchar *html_text);
+void           e_editor_dom_convert_element_from_html_to_plain_text
+                                               (EEditorPage *editor_page,
+                                                WebKitDOMElement *element);
+gchar *                e_editor_dom_process_content_for_draft
+                                               (EEditorPage *editor_page,
+                                                gboolean only_inner_body);
+gchar *                e_editor_dom_process_content_to_plain_text_for_exporting
+                                               (EEditorPage *editor_page);
+void           e_editor_dom_restore_images     (EEditorPage *editor_page,
+                                                GVariant *inline_images_to_restore);
+gchar *                e_editor_dom_process_content_to_html_for_exporting
+                                               (EEditorPage *editor_page);
+gboolean       e_editor_dom_check_if_conversion_needed
+                                               (EEditorPage *editor_page);
+void           e_editor_dom_process_content_after_mode_change
+                                               (EEditorPage *editor_page);
+guint          e_editor_dom_get_caret_offset   (EEditorPage *editor_page);
+guint          e_editor_dom_get_caret_position (EEditorPage *editor_page);
+void           e_editor_dom_drag_and_drop_end  (EEditorPage *editor_page);
+void           e_editor_dom_set_link_color     (EEditorPage *editor_page,
+                                                const gchar *color);
+void           e_editor_dom_set_visited_link_color
+                                               (EEditorPage *editor_page,
+                                                const gchar *color);
+gboolean       e_editor_dom_move_quoted_block_level_up
+                                               (EEditorPage *editor_page);
+gboolean       e_editor_dom_delete_last_character_on_line_in_quoted_block
+                                               (EEditorPage *editor_page,
+                                                glong key_code,
+                                                gboolean control_key);
+gboolean       e_editor_dom_fix_structure_after_delete_before_quoted_content
+                                               (EEditorPage *editor_page,
+                                                glong key_code,
+                                                gboolean control_key,
+                                                gboolean delete_key);
+void           e_editor_dom_disable_quote_marks_select
+                                               (EEditorPage *editor_page);
+void           e_editor_dom_remove_node_and_parents_if_empty
+                                               (WebKitDOMNode *node);
+gboolean       e_editor_dom_return_pressed_in_empty_list_item
+                                               (EEditorPage *editor_page);
+void           e_editor_dom_merge_siblings_if_necessary
+                                               (EEditorPage *editor_page,
+                                                WebKitDOMDocumentFragment *deleted_content);
+void           e_editor_dom_body_key_up_event_process_return_key
+                                               (EEditorPage *editor_page);
+gboolean       e_editor_dom_key_press_event_process_backspace_key
+                                               (EEditorPage *editor_page);
+gboolean       e_editor_dom_key_press_event_process_delete_or_backspace_key
+                                               (EEditorPage *editor_page,
+                                                glong key_code,
+                                                gboolean control_key,
+                                                gboolean delete);
+void           e_editor_dom_body_input_event_process
+                                               (EEditorPage *editor_page,
+                                                WebKitDOMEvent *event);
+void           e_editor_dom_body_key_up_event_process_backspace_or_delete
+                                               (EEditorPage *editor_page,
+                                                gboolean delete);
+gboolean       e_editor_dom_key_press_event_process_return_key
+                                               (EEditorPage *editor_page);
+WebKitDOMElement *
+               e_editor_dom_wrap_and_quote_element
+                                               (EEditorPage *editor_page,
+                                                WebKitDOMElement *element);
+gint           e_editor_dom_get_citation_level (WebKitDOMNode *node,
+                                                gboolean set_plaintext_quoted);
+void           e_editor_dom_save_history_for_drop
+                                               (EEditorPage *editor_page);
+void           e_editor_dom_fix_file_uri_images
+                                               (EEditorPage *editor_page);
+
+/* ******************** Selection ******************** */
+
+void           e_editor_dom_replace_base64_image_src
+                                               (EEditorPage *editor_page,
+                                                const gchar *selector,
+                                                const gchar *base64_content,
+                                                const gchar *filename,
+                                                const gchar *uri);
+WebKitDOMRange *
+               e_editor_dom_get_current_range  (EEditorPage *editor_page);
+void           e_editor_dom_move_caret_into_element
+                                               (EEditorPage *editor_page,
+                                                WebKitDOMElement *element,
+                                                gboolean to_start);
+void           e_editor_dom_insert_base64_image
+                                               (EEditorPage *editor_page,
+                                                const gchar *base64_content,
+                                                const gchar *filename,
+                                                const gchar *uri);
+void           e_editor_dom_insert_image       (EEditorPage *editor_page,
+                                                const gchar *uri);
+void           e_editor_dom_replace_image_src  (EEditorPage *editor_page,
+                                                const gchar *selector,
+                                                const gchar *uri);
+void           e_editor_dom_selection_unlink   (EEditorPage *editor_page);
+void           e_editor_dom_create_link        (EEditorPage *editor_page,
+                                                const gchar *uri);
+void           e_editor_dom_selection_indent   (EEditorPage *editor_page);
+void           e_editor_dom_selection_unindent (EEditorPage *editor_page);
+void           e_editor_dom_selection_save     (EEditorPage *editor_page);
+void           e_editor_dom_selection_restore  (EEditorPage *editor_page);
+gboolean       e_editor_dom_selection_is_collapsed
+                                               (EEditorPage *editor_page);
+void           e_editor_dom_scroll_to_caret    (EEditorPage *editor_page);
+void           e_editor_dom_remove_wrapping_from_element
+                                               (WebKitDOMElement *element);
+void           e_editor_dom_remove_quoting_from_element
+                                               (WebKitDOMElement *element);
+void           e_editor_dom_set_paragraph_style
+                                               (EEditorPage *editor_page,
+                                                WebKitDOMElement *element,
+                                                gint width,
+                                                gint offset,
+                                                const gchar *style_to_add);
+WebKitDOMElement *
+               e_editor_dom_get_paragraph_element
+                                               (EEditorPage *editor_page,
+                                                gint width,
+                                                gint offset);
+WebKitDOMElement *
+               e_editor_dom_put_node_into_paragraph
+                                               (EEditorPage *editor_page,
+                                                WebKitDOMNode *node,
+                                                gboolean with_input);
+void           e_editor_dom_selection_wrap     (EEditorPage *editor_page);
+WebKitDOMElement *
+               e_editor_dom_wrap_paragraph_length
+                                               (EEditorPage *editor_page,
+                                                WebKitDOMElement *paragraph,
+                                                gint length);
+WebKitDOMElement *
+               e_editor_dom_wrap_paragraph     (EEditorPage *editor_page,
+                                                WebKitDOMElement *paragraph);
+void           e_editor_dom_wrap_paragraphs_in_document
+                                               (EEditorPage *editor_page);
+gboolean       e_editor_dom_selection_is_underline
+                                               (EEditorPage *editor_page);
+void           e_editor_dom_selection_set_underline
+                                               (EEditorPage *editor_page,
+                                                gboolean underline);
+gboolean       e_editor_dom_selection_is_subscript
+                                               (EEditorPage *editor_page);
+void           e_editor_dom_selection_set_subscript
+                                               (EEditorPage *editor_page,
+                                                gboolean subscript);
+gboolean       e_editor_dom_selection_is_superscript
+                                               (EEditorPage *editor_page);
+void           e_editor_dom_selection_set_superscript
+                                               (EEditorPage *editor_page,
+                                                gboolean superscript);
+gboolean       e_editor_dom_selection_is_strikethrough
+                                               (EEditorPage *editor_page);
+void           e_editor_dom_selection_set_strikethrough
+                                               (EEditorPage *editor_page,
+                                                gboolean strikethrough);
+gboolean       e_editor_dom_selection_is_monospace
+                                               (EEditorPage *editor_page);
+void           e_editor_dom_selection_set_monospace
+                                               (EEditorPage *editor_page,
+                                                gboolean monospaced);
+gboolean       e_editor_dom_selection_is_bold  (EEditorPage *editor_page);
+void           e_editor_dom_selection_set_bold (EEditorPage *editor_page,
+                                                gboolean bold);
+gboolean       e_editor_dom_selection_is_italic
+                                               (EEditorPage *editor_page);
+void           e_editor_dom_selection_set_italic
+                                               (EEditorPage *editor_page,
+                                                gboolean italic);
+gboolean       e_editor_dom_selection_is_indented
+                                               (EEditorPage *editor_page);
+gboolean       e_editor_dom_selection_is_citation
+                                               (EEditorPage *editor_page);
+guint          e_editor_dom_selection_get_font_size
+                                               (EEditorPage *editor_page);
+void           e_editor_dom_selection_set_font_size
+                                               (EEditorPage *editor_page,
+                                                guint font_size);
+gchar *                e_editor_dom_selection_get_font_name
+                                               (EEditorPage *editor_page);
+void           e_editor_dom_selection_set_font_name
+                                               (EEditorPage *editor_page,
+                                                const gchar *font_size);
+gchar *                e_editor_dom_selection_get_font_color
+                                               (EEditorPage *editor_page);
+void           e_editor_dom_selection_set_font_color
+                                               (EEditorPage *editor_page,
+                                                const gchar *font_color);
+gchar *                e_editor_dom_selection_get_background_color
+                                               (EEditorPage *editor_page);
+void           e_editor_dom_selection_set_background_color
+                                               (EEditorPage *editor_page,
+                                                const gchar *bg_color);
+EContentEditorBlockFormat
+               e_editor_dom_selection_get_block_format
+                                               (EEditorPage *editor_page);
+void           e_editor_dom_selection_set_block_format
+                                               (EEditorPage *editor_page,
+                                                EContentEditorBlockFormat format);
+EContentEditorAlignment
+               e_editor_dom_selection_get_alignment
+                                               (EEditorPage *editor_page);
+void           e_editor_dom_selection_set_alignment
+                                               (EEditorPage *editor_page,
+                                                EContentEditorAlignment alignment);
+void           e_editor_dom_selection_replace  (EEditorPage *editor_page,
+                                                const gchar *replacement);
+void           e_editor_dom_replace_caret_word (EEditorPage *editor_page,
+                                                const gchar *replacement);
+gchar *                e_editor_dom_get_caret_word     (EEditorPage *editor_page);
+EContentEditorAlignment
+               e_editor_dom_get_list_alignment_from_node
+                                               (WebKitDOMNode *node);
+WebKitDOMElement *
+               e_editor_dom_prepare_paragraph  (EEditorPage *editor_page,
+                                                gboolean with_selection);
+void           e_editor_dom_selection_set_on_point
+                                               (EEditorPage *editor_page,
+                                                guint x,
+                                                guint y);
+void           e_editor_dom_selection_get_coordinates
+                                               (EEditorPage *editor_page,
+                                                guint *start_x,
+                                                guint *start_y,
+                                                guint *end_x,
+                                                guint *end_y);
+gboolean       e_editor_dom_is_selection_position_node
+                                               (WebKitDOMNode *node);
+
+G_END_DECLS
+
+#endif /* E_EDITOR_DOM_FUNCTIONS_H */
diff --git a/modules/webkit-editor/web-extension/e-editor-page.c 
b/modules/webkit-editor/web-extension/e-editor-page.c
new file mode 100644
index 0000000..6d362e5
--- /dev/null
+++ b/modules/webkit-editor/web-extension/e-editor-page.c
@@ -0,0 +1,940 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2016 Red Hat, Inc. (www.redhat.com)
+ *
+ * This library 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 library 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 Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <glib.h>
+#include <webkit2/webkit-web-extension.h>
+
+#include "web-extensions/e-dom-utils.h"
+
+#include "e-editor-dom-functions.h"
+#include "e-editor-web-extension.h"
+#include "e-editor-undo-redo-manager.h"
+
+#include "e-editor-page.h"
+
+struct _EEditorPagePrivate {
+       WebKitWebPage *web_page; /* not referenced */
+       EEditorWebExtension *web_extension; /* not referenced */
+
+       EEditorUndoRedoManager *undo_redo_manager;
+       ESpellChecker *spell_checker;
+
+       guint spell_check_on_scroll_event_source_id;
+
+       EContentEditorAlignment alignment;
+       EContentEditorBlockFormat block_format;
+       guint32 style_flags; /* bit-OR of EContentEditorStyleFlags */
+       gchar *background_color;
+       gchar *font_color;
+       gchar *font_name;
+       gint font_size;
+
+       guint selection_changed_blocked;
+       gboolean selection_changed;
+
+       gboolean force_image_load;
+       gboolean html_mode;
+       gboolean return_key_pressed;
+       gboolean space_key_pressed;
+       gboolean smiley_written;
+       gint word_wrap_length;
+
+       gboolean convert_in_situ;
+       gboolean body_input_event_removed;
+       gboolean dont_save_history_in_body_input;
+       gboolean composition_in_progress;
+       gboolean pasting_content_from_itself;
+       gboolean renew_history_after_coordinates;
+
+       GHashTable *inline_images;
+
+       WebKitDOMNode *node_under_mouse_click;
+
+       GSettings *mail_settings;
+};
+
+G_DEFINE_TYPE (EEditorPage, e_editor_page, G_TYPE_OBJECT)
+
+
+static void
+web_page_document_loaded_cb (WebKitWebPage *web_page,
+                            EEditorPage *editor_page)
+{
+       g_return_if_fail (WEBKIT_IS_WEB_PAGE (web_page));
+       g_return_if_fail (E_IS_EDITOR_PAGE (editor_page));
+
+       editor_page->priv->body_input_event_removed = TRUE;
+
+       e_editor_undo_redo_manager_clean_history (editor_page->priv->undo_redo_manager);
+       e_editor_dom_process_content_after_load (editor_page);
+}
+
+static gboolean
+web_page_context_menu_cb (WebKitWebPage *web_page,
+                         WebKitContextMenu *context_menu,
+                         WebKitWebHitTestResult *hit_test_result,
+                         EEditorPage *editor_page)
+{
+       WebKitDOMNode *node;
+       EContentEditorNodeFlags flags = 0;
+       GVariant *variant;
+
+       g_return_val_if_fail (E_IS_EDITOR_PAGE (editor_page), FALSE);
+
+       node = webkit_web_hit_test_result_get_node (hit_test_result);
+       editor_page->priv->node_under_mouse_click = node;
+
+       if (WEBKIT_DOM_IS_HTML_HR_ELEMENT (node))
+               flags |= E_CONTENT_EDITOR_NODE_IS_H_RULE;
+
+       if (WEBKIT_DOM_IS_HTML_ANCHOR_ELEMENT (node) ||
+           (dom_node_find_parent_element (node, "A") != NULL))
+               flags |= E_CONTENT_EDITOR_NODE_IS_ANCHOR;
+
+       if (WEBKIT_DOM_IS_HTML_IMAGE_ELEMENT (node) ||
+           (dom_node_find_parent_element (node, "IMG") != NULL))
+               flags |= E_CONTENT_EDITOR_NODE_IS_IMAGE;
+
+       if (WEBKIT_DOM_IS_HTML_TABLE_CELL_ELEMENT (node) ||
+           (dom_node_find_parent_element (node, "TD") != NULL) ||
+           (dom_node_find_parent_element (node, "TH") != NULL))
+               flags |= E_CONTENT_EDITOR_NODE_IS_TABLE_CELL;
+
+       if (flags & E_CONTENT_EDITOR_NODE_IS_TABLE_CELL &&
+           (WEBKIT_DOM_IS_HTML_TABLE_ELEMENT (node) ||
+           dom_node_find_parent_element (node, "TABLE") != NULL))
+               flags |= E_CONTENT_EDITOR_NODE_IS_TABLE;
+
+       if (flags == 0)
+               flags |= E_CONTENT_EDITOR_NODE_IS_TEXT;
+
+       variant = g_variant_new_int32 (flags);
+       webkit_context_menu_set_user_data (context_menu, variant);
+
+       return FALSE;
+}
+
+static void
+e_editor_page_setup (EEditorPage *editor_page,
+                    WebKitWebPage *web_page,
+                    struct _EEditorWebExtension *web_extension)
+{
+       WebKitWebEditor *web_editor;
+
+       g_return_if_fail (E_IS_EDITOR_PAGE (editor_page));
+
+       editor_page->priv->web_page = web_page;
+       editor_page->priv->web_extension = web_extension;
+       editor_page->priv->undo_redo_manager = e_editor_undo_redo_manager_new (editor_page);
+
+       g_signal_connect_swapped (
+               editor_page->priv->undo_redo_manager, "notify::can-undo",
+               G_CALLBACK (e_editor_page_emit_undo_redo_state_changed), editor_page);
+
+       g_signal_connect_swapped (
+               editor_page->priv->undo_redo_manager, "notify::can-redo",
+               G_CALLBACK (e_editor_page_emit_undo_redo_state_changed), editor_page);
+
+       web_editor = webkit_web_page_get_editor (web_page);
+
+       g_signal_connect_swapped (
+               web_editor, "selection-changed",
+               G_CALLBACK (e_editor_page_emit_selection_changed), editor_page);
+
+       g_signal_connect (
+               web_page, "document-loaded",
+               G_CALLBACK (web_page_document_loaded_cb), editor_page);
+
+       g_signal_connect (
+               web_page, "context-menu",
+               G_CALLBACK (web_page_context_menu_cb), editor_page);
+}
+
+static void
+e_editor_page_dispose (GObject *object)
+{
+       EEditorPage *editor_page = E_EDITOR_PAGE (object);
+
+       if (editor_page->priv->spell_check_on_scroll_event_source_id > 0) {
+               g_source_remove (editor_page->priv->spell_check_on_scroll_event_source_id);
+               editor_page->priv->spell_check_on_scroll_event_source_id = 0;
+       }
+
+       if (editor_page->priv->background_color != NULL) {
+               g_free (editor_page->priv->background_color);
+               editor_page->priv->background_color = NULL;
+       }
+
+       if (editor_page->priv->font_color != NULL) {
+               g_free (editor_page->priv->font_color);
+               editor_page->priv->font_color = NULL;
+       }
+
+       if (editor_page->priv->font_name != NULL) {
+               g_free (editor_page->priv->font_name);
+               editor_page->priv->font_name = NULL;
+       }
+
+       if (editor_page->priv->mail_settings != NULL) {
+               g_signal_handlers_disconnect_by_data (editor_page->priv->mail_settings, object);
+               g_object_unref (editor_page->priv->mail_settings);
+               editor_page->priv->mail_settings = NULL;
+       }
+
+       g_clear_object (&editor_page->priv->undo_redo_manager);
+       g_clear_object (&editor_page->priv->spell_checker);
+
+       g_hash_table_remove_all (editor_page->priv->inline_images);
+
+       /* Chain up to parent's method. */
+       G_OBJECT_CLASS (e_editor_page_parent_class)->dispose (object);
+}
+
+static void
+e_editor_page_finalize (GObject *object)
+{
+       EEditorPage *editor_page = E_EDITOR_PAGE (object);
+
+       g_hash_table_destroy (editor_page->priv->inline_images);
+
+       /* Chain up to parent's method. */
+       G_OBJECT_CLASS (e_editor_page_parent_class)->finalize (object);
+}
+
+static void
+e_editor_page_class_init (EEditorPageClass *class)
+{
+       GObjectClass *object_class;
+
+       g_type_class_add_private (class, sizeof (EEditorPagePrivate));
+
+       object_class = G_OBJECT_CLASS (class);
+       object_class->dispose = e_editor_page_dispose;
+       object_class->finalize = e_editor_page_finalize;
+}
+
+static void
+e_editor_page_init (EEditorPage *editor_page)
+{
+       editor_page->priv = G_TYPE_INSTANCE_GET_PRIVATE (editor_page, E_TYPE_EDITOR_PAGE, EEditorPagePrivate);
+       editor_page->priv->style_flags = 0;
+       editor_page->priv->selection_changed_blocked = 0;
+       editor_page->priv->background_color = g_strdup ("");
+       editor_page->priv->font_color = g_strdup ("");
+       editor_page->priv->font_name = g_strdup ("");
+       editor_page->priv->font_size = E_CONTENT_EDITOR_FONT_SIZE_NORMAL;
+       editor_page->priv->alignment = E_CONTENT_EDITOR_ALIGNMENT_LEFT;
+       editor_page->priv->block_format = E_CONTENT_EDITOR_BLOCK_FORMAT_PARAGRAPH;
+       editor_page->priv->force_image_load = FALSE;
+       editor_page->priv->html_mode = TRUE;
+       editor_page->priv->return_key_pressed = FALSE;
+       editor_page->priv->space_key_pressed = FALSE;
+       editor_page->priv->smiley_written = FALSE;
+       editor_page->priv->convert_in_situ = FALSE;
+       editor_page->priv->body_input_event_removed = TRUE;
+       editor_page->priv->dont_save_history_in_body_input = FALSE;
+       editor_page->priv->pasting_content_from_itself = FALSE;
+       editor_page->priv->composition_in_progress = FALSE;
+       editor_page->priv->renew_history_after_coordinates = TRUE;
+       editor_page->priv->spell_check_on_scroll_event_source_id = 0;
+       editor_page->priv->mail_settings = e_util_ref_settings ("org.gnome.evolution.mail");
+       editor_page->priv->word_wrap_length = g_settings_get_int (editor_page->priv->mail_settings, 
"composer-word-wrap-length");
+       editor_page->priv->inline_images = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
+       editor_page->priv->spell_checker = e_spell_checker_new ();
+}
+
+EEditorPage *
+e_editor_page_new (WebKitWebPage *web_page,
+                  struct _EEditorWebExtension *web_extension)
+{
+       EEditorPage *editor_page;
+
+       g_return_val_if_fail (WEBKIT_IS_WEB_PAGE (web_page), NULL);
+       g_return_val_if_fail (E_IS_EDITOR_WEB_EXTENSION (web_extension), NULL);
+
+       editor_page = g_object_new (E_TYPE_EDITOR_PAGE, NULL);
+       e_editor_page_setup (editor_page, web_page, web_extension);
+
+       return editor_page;
+}
+
+WebKitWebPage *
+e_editor_page_get_web_page (EEditorPage *editor_page)
+{
+       g_return_val_if_fail (E_IS_EDITOR_PAGE (editor_page), NULL);
+
+       return editor_page->priv->web_page;
+}
+
+struct _EEditorWebExtension *
+e_editor_page_get_web_extension (EEditorPage *editor_page)
+{
+       g_return_val_if_fail (E_IS_EDITOR_PAGE (editor_page), NULL);
+
+       return editor_page->priv->web_extension;
+}
+
+guint64
+e_editor_page_get_page_id (EEditorPage *editor_page)
+{
+       g_return_val_if_fail (E_IS_EDITOR_PAGE (editor_page), 0);
+
+       if (!editor_page->priv->web_page)
+               return 0;
+
+       return webkit_web_page_get_id (editor_page->priv->web_page);
+}
+
+WebKitDOMDocument *
+e_editor_page_get_document (EEditorPage *editor_page)
+{
+       g_return_val_if_fail (E_IS_EDITOR_PAGE (editor_page), NULL);
+
+       if (!editor_page->priv->web_page)
+               return NULL;
+
+       return webkit_web_page_get_dom_document (editor_page->priv->web_page);
+}
+
+struct _EEditorUndoRedoManager *
+e_editor_page_get_undo_redo_manager (EEditorPage *editor_page)
+{
+       g_return_val_if_fail (E_IS_EDITOR_PAGE (editor_page), NULL);
+
+       return editor_page->priv->undo_redo_manager;
+}
+
+void
+e_editor_page_block_selection_changed (EEditorPage *editor_page)
+{
+       g_return_if_fail (E_IS_EDITOR_PAGE (editor_page));
+
+       editor_page->priv->selection_changed_blocked++;
+}
+
+void
+e_editor_page_unblock_selection_changed (EEditorPage *editor_page)
+{
+       g_return_if_fail (E_IS_EDITOR_PAGE (editor_page));
+       g_return_if_fail (editor_page->priv->selection_changed_blocked > 0);
+
+       editor_page->priv->selection_changed_blocked--;
+
+       if (!editor_page->priv->selection_changed_blocked &&
+           editor_page->priv->selection_changed) {
+               editor_page->priv->selection_changed = FALSE;
+               e_editor_page_emit_selection_changed (editor_page);
+       }
+}
+
+gboolean
+e_editor_page_get_html_mode (EEditorPage *editor_page)
+{
+       g_return_val_if_fail (E_IS_EDITOR_PAGE (editor_page), FALSE);
+
+       return editor_page->priv->html_mode;
+}
+
+void
+e_editor_page_set_html_mode (EEditorPage *editor_page,
+                            gboolean value)
+{
+       g_return_if_fail (E_IS_EDITOR_PAGE (editor_page));
+
+       editor_page->priv->html_mode = value;
+}
+
+gboolean
+e_editor_page_get_force_image_load (EEditorPage *editor_page)
+{
+       g_return_val_if_fail (E_IS_EDITOR_PAGE (editor_page), FALSE);
+
+       return editor_page->priv->force_image_load;
+}
+
+void
+e_editor_page_set_force_image_load (EEditorPage *editor_page,
+                                   gboolean value)
+{
+       g_return_if_fail (E_IS_EDITOR_PAGE (editor_page));
+
+       editor_page->priv->force_image_load = value;
+}
+
+gint
+e_editor_page_get_word_wrap_length (EEditorPage *editor_page)
+{
+       g_return_val_if_fail (E_IS_EDITOR_PAGE (editor_page), 0);
+
+       return editor_page->priv->word_wrap_length;
+}
+
+static gboolean
+e_editor_page_check_style_flag (EEditorPage *editor_page,
+                               EContentEditorStyleFlags flag)
+{
+       g_return_val_if_fail (E_IS_EDITOR_PAGE (editor_page), FALSE);
+
+       return (editor_page->priv->style_flags & flag) != 0;
+}
+
+static gboolean
+e_editor_page_set_style_flag (EEditorPage *editor_page,
+                             EContentEditorStyleFlags flag,
+                             gboolean value)
+{
+       g_return_val_if_fail (E_IS_EDITOR_PAGE (editor_page), FALSE);
+
+       if ((((editor_page->priv->style_flags & flag) != 0) ? 1 : 0) == (value ? 1 : 0))
+               return FALSE;
+
+       editor_page->priv->style_flags = (editor_page->priv->style_flags & ~flag) | (value ? flag : 0);
+
+       return TRUE;
+}
+
+gboolean
+e_editor_page_get_bold (EEditorPage *editor_page)
+{
+       g_return_val_if_fail (E_IS_EDITOR_PAGE (editor_page), FALSE);
+
+       return e_editor_page_check_style_flag (editor_page, E_CONTENT_EDITOR_STYLE_IS_BOLD);
+}
+
+void
+e_editor_page_set_bold (EEditorPage *editor_page,
+                       gboolean value)
+{
+       g_return_if_fail (E_IS_EDITOR_PAGE (editor_page));
+
+       if (e_editor_page_set_style_flag (editor_page, E_CONTENT_EDITOR_STYLE_IS_BOLD, value))
+               e_editor_dom_selection_set_bold (editor_page, value);
+}
+
+gboolean
+e_editor_page_get_italic (EEditorPage *editor_page)
+{
+       g_return_val_if_fail (E_IS_EDITOR_PAGE (editor_page), FALSE);
+
+       return e_editor_page_check_style_flag (editor_page, E_CONTENT_EDITOR_STYLE_IS_ITALIC);
+}
+
+void
+e_editor_page_set_italic (EEditorPage *editor_page,
+                         gboolean value)
+{
+       g_return_if_fail (E_IS_EDITOR_PAGE (editor_page));
+
+       if (e_editor_page_set_style_flag (editor_page, E_CONTENT_EDITOR_STYLE_IS_ITALIC, value))
+               e_editor_dom_selection_set_italic (editor_page, value);
+}
+
+gboolean
+e_editor_page_get_underline (EEditorPage *editor_page)
+{
+       g_return_val_if_fail (E_IS_EDITOR_PAGE (editor_page), FALSE);
+
+       return e_editor_page_check_style_flag (editor_page, E_CONTENT_EDITOR_STYLE_IS_UNDERLINE);
+}
+
+void
+e_editor_page_set_underline (EEditorPage *editor_page,
+                            gboolean value)
+{
+       g_return_if_fail (E_IS_EDITOR_PAGE (editor_page));
+
+       if (e_editor_page_set_style_flag (editor_page, E_CONTENT_EDITOR_STYLE_IS_UNDERLINE, value))
+               e_editor_dom_selection_set_underline (editor_page, value);
+}
+
+gboolean
+e_editor_page_get_monospace (EEditorPage *editor_page)
+{
+       g_return_val_if_fail (E_IS_EDITOR_PAGE (editor_page), FALSE);
+
+       return e_editor_page_check_style_flag (editor_page, E_CONTENT_EDITOR_STYLE_IS_MONOSPACE);
+}
+
+void
+e_editor_page_set_monospace (EEditorPage *editor_page,
+                            gboolean value)
+{
+       g_return_if_fail (E_IS_EDITOR_PAGE (editor_page));
+
+       if (e_editor_page_set_style_flag (editor_page, E_CONTENT_EDITOR_STYLE_IS_MONOSPACE, value))
+               e_editor_dom_selection_set_monospace (editor_page, value);
+}
+
+gboolean
+e_editor_page_get_strikethrough (EEditorPage *editor_page)
+{
+       g_return_val_if_fail (E_IS_EDITOR_PAGE (editor_page), FALSE);
+
+       return e_editor_page_check_style_flag (editor_page, E_CONTENT_EDITOR_STYLE_IS_STRIKETHROUGH);
+}
+
+void
+e_editor_page_set_strikethrough (EEditorPage *editor_page,
+                                gboolean value)
+{
+       g_return_if_fail (E_IS_EDITOR_PAGE (editor_page));
+
+       if (e_editor_page_set_style_flag (editor_page, E_CONTENT_EDITOR_STYLE_IS_STRIKETHROUGH, value))
+               e_editor_dom_selection_set_strikethrough (editor_page, value);
+}
+
+guint
+e_editor_page_get_font_size (EEditorPage *editor_page)
+{
+       g_return_val_if_fail (E_IS_EDITOR_PAGE (editor_page), 0);
+
+       return editor_page->priv->font_size;
+}
+
+void
+e_editor_page_set_font_size (EEditorPage *editor_page,
+                            guint value)
+{
+       g_return_if_fail (E_IS_EDITOR_PAGE (editor_page));
+
+       if (editor_page->priv->font_size == value)
+               return;
+
+       editor_page->priv->font_size = value;
+}
+
+const gchar *
+e_editor_page_get_font_color (EEditorPage *editor_page)
+{
+       g_return_val_if_fail (E_IS_EDITOR_PAGE (editor_page), NULL);
+
+       return editor_page->priv->font_color;
+}
+
+EContentEditorAlignment
+e_editor_page_get_alignment (EEditorPage *editor_page)
+{
+       g_return_val_if_fail (E_IS_EDITOR_PAGE (editor_page), E_CONTENT_EDITOR_ALIGNMENT_LEFT);
+
+       return editor_page->priv->alignment;
+}
+
+void
+e_editor_page_set_alignment (EEditorPage *editor_page,
+                            EContentEditorAlignment value)
+{
+       g_return_if_fail (E_IS_EDITOR_PAGE (editor_page));
+
+       editor_page->priv->alignment = value;
+}
+
+gboolean
+e_editor_page_get_return_key_pressed (EEditorPage *editor_page)
+{
+       g_return_val_if_fail (E_IS_EDITOR_PAGE (editor_page), FALSE);
+
+       return editor_page->priv->return_key_pressed;
+}
+
+void
+e_editor_page_set_return_key_pressed (EEditorPage *editor_page,
+                                     gboolean value)
+{
+       g_return_if_fail (E_IS_EDITOR_PAGE (editor_page));
+
+       editor_page->priv->return_key_pressed = value;
+}
+
+gboolean
+e_editor_page_get_space_key_pressed (EEditorPage *editor_page)
+{
+       g_return_val_if_fail (E_IS_EDITOR_PAGE (editor_page), FALSE);
+
+       return editor_page->priv->space_key_pressed;
+}
+
+void
+e_editor_page_set_space_key_pressed (EEditorPage *editor_page,
+                                    gboolean value)
+{
+       g_return_if_fail (E_IS_EDITOR_PAGE (editor_page));
+
+       editor_page->priv->space_key_pressed = value;
+}
+
+gboolean
+e_editor_page_get_magic_links_enabled (EEditorPage *editor_page)
+{
+       g_return_val_if_fail (E_IS_EDITOR_PAGE (editor_page), FALSE);
+
+       return g_settings_get_boolean (editor_page->priv->mail_settings, "composer-magic-links");
+}
+
+gboolean
+e_editor_page_get_magic_smileys_enabled (EEditorPage *editor_page)
+{
+       g_return_val_if_fail (E_IS_EDITOR_PAGE (editor_page), FALSE);
+
+       return g_settings_get_boolean (editor_page->priv->mail_settings, "composer-magic-smileys");
+}
+
+gboolean
+e_editor_page_get_unicode_smileys_enabled (EEditorPage *editor_page)
+{
+       g_return_val_if_fail (E_IS_EDITOR_PAGE (editor_page), FALSE);
+
+       return g_settings_get_boolean (editor_page->priv->mail_settings, "composer-unicode-smileys");
+}
+
+EImageLoadingPolicy
+e_editor_page_get_image_loading_policy (EEditorPage *editor_page)
+{
+       g_return_val_if_fail (E_IS_EDITOR_PAGE (editor_page), E_IMAGE_LOADING_POLICY_NEVER);
+
+       return g_settings_get_enum (editor_page->priv->mail_settings, "image-loading-policy");
+}
+
+gboolean
+e_editor_page_get_inline_spelling_enabled (EEditorPage *editor_page)
+{
+       g_return_val_if_fail (E_IS_EDITOR_PAGE (editor_page), FALSE);
+
+       return g_settings_get_boolean (editor_page->priv->mail_settings, "composer-inline-spelling");
+}
+
+gboolean
+e_editor_page_check_word_spelling (EEditorPage *editor_page,
+                                  const gchar *word,
+                                  const gchar * const *languages)
+{
+       g_return_val_if_fail (E_IS_EDITOR_PAGE (editor_page), TRUE);
+
+       if (!word || !languages || !*languages)
+               return TRUE;
+
+       e_spell_checker_set_active_languages (editor_page->priv->spell_checker, languages);
+
+       return e_spell_checker_check_word (editor_page->priv->spell_checker, word, -1);
+}
+
+gboolean
+e_editor_page_get_body_input_event_removed (EEditorPage *editor_page)
+{
+       g_return_val_if_fail (E_IS_EDITOR_PAGE (editor_page), FALSE);
+
+       return editor_page->priv->body_input_event_removed;
+}
+
+void
+e_editor_page_set_body_input_event_removed (EEditorPage *editor_page,
+                                           gboolean value)
+{
+       g_return_if_fail (E_IS_EDITOR_PAGE (editor_page));
+
+       editor_page->priv->body_input_event_removed = value;
+}
+
+gboolean
+e_editor_page_get_convert_in_situ (EEditorPage *editor_page)
+{
+       g_return_val_if_fail (E_IS_EDITOR_PAGE (editor_page), FALSE);
+
+       return editor_page->priv->convert_in_situ;
+}
+
+void
+e_editor_page_set_convert_in_situ (EEditorPage *editor_page,
+                                  gboolean value)
+{
+       g_return_if_fail (E_IS_EDITOR_PAGE (editor_page));
+
+       editor_page->priv->convert_in_situ = value;
+}
+
+GHashTable *
+e_editor_page_get_inline_images (EEditorPage *editor_page)
+{
+       g_return_val_if_fail (E_IS_EDITOR_PAGE (editor_page), NULL);
+
+       return editor_page->priv->inline_images;
+}
+
+void
+e_editor_page_add_new_inline_image_into_list (EEditorPage *editor_page,
+                                             const gchar *cid_src,
+                                             const gchar *src)
+{
+       g_return_if_fail (E_IS_EDITOR_PAGE (editor_page));
+
+       g_hash_table_insert (editor_page->priv->inline_images, g_strdup (cid_src), g_strdup (src));
+}
+
+gboolean
+e_editor_page_get_is_smiley_written (EEditorPage *editor_page)
+{
+       g_return_val_if_fail (E_IS_EDITOR_PAGE (editor_page), FALSE);
+
+       return editor_page->priv->smiley_written;
+}
+
+void
+e_editor_page_set_is_smiley_written (EEditorPage *editor_page,
+                                    gboolean value)
+{
+       g_return_if_fail (E_IS_EDITOR_PAGE (editor_page));
+
+       editor_page->priv->smiley_written = value;
+}
+
+gboolean
+e_editor_page_get_dont_save_history_in_body_input (EEditorPage *editor_page)
+{
+       g_return_val_if_fail (E_IS_EDITOR_PAGE (editor_page), FALSE);
+
+       return editor_page->priv->dont_save_history_in_body_input;
+}
+
+void
+e_editor_page_set_dont_save_history_in_body_input (EEditorPage *editor_page,
+                                                  gboolean value)
+{
+       g_return_if_fail (E_IS_EDITOR_PAGE (editor_page));
+
+       editor_page->priv->dont_save_history_in_body_input = value;
+}
+
+gboolean
+e_editor_page_is_pasting_content_from_itself (EEditorPage *editor_page)
+{
+       g_return_val_if_fail (E_IS_EDITOR_PAGE (editor_page), FALSE);
+
+       return editor_page->priv->pasting_content_from_itself;
+}
+
+void
+e_editor_page_set_pasting_content_from_itself (EEditorPage *editor_page,
+                                              gboolean value)
+{
+       g_return_if_fail (E_IS_EDITOR_PAGE (editor_page));
+
+       editor_page->priv->pasting_content_from_itself = value;
+}
+
+gboolean
+e_editor_page_get_renew_history_after_coordinates (EEditorPage *editor_page)
+{
+       g_return_val_if_fail (E_IS_EDITOR_PAGE (editor_page), FALSE);
+
+       return editor_page->priv->renew_history_after_coordinates;
+}
+
+void
+e_editor_page_set_renew_history_after_coordinates (EEditorPage *editor_page,
+                                                  gboolean renew_history_after_coordinates)
+{
+       g_return_if_fail (E_IS_EDITOR_PAGE (editor_page));
+
+       editor_page->priv->renew_history_after_coordinates = renew_history_after_coordinates;
+}
+
+gboolean
+e_editor_page_is_composition_in_progress (EEditorPage *editor_page)
+{
+       g_return_val_if_fail (E_IS_EDITOR_PAGE (editor_page), FALSE);
+
+       return editor_page->priv->composition_in_progress;
+}
+
+void
+e_editor_page_set_composition_in_progress (EEditorPage *editor_page,
+                                          gboolean value)
+{
+       g_return_if_fail (E_IS_EDITOR_PAGE (editor_page));
+
+       editor_page->priv->composition_in_progress = value;
+}
+
+guint
+e_editor_page_get_spell_check_on_scroll_event_source_id (EEditorPage *editor_page)
+{
+       g_return_val_if_fail (E_IS_EDITOR_PAGE (editor_page), 0);
+
+       return editor_page->priv->spell_check_on_scroll_event_source_id;
+}
+
+void
+e_editor_page_set_spell_check_on_scroll_event_source_id (EEditorPage *editor_page,
+                                                        guint value)
+{
+       g_return_if_fail (E_IS_EDITOR_PAGE (editor_page));
+
+       editor_page->priv->spell_check_on_scroll_event_source_id = value;
+}
+
+WebKitDOMNode *
+e_editor_page_get_node_under_mouse_click (EEditorPage *editor_page)
+{
+       g_return_val_if_fail (E_IS_EDITOR_PAGE (editor_page), NULL);
+
+       return editor_page->priv->node_under_mouse_click;
+}
+
+void
+e_editor_page_emit_selection_changed (EEditorPage *editor_page)
+{
+       WebKitDOMDocument *document;
+       WebKitDOMRange *range = NULL;
+       GDBusConnection *connection;
+       GError *error = NULL;
+
+       g_return_if_fail (E_IS_EDITOR_PAGE (editor_page));
+
+       if (!editor_page->priv->web_extension ||
+           editor_page->priv->selection_changed_blocked) {
+               editor_page->priv->selection_changed = TRUE;
+               return;
+       }
+
+       document = e_editor_page_get_document (editor_page);
+       if (!document)
+               return;
+
+       connection = e_editor_web_extension_get_connection (editor_page->priv->web_extension);
+       if (!connection)
+               return;
+
+       range = e_editor_dom_get_current_range (editor_page);
+       if (!range)
+               return;
+
+       g_clear_object (&range);
+
+       editor_page->priv->alignment = e_editor_dom_selection_get_alignment (editor_page);
+       editor_page->priv->block_format = e_editor_dom_selection_get_block_format (editor_page);
+
+       if (editor_page->priv->html_mode) {
+               guint32 style_flags = E_CONTENT_EDITOR_STYLE_NONE;
+
+               #define set_flag_if(tst, flg) G_STMT_START { \
+                       if (tst (editor_page)) \
+                               style_flags |= flg; \
+                       } G_STMT_END
+
+               set_flag_if (e_editor_dom_selection_is_bold, E_CONTENT_EDITOR_STYLE_IS_BOLD);
+               set_flag_if (e_editor_dom_selection_is_italic, E_CONTENT_EDITOR_STYLE_IS_ITALIC);
+               set_flag_if (e_editor_dom_selection_is_underline, E_CONTENT_EDITOR_STYLE_IS_UNDERLINE);
+               set_flag_if (e_editor_dom_selection_is_strikethrough, 
E_CONTENT_EDITOR_STYLE_IS_STRIKETHROUGH);
+               set_flag_if (e_editor_dom_selection_is_monospace, E_CONTENT_EDITOR_STYLE_IS_MONOSPACE);
+               set_flag_if (e_editor_dom_selection_is_subscript, E_CONTENT_EDITOR_STYLE_IS_SUBSCRIPT);
+               set_flag_if (e_editor_dom_selection_is_superscript, E_CONTENT_EDITOR_STYLE_IS_SUPERSCRIPT);
+
+               #undef set_flag_if
+
+               editor_page->priv->style_flags = style_flags;
+               editor_page->priv->font_size = e_editor_dom_selection_get_font_size (editor_page);
+               g_free (editor_page->priv->font_color);
+               editor_page->priv->font_color = e_editor_dom_selection_get_font_color (editor_page);
+       }
+
+       g_dbus_connection_emit_signal (
+               connection,
+               NULL,
+               E_WEBKIT_EDITOR_WEB_EXTENSION_OBJECT_PATH,
+               E_WEBKIT_EDITOR_WEB_EXTENSION_INTERFACE,
+               "SelectionChanged",
+               g_variant_new ("(tiibiis)",
+                       e_editor_page_get_page_id (editor_page),
+                       (gint32) editor_page->priv->alignment,
+                       (gint32) editor_page->priv->block_format,
+                       e_editor_dom_selection_is_indented (editor_page),
+                       editor_page->priv->style_flags,
+                       (gint32) editor_page->priv->font_size,
+                       editor_page->priv->font_color ? editor_page->priv->font_color : ""),
+               &error);
+
+       if (error) {
+               g_warning ("%s: Failed to emit signal: %s", G_STRFUNC, error->message);
+               g_error_free (error);
+       }
+}
+
+void
+e_editor_page_emit_content_changed (EEditorPage *editor_page)
+{
+       GDBusConnection *connection;
+       GError *error = NULL;
+
+       g_return_if_fail (E_IS_EDITOR_PAGE (editor_page));
+
+       if (!editor_page->priv->web_extension)
+               return;
+
+       connection = e_editor_web_extension_get_connection (editor_page->priv->web_extension);
+       if (!connection)
+               return;
+
+       g_dbus_connection_emit_signal (
+               connection,
+               NULL,
+               E_WEBKIT_EDITOR_WEB_EXTENSION_OBJECT_PATH,
+               E_WEBKIT_EDITOR_WEB_EXTENSION_INTERFACE,
+               "ContentChanged",
+               g_variant_new ("(t)", e_editor_page_get_page_id (editor_page)),
+               &error);
+
+       if (error) {
+               g_warning ("%s: Failed to emit signal: %s", G_STRFUNC, error->message);
+               g_error_free (error);
+       }
+}
+
+void
+e_editor_page_emit_undo_redo_state_changed (EEditorPage *editor_page)
+{
+       GDBusConnection *connection;
+       GError *error = NULL;
+
+       g_return_if_fail (E_IS_EDITOR_PAGE (editor_page));
+
+       if (!editor_page->priv->web_extension)
+               return;
+
+       connection = e_editor_web_extension_get_connection (editor_page->priv->web_extension);
+       if (!connection)
+               return;
+
+       g_dbus_connection_emit_signal (
+               connection,
+               NULL,
+               E_WEBKIT_EDITOR_WEB_EXTENSION_OBJECT_PATH,
+               E_WEBKIT_EDITOR_WEB_EXTENSION_INTERFACE,
+               "UndoRedoStateChanged",
+               g_variant_new ("(tbb)",
+                       e_editor_page_get_page_id (editor_page),
+                       e_editor_undo_redo_manager_can_undo (editor_page->priv->undo_redo_manager),
+                       e_editor_undo_redo_manager_can_redo (editor_page->priv->undo_redo_manager)),
+               &error);
+
+       if (error) {
+               g_warning ("%s: Failed to emit signal: %s", G_STRFUNC, error->message);
+               g_error_free (error);
+       }
+}
diff --git a/modules/webkit-editor/web-extension/e-editor-page.h 
b/modules/webkit-editor/web-extension/e-editor-page.h
new file mode 100644
index 0000000..160b9ae
--- /dev/null
+++ b/modules/webkit-editor/web-extension/e-editor-page.h
@@ -0,0 +1,197 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2016 Red Hat, Inc. (www.redhat.com)
+ *
+ * This library 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 library 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 Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef E_EDITOR_PAGE_H
+#define E_EDITOR_PAGE_H
+
+#include <glib-object.h>
+#include <webkit2/webkit-web-extension.h>
+
+#define E_UTIL_INCLUDE_WITHOUT_WEBKIT
+#include <e-util/e-util.h>
+#undef E_UTIL_INCLUDE_WITHOUT_WEBKIT
+
+/* Standard GObject macros */
+#define E_TYPE_EDITOR_PAGE \
+       (e_editor_page_get_type ())
+#define E_EDITOR_PAGE(obj) \
+       (G_TYPE_CHECK_INSTANCE_CAST \
+       ((obj), E_TYPE_EDITOR_PAGE, EEditorPage))
+#define E_EDITOR_PAGE_CLASS(cls) \
+       (G_TYPE_CHECK_CLASS_CAST \
+       ((cls), E_TYPE_EDITOR_PAGE, EEditorPageClass))
+#define E_IS_EDITOR_PAGE(obj) \
+       (G_TYPE_CHECK_INSTANCE_TYPE \
+       ((obj), E_TYPE_EDITOR_PAGE))
+#define E_IS_EDITOR_PAGE_CLASS(cls) \
+       (G_TYPE_CHECK_CLASS_TYPE \
+       ((cls), E_TYPE_EDITOR_PAGE))
+#define E_EDITOR_PAGE_GET_CLASS(obj) \
+       (G_TYPE_INSTANCE_GET_CLASS \
+       ((obj), E_TYPE_EDITOR_PAGE, EEditorPageClass))
+
+G_BEGIN_DECLS
+
+struct _EEditorWebExtension;
+struct _EEditorUndoRedoManager;
+
+typedef struct _EEditorPage EEditorPage;
+typedef struct _EEditorPageClass EEditorPageClass;
+typedef struct _EEditorPagePrivate EEditorPagePrivate;
+
+struct _EEditorPage {
+       GObject parent;
+       EEditorPagePrivate *priv;
+};
+
+struct _EEditorPageClass {
+       GObjectClass parent_class;
+};
+
+GType          e_editor_page_get_type          (void) G_GNUC_CONST;
+EEditorPage *  e_editor_page_new               (WebKitWebPage *web_page,
+                                                struct _EEditorWebExtension *web_extension);
+WebKitWebPage *        e_editor_page_get_web_page      (EEditorPage *editor_page);
+struct _EEditorWebExtension *
+               e_editor_page_get_web_extension (EEditorPage *editor_page);
+guint64                e_editor_page_get_page_id       (EEditorPage *editor_page);
+WebKitDOMDocument *
+               e_editor_page_get_document      (EEditorPage *editor_page);
+struct _EEditorUndoRedoManager *
+               e_editor_page_get_undo_redo_manager
+                                               (EEditorPage *editor_page);
+
+void           e_editor_page_block_selection_changed
+                                               (EEditorPage *editor_page);
+void           e_editor_page_unblock_selection_changed
+                                               (EEditorPage *editor_page);
+gboolean       e_editor_page_get_html_mode     (EEditorPage *editor_page);
+void           e_editor_page_set_html_mode     (EEditorPage *editor_page,
+                                                gboolean value);
+gboolean       e_editor_page_get_force_image_load
+                                               (EEditorPage *editor_page);
+void           e_editor_page_set_force_image_load
+                                               (EEditorPage *editor_page,
+                                                gboolean value);
+gboolean       e_editor_page_get_bold          (EEditorPage *editor_page);
+void           e_editor_page_set_bold          (EEditorPage *editor_page,
+                                                gboolean value);
+gboolean       e_editor_page_get_italic        (EEditorPage *editor_page);
+void           e_editor_page_set_italic        (EEditorPage *editor_page,
+                                                gboolean value);
+gboolean       e_editor_page_get_underline     (EEditorPage *editor_page);
+void           e_editor_page_set_underline     (EEditorPage *editor_page,
+                                                gboolean value);
+gboolean       e_editor_page_get_monospace     (EEditorPage *editor_page);
+void           e_editor_page_set_monospace     (EEditorPage *editor_page,
+                                                gboolean value);
+gboolean       e_editor_page_get_strikethrough (EEditorPage *editor_page);
+void           e_editor_page_set_strikethrough (EEditorPage *editor_page,
+                                                gboolean value);
+guint          e_editor_page_get_font_size     (EEditorPage *editor_page);
+void           e_editor_page_set_font_size     (EEditorPage *editor_page,
+                                                guint value);
+const gchar *  e_editor_page_get_font_color    (EEditorPage *editor_page);
+EContentEditorAlignment
+               e_editor_page_get_alignment     (EEditorPage *editor_page);
+void           e_editor_page_set_alignment     (EEditorPage *editor_page,
+                                                EContentEditorAlignment value);
+gint           e_editor_page_get_word_wrap_length
+                                               (EEditorPage *editor_page);
+gboolean       e_editor_page_get_return_key_pressed
+                                               (EEditorPage *editor_page);
+void           e_editor_page_set_return_key_pressed
+                                               (EEditorPage *editor_page,
+                                                gboolean value);
+gboolean       e_editor_page_get_space_key_pressed
+                                               (EEditorPage *editor_page);
+void           e_editor_page_set_space_key_pressed
+                                               (EEditorPage *editor_page,
+                                                gboolean value);
+gboolean       e_editor_page_get_magic_links_enabled
+                                               (EEditorPage *editor_page);
+gboolean       e_editor_page_get_magic_smileys_enabled
+                                               (EEditorPage *editor_page);
+gboolean       e_editor_page_get_unicode_smileys_enabled
+                                               (EEditorPage *editor_page);
+EImageLoadingPolicy
+               e_editor_page_get_image_loading_policy
+                                               (EEditorPage *editor_page);
+gboolean       e_editor_page_get_inline_spelling_enabled
+                                               (EEditorPage *editor_page);
+gboolean       e_editor_page_check_word_spelling
+                                               (EEditorPage *editor_page,
+                                                const gchar *word,
+                                                const gchar * const *languages);
+gboolean       e_editor_page_get_body_input_event_removed
+                                               (EEditorPage *editor_page);
+void           e_editor_page_set_body_input_event_removed
+                                               (EEditorPage *editor_page,
+                                                gboolean value);
+gboolean       e_editor_page_get_convert_in_situ
+                                               (EEditorPage *editor_page);
+void           e_editor_page_set_convert_in_situ
+                                               (EEditorPage *editor_page,
+                                                gboolean value);
+GHashTable *   e_editor_page_get_inline_images
+                                               (EEditorPage *editor_page);
+void           e_editor_page_add_new_inline_image_into_list
+                                               (EEditorPage *editor_page,
+                                                const gchar *cid_src,
+                                                const gchar *src);
+gboolean       e_editor_page_get_is_smiley_written
+                                               (EEditorPage *editor_page);
+void           e_editor_page_set_is_smiley_written
+                                               (EEditorPage *editor_page,
+                                                gboolean value);
+gboolean       e_editor_page_get_dont_save_history_in_body_input
+                                               (EEditorPage *editor_page);
+void           e_editor_page_set_dont_save_history_in_body_input
+                                               (EEditorPage *editor_page,
+                                                gboolean value);
+gboolean       e_editor_page_is_pasting_content_from_itself
+                                               (EEditorPage *editor_page);
+void           e_editor_page_set_pasting_content_from_itself
+                                               (EEditorPage *editor_page,
+                                                gboolean value);
+gboolean       e_editor_page_get_renew_history_after_coordinates
+                                               (EEditorPage *editor_page);
+void           e_editor_page_set_renew_history_after_coordinates
+                                               (EEditorPage *editor_page,
+                                                gboolean renew_history_after_coordinates);
+gboolean       e_editor_page_is_composition_in_progress
+                                               (EEditorPage *editor_page);
+void           e_editor_page_set_composition_in_progress
+                                               (EEditorPage *editor_page,
+                                                gboolean value);
+guint          e_editor_page_get_spell_check_on_scroll_event_source_id
+                                               (EEditorPage *editor_page);
+void           e_editor_page_set_spell_check_on_scroll_event_source_id
+                                               (EEditorPage *editor_page,
+                                                guint value);
+WebKitDOMNode *        e_editor_page_get_node_under_mouse_click
+                                               (EEditorPage *editor_page);
+
+void           e_editor_page_emit_selection_changed
+                                               (EEditorPage *editor_page);
+void           e_editor_page_emit_content_changed
+                                               (EEditorPage *editor_page);
+void           e_editor_page_emit_undo_redo_state_changed
+                                               (EEditorPage *editor_page);
+G_END_DECLS
+
+#endif /* E_EDITOR_PAGE_H */
diff --git a/modules/webkit-editor/web-extension/e-editor-undo-redo-manager.c 
b/modules/webkit-editor/web-extension/e-editor-undo-redo-manager.c
new file mode 100644
index 0000000..2f36941
--- /dev/null
+++ b/modules/webkit-editor/web-extension/e-editor-undo-redo-manager.c
@@ -0,0 +1,2831 @@
+/*
+ * e-editor-undo-redo-manager.c
+ *
+ * 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; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#define WEBKIT_DOM_USE_UNSTABLE_API
+#include <webkitdom/WebKitDOMDocumentFragmentUnstable.h>
+#include <webkitdom/WebKitDOMRangeUnstable.h>
+#include <webkitdom/WebKitDOMDOMSelection.h>
+#include <webkitdom/WebKitDOMDOMWindowUnstable.h>
+#include <webkitdom/WebKitDOMHTMLElementUnstable.h>
+#include <webkitdom/WebKitDOMDocumentUnstable.h>
+#undef WEBKIT_DOM_USE_UNSTABLE_API
+
+#include "web-extensions/e-dom-utils.h"
+
+#include "e-editor-page.h"
+#include "e-editor-dom-functions.h"
+#include "e-editor-undo-redo-manager.h"
+
+#define E_EDITOR_UNDO_REDO_MANAGER_GET_PRIVATE(obj) \
+       (G_TYPE_INSTANCE_GET_PRIVATE \
+       ((obj), E_TYPE_EDITOR_UNDO_REDO_MANAGER, EEditorUndoRedoManagerPrivate))
+
+struct _EEditorUndoRedoManagerPrivate {
+       GWeakRef editor_page;
+
+       gboolean operation_in_progress;
+
+       GList *history;
+       guint history_size;
+};
+
+enum {
+       PROP_0,
+       PROP_CAN_REDO,
+       PROP_CAN_UNDO,
+       PROP_EDITOR_PAGE
+};
+
+#define HISTORY_SIZE_LIMIT 30
+
+#define d(x)
+
+G_DEFINE_TYPE (EEditorUndoRedoManager, e_editor_undo_redo_manager, G_TYPE_OBJECT)
+
+EEditorUndoRedoManager *
+e_editor_undo_redo_manager_new (EEditorPage *editor_page)
+{
+       g_return_val_if_fail (E_IS_EDITOR_PAGE (editor_page), NULL);
+
+       return g_object_new (E_TYPE_EDITOR_UNDO_REDO_MANAGER,
+               "editor-page", editor_page,
+               NULL);
+}
+
+static EEditorPage *
+editor_undo_redo_manager_ref_editor_page (EEditorUndoRedoManager *manager)
+{
+       g_return_val_if_fail (E_IS_EDITOR_UNDO_REDO_MANAGER (manager), NULL);
+
+       return g_weak_ref_get (&manager->priv->editor_page);
+}
+
+static WebKitDOMRange *
+get_range_for_point (WebKitDOMDocument *document,
+                     EEditorSelectionPoint point)
+{
+       glong scroll_left, scroll_top;
+       WebKitDOMHTMLElement *body;
+       WebKitDOMRange *range = NULL;
+
+       body = webkit_dom_document_get_body (document);
+       scroll_left = webkit_dom_element_get_scroll_left (WEBKIT_DOM_ELEMENT (body));
+       scroll_top = webkit_dom_element_get_scroll_top (WEBKIT_DOM_ELEMENT (body));
+
+       range = webkit_dom_document_caret_range_from_point (
+               document, point.x - scroll_left, point.y - scroll_top);
+
+       /* The point is outside the viewport, scroll to it. */
+       if (!range) {
+               WebKitDOMDOMWindow *dom_window = NULL;
+
+               dom_window = webkit_dom_document_get_default_view (document);
+               webkit_dom_dom_window_scroll_to (dom_window, point.x, point.y);
+
+               scroll_left = webkit_dom_element_get_scroll_left (WEBKIT_DOM_ELEMENT (body));
+               scroll_top = webkit_dom_element_get_scroll_top (WEBKIT_DOM_ELEMENT (body));
+               range = webkit_dom_document_caret_range_from_point (
+                       document, point.x - scroll_left, point.y - scroll_top);
+               g_clear_object (&dom_window);
+       }
+
+       return range;
+}
+
+static void
+restore_selection_to_history_event_state (EEditorPage *editor_page,
+                                          EEditorSelection selection_state)
+{
+       WebKitDOMDocument *document;
+       WebKitDOMDOMWindow *dom_window = NULL;
+       WebKitDOMDOMSelection *dom_selection = NULL;
+       WebKitDOMElement *element, *tmp;
+       WebKitDOMRange *range = NULL;
+       gboolean was_collapsed = FALSE;
+
+       g_return_if_fail (E_IS_EDITOR_PAGE (editor_page));
+
+       document = e_editor_page_get_document (editor_page);
+       dom_window = webkit_dom_document_get_default_view (document);
+       dom_selection = webkit_dom_dom_window_get_selection (dom_window);
+       g_clear_object (&dom_window);
+
+       /* Restore the selection how it was before the event occured. */
+       range = get_range_for_point (document, selection_state.start);
+       webkit_dom_dom_selection_remove_all_ranges (dom_selection);
+       webkit_dom_dom_selection_add_range (dom_selection, range);
+       g_clear_object (&range);
+
+       was_collapsed = selection_state.start.x == selection_state.end.x;
+       was_collapsed = was_collapsed && selection_state.start.y == selection_state.end.y;
+       if (was_collapsed) {
+               g_clear_object (&dom_selection);
+               return;
+       }
+
+       e_editor_dom_selection_save (editor_page);
+
+       element = webkit_dom_document_get_element_by_id (
+               document, "-x-evo-selection-end-marker");
+
+       remove_node (WEBKIT_DOM_NODE (element));
+
+       element = webkit_dom_document_get_element_by_id (
+               document, "-x-evo-selection-start-marker");
+
+       webkit_dom_element_remove_attribute (element, "id");
+
+       range = get_range_for_point (document, selection_state.end);
+       webkit_dom_dom_selection_remove_all_ranges (dom_selection);
+       webkit_dom_dom_selection_add_range (dom_selection, range);
+       g_clear_object (&range);
+
+       e_editor_dom_selection_save (editor_page);
+
+       tmp = webkit_dom_document_get_element_by_id (
+               document, "-x-evo-selection-start-marker");
+
+       remove_node (WEBKIT_DOM_NODE (tmp));
+
+       webkit_dom_element_set_id (
+               element, "-x-evo-selection-start-marker");
+
+       e_editor_dom_selection_restore (editor_page);
+
+       g_clear_object (&dom_selection);
+}
+
+#if d(1)+0
+static void
+print_node_inner_html (WebKitDOMNode *node)
+{
+       gchar *inner_html;
+
+       if (!node) {
+               printf ("    none\n");
+               return;
+       }
+
+       inner_html = dom_get_node_inner_html (node);
+
+       printf ("    '%s'\n", inner_html);
+
+       g_free (inner_html);
+}
+
+static void
+print_history_event (EEditorHistoryEvent *event)
+{
+       if (event->type != HISTORY_START && event->type != HISTORY_AND) {
+               printf ("  HISTORY EVENT: %d ; \n", event->type);
+               printf ("    before: start_x: %u ; start_y: %u ; end_x: %u ; end_y: %u ;\n",
+                       event->before.start.x, event->before.start.y, event->before.end.x, 
event->before.end.y);
+               printf ("    after:  start_x: %u ; start_y: %u ; end_x: %u ; end_y: %u ;\n",
+                       event->after.start.x, event->after.start.y, event->after.end.x, event->after.end.y);
+       }
+       switch (event->type) {
+               case HISTORY_DELETE:
+               case HISTORY_INPUT:
+               case HISTORY_REMOVE_LINK:
+               case HISTORY_SMILEY:
+               case HISTORY_IMAGE:
+               case HISTORY_CITATION_SPLIT:
+                       print_node_inner_html (WEBKIT_DOM_NODE (event->data.fragment));
+                       break;
+               case HISTORY_ALIGNMENT:
+               case HISTORY_BLOCK_FORMAT:
+               case HISTORY_BOLD:
+               case HISTORY_FONT_SIZE:
+               case HISTORY_INDENT:
+               case HISTORY_ITALIC:
+               case HISTORY_MONOSPACE:
+               case HISTORY_UNDERLINE:
+               case HISTORY_STRIKETHROUGH:
+               case HISTORY_WRAP:
+                       printf ("    from %d to %d ;\n", event->data.style.from, event->data.style.to);
+                       break;
+               case HISTORY_PASTE:
+               case HISTORY_PASTE_AS_TEXT:
+               case HISTORY_PASTE_QUOTED:
+               case HISTORY_INSERT_HTML:
+                       printf ("    pasting: '%s' ; \n", event->data.string.to);
+                       break;
+               case HISTORY_HRULE_DIALOG:
+               case HISTORY_IMAGE_DIALOG:
+               case HISTORY_LINK_DIALOG:
+               case HISTORY_CELL_DIALOG:
+               case HISTORY_TABLE_DIALOG:
+               case HISTORY_PAGE_DIALOG:
+               case HISTORY_UNQUOTE:
+                       print_node_inner_html (event->data.dom.from);
+                       print_node_inner_html (event->data.dom.to);
+                       break;
+               case HISTORY_FONT_COLOR:
+               case HISTORY_REPLACE:
+               case HISTORY_REPLACE_ALL:
+                       printf ("    from '%s' to '%s';\n", event->data.string.from, event->data.string.to);
+                       break;
+               case HISTORY_START:
+                       printf ("  HISTORY START\n");
+                       break;
+               case HISTORY_AND:
+                       printf ("  HISTORY AND\n");
+                       break;
+               default:
+                       printf ("  Unknown history type\n");
+       }
+}
+
+static void
+print_history (EEditorUndoRedoManager *manager)
+{
+       printf ("-------------------\nWHOLE HISTORY STACK\n");
+       if (manager->priv->history) {
+               g_list_foreach (
+                       manager->priv->history, (GFunc) print_history_event, NULL);
+       }
+       printf ("-------------------\n");
+}
+
+static void
+print_undo_events (EEditorUndoRedoManager *manager)
+{
+       GList *item = manager->priv->history;
+
+       printf ("------------------\nUNDO HISTORY STACK\n");
+       if (!item || !item->next) {
+               printf ("------------------\n");
+               return;
+       }
+
+       print_history_event (item->data);
+       item = item->next;
+       while (item) {
+               print_history_event (item->data);
+               item = item->next;
+       }
+
+       printf ("------------------\n");
+}
+
+static void
+print_redo_events (EEditorUndoRedoManager *manager)
+{
+       GList *item = manager->priv->history;
+
+       printf ("------------------\nREDO HISTORY STACK\n");
+       if (!item || !item->prev) {
+               printf ("------------------\n");
+               return;
+       }
+
+       item = item->prev;
+       while (item) {
+               print_history_event (item->data);
+               item = item->prev;
+       }
+
+       printf ("------------------\n");
+}
+#endif
+
+static gboolean
+event_selection_was_collapsed (EEditorHistoryEvent *ev)
+{
+       return (ev->before.start.x == ev->before.end.x) && (ev->before.start.y == ev->before.end.y);
+}
+
+static WebKitDOMNode *
+split_node_into_two (WebKitDOMNode *item,
+                     gint level)
+{
+       gint current_level = 1;
+       WebKitDOMDocument *document;
+       WebKitDOMDocumentFragment *fragment;
+       WebKitDOMNode *parent, *prev_parent = NULL, *tmp = NULL;
+
+       document = webkit_dom_node_get_owner_document (item);
+       fragment = webkit_dom_document_create_document_fragment (document);
+
+       tmp = item;
+       parent = webkit_dom_node_get_parent_node (item);
+       while (!WEBKIT_DOM_IS_HTML_BODY_ELEMENT (parent)) {
+               WebKitDOMNode *clone, *first_child, *insert_before = NULL, *sibling;
+
+               first_child = webkit_dom_node_get_first_child (WEBKIT_DOM_NODE (fragment));
+               clone = webkit_dom_node_clone_node_with_error (parent, FALSE, NULL);
+               webkit_dom_node_insert_before (
+                       WEBKIT_DOM_NODE (fragment), clone, first_child, NULL);
+
+               if (first_child)
+                       insert_before = webkit_dom_node_get_first_child (first_child);
+
+               while (first_child && (sibling = webkit_dom_node_get_next_sibling (first_child)))
+                       webkit_dom_node_insert_before (first_child, sibling, insert_before, NULL);
+
+               while ((sibling = webkit_dom_node_get_next_sibling (tmp)))
+                       webkit_dom_node_append_child (clone, sibling, NULL);
+
+               webkit_dom_node_insert_before (
+                       clone, tmp, webkit_dom_node_get_first_child (clone), NULL);
+
+               prev_parent = parent;
+               tmp = webkit_dom_node_get_next_sibling (parent);
+               parent = webkit_dom_node_get_parent_node (parent);
+               if (WEBKIT_DOM_IS_HTML_BODY_ELEMENT (parent)) {
+                       first_child = webkit_dom_node_get_first_child (WEBKIT_DOM_NODE (fragment));
+                       insert_before = webkit_dom_node_get_first_child (first_child);
+                       while (first_child && (sibling = webkit_dom_node_get_next_sibling (first_child))) {
+                               webkit_dom_node_insert_before (
+                                       first_child, sibling, insert_before, NULL);
+                       }
+               }
+
+               if (current_level >= level && level >= 0)
+                       break;
+
+               current_level++;
+       }
+
+       if (prev_parent) {
+               tmp = webkit_dom_node_insert_before (
+                       parent,
+                       webkit_dom_node_get_first_child (WEBKIT_DOM_NODE (fragment)),
+                       webkit_dom_node_get_next_sibling (prev_parent),
+                       NULL);
+               remove_node_if_empty (prev_parent);
+       }
+
+       return tmp;
+}
+
+static void
+undo_delete (EEditorPage *editor_page,
+             EEditorHistoryEvent *event)
+{
+       gboolean empty, single_block;
+       gchar *content;
+       WebKitDOMDocument *document;
+       WebKitDOMDOMWindow *dom_window = NULL;
+       WebKitDOMDOMSelection *dom_selection = NULL;
+       WebKitDOMRange *range = NULL;
+       WebKitDOMElement *element;
+       WebKitDOMNode *first_child, *fragment;
+
+       document = e_editor_page_get_document (editor_page);
+       dom_window = webkit_dom_document_get_default_view (document);
+       dom_selection = webkit_dom_dom_window_get_selection (dom_window);
+       g_clear_object (&dom_window);
+
+       fragment = webkit_dom_node_clone_node_with_error (WEBKIT_DOM_NODE (event->data.fragment), TRUE, NULL);
+       first_child = webkit_dom_node_get_first_child (fragment);
+
+       content = webkit_dom_node_get_text_content (fragment);
+       empty = content && !*content;
+       g_free (content);
+
+       /* Tabulator */
+       single_block = event->type == HISTORY_INPUT;
+       single_block = single_block && event->before.start.x != 0 && event->before.end.y != 0;
+
+       if (!single_block) {
+               /* One block delete */
+               if ((single_block = WEBKIT_DOM_IS_ELEMENT (first_child)))
+                       single_block = element_has_id (WEBKIT_DOM_ELEMENT (first_child), 
"-x-evo-selection-start-marker");
+               else
+                       single_block = WEBKIT_DOM_IS_TEXT (first_child);
+       }
+
+       /* Delete or BackSpace pressed in the beginning of a block or on its end. */
+       if (event->type == HISTORY_DELETE && !single_block &&
+            g_object_get_data (G_OBJECT (event->data.fragment), "history-concatenating-blocks")) {
+               WebKitDOMNode *node, *block;
+
+               range = get_range_for_point (document, event->after.start);
+               webkit_dom_dom_selection_remove_all_ranges (dom_selection);
+               webkit_dom_dom_selection_add_range (dom_selection, range);
+
+               node = webkit_dom_range_get_end_container (range, NULL);
+               block = e_editor_dom_get_parent_block_node_from_child (node);
+
+               if (webkit_dom_document_fragment_query_selector (event->data.fragment, ".-x-evo-quoted", 
NULL)) {
+                       while ((node = webkit_dom_node_get_first_child (fragment))) {
+                               if (WEBKIT_DOM_IS_ELEMENT (node) &&
+                                   webkit_dom_element_query_selector (WEBKIT_DOM_ELEMENT (node), 
".-x-evo-quoted", NULL))
+
+                                       if (e_editor_dom_get_citation_level (block, FALSE) > 0) {
+                                               webkit_dom_node_insert_before (
+                                                       webkit_dom_node_get_parent_node (block),
+                                                       node,
+                                                       block,
+                                                       NULL);
+                                       } else {
+                                               WebKitDOMNode *next_block;
+
+                                               next_block = webkit_dom_node_get_next_sibling (block);
+                                               while (next_block && e_editor_dom_node_is_citation_node 
(next_block))
+                                                       next_block = webkit_dom_node_get_first_child 
(next_block);
+
+                                               webkit_dom_node_insert_before (
+                                                       webkit_dom_node_get_parent_node (next_block),
+                                                       node,
+                                                       next_block,
+                                                       NULL);
+                                       }
+                               else {
+                                       if (e_editor_dom_get_citation_level (block, FALSE) > 0) {
+                                               WebKitDOMNode *next_node;
+
+                                               if ((next_node = split_node_into_two (block, -1)))
+                                                       webkit_dom_node_insert_before (
+                                                               webkit_dom_node_get_parent_node (next_node),
+                                                               node,
+                                                               next_node,
+                                                               NULL);
+                                               else
+                                                       webkit_dom_node_insert_before (
+                                                               webkit_dom_node_get_parent_node (block),
+                                                               node,
+                                                               block,
+                                                               NULL);
+                                       } else
+                                               webkit_dom_node_insert_before (
+                                                       webkit_dom_node_get_parent_node (block),
+                                                       node,
+                                                       block,
+                                                       NULL);
+                               }
+                       }
+               } else {
+                       while ((node = webkit_dom_node_get_first_child (fragment))) {
+                               webkit_dom_node_insert_before (
+                                       webkit_dom_node_get_parent_node (block),
+                                       node,
+                                       block,
+                                       NULL);
+                       }
+               }
+
+               remove_node (block);
+
+               g_clear_object (&range);
+               g_clear_object (&dom_selection);
+
+               restore_selection_to_history_event_state (editor_page, event->before);
+
+               e_editor_dom_force_spell_check_in_viewport (editor_page);
+
+               return;
+       }
+
+       /* Redoing Return key press */
+       if (event->type == HISTORY_INPUT && (empty ||
+           g_object_get_data (G_OBJECT (event->data.fragment), "history-return-key"))) {
+               if (e_editor_dom_key_press_event_process_return_key (editor_page)) {
+                       e_editor_dom_body_key_up_event_process_return_key (editor_page);
+               } else {
+                       WebKitDOMElement *element;
+                       WebKitDOMNode *next_sibling;
+
+                       range = get_range_for_point (document, event->before.start);
+                       webkit_dom_dom_selection_remove_all_ranges (dom_selection);
+                       webkit_dom_dom_selection_add_range (dom_selection, range);
+                       g_clear_object (&range);
+
+                       e_editor_dom_selection_save (editor_page);
+
+                       element = webkit_dom_document_get_element_by_id (
+                               document, "-x-evo-selection-end-marker");
+
+                       next_sibling = webkit_dom_node_get_next_sibling (WEBKIT_DOM_NODE (element));
+                       if (next_sibling && !(WEBKIT_DOM_IS_HTML_BR_ELEMENT (next_sibling))) {
+                               split_node_into_two (WEBKIT_DOM_NODE (element), 1);
+                       } else {
+                               WebKitDOMNode *block;
+
+                               block = e_editor_dom_get_parent_block_node_from_child (
+                                       WEBKIT_DOM_NODE (element));
+                               dom_remove_selection_markers (document);
+                               webkit_dom_node_insert_before (
+                                       webkit_dom_node_get_parent_node (block),
+                                       fragment,
+                                       webkit_dom_node_get_next_sibling (block),
+                                       NULL);
+                       }
+                       e_editor_dom_selection_restore (editor_page);
+               }
+
+               e_editor_page_set_return_key_pressed (editor_page, TRUE);
+               e_editor_dom_check_magic_links (editor_page, FALSE);
+               e_editor_page_set_return_key_pressed (editor_page, FALSE);
+               e_editor_dom_force_spell_check_in_viewport (editor_page);
+
+               g_clear_object (&dom_selection);
+
+               return;
+       }
+
+       if (!single_block) {
+               if (WEBKIT_DOM_IS_ELEMENT (first_child) &&
+                   !(WEBKIT_DOM_IS_HTML_QUOTE_ELEMENT (first_child) ||
+                     WEBKIT_DOM_IS_HTML_PRE_ELEMENT (first_child) ||
+                     WEBKIT_DOM_IS_HTML_PARAGRAPH_ELEMENT (first_child)))
+                       single_block = TRUE;
+       }
+
+       /* Multi block delete */
+       if (WEBKIT_DOM_IS_ELEMENT (first_child) && !single_block) {
+               gboolean delete;
+               WebKitDOMElement *signature;
+               WebKitDOMNode *node, *current_block, *last_child;
+               WebKitDOMNode *next_block, *insert_before;
+
+               range = get_range_for_point (document, event->after.start);
+               webkit_dom_dom_selection_remove_all_ranges (dom_selection);
+               webkit_dom_dom_selection_add_range (dom_selection, range);
+               g_clear_object (&range);
+               e_editor_dom_selection_save (editor_page);
+
+               if ((element = webkit_dom_document_get_element_by_id (document, 
"-x-evo-selection-end-marker"))) {
+                       WebKitDOMNode *next_sibling;
+
+                       if ((next_sibling = webkit_dom_node_get_next_sibling (WEBKIT_DOM_NODE (element))) &&
+                            WEBKIT_DOM_IS_CHARACTER_DATA (next_sibling) &&
+                            webkit_dom_character_data_get_length (WEBKIT_DOM_CHARACTER_DATA (next_sibling)) 
== 1) {
+                               gchar *data;
+
+                               data = webkit_dom_character_data_get_data (WEBKIT_DOM_CHARACTER_DATA 
(next_sibling));
+                               if (data && *data == ' ') {
+                                       WebKitDOMElement *hidden_space;
+
+                                       hidden_space = webkit_dom_document_create_element (document, "span", 
NULL);
+                                       webkit_dom_element_set_attribute (
+                                               hidden_space, "data-hidden-space", "", NULL);
+                                       remove_node (next_sibling);
+                                       webkit_dom_node_insert_before (
+                                               webkit_dom_node_get_parent_node (WEBKIT_DOM_NODE (element)),
+                                               WEBKIT_DOM_NODE (hidden_space),
+                                               webkit_dom_node_get_previous_sibling (
+                                                       WEBKIT_DOM_NODE (element)),
+                                               NULL);
+                               }
+                               g_free (data);
+                       }
+               }
+
+               element = webkit_dom_document_get_element_by_id (document, "-x-evo-selection-start-marker");
+
+               /* Get the last block in deleted content. */
+               last_child = webkit_dom_node_get_last_child (fragment);
+               while (WEBKIT_DOM_IS_HTML_QUOTE_ELEMENT (last_child))
+                       last_child = webkit_dom_node_get_last_child (last_child);
+
+               /* All the nodes that are in current block after the caret position
+                * belongs on the end of the deleted content. */
+               node = webkit_dom_node_get_next_sibling (WEBKIT_DOM_NODE (element));
+
+               /* FIXME Ugly hack */
+               /* If the selection ended in signature, the structure will be broken
+                * thus we saved the whole signature into deleted fragment and we will
+                * restore the whole signature, but we need to remove the rest of the
+                * signature that's left after delete to avoid duplications. */
+               signature = webkit_dom_document_query_selector (document, ".-x-evo-signature-wrapper", NULL);
+               delete = signature && webkit_dom_node_contains (WEBKIT_DOM_NODE (signature), WEBKIT_DOM_NODE 
(element));
+               if (!delete) {
+                       WebKitDOMNode *tmp_node;
+
+                       tmp_node = webkit_dom_node_get_last_child (fragment);
+                       delete = tmp_node && WEBKIT_DOM_IS_ELEMENT (tmp_node) &&
+                               element_has_class (WEBKIT_DOM_ELEMENT (tmp_node), "-x-evo-signature-wrapper");
+               }
+
+               current_block = e_editor_dom_get_parent_block_node_from_child (WEBKIT_DOM_NODE (element));
+
+               while (node) {
+                       WebKitDOMNode *next_sibling, *parent_node;
+
+                       next_sibling = webkit_dom_node_get_next_sibling (node);
+                       parent_node = webkit_dom_node_get_parent_node (node);
+                       /* Check if the whole element was deleted. If so replace it. */
+                       if (!next_sibling && WEBKIT_DOM_IS_HTML_BR_ELEMENT (node) &&
+                                       !webkit_dom_node_get_previous_sibling (WEBKIT_DOM_NODE (element))) {
+                               WebKitDOMNode *tmp_node;
+                               WebKitDOMElement *tmp_element;
+
+                               tmp_node = webkit_dom_node_get_parent_node (WEBKIT_DOM_NODE (element));
+                               webkit_dom_node_replace_child (
+                                       webkit_dom_node_get_parent_node (tmp_node),
+                                       fragment,
+                                       tmp_node,
+                                       NULL);
+
+                               /* Remove empty blockquotes, if presented. */
+                               tmp_element = webkit_dom_document_query_selector (
+                                       document, "blockquote[type=cite]:empty", NULL);
+                               if (tmp_element)
+                                       remove_node (WEBKIT_DOM_NODE (tmp_element));
+
+                               e_editor_dom_merge_siblings_if_necessary (editor_page, event->data.fragment);
+
+                               tmp_node = webkit_dom_node_get_last_child (last_child);
+                               if (tmp_node && WEBKIT_DOM_IS_ELEMENT (tmp_node) &&
+                                   element_has_class (WEBKIT_DOM_ELEMENT (tmp_node), "-x-evo-quoted")) {
+                                       webkit_dom_node_append_child (
+                                               last_child,
+                                               WEBKIT_DOM_NODE (
+                                                       webkit_dom_document_create_element (
+                                                               document, "br", NULL)),
+                                               NULL);
+                               }
+
+                               dom_remove_selection_markers (document);
+
+                               restore_selection_to_history_event_state (editor_page, event->before);
+
+                               e_editor_dom_force_spell_check_in_viewport (editor_page);
+
+                               g_clear_object (&dom_selection);
+
+                               return;
+                       } else if (!next_sibling && !webkit_dom_node_is_same_node (parent_node, 
current_block))
+                               next_sibling = webkit_dom_node_get_next_sibling (parent_node);
+
+                       if (delete)
+                               remove_node (node);
+                       else
+                               webkit_dom_node_append_child (last_child, node, NULL);
+                       node = next_sibling;
+               }
+
+               /* Get the first block in deleted content. */
+               while (WEBKIT_DOM_IS_HTML_QUOTE_ELEMENT (first_child))
+                       first_child = webkit_dom_node_get_first_child (first_child);
+
+               /* All the nodes that are in the first block of the deleted content
+                * belongs to the current block right after the caret position. */
+               while ((node = webkit_dom_node_get_first_child (first_child)))
+                       webkit_dom_node_append_child (current_block, node, NULL);
+
+               next_block = webkit_dom_node_get_next_sibling (current_block);
+               insert_before = next_block;
+
+               if (!insert_before) {
+                       WebKitDOMNode *parent = NULL;
+
+                       parent = current_block;
+                       while ((parent = webkit_dom_node_get_parent_node (parent)) &&
+                                       !WEBKIT_DOM_IS_HTML_BODY_ELEMENT (parent)) {
+                               insert_before = webkit_dom_node_get_next_sibling (parent);
+                               if (insert_before)
+                                       break;
+                       }
+               }
+
+               /* Split a BLOCKQUOTE if the deleted content started with BLOCKQUOTE */
+               if (insert_before &&
+                   WEBKIT_DOM_IS_HTML_QUOTE_ELEMENT (
+                       webkit_dom_node_get_first_child (WEBKIT_DOM_NODE (fragment))) &&
+                   e_editor_dom_get_citation_level (insert_before, FALSE > 0))
+                       insert_before = split_node_into_two (insert_before, -1);
+
+               /* Remove the first block from deleted content as its content was already
+                * moved to the right place. */
+               remove_node (first_child);
+
+               /* Insert the deleted content. */
+               if (insert_before)
+                       webkit_dom_node_insert_before (
+                               webkit_dom_node_get_parent_node (insert_before),
+                               WEBKIT_DOM_NODE (fragment),
+                               insert_before,
+                               NULL);
+               else
+                       webkit_dom_node_append_child (
+                               WEBKIT_DOM_NODE (
+                                       webkit_dom_document_get_body (document)),
+                               WEBKIT_DOM_NODE (fragment),
+                               NULL);
+
+               e_editor_dom_wrap_and_quote_element (editor_page, WEBKIT_DOM_ELEMENT (current_block));
+
+               if (WEBKIT_DOM_IS_ELEMENT (last_child))
+                       e_editor_dom_wrap_and_quote_element (editor_page, WEBKIT_DOM_ELEMENT (last_child));
+
+               e_editor_dom_merge_siblings_if_necessary (editor_page, event->data.fragment);
+
+               /* If undoing drag and drop where the whole line was moved we need
+                * to correct the selection. */
+               if (g_object_get_data (G_OBJECT (event->data.fragment), "history-drag-and-drop") &&
+                   (element = webkit_dom_document_get_element_by_id (document, 
"-x-evo-selection-end-marker"))) {
+                       WebKitDOMNode *prev_block;
+
+                       prev_block = webkit_dom_node_get_parent_node (WEBKIT_DOM_NODE (element));
+                       if ((prev_block = webkit_dom_node_get_previous_sibling (prev_block)))
+                               webkit_dom_node_append_child (
+                                       prev_block, WEBKIT_DOM_NODE (element), NULL);
+               }
+
+               e_editor_dom_selection_restore (editor_page);
+               e_editor_dom_force_spell_check_in_viewport (editor_page);
+       } else {
+               gboolean empty_text = FALSE, was_link = FALSE;
+               WebKitDOMNode *prev_sibling, *next_sibling, *nd;
+               WebKitDOMNode *parent;
+
+               element = webkit_dom_document_create_element (document, "span", NULL);
+
+               /* Create temporary node on the selection where the delete occured. */
+               if (webkit_dom_document_fragment_query_selector (event->data.fragment, ".Apple-tab-span", 
NULL))
+                       range = get_range_for_point (document, event->before.start);
+               else
+                       range = get_range_for_point (document, event->after.start);
+
+               webkit_dom_range_surround_contents (range, WEBKIT_DOM_NODE (element), NULL);
+               webkit_dom_dom_selection_remove_all_ranges (dom_selection);
+               webkit_dom_dom_selection_add_range (dom_selection, range);
+               g_clear_object (&range);
+
+               nd = webkit_dom_node_get_previous_sibling (WEBKIT_DOM_NODE (element));
+               if (nd && WEBKIT_DOM_IS_TEXT (nd)) {
+                       gchar *text = webkit_dom_character_data_get_data (WEBKIT_DOM_CHARACTER_DATA (nd));
+                       glong length = webkit_dom_character_data_get_length (WEBKIT_DOM_CHARACTER_DATA (nd));
+
+                       /* We have to preserve empty paragraphs with just UNICODE_ZERO_WIDTH_SPACE
+                        * character as when we will remove it paragraph will collapse. */
+                       if (length > 1) {
+                               if (g_str_has_prefix (text, UNICODE_ZERO_WIDTH_SPACE))
+                                       webkit_dom_character_data_replace_data (
+                                               WEBKIT_DOM_CHARACTER_DATA (nd), 0, 1, "", NULL);
+                               else if (g_str_has_suffix (text, UNICODE_ZERO_WIDTH_SPACE))
+                                       webkit_dom_character_data_replace_data (
+                                               WEBKIT_DOM_CHARACTER_DATA (nd), length - 1, 1, "", NULL);
+                       } else if (length == 0)
+                               empty_text = TRUE;
+
+                       g_free (text);
+               }
+
+               parent = webkit_dom_node_get_parent_node (WEBKIT_DOM_NODE (element));
+               if (!nd || empty_text) {
+                       if (WEBKIT_DOM_IS_HTML_ANCHOR_ELEMENT (parent))
+                               webkit_dom_node_insert_before (
+                                       webkit_dom_node_get_parent_node (parent),
+                                       WEBKIT_DOM_NODE (element),
+                                       parent,
+                                       NULL);
+               }
+
+               /* Insert the deleted content back to the body. */
+               if (WEBKIT_DOM_IS_HTML_ANCHOR_ELEMENT (parent)) {
+                       if (WEBKIT_DOM_IS_HTML_ANCHOR_ELEMENT (first_child)) {
+                               WebKitDOMNode *child;
+
+                               while ((child = webkit_dom_node_get_first_child (first_child)))
+                                       webkit_dom_node_append_child (parent, child, NULL);
+
+                               remove_node (first_child);
+
+                               was_link = TRUE;
+                               webkit_dom_node_insert_before (
+                                       webkit_dom_node_get_parent_node (parent),
+                                       fragment,
+                                       webkit_dom_node_get_next_sibling (parent),
+                                       NULL);
+                       } else {
+                               if (g_object_get_data (G_OBJECT (event->data.fragment), 
"history-removing-from-anchor") ||
+                                   !event_selection_was_collapsed (event)) {
+                                       webkit_dom_node_insert_before (
+                                               webkit_dom_node_get_parent_node (WEBKIT_DOM_NODE (element)),
+                                               fragment,
+                                               WEBKIT_DOM_NODE (element),
+                                               NULL);
+                               } else {
+                                       webkit_dom_node_insert_before (
+                                               webkit_dom_node_get_parent_node (parent),
+                                               fragment,
+                                               webkit_dom_node_get_next_sibling (parent),
+                                               NULL);
+                               }
+                       }
+               } else {
+                       webkit_dom_node_insert_before (
+                               webkit_dom_node_get_parent_node (WEBKIT_DOM_NODE (element)),
+                               fragment,
+                               WEBKIT_DOM_NODE (element),
+                               NULL);
+               }
+
+               webkit_dom_node_normalize (parent);
+               prev_sibling = webkit_dom_node_get_previous_sibling (WEBKIT_DOM_NODE (element));
+               next_sibling = webkit_dom_node_get_next_sibling (WEBKIT_DOM_NODE (element));
+               if (prev_sibling && next_sibling) {
+                       WebKitDOMNode *clone_prev, *clone_next;
+
+                       clone_prev = webkit_dom_node_clone_node_with_error (prev_sibling, FALSE, NULL);
+                       clone_next = webkit_dom_node_clone_node_with_error (next_sibling, FALSE, NULL);
+
+                       if (webkit_dom_node_is_equal_node (clone_prev, clone_next)) {
+                               WebKitDOMNode *child;
+
+                               while ((child = webkit_dom_node_get_first_child (next_sibling)))
+                                       webkit_dom_node_append_child (prev_sibling, child, NULL);
+
+                               remove_node (next_sibling);
+                       }
+               }
+
+               remove_node (WEBKIT_DOM_NODE (element));
+
+               if (event->type == HISTORY_DELETE && !e_editor_page_get_html_mode (editor_page)) {
+                       WebKitDOMNode *current_block;
+
+                       current_block = e_editor_dom_get_parent_block_node_from_child (parent);
+                       if (e_editor_dom_get_citation_level (current_block, FALSE) > 0)
+                               e_editor_dom_wrap_and_quote_element (editor_page, WEBKIT_DOM_ELEMENT 
(current_block));
+               }
+
+               /* If the selection markers are presented restore the selection,
+                * otherwise the selection was not collapsed so select the deleted
+                * content as it was before the delete occurred. */
+               if (webkit_dom_document_fragment_query_selector (event->data.fragment, 
"span#-x-evo-selection-start-marker", NULL))
+                       e_editor_dom_selection_restore (editor_page);
+               else
+                       restore_selection_to_history_event_state (editor_page, event->before);
+
+               if (event->type != HISTORY_INPUT) {
+                       if (e_editor_page_get_magic_smileys_enabled (editor_page))
+                               e_editor_dom_check_magic_smileys (editor_page);
+                       if (!was_link && e_editor_page_get_magic_links_enabled (editor_page))
+                               e_editor_dom_check_magic_links (editor_page, FALSE);
+               }
+               e_editor_dom_force_spell_check_for_current_paragraph (editor_page);
+       }
+
+       g_clear_object (&dom_selection);
+}
+
+static void
+redo_delete (EEditorPage *editor_page,
+             EEditorHistoryEvent *event)
+{
+       EEditorUndoRedoManager *manager;
+       WebKitDOMDocument *document;
+       WebKitDOMDocumentFragment *fragment = event->data.fragment;
+       WebKitDOMNode *node;
+       WebKitDOMHTMLElement *body;
+       gboolean delete_key, control_key, html_mode;
+       glong length = 1;
+       gint ii;
+
+       manager = e_editor_page_get_undo_redo_manager (editor_page);
+       document = e_editor_page_get_document (editor_page);
+       html_mode = e_editor_page_get_html_mode (editor_page);
+       restore_selection_to_history_event_state (editor_page, event->before);
+
+       delete_key = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (event->data.fragment), 
"history-delete-key"));
+       control_key = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (event->data.fragment), 
"history-control-key"));
+
+       body = webkit_dom_document_get_body (document);
+
+       if (!html_mode)
+               e_editor_dom_set_monospace_font_family_on_body (WEBKIT_DOM_ELEMENT (body), TRUE);
+
+       if (!delete_key && e_editor_dom_key_press_event_process_backspace_key (editor_page))
+               goto out;
+
+       if (e_editor_dom_key_press_event_process_delete_or_backspace_key (editor_page, ~0, 0, delete_key))
+               goto out;
+
+       if (control_key) {
+               gchar *text_content;
+
+               text_content = webkit_dom_node_get_text_content (WEBKIT_DOM_NODE (fragment));
+               length = g_utf8_strlen (text_content, -1);
+               control_key = length > 1;
+
+               g_free (text_content);
+       }
+
+       /* If concatenating two blocks with pressing Delete on the end
+        * of the previous one and the next node contain content that
+        * is wrapped on multiple lines, the last line will by separated
+        * by WebKit to the separate block. To avoid it let's remove
+        * all quoting and wrapping from the next paragraph. */
+       if (delete_key &&
+           GPOINTER_TO_INT (g_object_get_data (G_OBJECT (event->data.fragment), 
"history-concatenating-blocks"))) {
+               WebKitDOMNode *current_block, *next_block, *node;
+               WebKitDOMRange *range = NULL;
+
+               range = e_editor_dom_get_current_range (editor_page);
+               node = webkit_dom_range_get_end_container (range, NULL);
+               g_clear_object (&range);
+               current_block = e_editor_dom_get_parent_block_node_from_child (node);
+               if (e_editor_dom_get_citation_level (current_block, FALSE) > 0 &&
+                   (next_block = webkit_dom_node_get_next_sibling (current_block))) {
+                       e_editor_dom_remove_wrapping_from_element (WEBKIT_DOM_ELEMENT (next_block));
+                       e_editor_dom_remove_quoting_from_element (WEBKIT_DOM_ELEMENT (next_block));
+               }
+       }
+
+       for (ii = 0; ii < length; ii++) {
+               e_editor_dom_exec_command (editor_page,
+                       delete_key ? E_CONTENT_EDITOR_COMMAND_FORWARD_DELETE :
+                                    E_CONTENT_EDITOR_COMMAND_DELETE,
+                       NULL);
+       }
+
+       /* Really don't know why, but when the selection marker nodes were in
+        * anchors then we need to do an extra delete command otherwise we will
+        * end with two blocks split in half. */
+       node = webkit_dom_node_get_first_child (WEBKIT_DOM_NODE (fragment));
+       while ((node = webkit_dom_node_get_first_child (node))) {
+               if (WEBKIT_DOM_IS_HTML_ANCHOR_ELEMENT (node)) {
+                       e_editor_dom_exec_command (editor_page,
+                               E_CONTENT_EDITOR_COMMAND_FORWARD_DELETE,
+                               NULL);
+                       break;
+               }
+       }
+
+       node = webkit_dom_node_get_last_child (WEBKIT_DOM_NODE (fragment));
+       while ((node = webkit_dom_node_get_last_child (node))) {
+               if (WEBKIT_DOM_IS_HTML_ANCHOR_ELEMENT (node)) {
+                       e_editor_dom_exec_command (editor_page,
+                               E_CONTENT_EDITOR_COMMAND_FORWARD_DELETE,
+                               NULL);
+                       break;
+               }
+       }
+
+ out:
+       e_editor_page_set_dont_save_history_in_body_input (editor_page, TRUE);
+       e_editor_undo_redo_manager_set_operation_in_progress (manager, FALSE);
+       e_editor_dom_body_input_event_process (editor_page, NULL);
+       e_editor_page_set_dont_save_history_in_body_input (editor_page, FALSE);
+       e_editor_undo_redo_manager_set_operation_in_progress (manager, TRUE);
+       e_editor_page_set_renew_history_after_coordinates (editor_page, FALSE);
+       e_editor_dom_body_key_up_event_process_backspace_or_delete (editor_page, delete_key);
+       e_editor_page_set_renew_history_after_coordinates (editor_page, TRUE);
+       if (!html_mode)
+               e_editor_dom_set_monospace_font_family_on_body (WEBKIT_DOM_ELEMENT (body), FALSE);
+
+       restore_selection_to_history_event_state (editor_page, event->after);
+
+       e_editor_dom_force_spell_check_for_current_paragraph (editor_page);
+}
+
+typedef void (*SelectionStyleChangeFunc) (EEditorPage *editor_page, gint style);
+
+static void
+undo_redo_style_change (EEditorPage *editor_page,
+                        EEditorHistoryEvent *event,
+                        gboolean undo)
+{
+       SelectionStyleChangeFunc func;
+
+       switch (event->type) {
+               case HISTORY_ALIGNMENT:
+                       func = (SelectionStyleChangeFunc) e_editor_dom_selection_set_alignment;
+                       break;
+               case HISTORY_BOLD:
+                       func = e_editor_dom_selection_set_bold;
+                       break;
+               case HISTORY_BLOCK_FORMAT:
+                       func = (SelectionStyleChangeFunc) e_editor_dom_selection_set_block_format;
+                       break;
+               case HISTORY_FONT_SIZE:
+                       func = (SelectionStyleChangeFunc) e_editor_dom_selection_set_font_size;
+                       break;
+               case HISTORY_ITALIC:
+                       func = e_editor_dom_selection_set_italic;
+                       break;
+               case HISTORY_MONOSPACE:
+                       func = e_editor_dom_selection_set_monospace;
+                       break;
+               case HISTORY_STRIKETHROUGH:
+                       func = e_editor_dom_selection_set_strikethrough;
+                       break;
+               case HISTORY_UNDERLINE:
+                       func = e_editor_dom_selection_set_underline;
+                       break;
+               default:
+                       return;
+       }
+
+       restore_selection_to_history_event_state (editor_page, undo ? event->after : event->before);
+
+       func (editor_page, undo ? event->data.style.from : event->data.style.to);
+
+       restore_selection_to_history_event_state (editor_page, undo ? event->before : event->after);
+}
+
+static void
+undo_redo_indent (EEditorPage *editor_page,
+                  EEditorHistoryEvent *event,
+                  gboolean undo)
+{
+       gboolean was_indent = FALSE;
+
+       if (undo)
+               restore_selection_to_history_event_state (editor_page, event->after);
+
+       was_indent = event->data.style.from && event->data.style.to;
+
+       if ((undo && was_indent) || (!undo && !was_indent))
+               e_editor_dom_selection_unindent (editor_page);
+       else
+               e_editor_dom_selection_indent (editor_page);
+
+       if (undo)
+               restore_selection_to_history_event_state (editor_page, event->before);
+}
+
+static void
+undo_redo_font_color (EEditorPage *editor_page,
+                      EEditorHistoryEvent *event,
+                      gboolean undo)
+{
+       if (undo)
+               restore_selection_to_history_event_state (editor_page, event->after);
+
+       e_editor_dom_exec_command (editor_page,
+               E_CONTENT_EDITOR_COMMAND_FORE_COLOR,
+               undo ? event->data.string.from : event->data.string.to);
+
+       if (undo)
+               restore_selection_to_history_event_state (editor_page, event->before);
+}
+
+static void
+undo_redo_wrap (EEditorPage *editor_page,
+                EEditorHistoryEvent *event,
+                gboolean undo)
+{
+       if (undo)
+               restore_selection_to_history_event_state (editor_page, event->after);
+
+       if (undo) {
+               WebKitDOMNode *node;
+               WebKitDOMElement *element;
+               WebKitDOMRange *range = NULL;
+
+               range = e_editor_dom_get_current_range (editor_page);
+               node = webkit_dom_range_get_common_ancestor_container (range, NULL);
+               g_clear_object (&range);
+               element = get_parent_block_element (WEBKIT_DOM_NODE (node));
+               webkit_dom_element_remove_attribute (element, "data-user-wrapped");
+               e_editor_dom_remove_wrapping_from_element (WEBKIT_DOM_ELEMENT (element));
+
+               e_editor_dom_force_spell_check_for_current_paragraph (editor_page);
+       } else
+               e_editor_dom_selection_wrap (editor_page);
+
+       if (undo)
+               restore_selection_to_history_event_state (editor_page, event->before);
+}
+
+static void
+undo_redo_page_dialog (EEditorPage *editor_page,
+                       EEditorHistoryEvent *event,
+                       gboolean undo)
+{
+       WebKitDOMDocument *document;
+       WebKitDOMHTMLElement *body;
+       WebKitDOMNamedNodeMap *attributes = NULL, *attributes_history = NULL;
+       gint length, length_history, ii, jj;
+
+       document = e_editor_page_get_document (editor_page);
+       body = webkit_dom_document_get_body (document);
+
+       if (undo)
+               restore_selection_to_history_event_state (editor_page, event->after);
+
+       if (undo) {
+               attributes = webkit_dom_element_get_attributes (WEBKIT_DOM_ELEMENT (body));
+               attributes_history = webkit_dom_element_get_attributes (
+                       WEBKIT_DOM_ELEMENT (event->data.dom.from));
+       } else {
+               attributes_history = webkit_dom_element_get_attributes (WEBKIT_DOM_ELEMENT (body));
+               attributes = webkit_dom_element_get_attributes (
+                       WEBKIT_DOM_ELEMENT (event->data.dom.to));
+       }
+
+       length = webkit_dom_named_node_map_get_length (attributes);
+       length_history = webkit_dom_named_node_map_get_length (attributes_history);
+       for (ii = length - 1; ii >= 0; ii--) {
+               gchar *name;
+               WebKitDOMNode *attr;
+               gboolean replaced = FALSE;
+
+               attr = webkit_dom_named_node_map_item (attributes, ii);
+               name = webkit_dom_node_get_local_name (attr);
+
+               for (jj = length_history - 1; jj >= 0; jj--) {
+                       gchar *name_history;
+                       WebKitDOMNode *attr_history;
+
+                       attr_history = webkit_dom_named_node_map_item (attributes_history, jj);
+                       name_history = webkit_dom_node_get_local_name (attr_history);
+                       if (g_strcmp0 (name, name_history) == 0) {
+                               WebKitDOMNode *attr_clone;
+
+                               attr_clone = webkit_dom_node_clone_node_with_error (
+                                               undo ? attr_history : attr, TRUE, NULL);
+                               webkit_dom_element_set_attribute_node (
+                                       WEBKIT_DOM_ELEMENT (body),
+                                       WEBKIT_DOM_ATTR (attr_clone),
+                                       NULL);
+
+                               /* Link color has to replaced in HEAD as well. */
+                               if (g_strcmp0 (name, "link") == 0) {
+                                       gchar *value;
+
+                                       value = webkit_dom_node_get_node_value (attr_clone);
+                                       e_editor_dom_set_link_color (editor_page, value);
+                                       g_free (value);
+                               } else if (g_strcmp0 (name, "vlink") == 0) {
+                                       gchar *value;
+
+                                       value = webkit_dom_node_get_node_value (attr_clone);
+                                       e_editor_dom_set_visited_link_color (editor_page, value);
+                                       g_free (value);
+                               }
+                               replaced = TRUE;
+                       }
+                       g_free (name_history);
+                       g_clear_object (&attr_history);
+                       if (replaced)
+                               break;
+               }
+
+               if (!replaced) {
+                       if (undo) {
+                               webkit_dom_element_remove_attribute_node (
+                                       WEBKIT_DOM_ELEMENT (body),
+                                       WEBKIT_DOM_ATTR (attr),
+                                       NULL);
+                       } else {
+                               webkit_dom_element_set_attribute_node (
+                                       WEBKIT_DOM_ELEMENT (body),
+                                       WEBKIT_DOM_ATTR (
+                                               webkit_dom_node_clone_node_with_error (attr, TRUE, NULL)),
+                                       NULL);
+                       }
+               }
+               g_free (name);
+               g_object_unref (attr);
+       }
+       g_clear_object (&attributes);
+       g_clear_object (&attributes_history);
+
+       if (undo)
+               restore_selection_to_history_event_state (editor_page, event->before);
+}
+
+static void
+undo_redo_hrule_dialog (EEditorPage *editor_page,
+                        EEditorHistoryEvent *event,
+                        gboolean undo)
+{
+       WebKitDOMDocument *document;
+       WebKitDOMElement *element;
+
+       document = e_editor_page_get_document (editor_page);
+
+       if (undo)
+               restore_selection_to_history_event_state (editor_page, event->after);
+
+       e_editor_dom_selection_save (editor_page);
+       element = webkit_dom_document_get_element_by_id (
+               document, "-x-evo-selection-start-marker");
+
+       if (undo) {
+               WebKitDOMNode *node;
+               WebKitDOMElement *parent;
+
+               parent = get_parent_block_element (WEBKIT_DOM_NODE (element));
+               if (event->data.dom.from)
+                       node = webkit_dom_node_get_previous_sibling (WEBKIT_DOM_NODE (parent));
+               else
+                       node = webkit_dom_node_get_next_sibling (WEBKIT_DOM_NODE (parent));
+
+               if (node && WEBKIT_DOM_IS_HTML_HR_ELEMENT (node)) {
+                       if (!event->data.dom.from)
+                               remove_node (node);
+                       else
+                               webkit_dom_node_replace_child (
+                                       webkit_dom_node_get_parent_node (node),
+                                       webkit_dom_node_clone_node_with_error (event->data.dom.from, TRUE, 
NULL),
+                                       node,
+                                       NULL);
+               }
+       } else {
+               WebKitDOMNode *node;
+               WebKitDOMElement *parent;
+
+               parent = get_parent_block_element (WEBKIT_DOM_NODE (element));
+
+               if (event->data.dom.from) {
+                       node = webkit_dom_node_get_next_sibling (WEBKIT_DOM_NODE (parent));
+
+                       if (node && WEBKIT_DOM_IS_HTML_HR_ELEMENT (node))
+                               webkit_dom_node_replace_child (
+                                       webkit_dom_node_get_parent_node (node),
+                                       webkit_dom_node_clone_node_with_error (event->data.dom.to, TRUE, 
NULL),
+                                       node,
+                                       NULL);
+               } else {
+                       webkit_dom_node_insert_before (
+                               webkit_dom_node_get_parent_node (WEBKIT_DOM_NODE (parent)),
+                               event->data.dom.to,
+                               webkit_dom_node_get_next_sibling (WEBKIT_DOM_NODE (parent)),
+                               NULL);
+               }
+       }
+
+       if (undo) {
+               dom_remove_selection_markers (document);
+               restore_selection_to_history_event_state (editor_page, event->before);
+       } else
+               e_editor_dom_selection_restore (editor_page);
+}
+
+static void
+undo_redo_image_dialog (EEditorPage *editor_page,
+                        EEditorHistoryEvent *event,
+                        gboolean undo)
+{
+       WebKitDOMDocument *document;
+       WebKitDOMElement *element;
+       WebKitDOMNode *sibling, *image = NULL;
+
+       document = e_editor_page_get_document (editor_page);
+
+       if (undo)
+               restore_selection_to_history_event_state (editor_page, event->after);
+
+       e_editor_dom_selection_save (editor_page);
+       element = webkit_dom_document_get_element_by_id (
+               document, "-x-evo-selection-start-marker");
+       sibling = webkit_dom_node_get_previous_sibling (WEBKIT_DOM_NODE (element));
+       if (sibling && WEBKIT_DOM_IS_ELEMENT (sibling)) {
+               if (WEBKIT_DOM_IS_HTML_IMAGE_ELEMENT (sibling))
+                       image = sibling;
+               else if (element_has_class (WEBKIT_DOM_ELEMENT (sibling), "-x-evo-resizable-wrapper"))
+                       image = webkit_dom_node_get_first_child (sibling);
+       }
+
+       if (!image) {
+               element = WEBKIT_DOM_ELEMENT (webkit_dom_node_get_next_sibling (WEBKIT_DOM_NODE (element)));
+               sibling = webkit_dom_node_get_next_sibling (WEBKIT_DOM_NODE (element));
+               if (sibling && WEBKIT_DOM_IS_ELEMENT (sibling)) {
+                       if (WEBKIT_DOM_IS_HTML_IMAGE_ELEMENT (sibling))
+                               image = sibling;
+                       else if (element_has_class (WEBKIT_DOM_ELEMENT (sibling), "-x-evo-resizable-wrapper"))
+                               image = webkit_dom_node_get_first_child (sibling);
+               }
+       }
+
+       if (!image)
+               return;
+
+       webkit_dom_node_replace_child (
+               webkit_dom_node_get_parent_node (image),
+               webkit_dom_node_clone_node_with_error (undo ? event->data.dom.from : event->data.dom.to, 
TRUE, NULL),
+               image,
+               NULL);
+
+       if (undo)
+               restore_selection_to_history_event_state (editor_page, event->before);
+       else
+               e_editor_dom_selection_restore (editor_page);
+}
+
+static void
+undo_redo_link_dialog (EEditorPage *editor_page,
+                       EEditorHistoryEvent *event,
+                       gboolean undo)
+{
+       WebKitDOMDocument *document;
+       WebKitDOMElement *anchor, *element;
+
+       document = e_editor_page_get_document (editor_page);
+
+       if (undo)
+               restore_selection_to_history_event_state (editor_page, event->after);
+       else
+               restore_selection_to_history_event_state (editor_page, event->before);
+
+       e_editor_dom_selection_save (editor_page);
+
+       element = webkit_dom_document_get_element_by_id (document, "-x-evo-selection-start-marker");
+       if (!element)
+               return;
+
+       anchor = dom_node_find_parent_element (WEBKIT_DOM_NODE (element), "A");
+       if (undo) {
+               if (anchor) {
+                       if (!event->data.dom.from)
+                               remove_node (WEBKIT_DOM_NODE (anchor));
+                       else
+                               webkit_dom_node_replace_child (
+                                       webkit_dom_node_get_parent_node (WEBKIT_DOM_NODE (anchor)),
+                                       webkit_dom_node_clone_node_with_error (event->data.dom.from, TRUE, 
NULL),
+                                       WEBKIT_DOM_NODE (anchor),
+                                       NULL);
+               }
+       } else {
+               if (!event->data.dom.to) {
+                       if (anchor)
+                               remove_node (WEBKIT_DOM_NODE (anchor));
+               } else {
+                       if (WEBKIT_DOM_IS_ELEMENT (event->data.dom.from) && anchor) {
+                               webkit_dom_node_replace_child (
+                                       webkit_dom_node_get_parent_node (WEBKIT_DOM_NODE (anchor)),
+                                       webkit_dom_node_clone_node_with_error (event->data.dom.to, TRUE, 
NULL),
+                                       WEBKIT_DOM_NODE (anchor),
+                                       NULL);
+                       } else {
+                               webkit_dom_node_insert_before (
+                                       webkit_dom_node_get_parent_node (WEBKIT_DOM_NODE (element)),
+                                       webkit_dom_node_clone_node_with_error (event->data.dom.to, TRUE, 
NULL),
+                                       WEBKIT_DOM_NODE (element),
+                                       NULL);
+
+                               if (event->data.dom.from)
+                                       e_editor_dom_exec_command (editor_page,
+                                               E_CONTENT_EDITOR_COMMAND_DELETE, NULL);
+                       }
+               }
+       }
+
+       if (undo)
+               restore_selection_to_history_event_state (editor_page, event->before);
+       else
+               e_editor_dom_selection_restore (editor_page);
+}
+
+static void
+undo_redo_table_dialog (EEditorPage *editor_page,
+                        EEditorHistoryEvent *event,
+                        gboolean undo)
+{
+       WebKitDOMDocument *document;
+       WebKitDOMElement *table, *element;
+
+       document = e_editor_page_get_document (editor_page);
+
+       if (undo)
+               restore_selection_to_history_event_state (editor_page, event->after);
+
+       e_editor_dom_selection_save (editor_page);
+       element = webkit_dom_document_get_element_by_id (document, "-x-evo-selection-start-marker");
+       if (!element)
+               return;
+
+       table = dom_node_find_parent_element (WEBKIT_DOM_NODE (element), "TABLE");
+
+       if (!table) {
+               if ((!event->data.dom.to && undo) || (!event->data.dom.from && !undo)) {
+                       WebKitDOMElement *parent;
+
+                       parent = get_parent_block_element (WEBKIT_DOM_NODE (element));
+                       webkit_dom_node_insert_before (
+                               webkit_dom_node_get_parent_node (WEBKIT_DOM_NODE (parent)),
+                               webkit_dom_node_clone_node_with_error (undo ? event->data.dom.from : 
event->data.dom.to, TRUE, NULL),
+                               WEBKIT_DOM_NODE (parent),
+                               NULL);
+                       restore_selection_to_history_event_state (editor_page, event->before);
+                       return;
+               } else
+                       return;
+       }
+
+       if (undo) {
+               if (!event->data.dom.from)
+                       remove_node (WEBKIT_DOM_NODE (table));
+               else
+                       webkit_dom_node_replace_child (
+                               webkit_dom_node_get_parent_node (WEBKIT_DOM_NODE (table)),
+                               webkit_dom_node_clone_node_with_error (event->data.dom.from, TRUE, NULL),
+                               WEBKIT_DOM_NODE (table),
+                               NULL);
+       } else {
+               if (!event->data.dom.to)
+                       remove_node (WEBKIT_DOM_NODE (table));
+               else
+                       webkit_dom_node_replace_child (
+                               webkit_dom_node_get_parent_node (WEBKIT_DOM_NODE (table)),
+                               webkit_dom_node_clone_node_with_error (event->data.dom.to, TRUE, NULL),
+                               WEBKIT_DOM_NODE (table),
+                               NULL);
+       }
+
+       if (undo)
+               restore_selection_to_history_event_state (editor_page, event->before);
+       else
+               e_editor_dom_selection_restore (editor_page);
+}
+
+static void
+undo_redo_table_input (EEditorPage *editor_page,
+                       EEditorHistoryEvent *event,
+                       gboolean undo)
+{
+       WebKitDOMDocument *document;
+       WebKitDOMDOMWindow *dom_window = NULL;
+       WebKitDOMDOMSelection *dom_selection = NULL;
+       WebKitDOMElement *element;
+       WebKitDOMNode *node;
+       WebKitDOMRange *range = NULL;
+
+       document = e_editor_page_get_document (editor_page);
+
+       if (undo)
+               restore_selection_to_history_event_state (editor_page, event->after);
+
+       dom_window = webkit_dom_document_get_default_view (document);
+       dom_selection = webkit_dom_dom_window_get_selection (dom_window);
+       g_clear_object (&dom_window);
+
+       if (!webkit_dom_dom_selection_get_range_count (dom_selection)) {
+               g_clear_object (&dom_selection);
+               return;
+       }
+       range = webkit_dom_dom_selection_get_range_at (dom_selection, 0, NULL);
+       g_clear_object (&dom_selection);
+
+       /* Find if writing into table. */
+       node = webkit_dom_range_get_start_container (range, NULL);
+       if (WEBKIT_DOM_IS_HTML_TABLE_CELL_ELEMENT (node))
+               element = WEBKIT_DOM_ELEMENT (node);
+       else
+               element = get_parent_block_element (node);
+
+       g_clear_object (&range);
+
+       /* If writing to table we have to create different history event. */
+       if (!WEBKIT_DOM_IS_HTML_TABLE_CELL_ELEMENT (element))
+               return;
+
+       webkit_dom_node_replace_child (
+               webkit_dom_node_get_parent_node (WEBKIT_DOM_NODE (element)),
+               webkit_dom_node_clone_node_with_error (undo ? event->data.dom.from : event->data.dom.to, 
TRUE, NULL),
+               WEBKIT_DOM_NODE (element),
+               NULL);
+
+       e_editor_dom_selection_restore (editor_page);
+}
+
+static void
+undo_redo_paste (EEditorPage *editor_page,
+                 EEditorHistoryEvent *event,
+                 gboolean undo)
+{
+       WebKitDOMDocument *document;
+
+       document = e_editor_page_get_document (editor_page);
+
+       if (undo) {
+               if (event->type == HISTORY_PASTE_QUOTED) {
+                       WebKitDOMElement *tmp;
+                       WebKitDOMNode *parent;
+
+                       restore_selection_to_history_event_state (editor_page, event->after);
+
+                       e_editor_dom_selection_save (editor_page);
+                       tmp = webkit_dom_document_get_element_by_id (
+                               document, "-x-evo-selection-start-marker");
+                       if (!tmp)
+                               return;
+
+                       parent = webkit_dom_node_get_parent_node (WEBKIT_DOM_NODE (tmp));
+                       while (!WEBKIT_DOM_IS_HTML_BODY_ELEMENT (webkit_dom_node_get_parent_node (parent)))
+                               parent = webkit_dom_node_get_parent_node (parent);
+
+                       webkit_dom_node_replace_child (
+                               webkit_dom_node_get_parent_node (parent),
+                               WEBKIT_DOM_NODE (e_editor_dom_prepare_paragraph (editor_page, TRUE)),
+                               parent,
+                               NULL);
+
+                       e_editor_dom_selection_restore (editor_page);
+               } else {
+                       WebKitDOMDOMWindow *dom_window = NULL;
+                       WebKitDOMDOMSelection *dom_selection = NULL;
+                       WebKitDOMElement *element, *tmp;
+                       WebKitDOMRange *range = NULL;
+
+                       dom_window = webkit_dom_document_get_default_view (document);
+                       dom_selection = webkit_dom_dom_window_get_selection (dom_window);
+                       g_clear_object (&dom_window);
+
+                       /* Restore the selection how it was before the event occured. */
+                       range = get_range_for_point (document, event->before.start);
+                       webkit_dom_dom_selection_remove_all_ranges (dom_selection);
+                       webkit_dom_dom_selection_add_range (dom_selection, range);
+                       g_clear_object (&range);
+
+                       e_editor_dom_selection_save (editor_page);
+
+                       element = webkit_dom_document_get_element_by_id (
+                               document, "-x-evo-selection-end-marker");
+
+                       remove_node (WEBKIT_DOM_NODE (element));
+
+                       element = webkit_dom_document_get_element_by_id (
+                               document, "-x-evo-selection-start-marker");
+
+                       webkit_dom_element_remove_attribute (element, "id");
+
+                       range = get_range_for_point (document, event->after.start);
+                       webkit_dom_dom_selection_remove_all_ranges (dom_selection);
+                       webkit_dom_dom_selection_add_range (dom_selection, range);
+                       g_clear_object (&range);
+                       g_clear_object (&dom_selection);
+
+                       e_editor_dom_selection_save (editor_page);
+
+                       tmp = webkit_dom_document_get_element_by_id (
+                               document, "-x-evo-selection-start-marker");
+
+                       remove_node (WEBKIT_DOM_NODE (tmp));
+
+                       webkit_dom_element_set_id (
+                               element, "-x-evo-selection-start-marker");
+
+                       e_editor_dom_selection_restore (editor_page);
+
+                       e_editor_dom_exec_command (editor_page, E_CONTENT_EDITOR_COMMAND_DELETE, NULL);
+
+                       e_editor_dom_force_spell_check_for_current_paragraph (editor_page);
+               }
+       } else {
+               restore_selection_to_history_event_state (editor_page, event->before);
+
+               if (event->type == HISTORY_PASTE)
+                       e_editor_dom_convert_and_insert_html_into_selection (editor_page, 
event->data.string.to, FALSE);
+               else if (event->type == HISTORY_PASTE_QUOTED)
+                       e_editor_dom_quote_and_insert_text_into_selection (editor_page, 
event->data.string.to, FALSE);
+               else if (event->type == HISTORY_INSERT_HTML)
+                       e_editor_dom_insert_html (editor_page, event->data.string.to);
+               else
+                       e_editor_dom_convert_and_insert_html_into_selection (editor_page, 
event->data.string.to, FALSE);
+                       /* e_editor_selection_insert_as_text (selection, event->data.string.to); */
+       }
+}
+
+static void
+undo_redo_image (EEditorPage *editor_page,
+                 EEditorHistoryEvent *event,
+                 gboolean undo)
+{
+       WebKitDOMDocument *document;
+       WebKitDOMDOMWindow *dom_window = NULL;
+       WebKitDOMDOMSelection *dom_selection = NULL;
+       WebKitDOMRange *range = NULL;
+
+       document = e_editor_page_get_document (editor_page);
+       dom_window = webkit_dom_document_get_default_view (document);
+       dom_selection = webkit_dom_dom_window_get_selection (dom_window);
+       g_clear_object (&dom_window);
+
+       if (undo) {
+               WebKitDOMElement *element;
+               WebKitDOMNode *node;
+
+               range = get_range_for_point (document, event->before.start);
+               webkit_dom_dom_selection_remove_all_ranges (dom_selection);
+               webkit_dom_dom_selection_add_range (dom_selection, range);
+               g_clear_object (&range);
+
+               e_editor_dom_selection_save (editor_page);
+               element = webkit_dom_document_get_element_by_id (
+                       document, "-x-evo-selection-end-marker");
+
+               node = webkit_dom_node_get_next_sibling  (WEBKIT_DOM_NODE (element));
+
+               if (WEBKIT_DOM_IS_ELEMENT (node))
+                       if (element_has_class (WEBKIT_DOM_ELEMENT (node), "-x-evo-resizable-wrapper") ||
+                           element_has_class (WEBKIT_DOM_ELEMENT (node), "-x-evo-smiley-wrapper"))
+                               remove_node (node);
+               e_editor_dom_selection_restore (editor_page);
+       } else {
+               WebKitDOMElement *element;
+
+               range = get_range_for_point (document, event->before.start);
+               /* Create temporary node on the selection where the delete occured. */
+               webkit_dom_dom_selection_remove_all_ranges (dom_selection);
+               webkit_dom_dom_selection_add_range (dom_selection, range);
+               g_clear_object (&range);
+
+               e_editor_dom_selection_save (editor_page);
+               element = webkit_dom_document_get_element_by_id (
+                       document, "-x-evo-selection-start-marker");
+
+               /* Insert the deleted content back to the body. */
+               webkit_dom_node_insert_before (
+                       webkit_dom_node_get_parent_node (WEBKIT_DOM_NODE (element)),
+                       webkit_dom_node_clone_node_with_error (WEBKIT_DOM_NODE (event->data.fragment), TRUE, 
NULL),
+                       WEBKIT_DOM_NODE (element),
+                       NULL);
+
+               e_editor_dom_selection_restore (editor_page);
+               e_editor_dom_force_spell_check_for_current_paragraph (editor_page);
+       }
+
+       g_clear_object (&dom_selection);
+}
+
+static void
+undo_redo_replace (EEditorPage *editor_page,
+                   EEditorHistoryEvent *event,
+                   gboolean undo)
+{
+       WebKitDOMDocument *document;
+
+       document = e_editor_page_get_document (editor_page);
+
+       restore_selection_to_history_event_state (editor_page, undo ? event->after : event->before);
+
+       if (undo) {
+               WebKitDOMDOMWindow *dom_window = NULL;
+               WebKitDOMDOMSelection *dom_selection = NULL;
+
+               dom_window = webkit_dom_document_get_default_view (document);
+               dom_selection = webkit_dom_dom_window_get_selection (dom_window);
+               g_clear_object (&dom_window);
+
+               webkit_dom_dom_selection_modify (dom_selection, "extend", "left", "word");
+               g_clear_object (&dom_selection);
+       }
+
+       e_editor_dom_exec_command (editor_page,
+               E_CONTENT_EDITOR_COMMAND_INSERT_TEXT,
+               undo ? event->data.string.from : event->data.string.to);
+
+       e_editor_dom_force_spell_check_for_current_paragraph (editor_page);
+
+       restore_selection_to_history_event_state (editor_page, undo ? event->before : event->after);
+}
+
+static void
+undo_redo_replace_all (EEditorUndoRedoManager *manager,
+                       EEditorPage *editor_page,
+                       EEditorHistoryEvent *event,
+                       gboolean undo)
+{
+       WebKitDOMDocument *document;
+
+       document = e_editor_page_get_document (editor_page);
+
+       if (undo) {
+               if (event->type == HISTORY_REPLACE) {
+                       undo_redo_replace (editor_page, event, undo);
+                       return;
+               } else {
+                       EEditorHistoryEvent *next_event;
+                       GList *next_item;
+                       WebKitDOMDOMWindow *dom_window = NULL;
+                       WebKitDOMDOMSelection *dom_selection = NULL;
+
+                       next_item = manager->priv->history->next;
+
+                       while (next_item) {
+                               next_event = next_item->data;
+
+                               if (next_event->type != HISTORY_REPLACE)
+                                       break;
+
+                               if (g_strcmp0 (next_event->data.string.from, event->data.string.from) != 0)
+                                       break;
+
+                               if (g_strcmp0 (next_event->data.string.to, event->data.string.to) != 0)
+                                       break;
+
+                               undo_redo_replace (editor_page, next_event, undo);
+
+                               next_item = next_item->next;
+                       }
+
+                       manager->priv->history = next_item->prev;
+
+                       dom_window = webkit_dom_document_get_default_view (document);
+                       dom_selection = webkit_dom_dom_window_get_selection (dom_window);
+                       webkit_dom_dom_selection_collapse_to_end (dom_selection, NULL);
+                       g_clear_object (&dom_window);
+                       g_clear_object (&dom_selection);
+               }
+       } else {
+               /* Find if this history item is part of HISTORY_REPLACE_ALL. */
+               EEditorHistoryEvent *prev_event;
+               GList *prev_item;
+               gboolean replace_all = FALSE;
+
+               prev_item = manager->priv->history->prev;
+               while (prev_item) {
+                       prev_event = prev_item->data;
+
+                       if (prev_event->type == HISTORY_REPLACE)
+                               prev_item = prev_item->prev;
+                       else if (prev_event->type == HISTORY_REPLACE_ALL) {
+                               replace_all = TRUE;
+                               break;
+                       } else
+                               break;
+               }
+
+               if (!replace_all) {
+                       undo_redo_replace (editor_page, event, undo);
+                       return;
+               }
+
+               prev_item = manager->priv->history->prev;
+               while (prev_item) {
+                       prev_event = prev_item->data;
+
+                       if (prev_event->type == HISTORY_REPLACE) {
+                               undo_redo_replace (editor_page, prev_event, undo);
+                               prev_item = prev_item->prev;
+                       } else
+                               break;
+               }
+
+               manager->priv->history = prev_item->next;
+       }
+}
+
+static void
+undo_redo_remove_link (EEditorPage *editor_page,
+                       EEditorHistoryEvent *event,
+                       gboolean undo)
+{
+       WebKitDOMDocument *document;
+
+       document = e_editor_page_get_document (editor_page);
+
+       if (undo)
+               restore_selection_to_history_event_state (editor_page, event->after);
+
+       if (undo) {
+               WebKitDOMDOMWindow *dom_window = NULL;
+               WebKitDOMDOMSelection *dom_selection = NULL;
+               WebKitDOMElement *element;
+               WebKitDOMRange *range = NULL;
+
+               dom_window = webkit_dom_document_get_default_view (document);
+               dom_selection = webkit_dom_dom_window_get_selection (dom_window);
+               /* Select the anchor. */
+               webkit_dom_dom_selection_modify (dom_selection, "move", "left", "word");
+               webkit_dom_dom_selection_modify (dom_selection, "extend", "right", "word");
+
+               range = e_editor_dom_get_current_range (editor_page);
+               element = webkit_dom_document_create_element (document, "SPAN", NULL);
+               webkit_dom_range_surround_contents (range, WEBKIT_DOM_NODE (element), NULL);
+               g_clear_object (&range);
+               webkit_dom_node_insert_before (
+                       webkit_dom_node_get_parent_node (WEBKIT_DOM_NODE (element)),
+                       webkit_dom_node_clone_node_with_error (WEBKIT_DOM_NODE (event->data.fragment), TRUE, 
NULL),
+                       WEBKIT_DOM_NODE (element),
+                       NULL);
+               remove_node (WEBKIT_DOM_NODE (element));
+               g_clear_object (&dom_window);
+               g_clear_object (&dom_selection);
+       } else
+               e_editor_dom_selection_unlink (editor_page);
+
+       if (undo)
+               restore_selection_to_history_event_state (editor_page, event->before);
+}
+
+static void
+undo_return_in_empty_list_item (EEditorPage *editor_page,
+                               EEditorHistoryEvent *event)
+{
+       WebKitDOMDocument *document;
+       WebKitDOMElement *selection_start_marker;
+       WebKitDOMNode *parent;
+
+       document = e_editor_page_get_document (editor_page);
+       e_editor_dom_selection_save (editor_page);
+
+       selection_start_marker = webkit_dom_document_get_element_by_id (document, 
"-x-evo-selection-start-marker");
+       parent = webkit_dom_node_get_parent_node (WEBKIT_DOM_NODE (selection_start_marker));
+
+       if (WEBKIT_DOM_IS_HTML_LI_ELEMENT (parent)) {
+               WebKitDOMNode *parent_list;
+
+               dom_remove_selection_markers (document);
+               webkit_dom_node_insert_before (
+                       webkit_dom_node_get_parent_node (parent),
+                       webkit_dom_node_clone_node_with_error (WEBKIT_DOM_NODE (event->data.fragment), TRUE, 
NULL),
+                       webkit_dom_node_get_next_sibling (parent),
+                       NULL);
+
+               parent_list = parent;
+               while (node_is_list_or_item (webkit_dom_node_get_parent_node (parent_list)))
+                       parent_list = webkit_dom_node_get_parent_node (parent_list);
+
+               merge_lists_if_possible (parent_list);
+       }
+
+       e_editor_dom_selection_restore (editor_page);
+}
+
+static gboolean
+undo_return_press_after_h_rule (EEditorPage *editor_page,
+                                EEditorHistoryEvent *event)
+{
+       WebKitDOMDocument *document;
+       WebKitDOMElement *selection_start_marker, *block;
+       WebKitDOMNode *node;
+
+       document = e_editor_page_get_document (editor_page);
+
+       e_editor_dom_selection_save (editor_page);
+
+       selection_start_marker = webkit_dom_document_get_element_by_id (
+               document, "-x-evo-selection-start-marker");
+
+       block = get_parent_block_element (WEBKIT_DOM_NODE (selection_start_marker));
+       node = webkit_dom_node_get_previous_sibling (WEBKIT_DOM_NODE( block));
+
+       if (!webkit_dom_node_get_previous_sibling (WEBKIT_DOM_NODE (selection_start_marker)) &&
+            WEBKIT_DOM_IS_HTML_HR_ELEMENT (node)) {
+
+               remove_node_if_empty (WEBKIT_DOM_NODE (block));
+               restore_selection_to_history_event_state (editor_page, event->before);
+
+               return TRUE;
+       }
+
+       return FALSE;
+}
+
+static void
+undo_input (EEditorUndoRedoManager *manager,
+            EEditorPage *editor_page,
+            EEditorHistoryEvent *event)
+{
+       WebKitDOMDocument *document;
+       WebKitDOMDOMWindow *dom_window = NULL;
+       WebKitDOMDOMSelection *dom_selection = NULL;
+       WebKitDOMNode *node, *tmp_node;
+       gboolean remove_anchor;
+
+       document = e_editor_page_get_document (editor_page);
+       dom_window = webkit_dom_document_get_default_view (document);
+       dom_selection = webkit_dom_dom_window_get_selection (dom_window);
+
+       restore_selection_to_history_event_state (editor_page, event->after);
+
+       /* Undoing Return press after the HR element */
+       if (e_editor_page_get_html_mode (editor_page) &&
+           g_object_get_data (G_OBJECT (event->data.fragment), "history-return-key")) {
+               if (undo_return_press_after_h_rule (editor_page, event)) {
+                       g_clear_object (&dom_window);
+                       g_clear_object (&dom_selection);
+                       return;
+               }
+       }
+
+       webkit_dom_dom_selection_modify (dom_selection, "extend", "left", "character");
+       if (e_editor_dom_selection_is_citation (editor_page)) {
+               /* Post processing of quoted text in body_input_event_cb needs to be called. */
+               manager->priv->operation_in_progress = FALSE;
+               e_editor_page_set_dont_save_history_in_body_input (editor_page, TRUE);
+       }
+
+       /* If we are undoing the text that was appended to the link we have to
+        * remove the link and make just the plain text from it. */
+       node = webkit_dom_dom_selection_get_anchor_node (dom_selection);
+       node = webkit_dom_node_get_parent_node (node);
+       remove_anchor = WEBKIT_DOM_IS_HTML_ANCHOR_ELEMENT (node);
+       if (remove_anchor) {
+               gchar *text_content;
+
+               text_content = webkit_dom_node_get_text_content (node);
+               /* Remove the anchor just in case we are undoing the input from
+                * the end of it. */
+               remove_anchor =
+                       g_utf8_strlen (text_content, -1) ==
+                       webkit_dom_dom_selection_get_anchor_offset (dom_selection);
+               g_free (text_content);
+       }
+
+       e_editor_dom_exec_command (editor_page, E_CONTENT_EDITOR_COMMAND_DELETE, NULL);
+
+       if (remove_anchor) {
+               WebKitDOMNode *child;
+
+               /* Don't ask me why, but I got into the situation where the node
+                * that I received above was out of the document, and all the
+                * modifications to it were of course not propagated to it. Let's
+                * get that node again. */
+               node = webkit_dom_dom_selection_get_anchor_node (dom_selection);
+               node = webkit_dom_node_get_parent_node (node);
+               while ((child = webkit_dom_node_get_first_child (node)))
+                       webkit_dom_node_insert_before (
+                               webkit_dom_node_get_parent_node (node), child, node, NULL);
+
+               remove_node (node);
+       }
+
+       tmp_node = webkit_dom_node_get_first_child (WEBKIT_DOM_NODE (event->data.fragment));
+       if (WEBKIT_DOM_IS_HTML_LI_ELEMENT (tmp_node) &&
+           WEBKIT_DOM_IS_HTML_BR_ELEMENT (webkit_dom_node_get_last_child (tmp_node)))
+               undo_return_in_empty_list_item (editor_page, event);
+
+       g_clear_object (&dom_window);
+       g_clear_object (&dom_selection);
+}
+
+static void
+undo_redo_citation_split (EEditorPage *editor_page,
+                          EEditorHistoryEvent *event,
+                          gboolean undo)
+{
+       WebKitDOMDocument *document;
+       gboolean in_situ = FALSE;
+
+       document = e_editor_page_get_document (editor_page);
+
+       if (event->before.start.x == event->after.start.x &&
+           event->before.start.y == event->after.start.y &&
+           event->before.end.x == event->after.end.x &&
+           event->before.end.y == event->after.end.y)
+               in_situ = TRUE;
+
+       if (undo) {
+               WebKitDOMElement *selection_start, *parent;
+               WebKitDOMNode *citation_before, *citation_after, *child, *last_child, *tmp;
+
+               restore_selection_to_history_event_state (editor_page, event->after);
+
+               e_editor_dom_selection_save (editor_page);
+               selection_start = webkit_dom_document_get_element_by_id (
+                       document, "-x-evo-selection-start-marker");
+               if (!selection_start)
+                       return;
+
+               parent = get_parent_block_element (WEBKIT_DOM_NODE (selection_start));
+
+               citation_before = webkit_dom_node_get_previous_sibling (WEBKIT_DOM_NODE (parent));
+               if (!e_editor_dom_node_is_citation_node (citation_before)) {
+                       e_editor_dom_selection_restore (editor_page);
+                       return;
+               }
+
+               citation_after = webkit_dom_node_get_next_sibling (WEBKIT_DOM_NODE (parent));
+               if (!e_editor_dom_node_is_citation_node (citation_after)) {
+                       e_editor_dom_selection_restore (editor_page);
+                       return;
+               }
+
+               /* Get first block in next citation. */
+               child = webkit_dom_node_get_first_child (citation_after);
+               while (child && e_editor_dom_node_is_citation_node (child))
+                       child = webkit_dom_node_get_first_child (child);
+
+               /* Get last block in previous citation. */
+               last_child = webkit_dom_node_get_last_child (citation_before);
+               while (last_child && e_editor_dom_node_is_citation_node (last_child))
+                       last_child = webkit_dom_node_get_last_child (last_child);
+
+               /* Before appending any content to the block, check that the
+                * last node is not BR, if it is, remove it. */
+               tmp = webkit_dom_node_get_last_child (last_child);
+               if (WEBKIT_DOM_IS_HTML_BR_ELEMENT (tmp))
+                       remove_node (tmp);
+
+               if (in_situ && event->data.fragment) {
+                       webkit_dom_node_append_child (
+                               webkit_dom_node_get_parent_node (last_child),
+                               webkit_dom_node_clone_node_with_error (
+                                       WEBKIT_DOM_NODE (event->data.fragment), TRUE, NULL),
+                               NULL);
+               } else {
+                       e_editor_dom_remove_quoting_from_element (WEBKIT_DOM_ELEMENT (child));
+                       e_editor_dom_remove_wrapping_from_element (WEBKIT_DOM_ELEMENT (child));
+
+                       e_editor_dom_remove_quoting_from_element (WEBKIT_DOM_ELEMENT (last_child));
+                       e_editor_dom_remove_wrapping_from_element (WEBKIT_DOM_ELEMENT (last_child));
+
+                       /* Copy the content of the first block to the last block to get
+                        * to the state how the block looked like before it was split. */
+                       while ((tmp = webkit_dom_node_get_first_child (child)))
+                               webkit_dom_node_append_child (last_child, tmp, NULL);
+
+                       e_editor_dom_wrap_and_quote_element (editor_page, WEBKIT_DOM_ELEMENT (last_child));
+
+                       remove_node (child);
+               }
+
+               /* Move all the block from next citation to the previous one. */
+               while ((child = webkit_dom_node_get_first_child (citation_after)))
+                       webkit_dom_node_append_child (citation_before, child, NULL);
+
+               dom_remove_selection_markers (document);
+
+               remove_node (WEBKIT_DOM_NODE (parent));
+               remove_node (WEBKIT_DOM_NODE (citation_after));
+
+               /* If enter was pressed when some text was selected, restore it. */
+               if (event->data.fragment != NULL && !in_situ)
+                       undo_delete (editor_page, event);
+
+               e_editor_dom_merge_siblings_if_necessary (editor_page, NULL);
+
+               restore_selection_to_history_event_state (editor_page, event->before);
+
+               e_editor_dom_force_spell_check_in_viewport (editor_page);
+       } else {
+               restore_selection_to_history_event_state (editor_page, event->before);
+
+               if (in_situ) {
+                       WebKitDOMElement *selection_start_marker;
+                       WebKitDOMNode *block;
+
+                       e_editor_dom_selection_save (editor_page);
+
+                       selection_start_marker = webkit_dom_document_get_element_by_id (
+                               document, "-x-evo-selection-start-marker");
+
+                       block = e_editor_dom_get_parent_block_node_from_child (
+                               WEBKIT_DOM_NODE (selection_start_marker));
+                       dom_remove_selection_markers (document);
+
+                       /* Remove current block (and all of its parents if they
+                        * are empty) as it will be replaced by a new block that
+                        * will be in the body and not in the blockquote. */
+                       e_editor_dom_remove_node_and_parents_if_empty (block);
+               }
+
+               e_editor_dom_insert_new_line_into_citation (editor_page, "");
+       }
+}
+
+static void
+undo_redo_unquote (EEditorPage *editor_page,
+                  EEditorHistoryEvent *event,
+                   gboolean undo)
+{
+       WebKitDOMDocument *document;
+       WebKitDOMElement *element;
+
+       document = e_editor_page_get_document (editor_page);
+
+       restore_selection_to_history_event_state (editor_page, undo ? event->after : event->before);
+
+       e_editor_dom_selection_save (editor_page);
+       element = webkit_dom_document_get_element_by_id (
+               document, "-x-evo-selection-start-marker");
+
+       if (undo) {
+               WebKitDOMNode *next_sibling, *prev_sibling;
+               WebKitDOMElement *block;
+
+               block = get_parent_block_element (WEBKIT_DOM_NODE (element));
+
+               next_sibling = webkit_dom_node_get_next_sibling (WEBKIT_DOM_NODE (block));
+               prev_sibling = webkit_dom_node_get_previous_sibling (WEBKIT_DOM_NODE (block));
+
+               if (prev_sibling && e_editor_dom_node_is_citation_node (prev_sibling)) {
+                       webkit_dom_node_append_child (
+                               prev_sibling,
+                               webkit_dom_node_clone_node_with_error (event->data.dom.from, TRUE, NULL),
+                               NULL);
+
+                       if (next_sibling && e_editor_dom_node_is_citation_node (next_sibling)) {
+                               WebKitDOMNode *child;
+
+                               while  ((child = webkit_dom_node_get_first_child (next_sibling)))
+                                       webkit_dom_node_append_child (
+                                               prev_sibling, child, NULL);
+
+                               remove_node (next_sibling);
+                       }
+               } else if (next_sibling && e_editor_dom_node_is_citation_node (next_sibling)) {
+                       webkit_dom_node_insert_before (
+                               next_sibling,
+                               webkit_dom_node_clone_node_with_error (event->data.dom.from, TRUE, NULL),
+                               webkit_dom_node_get_first_child (next_sibling),
+                               NULL);
+               }
+
+               remove_node (WEBKIT_DOM_NODE (block));
+       } else
+               e_editor_dom_move_quoted_block_level_up (editor_page);
+
+       if (undo)
+               e_editor_dom_selection_restore (editor_page);
+       else
+               restore_selection_to_history_event_state (editor_page, event->after);
+
+       e_editor_dom_force_spell_check_for_current_paragraph (editor_page);
+}
+
+gboolean
+e_editor_undo_redo_manager_is_operation_in_progress (EEditorUndoRedoManager *manager)
+{
+       g_return_val_if_fail (E_IS_EDITOR_UNDO_REDO_MANAGER (manager), FALSE);
+
+       return manager->priv->operation_in_progress;
+}
+
+void
+e_editor_undo_redo_manager_set_operation_in_progress (EEditorUndoRedoManager *manager,
+                                                           gboolean value)
+{
+       g_return_if_fail (E_IS_EDITOR_UNDO_REDO_MANAGER (manager));
+
+       manager->priv->operation_in_progress = value;
+}
+
+static void
+free_history_event_content (EEditorHistoryEvent *event)
+{
+       switch (event->type) {
+               case HISTORY_INPUT:
+               case HISTORY_DELETE:
+               case HISTORY_CITATION_SPLIT:
+               case HISTORY_IMAGE:
+               case HISTORY_SMILEY:
+               case HISTORY_REMOVE_LINK:
+                       if (event->data.fragment != NULL)
+                               g_clear_object (&event->data.fragment);
+                       break;
+               case HISTORY_FONT_COLOR:
+               case HISTORY_PASTE:
+               case HISTORY_PASTE_AS_TEXT:
+               case HISTORY_PASTE_QUOTED:
+               case HISTORY_INSERT_HTML:
+               case HISTORY_REPLACE:
+               case HISTORY_REPLACE_ALL:
+                       if (event->data.string.from != NULL)
+                               g_free (event->data.string.from);
+                       if (event->data.string.to != NULL)
+                               g_free (event->data.string.to);
+                       break;
+               case HISTORY_HRULE_DIALOG:
+               case HISTORY_IMAGE_DIALOG:
+               case HISTORY_CELL_DIALOG:
+               case HISTORY_TABLE_DIALOG:
+               case HISTORY_TABLE_INPUT:
+               case HISTORY_PAGE_DIALOG:
+               case HISTORY_UNQUOTE:
+               case HISTORY_LINK_DIALOG:
+                       if (event->data.dom.from != NULL)
+                               g_clear_object (&event->data.dom.from);
+                       if (event->data.dom.to != NULL)
+                               g_clear_object (&event->data.dom.to);
+                       break;
+               default:
+                       break;
+       }
+}
+
+static void
+free_history_event (EEditorHistoryEvent *event)
+{
+       if (event == NULL)
+               return;
+
+       free_history_event_content (event);
+
+       g_free (event);
+}
+
+static void
+remove_history_event (EEditorUndoRedoManager *manager,
+                      GList *item)
+{
+       free_history_event_content (item->data);
+
+       manager->priv->history = g_list_delete_link (manager->priv->history, item);
+       manager->priv->history_size--;
+}
+
+static void
+remove_forward_redo_history_events_if_needed (EEditorUndoRedoManager *manager)
+{
+       GList *history = manager->priv->history;
+       GList *item;
+
+       if (!history || !history->prev)
+               return;
+
+       item = history->prev;
+       while (item) {
+               GList *prev_item = item->prev;
+
+               remove_history_event (manager, item);
+               item = prev_item;
+       }
+}
+
+void
+e_editor_undo_redo_manager_insert_history_event (EEditorUndoRedoManager *manager,
+                                                EEditorHistoryEvent *event)
+{
+       g_return_if_fail (E_IS_EDITOR_UNDO_REDO_MANAGER (manager));
+
+       if (manager->priv->operation_in_progress)
+               return;
+
+       d (printf ("\nINSERTING EVENT:\n"));
+       d (print_history_event (event));
+
+       remove_forward_redo_history_events_if_needed (manager);
+
+       if (manager->priv->history_size >= HISTORY_SIZE_LIMIT) {
+               remove_history_event (manager, g_list_last (manager->priv->history)->prev);
+               /* FIXME WK2 - what if g_list_last (manager->priv->history) returns NULL? */
+               while (((EEditorHistoryEvent *) (g_list_last (manager->priv->history)->prev))->type == 
HISTORY_AND) {
+                       remove_history_event (manager, g_list_last (manager->priv->history)->prev);
+                       remove_history_event (manager, g_list_last (manager->priv->history)->prev);
+               }
+
+       }
+
+       manager->priv->history = g_list_prepend (manager->priv->history, event);
+       manager->priv->history_size++;
+
+       d (print_history (manager));
+
+       g_object_notify (G_OBJECT (manager), "can-undo");
+}
+
+EEditorHistoryEvent *
+e_editor_undo_redo_manager_get_current_history_event (EEditorUndoRedoManager *manager)
+{
+       g_return_val_if_fail (E_IS_EDITOR_UNDO_REDO_MANAGER (manager), NULL);
+
+       if (manager->priv->history)
+               return manager->priv->history->data;
+
+       return NULL;
+}
+
+void
+e_editor_undo_redo_manager_remove_current_history_event (EEditorUndoRedoManager *manager)
+{
+       g_return_if_fail (E_IS_EDITOR_UNDO_REDO_MANAGER (manager));
+
+       if (!manager->priv->history)
+               return;
+
+       remove_history_event (manager, manager->priv->history);
+}
+
+void
+e_editor_undo_redo_manager_insert_dash_history_event (EEditorUndoRedoManager *manager)
+{
+       WebKitDOMDocument *document;
+       WebKitDOMDocumentFragment *fragment;
+       EEditorPage *editor_page;
+       EEditorHistoryEvent *event, *last;
+       GList *history;
+
+       g_return_if_fail (E_IS_EDITOR_UNDO_REDO_MANAGER (manager));
+
+       editor_page = editor_undo_redo_manager_ref_editor_page (manager);
+       g_return_if_fail (editor_page != NULL);
+
+       event = g_new0 (EEditorHistoryEvent, 1);
+       event->type = HISTORY_INPUT;
+
+       document = e_editor_page_get_document (editor_page);
+       fragment = webkit_dom_document_create_document_fragment (document);
+       webkit_dom_node_append_child (
+               WEBKIT_DOM_NODE (fragment),
+               WEBKIT_DOM_NODE (
+                       webkit_dom_document_create_text_node (document, "-")),
+               NULL);
+       webkit_dom_node_append_child (
+               WEBKIT_DOM_NODE (fragment),
+               WEBKIT_DOM_NODE (
+                       dom_create_selection_marker (document, TRUE)),
+               NULL);
+       webkit_dom_node_append_child (
+               WEBKIT_DOM_NODE (fragment),
+               WEBKIT_DOM_NODE (
+                       dom_create_selection_marker (document, FALSE)),
+               NULL);
+       event->data.fragment = fragment;
+
+       last = e_editor_undo_redo_manager_get_current_history_event (manager);
+       /* The dash event needs to have the same coordinates as the character
+        * that is right after it. */
+       event->after.start.x = last->after.start.x;
+       event->after.start.y = last->after.start.y;
+       event->after.end.x = last->after.end.x;
+       event->after.end.y = last->after.end.y;
+
+       history = manager->priv->history;
+       if (history) {
+               EEditorHistoryEvent *item;
+               WebKitDOMNode *first_child;
+
+               item = history->data;
+
+               if (item->type != HISTORY_INPUT) {
+                       g_object_unref (editor_page);
+                       return;
+               }
+
+               first_child = webkit_dom_node_get_first_child (WEBKIT_DOM_NODE (item->data.fragment));
+               if (WEBKIT_DOM_IS_TEXT (first_child)) {
+                       guint diff;
+
+                       diff = event->after.start.x - item->after.start.x;
+
+                       /* We need to move the coordinate of the last
+                        * event by one character. */
+                       last->after.start.x += diff;
+                       last->after.end.x += diff;
+
+                       manager->priv->history = g_list_insert_before (
+                               manager->priv->history, history, event);
+               }
+       }
+
+       g_object_unref (editor_page);
+}
+
+gboolean
+e_editor_undo_redo_manager_can_undo (EEditorUndoRedoManager *manager)
+{
+       g_return_val_if_fail (E_IS_EDITOR_UNDO_REDO_MANAGER (manager), FALSE);
+
+       if (manager->priv->history) {
+               EEditorHistoryEvent *event;
+
+               event = manager->priv->history->data;
+
+               return (event->type != HISTORY_START);
+       } else
+               return FALSE;
+}
+
+void
+e_editor_undo_redo_manager_undo (EEditorUndoRedoManager *manager)
+{
+       EEditorHistoryEvent *event;
+       EEditorPage *editor_page;
+       GList *history;
+
+       g_return_if_fail (E_IS_EDITOR_UNDO_REDO_MANAGER (manager));
+
+       if (!e_editor_undo_redo_manager_can_undo (manager))
+               return;
+
+       history = manager->priv->history;
+       event = history->data;
+
+       d (printf ("\nUNDOING EVENT:\n"));
+       d (print_history_event (event));
+
+       manager->priv->operation_in_progress = TRUE;
+
+       editor_page = editor_undo_redo_manager_ref_editor_page (manager);
+       g_return_if_fail (editor_page != NULL);
+
+       switch (event->type) {
+               case HISTORY_BOLD:
+               case HISTORY_ITALIC:
+               case HISTORY_STRIKETHROUGH:
+               case HISTORY_UNDERLINE:
+               case HISTORY_FONT_SIZE:
+                       if (event_selection_was_collapsed (event)) {
+                               if (history->next) {
+                                       manager->priv->history = history->next;
+                                       e_editor_undo_redo_manager_undo (manager);
+                               }
+                               manager->priv->operation_in_progress = FALSE;
+                               g_object_unref (editor_page);
+                               return;
+                       }
+               case HISTORY_ALIGNMENT:
+               case HISTORY_BLOCK_FORMAT:
+               case HISTORY_MONOSPACE:
+                       undo_redo_style_change (editor_page, event, TRUE);
+                       break;
+               case HISTORY_DELETE:
+                       undo_delete (editor_page, event);
+                       break;
+               case HISTORY_INDENT:
+                       undo_redo_indent (editor_page, event, TRUE);
+                       break;
+               case HISTORY_INPUT:
+                       undo_input (manager, editor_page, event);
+                       break;
+               case HISTORY_REMOVE_LINK:
+                       undo_redo_remove_link (editor_page, event, TRUE);
+                       break;
+               case HISTORY_FONT_COLOR:
+                       undo_redo_font_color (editor_page, event, TRUE);
+                       break;
+               case HISTORY_CITATION_SPLIT:
+                       undo_redo_citation_split (editor_page, event, TRUE);
+                       break;
+               case HISTORY_PASTE:
+               case HISTORY_PASTE_AS_TEXT:
+               case HISTORY_PASTE_QUOTED:
+               case HISTORY_INSERT_HTML:
+                       undo_redo_paste (editor_page, event, TRUE);
+                       break;
+               case HISTORY_IMAGE:
+               case HISTORY_SMILEY:
+                       undo_redo_image (editor_page, event, TRUE);
+                       break;
+               case HISTORY_WRAP:
+                       undo_redo_wrap (editor_page, event, TRUE);
+                       break;
+               case HISTORY_IMAGE_DIALOG:
+                       undo_redo_image_dialog (editor_page, event, TRUE);
+                       break;
+               case HISTORY_LINK_DIALOG:
+                       undo_redo_link_dialog (editor_page, event, TRUE);
+                       break;
+               case HISTORY_TABLE_DIALOG:
+                       undo_redo_table_dialog (editor_page, event, TRUE);
+                       break;
+               case HISTORY_TABLE_INPUT:
+                       undo_redo_table_input (editor_page, event, TRUE);
+                       break;
+               case HISTORY_PAGE_DIALOG:
+                       undo_redo_page_dialog (editor_page, event, TRUE);
+                       break;
+               case HISTORY_HRULE_DIALOG:
+                       undo_redo_hrule_dialog (editor_page, event, TRUE);
+                       break;
+               case HISTORY_REPLACE:
+               case HISTORY_REPLACE_ALL:
+                       undo_redo_replace_all (manager, editor_page, event, TRUE);
+                       break;
+               case HISTORY_UNQUOTE:
+                       undo_redo_unquote (editor_page, event, TRUE);
+                       break;
+               case HISTORY_AND:
+                       g_warning ("Unhandled HISTORY_AND event!");
+                       break;
+               default:
+                       g_object_unref (editor_page);
+                       return;
+       }
+
+       /* FIXME WK2 - history->next can be NULL! */
+       event = history->next->data;
+       if (event->type == HISTORY_AND) {
+               manager->priv->history = history->next->next;
+               e_editor_undo_redo_manager_undo (manager);
+               g_object_unref (editor_page);
+               return;
+       }
+
+       if (history->next)
+               manager->priv->history = manager->priv->history->next;
+
+       d (print_undo_events (manager));
+/* FIXME WK2
+       html_editor_view_user_changed_contents_cb (view);*/
+
+       manager->priv->operation_in_progress = FALSE;
+
+       g_object_unref (editor_page);
+
+       g_object_notify (G_OBJECT (manager), "can-undo");
+       g_object_notify (G_OBJECT (manager), "can-redo");
+}
+
+gboolean
+e_editor_undo_redo_manager_can_redo (EEditorUndoRedoManager *manager)
+{
+       g_return_val_if_fail (E_IS_EDITOR_UNDO_REDO_MANAGER (manager), FALSE);
+
+       if (manager->priv->history && manager->priv->history->prev)
+               return TRUE;
+       else
+               return FALSE;
+}
+
+void
+e_editor_undo_redo_manager_redo (EEditorUndoRedoManager *manager)
+{
+       EEditorPage *editor_page;
+       EEditorHistoryEvent *event;
+       GList *history;
+
+       if (!e_editor_undo_redo_manager_can_redo (manager))
+               return;
+
+       history = manager->priv->history;
+       event = history->prev->data;
+
+       d (printf ("\nREDOING EVENT:\n"));
+       d (print_history_event (event));
+
+       editor_page = editor_undo_redo_manager_ref_editor_page (manager);
+       g_return_if_fail (editor_page != NULL);
+
+       manager->priv->operation_in_progress = TRUE;
+
+       switch (event->type) {
+               case HISTORY_BOLD:
+               case HISTORY_MONOSPACE:
+               case HISTORY_STRIKETHROUGH:
+               case HISTORY_UNDERLINE:
+               case HISTORY_ALIGNMENT:
+               case HISTORY_BLOCK_FORMAT:
+               case HISTORY_FONT_SIZE:
+               case HISTORY_ITALIC:
+                       undo_redo_style_change (editor_page, event, FALSE);
+                       break;
+               case HISTORY_DELETE:
+                       redo_delete (editor_page, event);
+                       break;
+               case HISTORY_INDENT:
+                       undo_redo_indent (editor_page, event, FALSE);
+                       break;
+               case HISTORY_INPUT:
+                       undo_delete (editor_page, event);
+                       e_editor_dom_check_magic_smileys (editor_page);
+                       {
+                               gchar *text_content;
+                               WebKitDOMNode *first_child;
+
+                               first_child = webkit_dom_node_get_first_child (
+                                       WEBKIT_DOM_NODE (event->data.fragment));
+                               text_content = webkit_dom_node_get_text_content (first_child);
+                               /* Call magic links when the space was pressed. */
+                               if (g_str_has_prefix (text_content, UNICODE_NBSP)) {
+                                       e_editor_page_set_space_key_pressed (editor_page, TRUE);
+                                       e_editor_dom_check_magic_links (editor_page, FALSE);
+                                       e_editor_page_set_space_key_pressed (editor_page, FALSE);
+                               }
+                               g_free (text_content);
+                       }
+                       break;
+               case HISTORY_REMOVE_LINK:
+                       undo_redo_remove_link (editor_page, event, FALSE);
+                       break;
+               case HISTORY_FONT_COLOR:
+                       undo_redo_font_color (editor_page, event, FALSE);
+                       break;
+               case HISTORY_CITATION_SPLIT:
+                       undo_redo_citation_split (editor_page, event, FALSE);
+                       break;
+               case HISTORY_PASTE:
+               case HISTORY_PASTE_AS_TEXT:
+               case HISTORY_PASTE_QUOTED:
+               case HISTORY_INSERT_HTML:
+                       undo_redo_paste (editor_page, event, FALSE);
+                       break;
+               case HISTORY_IMAGE:
+               case HISTORY_SMILEY:
+                       undo_redo_image (editor_page, event, FALSE);
+                       break;
+               case HISTORY_WRAP:
+                       undo_redo_wrap (editor_page, event, FALSE);
+                       break;
+               case HISTORY_IMAGE_DIALOG:
+                       undo_redo_image_dialog (editor_page, event, FALSE);
+                       break;
+               case HISTORY_LINK_DIALOG:
+                       undo_redo_link_dialog (editor_page, event, FALSE);
+                       break;
+               case HISTORY_TABLE_DIALOG:
+                       undo_redo_table_dialog (editor_page, event, FALSE);
+                       break;
+               case HISTORY_TABLE_INPUT:
+                       undo_redo_table_input (editor_page, event, FALSE);
+                       break;
+               case HISTORY_PAGE_DIALOG:
+                       undo_redo_page_dialog (editor_page, event, FALSE);
+                       break;
+               case HISTORY_HRULE_DIALOG:
+                       undo_redo_hrule_dialog (editor_page, event, FALSE);
+                       break;
+               case HISTORY_REPLACE:
+               case HISTORY_REPLACE_ALL:
+                       undo_redo_replace_all (manager, editor_page, event, FALSE);
+                       break;
+               case HISTORY_UNQUOTE:
+                       undo_redo_unquote (editor_page, event, FALSE);
+                       break;
+               case HISTORY_AND:
+                       g_warning ("Unhandled HISTORY_AND event!");
+                       break;
+               default:
+                       g_object_unref (editor_page);
+                       return;
+       }
+
+       /* FIXME WK2 - what if history->prev is NULL? */
+       if (history->prev->prev) {
+               event = history->prev->prev->data;
+               if (event->type == HISTORY_AND) {
+                       manager->priv->history = manager->priv->history->prev->prev;
+                       e_editor_undo_redo_manager_redo (manager);
+                       g_object_unref (editor_page);
+                       return;
+               }
+       }
+
+       manager->priv->history = manager->priv->history->prev;
+
+       d (print_redo_events (manager));
+/* FIXME WK2
+       html_editor_view_user_changed_contents_cb (view);*/
+
+       manager->priv->operation_in_progress = FALSE;
+
+       g_object_unref (editor_page);
+
+       g_object_notify (G_OBJECT (manager), "can-undo");
+       g_object_notify (G_OBJECT (manager), "can-redo");
+}
+
+void
+e_editor_undo_redo_manager_clean_history (EEditorUndoRedoManager *manager)
+{
+       EEditorPage *editor_page;
+       EEditorHistoryEvent *ev;
+
+       g_return_if_fail (E_IS_EDITOR_UNDO_REDO_MANAGER (manager));
+
+       if (manager->priv->history != NULL) {
+               g_list_free_full (manager->priv->history, (GDestroyNotify) free_history_event);
+               manager->priv->history = NULL;
+       }
+
+       manager->priv->history_size = 0;
+       editor_page = editor_undo_redo_manager_ref_editor_page (manager);
+       g_return_if_fail (editor_page != NULL);
+       e_editor_page_set_dont_save_history_in_body_input (editor_page, FALSE);
+       g_object_unref (editor_page);
+       manager->priv->operation_in_progress = FALSE;
+
+       ev = g_new0 (EEditorHistoryEvent, 1);
+       ev->type = HISTORY_START;
+       manager->priv->history = g_list_append (manager->priv->history, ev);
+
+       g_object_notify (G_OBJECT (manager), "can-undo");
+       g_object_notify (G_OBJECT (manager), "can-redo");
+}
+
+static void
+editor_undo_redo_manager_set_editor_page (EEditorUndoRedoManager *manager,
+                                         EEditorPage *editor_page)
+{
+       g_return_if_fail (E_IS_EDITOR_PAGE (editor_page));
+
+       g_weak_ref_set (&manager->priv->editor_page, editor_page);
+}
+
+static void
+editor_undo_redo_manager_dispose (GObject *object)
+{
+       EEditorUndoRedoManagerPrivate *priv;
+
+       priv = E_EDITOR_UNDO_REDO_MANAGER_GET_PRIVATE (object);
+
+       if (priv->history != NULL) {
+               g_list_free_full (priv->history, (GDestroyNotify) free_history_event);
+               priv->history = NULL;
+       }
+
+       g_weak_ref_set (&priv->editor_page, NULL);
+
+       /* Chain up to parent's dispose() method. */
+       G_OBJECT_CLASS (e_editor_undo_redo_manager_parent_class)->dispose (object);
+}
+
+static void
+editor_undo_redo_manager_get_property (GObject *object,
+                                      guint property_id,
+                                      GValue *value,
+                                      GParamSpec *pspec)
+{
+       switch (property_id) {
+               case PROP_CAN_REDO:
+                       g_value_set_boolean (
+                               value, e_editor_undo_redo_manager_can_redo (
+                               E_EDITOR_UNDO_REDO_MANAGER (object)));
+                       return;
+
+               case PROP_CAN_UNDO:
+                       g_value_set_boolean (
+                               value, e_editor_undo_redo_manager_can_undo (
+                               E_EDITOR_UNDO_REDO_MANAGER (object)));
+                       return;
+       }
+
+       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+}
+
+static void
+editor_undo_redo_manager_set_property (GObject *object,
+                                            guint property_id,
+                                            const GValue *value,
+                                            GParamSpec *pspec)
+{
+       switch (property_id) {
+               case PROP_EDITOR_PAGE:
+                       editor_undo_redo_manager_set_editor_page (
+                               E_EDITOR_UNDO_REDO_MANAGER (object),
+                               g_value_get_object (value));
+                       return;
+       }
+
+       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+}
+
+static void
+e_editor_undo_redo_manager_class_init (EEditorUndoRedoManagerClass *class)
+{
+       GObjectClass *object_class;
+
+       g_type_class_add_private (class, sizeof (EEditorUndoRedoManagerPrivate));
+
+       object_class = G_OBJECT_CLASS (class);
+       object_class->dispose = editor_undo_redo_manager_dispose;
+       object_class->get_property = editor_undo_redo_manager_get_property;
+       object_class->set_property = editor_undo_redo_manager_set_property;
+
+       /**
+        * EEditorUndoRedoManager:can-redo
+        *
+        * Determines whether it's possible to redo previous action. The action
+        * is usually disabled when there is no action to redo.
+        */
+       g_object_class_install_property (
+               object_class,
+               PROP_CAN_REDO,
+               g_param_spec_boolean (
+                       "can-redo",
+                       "Can Redo",
+                       NULL,
+                       FALSE,
+                       G_PARAM_READABLE |
+                       G_PARAM_STATIC_STRINGS));
+
+       /**
+        * EEditorUndoRedoManager:can-undo
+        *
+        * Determines whether it's possible to undo last action. The action
+        * is usually disabled when there is no previous action to undo.
+        */
+       g_object_class_install_property (
+               object_class,
+               PROP_CAN_UNDO,
+               g_param_spec_boolean (
+                       "can-undo",
+                       "Can Undo",
+                       NULL,
+                       FALSE,
+                       G_PARAM_READABLE |
+                       G_PARAM_STATIC_STRINGS));
+
+       g_object_class_install_property (
+               object_class,
+               PROP_EDITOR_PAGE,
+               g_param_spec_object (
+                       "editor-page",
+                       NULL,
+                       NULL,
+                       E_TYPE_EDITOR_PAGE,
+                       G_PARAM_READWRITE |
+                       G_PARAM_CONSTRUCT_ONLY |
+                       G_PARAM_STATIC_STRINGS));
+}
+
+static void
+e_editor_undo_redo_manager_init (EEditorUndoRedoManager *manager)
+{
+       manager->priv = E_EDITOR_UNDO_REDO_MANAGER_GET_PRIVATE (manager);
+
+       manager->priv->operation_in_progress = FALSE;
+       manager->priv->history = NULL;
+       manager->priv->history_size = 0;
+}
diff --git a/modules/webkit-editor/web-extension/e-editor-undo-redo-manager.h 
b/modules/webkit-editor/web-extension/e-editor-undo-redo-manager.h
new file mode 100644
index 0000000..60dabaf
--- /dev/null
+++ b/modules/webkit-editor/web-extension/e-editor-undo-redo-manager.h
@@ -0,0 +1,175 @@
+/*
+ * e-editor-undo-redo-manager.h
+ *
+ * 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; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+#ifndef E_EDITOR_UNDO_REDO_MANAGER_H
+#define E_EDITOR_UNDO_REDO_MANAGER_H
+
+#include <glib-object.h>
+#include <webkitdom/webkitdom.h>
+
+#define E_TYPE_EDITOR_UNDO_REDO_MANAGER \
+       (e_editor_undo_redo_manager_get_type ())
+#define E_EDITOR_UNDO_REDO_MANAGER(obj) \
+       (G_TYPE_CHECK_INSTANCE_CAST \
+       ((obj), E_TYPE_EDITOR_UNDO_REDO_MANAGER, EEditorUndoRedoManager))
+#define E_EDITOR_UNDO_REDO_MANAGER_CLASS(cls) \
+       (G_TYPE_CHECK_CLASS_CAST \
+       ((cls), E_TYPE_EDITOR_UNDO_REDO_MANAGER, EEditorUndoRedoManagerClass))
+#define E_IS_EDITOR_UNDO_REDO_MANAGER(obj) \
+       (G_TYPE_CHECK_INSTANCE_TYPE \
+       ((obj), E_TYPE_EDITOR_UNDO_REDO_MANAGER))
+#define E_IS_EDITOR_UNDO_REDO_MANAGER_CLASS(cls) \
+       (G_TYPE_CHECK_CLASS_TYPE \
+       ((cls), E_TYPE_EDITOR_UNDO_REDO_MANAGER))
+#define E_EDITOR_UNDO_REDO_MANAGER_GET_CLASS(obj) \
+       (G_TYPE_INSTANCE_GET_CLASS \
+       ((obj), E_TYPE_EDITOR_UNDO_REDO_MANAGER, EEditorUndoRedoManagerClass))
+
+G_BEGIN_DECLS
+
+struct _EEditorPage;
+
+enum EEditorHistoryEventType {
+       HISTORY_ALIGNMENT,
+       HISTORY_AND,
+       HISTORY_BLOCK_FORMAT,
+       HISTORY_BOLD,
+       HISTORY_CELL_DIALOG,
+       HISTORY_DELETE, /* BackSpace, Delete, with and without selection */
+       HISTORY_FONT_COLOR,
+       HISTORY_FONT_SIZE,
+       HISTORY_HRULE_DIALOG,
+       HISTORY_INDENT,
+       HISTORY_INPUT,
+       HISTORY_IMAGE,
+       HISTORY_IMAGE_DIALOG,
+       HISTORY_INSERT_HTML,
+       HISTORY_ITALIC,
+       HISTORY_LINK_DIALOG,
+       HISTORY_MONOSPACE,
+       HISTORY_PAGE_DIALOG,
+       HISTORY_PASTE,
+       HISTORY_PASTE_AS_TEXT,
+       HISTORY_PASTE_QUOTED,
+       HISTORY_REMOVE_LINK,
+       HISTORY_REPLACE,
+       HISTORY_REPLACE_ALL,
+       HISTORY_CITATION_SPLIT,
+       HISTORY_SMILEY,
+       HISTORY_START, /* Start of history */
+       HISTORY_STRIKETHROUGH,
+       HISTORY_TABLE_DIALOG,
+       HISTORY_TABLE_INPUT,
+       HISTORY_UNDERLINE,
+       HISTORY_WRAP,
+       HISTORY_UNQUOTE
+};
+
+typedef struct {
+       gint from; /* From what format we are changing. */
+       gint to; /* To what format we are changing. */
+} EEditorStyleChange;
+
+/* This is used for e-html-editor-*-dialogs */
+typedef struct {
+       WebKitDOMNode *from; /* From what node we are changing. */
+       WebKitDOMNode *to; /* To what node we are changing. */
+} EEditorDOMChange;
+
+typedef struct {
+       gchar *from; /* From what format we are changing. */
+       gchar *to; /* To what format we are changing. */
+} EEditorStringChange;
+
+typedef struct {
+       guint x;
+       guint y;
+} EEditorSelectionPoint;
+
+typedef struct {
+       EEditorSelectionPoint start;
+       EEditorSelectionPoint end;
+} EEditorSelection;
+
+typedef struct {
+       enum EEditorHistoryEventType type;
+       EEditorSelection before;
+       EEditorSelection after;
+       union {
+               WebKitDOMDocumentFragment *fragment;
+               EEditorStyleChange style;
+               EEditorStringChange string;
+               EEditorDOMChange dom;
+       } data;
+} EEditorHistoryEvent;
+
+typedef struct _EEditorUndoRedoManager EEditorUndoRedoManager;
+typedef struct _EEditorUndoRedoManagerClass EEditorUndoRedoManagerClass;
+typedef struct _EEditorUndoRedoManagerPrivate EEditorUndoRedoManagerPrivate;
+
+struct _EEditorUndoRedoManager {
+       GObject parent;
+       EEditorUndoRedoManagerPrivate *priv;
+};
+
+struct _EEditorUndoRedoManagerClass
+{
+       GObjectClass parent_class;
+};
+
+GType          e_editor_undo_redo_manager_get_type
+                                               (void) G_GNUC_CONST;
+
+EEditorUndoRedoManager *
+               e_editor_undo_redo_manager_new  (struct _EEditorPage *editor_page);
+gboolean       e_editor_undo_redo_manager_is_operation_in_progress
+                                               (EEditorUndoRedoManager *manager);
+
+void           e_editor_undo_redo_manager_set_operation_in_progress
+                                               (EEditorUndoRedoManager *manager,
+                                                gboolean value);
+
+void           e_editor_undo_redo_manager_insert_history_event
+                                               (EEditorUndoRedoManager *manager,
+                                                EEditorHistoryEvent *event);
+
+EEditorHistoryEvent *
+               e_editor_undo_redo_manager_get_current_history_event
+                                               (EEditorUndoRedoManager *manager);
+void           e_editor_undo_redo_manager_remove_current_history_event
+                                               (EEditorUndoRedoManager *manager);
+
+void           e_editor_undo_redo_manager_insert_dash_history_event
+                                               (EEditorUndoRedoManager *manager);
+
+gboolean       e_editor_undo_redo_manager_can_undo
+                                               (EEditorUndoRedoManager *manager);
+
+void           e_editor_undo_redo_manager_undo (EEditorUndoRedoManager *manager);
+
+gboolean       e_editor_undo_redo_manager_can_redo
+                                               (EEditorUndoRedoManager *manager);
+
+void           e_editor_undo_redo_manager_redo (EEditorUndoRedoManager *manager);
+
+void           e_editor_undo_redo_manager_clean_history
+                                               (EEditorUndoRedoManager *manager);
+
+G_END_DECLS
+
+#endif /* E_EDITOR_UNDO_REDO_MANAGER_H */
diff --git a/modules/webkit-editor/web-extension/e-editor-web-extension-main.c 
b/modules/webkit-editor/web-extension/e-editor-web-extension-main.c
new file mode 100644
index 0000000..244bc4c
--- /dev/null
+++ b/modules/webkit-editor/web-extension/e-editor-web-extension-main.c
@@ -0,0 +1,57 @@
+/*
+ * e-html-editor-web-extension-main.c
+ *
+ * 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; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <camel/camel.h>
+
+#include "e-editor-web-extension.h"
+
+static void
+bus_acquired_cb (GDBusConnection *connection,
+                 const gchar *name,
+                 EEditorWebExtension *extension)
+{
+       e_editor_web_extension_dbus_register (extension, connection);
+}
+
+/* Forward declaration */
+G_MODULE_EXPORT void webkit_web_extension_initialize (WebKitWebExtension *wk_extension);
+
+G_MODULE_EXPORT void
+webkit_web_extension_initialize (WebKitWebExtension *wk_extension)
+{
+       EEditorWebExtension *extension;
+
+       camel_debug_init ();
+
+       extension = e_editor_web_extension_get_default ();
+       e_editor_web_extension_initialize (extension, wk_extension);
+
+       g_bus_own_name (
+               G_BUS_TYPE_SESSION,
+               E_WEBKIT_EDITOR_WEB_EXTENSION_SERVICE_NAME,
+               G_BUS_NAME_OWNER_FLAGS_NONE,
+               (GBusAcquiredCallback) bus_acquired_cb,
+               NULL, /* GBusNameAcquiredCallback */
+               NULL, /* GBusNameLostCallback */
+               g_object_ref (extension),
+               (GDestroyNotify) g_object_unref);
+}
diff --git a/modules/webkit-editor/web-extension/e-editor-web-extension-names.h 
b/modules/webkit-editor/web-extension/e-editor-web-extension-names.h
new file mode 100644
index 0000000..d1b5f02
--- /dev/null
+++ b/modules/webkit-editor/web-extension/e-editor-web-extension-names.h
@@ -0,0 +1,26 @@
+/*
+ * e-editor-web-extension-names.h
+ *
+ * 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; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+#ifndef E_EDITOR_WEB_EXTENSION_NAMES_H
+#define E_EDITOR_WEB_EXTENSION_NAMES_H
+
+#define E_WEBKIT_EDITOR_WEB_EXTENSION_SERVICE_NAME "org.gnome.Evolution.WebExtension.EWebKitEditor"
+#define E_WEBKIT_EDITOR_WEB_EXTENSION_OBJECT_PATH  "/org/gnome/Evolution/WebExtension/EWebKitEditor"
+#define E_WEBKIT_EDITOR_WEB_EXTENSION_INTERFACE    "org.gnome.Evolution.WebExtension.EWebKitEditor"
+
+#endif /* E_EDITOR_WEB_EXTENSION_NAMES_H */
diff --git a/modules/webkit-editor/web-extension/e-editor-web-extension.c 
b/modules/webkit-editor/web-extension/e-editor-web-extension.c
new file mode 100644
index 0000000..f387e26
--- /dev/null
+++ b/modules/webkit-editor/web-extension/e-editor-web-extension.c
@@ -0,0 +1,2501 @@
+/*
+ * e-editor-web-extension.c
+ *
+ * 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; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <string.h>
+
+#include <glib/gstdio.h>
+#include <gio/gio.h>
+#include <gtk/gtk.h>
+#include <webkit2/webkit-web-extension.h>
+#include <camel/camel.h>
+
+#include "web-extensions/e-dom-utils.h"
+
+#include "e-editor-page.h"
+#include "e-composer-dom-functions.h"
+#include "e-dialogs-dom-functions.h"
+#include "e-editor-dom-functions.h"
+#include "e-editor-undo-redo-manager.h"
+
+#include "e-editor-web-extension.h"
+
+#define E_EDITOR_WEB_EXTENSION_GET_PRIVATE(obj) \
+       (G_TYPE_INSTANCE_GET_PRIVATE \
+       ((obj), E_TYPE_EDITOR_WEB_EXTENSION, EEditorWebExtensionPrivate))
+
+struct _EEditorWebExtensionPrivate {
+       WebKitWebExtension *wk_extension;
+
+       GDBusConnection *dbus_connection;
+       guint registration_id;
+
+       GHashTable *editor_pages; /* guint64 *webpage_id ~> EEditorPage * */
+};
+
+static CamelDataCache *emd_global_http_cache = NULL;
+
+static const gchar *introspection_xml =
+"<node>"
+"  <interface name='" E_WEBKIT_EDITOR_WEB_EXTENSION_INTERFACE "'>"
+"<!-- ********************************************************* -->"
+"<!--                          SIGNALS                          -->"
+"<!-- ********************************************************* -->"
+"    <signal name='SelectionChanged'>"
+"      <arg type='t' name='page_id' direction='out'/>"
+"      <arg type='i' name='alignment' direction='out'/>"
+"      <arg type='i' name='block_format' direction='out'/>"
+"      <arg type='b' name='indented' direction='out'/>"
+"      <arg type='i' name='style_flags' direction='out'/>"
+"      <arg type='i' name='font_size' direction='out'/>"
+"      <arg type='s' name='font_color' direction='out'/>"
+"    </signal>"
+"    <signal name='ContentChanged'>"
+"      <arg type='t' name='page_id' direction='out'/>"
+"    </signal>"
+"    <signal name='UndoRedoStateChanged'>"
+"      <arg type='t' name='page_id' direction='out'/>"
+"      <arg type='b' name='can_undo' direction='out'/>"
+"      <arg type='b' name='can_redo' direction='out'/>"
+"    </signal>"
+"<!-- ********************************************************* -->"
+"<!--                          METHODS                          -->"
+"<!-- ********************************************************* -->"
+"<!-- ********************************************************* -->"
+"<!--                       FOR TESTING ONLY                    -->"
+"<!-- ********************************************************* -->"
+"    <method name='TestHTMLEqual'>"
+"      <arg type='t' name='page_id' direction='in'/>"
+"      <arg type='s' name='html1' direction='in'/>"
+"      <arg type='s' name='html2' direction='in'/>"
+"      <arg type='b' name='equal' direction='out'/>"
+"    </method>"
+"<!-- ********************************************************* -->"
+"<!--                          GENERIC                          -->"
+"<!-- ********************************************************* -->"
+"    <method name='ElementHasAttribute'>"
+"      <arg type='t' name='page_id' direction='in'/>"
+"      <arg type='s' name='element_id' direction='in'/>"
+"      <arg type='s' name='attribute' direction='in'/>"
+"      <arg type='b' name='has_attribute' direction='out'/>"
+"    </method>"
+"    <method name='ElementGetAttribute'>"
+"      <arg type='t' name='page_id' direction='in'/>"
+"      <arg type='s' name='element_id' direction='in'/>"
+"      <arg type='s' name='attribute' direction='in'/>"
+"      <arg type='s' name='value' direction='out'/>"
+"    </method>"
+"    <method name='ElementGetAttributeBySelector'>"
+"      <arg type='t' name='page_id' direction='in'/>"
+"      <arg type='s' name='selector' direction='in'/>"
+"      <arg type='s' name='attribute' direction='in'/>"
+"      <arg type='s' name='value' direction='out'/>"
+"    </method>"
+"    <method name='ElementRemoveAttribute'>"
+"      <arg type='t' name='page_id' direction='in'/>"
+"      <arg type='s' name='element_id' direction='in'/>"
+"      <arg type='s' name='attribute' direction='in'/>"
+"    </method>"
+"    <method name='ElementRemoveAttributeBySelector'>"
+"      <arg type='t' name='page_id' direction='in'/>"
+"      <arg type='s' name='selector' direction='in'/>"
+"      <arg type='s' name='attribute' direction='in'/>"
+"    </method>"
+"    <method name='ElementSetAttribute'>"
+"      <arg type='t' name='page_id' direction='in'/>"
+"      <arg type='s' name='element_id' direction='in'/>"
+"      <arg type='s' name='attribute' direction='in'/>"
+"      <arg type='s' name='value' direction='in'/>"
+"    </method>"
+"    <method name='ElementSetAttributeBySelector'>"
+"      <arg type='t' name='page_id' direction='in'/>"
+"      <arg type='s' name='selector' direction='in'/>"
+"      <arg type='s' name='attribute' direction='in'/>"
+"      <arg type='s' name='value' direction='in'/>"
+"    </method>"
+"    <method name='ElementGetTagName'>"
+"      <arg type='t' name='page_id' direction='in'/>"
+"      <arg type='s' name='element_id' direction='in'/>"
+"      <arg type='s' name='tag_name' direction='out'/>"
+"    </method>"
+"<!-- ********************************************************* -->"
+"<!--     Functions that are specific to composer               -->"
+"<!-- ********************************************************* -->"
+"    <method name='RemoveImageAttributesFromElementBySelector'>"
+"      <arg type='t' name='page_id' direction='in'/>"
+"      <arg type='s' name='selector' direction='in'/>"
+"    </method>"
+"<!-- ********************************************************* -->"
+"<!--     Functions that are used in EEditorCellDialog      -->"
+"<!-- ********************************************************* -->"
+"    <method name='EEditorCellDialogMarkCurrentCellElement'>"
+"      <arg type='t' name='page_id' direction='in'/>"
+"      <arg type='s' name='element_id' direction='in'/>"
+"    </method>"
+"    <method name='EEditorCellDialogSaveHistoryOnExit'>"
+"      <arg type='t' name='page_id' direction='in'/>"
+"    </method>"
+"    <method name='EEditorCellDialogSetElementVAlign'>"
+"      <arg type='t' name='page_id' direction='in'/>"
+"      <arg type='s' name='value' direction='in'/>"
+"      <arg type='i' name='scope' direction='in'/>"
+"    </method>"
+"    <method name='EEditorCellDialogSetElementAlign'>"
+"      <arg type='t' name='page_id' direction='in'/>"
+"      <arg type='s' name='value' direction='in'/>"
+"      <arg type='i' name='scope' direction='in'/>"
+"    </method>"
+"    <method name='EEditorCellDialogSetElementNoWrap'>"
+"      <arg type='t' name='page_id' direction='in'/>"
+"      <arg type='b' name='value' direction='in'/>"
+"      <arg type='i' name='scope' direction='in'/>"
+"    </method>"
+"    <method name='EEditorCellDialogSetElementHeaderStyle'>"
+"      <arg type='t' name='page_id' direction='in'/>"
+"      <arg type='b' name='value' direction='in'/>"
+"      <arg type='i' name='scope' direction='in'/>"
+"    </method>"
+"    <method name='EEditorCellDialogSetElementWidth'>"
+"      <arg type='t' name='page_id' direction='in'/>"
+"      <arg type='s' name='value' direction='in'/>"
+"      <arg type='i' name='scope' direction='in'/>"
+"    </method>"
+"    <method name='EEditorCellDialogSetElementColSpan'>"
+"      <arg type='t' name='page_id' direction='in'/>"
+"      <arg type='i' name='value' direction='in'/>"
+"      <arg type='i' name='scope' direction='in'/>"
+"    </method>"
+"    <method name='EEditorCellDialogSetElementRowSpan'>"
+"      <arg type='t' name='page_id' direction='in'/>"
+"      <arg type='i' name='value' direction='in'/>"
+"      <arg type='i' name='scope' direction='in'/>"
+"    </method>"
+"    <method name='EEditorCellDialogSetElementBgColor'>"
+"      <arg type='t' name='page_id' direction='in'/>"
+"      <arg type='s' name='value' direction='in'/>"
+"      <arg type='i' name='scope' direction='in'/>"
+"    </method>"
+"<!-- ********************************************************* -->"
+"<!--     Functions that are used in EEditorHRuleDialog      -->"
+"<!-- ********************************************************* -->"
+"    <method name='EEditorHRuleDialogFindHRule'>"
+"      <arg type='t' name='page_id' direction='in'/>"
+"      <arg type='b' name='created_new_hr' direction='out'/>"
+"    </method>"
+"    <method name='EEditorHRuleDialogOnClose'>"
+"      <arg type='t' name='page_id' direction='in'/>"
+"    </method>"
+"<!-- ********************************************************* -->"
+"<!--     Functions that are used in EEditorImageDialog     -->"
+"<!-- ********************************************************* -->"
+"    <method name='EEditorImageDialogMarkImage'>"
+"      <arg type='t' name='page_id' direction='in'/>"
+"    </method>"
+"    <method name='EEditorImageDialogSaveHistoryOnExit'>"
+"      <arg type='t' name='page_id' direction='in'/>"
+"    </method>"
+"    <method name='EEditorImageDialogSetElementUrl'>"
+"      <arg type='t' name='page_id' direction='in'/>"
+"      <arg type='s' name='value' direction='in'/>"
+"    </method>"
+"    <method name='EEditorImageDialogGetElementUrl'>"
+"      <arg type='t' name='page_id' direction='in'/>"
+"      <arg type='s' name='value' direction='out'/>"
+"    </method>"
+"    <method name='ImageElementSetWidth'>"
+"      <arg type='t' name='page_id' direction='in'/>"
+"      <arg type='s' name='element_id' direction='in'/>"
+"      <arg type='i' name='value' direction='in'/>"
+"    </method>"
+"    <method name='ImageElementGetWidth'>"
+"      <arg type='t' name='page_id' direction='in'/>"
+"      <arg type='s' name='element_id' direction='in'/>"
+"      <arg type='i' name='value' direction='out'/>"
+"    </method>"
+"    <method name='ImageElementSetHeight'>"
+"      <arg type='t' name='page_id' direction='in'/>"
+"      <arg type='s' name='element_id' direction='in'/>"
+"      <arg type='i' name='value' direction='in'/>"
+"    </method>"
+"    <method name='ImageElementGetHeight'>"
+"      <arg type='t' name='page_id' direction='in'/>"
+"      <arg type='s' name='element_id' direction='in'/>"
+"      <arg type='i' name='value' direction='out'/>"
+"    </method>"
+"    <method name='ImageElementGetNaturalWidth'>"
+"      <arg type='t' name='page_id' direction='in'/>"
+"      <arg type='s' name='element_id' direction='in'/>"
+"      <arg type='i' name='value' direction='out'/>"
+"    </method>"
+"    <method name='ImageElementGetNaturalHeight'>"
+"      <arg type='t' name='page_id' direction='in'/>"
+"      <arg type='s' name='element_id' direction='in'/>"
+"      <arg type='i' name='value' direction='out'/>"
+"    </method>"
+"    <method name='ImageElementSetHSpace'>"
+"      <arg type='t' name='page_id' direction='in'/>"
+"      <arg type='s' name='element_id' direction='in'/>"
+"      <arg type='i' name='value' direction='in'/>"
+"    </method>"
+"    <method name='ImageElementGetHSpace'>"
+"      <arg type='t' name='page_id' direction='in'/>"
+"      <arg type='s' name='element_id' direction='in'/>"
+"      <arg type='i' name='value' direction='out'/>"
+"    </method>"
+"    <method name='ImageElementSetVSpace'>"
+"      <arg type='t' name='page_id' direction='in'/>"
+"      <arg type='s' name='element_id' direction='in'/>"
+"      <arg type='i' name='value' direction='in'/>"
+"    </method>"
+"    <method name='ImageElementGetVSpace'>"
+"      <arg type='t' name='page_id' direction='in'/>"
+"      <arg type='s' name='element_id' direction='in'/>"
+"      <arg type='i' name='value' direction='out'/>"
+"    </method>"
+"<!-- ********************************************************* -->"
+"<!--     Functions that are used in EEditorLinkDialog      -->"
+"<!-- ********************************************************* -->"
+"    <method name='EEditorLinkDialogOk'>"
+"      <arg type='t' name='page_id' direction='in'/>"
+"      <arg type='s' name='url' direction='in'/>"
+"      <arg type='s' name='inner_text' direction='in'/>"
+"    </method>"
+"    <method name='EEditorLinkDialogShow'>"
+"      <arg type='t' name='page_id' direction='in'/>"
+"      <arg type='s' name='url' direction='out'/>"
+"      <arg type='s' name='inner_text' direction='out'/>"
+"    </method>"
+"    <method name='EEditorLinkDialogOnOpen'>"
+"      <arg type='t' name='page_id' direction='in'/>"
+"    </method>"
+"    <method name='EEditorLinkDialogOnClose'>"
+"      <arg type='t' name='page_id' direction='in'/>"
+"    </method>"
+"    <method name='EEditorLinkDialogUnlink'>"
+"      <arg type='t' name='page_id' direction='in'/>"
+"    </method>"
+"<!-- ********************************************************* -->"
+"<!--     Functions that are used in EEditorPageDialog     -->"
+"<!-- ********************************************************* -->"
+"    <method name='EEditorPageDialogSaveHistory'>"
+"      <arg type='t' name='page_id' direction='in'/>"
+"    </method>"
+"    <method name='EEditorPageDialogSaveHistoryOnExit'>"
+"      <arg type='t' name='page_id' direction='in'/>"
+"    </method>"
+"<!-- ********************************************************* -->"
+"<!--   Functions that are used in EEditorSpellCheckDialog  -->"
+"<!-- ********************************************************* -->"
+"    <method name='EEditorSpellCheckDialogNext'>"
+"      <arg type='t' name='page_id' direction='in'/>"
+"      <arg type='s' name='word' direction='in'/>"
+"      <arg type='as' name='languages' direction='in'/>"
+"      <arg type='s' name='next_word' direction='out'/>"
+"    </method>"
+"    <method name='EEditorSpellCheckDialogPrev'>"
+"      <arg type='t' name='page_id' direction='in'/>"
+"      <arg type='s' name='word' direction='in'/>"
+"      <arg type='as' name='languages' direction='in'/>"
+"      <arg type='s' name='prev_word' direction='out'/>"
+"    </method>"
+"<!-- ********************************************************* -->"
+"<!--     Functions that are used in EEditorTableDialog     -->"
+"<!-- ********************************************************* -->"
+"    <method name='EEditorTableDialogSetRowCount'>"
+"      <arg type='t' name='page_id' direction='in'/>"
+"      <arg type='u' name='value' direction='in'/>"
+"    </method>"
+"    <method name='EEditorTableDialogGetRowCount'>"
+"      <arg type='t' name='page_id' direction='in'/>"
+"      <arg type='u' name='value' direction='out'/>"
+"    </method>"
+"    <method name='EEditorTableDialogSetColumnCount'>"
+"      <arg type='t' name='page_id' direction='in'/>"
+"      <arg type='u' name='value' direction='in'/>"
+"    </method>"
+"    <method name='EEditorTableDialogGetColumnCount'>"
+"      <arg type='t' name='page_id' direction='in'/>"
+"      <arg type='u' name='value' direction='out'/>"
+"    </method>"
+"    <method name='EEditorTableDialogShow'>"
+"      <arg type='t' name='page_id' direction='in'/>"
+"      <arg type='b' name='created_new_table' direction='out'/>"
+"    </method>"
+"    <method name='EEditorTableDialogSaveHistoryOnExit'>"
+"      <arg type='t' name='page_id' direction='in'/>"
+"    </method>"
+"<!-- ********************************************************* -->"
+"<!--     Functions that are used in EEditorActions         -->"
+"<!-- ********************************************************* -->"
+"    <method name='TableCellElementGetNoWrap'>"
+"      <arg type='t' name='page_id' direction='in'/>"
+"      <arg type='s' name='element_id' direction='in'/>"
+"      <arg type='b' name='no_wrap' direction='out'/>"
+"    </method>"
+"    <method name='TableCellElementGetRowSpan'>"
+"      <arg type='t' name='page_id' direction='in'/>"
+"      <arg type='s' name='element_id' direction='in'/>"
+"      <arg type='i' name='row_span' direction='out'/>"
+"    </method>"
+"    <method name='TableCellElementGetColSpan'>"
+"      <arg type='t' name='page_id' direction='in'/>"
+"      <arg type='s' name='element_id' direction='in'/>"
+"      <arg type='i' name='col_span' direction='out'/>"
+"    </method>"
+"    <method name='EEditorDialogDeleteCellContents'>"
+"      <arg type='t' name='page_id' direction='in'/>"
+"    </method>"
+"    <method name='EEditorDialogDeleteColumn'>"
+"      <arg type='t' name='page_id' direction='in'/>"
+"    </method>"
+"    <method name='EEditorDialogDeleteRow'>"
+"      <arg type='t' name='page_id' direction='in'/>"
+"    </method>"
+"    <method name='EEditorDialogDeleteTable'>"
+"      <arg type='t' name='page_id' direction='in'/>"
+"    </method>"
+"    <method name='EEditorDialogInsertColumnAfter'>"
+"      <arg type='t' name='page_id' direction='in'/>"
+"    </method>"
+"    <method name='EEditorDialogInsertColumnBefore'>"
+"      <arg type='t' name='page_id' direction='in'/>"
+"    </method>"
+"    <method name='EEditorDialogInsertRowAbove'>"
+"      <arg type='t' name='page_id' direction='in'/>"
+"    </method>"
+"    <method name='EEditorDialogInsertRowBelow'>"
+"      <arg type='t' name='page_id' direction='in'/>"
+"    </method>"
+"    <method name='EEditorActionsSaveHistoryForCut'>"
+"      <arg type='t' name='page_id' direction='in'/>"
+"    </method>"
+"<!-- ********************************************************* -->"
+"<!--     Functions that are used in EEditorView            -->"
+"<!-- ********************************************************* -->"
+"    <method name='SetPastingContentFromItself'>"
+"      <arg type='t' name='page_id' direction='in'/>"
+"      <arg type='b' name='value' direction='in'/>"
+"    </method>"
+"    <method name='SetEditorHTMLMode'>"
+"      <arg type='t' name='page_id' direction='in'/>"
+"      <arg type='b' name='html_mode' direction='in'/>"
+"      <arg type='b' name='convert' direction='in'/>"
+"    </method>"
+"    <method name='SetConvertInSitu'>"
+"      <arg type='t' name='page_id' direction='in'/>"
+"      <arg type='b' name='value' direction='in'/>"
+"    </method>"
+"    <method name='DOMForceSpellCheck'>"
+"      <arg type='t' name='page_id' direction='in'/>"
+"    </method>"
+"    <method name='DOMTurnSpellCheckOff'>"
+"      <arg type='t' name='page_id' direction='in'/>"
+"    </method>"
+"    <method name='DOMScrollToCaret'>"
+"      <arg type='t' name='page_id' direction='in'/>"
+"    </method>"
+"    <method name='DOMEmbedStyleSheet'>"
+"      <arg type='t' name='page_id' direction='in'/>"
+"      <arg type='s' name='style_sheet_content' direction='in'/>"
+"    </method>"
+"    <method name='DOMRemoveEmbeddedStyleSheet'>"
+"      <arg type='t' name='page_id' direction='in'/>"
+"    </method>"
+"    <method name='DOMSaveSelection'>"
+"      <arg type='t' name='page_id' direction='in'/>"
+"    </method>"
+"    <method name='DOMRestoreSelection'>"
+"      <arg type='t' name='page_id' direction='in'/>"
+"    </method>"
+"    <method name='DOMUndo'>"
+"      <arg type='t' name='page_id' direction='in'/>"
+"    </method>"
+"    <method name='DOMRedo'>"
+"      <arg type='t' name='page_id' direction='in'/>"
+"    </method>"
+"    <method name='DOMQuoteAndInsertTextIntoSelection'>"
+"      <arg type='t' name='page_id' direction='in'/>"
+"      <arg type='s' name='text' direction='in'/>"
+"      <arg type='b' name='is_html' direction='in'/>"
+"    </method>"
+"    <method name='DOMConvertAndInsertHTMLIntoSelection'>"
+"      <arg type='t' name='page_id' direction='in'/>"
+"      <arg type='s' name='text' direction='in'/>"
+"      <arg type='b' name='is_html' direction='in'/>"
+"    </method>"
+"    <method name='DOMCheckIfConversionNeeded'>"
+"      <arg type='t' name='page_id' direction='in'/>"
+"      <arg type='b' name='conversion_needed' direction='out'/>"
+"    </method>"
+"    <method name='DOMGetContent'>"
+"      <arg type='t' name='page_id' direction='in'/>"
+"      <arg type='s' name='from_domain' direction='in'/>"
+"      <arg type='i' name='flags' direction='in'/>"
+"      <arg type='s' name='content' direction='out'/>"
+"      <arg type='v' name='inline_images' direction='out'/>"
+"    </method>"
+"    <method name='DOMInsertHTML'>"
+"      <arg type='t' name='page_id' direction='in'/>"
+"      <arg type='s' name='html' direction='in'/>"
+"    </method>"
+"    <method name='DOMConvertContent'>"
+"      <arg type='t' name='page_id' direction='in'/>"
+"      <arg type='s' name='preffered_text' direction='in'/>"
+"    </method>"
+"    <method name='DOMAddNewInlineImageIntoList'>"
+"      <arg type='t' name='page_id' direction='in'/>"
+"      <arg type='s' name='filename' direction='in'/>"
+"      <arg type='s' name='cid_src' direction='in'/>"
+"      <arg type='s' name='src' direction='in'/>"
+"    </method>"
+"    <method name='DOMReplaceImageSrc'>"
+"      <arg type='t' name='page_id' direction='in'/>"
+"      <arg type='s' name='selector' direction='in'/>"
+"      <arg type='s' name='uri' direction='in'/>"
+"    </method>"
+"    <method name='DOMDragAndDropEnd'>"
+"      <arg type='t' name='page_id' direction='in'/>"
+"    </method>"
+"    <method name='DOMMoveSelectionOnPoint'>"
+"      <arg type='t' name='page_id' direction='in'/>"
+"      <arg type='i' name='x' direction='in'/>"
+"      <arg type='i' name='y' direction='in'/>"
+"      <arg type='b' name='cancel_if_not_collapsed' direction='in'/>"
+"    </method>"
+"    <method name='DOMInsertSmiley'>"
+"      <arg type='t' name='page_id' direction='in'/>"
+"      <arg type='s' name='smiley_name' direction='in'/>"
+"    </method>"
+"<!-- ********************************************************* -->"
+"<!--     Functions that are used in EEditorSelection       -->"
+"<!-- ********************************************************* -->"
+"    <method name='DOMSelectionIndent'>"
+"      <arg type='t' name='page_id' direction='in'/>"
+"    </method>"
+"    <method name='DOMSelectionInsertImage'>"
+"      <arg type='t' name='page_id' direction='in'/>"
+"      <arg type='s' name='uri' direction='in'/>"
+"    </method>"
+"    <method name='DOMSelectionReplace'>"
+"      <arg type='t' name='page_id' direction='in'/>"
+"      <arg type='s' name='replacement' direction='in'/>"
+"    </method>"
+"    <method name='DOMSelectionSetAlignment'>"
+"      <arg type='t' name='page_id' direction='in'/>"
+"      <arg type='i' name='alignment' direction='in'/>"
+"    </method>"
+"    <method name='DOMSelectionSetBold'>"
+"      <arg type='t' name='page_id' direction='in'/>"
+"      <arg type='b' name='bold' direction='in'/>"
+"    </method>"
+"    <method name='DOMSelectionSetBlockFormat'>"
+"      <arg type='t' name='page_id' direction='in'/>"
+"      <arg type='i' name='block_format' direction='in'/>"
+"    </method>"
+"    <method name='DOMSelectionSetFontColor'>"
+"      <arg type='t' name='page_id' direction='in'/>"
+"      <arg type='s' name='color' direction='in'/>"
+"    </method>"
+"    <method name='DOMSelectionSetFontSize'>"
+"      <arg type='t' name='page_id' direction='in'/>"
+"      <arg type='i' name='font_size' direction='in'/>"
+"    </method>"
+"    <method name='DOMSelectionSetItalic'>"
+"      <arg type='t' name='page_id' direction='in'/>"
+"      <arg type='b' name='italic' direction='in'/>"
+"    </method>"
+"    <method name='DOMSelectionSetMonospaced'>"
+"      <arg type='t' name='page_id' direction='in'/>"
+"      <arg type='b' name='monospaced' direction='in'/>"
+"    </method>"
+"    <method name='DOMSelectionSetStrikethrough'>"
+"      <arg type='t' name='page_id' direction='in'/>"
+"      <arg type='b' name='strikethrough' direction='in'/>"
+"    </method>"
+"    <method name='DOMSelectionSetSubscript'>"
+"      <arg type='t' name='page_id' direction='in'/>"
+"      <arg type='b' name='subscript' direction='in'/>"
+"    </method>"
+"    <method name='DOMSelectionSetSuperscript'>"
+"      <arg type='t' name='page_id' direction='in'/>"
+"      <arg type='b' name='superscript' direction='in'/>"
+"    </method>"
+"    <method name='DOMSelectionSetUnderline'>"
+"      <arg type='t' name='page_id' direction='in'/>"
+"      <arg type='b' name='underline' direction='in'/>"
+"    </method>"
+"    <method name='DOMSelectionUnindent'>"
+"      <arg type='t' name='page_id' direction='in'/>"
+"    </method>"
+"    <method name='DOMSelectionWrap'>"
+"      <arg type='t' name='page_id' direction='in'/>"
+"    </method>"
+"    <method name='DOMGetCaretWord'>"
+"      <arg type='t' name='page_id' direction='in'/>"
+"      <arg type='s' name='word' direction='out'/>"
+"    </method>"
+"<!-- ********************************************************* -->"
+"<!--     Functions that are used in EComposerPrivate           -->"
+"<!-- ********************************************************* -->"
+"    <method name='DOMInsertSignature'>"
+"      <arg type='t' name='page_id' direction='in'/>"
+"      <arg type='s' name='content' direction='in'/>"
+"      <arg type='b' name='is_html' direction='in'/>"
+"      <arg type='s' name='signature_id' direction='in'/>"
+"      <arg type='b' name='set_signature_from_message' direction='in'/>"
+"      <arg type='b' name='check_if_signature_is_changed' direction='in'/>"
+"      <arg type='b' name='ignore_next_signature_change' direction='in'/>"
+"      <arg type='s' name='new_signature_id' direction='out'/>"
+"      <arg type='b' name='out_set_signature_from_message' direction='out'/>"
+"      <arg type='b' name='out_check_if_signature_is_changed' direction='out'/>"
+"      <arg type='b' name='out_ignore_next_signature_change' direction='out'/>"
+"    </method>"
+"    <method name='DOMSaveDragAndDropHistory'>"
+"      <arg type='t' name='page_id' direction='in'/>"
+"    </method>"
+"    <method name='DOMCleanAfterDragAndDrop'>"
+"      <arg type='t' name='page_id' direction='in'/>"
+"    </method>"
+"<!-- ********************************************************* -->"
+"<!--     Functions that are used in External Editor plugin     -->"
+"<!-- ********************************************************* -->"
+"    <method name='DOMGetCaretPosition'>"
+"      <arg type='t' name='page_id' direction='in'/>"
+"      <arg type='u' name='position' direction='out'/>"
+"    </method>"
+"    <method name='DOMGetCaretOffset'>"
+"      <arg type='t' name='page_id' direction='in'/>"
+"      <arg type='u' name='offset' direction='out'/>"
+"    </method>"
+"    <method name='DOMClearUndoRedoHistory'>"
+"      <arg type='t' name='page_id' direction='in'/>"
+"    </method>"
+"  </interface>"
+"</node>";
+
+G_DEFINE_TYPE (EEditorWebExtension, e_editor_web_extension, G_TYPE_OBJECT)
+
+static EEditorPage *
+get_editor_page (EEditorWebExtension *extension,
+                guint64 page_id)
+{
+       g_return_val_if_fail (E_IS_EDITOR_WEB_EXTENSION (extension), NULL);
+
+       return g_hash_table_lookup (extension->priv->editor_pages, &page_id);
+}
+
+static EEditorPage *
+get_editor_page_or_return_dbus_error (GDBusMethodInvocation *invocation,
+                                     EEditorWebExtension *extension,
+                                     guint64 page_id)
+{
+       WebKitWebPage *web_page;
+       EEditorPage *editor_page;
+
+       g_return_val_if_fail (E_IS_EDITOR_WEB_EXTENSION (extension), NULL);
+
+       web_page = webkit_web_extension_get_page (extension->priv->wk_extension, page_id);
+       if (!web_page) {
+               g_dbus_method_invocation_return_error (
+                       invocation, G_DBUS_ERROR, G_DBUS_ERROR_INVALID_ARGS,
+                       "Invalid page ID: %" G_GUINT64_FORMAT, page_id);
+
+               return NULL;
+       }
+
+       editor_page = get_editor_page (extension, page_id);
+       if (!editor_page) {
+               g_dbus_method_invocation_return_error (
+                       invocation, G_DBUS_ERROR, G_DBUS_ERROR_INVALID_ARGS,
+                       "Invalid page ID: %" G_GUINT64_FORMAT, page_id);
+       }
+
+       return editor_page;
+}
+
+static void
+handle_method_call (GDBusConnection *connection,
+                    const char *sender,
+                    const char *object_path,
+                    const char *interface_name,
+                    const char *method_name,
+                    GVariant *parameters,
+                    GDBusMethodInvocation *invocation,
+                    gpointer user_data)
+{
+       guint64 page_id;
+        EEditorWebExtension *extension = E_EDITOR_WEB_EXTENSION (user_data);
+       WebKitDOMDocument *document;
+       EEditorPage *editor_page;
+
+       if (g_strcmp0 (interface_name, E_WEBKIT_EDITOR_WEB_EXTENSION_INTERFACE) != 0)
+               return;
+
+       if (g_strcmp0 (method_name, "TestHTMLEqual") == 0) {
+               gboolean equal = FALSE;
+               const gchar *html1 = NULL, *html2 = NULL;
+
+               g_variant_get (parameters, "(t&s&s)", &page_id, &html1, &html2);
+
+               editor_page = get_editor_page_or_return_dbus_error (invocation, extension, page_id);
+               if (!editor_page)
+                       goto error;
+
+               document = e_editor_page_get_document (editor_page);
+               equal = e_editor_dom_test_html_equal (document, html1, html2);
+
+               g_dbus_method_invocation_return_value (invocation, g_variant_new ("(b)", equal));
+       } else if (g_strcmp0 (method_name, "ElementHasAttribute") == 0) {
+               gboolean value = FALSE;
+               const gchar *element_id, *attribute;
+               WebKitDOMElement *element;
+
+               g_variant_get (
+                       parameters, "(t&s&s)", &page_id, &element_id, &attribute);
+
+               editor_page = get_editor_page_or_return_dbus_error (invocation, extension, page_id);
+               if (!editor_page)
+                       goto error;
+
+               document = e_editor_page_get_document (editor_page);
+               element = webkit_dom_document_get_element_by_id (document, element_id);
+               if (element)
+                       value = webkit_dom_element_has_attribute (element, attribute);
+
+               g_dbus_method_invocation_return_value (
+                       invocation, g_variant_new ("(b)", value));
+       } else if (g_strcmp0 (method_name, "ElementGetAttribute") == 0) {
+               const gchar *element_id, *attribute;
+               gchar *value = NULL;
+               WebKitDOMElement *element;
+
+               g_variant_get (
+                       parameters, "(t&s&s)", &page_id, &element_id, &attribute);
+
+               editor_page = get_editor_page_or_return_dbus_error (invocation, extension, page_id);
+               if (!editor_page)
+                       goto error;
+
+               document = e_editor_page_get_document (editor_page);
+               element = webkit_dom_document_get_element_by_id (document, element_id);
+               if (element)
+                       value = webkit_dom_element_get_attribute (element, attribute);
+
+               g_dbus_method_invocation_return_value (
+                       invocation,
+                       g_variant_new (
+                               "(@s)",
+                               g_variant_new_take_string (
+                                       value ? value : g_strdup (""))));
+       } else if (g_strcmp0 (method_name, "ElementGetAttributeBySelector") == 0) {
+               const gchar *attribute, *selector;
+               gchar *value = NULL;
+               WebKitDOMElement *element;
+
+               g_variant_get (
+                       parameters, "(t&s&s)", &page_id, &selector, &attribute);
+
+               editor_page = get_editor_page_or_return_dbus_error (invocation, extension, page_id);
+               if (!editor_page)
+                       goto error;
+
+               document = e_editor_page_get_document (editor_page);
+               element = webkit_dom_document_query_selector (document, selector, NULL);
+               if (element)
+                       value = webkit_dom_element_get_attribute (element, attribute);
+
+               g_dbus_method_invocation_return_value (
+                       invocation,
+                       g_variant_new (
+                               "(@s)",
+                               g_variant_new_take_string (
+                                       value ? value : g_strdup (""))));
+       } else if (g_strcmp0 (method_name, "ElementRemoveAttribute") == 0) {
+               const gchar *element_id, *attribute;
+               WebKitDOMElement *element;
+
+               g_variant_get (
+                       parameters, "(t&s&s)", &page_id, &element_id, &attribute);
+
+               editor_page = get_editor_page_or_return_dbus_error (invocation, extension, page_id);
+               if (!editor_page)
+                       goto error;
+
+               document = e_editor_page_get_document (editor_page);
+               element = webkit_dom_document_get_element_by_id (document, element_id);
+               if (element)
+                       webkit_dom_element_remove_attribute (element, attribute);
+
+               g_dbus_method_invocation_return_value (invocation, NULL);
+       } else if (g_strcmp0 (method_name, "ElementRemoveAttributeBySelector") == 0) {
+               const gchar *attribute, *selector;
+               WebKitDOMElement *element;
+
+               g_variant_get (
+                       parameters, "(t&s&s)", &page_id, &selector, &attribute);
+
+               editor_page = get_editor_page_or_return_dbus_error (invocation, extension, page_id);
+               if (!editor_page)
+                       goto error;
+
+               document = e_editor_page_get_document (editor_page);
+               element = webkit_dom_document_query_selector (document, selector, NULL);
+               if (element)
+                       webkit_dom_element_remove_attribute (element, attribute);
+
+               g_dbus_method_invocation_return_value (invocation, NULL);
+       } else if (g_strcmp0 (method_name, "ElementSetAttribute") == 0) {
+               const gchar *element_id, *attribute, *value;
+               WebKitDOMElement *element;
+
+               g_variant_get (
+                       parameters,
+                       "(t&s&s&s)",
+                       &page_id, &element_id, &attribute, &value);
+
+               editor_page = get_editor_page_or_return_dbus_error (invocation, extension, page_id);
+               if (!editor_page)
+                       goto error;
+
+               document = e_editor_page_get_document (editor_page);
+               element = webkit_dom_document_get_element_by_id (document, element_id);
+               if (element)
+                       webkit_dom_element_set_attribute (
+                               element, attribute, value, NULL);
+
+               g_dbus_method_invocation_return_value (invocation, NULL);
+       } else if (g_strcmp0 (method_name, "ElementSetAttributeBySelector") == 0) {
+               const gchar *attribute, *selector, *value;
+               WebKitDOMElement *element;
+
+               g_variant_get (
+                       parameters, "(t&s&s&s)", &page_id, &selector, &attribute, &value);
+
+               editor_page = get_editor_page_or_return_dbus_error (invocation, extension, page_id);
+               if (!editor_page)
+                       goto error;
+
+               document = e_editor_page_get_document (editor_page);
+               element = webkit_dom_document_query_selector (document, selector, NULL);
+               if (element) {
+                       if (g_strcmp0 (selector, "body") == 0 &&
+                           g_strcmp0 (attribute, "link") == 0)
+                               e_editor_dom_set_link_color (editor_page, value);
+                       else if (g_strcmp0 (selector, "body") == 0 &&
+                                g_strcmp0 (attribute, "vlink") == 0)
+                               e_editor_dom_set_visited_link_color (editor_page, value);
+                       else
+                               webkit_dom_element_set_attribute (
+                                       element, attribute, value, NULL);
+               }
+
+               g_dbus_method_invocation_return_value (invocation, NULL);
+       } else if (g_strcmp0 (method_name, "ElementGetTagName") == 0) {
+               const gchar *element_id;
+               gchar *value = NULL;
+               WebKitDOMElement *element;
+
+               g_variant_get (parameters, "(t&s)", &page_id, &element_id);
+
+               editor_page = get_editor_page_or_return_dbus_error (invocation, extension, page_id);
+               if (!editor_page)
+                       goto error;
+
+               document = e_editor_page_get_document (editor_page);
+               element = webkit_dom_document_get_element_by_id (document, element_id);
+               if (element)
+                       value = webkit_dom_element_get_tag_name (element);
+
+               g_dbus_method_invocation_return_value (
+                       invocation,
+                       g_variant_new (
+                               "(@s)",
+                               g_variant_new_take_string (
+                                       value ? value : g_strdup (""))));
+       } else if (g_strcmp0 (method_name, "RemoveImageAttributesFromElementBySelector") == 0) {
+               const gchar *selector;
+               WebKitDOMElement *element;
+
+               g_variant_get (parameters, "(t&s)", &page_id, &selector);
+
+               editor_page = get_editor_page_or_return_dbus_error (invocation, extension, page_id);
+               if (!editor_page)
+                       goto error;
+
+               document = e_editor_page_get_document (editor_page);
+               element = webkit_dom_document_query_selector (document, selector, NULL);
+               if (element) {
+                       webkit_dom_element_remove_attribute (element, "background");
+                       webkit_dom_element_remove_attribute (element, "data-uri");
+                       webkit_dom_element_remove_attribute (element, "data-inline");
+                       webkit_dom_element_remove_attribute (element, "data-name");
+               }
+
+               g_dbus_method_invocation_return_value (invocation, NULL);
+       } else if (g_strcmp0 (method_name, "EEditorCellDialogMarkCurrentCellElement") == 0) {
+               const gchar *element_id;
+
+               g_variant_get (parameters, "(t&s)", &page_id, &element_id);
+
+               editor_page = get_editor_page_or_return_dbus_error (invocation, extension, page_id);
+               if (!editor_page)
+                       goto error;
+
+               e_dialogs_dom_cell_mark_current_cell_element (editor_page, element_id);
+
+               g_dbus_method_invocation_return_value (invocation, NULL);
+       } else if (g_strcmp0 (method_name, "EEditorCellDialogSaveHistoryOnExit") == 0) {
+               g_variant_get (parameters, "(t)", &page_id);
+
+               editor_page = get_editor_page_or_return_dbus_error (invocation, extension, page_id);
+               if (!editor_page)
+                       goto error;
+
+               e_dialogs_dom_cell_save_history_on_exit (editor_page);
+
+               g_dbus_method_invocation_return_value (invocation, NULL);
+       } else if (g_strcmp0 (method_name, "EEditorCellDialogSetElementVAlign") == 0) {
+               const gchar *value;
+               EContentEditorScope scope;
+
+               g_variant_get (parameters, "(t&si)", &page_id, &value, &scope);
+
+               editor_page = get_editor_page_or_return_dbus_error (invocation, extension, page_id);
+               if (!editor_page)
+                       goto error;
+
+               e_dialogs_dom_cell_set_element_v_align (editor_page, value, scope);
+
+               g_dbus_method_invocation_return_value (invocation, NULL);
+       } else if (g_strcmp0 (method_name, "EEditorCellDialogSetElementAlign") == 0) {
+               const gchar *value;
+               EContentEditorScope scope;
+
+               g_variant_get (parameters, "(t&si)", &page_id, &value, &scope);
+
+               editor_page = get_editor_page_or_return_dbus_error (invocation, extension, page_id);
+               if (!editor_page)
+                       goto error;
+
+               e_dialogs_dom_cell_set_element_align (editor_page, value, scope);
+
+               g_dbus_method_invocation_return_value (invocation, NULL);
+       } else if (g_strcmp0 (method_name, "EEditorCellDialogSetElementNoWrap") == 0) {
+               gboolean value;
+               EContentEditorScope scope;
+
+               g_variant_get (parameters, "(tbi)", &page_id, &value, &scope);
+
+               editor_page = get_editor_page_or_return_dbus_error (invocation, extension, page_id);
+               if (!editor_page)
+                       goto error;
+
+               e_dialogs_dom_cell_set_element_no_wrap (editor_page, value, scope);
+
+               g_dbus_method_invocation_return_value (invocation, NULL);
+       } else if (g_strcmp0 (method_name, "EEditorCellDialogSetElementHeaderStyle") == 0) {
+               gboolean value;
+               EContentEditorScope scope;
+
+               g_variant_get (parameters, "(tbi)", &page_id, &value, &scope);
+
+               editor_page = get_editor_page_or_return_dbus_error (invocation, extension, page_id);
+               if (!editor_page)
+                       goto error;
+
+               e_dialogs_dom_cell_set_element_header_style (editor_page, value, scope);
+
+               g_dbus_method_invocation_return_value (invocation, NULL);
+       } else if (g_strcmp0 (method_name, "EEditorCellDialogSetElementWidth") == 0) {
+               const gchar *value;
+               EContentEditorScope scope;
+
+               g_variant_get (parameters, "(t&si)", &page_id, &value, &scope);
+
+               editor_page = get_editor_page_or_return_dbus_error (invocation, extension, page_id);
+               if (!editor_page)
+                       goto error;
+
+               e_dialogs_dom_cell_set_element_width (editor_page, value, scope);
+
+               g_dbus_method_invocation_return_value (invocation, NULL);
+       } else if (g_strcmp0 (method_name, "EEditorCellDialogSetElementColSpan") == 0) {
+               glong value;
+               EContentEditorScope scope;
+
+               g_variant_get (parameters, "(tii)", &page_id, &value, &scope);
+
+               editor_page = get_editor_page_or_return_dbus_error (invocation, extension, page_id);
+               if (!editor_page)
+                       goto error;
+
+               e_dialogs_dom_cell_set_element_col_span (editor_page, value, scope);
+
+               g_dbus_method_invocation_return_value (invocation, NULL);
+       } else if (g_strcmp0 (method_name, "EEditorCellDialogSetElementRowSpan") == 0) {
+               glong value;
+               EContentEditorScope scope;
+
+               g_variant_get (parameters, "(tii)", &page_id, &value, &scope);
+
+               editor_page = get_editor_page_or_return_dbus_error (invocation, extension, page_id);
+               if (!editor_page)
+                       goto error;
+
+               e_dialogs_dom_cell_set_element_row_span (editor_page, value, scope);
+
+               g_dbus_method_invocation_return_value (invocation, NULL);
+       } else if (g_strcmp0 (method_name, "EEditorCellDialogSetElementBgColor") == 0) {
+               const gchar *value;
+               EContentEditorScope scope;
+
+               g_variant_get (parameters, "(t&si)", &page_id, &value, &scope);
+
+               editor_page = get_editor_page_or_return_dbus_error (invocation, extension, page_id);
+               if (!editor_page)
+                       goto error;
+
+               e_dialogs_dom_cell_set_element_bg_color (editor_page, value, scope);
+
+               g_dbus_method_invocation_return_value (invocation, NULL);
+       } else if (g_strcmp0 (method_name, "EEditorHRuleDialogFindHRule") == 0) {
+               gboolean created_new_hr = FALSE;
+               g_variant_get (parameters, "(t)", &page_id);
+
+               editor_page = get_editor_page_or_return_dbus_error (invocation, extension, page_id);
+               if (!editor_page)
+                       goto error;
+
+               created_new_hr = e_dialogs_dom_h_rule_find_hrule (editor_page);
+
+               g_dbus_method_invocation_return_value (
+                       invocation, g_variant_new ("(b)", created_new_hr));
+       } else if (g_strcmp0 (method_name, "EEditorHRuleDialogOnClose") == 0) {
+               g_variant_get (parameters, "(t)", &page_id);
+
+               editor_page = get_editor_page_or_return_dbus_error (invocation, extension, page_id);
+               if (!editor_page)
+                       goto error;
+
+               e_dialogs_dom_h_rule_dialog_on_close (editor_page);
+
+               g_dbus_method_invocation_return_value (invocation, NULL);
+       } else if (g_strcmp0 (method_name, "EEditorImageDialogMarkImage") == 0) {
+               g_variant_get (parameters, "(t)", &page_id);
+
+               editor_page = get_editor_page_or_return_dbus_error (invocation, extension, page_id);
+               if (!editor_page)
+                       goto error;
+
+               e_dialogs_dom_image_mark_image (editor_page);
+
+               g_dbus_method_invocation_return_value (invocation, NULL);
+       } else if (g_strcmp0 (method_name, "EEditorImageDialogSaveHistoryOnExit") == 0) {
+               g_variant_get (parameters, "(t)", &page_id);
+
+               editor_page = get_editor_page_or_return_dbus_error (invocation, extension, page_id);
+               if (!editor_page)
+                       goto error;
+
+               e_dialogs_dom_image_save_history_on_exit (editor_page);
+
+               g_dbus_method_invocation_return_value (invocation, NULL);
+       } else if (g_strcmp0 (method_name, "EEditorImageDialogSetElementUrl") == 0) {
+               const gchar *value;
+
+               g_variant_get (parameters, "(t&s)", &page_id, &value);
+
+               editor_page = get_editor_page_or_return_dbus_error (invocation, extension, page_id);
+               if (!editor_page)
+                       goto error;
+
+               e_dialogs_dom_image_set_element_url (editor_page, value);
+
+               g_dbus_method_invocation_return_value (invocation, NULL);
+       } else if (g_strcmp0 (method_name, "EEditorImageDialogGetElementUrl") == 0) {
+               gchar *value;
+
+               g_variant_get (parameters, "(t)", &page_id);
+
+               editor_page = get_editor_page_or_return_dbus_error (invocation, extension, page_id);
+               if (!editor_page)
+                       goto error;
+
+               value = e_dialogs_dom_image_get_element_url (editor_page);
+
+               g_dbus_method_invocation_return_value (
+                       invocation,
+                       g_variant_new (
+                               "(@s)",
+                               g_variant_new_take_string (
+                                       value ? value : g_strdup (""))));
+       } else if (g_strcmp0 (method_name, "ImageElementSetWidth") == 0) {
+               const gchar *element_id;
+               gint32 value;
+               WebKitDOMElement *element;
+
+               g_variant_get (
+                       parameters, "(t&si)", &page_id, &element_id, &value);
+
+               editor_page = get_editor_page_or_return_dbus_error (invocation, extension, page_id);
+               if (!editor_page)
+                       goto error;
+
+               document = e_editor_page_get_document (editor_page);
+               element = webkit_dom_document_get_element_by_id (document, element_id);
+               if (element)
+                       webkit_dom_html_image_element_set_width (
+                               WEBKIT_DOM_HTML_IMAGE_ELEMENT (element), value);
+
+               g_dbus_method_invocation_return_value (invocation, NULL);
+       } else if (g_strcmp0 (method_name, "ImageElementGetWidth") == 0) {
+               const gchar *element_id;
+               glong value = 0;
+               WebKitDOMElement *element;
+
+               g_variant_get (
+                       parameters, "(t&s)", &page_id, &element_id);
+
+               editor_page = get_editor_page_or_return_dbus_error (invocation, extension, page_id);
+               if (!editor_page)
+                       goto error;
+
+               document = e_editor_page_get_document (editor_page);
+               element = webkit_dom_document_get_element_by_id (document, element_id);
+               if (element)
+                       value = webkit_dom_html_image_element_get_width (
+                               WEBKIT_DOM_HTML_IMAGE_ELEMENT (element));
+
+               g_dbus_method_invocation_return_value (
+                       invocation, g_variant_new ("(i)", value));
+       } else if (g_strcmp0 (method_name, "ImageElementSetHeight") == 0) {
+               const gchar *element_id;
+               gint32 value;
+               WebKitDOMElement *element;
+
+               g_variant_get (
+                       parameters, "(t&si)", &page_id, &element_id, &value);
+
+               editor_page = get_editor_page_or_return_dbus_error (invocation, extension, page_id);
+               if (!editor_page)
+                       goto error;
+
+               document = e_editor_page_get_document (editor_page);
+               element = webkit_dom_document_get_element_by_id (document, element_id);
+               if (element)
+                       webkit_dom_html_image_element_set_width (
+                               WEBKIT_DOM_HTML_IMAGE_ELEMENT (element), value);
+
+               g_dbus_method_invocation_return_value (invocation, NULL);
+       } else if (g_strcmp0 (method_name, "ImageElementGetHeight") == 0) {
+               const gchar *element_id;
+               glong value = 0;
+               WebKitDOMElement *element;
+
+               g_variant_get (
+                       parameters, "(t&s)", &page_id, &element_id);
+
+               editor_page = get_editor_page_or_return_dbus_error (invocation, extension, page_id);
+               if (!editor_page)
+                       goto error;
+
+               document = e_editor_page_get_document (editor_page);
+               element = webkit_dom_document_get_element_by_id (document, element_id);
+               if (element)
+                       value = webkit_dom_html_image_element_get_height (
+                               WEBKIT_DOM_HTML_IMAGE_ELEMENT (element));
+
+               g_dbus_method_invocation_return_value (
+                       invocation, g_variant_new ("(i)", value));
+       } else if (g_strcmp0 (method_name, "ImageElementGetNaturalWidth") == 0) {
+               const gchar *element_id;
+               glong value = 0;
+               WebKitDOMElement *element;
+
+               g_variant_get (
+                       parameters, "(t&s)", &page_id, &element_id);
+
+               editor_page = get_editor_page_or_return_dbus_error (invocation, extension, page_id);
+               if (!editor_page)
+                       goto error;
+
+               document = e_editor_page_get_document (editor_page);
+               element = webkit_dom_document_get_element_by_id (document, element_id);
+               if (element)
+                       value = webkit_dom_html_image_element_get_natural_width (
+                               WEBKIT_DOM_HTML_IMAGE_ELEMENT (element));
+
+               g_dbus_method_invocation_return_value (
+                       invocation, g_variant_new ("(i)", value));
+       } else if (g_strcmp0 (method_name, "ImageElementGetNaturalHeight") == 0) {
+               const gchar *element_id;
+               glong value = 0;
+               WebKitDOMElement *element;
+
+               g_variant_get (
+                       parameters, "(t&s)", &page_id, &element_id);
+
+               editor_page = get_editor_page_or_return_dbus_error (invocation, extension, page_id);
+               if (!editor_page)
+                       goto error;
+
+               document = e_editor_page_get_document (editor_page);
+               element = webkit_dom_document_get_element_by_id (document, element_id);
+               if (element)
+                       value = webkit_dom_html_image_element_get_natural_height (
+                               WEBKIT_DOM_HTML_IMAGE_ELEMENT (element));
+
+               g_dbus_method_invocation_return_value (
+                       invocation, g_variant_new ("(i)", value));
+       } else if (g_strcmp0 (method_name, "ImageElementSetHSpace") == 0) {
+               const gchar *element_id;
+               gint32 value;
+               WebKitDOMElement *element;
+
+               g_variant_get (
+                       parameters, "(t&si)", &page_id, &element_id, &value);
+
+               editor_page = get_editor_page_or_return_dbus_error (invocation, extension, page_id);
+               if (!editor_page)
+                       goto error;
+
+               document = e_editor_page_get_document (editor_page);
+               element = webkit_dom_document_get_element_by_id (document, element_id);
+               if (element)
+                       webkit_dom_html_image_element_set_hspace (
+                               WEBKIT_DOM_HTML_IMAGE_ELEMENT (element), value);
+
+               g_dbus_method_invocation_return_value (invocation, NULL);
+       } else if (g_strcmp0 (method_name, "ImageElementGetHSpace") == 0) {
+               const gchar *element_id;
+               glong value = 0;
+               WebKitDOMElement *element;
+
+               g_variant_get (
+                       parameters, "(t&s)", &page_id, &element_id);
+
+               editor_page = get_editor_page_or_return_dbus_error (invocation, extension, page_id);
+               if (!editor_page)
+                       goto error;
+
+               document = e_editor_page_get_document (editor_page);
+               element = webkit_dom_document_get_element_by_id (document, element_id);
+               if (element)
+                       value = webkit_dom_html_image_element_get_hspace (
+                               WEBKIT_DOM_HTML_IMAGE_ELEMENT (element));
+
+               g_dbus_method_invocation_return_value (
+                       invocation, g_variant_new ("(i)", value));
+       } else if (g_strcmp0 (method_name, "ImageElementSetVSpace") == 0) {
+               const gchar *element_id;
+               gint32 value;
+               WebKitDOMElement *element;
+
+               g_variant_get (
+                       parameters, "(t&si)", &page_id, &element_id, &value);
+
+               editor_page = get_editor_page_or_return_dbus_error (invocation, extension, page_id);
+               if (!editor_page)
+                       goto error;
+
+               document = e_editor_page_get_document (editor_page);
+               element = webkit_dom_document_get_element_by_id (document, element_id);
+               if (element)
+                       webkit_dom_html_image_element_set_vspace (
+                               WEBKIT_DOM_HTML_IMAGE_ELEMENT (element), value);
+
+               g_dbus_method_invocation_return_value (invocation, NULL);
+       } else if (g_strcmp0 (method_name, "ImageElementGetVSpace") == 0) {
+               const gchar *element_id;
+               glong value = 0;
+               WebKitDOMElement *element;
+
+               g_variant_get (
+                       parameters, "(t&s)", &page_id, &element_id);
+
+               editor_page = get_editor_page_or_return_dbus_error (invocation, extension, page_id);
+               if (!editor_page)
+                       goto error;
+
+               document = e_editor_page_get_document (editor_page);
+               element = webkit_dom_document_get_element_by_id (document, element_id);
+               if (element)
+                       value = webkit_dom_html_image_element_get_vspace (
+                               WEBKIT_DOM_HTML_IMAGE_ELEMENT (element));
+
+               g_dbus_method_invocation_return_value (
+                       invocation, g_variant_new ("(i)", value));
+       } else if (g_strcmp0 (method_name, "EEditorLinkDialogOk") == 0) {
+               const gchar *url, *inner_text;
+
+               g_variant_get (parameters, "(t&s&s)", &page_id, &url, &inner_text);
+
+               editor_page = get_editor_page_or_return_dbus_error (invocation, extension, page_id);
+               if (!editor_page)
+                       goto error;
+
+               e_dialogs_dom_link_commit (editor_page, url, inner_text);
+
+               g_dbus_method_invocation_return_value (invocation, NULL);
+       } else if (g_strcmp0 (method_name, "EEditorLinkDialogShow") == 0) {
+               g_variant_get (parameters, "(t)", &page_id);
+
+               editor_page = get_editor_page_or_return_dbus_error (invocation, extension, page_id);
+               if (!editor_page)
+                       goto error;
+
+               g_dbus_method_invocation_return_value (
+                       invocation, e_dialogs_dom_link_show (editor_page));
+       } else if (g_strcmp0 (method_name, "EEditorPageDialogSaveHistory") == 0) {
+               g_variant_get (parameters, "(t)", &page_id);
+
+               editor_page = get_editor_page_or_return_dbus_error (invocation, extension, page_id);
+               if (!editor_page)
+                       goto error;
+
+               e_dialogs_dom_page_save_history (editor_page);
+
+               g_dbus_method_invocation_return_value (invocation, NULL);
+       } else if (g_strcmp0 (method_name, "EEditorPageDialogSaveHistoryOnExit") == 0) {
+               g_variant_get (parameters, "(t)", &page_id);
+
+               editor_page = get_editor_page_or_return_dbus_error (invocation, extension, page_id);
+               if (!editor_page)
+                       goto error;
+
+               e_dialogs_dom_page_save_history_on_exit (editor_page);
+
+               g_dbus_method_invocation_return_value (invocation, NULL);
+       } else if (g_strcmp0 (method_name, "EEditorSpellCheckDialogNext") == 0) {
+               const gchar *from_word = NULL;
+               const gchar * const *languages = NULL;
+               gchar *value = NULL;
+
+               g_variant_get (parameters, "(t&s^as)", &page_id, &from_word, &languages);
+
+               editor_page = get_editor_page_or_return_dbus_error (invocation, extension, page_id);
+               if (!editor_page)
+                       goto error;
+
+               value = e_dialogs_dom_spell_check_next (editor_page, from_word, languages);
+
+               g_dbus_method_invocation_return_value (
+                       invocation,
+                       g_variant_new (
+                               "(@s)",
+                               g_variant_new_take_string (
+                                       value ? value : g_strdup (""))));
+       } else if (g_strcmp0 (method_name, "EEditorSpellCheckDialogPrev") == 0) {
+               const gchar *from_word = NULL;
+               const gchar * const *languages = NULL;
+               gchar *value = NULL;
+
+               g_variant_get (parameters, "(t&s^as)", &page_id, &from_word, &languages);
+
+               editor_page = get_editor_page_or_return_dbus_error (invocation, extension, page_id);
+               if (!editor_page)
+                       goto error;
+
+               value = e_dialogs_dom_spell_check_prev (editor_page, from_word, languages);
+
+               g_dbus_method_invocation_return_value (
+                       invocation,
+                       g_variant_new (
+                               "(@s)",
+                               g_variant_new_take_string (
+                                       value ? value : g_strdup (""))));
+       } else if (g_strcmp0 (method_name, "EEditorTableDialogSetRowCount") == 0) {
+               guint32 value;
+
+               g_variant_get (parameters, "(tu)", &page_id, &value);
+
+               editor_page = get_editor_page_or_return_dbus_error (invocation, extension, page_id);
+               if (!editor_page)
+                       goto error;
+
+               e_dialogs_dom_table_set_row_count (editor_page, value);
+
+               g_dbus_method_invocation_return_value (invocation, NULL);
+       } else if (g_strcmp0 (method_name, "EEditorTableDialogGetRowCount") == 0) {
+               gulong value;
+
+               g_variant_get (parameters, "(t)", &page_id);
+
+               editor_page = get_editor_page_or_return_dbus_error (invocation, extension, page_id);
+               if (!editor_page)
+                       goto error;
+
+               value = e_dialogs_dom_table_get_row_count (editor_page);
+
+               g_dbus_method_invocation_return_value (
+                       invocation, g_variant_new ("(u)", value));
+       } else if (g_strcmp0 (method_name, "EEditorTableDialogSetColumnCount") == 0) {
+               guint32 value;
+
+               g_variant_get (parameters, "(tu)", &page_id, &value);
+
+               editor_page = get_editor_page_or_return_dbus_error (invocation, extension, page_id);
+               if (!editor_page)
+                       goto error;
+
+               e_dialogs_dom_table_set_column_count (editor_page, value);
+
+               g_dbus_method_invocation_return_value (invocation, NULL);
+       } else if (g_strcmp0 (method_name, "EEditorTableDialogGetColumnCount") == 0) {
+               gulong value;
+
+               g_variant_get (parameters, "(t)", &page_id);
+
+               editor_page = get_editor_page_or_return_dbus_error (invocation, extension, page_id);
+               if (!editor_page)
+                       goto error;
+
+               value = e_dialogs_dom_table_get_column_count (editor_page);
+
+               g_dbus_method_invocation_return_value (
+                       invocation, g_variant_new ("(u)", value));
+       } else if (g_strcmp0 (method_name, "EEditorTableDialogShow") == 0) {
+               gboolean created_new_table;
+
+               g_variant_get (parameters, "(t)", &page_id);
+
+               editor_page = get_editor_page_or_return_dbus_error (invocation, extension, page_id);
+               if (!editor_page)
+                       goto error;
+
+               created_new_table = e_dialogs_dom_table_show (editor_page);
+
+               g_dbus_method_invocation_return_value (
+                       invocation, g_variant_new ("(b)", created_new_table));
+       } else if (g_strcmp0 (method_name, "EEditorTableDialogSaveHistoryOnExit") == 0) {
+               g_variant_get (parameters, "(t)", &page_id);
+
+               editor_page = get_editor_page_or_return_dbus_error (invocation, extension, page_id);
+               if (!editor_page)
+                       goto error;
+
+               e_dialogs_dom_table_save_history_on_exit (editor_page);
+
+               g_dbus_method_invocation_return_value (invocation, NULL);
+       } else if (g_strcmp0 (method_name, "EEditorDialogDeleteCellContents") == 0) {
+               g_variant_get (parameters, "(t)", &page_id);
+
+               editor_page = get_editor_page_or_return_dbus_error (invocation, extension, page_id);
+               if (!editor_page)
+                       goto error;
+
+               e_editor_dom_delete_cell_contents (editor_page);
+
+               g_dbus_method_invocation_return_value (invocation, NULL);
+       } else if (g_strcmp0 (method_name, "EEditorDialogDeleteColumn") == 0) {
+               g_variant_get (parameters, "(t)", &page_id);
+
+               editor_page = get_editor_page_or_return_dbus_error (invocation, extension, page_id);
+               if (!editor_page)
+                       goto error;
+
+               e_editor_dom_delete_column (editor_page);
+
+               g_dbus_method_invocation_return_value (invocation, NULL);
+       } else if (g_strcmp0 (method_name, "EEditorDialogDeleteRow") == 0) {
+               g_variant_get (parameters, "(t)", &page_id);
+
+               editor_page = get_editor_page_or_return_dbus_error (invocation, extension, page_id);
+               if (!editor_page)
+                       goto error;
+
+               e_editor_dom_delete_row (editor_page);
+
+               g_dbus_method_invocation_return_value (invocation, NULL);
+       } else if (g_strcmp0 (method_name, "EEditorDialogDeleteTable") == 0) {
+               g_variant_get (parameters, "(t)", &page_id);
+
+               editor_page = get_editor_page_or_return_dbus_error (invocation, extension, page_id);
+               if (!editor_page)
+                       goto error;
+
+               e_editor_dom_delete_table (editor_page);
+
+               g_dbus_method_invocation_return_value (invocation, NULL);
+       } else if (g_strcmp0 (method_name, "EEditorDialogInsertColumnAfter") == 0) {
+               g_variant_get (parameters, "(t)", &page_id);
+
+               editor_page = get_editor_page_or_return_dbus_error (invocation, extension, page_id);
+               if (!editor_page)
+                       goto error;
+
+               e_editor_dom_insert_column_after (editor_page);
+
+               g_dbus_method_invocation_return_value (invocation, NULL);
+       } else if (g_strcmp0 (method_name, "EEditorDialogInsertColumnBefore") == 0) {
+               g_variant_get (parameters, "(t)", &page_id);
+
+               editor_page = get_editor_page_or_return_dbus_error (invocation, extension, page_id);
+               if (!editor_page)
+                       goto error;
+
+               e_editor_dom_insert_column_before (editor_page);
+
+               g_dbus_method_invocation_return_value (invocation, NULL);
+       } else if (g_strcmp0 (method_name, "EEditorDialogInsertRowAbove") == 0) {
+               g_variant_get (parameters, "(t)", &page_id);
+
+               editor_page = get_editor_page_or_return_dbus_error (invocation, extension, page_id);
+               if (!editor_page)
+                       goto error;
+
+               e_editor_dom_insert_row_above (editor_page);
+
+               g_dbus_method_invocation_return_value (invocation, NULL);
+       } else if (g_strcmp0 (method_name, "EEditorDialogInsertRowBelow") == 0) {
+               g_variant_get (parameters, "(t)", &page_id);
+
+               editor_page = get_editor_page_or_return_dbus_error (invocation, extension, page_id);
+               if (!editor_page)
+                       goto error;
+
+               e_editor_dom_insert_row_below (editor_page);
+
+               g_dbus_method_invocation_return_value (invocation, NULL);
+       } else if (g_strcmp0 (method_name, "EEditorLinkDialogOnOpen") == 0) {
+               g_variant_get (parameters, "(t)", &page_id);
+
+               editor_page = get_editor_page_or_return_dbus_error (invocation, extension, page_id);
+               if (!editor_page)
+                       goto error;
+
+               e_dialogs_dom_link_dialog_on_open (editor_page);
+
+               g_dbus_method_invocation_return_value (invocation, NULL);
+       } else if (g_strcmp0 (method_name, "EEditorLinkDialogOnClose") == 0) {
+               g_variant_get (parameters, "(t)", &page_id);
+
+               editor_page = get_editor_page_or_return_dbus_error (invocation, extension, page_id);
+               if (!editor_page)
+                       goto error;
+
+               e_dialogs_dom_link_dialog_on_close (editor_page);
+
+               g_dbus_method_invocation_return_value (invocation, NULL);
+       } else if (g_strcmp0 (method_name, "EEditorLinkDialogUnlink") == 0) {
+               EEditorUndoRedoManager *manager;
+
+               g_variant_get (parameters, "(t)", &page_id);
+
+               editor_page = get_editor_page_or_return_dbus_error (invocation, extension, page_id);
+               if (!editor_page)
+                       goto error;
+
+               manager = e_editor_page_get_undo_redo_manager (editor_page);
+               /* Remove the history event that was saved when the dialog was opened */
+               e_editor_undo_redo_manager_remove_current_history_event (manager);
+
+               e_editor_dom_selection_unlink (editor_page);
+
+               g_dbus_method_invocation_return_value (invocation, NULL);
+       } else if (g_strcmp0 (method_name, "EEditorActionsSaveHistoryForCut") == 0) {
+               g_variant_get (parameters, "(t)", &page_id);
+
+               editor_page = get_editor_page_or_return_dbus_error (invocation, extension, page_id);
+               if (!editor_page)
+                       goto error;
+
+               e_editor_dom_save_history_for_cut (editor_page);
+
+               g_dbus_method_invocation_return_value (invocation, NULL);
+       } else if (g_strcmp0 (method_name, "TableCellElementGetNoWrap") == 0) {
+               const gchar *element_id;
+               gboolean value = FALSE;
+               WebKitDOMElement *element;
+
+               g_variant_get (parameters, "(t&s)", &page_id, &element_id);
+
+               editor_page = get_editor_page_or_return_dbus_error (invocation, extension, page_id);
+               if (!editor_page)
+                       goto error;
+
+               document = e_editor_page_get_document (editor_page);
+               element = webkit_dom_document_get_element_by_id (document, element_id);
+               if (element)
+                       value = webkit_dom_html_table_cell_element_get_no_wrap (
+                               WEBKIT_DOM_HTML_TABLE_CELL_ELEMENT (element));
+
+               g_dbus_method_invocation_return_value (
+                       invocation, g_variant_new ("(b)", value));
+       } else if (g_strcmp0 (method_name, "TableCellElementGetRowSpan") == 0) {
+               const gchar *element_id;
+               glong value = 0;
+               WebKitDOMElement *element;
+
+               g_variant_get (parameters, "(t&s)", &page_id, &element_id);
+
+               editor_page = get_editor_page_or_return_dbus_error (invocation, extension, page_id);
+               if (!editor_page)
+                       goto error;
+
+               document = e_editor_page_get_document (editor_page);
+               element = webkit_dom_document_get_element_by_id (document, element_id);
+               if (element)
+                       value = webkit_dom_html_table_cell_element_get_row_span (
+                               WEBKIT_DOM_HTML_TABLE_CELL_ELEMENT (element));
+
+               g_dbus_method_invocation_return_value (
+                       invocation, g_variant_new ("(i)", value));
+       } else if (g_strcmp0 (method_name, "TableCellElementGetColSpan") == 0) {
+               const gchar *element_id;
+               glong value = 0;
+               WebKitDOMElement *element;
+
+               g_variant_get (parameters, "(t&s)", &page_id, &element_id);
+
+               editor_page = get_editor_page_or_return_dbus_error (invocation, extension, page_id);
+               if (!editor_page)
+                       goto error;
+
+               document = e_editor_page_get_document (editor_page);
+               element = webkit_dom_document_get_element_by_id (document, element_id);
+               if (element)
+                       value = webkit_dom_html_table_cell_element_get_col_span (
+                               WEBKIT_DOM_HTML_TABLE_CELL_ELEMENT (element));
+
+               g_dbus_method_invocation_return_value (
+                       invocation, g_variant_new ("(i)", value));
+       } else if (g_strcmp0 (method_name, "DOMSaveSelection") == 0) {
+               g_variant_get (parameters, "(t)", &page_id);
+
+               editor_page = get_editor_page_or_return_dbus_error (invocation, extension, page_id);
+               if (!editor_page)
+                       goto error;
+
+               e_editor_dom_selection_save (editor_page);
+               g_dbus_method_invocation_return_value (invocation, NULL);
+       } else if (g_strcmp0 (method_name, "DOMRestoreSelection") == 0) {
+               g_variant_get (parameters, "(t)", &page_id);
+
+               editor_page = get_editor_page_or_return_dbus_error (invocation, extension, page_id);
+               if (!editor_page)
+                       goto error;
+
+               e_editor_dom_selection_restore (editor_page);
+               g_dbus_method_invocation_return_value (invocation, NULL);
+       } else if (g_strcmp0 (method_name, "DOMUndo") == 0) {
+               EEditorUndoRedoManager *manager;
+
+               g_variant_get (parameters, "(t)", &page_id);
+
+               editor_page = get_editor_page_or_return_dbus_error (invocation, extension, page_id);
+               if (!editor_page)
+                       goto error;
+
+               manager = e_editor_page_get_undo_redo_manager (editor_page);
+
+               e_editor_undo_redo_manager_undo (manager);
+
+               g_dbus_method_invocation_return_value (invocation, NULL);
+       } else if (g_strcmp0 (method_name, "DOMRedo") == 0) {
+               EEditorUndoRedoManager *manager;
+
+               g_variant_get (parameters, "(t)", &page_id);
+
+               editor_page = get_editor_page_or_return_dbus_error (invocation, extension, page_id);
+               if (!editor_page)
+                       goto error;
+
+               manager = e_editor_page_get_undo_redo_manager (editor_page);
+
+               e_editor_undo_redo_manager_redo (manager);
+
+               g_dbus_method_invocation_return_value (invocation, NULL);
+       } else if (g_strcmp0 (method_name, "DOMTurnSpellCheckOff") == 0) {
+               g_variant_get (parameters, "(t)", &page_id);
+
+               editor_page = get_editor_page_or_return_dbus_error (invocation, extension, page_id);
+               if (!editor_page)
+                       goto error;
+
+               e_editor_dom_turn_spell_check_off (editor_page);
+               g_dbus_method_invocation_return_value (invocation, NULL);
+       } else if (g_strcmp0 (method_name, "DOMQuoteAndInsertTextIntoSelection") == 0) {
+               gboolean is_html = FALSE;
+               const gchar *text;
+
+               g_variant_get (parameters, "(t&sb)", &page_id, &text, &is_html);
+
+               editor_page = get_editor_page_or_return_dbus_error (invocation, extension, page_id);
+               if (!editor_page)
+                       goto error;
+
+               e_editor_dom_quote_and_insert_text_into_selection (editor_page, text, is_html);
+               g_dbus_method_invocation_return_value (invocation, NULL);
+       } else if (g_strcmp0 (method_name, "DOMConvertAndInsertHTMLIntoSelection") == 0) {
+               gboolean is_html;
+               const gchar *text;
+
+               g_variant_get (parameters, "(t&sb)", &page_id, &text, &is_html);
+
+               editor_page = get_editor_page_or_return_dbus_error (invocation, extension, page_id);
+               if (!editor_page)
+                       goto error;
+
+               e_editor_dom_convert_and_insert_html_into_selection (editor_page, text, is_html);
+               g_dbus_method_invocation_return_value (invocation, NULL);
+       } else if (g_strcmp0 (method_name, "DOMEmbedStyleSheet") == 0) {
+               const gchar *style_sheet_content;
+
+               g_variant_get (parameters, "(t&s)", &page_id, &style_sheet_content);
+
+               editor_page = get_editor_page_or_return_dbus_error (invocation, extension, page_id);
+               if (!editor_page)
+                       goto error;
+
+               e_editor_dom_embed_style_sheet (editor_page, style_sheet_content);
+               g_dbus_method_invocation_return_value (invocation, NULL);
+       } else if (g_strcmp0 (method_name, "DOMRemoveEmbeddedStyleSheet") == 0) {
+               g_variant_get (parameters, "(t)", &page_id);
+
+               editor_page = get_editor_page_or_return_dbus_error (invocation, extension, page_id);
+               if (!editor_page)
+                       goto error;
+
+               e_editor_dom_remove_embedded_style_sheet (editor_page);
+               g_dbus_method_invocation_return_value (invocation, NULL);
+       } else if (g_strcmp0 (method_name, "SetPastingContentFromItself") == 0) {
+               gboolean value = FALSE;
+
+               g_variant_get (parameters, "(tb)", &page_id, &value);
+
+               editor_page = get_editor_page_or_return_dbus_error (invocation, extension, page_id);
+               if (!editor_page)
+                       goto error;
+
+               e_editor_page_set_pasting_content_from_itself (editor_page, value);
+
+               g_dbus_method_invocation_return_value (invocation, NULL);
+       } else if (g_strcmp0 (method_name, "SetEditorHTMLMode") == 0) {
+               gboolean html_mode = FALSE;
+               gboolean convert = FALSE;
+
+               g_variant_get (parameters, "(tbb)", &page_id, &html_mode, &convert);
+
+               editor_page = get_editor_page_or_return_dbus_error (invocation, extension, page_id);
+               if (!editor_page)
+                       goto error;
+
+               convert = convert && e_editor_page_get_html_mode (editor_page) && !html_mode;
+               e_editor_page_set_html_mode (editor_page, html_mode);
+
+               if (convert)
+                       e_editor_dom_convert_when_changing_composer_mode (editor_page);
+               else
+                       e_editor_dom_process_content_after_mode_change (editor_page);
+
+               g_dbus_method_invocation_return_value (invocation, NULL);
+       } else if (g_strcmp0 (method_name, "SetConvertInSitu") == 0) {
+               gboolean value = FALSE;
+
+               g_variant_get (parameters, "(tb)", &page_id, &value);
+
+               editor_page = get_editor_page_or_return_dbus_error (invocation, extension, page_id);
+               if (!editor_page)
+                       goto error;
+
+               e_editor_page_set_convert_in_situ (editor_page, value);
+
+               g_dbus_method_invocation_return_value (invocation, NULL);
+       } else if (g_strcmp0 (method_name, "DOMForceSpellCheck") == 0) {
+               g_variant_get (parameters, "(t)", &page_id);
+
+               editor_page = get_editor_page_or_return_dbus_error (invocation, extension, page_id);
+               if (!editor_page)
+                       goto error;
+
+               e_editor_dom_force_spell_check (editor_page);
+               g_dbus_method_invocation_return_value (invocation, NULL);
+       } else if (g_strcmp0 (method_name, "DOMCheckIfConversionNeeded") == 0) {
+               gboolean conversion_needed;
+
+               g_variant_get (parameters, "(t)", &page_id);
+
+               editor_page = get_editor_page_or_return_dbus_error (invocation, extension, page_id);
+               if (!editor_page)
+                       goto error;
+
+               conversion_needed = e_editor_dom_check_if_conversion_needed (editor_page);
+               g_dbus_method_invocation_return_value (
+                       invocation, g_variant_new ("(b)", conversion_needed));
+       } else if (g_strcmp0 (method_name, "DOMGetContent") == 0) {
+               EContentEditorGetContentFlags flags;
+               const gchar *from_domain;
+               gchar *value = NULL;
+               GVariant *inline_images = NULL;
+
+               g_variant_get (parameters, "(t&si)", &page_id, &from_domain, &flags);
+
+               editor_page = get_editor_page_or_return_dbus_error (invocation, extension, page_id);
+               if (!editor_page)
+                       goto error;
+
+               if ((flags & E_CONTENT_EDITOR_GET_INLINE_IMAGES) && from_domain && *from_domain)
+                       inline_images = e_editor_dom_get_inline_images_data (editor_page, from_domain);
+
+               if ((flags & E_CONTENT_EDITOR_GET_TEXT_HTML) &&
+                   !(flags & E_CONTENT_EDITOR_GET_PROCESSED)) {
+                       value = e_editor_dom_process_content_for_draft (
+                               editor_page, (flags & E_CONTENT_EDITOR_GET_BODY));
+               } else if ((flags & E_CONTENT_EDITOR_GET_TEXT_HTML) &&
+                          (flags & E_CONTENT_EDITOR_GET_PROCESSED) &&
+                          !(flags & E_CONTENT_EDITOR_GET_BODY)) {
+                       value = e_editor_dom_process_content_to_html_for_exporting (editor_page);
+               } else if ((flags & E_CONTENT_EDITOR_GET_TEXT_PLAIN) &&
+                          (flags & E_CONTENT_EDITOR_GET_PROCESSED) &&
+                          !(flags & E_CONTENT_EDITOR_GET_BODY)) {
+                       value = e_editor_dom_process_content_to_plain_text_for_exporting (editor_page);
+               } else if ((flags & E_CONTENT_EDITOR_GET_TEXT_PLAIN) &&
+                          (flags & E_CONTENT_EDITOR_GET_BODY) &&
+                          !(flags & E_CONTENT_EDITOR_GET_PROCESSED)) {
+                       if (flags & E_CONTENT_EDITOR_GET_EXCLUDE_SIGNATURE)
+                               value = e_composer_dom_get_raw_body_content_without_signature (editor_page);
+                       else
+                               value = e_composer_dom_get_raw_body_content (editor_page);
+               } else {
+                       g_warning ("Unsupported flags combination (%d) in (%s)", flags, G_STRFUNC);
+               }
+
+               /* If no inline images are requested we still have to return
+                * something even it won't be used at all. */
+               g_dbus_method_invocation_return_value (
+                       invocation,
+                       g_variant_new (
+                               "(sv)",
+                               value ? value : "",
+                               inline_images ? inline_images : g_variant_new_int32 (0)));
+
+               g_free (value);
+
+               if ((flags & E_CONTENT_EDITOR_GET_INLINE_IMAGES) && from_domain && *from_domain && 
inline_images)
+                       e_editor_dom_restore_images (editor_page, inline_images);
+       } else if (g_strcmp0 (method_name, "DOMInsertHTML") == 0) {
+               const gchar *html;
+
+               g_variant_get (parameters, "(t&s)", &page_id, &html);
+
+               editor_page = get_editor_page_or_return_dbus_error (invocation, extension, page_id);
+               if (!editor_page)
+                       goto error;
+
+               e_editor_dom_insert_html (editor_page, html);
+               g_dbus_method_invocation_return_value (invocation, NULL);
+       } else if (g_strcmp0 (method_name, "DOMConvertContent") == 0) {
+               const gchar *preferred_text;
+
+               g_variant_get (parameters, "(t&s)", &page_id, &preferred_text);
+
+               editor_page = get_editor_page_or_return_dbus_error (invocation, extension, page_id);
+               if (!editor_page)
+                       goto error;
+
+               e_editor_dom_convert_content (editor_page, preferred_text);
+               g_dbus_method_invocation_return_value (invocation, NULL);
+       } else if (g_strcmp0 (method_name, "DOMAddNewInlineImageIntoList") == 0) {
+               const gchar *cid_uri, *src, *filename;
+
+               g_variant_get (parameters, "(t&s&s&s)", &page_id, &filename, &cid_uri, &src);
+
+               editor_page = get_editor_page_or_return_dbus_error (invocation, extension, page_id);
+               if (!editor_page)
+                       goto error;
+
+               e_editor_page_add_new_inline_image_into_list (
+                       editor_page, cid_uri, src);
+
+               g_dbus_method_invocation_return_value (invocation, NULL);
+       } else if (g_strcmp0 (method_name, "DOMReplaceImageSrc") == 0) {
+               const gchar *selector, *uri;
+
+               g_variant_get (parameters, "(t&s&s)", &page_id, &selector, &uri);
+
+               editor_page = get_editor_page_or_return_dbus_error (invocation, extension, page_id);
+               if (!editor_page)
+                       goto error;
+
+               e_editor_dom_replace_image_src (editor_page, selector, uri);
+
+               g_dbus_method_invocation_return_value (invocation, NULL);
+       } else if (g_strcmp0 (method_name, "DOMDragAndDropEnd") == 0) {
+               g_variant_get (parameters, "(t)", &page_id);
+
+               editor_page = get_editor_page_or_return_dbus_error (invocation, extension, page_id);
+               if (!editor_page)
+                       goto error;
+
+               e_editor_dom_drag_and_drop_end (editor_page);
+               g_dbus_method_invocation_return_value (invocation, NULL);
+       } else if (g_strcmp0 (method_name, "DOMInsertSmiley") == 0) {
+               const gchar *smiley_name;
+
+               g_variant_get (parameters, "(t&s)", &page_id, &smiley_name);
+
+               editor_page = get_editor_page_or_return_dbus_error (invocation, extension, page_id);
+               if (!editor_page)
+                       goto error;
+
+               e_editor_dom_insert_smiley_by_name (editor_page, smiley_name);
+               g_dbus_method_invocation_return_value (invocation, NULL);
+       } else if (g_strcmp0 (method_name, "DOMMoveSelectionOnPoint") == 0) {
+               gboolean cancel_if_not_collapsed;
+               gint x, y;
+
+               g_variant_get (parameters, "(tiib)", &page_id, &x, &y, &cancel_if_not_collapsed);
+
+               editor_page = get_editor_page_or_return_dbus_error (invocation, extension, page_id);
+               if (!editor_page)
+                       goto error;
+
+               if (cancel_if_not_collapsed) {
+                       if (e_editor_dom_selection_is_collapsed (editor_page))
+                               e_editor_dom_selection_set_on_point (editor_page, x, y);
+               } else
+                       e_editor_dom_selection_set_on_point (editor_page, x, y);
+
+               g_dbus_method_invocation_return_value (invocation, NULL);
+       } else if (g_strcmp0 (method_name, "DOMSelectionIndent") == 0) {
+               g_variant_get (parameters, "(t)", &page_id);
+
+               editor_page = get_editor_page_or_return_dbus_error (invocation, extension, page_id);
+               if (!editor_page)
+                       goto error;
+
+               e_editor_dom_selection_indent (editor_page);
+               g_dbus_method_invocation_return_value (invocation, NULL);
+       } else if (g_strcmp0 (method_name, "DOMSelectionSave") == 0) {
+               g_variant_get (parameters, "(t)", &page_id);
+
+               editor_page = get_editor_page_or_return_dbus_error (invocation, extension, page_id);
+               if (!editor_page)
+                       goto error;
+
+               e_editor_dom_selection_save (editor_page);
+               g_dbus_method_invocation_return_value (invocation, NULL);
+       } else if (g_strcmp0 (method_name, "DOMSelectionRestore") == 0) {
+               g_variant_get (parameters, "(t)", &page_id);
+
+               editor_page = get_editor_page_or_return_dbus_error (invocation, extension, page_id);
+               if (!editor_page)
+                       goto error;
+
+               e_editor_dom_selection_restore (editor_page);
+               g_dbus_method_invocation_return_value (invocation, NULL);
+       } else if (g_strcmp0 (method_name, "DOMSelectionInsertImage") == 0) {
+               const gchar *uri;
+
+               g_variant_get (parameters, "(t&s)", &page_id, &uri);
+
+               editor_page = get_editor_page_or_return_dbus_error (invocation, extension, page_id);
+               if (!editor_page)
+                       goto error;
+
+               e_editor_dom_insert_image (editor_page, uri);
+
+               g_dbus_method_invocation_return_value (invocation, NULL);
+       } else if (g_strcmp0 (method_name, "DOMSelectionReplace") == 0) {
+               const gchar *replacement;
+
+               g_variant_get (parameters, "(t&s)", &page_id, &replacement);
+
+               editor_page = get_editor_page_or_return_dbus_error (invocation, extension, page_id);
+               if (!editor_page)
+                       goto error;
+
+               e_editor_dom_selection_replace (editor_page, replacement);
+               g_dbus_method_invocation_return_value (invocation, NULL);
+       } else if (g_strcmp0 (method_name, "DOMSelectionSetAlignment") == 0) {
+               EContentEditorAlignment alignment;
+
+               g_variant_get (parameters, "(ti)", &page_id, &alignment);
+
+               editor_page = get_editor_page_or_return_dbus_error (invocation, extension, page_id);
+               if (!editor_page)
+                       goto error;
+
+               e_editor_dom_selection_set_alignment (editor_page, alignment);
+               e_editor_page_set_alignment (editor_page, alignment);
+               g_dbus_method_invocation_return_value (invocation, NULL);
+       } else if (g_strcmp0 (method_name, "DOMSelectionSetBold") == 0) {
+               gboolean bold;
+
+               g_variant_get (parameters, "(tb)", &page_id, &bold);
+
+               editor_page = get_editor_page_or_return_dbus_error (invocation, extension, page_id);
+               if (!editor_page)
+                       goto error;
+
+               e_editor_page_set_bold (editor_page, bold);
+               g_dbus_method_invocation_return_value (invocation, NULL);
+       } else if (g_strcmp0 (method_name, "DOMSelectionSetBlockFormat") == 0) {
+               EContentEditorBlockFormat block_format;
+
+               g_variant_get (parameters, "(ti)", &page_id, &block_format);
+
+               editor_page = get_editor_page_or_return_dbus_error (invocation, extension, page_id);
+               if (!editor_page)
+                       goto error;
+
+               e_editor_dom_selection_set_block_format (editor_page, block_format);
+               g_dbus_method_invocation_return_value (invocation, NULL);
+       } else if (g_strcmp0 (method_name, "DOMSelectionSetFontColor") == 0) {
+               const gchar *color;
+
+               g_variant_get (parameters, "(t&s)", &page_id, &color);
+
+               editor_page = get_editor_page_or_return_dbus_error (invocation, extension, page_id);
+               if (!editor_page)
+                       goto error;
+
+               e_editor_dom_selection_set_font_color (editor_page, color);
+               g_dbus_method_invocation_return_value (invocation, NULL);
+       } else if (g_strcmp0 (method_name, "DOMSelectionSetFontSize") == 0) {
+               EContentEditorFontSize font_size;
+
+               g_variant_get (parameters, "(ti)", &page_id, &font_size);
+
+               editor_page = get_editor_page_or_return_dbus_error (invocation, extension, page_id);
+               if (!editor_page)
+                       goto error;
+
+               e_editor_dom_selection_set_font_size (editor_page, font_size);
+               g_dbus_method_invocation_return_value (invocation, NULL);
+       } else if (g_strcmp0 (method_name, "DOMSelectionSetItalic") == 0) {
+               gboolean italic;
+
+               g_variant_get (parameters, "(tb)", &page_id, &italic);
+
+               editor_page = get_editor_page_or_return_dbus_error (invocation, extension, page_id);
+               if (!editor_page)
+                       goto error;
+
+               e_editor_page_set_italic (editor_page, italic);
+               g_dbus_method_invocation_return_value (invocation, NULL);
+       } else if (g_strcmp0 (method_name, "DOMSelectionSetMonospaced") == 0) {
+               gboolean monospaced;
+
+               g_variant_get (parameters, "(tb)", &page_id, &monospaced);
+
+               editor_page = get_editor_page_or_return_dbus_error (invocation, extension, page_id);
+               if (!editor_page)
+                       goto error;
+
+               e_editor_page_set_monospace (editor_page, monospaced);
+               g_dbus_method_invocation_return_value (invocation, NULL);
+       } else if (g_strcmp0 (method_name, "DOMSelectionSetStrikethrough") == 0) {
+               gboolean strikethrough;
+
+               g_variant_get (parameters, "(tb)", &page_id, &strikethrough);
+
+               editor_page = get_editor_page_or_return_dbus_error (invocation, extension, page_id);
+               if (!editor_page)
+                       goto error;
+
+               e_editor_page_set_strikethrough (editor_page, strikethrough);
+               g_dbus_method_invocation_return_value (invocation, NULL);
+       } else if (g_strcmp0 (method_name, "DOMSelectionSetSubscript") == 0) {
+               gboolean subscript;
+
+               g_variant_get (parameters, "(tb)", &page_id, &subscript);
+
+               editor_page = get_editor_page_or_return_dbus_error (invocation, extension, page_id);
+               if (!editor_page)
+                       goto error;
+
+               e_editor_dom_selection_set_subscript (editor_page, subscript);
+               g_dbus_method_invocation_return_value (invocation, NULL);
+       } else if (g_strcmp0 (method_name, "DOMSelectionSetSuperscript") == 0) {
+               gboolean superscript;
+
+               g_variant_get (parameters, "(tb)", &page_id, &superscript);
+
+               editor_page = get_editor_page_or_return_dbus_error (invocation, extension, page_id);
+               if (!editor_page)
+                       goto error;
+
+               e_editor_dom_selection_set_superscript (editor_page, superscript);
+               g_dbus_method_invocation_return_value (invocation, NULL);
+       } else if (g_strcmp0 (method_name, "DOMSelectionSetUnderline") == 0) {
+               gboolean underline;
+
+               g_variant_get (parameters, "(tb)", &page_id, &underline);
+
+               editor_page = get_editor_page_or_return_dbus_error (invocation, extension, page_id);
+               if (!editor_page)
+                       goto error;
+
+               e_editor_page_set_underline (editor_page, underline);
+               g_dbus_method_invocation_return_value (invocation, NULL);
+       } else if (g_strcmp0 (method_name, "DOMSelectionUnindent") == 0) {
+               g_variant_get (parameters, "(t)", &page_id);
+
+               editor_page = get_editor_page_or_return_dbus_error (invocation, extension, page_id);
+               if (!editor_page)
+                       goto error;
+
+               e_editor_dom_selection_unindent (editor_page);
+               g_dbus_method_invocation_return_value (invocation, NULL);
+       } else if (g_strcmp0 (method_name, "DOMSelectionWrap") == 0) {
+               g_variant_get (parameters, "(t)", &page_id);
+
+               editor_page = get_editor_page_or_return_dbus_error (invocation, extension, page_id);
+               if (!editor_page)
+                       goto error;
+
+               e_editor_dom_selection_wrap (editor_page);
+               g_dbus_method_invocation_return_value (invocation, NULL);
+       } else if (g_strcmp0 (method_name, "DOMGetCaretWord") == 0) {
+               gchar *word;
+
+               g_variant_get (parameters, "(t)", &page_id);
+
+               editor_page = get_editor_page_or_return_dbus_error (invocation, extension, page_id);
+               if (!editor_page)
+                       goto error;
+
+               word = e_editor_dom_get_caret_word (editor_page);
+
+               g_dbus_method_invocation_return_value (
+                       invocation,
+                       g_variant_new (
+                               "(@s)",
+                               g_variant_new_take_string (
+                                       word ? word : g_strdup (""))));
+       } else if (g_strcmp0 (method_name, "DOMInsertSignature") == 0) {
+               gboolean is_html, set_signature_from_message;
+               gboolean check_if_signature_is_changed, ignore_next_signature_change;
+               const gchar *content, *signature_id;
+               gchar *new_signature_id = NULL;
+
+               g_variant_get (
+                       parameters,
+                       "(t&sb&sbbb)",
+                       &page_id,
+                       &content,
+                       &is_html,
+                       &signature_id,
+                       &set_signature_from_message,
+                       &check_if_signature_is_changed,
+                       &ignore_next_signature_change);
+
+               editor_page = get_editor_page_or_return_dbus_error (invocation, extension, page_id);
+               if (!editor_page)
+                       goto error;
+
+               new_signature_id = e_composer_dom_insert_signature (
+                       editor_page,
+                       content,
+                       is_html,
+                       signature_id,
+                       &set_signature_from_message,
+                       &check_if_signature_is_changed,
+                       &ignore_next_signature_change);
+
+               g_dbus_method_invocation_return_value (
+                       invocation,
+                       g_variant_new (
+                               "(sbbb)",
+                               new_signature_id ? new_signature_id : "",
+                               set_signature_from_message,
+                               check_if_signature_is_changed,
+                               ignore_next_signature_change));
+
+               g_free (new_signature_id);
+       } else if (g_strcmp0 (method_name, "DOMSaveDragAndDropHistory") == 0) {
+               g_variant_get (
+                       parameters, "(t)", &page_id);
+
+               editor_page = get_editor_page_or_return_dbus_error (invocation, extension, page_id);
+               if (!editor_page)
+                       goto error;
+
+               e_composer_dom_save_drag_and_drop_history (editor_page);
+
+               g_dbus_method_invocation_return_value (invocation, NULL);
+       } else if (g_strcmp0 (method_name, "DOMCleanAfterDragAndDrop") == 0) {
+               g_variant_get (
+                       parameters, "(t)", &page_id);
+
+               editor_page = get_editor_page_or_return_dbus_error (invocation, extension, page_id);
+               if (!editor_page)
+                       goto error;
+
+               e_composer_dom_clean_after_drag_and_drop (editor_page);
+
+               g_dbus_method_invocation_return_value (invocation, NULL);
+       } else if (g_strcmp0 (method_name, "DOMGetActiveSignatureUid") == 0) {
+               gchar *value;
+
+               g_variant_get (parameters, "(t)", &page_id);
+
+               editor_page = get_editor_page_or_return_dbus_error (invocation, extension, page_id);
+               if (!editor_page)
+                       goto error;
+
+               value = e_composer_dom_get_active_signature_uid (editor_page);
+
+               g_dbus_method_invocation_return_value (
+                       invocation,
+                       g_variant_new (
+                               "(@s)",
+                               g_variant_new_take_string (
+                                       value ? value : g_strdup (""))));
+       } else if (g_strcmp0 (method_name, "DOMGetCaretPosition") == 0) {
+               guint32 value;
+
+               g_variant_get (parameters, "(t)", &page_id);
+
+               editor_page = get_editor_page_or_return_dbus_error (invocation, extension, page_id);
+               if (!editor_page)
+                       goto error;
+
+               value = e_editor_dom_get_caret_position (editor_page);
+
+               g_dbus_method_invocation_return_value (
+                       invocation,
+                       value ? g_variant_new_uint32 (value) : NULL);
+       } else if (g_strcmp0 (method_name, "DOMGetCaretOffset") == 0) {
+               guint32 value;
+
+               g_variant_get (parameters, "(t)", &page_id);
+
+               editor_page = get_editor_page_or_return_dbus_error (invocation, extension, page_id);
+               if (!editor_page)
+                       goto error;
+
+               value = e_editor_dom_get_caret_offset (editor_page);
+
+               g_dbus_method_invocation_return_value (
+                       invocation,
+                       value ? g_variant_new_uint32 (value) : NULL);
+       } else if (g_strcmp0 (method_name, "DOMClearUndoRedoHistory") == 0) {
+               EEditorUndoRedoManager *manager;
+
+               g_variant_get (parameters, "(t)", &page_id);
+
+               editor_page = get_editor_page_or_return_dbus_error (invocation, extension, page_id);
+               if (!editor_page)
+                       goto error;
+
+               manager = e_editor_page_get_undo_redo_manager (editor_page);
+               if (manager)
+                       e_editor_undo_redo_manager_clean_history (manager);
+
+               g_dbus_method_invocation_return_value (invocation, NULL);
+       } else {
+               g_warning ("UNKNOWN METHOD '%s'", method_name);
+       }
+
+       return;
+
+ error:
+       g_warning ("Cannot obtain WebKitWebPage for '%ld'", page_id);
+}
+
+static void
+web_page_gone_cb (gpointer user_data,
+                 GObject *gone_web_page)
+{
+       EEditorWebExtension *extension = user_data;
+       GHashTableIter iter;
+       gpointer key, value;
+
+       g_return_if_fail (E_IS_EDITOR_WEB_EXTENSION (extension));
+
+       g_hash_table_iter_init (&iter, extension->priv->editor_pages);
+       while (g_hash_table_iter_next (&iter, &key, &value)) {
+               if (value == gone_web_page) {
+                       g_hash_table_remove (extension->priv->editor_pages, key);
+                       break;
+               }
+       }
+}
+
+static const GDBusInterfaceVTable interface_vtable = {
+       handle_method_call,
+       NULL,
+       NULL
+};
+
+static void
+e_editor_web_extension_dispose (GObject *object)
+{
+       EEditorWebExtension *extension = E_EDITOR_WEB_EXTENSION (object);
+
+       if (extension->priv->dbus_connection) {
+               g_dbus_connection_unregister_object (
+                       extension->priv->dbus_connection,
+                       extension->priv->registration_id);
+               extension->priv->registration_id = 0;
+               extension->priv->dbus_connection = NULL;
+       }
+
+       g_hash_table_remove_all (extension->priv->editor_pages);
+
+       g_clear_object (&extension->priv->wk_extension);
+
+       /* Chain up to parent's dispose() method. */
+       G_OBJECT_CLASS (e_editor_web_extension_parent_class)->dispose (object);
+}
+
+static void
+e_editor_web_extension_finalize (GObject *object)
+{
+       EEditorWebExtension *extension = E_EDITOR_WEB_EXTENSION (object);
+
+       if (extension->priv->editor_pages) {
+               g_hash_table_destroy (extension->priv->editor_pages);
+               extension->priv->editor_pages = NULL;
+       }
+
+       /* Chain up to parent's finalize() method. */
+       G_OBJECT_CLASS (e_editor_web_extension_parent_class)->finalize (object);
+}
+
+static void
+e_editor_web_extension_class_init (EEditorWebExtensionClass *class)
+{
+       GObjectClass *object_class = G_OBJECT_CLASS (class);
+
+       object_class->dispose = e_editor_web_extension_dispose;
+       object_class->finalize = e_editor_web_extension_finalize;
+
+       g_type_class_add_private (object_class, sizeof(EEditorWebExtensionPrivate));
+}
+
+static void
+e_editor_web_extension_init (EEditorWebExtension *extension)
+{
+       extension->priv = E_EDITOR_WEB_EXTENSION_GET_PRIVATE (extension);
+       extension->priv->editor_pages = g_hash_table_new_full (g_int64_hash, g_int64_equal, g_free, 
g_object_unref);
+}
+
+static gpointer
+e_editor_web_extension_create_instance(gpointer data)
+{
+       return g_object_new (E_TYPE_EDITOR_WEB_EXTENSION, NULL);
+}
+
+EEditorWebExtension *
+e_editor_web_extension_get_default (void)
+{
+       static GOnce once_init = G_ONCE_INIT;
+       return E_EDITOR_WEB_EXTENSION (g_once (&once_init, e_editor_web_extension_create_instance, NULL));
+}
+
+static gboolean
+image_exists_in_cache (const gchar *image_uri)
+{
+       gchar *filename;
+       gchar *hash;
+       gboolean exists = FALSE;
+
+       if (!emd_global_http_cache)
+               return FALSE;
+
+       hash = g_compute_checksum_for_string (G_CHECKSUM_MD5, image_uri, -1);
+       filename = camel_data_cache_get_filename (
+               emd_global_http_cache, "http", hash);
+
+       if (filename != NULL) {
+               struct stat st;
+
+               exists = g_file_test (filename, G_FILE_TEST_EXISTS);
+               if (exists && g_stat (filename, &st) == 0) {
+                       exists = st.st_size != 0;
+               } else {
+                       exists = FALSE;
+               }
+               g_free (filename);
+       }
+
+       g_free (hash);
+
+       return exists;
+}
+
+static void
+redirect_http_uri (EEditorWebExtension *extension,
+                   WebKitWebPage *web_page,
+                   WebKitURIRequest *request)
+{
+       const gchar *uri;
+       gchar *new_uri;
+       SoupURI *soup_uri;
+       gboolean image_exists;
+       EEditorPage *editor_page;
+       EImageLoadingPolicy image_policy;
+
+       editor_page = get_editor_page (extension, webkit_web_page_get_id (web_page));
+       g_return_if_fail (E_IS_EDITOR_PAGE (editor_page));
+
+       uri = webkit_uri_request_get_uri (request);
+
+       /* Check Evolution's cache */
+       image_exists = image_exists_in_cache (uri);
+
+       /* If the URI is not cached and we are not allowed to load it
+        * then redirect to invalid URI, so that webkit would display
+        * a native placeholder for it. */
+       image_policy = e_editor_page_get_image_loading_policy (editor_page);
+       if (!image_exists && !e_editor_page_get_force_image_load (editor_page) &&
+           (image_policy == E_IMAGE_LOADING_POLICY_NEVER)) {
+               webkit_uri_request_set_uri (request, "about:blank");
+               return;
+       }
+
+       new_uri = g_strconcat ("evo-", uri, NULL);
+       soup_uri = soup_uri_new (new_uri);
+       g_free (new_uri);
+
+       new_uri = soup_uri_to_string (soup_uri, FALSE);
+       webkit_uri_request_set_uri (request, new_uri);
+       soup_uri_free (soup_uri);
+
+       g_free (new_uri);
+}
+
+static gboolean
+web_page_send_request_cb (WebKitWebPage *web_page,
+                          WebKitURIRequest *request,
+                          WebKitURIResponse *redirected_response,
+                          EEditorWebExtension *extension)
+{
+       const char *request_uri;
+       const char *page_uri;
+       gboolean uri_is_http;
+
+       request_uri = webkit_uri_request_get_uri (request);
+       page_uri = webkit_web_page_get_uri (web_page);
+
+       /* Always load the main resource. */
+       if (g_strcmp0 (request_uri, page_uri) == 0)
+               return FALSE;
+
+       uri_is_http =
+               g_str_has_prefix (request_uri, "http:") ||
+               g_str_has_prefix (request_uri, "https:") ||
+               g_str_has_prefix (request_uri, "evo-http:") ||
+               g_str_has_prefix (request_uri, "evo-https:");
+
+       if (uri_is_http)
+               redirect_http_uri (extension, web_page, request);
+
+       return FALSE;
+}
+
+static void
+web_page_created_cb (WebKitWebExtension *wk_extension,
+                     WebKitWebPage *web_page,
+                     EEditorWebExtension *extension)
+{
+       EEditorPage *editor_page;
+       guint64 *ppage_id;
+
+       g_return_if_fail (WEBKIT_IS_WEB_PAGE (web_page));
+       g_return_if_fail (E_IS_EDITOR_WEB_EXTENSION (extension));
+
+       ppage_id = g_new (guint64, 1);
+       *ppage_id = webkit_web_page_get_id (web_page);
+
+       editor_page = e_editor_page_new (web_page, extension);
+       g_hash_table_insert (extension->priv->editor_pages, ppage_id, editor_page);
+
+       g_object_weak_ref (G_OBJECT (web_page), web_page_gone_cb, extension);
+
+       g_signal_connect (
+               web_page, "send-request",
+               G_CALLBACK (web_page_send_request_cb), extension);
+}
+
+void
+e_editor_web_extension_initialize (EEditorWebExtension *extension,
+                                  WebKitWebExtension *wk_extension)
+{
+       g_return_if_fail (E_IS_EDITOR_WEB_EXTENSION (extension));
+
+       extension->priv->wk_extension = g_object_ref (wk_extension);
+
+       if (emd_global_http_cache == NULL) {
+               emd_global_http_cache = camel_data_cache_new (
+                       e_get_user_cache_dir (), NULL);
+
+               if (emd_global_http_cache) {
+                       /* cache expiry - 2 hour access, 1 day max */
+                       camel_data_cache_set_expire_age (
+                               emd_global_http_cache, 24 * 60 * 60);
+                       camel_data_cache_set_expire_access (
+                               emd_global_http_cache, 2 * 60 * 60);
+               }
+       }
+
+       g_signal_connect (
+               wk_extension, "page-created",
+               G_CALLBACK (web_page_created_cb), extension);
+}
+
+void
+e_editor_web_extension_dbus_register (EEditorWebExtension *extension,
+                                     GDBusConnection *connection)
+{
+       GError *error = NULL;
+       static GDBusNodeInfo *introspection_data = NULL;
+
+       g_return_if_fail (E_IS_EDITOR_WEB_EXTENSION (extension));
+       g_return_if_fail (G_IS_DBUS_CONNECTION (connection));
+
+       if (!introspection_data) {
+               introspection_data =
+                       g_dbus_node_info_new_for_xml (introspection_xml, NULL);
+
+               extension->priv->registration_id =
+                       g_dbus_connection_register_object (
+                               connection,
+                               E_WEBKIT_EDITOR_WEB_EXTENSION_OBJECT_PATH,
+                               introspection_data->interfaces[0],
+                               &interface_vtable,
+                               extension,
+                               NULL,
+                               &error);
+
+               if (!extension->priv->registration_id) {
+                       g_warning ("Failed to register object: %s\n", error->message);
+                       g_error_free (error);
+               } else {
+                       extension->priv->dbus_connection = connection;
+                       g_object_add_weak_pointer (
+                               G_OBJECT (connection),
+                               (gpointer *) &extension->priv->dbus_connection);
+               }
+       }
+}
+
+GDBusConnection *
+e_editor_web_extension_get_connection (EEditorWebExtension *extension)
+{
+       g_return_val_if_fail (E_IS_EDITOR_WEB_EXTENSION (extension), NULL);
+
+       return extension->priv->dbus_connection;
+}
diff --git a/modules/webkit-editor/web-extension/e-editor-web-extension.h 
b/modules/webkit-editor/web-extension/e-editor-web-extension.h
new file mode 100644
index 0000000..987b3de
--- /dev/null
+++ b/modules/webkit-editor/web-extension/e-editor-web-extension.h
@@ -0,0 +1,82 @@
+/*
+ * e-editor-web-extension.h
+ *
+ * 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; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+#ifndef E_EDITOR_WEB_EXTENSION_H
+#define E_EDITOR_WEB_EXTENSION_H
+
+#include <glib-object.h>
+#include <webkit2/webkit-web-extension.h>
+
+#define E_UTIL_INCLUDE_WITHOUT_WEBKIT
+#include <e-util/e-util.h>
+#undef E_UTIL_INCLUDE_WITHOUT_WEBKIT
+
+#include "e-editor-web-extension-names.h"
+
+/* Standard GObject macros */
+#define E_TYPE_EDITOR_WEB_EXTENSION \
+       (e_editor_web_extension_get_type ())
+#define E_EDITOR_WEB_EXTENSION(obj) \
+       (G_TYPE_CHECK_INSTANCE_CAST \
+       ((obj), E_TYPE_EDITOR_WEB_EXTENSION, EEditorWebExtension))
+#define E_EDITOR_WEB_EXTENSION_CLASS(cls) \
+       (G_TYPE_CHECK_CLASS_CAST \
+       ((cls), E_TYPE_EDITOR_WEB_EXTENSION, EEditorWebExtensionClass))
+#define E_IS_EDITOR_WEB_EXTENSION(obj) \
+       (G_TYPE_CHECK_INSTANCE_TYPE \
+       ((obj), E_TYPE_EDITOR_WEB_EXTENSION))
+#define E_IS_EDITOR_WEB_EXTENSION_CLASS(cls) \
+       (G_TYPE_CHECK_CLASS_TYPE \
+       ((cls), E_TYPE_EDITOR_WEB_EXTENSION))
+#define E_EDITOR_WEB_EXTENSION_GET_CLASS(obj) \
+       (G_TYPE_INSTANCE_GET_CLASS \
+       ((obj), E_TYPE_EDITOR_WEB_EXTENSION, EEditorWebExtensionClass))
+
+G_BEGIN_DECLS
+
+typedef struct _EEditorWebExtension EEditorWebExtension;
+typedef struct _EEditorWebExtensionClass EEditorWebExtensionClass;
+typedef struct _EEditorWebExtensionPrivate EEditorWebExtensionPrivate;
+
+struct _EEditorWebExtension {
+       GObject parent;
+       EEditorWebExtensionPrivate *priv;
+};
+
+struct _EEditorWebExtensionClass
+{
+       GObjectClass parent_class;
+};
+
+GType          e_editor_web_extension_get_type (void) G_GNUC_CONST;
+
+EEditorWebExtension *
+               e_editor_web_extension_get_default
+                                               (void);
+
+void           e_editor_web_extension_initialize
+                                               (EEditorWebExtension *extension,
+                                                WebKitWebExtension *wk_extension);
+void           e_editor_web_extension_dbus_register
+                                               (EEditorWebExtension *extension,
+                                                GDBusConnection *connection);
+GDBusConnection *
+               e_editor_web_extension_get_connection
+                                               (EEditorWebExtension *extension);
+
+#endif /* E_EDITOR_WEB_EXTENSION_H */
diff --git a/modules/web-inspector/Makefile.am b/modules/webkit-inspector/Makefile.am
similarity index 56%
rename from modules/web-inspector/Makefile.am
rename to modules/webkit-inspector/Makefile.am
index 237121e..eedaf12 100644
--- a/modules/web-inspector/Makefile.am
+++ b/modules/webkit-inspector/Makefile.am
@@ -1,24 +1,24 @@
-module_LTLIBRARIES = module-web-inspector.la
+module_LTLIBRARIES = module-webkit-inspector.la
 
-module_web_inspector_la_CPPFLAGS =                     \
+module_webkit_inspector_la_CPPFLAGS =                  \
        $(AM_CPPFLAGS)                                  \
        -I$(top_srcdir)                                 \
-       -DG_LOG_DOMAIN=\"evolution-web-inspector\"      \
+       -DG_LOG_DOMAIN=\"evolution-webkit-inspector\"   \
        $(EVOLUTION_DATA_SERVER_CFLAGS)                 \
        $(GNOME_PLATFORM_CFLAGS)                        \
        $(CODE_COVERAGE_CFLAGS)                         \
        $(NULL)
 
-module_web_inspector_la_SOURCES = \
-       evolution-web-inspector.c
+module_webkit_inspector_la_SOURCES = \
+       evolution-webkit-inspector.c
 
-module_web_inspector_la_LIBADD = \
+module_webkit_inspector_la_LIBADD = \
        $(top_builddir)/e-util/libevolution-util.la     \
        $(EVOLUTION_DATA_SERVER_LIBS)                   \
        $(GNOME_PLATFORM_LIBS)                          \
        $(NULL)
 
-module_web_inspector_la_LDFLAGS = \
+module_webkit_inspector_la_LDFLAGS = \
        -module -avoid-version $(NO_UNDEFINED) $(CODE_COVERAGE_LDFLAGS)
 
 -include $(top_srcdir)/git.mk
diff --git a/modules/webkit-inspector/evolution-webkit-inspector.c 
b/modules/webkit-inspector/evolution-webkit-inspector.c
new file mode 100644
index 0000000..4c9a141
--- /dev/null
+++ b/modules/webkit-inspector/evolution-webkit-inspector.c
@@ -0,0 +1,140 @@
+/*
+ * evolution-webkit-inspector.c
+ *
+ * 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/>.
+ *
+ */
+
+#include <config.h>
+#include <glib/gi18n-lib.h>
+#include <gdk/gdkkeysyms.h>
+
+#include <libebackend/libebackend.h>
+
+#include <e-util/e-util.h>
+
+/* Standard GObject macros */
+#define E_TYPE_WEBKIT_INSPECTOR \
+       (e_webkit_inspector_get_type ())
+#define E_WEBKIT_INSPECTOR(obj) \
+       (G_TYPE_CHECK_INSTANCE_CAST \
+       ((obj), E_TYPE_WEBKIT_INSPECTOR, EWebKitInspector))
+
+/* <Control>+<Shift>+I */
+#define WEBKIT_INSPECTOR_MOD  (GDK_CONTROL_MASK | GDK_SHIFT_MASK)
+#define WEBKIT_INSPECTOR_KEY  (GDK_KEY_I)
+
+#define WEBKIT_INSPECTOR_SHORTCUT_SHOW(event) \
+       ((((event)->state & WEBKIT_INSPECTOR_MOD) == WEBKIT_INSPECTOR_MOD) && \
+       ((event)->keyval == WEBKIT_INSPECTOR_KEY))
+
+typedef struct _EWebKitInspector EWebKitInspector;
+typedef struct _EWebKitInspectorClass EWebKitInspectorClass;
+
+struct _EWebKitInspector {
+       EExtension parent;
+};
+
+struct _EWebKitInspectorClass {
+       EExtensionClass parent_class;
+};
+
+/* Module Entry Points */
+void e_module_load (GTypeModule *type_module);
+void e_module_unload (GTypeModule *type_module);
+
+/* Forward Declarations */
+GType e_webkit_inspector_get_type (void);
+
+G_DEFINE_DYNAMIC_TYPE (EWebKitInspector, e_webkit_inspector, E_TYPE_EXTENSION)
+
+static WebKitWebView *
+webkit_inspector_get_web_view (EWebKitInspector *extension)
+{
+       EExtensible *extensible;
+
+       extensible = e_extension_get_extensible (E_EXTENSION (extension));
+
+       return WEBKIT_WEB_VIEW (extensible);
+}
+
+static gboolean
+webkit_inspector_key_press_event_cb (WebKitWebView *web_view,
+                                  GdkEventKey *event)
+{
+       WebKitWebInspector *inspector;
+       gboolean handled = FALSE;
+
+       inspector = webkit_web_view_get_inspector (web_view);
+
+       if (WEBKIT_INSPECTOR_SHORTCUT_SHOW (event)) {
+               webkit_web_inspector_show (inspector);
+               handled = TRUE;
+       }
+
+       return handled;
+}
+
+static void
+webkit_inspector_constructed (GObject *object)
+{
+       EWebKitInspector *extension;
+       WebKitWebView *web_view;
+       WebKitSettings *settings;
+
+       /* Chain up to parent's method. */
+       G_OBJECT_CLASS (e_webkit_inspector_parent_class)->constructed (object);
+
+       extension = E_WEBKIT_INSPECTOR (object);
+       web_view = webkit_inspector_get_web_view (extension);
+       settings = webkit_web_view_get_settings (web_view);
+       webkit_settings_set_enable_developer_extras (settings, TRUE);
+
+       g_signal_connect (
+               web_view, "key-press-event",
+               G_CALLBACK (webkit_inspector_key_press_event_cb), NULL);
+}
+
+static void
+e_webkit_inspector_class_init (EWebKitInspectorClass *class)
+{
+       GObjectClass *object_class;
+       EExtensionClass *extension_class;
+
+       object_class = G_OBJECT_CLASS (class);
+       object_class->constructed = webkit_inspector_constructed;
+
+       extension_class = E_EXTENSION_CLASS (class);
+       extension_class->extensible_type = WEBKIT_TYPE_WEB_VIEW;
+}
+
+static void
+e_webkit_inspector_class_finalize (EWebKitInspectorClass *class)
+{
+}
+
+static void
+e_webkit_inspector_init (EWebKitInspector *extension)
+{
+}
+
+G_MODULE_EXPORT void
+e_module_load (GTypeModule *type_module)
+{
+       e_webkit_inspector_register_type (type_module);
+}
+
+G_MODULE_EXPORT void
+e_module_unload (GTypeModule *type_module)
+{
+}
diff --git a/plugins/external-editor/external-editor.c b/plugins/external-editor/external-editor.c
index 2709c84..f349b5f 100644
--- a/plugins/external-editor/external-editor.c
+++ b/plugins/external-editor/external-editor.c
@@ -150,16 +150,16 @@ enable_disable_composer (EMsgComposer *composer,
                          gboolean enable)
 {
        EHTMLEditor *editor;
-       EHTMLEditorView *view;
+       EContentEditor *cnt_editor;
        GtkAction *action;
        GtkActionGroup *action_group;
 
        g_return_if_fail (E_IS_MSG_COMPOSER (composer));
 
        editor = e_msg_composer_get_editor (composer);
-       view = e_html_editor_get_view (editor);
+       cnt_editor = e_html_editor_get_content_editor (editor);
 
-       webkit_web_view_set_editable (WEBKIT_WEB_VIEW (view), enable);
+       e_content_editor_set_editable (cnt_editor, enable);
 
        action = E_HTML_EDITOR_ACTION_EDIT_MENU (editor);
        gtk_action_set_sensitive (action, enable);
@@ -192,20 +192,20 @@ update_composer_text (GArray *array)
 {
        EMsgComposer *composer;
        EHTMLEditor *editor;
-       EHTMLEditorView *view;
+       EContentEditor *cnt_editor;
        gchar *text;
 
        composer = g_array_index (array, gpointer, 0);
        text = g_array_index (array, gpointer, 1);
 
        editor = e_msg_composer_get_editor (composer);
-       view = e_html_editor_get_view (editor);
+       cnt_editor = e_html_editor_get_content_editor (editor);
 
        e_msg_composer_set_body_text (composer, text, FALSE);
 
        enable_composer (composer);
 
-       e_html_editor_view_set_changed (view, TRUE);
+       e_content_editor_set_changed (cnt_editor, TRUE);
 
        g_free (text);
 
@@ -239,117 +239,19 @@ numlines (const gchar *text,
        gint ctr = 0;
        gint lineno = 0;
 
-       while (text && *text && ctr < pos) {
-               if (*text == '\n') {
-                       /* Because the get_caret_position is returning a position
-                        * without new lines, we can't increment the counter when
-                        * we hit the new line. */
+       while (text && *text && ctr <= pos) {
+               if (*text == '\n')
                        lineno++;
-               } else {
-                       ctr++;
-               }
-               text++;
 
+               text++;
+               ctr++;
        }
 
-       if (lineno > 0)
+       if (lineno > 0) {
                lineno++;
-
-       return lineno;
-}
-
-static gint
-get_caret_offset (EHTMLEditorView *view)
-{
-       gint ret_val;
-       gchar *text;
-       WebKitDOMDocument *document;
-       WebKitDOMDOMWindow *dom_window;
-       WebKitDOMDOMSelection *dom_selection;
-       WebKitDOMNode *anchor;
-       WebKitDOMRange *range;
-
-       document = webkit_web_view_get_dom_document (WEBKIT_WEB_VIEW (view));
-       dom_window = webkit_dom_document_get_default_view (document);
-       dom_selection = webkit_dom_dom_window_get_selection (dom_window);
-       g_object_unref (dom_window);
-
-       if (webkit_dom_dom_selection_get_range_count (dom_selection) < 1) {
-               g_object_unref (dom_selection);
-               return 0;
        }
 
-       webkit_dom_dom_selection_collapse_to_start (dom_selection, NULL);
-       /* Select the text from the current caret position to the beginning of the line. */
-       webkit_dom_dom_selection_modify (dom_selection, "extend", "left", "lineBoundary");
-
-       range = webkit_dom_dom_selection_get_range_at (dom_selection, 0, NULL);
-       anchor = webkit_dom_dom_selection_get_anchor_node (dom_selection);
-       text = webkit_dom_range_to_string (range, NULL);
-       ret_val = strlen (text);
-       g_free (text);
-
-       webkit_dom_dom_selection_collapse_to_end (dom_selection, NULL);
-
-       /* In the plain text mode we need to increase the return value by 2 per
-        * citation level because of "> ". */
-       if (!e_html_editor_view_get_html_mode (view)) {
-               WebKitDOMNode *parent = anchor;
-
-               while (parent && !WEBKIT_DOM_IS_HTML_BODY_ELEMENT (parent)) {
-                       if (WEBKIT_DOM_IS_HTML_QUOTE_ELEMENT (parent) &&
-                           element_has_class (WEBKIT_DOM_ELEMENT (parent), "-x-evo-plaintext-quoted"))
-                               ret_val += 2;
-
-                       parent = webkit_dom_node_get_parent_node (parent);
-               }
-       }
-
-       g_object_unref (range);
-       g_object_unref (dom_selection);
-
-       return ret_val;
-}
-
-static gint
-get_caret_position (EHTMLEditorView *view)
-{
-       gint ret_val;
-       gchar *text;
-       WebKitDOMDocument *document;
-       WebKitDOMHTMLElement *body;
-       WebKitDOMDOMWindow *dom_window;
-       WebKitDOMDOMSelection *dom_selection;
-       WebKitDOMRange *range, *range_clone;
-
-       document = webkit_web_view_get_dom_document (WEBKIT_WEB_VIEW (view));
-       dom_window = webkit_dom_document_get_default_view (document);
-       dom_selection = webkit_dom_dom_window_get_selection (dom_window);
-       g_object_unref (dom_window);
-
-       if (webkit_dom_dom_selection_get_range_count (dom_selection) < 1) {
-               g_object_unref (dom_selection);
-               return 0;
-       }
-
-       range = webkit_dom_dom_selection_get_range_at (dom_selection, 0, NULL);
-       range_clone = webkit_dom_range_clone_range (range, NULL);
-
-       body = webkit_dom_document_get_body (document);
-       /* Select the text from the beginning of the body to the current caret. */
-       webkit_dom_range_set_start_before (
-               range_clone, webkit_dom_node_get_first_child (WEBKIT_DOM_NODE (body)), NULL);
-
-       /* This is returning a text without new lines! */
-       text = webkit_dom_range_to_string (range_clone, NULL);
-       ret_val = strlen (text);
-       g_free (text);
-
-       g_object_unref (range_clone);
-       g_object_unref (range);
-       g_object_unref (dom_selection);
-
-       return ret_val;
+       return lineno;
 }
 
 static gboolean external_editor_running = FALSE;
@@ -365,10 +267,10 @@ external_editor_thread (gpointer user_data)
        gchar *editor_cmd_line = NULL, *editor_cmd = NULL, *content;
        gint fd, position = -1, offset = -1;
        EHTMLEditor *editor;
-       EHTMLEditorView *view;
+       EContentEditor *cnt_editor;
 
        editor = e_msg_composer_get_editor (composer);
-       view = e_html_editor_get_view (editor);
+       cnt_editor = e_html_editor_get_content_editor (editor);
 
        /* prefix temp files with evo so .*vimrc can be setup to recognize them */
        fd = g_file_open_tmp ("evoXXXXXX", &filename, NULL);
@@ -377,8 +279,13 @@ external_editor_thread (gpointer user_data)
                d (printf ("\n\aTemporary-file Name is : [%s] \n\a", filename));
 
                /* Push the text (if there is one) from the composer to the file */
-               content = e_html_editor_view_get_text_plain (view);
-               g_file_set_contents (filename, content, strlen (content), NULL);
+               content = e_content_editor_get_content (
+                       cnt_editor,
+                       E_CONTENT_EDITOR_GET_TEXT_PLAIN |
+                       E_CONTENT_EDITOR_GET_PROCESSED,
+                       NULL, NULL);
+               if (content && *content)
+                       g_file_set_contents (filename, content, strlen (content), NULL);
        } else {
                struct run_error_dialog_data *data;
 
@@ -406,14 +313,14 @@ external_editor_thread (gpointer user_data)
        g_object_unref (settings);
 
        if (g_strrstr (editor_cmd, "vim") != NULL &&
-           ((position = get_caret_position (view)) > 0)) {
+           ((position = e_content_editor_get_caret_position (cnt_editor)) > 0)) {
                gchar *tmp = editor_cmd;
                gint lineno;
                gboolean set_nofork;
 
                set_nofork = g_strrstr (editor_cmd, "gvim") != NULL;
 
-               offset = get_caret_offset (view);
+               offset = e_content_editor_get_caret_offset (cnt_editor);
                /* Increment by 1 so that entering vim insert mode places you
                 * in the same entry position you were at in the html. */
                offset++;
@@ -500,7 +407,7 @@ finished:
 static void launch_editor (GtkAction *action, EMsgComposer *composer)
 {
        EHTMLEditor *editor;
-       EHTMLEditorView *view;
+       EContentEditor *cnt_editor;
 
        d (printf ("\n\nexternal_editor plugin is launched \n\n"));
 
@@ -510,9 +417,9 @@ static void launch_editor (GtkAction *action, EMsgComposer *composer)
        }
 
        editor = e_msg_composer_get_editor (composer);
-       view = e_html_editor_get_view (editor);
+       cnt_editor = e_html_editor_get_content_editor (editor);
 
-       e_html_editor_view_clear_history (view);
+       e_content_editor_clear_undo_redo_history (cnt_editor);
        disable_composer (composer);
 
        g_mutex_lock (&external_editor_running_lock);
@@ -596,10 +503,10 @@ e_plugin_ui_init (GtkUIManager *manager,
                   EMsgComposer *composer)
 {
        EHTMLEditor *editor;
-       EHTMLEditorView *view;
+       EContentEditor *cnt_editor;
 
        editor = e_msg_composer_get_editor (composer);
-       view = e_html_editor_get_view (editor);
+       cnt_editor = e_html_editor_get_content_editor (editor);
 
        /* Add actions to the "composer" action group. */
        gtk_action_group_add_actions (
@@ -607,11 +514,11 @@ e_plugin_ui_init (GtkUIManager *manager,
                entries, G_N_ELEMENTS (entries), composer);
 
        g_signal_connect (
-               view, "key_press_event",
+               cnt_editor, "key_press_event",
                G_CALLBACK (key_press_cb), composer);
 
        g_signal_connect (
-               view, "delete-event",
+               cnt_editor, "delete-event",
                G_CALLBACK (delete_cb), composer);
 
        return TRUE;
diff --git a/plugins/mail-to-task/mail-to-task.c b/plugins/mail-to-task/mail-to-task.c
index 7774044..ef0fda4 100644
--- a/plugins/mail-to-task/mail-to-task.c
+++ b/plugins/mail-to-task/mail-to-task.c
@@ -799,7 +799,7 @@ typedef struct {
        ECalClientSourceType source_type;
        CamelFolder *folder;
        GPtrArray *uids;
-       gchar *selected_text;
+       const gchar *selected_text;
        gboolean with_attendees;
 }AsyncData;
 
@@ -1009,7 +1009,6 @@ do_mail_to_event (AsyncData *data)
 
        g_object_unref (data->client_cache);
        g_object_unref (data->source);
-       g_free (data->selected_text);
        g_free (data);
        data = NULL;
 
@@ -1045,24 +1044,21 @@ text_contains_nonwhitespace (const gchar *text,
        return p - text < len - 1 && c != 0;
 }
 
-/* should be freed with g_free after done with it */
-static gchar *
+static const gchar *
 get_selected_text (EMailReader *reader)
 {
        EMailDisplay *display;
-       gchar *text = NULL;
+       const gchar *text = NULL;
 
        display = e_mail_reader_get_mail_display (reader);
 
        if (!e_web_view_is_selection_active (E_WEB_VIEW (display)))
                return NULL;
 
-       text = e_mail_display_get_selection_plain_text (display);
+       text = e_mail_display_get_selection_plain_text_sync (display, NULL, NULL);
 
-       if (text == NULL || !text_contains_nonwhitespace (text, strlen (text))) {
-               g_free (text);
+       if (text == NULL || !text_contains_nonwhitespace (text, strlen (text)))
                return NULL;
-       }
 
        return text;
 }
diff --git a/plugins/mailing-list-actions/mailing-list-actions.c 
b/plugins/mailing-list-actions/mailing-list-actions.c
index 498a94d..58f58f6 100644
--- a/plugins/mailing-list-actions/mailing-list-actions.c
+++ b/plugins/mailing-list-actions/mailing-list-actions.c
@@ -116,6 +116,44 @@ async_context_free (AsyncContext *context)
        g_slice_free (AsyncContext, context);
 }
 
+typedef struct _SendMessageData {
+       gchar *url;
+       gchar *uid;
+} SendMessageData;
+
+static void
+send_message_composer_created_cb (GObject *source_object,
+                                 GAsyncResult *result,
+                                 gpointer user_data)
+{
+       SendMessageData *smd = user_data;
+       EMsgComposer *composer;
+       GError *error = NULL;
+
+       g_return_if_fail (smd != NULL);
+
+       composer = e_msg_composer_new_finish (result, &error);
+       if (error) {
+               g_warning ("%s: Failed to create msg composer: %s", G_STRFUNC, error->message);
+               g_clear_error (&error);
+       } else {
+               EComposerHeaderTable *table;
+
+               /* directly send message */
+               e_msg_composer_setup_from_url (composer, smd->url);
+               table = e_msg_composer_get_header_table (composer);
+
+               if (smd->uid)
+                       e_composer_header_table_set_identity_uid (table, smd->uid);
+
+               e_msg_composer_send (composer);
+       }
+
+       g_free (smd->url);
+       g_free (smd->uid);
+       g_free (smd);
+}
+
 static void
 emla_list_action_cb (CamelFolder *folder,
                      GAsyncResult *result,
@@ -124,7 +162,6 @@ emla_list_action_cb (CamelFolder *folder,
        const gchar *header = NULL, *headerpos;
        gchar *end, *url = NULL;
        gint t;
-       EMsgComposer *composer;
        EAlertSink *alert_sink;
        CamelMimeMessage *message;
        gint send_message_response;
@@ -239,15 +276,13 @@ emla_list_action_cb (CamelFolder *folder,
                                        url, NULL);
 
                        if (send_message_response == GTK_RESPONSE_YES) {
-                               EComposerHeaderTable *table;
+                               SendMessageData *smd;
 
-                               /* directly send message */
-                               composer = e_msg_composer_new_from_url (shell, url);
-                               table = e_msg_composer_get_header_table (composer);
+                               smd = g_new0 (SendMessageData, 1);
+                               smd->url = g_strdup (url);
+                               smd->uid = g_strdup (uid);
 
-                               if (uid != NULL)
-                                       e_composer_header_table_set_identity_uid (table, uid);
-                               e_msg_composer_send (composer);
+                               e_msg_composer_new (shell, send_message_composer_created_cb, smd);
                        } else if (send_message_response == GTK_RESPONSE_NO) {
                                /* show composer */
                                em_utils_compose_new_message_with_mailto (shell, url, folder);
diff --git a/plugins/templates/templates.c b/plugins/templates/templates.c
index 57229dd..3f98a6e 100644
--- a/plugins/templates/templates.c
+++ b/plugins/templates/templates.c
@@ -51,6 +51,7 @@ struct _AsyncContext {
        EActivity *activity;
        EMailReader *reader;
        CamelMimeMessage *message;
+       CamelMimeMessage *template;
        CamelFolder *template_folder;
        gchar *source_folder_uri;
        gchar *message_uid;
@@ -123,17 +124,11 @@ static gboolean plugin_enabled;
 static void
 async_context_free (AsyncContext *context)
 {
-       if (context->activity != NULL)
-               g_object_unref (context->activity);
-
-       if (context->reader != NULL)
-               g_object_unref (context->reader);
-
-       if (context->message != NULL)
-               g_object_unref (context->message);
-
-       if (context->template_folder != NULL)
-               g_object_unref (context->template_folder);
+       g_clear_object (&context->activity);
+       g_clear_object (&context->reader);
+       g_clear_object (&context->message);
+       g_clear_object (&context->template);
+       g_clear_object (&context->template_folder);
 
        g_free (context->source_folder_uri);
        g_free (context->message_uid);
@@ -846,10 +841,11 @@ find_template_part_in_multipart (CamelMultipart *multipart,
 }
 
 static void
-create_new_message (CamelFolder *folder,
-                    GAsyncResult *result,
-                    AsyncContext *context)
+create_new_message_composer_created_cb (GObject *source_object,
+                                       GAsyncResult *result,
+                                       gpointer user_data)
 {
+       AsyncContext *context = user_data;
        EAlertSink *alert_sink;
        CamelMimeMessage *new;
        CamelMimeMessage *message;
@@ -859,24 +855,26 @@ create_new_message (CamelFolder *folder,
        struct _camel_header_raw *header;
        EMailBackend *backend;
        EMailSession *session;
-       EShell *shell;
        const gchar *message_uid;
        EMsgComposer *composer;
        GError *error = NULL;
        CamelMimePart *template_part = NULL;
+       CamelFolder *folder;
+
+       g_return_if_fail (context != NULL);
 
        alert_sink = e_activity_get_alert_sink (context->activity);
 
-       template = camel_folder_get_message_finish (folder, result, &error);
+       composer = e_msg_composer_new_finish (result, &error);
 
        if (e_activity_handle_cancellation (context->activity, error)) {
-               g_warn_if_fail (template == NULL);
+               g_warn_if_fail (context->template == NULL);
                async_context_free (context);
                g_error_free (error);
                return;
 
        } else if (error != NULL) {
-               g_warn_if_fail (template == NULL);
+               g_warn_if_fail (context->template == NULL);
                e_alert_submit (
                        alert_sink, "mail:no-retrieve-message",
                        error->message, NULL);
@@ -885,17 +883,16 @@ create_new_message (CamelFolder *folder,
                return;
        }
 
-       g_return_if_fail (CAMEL_IS_MIME_MESSAGE (template));
+       g_return_if_fail (E_IS_MSG_COMPOSER (composer));
 
        message = context->message;
        message_uid = context->message_uid;
+       template = context->template;
 
        backend = e_mail_reader_get_backend (context->reader);
        session = e_mail_backend_get_session (backend);
-       shell = e_shell_backend_get_shell (E_SHELL_BACKEND (backend));
 
-       folder = e_mail_session_get_local_folder (
-               session, E_MAIL_LOCAL_FOLDER_TEMPLATES);
+       folder = e_mail_session_get_local_folder (session, E_MAIL_LOCAL_FOLDER_TEMPLATES);
 
        new = camel_mime_message_new ();
        new_multipart = camel_multipart_new ();
@@ -989,14 +986,12 @@ create_new_message (CamelFolder *folder,
                        template, CAMEL_RECIPIENT_TYPE_BCC));
 
        /* Create the composer */
-       composer = em_utils_edit_message (
-               shell, folder, new, message_uid, TRUE);
+       em_utils_edit_message (composer, folder, new, message_uid, TRUE);
        if (composer && context->source_folder_uri && context->message_uid)
                e_msg_composer_set_source_headers (
                        composer, context->source_folder_uri,
                        context->message_uid, CAMEL_MESSAGE_ANSWERED | CAMEL_MESSAGE_SEEN);
 
-       g_object_unref (template);
        g_object_unref (new_multipart);
        g_object_unref (new);
 
@@ -1004,6 +999,51 @@ create_new_message (CamelFolder *folder,
 }
 
 static void
+create_new_message (GObject *source_object,
+                    GAsyncResult *result,
+                    gpointer user_data)
+{
+       AsyncContext *context = user_data;
+       EAlertSink *alert_sink;
+       EMailBackend *backend;
+       EShell *shell;
+       CamelFolder *folder;
+       GError *error = NULL;
+
+       g_return_if_fail (CAMEL_IS_FOLDER (source_object));
+       g_return_if_fail (context != NULL);
+
+       folder = CAMEL_FOLDER (source_object);
+
+       alert_sink = e_activity_get_alert_sink (context->activity);
+
+       context->template = camel_folder_get_message_finish (folder, result, &error);
+
+       if (e_activity_handle_cancellation (context->activity, error)) {
+               g_warn_if_fail (context->template == NULL);
+               async_context_free (context);
+               g_error_free (error);
+               return;
+
+       } else if (error != NULL) {
+               g_warn_if_fail (context->template == NULL);
+               e_alert_submit (
+                       alert_sink, "mail:no-retrieve-message",
+                       error->message, NULL);
+               async_context_free (context);
+               g_error_free (error);
+               return;
+       }
+
+       g_return_if_fail (CAMEL_IS_MIME_MESSAGE (context->template));
+
+       backend = e_mail_reader_get_backend (context->reader);
+       shell = e_shell_backend_get_shell (E_SHELL_BACKEND (backend));
+
+       e_msg_composer_new (shell, create_new_message_composer_created_cb, context);
+}
+
+static void
 template_got_source_message (CamelFolder *folder,
                              GAsyncResult *result,
                              AsyncContext *context)
@@ -1044,8 +1084,7 @@ template_got_source_message (CamelFolder *folder,
                context->template_folder,
                context->template_message_uid,
                G_PRIORITY_DEFAULT, cancellable,
-               (GAsyncReadyCallback) create_new_message,
-               context);
+               create_new_message, context);
 }
 
 static void
diff --git a/po/POTFILES.in b/po/POTFILES.in
index b536530..218cb2d 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -145,6 +145,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
@@ -213,7 +214,6 @@ e-util/e-html-editor-replace-dialog.c
 e-util/e-html-editor-spell-check-dialog.c
 e-util/e-html-editor-table-dialog.c
 e-util/e-html-editor-text-dialog.c
-e-util/e-html-editor-view.c
 e-util/e-image-chooser.c
 e-util/e-image-chooser-dialog.c
 e-util/e-import-assistant.c
@@ -463,7 +463,7 @@ modules/text-highlight/languages.c
 modules/vcard-inline/e-mail-formatter-vcard.c
 modules/vcard-inline/e-mail-parser-vcard.c
 modules/vcard-inline/e-mail-part-vcard.c
-modules/web-inspector/evolution-web-inspector.c
+modules/webkit-inspector/evolution-webkit-inspector.c
 plugins/attachment-reminder/attachment-reminder.c
 plugins/attachment-reminder/org-gnome-attachment-reminder.error.xml
 plugins/attachment-reminder/org-gnome-evolution-attachment-reminder.eplug.xml
diff --git a/shell/e-shell.c b/shell/e-shell.c
index 30c4670..c3d0be6 100644
--- a/shell/e-shell.c
+++ b/shell/e-shell.c
@@ -1628,13 +1628,13 @@ shell_initable_init (GInitable *initable,
        /* Configure WebKit's default SoupSession. */
 
        proxy_source = e_source_registry_ref_builtin_proxy (registry);
-
+/* FIXME WK2
        g_object_set (
                webkit_get_default_session (),
                SOUP_SESSION_PROXY_RESOLVER,
                G_PROXY_RESOLVER (proxy_source),
                NULL);
-
+*/
        g_object_unref (proxy_source);
        g_object_unref (registry);
 
diff --git a/shell/main.c b/shell/main.c
index 1083c7d..f472b2c 100644
--- a/shell/main.c
+++ b/shell/main.c
@@ -52,7 +52,7 @@
 #include <libxml/parser.h>
 #include <libxml/tree.h>
 
-#include <webkit/webkit.h>
+#include <webkit2/webkit2.h>
 
 #include "e-shell.h"
 #include "e-shell-migrate.h"
@@ -569,6 +569,7 @@ main (gint argc,
                handle_term_signal, NULL, NULL);
 #endif
 
+       e_util_init_main_thread (NULL);
        e_passwords_init ();
 
        gtk_window_set_default_icon_name ("evolution");
@@ -592,6 +593,7 @@ main (gint argc,
        g_object_unref (settings);
 #endif
 
+       /* FIXME WK2 - Look if we still need this it looks like it's not. */
        /* Workaround https://bugzilla.gnome.org/show_bug.cgi?id=683548 */
        if (!quit)
                g_type_ensure (WEBKIT_TYPE_WEB_VIEW);
diff --git a/ui/evolution-mail.ui b/ui/evolution-mail.ui
index 7d95d73..322c83b 100644
--- a/ui/evolution-mail.ui
+++ b/ui/evolution-mail.ui
@@ -19,6 +19,7 @@
       <placeholder name='view-custom-menus'>
         <menu action='mail-preview-menu'>
           <menuitem action='mail-preview'/>
+          <menuitem action='mail-attachment-bar'/>
           <separator/>
           <menuitem action='mail-view-classic'/>
           <menuitem action='mail-view-vertical'/>
diff --git a/web-extensions/Makefile.am b/web-extensions/Makefile.am
new file mode 100644
index 0000000..07854c2
--- /dev/null
+++ b/web-extensions/Makefile.am
@@ -0,0 +1,44 @@
+webextensions_LTLIBRARIES = libewebextension.la libedomutils.la
+
+libedomutils_la_SOURCES =                              \
+       e-dom-utils.h                                   \
+       e-dom-utils.c
+
+libedomutils_la_CPPFLAGS =                             \
+       $(AM_CPPFLAGS)                                  \
+       -I$(top_srcdir)                                 \
+       -DEVOLUTION_IMAGESDIR=\""$(imagesdir)"\"        \
+       $(EVOLUTION_DATA_SERVER_CFLAGS)                 \
+       $(GNOME_PLATFORM_CFLAGS)
+
+libedomutils_la_LIBADD =                               \
+       $(top_builddir)/e-util/libevolution-util.la     \
+       $(EVOLUTION_DATA_SERVER_LIBS)                   \
+       $(GNOME_PLATFORM_LIBS)
+
+libewebextension_la_SOURCES =                          \
+       e-web-extension.h                               \
+       e-web-extension-names.h                         \
+       e-dom-utils.h                                   \
+       e-web-extension.c                               \
+       e-web-extension-main.c                          \
+       e-dom-utils.c
+
+libewebextension_la_CPPFLAGS =                         \
+       $(AM_CPPFLAGS)                                  \
+       -I$(top_srcdir)                                 \
+       -DEVOLUTION_IMAGESDIR=\""$(imagesdir)"\"        \
+       $(EVOLUTION_DATA_SERVER_CFLAGS)                 \
+       $(GNOME_PLATFORM_CFLAGS)                        \
+       $(WEB_EXTENSIONS_CFLAGS)
+
+libewebextension_la_LIBADD =                           \
+       $(top_builddir)/e-util/libevolution-util.la     \
+       $(EVOLUTION_DATA_SERVER_LIBS)                   \
+       $(GNOME_PLATFORM_LIBS)                          \
+       $(WEB_EXTENSIONS_LIBS)
+
+libewebextension_la_LDFLAGS =                          \
+       -module -avoid-version -no-undefined
+
+-include $(top_srcdir)/git.mk
diff --git a/web-extensions/e-dom-utils.c b/web-extensions/e-dom-utils.c
new file mode 100644
index 0000000..f671f77
--- /dev/null
+++ b/web-extensions/e-dom-utils.c
@@ -0,0 +1,2022 @@
+/*
+ * e-dom-utils.c
+ *
+ * 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; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <string.h>
+
+#define WEBKIT_DOM_USE_UNSTABLE_API
+#include <webkitdom/WebKitDOMDOMSelection.h>
+#include <webkitdom/WebKitDOMDOMWindowUnstable.h>
+#include <webkitdom/WebKitDOMHTMLElementUnstable.h>
+
+#include "e-web-extension.h"
+#include "e-web-extension-names.h"
+
+#include "e-dom-utils.h"
+
+void
+e_dom_utils_replace_local_image_links (WebKitDOMDocument *document)
+{
+       gint ii, length;
+       WebKitDOMNodeList *list = NULL;
+
+       list = webkit_dom_document_query_selector_all (
+               document, "img[src^=\"file://\"]", NULL);
+       length = webkit_dom_node_list_get_length (list);
+
+       for (ii = 0; ii < length; ii++) {
+               gchar *src, *new_src;
+               WebKitDOMHTMLImageElement *img;
+
+               img = WEBKIT_DOM_HTML_IMAGE_ELEMENT (
+                       webkit_dom_node_list_item (list, ii));
+               src = webkit_dom_html_image_element_get_src (img);
+
+               /* this forms "evo-file://", which can be loaded,
+                * while "file://" cannot be, due to WebKit policy */
+               new_src = g_strconcat ("evo-", src, NULL);
+               webkit_dom_html_image_element_set_src (img, new_src);
+               g_free (new_src);
+               g_free (src);
+               g_object_unref (img);
+       }
+       g_clear_object (&list);
+
+       list = webkit_dom_document_query_selector_all (
+               document, "iframe", NULL);
+       length = webkit_dom_node_list_get_length (list);
+       for (ii = 0; ii < length; ii++) {
+               WebKitDOMDocument *content_document;
+               WebKitDOMHTMLIFrameElement *iframe;
+
+               iframe = WEBKIT_DOM_HTML_IFRAME_ELEMENT (
+                       webkit_dom_node_list_item (list, ii));
+
+               content_document =
+                       webkit_dom_html_iframe_element_get_content_document (iframe);
+
+               if (content_document && WEBKIT_DOM_IS_DOCUMENT (content_document))
+                       e_dom_utils_replace_local_image_links (content_document);
+               g_object_unref (iframe);
+       }
+       g_clear_object (&list);
+}
+
+gboolean
+e_dom_utils_document_has_selection (WebKitDOMDocument *document)
+{
+       gboolean ret_val = FALSE;
+       WebKitDOMDOMWindow *dom_window = NULL;
+       WebKitDOMDOMSelection *dom_selection = NULL;
+
+       if (!document)
+               return FALSE;
+
+       dom_window = webkit_dom_document_get_default_view (document);
+       if (!dom_window)
+               goto out;
+
+       dom_selection = webkit_dom_dom_window_get_selection (dom_window);
+       if (!WEBKIT_DOM_IS_DOM_SELECTION (dom_selection))
+               goto out;
+
+       if (webkit_dom_dom_selection_get_is_collapsed (dom_selection))
+               goto out;
+
+       ret_val = TRUE;
+ out:
+       g_clear_object (&dom_window);
+       g_clear_object (&dom_selection);
+
+       if (!ret_val) {
+               WebKitDOMHTMLCollection *frames = NULL;
+               gulong ii, length;
+
+               frames = webkit_dom_document_get_elements_by_tag_name_as_html_collection (document, "iframe");
+               length = webkit_dom_html_collection_get_length (frames);
+               for (ii = 0; ii < length; ii++) {
+                       WebKitDOMNode *node;
+                       WebKitDOMDocument *content_document;
+
+                       node = webkit_dom_html_collection_item (frames, ii);
+                       content_document = webkit_dom_html_iframe_element_get_content_document (
+                               WEBKIT_DOM_HTML_IFRAME_ELEMENT (node));
+
+                       if ((ret_val = e_dom_utils_document_has_selection (content_document))) {
+                               g_object_unref (node);
+                               break;
+                       }
+
+                       g_object_unref (node);
+               }
+
+               g_clear_object (&frames);
+       }
+
+       return ret_val;
+}
+
+
+gchar *
+e_dom_utils_get_document_content_html (WebKitDOMDocument *document)
+{
+       WebKitDOMElement *element;
+
+       element = webkit_dom_document_get_document_element (document);
+
+       return webkit_dom_element_get_outer_html (element);
+}
+
+static gboolean
+element_is_in_pre_tag (WebKitDOMNode *node)
+{
+       WebKitDOMElement *element;
+
+       if (!node)
+               return FALSE;
+
+       while (element = webkit_dom_node_get_parent_element (node), element) {
+               node = WEBKIT_DOM_NODE (element);
+
+               if (WEBKIT_DOM_IS_HTML_PRE_ELEMENT (element)) {
+                       return TRUE;
+               } else if (WEBKIT_DOM_IS_HTML_IFRAME_ELEMENT (element)) {
+                       break;
+               }
+       }
+
+       return FALSE;
+}
+
+static gchar *
+get_frame_selection_html (WebKitDOMElement *iframe)
+{
+       WebKitDOMDocument *content_document;
+       WebKitDOMDOMWindow *dom_window = NULL;
+       WebKitDOMDOMSelection *dom_selection = NULL;
+       WebKitDOMHTMLCollection *frames = NULL;
+       gulong ii, length;
+
+       content_document = webkit_dom_html_iframe_element_get_content_document (
+               WEBKIT_DOM_HTML_IFRAME_ELEMENT (iframe));
+
+       if (!content_document)
+               return NULL;
+
+       dom_window = webkit_dom_document_get_default_view (content_document);
+       dom_selection = webkit_dom_dom_window_get_selection (dom_window);
+       g_clear_object (&dom_window);
+       if (dom_selection && (webkit_dom_dom_selection_get_range_count (dom_selection) > 0)) {
+               WebKitDOMRange *range = NULL;
+               WebKitDOMElement *element;
+               WebKitDOMDocumentFragment *fragment;
+
+               range = webkit_dom_dom_selection_get_range_at (dom_selection, 0, NULL);
+               if (range != NULL) {
+                       gchar *inner_html;
+                       WebKitDOMNode *node;
+
+                       fragment = webkit_dom_range_clone_contents (
+                               range, NULL);
+
+                       element = webkit_dom_document_create_element (
+                               content_document, "DIV", NULL);
+                       webkit_dom_node_append_child (
+                               WEBKIT_DOM_NODE (element),
+                               WEBKIT_DOM_NODE (fragment), NULL);
+
+                       inner_html = webkit_dom_element_get_inner_html (element);
+
+                       node = webkit_dom_range_get_start_container (range, NULL);
+                       if (element_is_in_pre_tag (node)) {
+                               gchar *tmp = inner_html;
+                               inner_html = g_strconcat ("<pre>", tmp, "</pre>", NULL);
+                               g_free (tmp);
+                       }
+
+                       g_clear_object (&range);
+                       g_clear_object (&dom_selection);
+                       return inner_html;
+               }
+       }
+
+       g_clear_object (&dom_selection);
+
+       frames = webkit_dom_document_get_elements_by_tag_name_as_html_collection (content_document, "iframe");
+       length = webkit_dom_html_collection_get_length (frames);
+       for (ii = 0; ii < length; ii++) {
+               WebKitDOMNode *node;
+               gchar *text;
+
+               node = webkit_dom_html_collection_item (frames, ii);
+
+               text = get_frame_selection_html (
+                       WEBKIT_DOM_ELEMENT (node));
+
+               g_object_unref (node);
+               if (text != NULL) {
+                       g_clear_object (&frames);
+                       return text;
+               }
+       }
+
+       g_clear_object (&frames);
+
+       return NULL;
+}
+
+gchar *
+e_dom_utils_get_selection_content_html (WebKitDOMDocument *document)
+{
+       WebKitDOMHTMLCollection *frames = NULL;
+       gulong ii, length;
+
+       if (!e_dom_utils_document_has_selection (document))
+               return NULL;
+
+       frames = webkit_dom_document_get_elements_by_tag_name_as_html_collection (document, "iframe");
+       length = webkit_dom_html_collection_get_length (frames);
+
+       for (ii = 0; ii < length; ii++) {
+               gchar *text;
+               WebKitDOMNode *node;
+
+               node = webkit_dom_html_collection_item (frames, ii);
+
+               text = get_frame_selection_html (
+                       WEBKIT_DOM_ELEMENT (node));
+
+               g_object_unref (node);
+               if (text != NULL) {
+                       g_clear_object (&frames);
+                       return text;
+               }
+       }
+
+       g_clear_object (&frames);
+       return NULL;
+}
+
+static gchar *
+get_frame_selection_content_text (WebKitDOMElement *iframe)
+{
+       WebKitDOMDocument *content_document;
+       WebKitDOMDOMWindow *dom_window = NULL;
+       WebKitDOMDOMSelection *dom_selection = NULL;
+       WebKitDOMHTMLCollection *frames = NULL;
+       gulong ii, length;
+
+       content_document = webkit_dom_html_iframe_element_get_content_document (
+               WEBKIT_DOM_HTML_IFRAME_ELEMENT (iframe));
+
+       if (!content_document)
+               return NULL;
+
+       dom_window = webkit_dom_document_get_default_view (content_document);
+       dom_selection = webkit_dom_dom_window_get_selection (dom_window);
+       g_clear_object (&dom_window);
+       if (dom_selection && (webkit_dom_dom_selection_get_range_count (dom_selection) > 0)) {
+               WebKitDOMRange *range = NULL;
+               gchar *text = NULL;
+
+               range = webkit_dom_dom_selection_get_range_at (dom_selection, 0, NULL);
+               if (range)
+                       text = webkit_dom_range_to_string (range, NULL);
+               g_clear_object (&range);
+               g_clear_object (&dom_selection);
+               return text;
+       }
+       g_clear_object (&dom_selection);
+
+       frames = webkit_dom_document_get_elements_by_tag_name_as_html_collection (content_document, "iframe");
+       length = webkit_dom_html_collection_get_length (frames);
+       for (ii = 0; ii < length; ii++) {
+               WebKitDOMNode *node;
+               gchar *text;
+
+               node = webkit_dom_html_collection_item (frames, ii);
+
+               text = get_frame_selection_content_text (
+                       WEBKIT_DOM_ELEMENT (node));
+
+               g_object_unref (node);
+               if (text != NULL) {
+                       g_clear_object (&frames);
+                       return text;
+               }
+       }
+
+       g_clear_object (&frames);
+       return NULL;
+}
+
+gchar *
+e_dom_utils_get_selection_content_text (WebKitDOMDocument *document)
+{
+       WebKitDOMHTMLCollection *frames = NULL;
+       gulong ii, length;
+
+       frames = webkit_dom_document_get_elements_by_tag_name_as_html_collection (document, "iframe");
+       length = webkit_dom_html_collection_get_length (frames);
+
+       for (ii = 0; ii < length; ii++) {
+               gchar *text;
+               WebKitDOMNode *node;
+
+               node = webkit_dom_html_collection_item (frames, ii);
+
+               text = get_frame_selection_content_text (
+                       WEBKIT_DOM_ELEMENT (node));
+
+               g_object_unref (node);
+               if (text != NULL) {
+                       g_clear_object (&frames);
+                       return text;
+               }
+       }
+
+       g_clear_object (&frames);
+       return NULL;
+}
+
+void
+e_dom_utils_create_and_add_css_style_sheet (WebKitDOMDocument *document,
+                                            const gchar *style_sheet_id)
+{
+       WebKitDOMElement *style_element;
+
+       style_element = webkit_dom_document_get_element_by_id (document, style_sheet_id);
+
+       if (!style_element) {
+               WebKitDOMText *dom_text;
+               WebKitDOMHTMLHeadElement *head;
+
+               dom_text = webkit_dom_document_create_text_node (document, "");
+
+               /* Create new <style> element */
+               style_element = webkit_dom_document_create_element (document, "style", NULL);
+               webkit_dom_element_set_id (
+                       style_element,
+                       style_sheet_id);
+               webkit_dom_html_style_element_set_media (
+                       WEBKIT_DOM_HTML_STYLE_ELEMENT (style_element),
+                       "screen");
+               webkit_dom_node_append_child (
+                       WEBKIT_DOM_NODE (style_element),
+                       /* WebKit hack - we have to insert empty TextNode into style element */
+                       WEBKIT_DOM_NODE (dom_text),
+                       NULL);
+
+               head = webkit_dom_document_get_head (document);
+
+               webkit_dom_node_append_child (
+                       WEBKIT_DOM_NODE (head),
+                       WEBKIT_DOM_NODE (style_element),
+                       NULL);
+
+               g_object_unref (head);
+               g_object_unref (dom_text);
+               g_object_unref (style_element);
+       }
+}
+
+static void
+add_css_rule_into_style_sheet (WebKitDOMDocument *document,
+                               const gchar *style_sheet_id,
+                               const gchar *selector,
+                               const gchar *style)
+{
+       WebKitDOMElement *style_element;
+       WebKitDOMStyleSheet *sheet = NULL;
+       WebKitDOMCSSRuleList *rules_list = NULL;
+       gint length, ii, selector_length;
+       gboolean removed = FALSE;
+
+       g_return_if_fail (selector != NULL);
+
+       selector_length = strlen (selector);
+       style_element = webkit_dom_document_get_element_by_id (document, style_sheet_id);
+
+       if (!style_element) {
+               e_dom_utils_create_and_add_css_style_sheet (document, style_sheet_id);
+               style_element = webkit_dom_document_get_element_by_id (document, style_sheet_id);
+       }
+
+       /* Get sheet that is associated with style element */
+       sheet = webkit_dom_html_style_element_get_sheet (WEBKIT_DOM_HTML_STYLE_ELEMENT (style_element));
+
+       rules_list = webkit_dom_css_style_sheet_get_css_rules (WEBKIT_DOM_CSS_STYLE_SHEET (sheet));
+       length = webkit_dom_css_rule_list_get_length (rules_list);
+
+       /* Check if rule exists */
+       for (ii = 0; ii < length && !removed; ii++) {
+               WebKitDOMCSSRule *rule;
+               gchar *rule_text = NULL;
+
+               rule = webkit_dom_css_rule_list_item (rules_list, ii);
+
+               g_return_if_fail (WEBKIT_DOM_IS_CSS_RULE (rule));
+
+               rule_text = webkit_dom_css_rule_get_css_text (rule);
+
+               /* Find the start of the style => end of the selector */
+               if (rule_text && selector && g_str_has_prefix (rule_text, selector) &&
+                   rule_text[selector_length] == ' ' && rule_text[selector_length + 1] == '{') {
+                       /* If exists remove it */
+                       webkit_dom_css_style_sheet_remove_rule (
+                               WEBKIT_DOM_CSS_STYLE_SHEET (sheet),
+                               ii, NULL);
+                       length--;
+                       removed = TRUE;
+               }
+
+               g_free (rule_text);
+               g_object_unref (rule);
+       }
+
+       g_clear_object (&rules_list);
+
+       /* Insert the rule at the end, so it will override previously inserted */
+       webkit_dom_css_style_sheet_add_rule (
+               WEBKIT_DOM_CSS_STYLE_SHEET (sheet), selector, style, length, NULL);
+
+       g_clear_object (&sheet);
+       g_object_unref (style_element);
+}
+
+static void
+add_css_rule_into_style_sheet_recursive (WebKitDOMDocument *document,
+                                         const gchar *style_sheet_id,
+                                         const gchar *selector,
+                                         const gchar *style)
+{
+       WebKitDOMHTMLCollection *frames = NULL;
+       gint ii, length;
+
+       /* Add rule to document */
+       add_css_rule_into_style_sheet (
+               document,
+               style_sheet_id,
+               selector,
+               style);
+
+       frames = webkit_dom_document_get_elements_by_tag_name_as_html_collection (document, "iframe");
+       length = webkit_dom_html_collection_get_length (frames);
+
+       /* Add rules to every sub document */
+       for (ii = 0; ii < length; ii++) {
+               WebKitDOMDocument *content_document = NULL;
+               WebKitDOMNode *node;
+
+               node = webkit_dom_html_collection_item (frames, ii);
+               content_document =
+                       webkit_dom_html_iframe_element_get_content_document (
+                               WEBKIT_DOM_HTML_IFRAME_ELEMENT (node));
+
+               if (!content_document)
+                       continue;
+
+               add_css_rule_into_style_sheet_recursive (
+                       content_document,
+                       style_sheet_id,
+                       selector,
+                       style);
+               g_object_unref (node);
+       }
+       g_clear_object (&frames);
+}
+
+void
+e_dom_utils_add_css_rule_into_style_sheet (WebKitDOMDocument *document,
+                                           const gchar *style_sheet_id,
+                                           const gchar *selector,
+                                           const gchar *style)
+{
+       g_return_if_fail (style_sheet_id && *style_sheet_id);
+       g_return_if_fail (selector && *selector);
+       g_return_if_fail (style && *style);
+
+       add_css_rule_into_style_sheet_recursive (
+               document,
+               style_sheet_id,
+               selector,
+               style);
+}
+
+static void
+collapse_contacts_list (WebKitDOMEventTarget *event_target,
+                        WebKitDOMEvent *event,
+                        gpointer user_data)
+{
+       WebKitDOMDocument *document;
+       WebKitDOMElement *list;
+       gchar *id, *list_id;
+       gchar *imagesdir, *src;
+       gboolean hidden;
+
+       document = user_data;
+       id = webkit_dom_element_get_id (WEBKIT_DOM_ELEMENT (event_target));
+
+       if (!id)
+               return;
+
+       list_id = g_strconcat ("list-", id, NULL);
+       list = webkit_dom_document_get_element_by_id (document, list_id);
+       g_free (id);
+       g_free (list_id);
+
+       if (list == NULL)
+               return;
+
+       imagesdir = g_filename_to_uri (EVOLUTION_IMAGESDIR, NULL, NULL);
+       hidden = webkit_dom_html_element_get_hidden (WEBKIT_DOM_HTML_ELEMENT (list));
+
+       if (hidden)
+               src = g_strdup_printf ("evo-file://%s/minus.png", imagesdir);
+       else
+               src = g_strdup_printf ("evo-file://%s/plus.png", imagesdir);
+
+       webkit_dom_html_element_set_hidden (
+               WEBKIT_DOM_HTML_ELEMENT (list), !hidden);
+       webkit_dom_html_image_element_set_src (
+               WEBKIT_DOM_HTML_IMAGE_ELEMENT (event_target), src);
+
+       g_free (src);
+       g_free (imagesdir);
+}
+
+static void
+toggle_headers_visibility (WebKitDOMElement *button,
+                           WebKitDOMEvent *event,
+                           WebKitDOMDocument *document)
+{
+       WebKitDOMElement *short_headers = NULL, *full_headers = NULL;
+       WebKitDOMCSSStyleDeclaration *css_short = NULL, *css_full = NULL;
+       gboolean expanded;
+       const gchar *path;
+       gchar *css_value;
+
+       short_headers = webkit_dom_document_get_element_by_id (
+               document, "__evo-short-headers");
+       if (short_headers == NULL)
+               return;
+
+       css_short = webkit_dom_element_get_style (short_headers);
+
+       full_headers = webkit_dom_document_get_element_by_id (
+               document, "__evo-full-headers");
+       if (full_headers == NULL)
+               goto clean;
+
+       css_full = webkit_dom_element_get_style (full_headers);
+       css_value = webkit_dom_css_style_declaration_get_property_value (
+               css_full, "display");
+       expanded = (g_strcmp0 (css_value, "table") == 0);
+       g_free (css_value);
+
+       webkit_dom_css_style_declaration_set_property (
+               css_full, "display",
+               expanded ? "none" : "table", "", NULL);
+       webkit_dom_css_style_declaration_set_property (
+               css_short, "display",
+               expanded ? "table" : "none", "", NULL);
+
+       if (expanded)
+               path = "evo-file://" EVOLUTION_IMAGESDIR "/plus.png";
+       else
+               path = "evo-file://" EVOLUTION_IMAGESDIR "/minus.png";
+
+       webkit_dom_html_image_element_set_src (
+               WEBKIT_DOM_HTML_IMAGE_ELEMENT (button), path);
+ clean:
+       g_clear_object (&short_headers);
+       g_clear_object (&css_short);
+       g_clear_object (&full_headers);
+       g_clear_object (&css_full);
+}
+
+static void
+toggle_address_visibility (WebKitDOMElement *button,
+                           WebKitDOMEvent *event,
+                           GDBusConnection *connection)
+{
+       WebKitDOMElement *full_addr = NULL, *ellipsis = NULL;
+       WebKitDOMElement *parent = NULL, *bold = NULL;
+       WebKitDOMCSSStyleDeclaration *css_full = NULL, *css_ellipsis = NULL;
+       const gchar *path;
+       gchar *property_value;
+       gboolean expanded;
+       GError *error = NULL;
+
+       /* <b> element */
+       bold = webkit_dom_node_get_parent_element (WEBKIT_DOM_NODE (button));
+       /* <td> element */
+       parent = webkit_dom_node_get_parent_element (WEBKIT_DOM_NODE (bold));
+       g_object_unref (bold);
+
+       full_addr = webkit_dom_element_query_selector (parent, "#__evo-moreaddr", NULL);
+
+       if (!full_addr)
+               goto clean;
+
+       css_full = webkit_dom_element_get_style (full_addr);
+
+       ellipsis = webkit_dom_element_query_selector (parent, "#__evo-moreaddr-ellipsis", NULL);
+
+       if (!ellipsis)
+               goto clean;
+
+       css_ellipsis = webkit_dom_element_get_style (ellipsis);
+
+       property_value = webkit_dom_css_style_declaration_get_property_value (css_full, "display");
+       expanded = g_strcmp0 (property_value, "inline") == 0;
+       g_free (property_value);
+
+       webkit_dom_css_style_declaration_set_property (
+               css_full, "display", (expanded ? "none" : "inline"), "", NULL);
+       webkit_dom_css_style_declaration_set_property (
+               css_ellipsis, "display", (expanded ? "inline" : "none"), "", NULL);
+
+       if (expanded)
+               path = "evo-file://" EVOLUTION_IMAGESDIR "/plus.png";
+       else
+               path = "evo-file://" EVOLUTION_IMAGESDIR "/minus.png";
+
+       if (!WEBKIT_DOM_IS_HTML_IMAGE_ELEMENT (button)) {
+               WebKitDOMElement *element;
+
+               element = webkit_dom_element_query_selector (parent, "#__evo-moreaddr-img", NULL);
+               if (!element)
+                       goto clean;
+
+               webkit_dom_html_image_element_set_src (WEBKIT_DOM_HTML_IMAGE_ELEMENT (element), path);
+
+               g_object_unref (element);
+       } else
+               webkit_dom_html_image_element_set_src (WEBKIT_DOM_HTML_IMAGE_ELEMENT (button), path);
+
+       g_dbus_connection_emit_signal (
+               connection,
+               NULL,
+               E_WEB_EXTENSION_OBJECT_PATH,
+               E_WEB_EXTENSION_INTERFACE,
+               "HeadersCollapsed",
+               g_variant_new ("(b)", expanded),
+               &error);
+
+       if (error) {
+               g_warning ("Error emitting signal HeadersCollapsed: %s\n", error->message);
+               g_error_free (error);
+       }
+
+ clean:
+       g_clear_object (&css_full);
+       g_clear_object (&css_ellipsis);
+       g_clear_object (&full_addr);
+       g_clear_object (&ellipsis);
+       g_clear_object (&parent);
+}
+
+static void
+e_dom_utils_bind_dom (WebKitDOMDocument *document,
+                      const gchar *selector,
+                      const gchar *event,
+                      gpointer callback,
+                      gpointer user_data)
+{
+       WebKitDOMNodeList *nodes = NULL;
+       gulong ii, length;
+
+       nodes = webkit_dom_document_query_selector_all (
+                       document, selector, NULL);
+
+       length = webkit_dom_node_list_get_length (nodes);
+       for (ii = 0; ii < length; ii++) {
+               WebKitDOMNode *node;
+
+               node = webkit_dom_node_list_item (nodes, ii);
+               webkit_dom_event_target_add_event_listener (
+                       WEBKIT_DOM_EVENT_TARGET (node), event,
+                       G_CALLBACK (callback), FALSE, user_data);
+       }
+       g_clear_object (&nodes);
+}
+
+static void
+e_dom_utils_bind_elements_recursively (WebKitDOMDocument *document,
+                                       const gchar *selector,
+                                       const gchar *event,
+                                       gpointer callback,
+                                       gpointer user_data)
+{
+       WebKitDOMNodeList *nodes = NULL;
+       WebKitDOMHTMLCollection *frames = NULL;
+       gulong ii, length;
+
+       nodes = webkit_dom_document_query_selector_all (
+                       document, selector, NULL);
+
+       length = webkit_dom_node_list_get_length (nodes);
+       for (ii = 0; ii < length; ii++) {
+               WebKitDOMNode *node;
+
+               node = webkit_dom_node_list_item (nodes, ii);
+               webkit_dom_event_target_add_event_listener (
+                       WEBKIT_DOM_EVENT_TARGET (node), event,
+                       G_CALLBACK (callback), FALSE, user_data);
+       }
+       g_clear_object (&nodes);
+
+       frames = webkit_dom_document_get_elements_by_tag_name_as_html_collection (document, "iframe");
+       length = webkit_dom_html_collection_get_length (frames);
+
+       /* Add rules to every sub document */
+       for (ii = 0; ii < length; ii++) {
+               WebKitDOMDocument *content_document = NULL;
+               WebKitDOMNode *node;
+
+               node = webkit_dom_html_collection_item (frames, ii);
+               content_document =
+                       webkit_dom_html_iframe_element_get_content_document (
+                               WEBKIT_DOM_HTML_IFRAME_ELEMENT (node));
+
+               if (!content_document)
+                       continue;
+
+               e_dom_utils_bind_elements_recursively (
+                       content_document,
+                       selector,
+                       event,
+                       callback,
+                       user_data);
+       }
+       g_clear_object (&frames);
+}
+
+static void
+element_focus_cb (WebKitDOMElement *element,
+                  WebKitDOMEvent *event,
+                 GDBusConnection *connection)
+{
+       g_dbus_connection_call (
+               connection,
+               E_WEB_EXTENSION_SERVICE_NAME,
+               E_WEB_EXTENSION_OBJECT_PATH,
+               E_WEB_EXTENSION_INTERFACE,
+               "Set",
+               g_variant_new (
+                       "(ssv)",
+                       E_WEB_EXTENSION_INTERFACE,
+                       "NeedInput",
+                       g_variant_new_boolean (TRUE)),
+               NULL,
+               G_DBUS_CALL_FLAGS_NONE,
+               -1,
+               NULL,
+               NULL,
+               NULL);
+
+}
+
+static void
+element_blur_cb (WebKitDOMElement *element,
+                 WebKitDOMEvent *event,
+                GDBusConnection *connection)
+{
+       g_dbus_connection_call (
+               connection,
+               E_WEB_EXTENSION_SERVICE_NAME,
+               E_WEB_EXTENSION_OBJECT_PATH,
+               E_WEB_EXTENSION_INTERFACE,
+               "Set",
+               g_variant_new (
+                       "(ssv)",
+                       E_WEB_EXTENSION_INTERFACE,
+                       "NeedInput",
+                       g_variant_new_boolean (FALSE)),
+               NULL,
+               G_DBUS_CALL_FLAGS_NONE,
+               -1,
+               NULL,
+               NULL,
+               NULL);
+}
+
+
+void
+e_dom_utils_bind_focus_on_elements (WebKitDOMDocument *document,
+                                    GDBusConnection *connection)
+{
+       const gchar *elements = "input, textarea, select, button, label";
+
+       e_dom_utils_bind_elements_recursively (
+               document,
+               elements,
+               "focus",
+               element_focus_cb,
+               connection);
+
+       e_dom_utils_bind_elements_recursively (
+               document,
+               elements,
+               "blur",
+               element_blur_cb,
+               connection);
+}
+
+void
+e_dom_utils_e_mail_display_bind_dom (WebKitDOMDocument *document,
+                                     GDBusConnection *connection)
+{
+       e_dom_utils_bind_dom (
+               document,
+               "#__evo-collapse-headers-img",
+               "click",
+               toggle_headers_visibility,
+               document);
+
+       e_dom_utils_bind_dom (
+               document,
+               "*[id^=__evo-moreaddr-]",
+               "click",
+               toggle_address_visibility,
+               connection);
+}
+
+void
+e_dom_utils_eab_contact_formatter_bind_dom (WebKitDOMDocument *document)
+{
+       e_dom_utils_bind_dom (
+               document,
+               "._evo_collapse_button",
+               "click",
+               collapse_contacts_list,
+               document);
+}
+
+/* ! This function can be called only from WK2 web-extension ! */
+WebKitDOMElement *
+e_dom_utils_find_element_by_selector (WebKitDOMDocument *document,
+                                      const gchar *selector)
+{
+       WebKitDOMHTMLCollection *frames = NULL;
+       WebKitDOMElement *element;
+       gulong ii, length;
+
+       /* Try to look up the element in this DOM document */
+       element = webkit_dom_document_query_selector (document, selector, NULL);
+       if (element != NULL)
+               return element;
+
+       /* If the element is not here then recursively scan all frames */
+       frames = webkit_dom_document_get_elements_by_tag_name_as_html_collection (document, "iframe");
+       length = webkit_dom_html_collection_get_length (frames);
+       for (ii = 0; ii < length; ii++) {
+               WebKitDOMHTMLIFrameElement *iframe;
+               WebKitDOMDocument *content_document;
+
+               iframe = WEBKIT_DOM_HTML_IFRAME_ELEMENT (
+                       webkit_dom_html_collection_item (frames, ii));
+
+               content_document = webkit_dom_html_iframe_element_get_content_document (iframe);
+               if (!content_document)
+                       continue;
+
+               element = e_dom_utils_find_element_by_id (content_document, selector);
+
+               g_object_unref (iframe);
+               if (element != NULL)
+                       break;
+       }
+
+       g_clear_object (&frames);
+       return element;
+}
+
+/* ! This function can be called only from WK2 web-extension ! */
+WebKitDOMElement *
+e_dom_utils_find_element_by_id (WebKitDOMDocument *document,
+                                const gchar *id)
+{
+       WebKitDOMHTMLCollection *frames = NULL;
+       WebKitDOMElement *element;
+       gulong ii, length;
+
+       /* Try to look up the element in this DOM document */
+       element = webkit_dom_document_get_element_by_id (document, id);
+       if (element != NULL)
+               return element;
+
+       /* If the element is not here then recursively scan all frames */
+       frames = webkit_dom_document_get_elements_by_tag_name_as_html_collection (document, "iframe");
+       length = webkit_dom_html_collection_get_length (frames);
+       for (ii = 0; ii < length; ii++) {
+               WebKitDOMHTMLIFrameElement *iframe;
+               WebKitDOMDocument *content_document;
+
+               iframe = WEBKIT_DOM_HTML_IFRAME_ELEMENT (
+                       webkit_dom_html_collection_item (frames, ii));
+
+               content_document = webkit_dom_html_iframe_element_get_content_document (iframe);
+               if (!content_document)
+                       continue;
+
+               element = e_dom_utils_find_element_by_id (content_document, id);
+
+               g_object_unref (iframe);
+               if (element != NULL)
+                       break;
+       }
+
+       g_clear_object (&frames);
+       return element;
+}
+
+gboolean
+e_dom_utils_element_exists (WebKitDOMDocument *document,
+                            const gchar *element_id)
+{
+       WebKitDOMElement *element;
+
+       element = e_dom_utils_find_element_by_id (document, element_id);
+
+       return element != NULL;
+}
+
+gchar *
+e_dom_utils_get_active_element_name (WebKitDOMDocument *document)
+{
+       WebKitDOMElement *element;
+
+       element = webkit_dom_document_get_active_element (document);
+
+       if (!element)
+               return NULL;
+
+       while (WEBKIT_DOM_IS_HTML_IFRAME_ELEMENT (element)) {
+               WebKitDOMDocument *content_document;
+
+               content_document =
+                       webkit_dom_html_iframe_element_get_content_document (
+                               WEBKIT_DOM_HTML_IFRAME_ELEMENT (element));
+
+               if (!content_document)
+                       break;
+
+               element = webkit_dom_document_get_active_element (content_document);
+       }
+
+       return webkit_dom_node_get_local_name (WEBKIT_DOM_NODE (element));
+}
+
+void
+e_dom_utils_e_mail_part_headers_bind_dom_element (WebKitDOMDocument *document,
+                                                  const gchar *element_id)
+{
+       WebKitDOMDocument *element_document;
+       WebKitDOMElement *element;
+       WebKitDOMElement *photo;
+       gchar *addr;
+
+       element = e_dom_utils_find_element_by_id (document, element_id);
+       if (!element)
+               return;
+
+       element_document = webkit_dom_node_get_owner_document (
+               WEBKIT_DOM_NODE (element));
+       photo = webkit_dom_document_get_element_by_id (
+               element_document, "__evo-contact-photo");
+
+       /* Contact photos disabled, the <img> tag is not there. */
+       if (!photo)
+               return;
+
+       addr = webkit_dom_element_get_attribute (photo, "data-mailaddr");
+       if (addr) {
+               gchar *uri;
+
+               uri = g_strdup_printf ("mail://contact-photo?mailaddr=%s", addr);
+
+               webkit_dom_html_image_element_set_src (
+                       WEBKIT_DOM_HTML_IMAGE_ELEMENT (photo), uri);
+
+               g_free (uri);
+       }
+
+       g_free (addr);
+}
+
+void
+e_dom_utils_element_set_inner_html (WebKitDOMDocument *document,
+                                    const gchar *element_id,
+                                    const gchar *inner_html)
+{
+       WebKitDOMElement *element;
+
+       element = e_dom_utils_find_element_by_id (document, element_id);
+
+       if (!element)
+               return;
+
+       webkit_dom_element_set_inner_html (element, inner_html, NULL);
+}
+
+void
+e_dom_utils_remove_element (WebKitDOMDocument *document,
+                            const gchar *element_id)
+{
+       WebKitDOMElement *element;
+
+       element = e_dom_utils_find_element_by_id (document, element_id);
+
+       if (!element)
+               return;
+
+       webkit_dom_node_remove_child (
+               webkit_dom_node_get_parent_node (WEBKIT_DOM_NODE (element)),
+               WEBKIT_DOM_NODE (element),
+               NULL);
+}
+
+void
+e_dom_utils_element_remove_child_nodes (WebKitDOMDocument *document,
+                                        const gchar *element_id)
+{
+       WebKitDOMNode *node;
+       WebKitDOMElement *element;
+
+       element = e_dom_utils_find_element_by_id (document, element_id);
+       if (!element)
+               return;
+
+       node = WEBKIT_DOM_NODE (element);
+
+       if (!node)
+               return;
+
+       while (webkit_dom_node_has_child_nodes (node)) {
+               webkit_dom_node_remove_child (
+                       node,
+                       webkit_dom_node_get_last_child (node),
+                       NULL);
+       }
+}
+
+void
+e_dom_utils_hide_element (WebKitDOMDocument *document,
+                          const gchar *element_id,
+                          gboolean hide)
+{
+       WebKitDOMElement *element;
+
+       element = e_dom_utils_find_element_by_id (document, element_id);
+
+       if (!element)
+               return;
+
+       webkit_dom_html_element_set_hidden (
+               WEBKIT_DOM_HTML_ELEMENT (element), hide);
+}
+
+gboolean
+e_dom_utils_element_is_hidden (WebKitDOMDocument *document,
+                               const gchar *element_id)
+{
+       WebKitDOMElement *element;
+
+       element = e_dom_utils_find_element_by_id (document, element_id);
+
+       if (!element)
+               return FALSE;
+
+       return webkit_dom_html_element_get_hidden (WEBKIT_DOM_HTML_ELEMENT (element));
+}
+
+static void
+get_total_offsets (WebKitDOMElement *element,
+                   glong *left,
+                   glong *top)
+{
+       WebKitDOMElement *offset_parent;
+
+       if (left)
+               *left = 0;
+
+       if (top)
+               *top = 0;
+
+       offset_parent = element;
+       do {
+               if (left) {
+                       *left += webkit_dom_element_get_offset_left (offset_parent);
+                       *left -= webkit_dom_element_get_scroll_left (offset_parent);
+               }
+               if (top) {
+                       *top += webkit_dom_element_get_offset_top (offset_parent);
+                       *top -= webkit_dom_element_get_scroll_top (offset_parent);
+               }
+               offset_parent = webkit_dom_element_get_offset_parent (offset_parent);
+       } while (offset_parent);
+}
+
+static WebKitDOMElement *
+find_element_from_point (WebKitDOMDocument *document,
+                         gint32 x,
+                         gint32 y,
+                         WebKitDOMElement *element_on_point)
+{
+       WebKitDOMDocument *content_document;
+       WebKitDOMElement *element;
+
+       if (!element_on_point)
+               element = webkit_dom_document_element_from_point (document, x, y);
+       else {
+               glong left, top;
+               get_total_offsets (element_on_point, &left, &top);
+
+               element = webkit_dom_document_element_from_point (
+                       document, x - left, y - top);
+       }
+
+       if (!element)
+               return element_on_point;
+       else if (!WEBKIT_DOM_IS_HTML_IFRAME_ELEMENT (element))
+               element_on_point = element;
+
+       if (element_on_point && webkit_dom_node_is_equal_node (
+               WEBKIT_DOM_NODE (element),
+               WEBKIT_DOM_NODE (element_on_point))) {
+               return element_on_point;
+       }
+
+       if (!WEBKIT_DOM_IS_HTML_IFRAME_ELEMENT (element))
+               return element_on_point;
+
+       content_document =
+               webkit_dom_html_iframe_element_get_content_document (
+                       WEBKIT_DOM_HTML_IFRAME_ELEMENT (element));
+
+       if (!content_document)
+               return element_on_point;
+
+       return find_element_from_point (content_document, x, y, element);
+}
+
+/* ! This function can be called only from WK2 web-extension ! */
+WebKitDOMElement *
+e_dom_utils_get_element_from_point (WebKitDOMDocument *document,
+                                    gint32 x,
+                                    gint32 y)
+{
+       return find_element_from_point (document, x, y, NULL);
+}
+
+/* ! This function can be called only from WK2 web-extension ! */
+WebKitDOMDocument *
+e_dom_utils_get_document_from_point (WebKitDOMDocument *document,
+                                     gint32 x,
+                                     gint32 y)
+{
+       WebKitDOMElement *element;
+
+       if (x == 0 && y == 0)
+               element = webkit_dom_document_get_active_element (document);
+       else
+               element = find_element_from_point (document, x, y, NULL);
+
+       if (WEBKIT_DOM_IS_HTML_IFRAME_ELEMENT (element))
+               return webkit_dom_html_iframe_element_get_content_document (
+                       WEBKIT_DOM_HTML_IFRAME_ELEMENT (element));
+       else
+               return webkit_dom_node_get_owner_document (
+                       WEBKIT_DOM_NODE (element));
+}
+
+/* VCard Inline Module DOM functions */
+
+static void
+display_mode_toggle_button_cb (WebKitDOMElement *button,
+                               WebKitDOMEvent *event,
+                               GDBusConnection *connection)
+{
+       GError *error = NULL;
+       gchar *element_id;
+
+       element_id = webkit_dom_element_get_id (button);
+
+       g_dbus_connection_emit_signal (
+               connection,
+               NULL,
+               E_WEB_EXTENSION_OBJECT_PATH,
+               E_WEB_EXTENSION_INTERFACE,
+               "VCardInlineDisplayModeToggled",
+               g_variant_new ("(s)", element_id ? element_id : ""),
+               &error);
+
+       if (error) {
+               g_warning ("Error emitting signal DisplayModeToggled: %s\n", error->message);
+               g_error_free (error);
+       }
+
+       g_free (element_id);
+}
+
+static void
+save_vcard_button_cb (WebKitDOMElement *button,
+                      WebKitDOMEvent *event,
+                      GDBusConnection *connection)
+{
+       GError *error = NULL;
+       gchar *button_value;
+
+       button_value = webkit_dom_html_button_element_get_value (
+               WEBKIT_DOM_HTML_BUTTON_ELEMENT (button));
+
+       g_dbus_connection_emit_signal (
+               connection,
+               NULL,
+               E_WEB_EXTENSION_OBJECT_PATH,
+               E_WEB_EXTENSION_INTERFACE,
+               "VCardInlineSaveButtonPressed",
+               g_variant_new ("(s)", button_value),
+               &error);
+
+       if (error) {
+               g_warning ("Error emitting signal SaveVCardButtonPressed: %s\n", error->message);
+               g_error_free (error);
+       }
+
+       g_free (button_value);
+}
+
+void
+e_dom_utils_module_vcard_inline_bind_dom (WebKitDOMDocument *document,
+                                          const gchar *element_id,
+                                          GDBusConnection *connection)
+{
+       WebKitDOMElement *element;
+       WebKitDOMDocument *element_document;
+       gchar *selector;
+
+       element = e_dom_utils_find_element_by_id (document, element_id);
+       if (!element)
+               return;
+
+       element_document = webkit_dom_node_get_owner_document (
+               WEBKIT_DOM_NODE (element));
+
+       selector = g_strconcat ("button[id='", element_id, "']", NULL);
+       e_dom_utils_bind_dom (
+               element_document,
+               selector,
+               "click",
+               display_mode_toggle_button_cb,
+               connection);
+       g_free (selector);
+
+       selector = g_strconcat ("button[value='", element_id, "']", NULL);
+       e_dom_utils_bind_dom (
+               element_document,
+               selector,
+               "click",
+               save_vcard_button_cb,
+               connection);
+       g_free (selector);
+
+       e_dom_utils_eab_contact_formatter_bind_dom (element_document);
+}
+
+void
+e_dom_utils_module_vcard_inline_update_button (WebKitDOMDocument *document,
+                                               const gchar *button_id,
+                                               const gchar *html_label,
+                                               const gchar *access_key)
+{
+       WebKitDOMElement *element;
+       gchar *selector;
+
+       selector = g_strconcat ("button[id='", button_id, "']", NULL);
+       element = e_dom_utils_find_element_by_selector (document, selector);
+       g_free (selector);
+
+       if (!element)
+               return;
+
+       webkit_dom_element_set_inner_html (element, html_label, NULL);
+
+       if (access_key) {
+               webkit_dom_html_element_set_access_key (
+                       WEBKIT_DOM_HTML_ELEMENT (element), access_key);
+       }
+}
+
+void
+e_dom_utils_module_vcard_inline_set_iframe_src (WebKitDOMDocument *document,
+                                                const gchar *button_id,
+                                                const gchar *src)
+{
+       WebKitDOMElement *element, *parent, *iframe;
+       gchar *selector;
+
+       selector = g_strconcat ("button[id='", button_id, "']", NULL);
+       element = e_dom_utils_find_element_by_selector (document, selector);
+       g_free (selector);
+
+       parent = webkit_dom_node_get_parent_element (WEBKIT_DOM_NODE (element));
+       if (!parent)
+               return;
+
+       iframe = webkit_dom_element_query_selector (parent, "iframe", NULL);
+       if (!iframe)
+               return;
+
+       webkit_dom_html_iframe_element_set_src (
+               WEBKIT_DOM_HTML_IFRAME_ELEMENT (iframe), src);
+}
+
+/**
+ * e_html_editor_dom_node_find_parent_element:
+ * @node: Start node
+ * @tagname: Tag name of element to search
+ *
+ * Recursively searches for first occurance of element with given @tagname
+ * that is parent of given @node.
+ *
+ * Returns: A #WebKitDOMElement with @tagname representing parent of @node or
+ * @NULL when @node has no parent with given @tagname. When @node matches @tagname,
+ * then the @node is returned.
+ */
+WebKitDOMElement *
+dom_node_find_parent_element (WebKitDOMNode *node,
+                              const gchar *tagname)
+{
+       WebKitDOMNode *tmp_node = node;
+       gint taglen = strlen (tagname);
+
+       while (tmp_node) {
+               if (WEBKIT_DOM_IS_ELEMENT (tmp_node)) {
+                       gchar *node_tagname;
+
+                       node_tagname = webkit_dom_element_get_tag_name (
+                               WEBKIT_DOM_ELEMENT (tmp_node));
+
+                       if (node_tagname &&
+                           (strlen (node_tagname) == taglen) &&
+                           (g_ascii_strncasecmp (node_tagname, tagname, taglen) == 0)) {
+                               g_free (node_tagname);
+                               return WEBKIT_DOM_ELEMENT (tmp_node);
+                       }
+
+                       g_free (node_tagname);
+               }
+
+               tmp_node = webkit_dom_node_get_parent_node (tmp_node);
+       }
+
+       return NULL;
+}
+
+/**
+ * e_html_editor_dom_node_find_child_element:
+ * @node: Start node
+ * @tagname: Tag name of element to search.
+ *
+ * Recursively searches for first occurrence of element with given @tagname that
+ * is a child of @node.
+ *
+ * Returns: A #WebKitDOMElement with @tagname representing a child of @node or
+ * @NULL when @node has no child with given @tagname. When @node matches @tagname,
+ * then the @node is returned.
+ */
+WebKitDOMElement *
+dom_node_find_child_element (WebKitDOMNode *node,
+                             const gchar *tagname)
+{
+       WebKitDOMNode *start_node = node;
+       gint taglen = strlen (tagname);
+
+       do {
+               if (WEBKIT_DOM_IS_ELEMENT (node)) {
+                       gchar *node_tagname;
+
+                       node_tagname = webkit_dom_element_get_tag_name (
+                                       WEBKIT_DOM_ELEMENT (node));
+
+                       if (node_tagname &&
+                           (strlen (node_tagname) == taglen) &&
+                           (g_ascii_strncasecmp (node_tagname, tagname, taglen) == 0)) {
+                               g_free (node_tagname);
+                               return WEBKIT_DOM_ELEMENT (node);
+                       }
+
+                       g_free (node_tagname);
+               }
+
+               if (webkit_dom_node_has_child_nodes (node)) {
+                       node = webkit_dom_node_get_first_child (node);
+               } else if (webkit_dom_node_get_next_sibling (node)) {
+                       node = webkit_dom_node_get_next_sibling (node);
+               } else {
+                       node = webkit_dom_node_get_parent_node (node);
+               }
+       } while (!webkit_dom_node_is_same_node (node, start_node));
+
+       return NULL;
+}
+
+gboolean
+element_has_id (WebKitDOMElement *element,
+                const gchar* id)
+{
+       gchar *element_id;
+
+       if (!element)
+               return FALSE;
+
+       if (!WEBKIT_DOM_IS_ELEMENT (element))
+               return FALSE;
+
+       element_id = webkit_dom_element_get_id (element);
+
+       if (element_id && g_ascii_strcasecmp (element_id, id) == 0) {
+               g_free (element_id);
+               return TRUE;
+       }
+       g_free (element_id);
+
+       return FALSE;
+}
+
+gboolean
+element_has_tag (WebKitDOMElement *element,
+                 const gchar* tag)
+{
+       gchar *element_tag;
+
+       if (!WEBKIT_DOM_IS_ELEMENT (element))
+               return FALSE;
+
+       element_tag = webkit_dom_node_get_local_name (WEBKIT_DOM_NODE (element));
+
+       if (g_ascii_strcasecmp (element_tag, tag) != 0) {
+               g_free (element_tag);
+               return FALSE;
+       }
+       g_free (element_tag);
+
+       return TRUE;
+}
+
+gboolean
+element_has_class (WebKitDOMElement *element,
+                   const gchar* class)
+{
+       gchar *element_class;
+
+       if (!element)
+               return FALSE;
+
+       if (!WEBKIT_DOM_IS_ELEMENT (element))
+               return FALSE;
+
+       element_class = webkit_dom_element_get_class_name (element);
+
+       if (element_class && g_strstr_len (element_class, -1, class)) {
+               g_free (element_class);
+               return TRUE;
+       }
+       g_free (element_class);
+
+       return FALSE;
+}
+
+void
+element_add_class (WebKitDOMElement *element,
+                   const gchar* class)
+{
+       gchar *element_class;
+       gchar *new_class;
+
+       if (!WEBKIT_DOM_IS_ELEMENT (element))
+               return;
+
+       if (element_has_class (element, class))
+               return;
+
+       element_class = webkit_dom_element_get_class_name (element);
+
+       if (!element_class)
+               new_class = g_strdup (class);
+       else
+               new_class = g_strconcat (element_class, " ", class, NULL);
+
+       webkit_dom_element_set_class_name (element, new_class);
+
+       g_free (element_class);
+       g_free (new_class);
+}
+
+void
+element_remove_class (WebKitDOMElement *element,
+                      const gchar* class)
+{
+       gchar *element_class, *final_class;
+       GRegex *regex;
+       gchar *pattern = NULL;
+
+       if (!WEBKIT_DOM_IS_ELEMENT (element))
+               return;
+
+       if (!element_has_class (element, class))
+               return;
+
+       element_class = webkit_dom_element_get_class_name (element);
+
+       pattern = g_strconcat ("[\\s]*", class, "[\\s]*", NULL);
+       regex = g_regex_new (pattern, 0, 0, NULL);
+       final_class = g_regex_replace (regex, element_class, -1, 0, " ", 0, NULL);
+
+       if (g_strcmp0 (final_class, " ") != 0)
+               webkit_dom_element_set_class_name (element, final_class);
+       else
+               webkit_dom_element_remove_attribute (element, "class");
+
+       g_free (element_class);
+       g_free (final_class);
+       g_free (pattern);
+       g_regex_unref (regex);
+}
+
+void
+element_rename_attribute (WebKitDOMElement *element,
+                      const gchar *from,
+                      const gchar *to)
+{
+       gchar *value;
+
+       if (!webkit_dom_element_has_attribute (element, from))
+               return;
+
+       value = webkit_dom_element_get_attribute (element, from);
+       webkit_dom_element_set_attribute (element, to, (value && *value) ? value : "", NULL);
+       webkit_dom_element_remove_attribute (element, from);
+       g_free (value);
+}
+
+void
+remove_node (WebKitDOMNode *node)
+{
+       WebKitDOMNode *parent = webkit_dom_node_get_parent_node (node);
+
+       /* Check if the parent exists, if so it means that the node is still
+        * in the DOM or at least the parent is. If it doesn't exists it is not
+        * in the DOM and we can free it. */
+       if (parent)
+               webkit_dom_node_remove_child (parent, node, NULL);
+       else
+               g_object_unref (node);
+}
+
+void
+remove_node_if_empty (WebKitDOMNode *node)
+{
+       WebKitDOMNode *child;
+
+       if (!WEBKIT_DOM_IS_NODE (node))
+               return;
+
+       if ((child = webkit_dom_node_get_first_child (node))) {
+               WebKitDOMNode *prev_sibling, *next_sibling;
+
+               prev_sibling = webkit_dom_node_get_previous_sibling (child);
+               next_sibling = webkit_dom_node_get_next_sibling (child);
+               /* Empty or BR as sibling, but no sibling after it. */
+               if (!webkit_dom_node_get_first_child (child) &&
+                   !WEBKIT_DOM_IS_TEXT (child) &&
+                   (!prev_sibling ||
+                    (WEBKIT_DOM_IS_HTML_BR_ELEMENT (prev_sibling) &&
+                     !webkit_dom_node_get_previous_sibling (prev_sibling))) &&
+                   (!next_sibling ||
+                    (WEBKIT_DOM_IS_HTML_BR_ELEMENT (next_sibling) &&
+                     !webkit_dom_node_get_next_sibling (next_sibling)))) {
+
+                       remove_node (node);
+               } else {
+                       gchar *text_content;
+
+                       text_content = webkit_dom_node_get_text_content (node);
+                       if (!text_content)
+                               remove_node (node);
+
+                       if (text_content && !*text_content)
+                               remove_node (node);
+
+                       g_free (text_content);
+               }
+       } else
+               remove_node (node);
+}
+
+WebKitDOMNode *
+split_list_into_two (WebKitDOMNode *item,
+                    gint level)
+{
+       gint current_level = 1;
+       WebKitDOMDocument *document;
+       WebKitDOMDocumentFragment *fragment;
+       WebKitDOMNode *parent, *prev_parent = NULL, *tmp;
+
+       document = webkit_dom_node_get_owner_document (item);
+       fragment = webkit_dom_document_create_document_fragment (document);
+
+       tmp = item;
+       parent = webkit_dom_node_get_parent_node (item);
+       while (!WEBKIT_DOM_IS_HTML_BODY_ELEMENT (parent)) {
+               WebKitDOMNode *clone, *first_child, *insert_before = NULL, *sibling;
+
+               first_child = webkit_dom_node_get_first_child (WEBKIT_DOM_NODE (fragment));
+               clone = webkit_dom_node_clone_node_with_error (parent, FALSE, NULL);
+               webkit_dom_node_insert_before (
+                       WEBKIT_DOM_NODE (fragment), clone, first_child, NULL);
+
+               if (first_child)
+                       insert_before = webkit_dom_node_get_first_child (first_child);
+
+               while (first_child && (sibling = webkit_dom_node_get_next_sibling (first_child)))
+                       webkit_dom_node_insert_before (first_child, sibling, insert_before, NULL);
+
+               while (tmp && (sibling = webkit_dom_node_get_next_sibling (tmp)))
+                       webkit_dom_node_append_child (clone, sibling, NULL);
+
+               if (tmp)
+                       webkit_dom_node_insert_before (
+                               clone, tmp, webkit_dom_node_get_first_child (clone), NULL);
+
+               prev_parent = parent;
+               tmp = webkit_dom_node_get_next_sibling (parent);
+               parent = webkit_dom_node_get_parent_node (parent);
+               if (WEBKIT_DOM_IS_HTML_BODY_ELEMENT (parent)) {
+                       first_child = webkit_dom_node_get_first_child (WEBKIT_DOM_NODE (fragment));
+                       insert_before = webkit_dom_node_get_first_child (first_child);
+                       while (first_child && (sibling = webkit_dom_node_get_next_sibling (first_child))) {
+                               webkit_dom_node_insert_before (
+                                       first_child, sibling, insert_before, NULL);
+                       }
+               }
+
+               if (current_level >= level && level >= 0)
+                       break;
+
+               current_level++;
+       }
+
+       tmp = webkit_dom_node_insert_before (
+               parent,
+               webkit_dom_node_get_first_child (WEBKIT_DOM_NODE (fragment)),
+               prev_parent ? webkit_dom_node_get_next_sibling (prev_parent) : NULL,
+               NULL);
+       remove_node_if_empty (prev_parent);
+
+       return tmp;
+}
+
+WebKitDOMElement *
+dom_create_selection_marker (WebKitDOMDocument *document,
+                             gboolean selection_start_marker)
+{
+       WebKitDOMElement *element;
+
+       element = webkit_dom_document_create_element (
+               document, "SPAN", NULL);
+       webkit_dom_element_set_id (
+               element,
+               selection_start_marker ?
+                       "-x-evo-selection-start-marker" :
+                       "-x-evo-selection-end-marker");
+
+       return element;
+}
+
+void
+dom_remove_selection_markers (WebKitDOMDocument *document)
+{
+       WebKitDOMElement *marker;
+
+       marker = webkit_dom_document_get_element_by_id (
+               document, "-x-evo-selection-start-marker");
+       if (marker)
+               remove_node (WEBKIT_DOM_NODE (marker));
+       marker = webkit_dom_document_get_element_by_id (
+               document, "-x-evo-selection-end-marker");
+       if (marker)
+               remove_node (WEBKIT_DOM_NODE (marker));
+}
+
+void
+dom_add_selection_markers_into_element_start (WebKitDOMDocument *document,
+                                              WebKitDOMElement *element,
+                                              WebKitDOMElement **selection_start_marker,
+                                              WebKitDOMElement **selection_end_marker)
+{
+       WebKitDOMElement *marker;
+
+       dom_remove_selection_markers (document);
+       marker = dom_create_selection_marker (document, FALSE);
+       webkit_dom_node_insert_before (
+               WEBKIT_DOM_NODE (element),
+               WEBKIT_DOM_NODE (marker),
+               webkit_dom_node_get_first_child (WEBKIT_DOM_NODE (element)),
+               NULL);
+       if (selection_end_marker)
+               *selection_end_marker = marker;
+
+       marker = dom_create_selection_marker (document, TRUE);
+       webkit_dom_node_insert_before (
+               WEBKIT_DOM_NODE (element),
+               WEBKIT_DOM_NODE (marker),
+               webkit_dom_node_get_first_child (WEBKIT_DOM_NODE (element)),
+               NULL);
+       if (selection_start_marker)
+               *selection_start_marker = marker;
+}
+
+void
+dom_add_selection_markers_into_element_end (WebKitDOMDocument *document,
+                                            WebKitDOMElement *element,
+                                            WebKitDOMElement **selection_start_marker,
+                                            WebKitDOMElement **selection_end_marker)
+{
+       WebKitDOMElement *marker;
+
+       dom_remove_selection_markers (document);
+       marker = dom_create_selection_marker (document, TRUE);
+       webkit_dom_node_append_child (
+               WEBKIT_DOM_NODE (element), WEBKIT_DOM_NODE (marker), NULL);
+       if (selection_start_marker)
+               *selection_start_marker = marker;
+
+       marker = dom_create_selection_marker (document, FALSE);
+       webkit_dom_node_append_child (
+               WEBKIT_DOM_NODE (element), WEBKIT_DOM_NODE (marker), NULL);
+       if (selection_end_marker)
+               *selection_end_marker = marker;
+}
+
+gboolean
+node_is_list_or_item (WebKitDOMNode *node)
+{
+       return node && (
+               WEBKIT_DOM_IS_HTML_O_LIST_ELEMENT (node) ||
+               WEBKIT_DOM_IS_HTML_U_LIST_ELEMENT (node) ||
+               WEBKIT_DOM_IS_HTML_LI_ELEMENT (node));
+}
+
+gboolean
+node_is_list (WebKitDOMNode *node)
+{
+       return node && (
+               WEBKIT_DOM_IS_HTML_O_LIST_ELEMENT (node) ||
+               WEBKIT_DOM_IS_HTML_U_LIST_ELEMENT (node));
+}
+
+/**
+ * e_html_editor_selection_get_list_format_from_node:
+ * @node: an #WebKitDOMNode
+ *
+ * Returns block format of given list.
+ *
+ * Returns: #EContentEditorBlockFormat
+ */
+EContentEditorBlockFormat
+dom_get_list_format_from_node (WebKitDOMNode *node)
+{
+       EContentEditorBlockFormat format =
+               E_CONTENT_EDITOR_BLOCK_FORMAT_UNORDERED_LIST;
+
+       if (WEBKIT_DOM_IS_HTML_LI_ELEMENT (node))
+               return E_CONTENT_EDITOR_BLOCK_FORMAT_NONE;
+
+       if (WEBKIT_DOM_IS_HTML_U_LIST_ELEMENT (node))
+               return format;
+
+       if (WEBKIT_DOM_IS_HTML_O_LIST_ELEMENT (node)) {
+               gchar *type_value = webkit_dom_element_get_attribute (
+                       WEBKIT_DOM_ELEMENT (node), "type");
+
+               if (!type_value)
+                       return E_CONTENT_EDITOR_BLOCK_FORMAT_ORDERED_LIST;
+
+               if (!*type_value)
+                       format = E_CONTENT_EDITOR_BLOCK_FORMAT_ORDERED_LIST;
+               else if (g_ascii_strcasecmp (type_value, "A") == 0)
+                       format = E_CONTENT_EDITOR_BLOCK_FORMAT_ORDERED_LIST_ALPHA;
+               else if (g_ascii_strcasecmp (type_value, "I") == 0)
+                       format = E_CONTENT_EDITOR_BLOCK_FORMAT_ORDERED_LIST_ROMAN;
+               g_free (type_value);
+
+               return format;
+       }
+
+       return E_CONTENT_EDITOR_BLOCK_FORMAT_NONE;
+}
+
+void
+merge_list_into_list (WebKitDOMNode *from,
+                      WebKitDOMNode *to,
+                      gboolean insert_before)
+{
+       WebKitDOMNode *item, *insert_before_node;
+
+       if (!(to && from))
+               return;
+
+       insert_before_node = webkit_dom_node_get_first_child (to);
+       while ((item = webkit_dom_node_get_first_child (from)) != NULL) {
+               if (insert_before)
+                       webkit_dom_node_insert_before (
+                               to, item, insert_before_node, NULL);
+               else
+                       webkit_dom_node_append_child (to, item, NULL);
+       }
+
+       if (!webkit_dom_node_has_child_nodes (from))
+               remove_node (from);
+
+}
+
+void
+merge_lists_if_possible (WebKitDOMNode *list)
+{
+       EContentEditorBlockFormat format, prev, next;
+       gint ii, length;
+       WebKitDOMNode *prev_sibling, *next_sibling;
+       WebKitDOMNodeList *lists = NULL;
+
+       prev_sibling = webkit_dom_node_get_previous_sibling (WEBKIT_DOM_NODE (list));
+       next_sibling = webkit_dom_node_get_next_sibling (WEBKIT_DOM_NODE (list));
+
+       format = dom_get_list_format_from_node (list),
+       prev = dom_get_list_format_from_node (prev_sibling);
+       next = dom_get_list_format_from_node (next_sibling);
+
+       if (format != E_CONTENT_EDITOR_BLOCK_FORMAT_NONE) {
+               if (format == prev && prev != E_CONTENT_EDITOR_BLOCK_FORMAT_NONE)
+                       merge_list_into_list (prev_sibling, list, TRUE);
+
+               if (format == next && next != E_CONTENT_EDITOR_BLOCK_FORMAT_NONE)
+                       merge_list_into_list (next_sibling, list, FALSE);
+       }
+
+       lists = webkit_dom_element_query_selector_all (
+               WEBKIT_DOM_ELEMENT (list), "ol + ol, ul + ul", NULL);
+       length = webkit_dom_node_list_get_length (lists);
+       for (ii = 0; ii < length; ii++) {
+               WebKitDOMNode *node;
+
+               node = webkit_dom_node_list_item (lists, ii);
+               merge_lists_if_possible (node);
+               g_object_unref (node);
+       }
+       g_clear_object (&lists);
+}
+
+WebKitDOMElement *
+get_parent_block_element (WebKitDOMNode *node)
+{
+       WebKitDOMElement *parent = webkit_dom_node_get_parent_element (node);
+
+       if (WEBKIT_DOM_IS_HTML_BODY_ELEMENT (parent))
+               return WEBKIT_DOM_ELEMENT (node);
+
+       while (parent &&
+              !WEBKIT_DOM_IS_HTML_PARAGRAPH_ELEMENT (parent) &&
+              !WEBKIT_DOM_IS_HTML_DIV_ELEMENT (parent) &&
+              !WEBKIT_DOM_IS_HTML_QUOTE_ELEMENT (parent) &&
+              !WEBKIT_DOM_IS_HTML_U_LIST_ELEMENT (parent) &&
+              !WEBKIT_DOM_IS_HTML_O_LIST_ELEMENT (parent) &&
+              !WEBKIT_DOM_IS_HTML_PRE_ELEMENT (parent) &&
+              !WEBKIT_DOM_IS_HTML_HEADING_ELEMENT (parent) &&
+              !WEBKIT_DOM_IS_HTML_TABLE_CELL_ELEMENT (parent) &&
+              !element_has_tag (parent, "address")) {
+               parent = webkit_dom_node_get_parent_element (
+                       WEBKIT_DOM_NODE (parent));
+       }
+
+       return parent;
+}
+
+gchar *
+dom_get_node_inner_html (WebKitDOMNode *node)
+{
+       gchar *inner_html;
+       WebKitDOMDocument *document;
+       WebKitDOMElement *div;
+
+       document = webkit_dom_node_get_owner_document (node);
+       div = webkit_dom_document_create_element (document, "div", NULL);
+       webkit_dom_node_append_child (
+                       WEBKIT_DOM_NODE (div),
+                       webkit_dom_node_clone_node_with_error (node, TRUE, NULL),
+                       NULL);
+
+       inner_html = webkit_dom_element_get_inner_html (div);
+       remove_node (WEBKIT_DOM_NODE (div));
+
+       return inner_html;
+}
+
+WebKitDOMDocument *
+e_dom_utils_find_document_with_uri (WebKitDOMDocument *root_document,
+                                   const gchar *find_document_uri)
+{
+       WebKitDOMDocument *res_document = NULL;
+       GSList *todo;
+
+       g_return_val_if_fail (WEBKIT_DOM_IS_DOCUMENT (root_document), NULL);
+       g_return_val_if_fail (find_document_uri != NULL, NULL);
+
+       todo = g_slist_append (NULL, root_document);
+
+       while (todo) {
+               WebKitDOMDocument *document;
+               WebKitDOMHTMLCollection *frames = NULL;
+               gchar *document_uri;
+               gint ii, length;
+
+               document = todo->data;
+               todo = g_slist_remove (todo, document);
+
+               document_uri = webkit_dom_document_get_document_uri (document);
+               if (g_strcmp0 (document_uri, find_document_uri) == 0) {
+                       g_free (document_uri);
+                       res_document = document;
+                       break;
+               }
+
+               g_free (document_uri);
+
+               frames = webkit_dom_document_get_elements_by_tag_name_as_html_collection (document, "iframe");
+               length = webkit_dom_html_collection_get_length (frames);
+
+               /* Add rules to every sub document */
+               for (ii = 0; ii < length; ii++) {
+                       WebKitDOMDocument *content_document;
+                       WebKitDOMNode *node;
+
+                       node = webkit_dom_html_collection_item (frames, ii);
+                       content_document =
+                               webkit_dom_html_iframe_element_get_content_document (
+                                       WEBKIT_DOM_HTML_IFRAME_ELEMENT (node));
+
+                       if (!content_document)
+                               continue;
+
+                       todo = g_slist_prepend (todo, content_document);
+
+                       g_object_unref (node);
+               }
+
+               g_clear_object (&frames);
+       }
+
+       g_slist_free (todo);
+
+       return res_document;
+}
+
+void
+dom_element_swap_attributes (WebKitDOMElement *element,
+                             const gchar *from,
+                             const gchar *to)
+{
+       gchar *value_from, *value_to;
+
+       if (!webkit_dom_element_has_attribute (element, from) ||
+           !webkit_dom_element_has_attribute (element, to))
+               return;
+
+       value_from = webkit_dom_element_get_attribute (element, from);
+       value_to = webkit_dom_element_get_attribute (element, to);
+       webkit_dom_element_set_attribute (element, to, (value_from && *value_from) ? value_from : "", NULL);
+       webkit_dom_element_set_attribute (element, from, (value_to && *value_to) ? value_to : "", NULL);
+       g_free (value_from);
+       g_free (value_to);
+}
diff --git a/web-extensions/e-dom-utils.h b/web-extensions/e-dom-utils.h
new file mode 100644
index 0000000..3ec289d
--- /dev/null
+++ b/web-extensions/e-dom-utils.h
@@ -0,0 +1,169 @@
+/*
+ * e-dom-utils.h
+ *
+ * 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; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+#ifndef E_DOM_UTILS_H
+#define E_DOM_UTILS_H
+
+#define E_UTIL_INCLUDE_WITHOUT_WEBKIT
+#include <e-util/e-util.h>
+#undef E_UTIL_INCLUDE_WITHOUT_WEBKIT
+
+#include <webkitdom/webkitdom.h>
+
+#include <gtk/gtk.h>
+
+G_BEGIN_DECLS
+
+void           e_dom_utils_replace_local_image_links
+                                               (WebKitDOMDocument *document);
+gboolean       e_dom_utils_document_has_selection
+                                               (WebKitDOMDocument *document);
+gchar *                e_dom_utils_get_document_content_html
+                                               (WebKitDOMDocument *document);
+gchar *                e_dom_utils_get_selection_content_html
+                                               (WebKitDOMDocument *document);
+gchar *                e_dom_utils_get_selection_content_text
+                                               (WebKitDOMDocument *document);
+void           e_dom_utils_create_and_add_css_style_sheet
+                                               (WebKitDOMDocument *document,
+                                                const gchar *style_sheet_id);
+void           e_dom_utils_add_css_rule_into_style_sheet
+                                               (WebKitDOMDocument *document,
+                                                const gchar *style_sheet_id,
+                                                const gchar *selector,
+                                                const gchar *style);
+void           e_dom_utils_eab_contact_formatter_bind_dom
+                                               (WebKitDOMDocument *document);
+void           e_dom_utils_bind_focus_on_elements
+                                               (WebKitDOMDocument *document,
+                                                GDBusConnection *connection);
+void           e_dom_utils_e_mail_display_bind_dom
+                                               (WebKitDOMDocument *document,
+                                                GDBusConnection *connection);
+WebKitDOMElement *
+               e_dom_utils_find_element_by_selector
+                                               (WebKitDOMDocument *document,
+                                                const gchar *selector);
+WebKitDOMElement *
+               e_dom_utils_find_element_by_id  (WebKitDOMDocument *document,
+                                                const gchar *element_id);
+gboolean       e_dom_utils_element_exists
+                                               (WebKitDOMDocument *document,
+                                                const gchar *element_id);
+gchar *                e_dom_utils_get_active_element_name
+                                               (WebKitDOMDocument *document);
+void           e_dom_utils_e_mail_part_headers_bind_dom_element
+                                               (WebKitDOMDocument *document,
+                                                const gchar *element_id);
+void           e_dom_utils_element_set_inner_html
+                                               (WebKitDOMDocument *document,
+                                                const gchar *element_id,
+                                                const gchar *inner_html);
+void           e_dom_utils_remove_element      (WebKitDOMDocument *document,
+                                                const gchar *element_id);
+void           e_dom_utils_element_remove_child_nodes
+                                               (WebKitDOMDocument *document,
+                                                const gchar *element_id);
+void           e_dom_utils_hide_element        (WebKitDOMDocument *document,
+                                                const gchar *element_id,
+                                                 gboolean hide);
+gboolean       e_dom_utils_element_is_hidden   (WebKitDOMDocument *document,
+                                                const gchar *element_id);
+WebKitDOMElement *
+               e_dom_utils_get_element_from_point
+                                               (WebKitDOMDocument *document,
+                                                gint32 x,
+                                                gint32 y);
+WebKitDOMDocument *
+               e_dom_utils_get_document_from_point
+                                               (WebKitDOMDocument *document,
+                                                gint32 x,
+                                                gint32 y);
+/* VCard Inline Module DOM functions */
+void           e_dom_utils_module_vcard_inline_bind_dom
+                                               (WebKitDOMDocument *document,
+                                                const gchar *element_id,
+                                                GDBusConnection *connection);
+void           e_dom_utils_module_vcard_inline_update_button
+                                               (WebKitDOMDocument *document,
+                                                const gchar *button_id,
+                                                const gchar *html_label,
+                                                const gchar *access_key);
+void           e_dom_utils_module_vcard_inline_set_iframe_src
+                                               (WebKitDOMDocument *document,
+                                                const gchar *button_id,
+                                                const gchar *src);
+WebKitDOMElement *
+               dom_node_find_parent_element    (WebKitDOMNode *node,
+                                                const gchar *tagname);
+WebKitDOMElement *
+               dom_node_find_child_element     (WebKitDOMNode *node,
+                                                const gchar *tagname);
+gboolean       element_has_id                  (WebKitDOMElement *element,
+                                                const gchar* id);
+gboolean       element_has_tag                 (WebKitDOMElement *element,
+                                                const gchar* tag);
+gboolean       element_has_class               (WebKitDOMElement *element,
+                                                const gchar* class);
+void           element_add_class               (WebKitDOMElement *element,
+                                                const gchar* class);
+void           element_remove_class            (WebKitDOMElement *element,
+                                                const gchar* class);
+void           element_rename_attribute        (WebKitDOMElement *element,
+                                                const gchar *from,
+                                                const gchar *to);
+void           remove_node                     (WebKitDOMNode *node);
+void           remove_node_if_empty            (WebKitDOMNode *node);
+WebKitDOMNode *        split_list_into_two             (WebKitDOMNode *item,
+                                                gint level);
+WebKitDOMElement *
+               dom_create_selection_marker     (WebKitDOMDocument *document,
+                                                gboolean start);
+void           dom_add_selection_markers_into_element_start
+                                               (WebKitDOMDocument *document,
+                                                WebKitDOMElement *element,
+                                                WebKitDOMElement **selection_start_marker,
+                                                WebKitDOMElement **selection_end_marker);
+void           dom_add_selection_markers_into_element_end
+                                               (WebKitDOMDocument *document,
+                                                WebKitDOMElement *element,
+                                                WebKitDOMElement **selection_start_marker,
+                                                WebKitDOMElement **selection_end_marker);
+void           dom_remove_selection_markers    (WebKitDOMDocument *document);
+gboolean       node_is_list                    (WebKitDOMNode *node);
+gboolean       node_is_list_or_item            (WebKitDOMNode *node);
+EContentEditorBlockFormat
+               dom_get_list_format_from_node   (WebKitDOMNode *node);
+void           merge_list_into_list            (WebKitDOMNode *from,
+                                                WebKitDOMNode *to,
+                                                gboolean insert_before);
+void           merge_lists_if_possible         (WebKitDOMNode *list);
+WebKitDOMElement *
+               get_parent_block_element        (WebKitDOMNode *node);
+gchar *                dom_get_node_inner_html         (WebKitDOMNode *node);
+WebKitDOMDocument *
+               e_dom_utils_find_document_with_uri
+                                               (WebKitDOMDocument *root_document,
+                                                const gchar *find_document_uri);
+void           dom_element_swap_attributes     (WebKitDOMElement *element,
+                                                 const gchar *from,
+                                                 const gchar *to);
+
+G_END_DECLS
+
+#endif /* E_DOM_UTILS_H */
diff --git a/web-extensions/e-web-extension-main.c b/web-extensions/e-web-extension-main.c
new file mode 100644
index 0000000..5276734
--- /dev/null
+++ b/web-extensions/e-web-extension-main.c
@@ -0,0 +1,61 @@
+/*
+ * e-web-extension-main.c
+ *
+ * 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; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <camel/camel.h>
+
+#include "e-web-extension.h"
+#include "e-web-extension-names.h"
+
+static void
+bus_acquired_cb (GDBusConnection *connection,
+                 const gchar *name,
+                 EWebExtension *extension)
+{
+       e_web_extension_dbus_register (extension, connection);
+}
+
+/* Forward declaration */
+G_MODULE_EXPORT void webkit_web_extension_initialize (WebKitWebExtension *wk_extension);
+
+G_MODULE_EXPORT void
+webkit_web_extension_initialize (WebKitWebExtension *wk_extension)
+{
+       EWebExtension *extension;
+
+       camel_debug_init ();
+
+       if (camel_debug ("wex"))
+               printf ("%s\n", G_STRFUNC);
+
+       extension = e_web_extension_get ();
+       e_web_extension_initialize (extension, wk_extension);
+
+       g_bus_own_name (
+               G_BUS_TYPE_SESSION,
+               E_WEB_EXTENSION_SERVICE_NAME,
+               G_BUS_NAME_OWNER_FLAGS_NONE,
+               (GBusAcquiredCallback) bus_acquired_cb,
+               NULL, /* GBusNameAcquiredCallback */
+               NULL, /* GBusNameLostCallback */
+               g_object_ref (extension),
+               (GDestroyNotify) g_object_unref);
+}
diff --git a/web-extensions/e-web-extension-names.h b/web-extensions/e-web-extension-names.h
new file mode 100644
index 0000000..eedf271
--- /dev/null
+++ b/web-extensions/e-web-extension-names.h
@@ -0,0 +1,26 @@
+/*
+ * e-web-extension-names.h
+ *
+ * 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; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+#ifndef E_WEB_EXTENSION_NAMES_H
+#define E_WEB_EXTENSION_NAMES_H
+
+#define E_WEB_EXTENSION_SERVICE_NAME "org.gnome.Evolution.WebExtension"
+#define E_WEB_EXTENSION_OBJECT_PATH  "/org/gnome/Evolution/WebExtension"
+#define E_WEB_EXTENSION_INTERFACE    "org.gnome.Evolution.WebExtension"
+
+#endif /* E_WEB_EXTENSION_NAMES_H */
diff --git a/web-extensions/e-web-extension.c b/web-extensions/e-web-extension.c
new file mode 100644
index 0000000..0492077
--- /dev/null
+++ b/web-extensions/e-web-extension.c
@@ -0,0 +1,999 @@
+/*
+ * e-web-extension.c
+ *
+ * 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; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <string.h>
+
+#include <gio/gio.h>
+#include <glib/gstdio.h>
+#include <gtk/gtk.h>
+
+#include <camel/camel.h>
+#include <libedataserver/libedataserver.h>
+
+#include "e-web-extension.h"
+#include "e-dom-utils.h"
+#include "e-web-extension-names.h"
+
+#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))
+
+struct _EWebExtensionPrivate {
+       WebKitWebExtension *wk_extension;
+
+       GDBusConnection *dbus_connection;
+       guint registration_id;
+
+       gboolean initialized;
+
+       gboolean need_input;
+};
+
+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>"
+"    <method name='SetElementHidden'>"
+"      <arg type='t' name='page_id' direction='in'/>"
+"      <arg type='s' name='element_id' direction='in'/>"
+"      <arg type='b' name='hidden' direction='in'/>"
+"    </method>"
+"    <method name='SetElementStyleProperty'>"
+"      <arg type='t' name='page_id' direction='in'/>"
+"      <arg type='s' name='element_id' direction='in'/>"
+"      <arg type='s' name='property_name' direction='in'/>"
+"      <arg type='s' name='value' direction='in'/>"
+"      <arg type='s' name='priority' direction='in'/>"
+"    </method>"
+"    <method name='SetElementAttribute'>"
+"      <arg type='t' name='page_id' direction='in'/>"
+"      <arg type='s' name='element_id' direction='in'/>"
+"      <arg type='s' name='namespace_uri' direction='in'/>"
+"      <arg type='s' name='qualified_name' direction='in'/>"
+"      <arg type='s' name='value' direction='in'/>"
+"    </method>"
+"    <signal name='HeadersCollapsed'>"
+"      <arg type='b' name='expanded' direction='out'/>"
+"    </signal>"
+"    <method name='DocumentHasSelection'>"
+"      <arg type='t' name='page_id' direction='in'/>"
+"      <arg type='b' name='has_selection' direction='out'/>"
+"    </method>"
+"    <method name='GetDocumentContentHTML'>"
+"      <arg type='t' name='page_id' direction='in'/>"
+"      <arg type='s' name='html_content' direction='out'/>"
+"    </method>"
+"    <method name='GetSelectionContentHTML'>"
+"      <arg type='t' name='page_id' direction='in'/>"
+"      <arg type='s' name='html_content' direction='out'/>"
+"    </method>"
+"    <method name='GetSelectionContentText'>"
+"      <arg type='t' name='page_id' direction='in'/>"
+"      <arg type='s' name='text_content' direction='out'/>"
+"    </method>"
+"    <method name='CreateAndAddCSSStyleSheet'>"
+"      <arg type='t' name='page_id' direction='in'/>"
+"      <arg type='s' name='style_sheet_id' direction='in'/>"
+"    </method>"
+"    <method name='AddCSSRuleIntoStyleSheet'>"
+"      <arg type='t' name='page_id' direction='in'/>"
+"      <arg type='s' name='style_sheet_id' direction='in'/>"
+"      <arg type='s' name='selector' direction='in'/>"
+"      <arg type='s' name='style' direction='in'/>"
+"    </method>"
+"    <method name='EABContactFormatterBindDOM'>"
+"      <arg type='t' name='page_id' direction='in'/>"
+"    </method>"
+"    <method name='EMailDisplayBindDOM'>"
+"      <arg type='t' name='page_id' direction='in'/>"
+"    </method>"
+"    <method name='ElementExists'>"
+"      <arg type='t' name='page_id' direction='in'/>"
+"      <arg type='s' name='element_id' direction='in'/>"
+"      <arg type='b' name='element_exists' direction='out'/>"
+"      <arg type='t' name='page_id' direction='out'/>"
+"    </method>"
+"    <method name='GetActiveElementName'>"
+"      <arg type='t' name='page_id' direction='in'/>"
+"      <arg type='s' name='element_name' direction='out'/>"
+"    </method>"
+"    <method name='EMailPartHeadersBindDOMElement'>"
+"      <arg type='t' name='page_id' direction='in'/>"
+"      <arg type='s' name='element_id' direction='in'/>"
+"    </method>"
+"    <signal name='VCardInlineDisplayModeToggled'>"
+"      <arg type='s' name='button_id' direction='out'/>"
+"    </signal>"
+"    <signal name='VCardInlineSaveButtonPressed'>"
+"      <arg type='s' name='button_value' direction='out'/>"
+"    </signal>"
+"    <method name='VCardInlineBindDOM'>"
+"      <arg type='t' name='page_id' direction='in'/>"
+"      <arg type='s' name='element_id' direction='in'/>"
+"    </method>"
+"    <method name='VCardInlineUpdateButton'>"
+"      <arg type='t' name='page_id' direction='in'/>"
+"      <arg type='s' name='button_id' direction='in'/>"
+"      <arg type='s' name='html_label' direction='in'/>"
+"      <arg type='s' name='access_key' direction='in'/>"
+"    </method>"
+"    <method name='VCardInlineSetIFrameSrc'>"
+"      <arg type='t' name='page_id' direction='in'/>"
+"      <arg type='s' name='button_id' direction='in'/>"
+"      <arg type='s' name='src' direction='in'/>"
+"    </method>"
+"    <method name='GetDocumentURIFromPoint'>"
+"      <arg type='t' name='page_id' direction='in'/>"
+"      <arg type='i' name='x' direction='in'/>"
+"      <arg type='i' name='y' direction='in'/>"
+"      <arg type='s' name='document_uri' direction='out'/>"
+"    </method>"
+"    <method name='SetDocumentIFrameSrc'>"
+"      <arg type='t' name='page_id' direction='in'/>"
+"      <arg type='s' name='document_uri' direction='in'/>"
+"      <arg type='s' name='new_iframe_src' direction='in'/>"
+"    </method>"
+"    <property type='b' name='NeedInput' access='readwrite'/>"
+"  </interface>"
+"</node>";
+
+G_DEFINE_TYPE (EWebExtension, e_web_extension, G_TYPE_OBJECT)
+
+static WebKitWebPage *
+get_webkit_web_page_or_return_dbus_error (GDBusMethodInvocation *invocation,
+                                          WebKitWebExtension *web_extension,
+                                          guint64 page_id)
+{
+       WebKitWebPage *web_page = webkit_web_extension_get_page (web_extension, page_id);
+       if (!web_page) {
+               g_dbus_method_invocation_return_error (
+                       invocation, G_DBUS_ERROR, G_DBUS_ERROR_INVALID_ARGS,
+                       "Invalid page ID: %" G_GUINT64_FORMAT, page_id);
+       }
+       return web_page;
+}
+
+static void
+element_clicked_cb (WebKitDOMElement *element,
+                   WebKitDOMEvent *event,
+                   gpointer user_data)
+{
+       EWebExtension *extension = user_data;
+       WebKitDOMElement *offset_parent;
+       WebKitDOMDOMWindow *dom_window = NULL;
+       gchar *attr_class, *attr_value;
+       const guint64 *ppage_id;
+       gdouble with_parents_left, with_parents_top;
+       glong scroll_x = 0, scroll_y = 0;
+       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);
+       }
+
+       dom_window = webkit_dom_document_get_default_view (webkit_dom_node_get_owner_document 
(WEBKIT_DOM_NODE (element)));
+       if (WEBKIT_DOM_IS_DOM_WINDOW (dom_window)) {
+               g_object_get (G_OBJECT (dom_window),
+                       "scroll-x", &scroll_x,
+                       "scroll-y", &scroll_y,
+                       NULL);
+       }
+       g_clear_object (&dom_window);
+
+       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 - scroll_x),
+                       (gint) (with_parents_top - scroll_y),
+                       (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
+web_extension_register_element_clicked_in_document (EWebExtension *extension,
+                                                   guint64 page_id,
+                                                   WebKitDOMDocument *document,
+                                                   const gchar *element_class)
+{
+       WebKitDOMHTMLCollection *collection = NULL;
+       gulong ii, len;
+
+       g_return_if_fail (E_IS_WEB_EXTENSION (extension));
+       g_return_if_fail (WEBKIT_DOM_IS_DOCUMENT (document));
+       g_return_if_fail (element_class && *element_class);
+
+       collection = webkit_dom_document_get_elements_by_class_name_as_html_collection (document, 
element_class);
+       if (collection) {
+               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);
+
+                               /* Remove first, in case there was a listener already (it's when
+                                  the page is dynamically filled and not all the elements are
+                                  available in time of the first call. */
+                               webkit_dom_event_target_remove_event_listener (
+                                       WEBKIT_DOM_EVENT_TARGET (node), "click",
+                                       G_CALLBACK (element_clicked_cb), FALSE);
+
+                               webkit_dom_event_target_add_event_listener (
+                                       WEBKIT_DOM_EVENT_TARGET (node), "click",
+                                       G_CALLBACK (element_clicked_cb), FALSE, extension);
+                       }
+               }
+       }
+       g_clear_object (&collection);
+
+       /* Traverse also iframe-s */
+       collection = webkit_dom_document_get_elements_by_tag_name_as_html_collection (document, "iframe");
+       if (collection) {
+               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_HTML_IFRAME_ELEMENT (node)) {
+                               WebKitDOMDocument *content;
+
+                               content = webkit_dom_html_iframe_element_get_content_document 
(WEBKIT_DOM_HTML_IFRAME_ELEMENT (node));
+                               if (content)
+                                       web_extension_register_element_clicked_in_document (extension, 
page_id, content, element_class);
+                       }
+               }
+       }
+       g_clear_object (&collection);
+}
+
+static void
+handle_method_call (GDBusConnection *connection,
+                    const char *sender,
+                    const char *object_path,
+                    const char *interface_name,
+                    const char *method_name,
+                    GVariant *parameters,
+                    GDBusMethodInvocation *invocation,
+                    gpointer user_data)
+{
+       guint64 page_id;
+        EWebExtension *extension = E_WEB_EXTENSION (user_data);
+       WebKitDOMDocument *document;
+       WebKitWebExtension *web_extension = extension->priv->wk_extension;
+       WebKitWebPage *web_page;
+
+       if (g_strcmp0 (interface_name, E_WEB_EXTENSION_INTERFACE) != 0)
+               return;
+
+       if (camel_debug ("wex"))
+               printf ("EWebExtension - %s - %s\n", G_STRFUNC, method_name);
+       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 {
+                       document = webkit_web_page_get_dom_document (web_page);
+                       web_extension_register_element_clicked_in_document (extension, page_id, document, 
element_class);
+               }
+
+               g_dbus_method_invocation_return_value (invocation, NULL);
+       } else if (g_strcmp0 (method_name, "SetElementHidden") == 0) {
+               const gchar *element_id = NULL;
+               gboolean hidden = FALSE;
+
+               g_variant_get (parameters, "(t&sb)", &page_id, &element_id, &hidden);
+
+               web_page = get_webkit_web_page_or_return_dbus_error (invocation, web_extension, page_id);
+               if (!web_page)
+                       return;
+
+               if (!element_id || !*element_id) {
+                       g_warn_if_fail (element_id && *element_id);
+               } else {
+                       document = webkit_web_page_get_dom_document (web_page);
+
+                       /* A secret short-cut, to not have two functions for basically the same thing ("hide 
attachment" and "hide element") */
+                       if (!hidden && g_str_has_prefix (element_id, "attachment-wrapper-")) {
+                               WebKitDOMElement *element;
+
+                               element = e_dom_utils_find_element_by_id (document, element_id);
+
+                               if (WEBKIT_DOM_IS_HTML_ELEMENT (element) &&
+                                   webkit_dom_element_get_child_element_count (element) == 0) {
+                                       gchar *inner_html_data;
+
+                                       inner_html_data = webkit_dom_element_get_attribute (element, 
"inner-html-data");
+                                       if (inner_html_data && *inner_html_data) {
+                                               webkit_dom_element_set_inner_html (element, inner_html_data, 
NULL);
+                                               webkit_dom_element_remove_attribute (element, 
"inner-html-data");
+                                       }
+
+                                       g_free (inner_html_data);
+                               }
+                       }
+
+                       e_dom_utils_hide_element (document, element_id, hidden);
+               }
+
+               g_dbus_method_invocation_return_value (invocation, NULL);
+       } else if (g_strcmp0 (method_name, "SetElementStyleProperty") == 0) {
+               const gchar *element_id = NULL, *property_name = NULL, *value = NULL, *priority = NULL;
+
+               g_variant_get (parameters, "(t&s&s&s&s)", &page_id, &element_id, &property_name, &value, 
&priority);
+
+               web_page = get_webkit_web_page_or_return_dbus_error (invocation, web_extension, page_id);
+               if (!web_page)
+                       return;
+
+               if (!element_id || !*element_id || !property_name || !*property_name) {
+                       g_warn_if_fail (element_id && *element_id);
+                       g_warn_if_fail (property_name && *property_name);
+               } else {
+                       WebKitDOMElement *element;
+                       gboolean use_child = FALSE;
+                       gchar *tmp = NULL;
+
+                       /* element_id can be also of the form: "id::child", where the change will
+                          be done on the first child of it */
+                       use_child = g_str_has_suffix (element_id, "::child");
+                       if (use_child) {
+                               tmp = g_strdup (element_id);
+                               tmp[strlen (tmp) - 7] = '\0';
+
+                               element_id = tmp;
+                       }
+
+                       document = webkit_web_page_get_dom_document (web_page);
+                       element = e_dom_utils_find_element_by_id (document, element_id);
+
+                       if (use_child && element)
+                               element = webkit_dom_element_get_first_element_child (element);
+
+                       if (element) {
+                               WebKitDOMCSSStyleDeclaration *css;
+
+                               css = webkit_dom_element_get_style (element);
+
+                               if (value && *value)
+                                       webkit_dom_css_style_declaration_set_property (css, property_name, 
value, priority, NULL);
+                               else
+                                       g_free (webkit_dom_css_style_declaration_remove_property (css, 
property_name, NULL));
+
+                               g_clear_object (&css);
+                       }
+
+                       g_free (tmp);
+               }
+
+               g_dbus_method_invocation_return_value (invocation, NULL);
+       } else if (g_strcmp0 (method_name, "SetElementAttribute") == 0) {
+               const gchar *element_id = NULL, *namespace_uri = NULL, *qualified_name = NULL, *value = NULL;
+
+               g_variant_get (parameters, "(t&s&s&s&s)", &page_id, &element_id, &namespace_uri, 
&qualified_name, &value);
+
+               web_page = get_webkit_web_page_or_return_dbus_error (invocation, web_extension, page_id);
+               if (!web_page)
+                       return;
+
+               if (!element_id || !*element_id || !qualified_name || !*qualified_name) {
+                       g_warn_if_fail (element_id && *element_id);
+                       g_warn_if_fail (qualified_name && *qualified_name);
+               } else {
+                       WebKitDOMElement *element;
+                       gboolean use_child = FALSE;
+                       gchar *tmp = NULL;
+
+                       /* element_id can be also of the form: "id::child", where the change will
+                          be done on the first child of it */
+                       use_child = g_str_has_suffix (element_id, "::child");
+                       if (use_child) {
+                               tmp = g_strdup (element_id);
+                               tmp[strlen (tmp) - 7] = '\0';
+
+                               element_id = tmp;
+                       }
+
+                       if (namespace_uri && !*namespace_uri)
+                               namespace_uri = NULL;
+
+                       document = webkit_web_page_get_dom_document (web_page);
+                       element = e_dom_utils_find_element_by_id (document, element_id);
+
+                       if (use_child && element)
+                               element = webkit_dom_element_get_first_element_child (element);
+
+                       if (element) {
+                               if (value && *value)
+                                       webkit_dom_element_set_attribute_ns (element, namespace_uri, 
qualified_name, value, NULL);
+                               else
+                                       webkit_dom_element_remove_attribute_ns (element, namespace_uri, 
qualified_name);
+                       }
+
+                       g_free (tmp);
+               }
+
+               g_dbus_method_invocation_return_value (invocation, NULL);
+       } else if (g_strcmp0 (method_name, "DocumentHasSelection") == 0) {
+               gboolean has_selection;
+
+               g_variant_get (parameters, "(t)", &page_id);
+               web_page = get_webkit_web_page_or_return_dbus_error (
+                       invocation, web_extension, page_id);
+               if (!web_page)
+                       return;
+
+               document = webkit_web_page_get_dom_document (web_page);
+               has_selection = e_dom_utils_document_has_selection (document);
+
+               g_dbus_method_invocation_return_value (
+                       invocation, g_variant_new ("(b)", has_selection));
+       } else if (g_strcmp0 (method_name, "GetDocumentContentHTML") == 0) {
+               gchar *html_content;
+
+               g_variant_get (parameters, "(t)", &page_id);
+               web_page = get_webkit_web_page_or_return_dbus_error (
+                       invocation, web_extension, page_id);
+               if (!web_page)
+                       return;
+
+               document = webkit_web_page_get_dom_document (web_page);
+               html_content = e_dom_utils_get_document_content_html (document);
+
+               g_dbus_method_invocation_return_value (
+                       invocation,
+                       g_variant_new (
+                               "(@s)",
+                               g_variant_new_take_string (
+                                       html_content ? html_content : g_strdup (""))));
+       } else if (g_strcmp0 (method_name, "GetSelectionContentHTML") == 0) {
+               gchar *html_content;
+
+               g_variant_get (parameters, "(t)", &page_id);
+               web_page = get_webkit_web_page_or_return_dbus_error (
+                       invocation, web_extension, page_id);
+               if (!web_page)
+                       return;
+
+               document = webkit_web_page_get_dom_document (web_page);
+               html_content = e_dom_utils_get_selection_content_html (document);
+
+               g_dbus_method_invocation_return_value (
+                       invocation,
+                       g_variant_new (
+                               "(@s)",
+                               g_variant_new_take_string (
+                                       html_content ? html_content : g_strdup (""))));
+       } else if (g_strcmp0 (method_name, "GetSelectionContentText") == 0) {
+               gchar *text_content;
+
+               g_variant_get (parameters, "(t)", &page_id);
+               web_page = get_webkit_web_page_or_return_dbus_error (
+                       invocation, web_extension, page_id);
+               if (!web_page)
+                       return;
+
+               document = webkit_web_page_get_dom_document (web_page);
+               text_content = e_dom_utils_get_selection_content_text (document);
+
+               g_dbus_method_invocation_return_value (
+                       invocation, g_variant_new_take_string (text_content));
+       } else if (g_strcmp0 (method_name, "AddCSSRuleIntoStyleSheet") == 0) {
+               const gchar *style_sheet_id, *selector, *style;
+
+               g_variant_get (
+                       parameters,
+                       "(t&s&s&s)",
+                       &page_id, &style_sheet_id, &selector, &style);
+
+               web_page = get_webkit_web_page_or_return_dbus_error (
+                       invocation, web_extension, page_id);
+               if (!web_page)
+                       return;
+
+               document = webkit_web_page_get_dom_document (web_page);
+               e_dom_utils_add_css_rule_into_style_sheet (document, style_sheet_id, selector, style);
+
+               g_dbus_method_invocation_return_value (invocation, NULL);
+       } else if (g_strcmp0 (method_name, "CreateAndAddCSSStyleSheet") == 0) {
+               const gchar *style_sheet_id;
+
+               g_variant_get (parameters, "(t&s)", &page_id, &style_sheet_id);
+               web_page = get_webkit_web_page_or_return_dbus_error (
+                       invocation, web_extension, page_id);
+               if (!web_page)
+                       return;
+
+               document = webkit_web_page_get_dom_document (web_page);
+               e_dom_utils_create_and_add_css_style_sheet (document, style_sheet_id);
+
+               g_dbus_method_invocation_return_value (invocation, NULL);
+       } else if (g_strcmp0 (method_name, "EABContactFormatterBindDOM") == 0) {
+               g_variant_get (parameters, "(t)", &page_id);
+               web_page = get_webkit_web_page_or_return_dbus_error (
+                       invocation, web_extension, page_id);
+               if (!web_page)
+                       return;
+
+               document = webkit_web_page_get_dom_document (web_page);
+               e_dom_utils_eab_contact_formatter_bind_dom (document);
+
+               g_dbus_method_invocation_return_value (invocation, NULL);
+       } else if (g_strcmp0 (method_name, "EMailDisplayBindDOM") == 0) {
+               g_variant_get (parameters, "(t)", &page_id);
+               web_page = get_webkit_web_page_or_return_dbus_error (
+                       invocation, web_extension, page_id);
+               if (!web_page)
+                       return;
+
+               document = webkit_web_page_get_dom_document (web_page);
+               e_dom_utils_e_mail_display_bind_dom (document, connection);
+               e_dom_utils_bind_focus_on_elements (document, connection);
+
+               g_dbus_method_invocation_return_value (invocation, NULL);
+       } else if (g_strcmp0 (method_name, "ElementExists") == 0) {
+               const gchar *element_id;
+               gboolean element_exists;
+
+               g_variant_get (parameters, "(t&s)", &page_id, &element_id);
+               web_page = get_webkit_web_page_or_return_dbus_error (
+                       invocation, web_extension, page_id);
+               if (!web_page)
+                       return;
+
+               document = webkit_web_page_get_dom_document (web_page);
+               element_exists = e_dom_utils_element_exists (document, element_id);
+
+               g_dbus_method_invocation_return_value (
+                       invocation, g_variant_new ("(bt)", element_exists, page_id));
+       } else if (g_strcmp0 (method_name, "GetActiveElementName") == 0) {
+               gchar *element_name;
+
+               g_variant_get (parameters, "(t)", &page_id);
+               web_page = get_webkit_web_page_or_return_dbus_error (
+                       invocation, web_extension, page_id);
+               if (!web_page)
+                       return;
+
+               document = webkit_web_page_get_dom_document (web_page);
+               element_name = e_dom_utils_get_active_element_name (document);
+
+               g_dbus_method_invocation_return_value (
+                       invocation,
+                       g_variant_new (
+                               "(@s)",
+                               g_variant_new_take_string (
+                                       element_name ? element_name : g_strdup (""))));
+       } else if (g_strcmp0 (method_name, "EMailPartHeadersBindDOMElement") == 0) {
+               const gchar *element_id;
+
+               g_variant_get (parameters, "(t&s)", &page_id, &element_id);
+               web_page = get_webkit_web_page_or_return_dbus_error (
+                       invocation, web_extension, page_id);
+               if (!web_page)
+                       return;
+
+               document = webkit_web_page_get_dom_document (web_page);
+               e_dom_utils_e_mail_part_headers_bind_dom_element (document, element_id);
+
+               g_dbus_method_invocation_return_value (invocation, NULL);
+       } else if (g_strcmp0 (method_name, "VCardInlineBindDOM") == 0) {
+               const gchar *element_id;
+
+               g_variant_get (parameters, "(t&s)", &page_id, &element_id);
+               web_page = get_webkit_web_page_or_return_dbus_error (
+                       invocation, web_extension, page_id);
+               if (!web_page)
+                       return;
+
+               document = webkit_web_page_get_dom_document (web_page);
+               e_dom_utils_module_vcard_inline_bind_dom (
+                       document, element_id, connection);
+
+               g_dbus_method_invocation_return_value (invocation, NULL);
+       } else if (g_strcmp0 (method_name, "VCardInlineUpdateButton") == 0) {
+               const gchar *button_id, *html_label, *access_key;
+
+               g_variant_get (
+                       parameters,
+                       "(t&s&s&s)",
+                       &page_id, &button_id, &html_label, &access_key);
+
+               web_page = get_webkit_web_page_or_return_dbus_error (
+                       invocation, web_extension, page_id);
+               if (!web_page)
+                       return;
+
+               document = webkit_web_page_get_dom_document (web_page);
+               e_dom_utils_module_vcard_inline_update_button (
+                       document, button_id, html_label, access_key);
+
+               g_dbus_method_invocation_return_value (invocation, NULL);
+       } else if (g_strcmp0 (method_name, "VCardInlineSetIFrameSrc") == 0) {
+               const gchar *src, *button_id;
+
+               g_variant_get (parameters, "(t&s&s)", &page_id, &button_id, &src);
+               web_page = get_webkit_web_page_or_return_dbus_error (
+                       invocation, web_extension, page_id);
+               if (!web_page)
+                       return;
+
+               document = webkit_web_page_get_dom_document (web_page);
+               e_dom_utils_module_vcard_inline_set_iframe_src (document, button_id, src);
+
+               g_dbus_method_invocation_return_value (invocation, NULL);
+       } else if (g_strcmp0 (method_name, "GetDocumentURIFromPoint") == 0) {
+               WebKitDOMDocument *document_at_point;
+               gchar *document_uri = NULL;
+               gint32 xx = 0, yy = 0;
+
+               g_variant_get (parameters, "(tii)", &page_id, &xx, &yy);
+               web_page = get_webkit_web_page_or_return_dbus_error (invocation, web_extension, page_id);
+               if (!web_page)
+                       return;
+
+               document = webkit_web_page_get_dom_document (web_page);
+               document_at_point = e_dom_utils_get_document_from_point (document, xx, yy);
+
+               if (document_at_point)
+                       document_uri = webkit_dom_document_get_document_uri (document_at_point);
+
+               g_dbus_method_invocation_return_value (
+                       invocation,
+                       g_variant_new ("(@s)", g_variant_new_take_string (document_uri ? document_uri : 
g_strdup (""))));
+       } else if (g_strcmp0 (method_name, "SetDocumentIFrameSrc") == 0) {
+               const gchar *document_uri = NULL, *new_iframe_src = NULL;
+               WebKitDOMDocument *iframe_document;
+
+               g_variant_get (parameters, "(t&s&s)", &page_id, &document_uri, &new_iframe_src);
+               web_page = get_webkit_web_page_or_return_dbus_error (invocation, web_extension, page_id);
+               if (!web_page)
+                       return;
+
+               document = webkit_web_page_get_dom_document (web_page);
+               iframe_document = e_dom_utils_find_document_with_uri (document, document_uri);
+
+               if (iframe_document) {
+                       WebKitDOMDOMWindow *dom_window;
+                       WebKitDOMElement *frame_element;
+
+                       /* Get frame's window and from the window the actual <iframe> element */
+                       dom_window = webkit_dom_document_get_default_view (iframe_document);
+                       frame_element = webkit_dom_dom_window_get_frame_element (dom_window);
+                       webkit_dom_html_iframe_element_set_src (
+                               WEBKIT_DOM_HTML_IFRAME_ELEMENT (frame_element), new_iframe_src);
+                       g_clear_object (&dom_window);
+               }
+
+               g_dbus_method_invocation_return_value (invocation, NULL);
+       }
+}
+
+static GVariant *
+handle_get_property (GDBusConnection *connection,
+                     const gchar *sender,
+                     const gchar *object_path,
+                     const gchar *interface_name,
+                     const gchar *property_name,
+                     GError **error,
+                     gpointer user_data)
+{
+       EWebExtension *extension = E_WEB_EXTENSION (user_data);
+       GVariant *variant = NULL;
+
+       if (g_strcmp0 (property_name, "NeedInput") == 0) {
+               variant = g_variant_new_boolean (extension->priv->need_input);
+       }
+
+       return variant;
+}
+
+static gboolean
+handle_set_property (GDBusConnection *connection,
+                     const gchar *sender,
+                     const gchar *object_path,
+                     const gchar *interface_name,
+                     const gchar *property_name,
+                     GVariant *variant,
+                     GError **error,
+                     gpointer user_data)
+{
+       EWebExtension *extension = E_WEB_EXTENSION (user_data);
+       GError *local_error = NULL;
+       GVariantBuilder *builder;
+
+       builder = g_variant_builder_new (G_VARIANT_TYPE_ARRAY);
+
+       if (g_strcmp0 (property_name, "NeedInput") == 0) {
+               gboolean value = g_variant_get_boolean (variant);
+
+               if (value == extension->priv->need_input)
+                       goto exit;
+
+               extension->priv->need_input = value;
+
+               g_variant_builder_add (builder,
+                       "{sv}",
+                       "NeedInput",
+                       g_variant_new_boolean (value));
+       }
+
+       g_dbus_connection_emit_signal (connection,
+               NULL,
+               object_path,
+               "org.freedesktop.DBus.Properties",
+               "PropertiesChanged",
+               g_variant_new (
+                       "(sa{sv}as)",
+                       interface_name,
+                       builder,
+                       NULL),
+               &local_error);
+
+       g_assert_no_error (local_error);
+
+ exit:
+       g_variant_builder_unref (builder);
+
+       return TRUE;
+}
+
+static const GDBusInterfaceVTable interface_vtable = {
+       handle_method_call,
+       handle_get_property,
+       handle_set_property
+};
+
+static void
+e_web_extension_dispose (GObject *object)
+{
+       EWebExtension *extension = E_WEB_EXTENSION (object);
+
+       if (extension->priv->dbus_connection) {
+               g_dbus_connection_unregister_object (
+                       extension->priv->dbus_connection,
+                       extension->priv->registration_id);
+               extension->priv->registration_id = 0;
+               extension->priv->dbus_connection = NULL;
+       }
+
+       g_clear_object (&extension->priv->wk_extension);
+
+       G_OBJECT_CLASS (e_web_extension_parent_class)->dispose (object);
+}
+
+static void
+e_web_extension_class_init (EWebExtensionClass *class)
+{
+       GObjectClass *object_class = G_OBJECT_CLASS (class);
+
+       object_class->dispose = e_web_extension_dispose;
+
+       g_type_class_add_private (object_class, sizeof(EWebExtensionPrivate));
+}
+
+static void
+e_web_extension_init (EWebExtension *extension)
+{
+       extension->priv = G_TYPE_INSTANCE_GET_PRIVATE (extension, E_TYPE_WEB_EXTENSION, EWebExtensionPrivate);
+
+       extension->priv->initialized = FALSE;
+       extension->priv->need_input = FALSE;
+}
+
+static gpointer
+e_web_extension_create_instance(gpointer data)
+{
+       return g_object_new (E_TYPE_WEB_EXTENSION, NULL);
+}
+
+EWebExtension *
+e_web_extension_get (void)
+{
+       static GOnce once_init = G_ONCE_INIT;
+       return E_WEB_EXTENSION (g_once (&once_init, e_web_extension_create_instance, NULL));
+}
+
+static gboolean
+web_page_send_request_cb (WebKitWebPage *web_page,
+                          WebKitURIRequest *request,
+                          WebKitURIResponse *redirected_response,
+                          EWebExtension *extension)
+{
+       const gchar *request_uri;
+       const gchar *page_uri;
+
+       request_uri = webkit_uri_request_get_uri (request);
+       page_uri = webkit_web_page_get_uri (web_page);
+
+       /* Always load the main resource. */
+       if (g_strcmp0 (request_uri, page_uri) == 0 ||
+           /* Do not influence real pages, like those with eds OAuth sign-in */
+           g_str_has_prefix (page_uri, "http:") ||
+           g_str_has_prefix (page_uri, "https:"))
+               return FALSE;
+
+       if (g_str_has_prefix (request_uri, "http:") ||
+           g_str_has_prefix (request_uri, "https:")) {
+               gchar *new_uri;
+
+               new_uri = g_strconcat ("evo-", request_uri, NULL);
+
+               webkit_uri_request_set_uri (request, new_uri);
+
+               g_free (new_uri);
+       }
+
+       return FALSE;
+}
+
+static void
+web_page_document_loaded_cb (WebKitWebPage *web_page,
+                             gpointer user_data)
+{
+       WebKitDOMDocument *document;
+
+       document = webkit_web_page_get_dom_document (web_page);
+
+       e_dom_utils_replace_local_image_links (document);
+
+       if ((webkit_dom_document_query_selector (
+               document, "[data-evo-signature-plain-text-mode]", NULL))) {
+
+               WebKitDOMHTMLElement *body;
+
+               body = webkit_dom_document_get_body (document);
+
+               webkit_dom_element_set_attribute (
+                       WEBKIT_DOM_ELEMENT (body),
+                       "style",
+                       "font-family: Monospace;",
+                       NULL);
+       }
+}
+
+static void
+web_page_created_cb (WebKitWebExtension *wk_extension,
+                     WebKitWebPage *web_page,
+                     EWebExtension *extension)
+{
+       g_signal_connect_object (
+               web_page, "send-request",
+               G_CALLBACK (web_page_send_request_cb),
+               extension, 0);
+
+       g_signal_connect_object (
+               web_page, "document-loaded",
+               G_CALLBACK (web_page_document_loaded_cb),
+               extension, 0);
+
+}
+
+void
+e_web_extension_initialize (EWebExtension *extension,
+                            WebKitWebExtension *wk_extension)
+{
+       g_return_if_fail (E_IS_WEB_EXTENSION (extension));
+
+       if (extension->priv->initialized)
+               return;
+
+       extension->priv->initialized = TRUE;
+
+       extension->priv->wk_extension = g_object_ref (wk_extension);
+
+       g_signal_connect (
+               wk_extension, "page-created",
+               G_CALLBACK (web_page_created_cb), extension);
+}
+
+void
+e_web_extension_dbus_register (EWebExtension *extension,
+                               GDBusConnection *connection)
+{
+       GError *error = NULL;
+       static GDBusNodeInfo *introspection_data = NULL;
+
+       g_return_if_fail (E_IS_WEB_EXTENSION (extension));
+       g_return_if_fail (G_IS_DBUS_CONNECTION (connection));
+
+       if (!introspection_data) {
+               introspection_data =
+                       g_dbus_node_info_new_for_xml (introspection_xml, NULL);
+
+               extension->priv->registration_id =
+                       g_dbus_connection_register_object (
+                               connection,
+                               E_WEB_EXTENSION_OBJECT_PATH,
+                               introspection_data->interfaces[0],
+                               &interface_vtable,
+                               extension,
+                               NULL,
+                               &error);
+
+               if (!extension->priv->registration_id) {
+                       g_warning ("Failed to register object: %s\n", error->message);
+                       g_error_free (error);
+               } else {
+                       extension->priv->dbus_connection = connection;
+                       g_object_add_weak_pointer (
+                               G_OBJECT (connection),
+                               (gpointer *)&extension->priv->dbus_connection);
+               }
+       }
+}
diff --git a/web-extensions/e-web-extension.h b/web-extensions/e-web-extension.h
new file mode 100644
index 0000000..a1f5837
--- /dev/null
+++ b/web-extensions/e-web-extension.h
@@ -0,0 +1,72 @@
+/*
+ * e-web-extension.h
+ *
+ * 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; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+#ifndef E_WEB_EXTENSION_H
+#define E_WEB_EXTENSION_H
+
+#include <webkit2/webkit-web-extension.h>
+#include <glib-object.h>
+
+/* Standard GObject macros */
+#define E_TYPE_WEB_EXTENSION \
+       (e_web_extension_get_type ())
+#define E_WEB_EXTENSION(obj) \
+       (G_TYPE_CHECK_INSTANCE_CAST \
+       ((obj), E_TYPE_WEB_EXTENSION, EWebExtension))
+#define E_WEB_EXTENSION_CLASS(cls) \
+       (G_TYPE_CHECK_CLASS_CAST \
+       ((cls), E_TYPE_WEB_EXTENSION, EWebExtensionClass))
+#define E_IS_WEB_EXTENSION(obj) \
+       (G_TYPE_CHECK_INSTANCE_TYPE \
+       ((obj), E_TYPE_WEB_EXTENSION))
+#define E_IS_WEB_EXTENSION_CLASS(cls) \
+       (G_TYPE_CHECK_CLASS_TYPE \
+       ((cls), E_TYPE_WEB_EXTENSION))
+#define E_WEB_EXTENSION_GET_CLASS(obj) \
+       (G_TYPE_INSTANCE_GET_CLASS \
+       ((obj), E_TYPE_WEB_EXTENSION, EWebExtensionClass))
+
+G_BEGIN_DECLS
+
+typedef struct _EWebExtension EWebExtension;
+typedef struct _EWebExtensionClass EWebExtensionClass;
+typedef struct _EWebExtensionPrivate EWebExtensionPrivate;
+
+struct _EWebExtension {
+       GObject parent;
+       EWebExtensionPrivate *priv;
+};
+
+struct _EWebExtensionClass
+{
+       GObjectClass parent_class;
+};
+
+GType          e_web_extension_get_type        (void) G_GNUC_CONST;
+
+EWebExtension *        e_web_extension_get             (void);
+
+void           e_web_extension_initialize      (EWebExtension *extension,
+                                                WebKitWebExtension *wk_extension);
+
+void           e_web_extension_dbus_register   (EWebExtension *extension,
+                                                GDBusConnection *connection);
+
+G_END_DECLS
+
+#endif /* E_WEB_EXTENSION_H */



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